Octopus
debug.F90
Go to the documentation of this file.
1!! Copyright (C) 2016 X. Andrade
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
21module debug_oct_m
22 use global_oct_m
24 use mpi_oct_m
26 use loct_oct_m
27 use parser_oct_m
28
29 implicit none
30
31 private
32 public :: &
33 debug_t, &
34 debug_init, &
39 debug, &
44
45 type debug_t
46 private
47 logical, public :: info
48 logical, public :: trace
49 logical, public :: trace_term
50 logical, public :: trace_file
51 logical :: extra_checks
52 logical, public :: interaction_graph
53 logical, public :: interaction_graph_full
54 logical, public :: propagation_graph
55 logical, public :: instrument
56 integer :: bits
57 character(len=MAX_PATH_LEN), public :: instr_sub_name
58 integer, public :: instr_tool
59 end type debug_t
60
61 type(debug_t), save :: debug
62
64 integer, parameter :: unit_offset = 1000
65
66 interface
67 subroutine debug_verrou_start_instrumentation() bind(C)
69
70 subroutine debug_verrou_stop_instrumentation() bind(C)
72
73 subroutine debug_fenv_start_instrumentation() bind(C)
75
76 subroutine debug_fenv_stop_instrumentation() bind(C)
78 end interface
79
80contains
81
82 subroutine debug_init(this, namespace)
83 type(debug_t), intent(out) :: this
84 type(namespace_t), intent(in) :: namespace
85
86 character(len=256) :: node_hook
87 logical :: file_exists, mpi_debug_hook
88 integer :: sec, usec
89 type(block_t) :: blk
90 integer :: line
91
92 !%Variable Debug
93 !%Type flag
94 !%Default no
95 !%Section Execution::Debug
96 !%Description
97 !% This variable controls the amount of debugging information
98 !% generated by Octopus. You can use include more than one option
99 !% with the + operator.
100 !%Option no 0
101 !% (default) <tt>Octopus</tt> does not enter debug mode.
102 !%Option info 1
103 !% Octopus prints additional information to the terminal.
104 !%Option trace 2
105 !% Octopus generates a stack trace as it enters end exits
106 !% subroutines. This information is reported if Octopus stops with
107 !% an error.
108 !%Option trace_term 4
109 !% The trace is printed to the terminal as Octopus enters or exits subroutines. This slows down execution considerably.
110 !%Option trace_file 8
111 !% The trace is written to files in the <tt>debug</tt>
112 !% directory. For each node (when running in parallel) there is a file called
113 !% <tt>debug_trace.&lt;rank&gt;</tt>. Writing these files slows down the code by a huge factor and
114 !% it is usually only necessary for parallel runs.
115 !%Option extra_checks 16
116 !% This enables Octopus to perform some extra checks, to ensure
117 !% code correctness, that might be too costly for regular runs.
118 !%Option interaction_graph 32
119 !% Octopus generates a dot file containing the graph for a multisystem run.
120 !%Option interaction_graph_full 64
121 !% Octopus generates a dot file containing the graph for a multisystem run including ghost interactions.
122 !%Option propagation_graph 128
123 !% Octopus generates a file with information for the propagation diagram.
124 !%Option instrument 256
125 !% Octopus adds instrumentation to functions specified in an <tt>InstrumentFunctions</tt> block.
126 !%End
127 call parse_variable(namespace, 'Debug', option__debug__no, this%bits)
128
129 call from_bits(this)
130
131 ! Ensure instrumentation fields are initialized even if block is absent.
132 this%instr_sub_name = ''
133 this%instr_tool = 0
134
135 !%Variable InstrumentFunctions
136 !%Type block
137 !%Section Execution::Debug
138 !%Description
139 !% This input options controls which routines are going to be instrumented
140 !% for the tools selected using the <tt>Debug=instrument</tt> option.
141 !%
142 !% <br>%<tt>InstrumentFunctions
143 !% <br>&nbsp;&nbsp;"function_name" | instrumentation_tool
144 !% <br>%</tt>
145 !%
146 !% Here is an example to better understand how this works:
147 !%
148 !% <br>%<tt>InstrumentFunctions
149 !% <br>&nbsp;&nbsp;"grid/grid.F90.grid_init_from_grid_stage_1" | verrou
150 !% <br>%</tt>
151 !%
152 !% NOTE: Currently only a single function can be instrumented!
153 !%
154 !% Available instrumentation tools:
155 !%Option verrou 1
156 !% Verrou helps you look for floating-point round-off errors.
157 !%Option fenv 2
158 !% Enable floating-point exceptions. Requires Octopus to be compiled against glibc.
159 !%End
160 if (parse_block(namespace, "InstrumentFunctions", blk) == 0) then
161 ! TODO: Allow instrumentation of more than a single function
162 if (parse_block_n(blk) .gt. 1) then
163 write(stderr,'(a)') "Only single function can be instrumented!"
164 call mpi_world%abort()
165 end if
166
167 do line = 0, parse_block_n(blk) - 1
168 call parse_block_string(blk, line, 0, this%instr_sub_name)
169 call parse_block_integer(blk, line, 1, this%instr_tool)
170 select case (this%instr_tool)
171 case (option__instrumentfunctions__verrou)
172 write(stderr,'(a)') "Instrumenting " // trim(this%instr_sub_name) // " for Verrou"
173#if !defined(HAVE_VERROU)
174 write(stderr,'(a)') "requires VERROU but that library was not linked."
175 call mpi_world%abort()
176#endif
177 case (option__instrumentfunctions__fenv)
178 write(stderr,'(a)') "Instrumenting " // trim(this%instr_sub_name) // " with floating-point exceptions"
179 case default
180 assert(.false.) ! Should not happen
181 end select
182 end do
183 call parse_block_end(blk)
184 else if (this%instrument) then
185 write(stderr,'(a)') "Debug=instrument requires InstrumentFunctions block."
186 call mpi_world%abort()
187 end if
188
189 call mpi_debug_init(mpi_world%rank, this%info)
190
191 if (this%info) then
192 !%Variable MPIDebugHook
193 !%Type logical
194 !%Default no
195 !%Section Execution::Debug
196 !%Description
197 !% When debugging the code in parallel it is usually difficult to find the origin
198 !% of race conditions that appear in MPI communications. This variable introduces
199 !% a facility to control separate MPI processes. If set to yes, all nodes will
200 !% start up, but will get trapped in an endless loop. In every cycle of the loop
201 !% each node is sleeping for one second and is then checking if a file with the
202 !% name <tt>node_hook.xxx</tt> (where <tt>xxx</tt> denotes the node number) exists. A given node can
203 !% only be released from the loop if the corresponding file is created. This allows
204 !% to selectively run, <i>e.g.</i>, a compute node first followed by the master node. Or, by
205 !% reversing the file creation of the node hooks, to run the master first followed
206 !% by a compute node.
207 !%End
208 call parse_variable(global_namespace, 'MPIDebugHook', .false., mpi_debug_hook)
209 if (mpi_debug_hook) then
210 call loct_gettimeofday(sec, usec)
211 call epoch_time_diff(sec,usec)
212 write(stdout,'(a,i6,a,i6.6,20x,a)') '* I ',sec,'.',usec,' | MPI debug hook'
213
214 write(stdout,'(a,i3,a)') 'node:', mpi_world%rank, ' In debug hook'
215 write(node_hook,'(i3.3)') mpi_world%rank
216 file_exists = .false.
217
218 do while (.not. file_exists)
219 inquire(file='node_hook.'//node_hook, exist=file_exists)
220 call loct_nanosleep(1,0)
221 write(stdout,'(a,i3,a)') 'node:', mpi_world%rank, &
222 ' - still sleeping. To release me touch: node_hook.'//trim(node_hook)
223 end do
224
225 write(stdout,'(a,i3,a)') 'node:', mpi_world%rank, ' Leaving debug hook'
226 ! remove possible debug hooks
227 call loct_rm('node_hook.'//trim(node_hook))
228
229 call loct_gettimeofday(sec, usec)
230 call epoch_time_diff(sec,usec)
231 write(stdout,'(a,i6,a,i6.6,20x,a)') '* O ', sec, '.', usec,' | MPI debug hook'
232 end if
233 end if
234
235 end subroutine debug_init
236
237 !--------------------------------------------------
238
239 subroutine debug_enable(this)
240 type(debug_t), intent(inout) :: this
241
242 this%info = .true.
243 this%trace = .true.
244 this%trace_term = .true.
245 this%trace_file = .true.
246 this%interaction_graph = .true.
247 this%interaction_graph_full = .true.
248 this%propagation_graph = .true.
249
250 end subroutine debug_enable
251
252 !--------------------------------------------------
253
254 subroutine debug_disable(this)
255 type(debug_t), intent(inout) :: this
256
257 call from_bits(this)
258
259 end subroutine debug_disable
260
261 !--------------------------------------------------
262
263 subroutine debug_delete_trace()
264
265 integer :: iunit
266 character(len=6) :: filenum
267
268 iunit = mpi_world%rank + unit_offset
269 write(filenum, '(i6.6)') iunit - unit_offset
270 call loct_mkdir('debug')
271 call loct_rm('debug/debug_trace.node.'//filenum)
272
273 end subroutine debug_delete_trace
274
275 ! ---------------------------------------------------------
276
277 subroutine debug_open_trace(iunit)
278 integer, intent(out) :: iunit
279
280 character(len=6) :: filenum
281
282 iunit = mpi_world%rank + unit_offset
283 write(filenum, '(i6.6)') iunit - unit_offset
284 call loct_mkdir('debug')
285 open(iunit, file = 'debug/debug_trace.node.'//filenum, &
286 action='write', status='unknown', position='append')
287
288 end subroutine debug_open_trace
289
290 ! ---------------------------------------------------------
291
292 subroutine from_bits(this)
293 type(debug_t), intent(inout) :: this
294
295 this%info = (bitand(this%bits, option__debug__info) /= 0)
296 this%trace_term = (bitand(this%bits, option__debug__trace_term) /= 0)
297 this%trace_file = (bitand(this%bits, option__debug__trace_file) /= 0)
298 this%trace = (bitand(this%bits, option__debug__trace) /= 0) .or. this%trace_term .or. this%trace_file
299 this%extra_checks = (bitand(this%bits, option__debug__extra_checks) /= 0) .or. this%trace_term .or. this%trace_file
300 this%interaction_graph = (bitand(this%bits, option__debug__interaction_graph) /= 0)
301 this%interaction_graph_full = (bitand(this%bits, option__debug__interaction_graph_full) /= 0)
302 this%propagation_graph = (bitand(this%bits, option__debug__propagation_graph) /= 0)
303 this%instrument = (bitand(this%bits, option__debug__instrument) /= 0)
304
305 end subroutine from_bits
306
307
308 ! ---------------------------------------------------------
309 subroutine epoch_time_diff(sec, usec)
310 integer, intent(inout) :: sec
311 integer, intent(inout) :: usec
312
313 ! this is called by push/pop so there cannot be a push/pop in this routine
314
315 call time_diff(s_epoch_sec, s_epoch_usec, sec, usec)
316 end subroutine epoch_time_diff
317
318
320 subroutine time_diff(sec1, usec1, sec2, usec2)
321 integer, intent(in) :: sec1
322 integer, intent(in) :: usec1
323 integer, intent(inout) :: sec2
324 integer, intent(inout) :: usec2
325
326 ! this is called by push/pop so there cannot be a push/pop in this routine
327
328 ! Correct overflow.
329 if (usec2 - usec1 < 0) then
330 usec2 = 1000000 + usec2
331 if (sec2 >= sec1) then
332 sec2 = sec2 - 1
333 end if
334 end if
335
336 ! Replace values.
337 if (sec2 >= sec1) then
338 sec2 = sec2 - sec1
339 end if
340 usec2 = usec2 - usec1
341
342 end subroutine time_diff
343
344
346 subroutine debug_push_sub(sub_name)
347 character(len=*), intent(in) :: sub_name
348
349 integer, parameter :: max_recursion_level = 50
350 integer iunit, sec, usec
351
352 if (debug%instrument) then
353 if (debug_clean_path(sub_name) == trim(debug%instr_sub_name)) then
354 select case (debug%instr_tool)
355 case (option__instrumentfunctions__verrou)
357 case (option__instrumentfunctions__fenv)
359 case default
360 assert(.false.) ! cannot happen
361 end select
362 end if
363 end if
364
365 if (.not. debug%trace) return
366
367 if (debug%trace_file .or. debug%trace_term) then
368 call loct_gettimeofday(sec, usec)
369 call epoch_time_diff(sec, usec)
370 end if
371
373 if (no_sub_stack >= max_recursion_level) then
374 sub_stack(max_recursion_level) = 'debug_push_sub'
375 write(stderr, '(a,i3,a)') 'Too many recursion levels in debug trace (max=', max_recursion_level, ')'
376 write(stderr, '(a,a)') 'Last sub name is ', trim(debug_clean_path(sub_name))
377 call mpi_world%abort()
378 end if
379
380 sub_stack(no_sub_stack) = trim(debug_clean_path(sub_name))
382
383 if (debug%trace_file) then
384 call debug_open_trace(iunit)
385 call push_sub_write(iunit)
386 ! close file to ensure flushing
387 close(iunit)
388 end if
389
390 if (debug%trace_term .and. mpi_world%is_root()) then
391 ! write to stderr if we are node 0
392 call push_sub_write(stderr)
393 end if
394
395 contains
396
397 subroutine push_sub_write(iunit_out)
398 integer, intent(in) :: iunit_out
399
400 integer :: ii
401 character(len=1000) :: tmpstr
402
403 write(tmpstr,'(a,i6,a,i6.6,f20.6,i8,a)') "* I ", &
404 sec, '.', usec, &
405 loct_clock(), &
406 loct_get_memory_usage() / 1024, " | "
407 do ii = no_sub_stack - 1, 1, -1
408 write(tmpstr, '(2a)') trim(tmpstr), "..|"
409 end do
410 write(tmpstr, '(2a)') trim(tmpstr), trim(debug_clean_path(sub_name))
411 write(iunit_out, '(a)') trim(tmpstr)
412
413 end subroutine push_sub_write
414
415 end subroutine debug_push_sub
416
417
419 subroutine debug_pop_sub(sub_name)
420 character(len=*), intent(in) :: sub_name
421
422 character(len=80) :: sub_name_short
423 integer iunit, sec, usec
424
425 if (debug%instrument) then
426 if (debug_clean_path(sub_name) == trim(debug%instr_sub_name)) then
427 select case (debug%instr_tool)
428 case (option__instrumentfunctions__verrou)
430 case (option__instrumentfunctions__fenv)
432 case default
433 assert(.false.) ! cannot happen
434 end select
435 end if
436 end if
437
438 if (.not. debug%trace) return
439
440 call loct_gettimeofday(sec, usec)
441 call epoch_time_diff(sec, usec)
442
443 if (no_sub_stack <= 0) then
444 no_sub_stack = 1
445 sub_stack(1) = 'pop_sub'
446 write(stderr, '(a)') 'Too few recursion levels in debug trace'
447 call mpi_world%abort()
448 end if
449
450 ! the name might be truncated in sub_stack, so we copy to a string
451 ! of the same size
452 sub_name_short = trim(debug_clean_path(sub_name))
453
454 if (sub_name_short /= sub_stack(no_sub_stack)) then
455 write(stderr, '(a)') 'Wrong sub name on pop_sub :'
456 write(stderr, '(2a)') ' got : ', sub_name_short
457 write(stderr, '(2a)') ' expected : ', sub_stack(no_sub_stack)
458 call mpi_world%abort()
459 end if
460
461 if (debug%trace_file) then
462 call debug_open_trace(iunit)
463 call pop_sub_write(iunit)
464 ! close file to ensure flushing
465 close(iunit)
466 end if
467
468 if (debug%trace_term .and. mpi_world%is_root()) then
469 ! write to stderr if we are node 0
470 call pop_sub_write(stderr)
471 end if
472
474
475 contains
476
477 subroutine pop_sub_write(iunit_out)
478 integer, intent(in) :: iunit_out
479
480 integer :: ii
481 character(len=1000) :: tmpstr
482
483 write(tmpstr,'(a,i6,a,i6.6,f20.6,i8, a)') "* O ", &
484 sec, '.', usec, &
486 loct_get_memory_usage() / 1024, " | "
487 do ii = no_sub_stack - 1, 1, -1
488 write(tmpstr,'(2a)') trim(tmpstr), "..|"
489 end do
490 write(tmpstr,'(2a)') trim(tmpstr), trim(sub_stack(no_sub_stack))
491
492 write(iunit_out, '(a)') trim(tmpstr)
493
494 end subroutine pop_sub_write
495
496 end subroutine debug_pop_sub
497
499 character(len=MAX_PATH_LEN) function debug_clean_path(filename) result(clean_path)
500 character(len=*), intent(in) :: filename
501
502 integer :: pos
503
504 pos = index(filename, 'src/', back = .true.)
505 if (pos == 0) then
506 ! 'src/' does not occur
507 clean_path = filename
508 else
509 ! remove 'src/'
510 clean_path = filename(pos+4:)
511 end if
512
513 end function debug_clean_path
515end module debug_oct_m
516
517!! Local Variables:
518!! mode: f90
519!! coding: utf-8
520!! End:
subroutine pop_sub_write(iunit_out)
Definition: debug.F90:573
subroutine push_sub_write(iunit_out)
Definition: debug.F90:493
character(len=max_path_len) function, public debug_clean_path(filename)
Prune a filename path to only include subdirectories of the "src" directory.
Definition: debug.F90:595
subroutine, public debug_enable(this)
Definition: debug.F90:335
type(debug_t), save, public debug
Definition: debug.F90:156
subroutine, public debug_pop_sub(sub_name)
Pop a routine from the debug trace.
Definition: debug.F90:515
subroutine, public debug_open_trace(iunit)
Definition: debug.F90:373
subroutine, public epoch_time_diff(sec, usec)
Definition: debug.F90:405
subroutine from_bits(this)
Definition: debug.F90:388
subroutine, public debug_init(this, namespace)
Definition: debug.F90:178
subroutine, public debug_disable(this)
Definition: debug.F90:350
subroutine time_diff(sec1, usec1, sec2, usec2)
Computes t2 <- t2-t1.
Definition: debug.F90:416
subroutine, public debug_push_sub(sub_name)
Push a routine to the debug trace.
Definition: debug.F90:442
subroutine, public debug_delete_trace()
Definition: debug.F90:359
integer, public s_epoch_sec
global epoch time (time at startup)
Definition: global.F90:262
integer, public no_sub_stack
Definition: global.F90:267
real(real64), dimension(50), public time_stack
Definition: global.F90:266
character(len=80), dimension(50), public sub_stack
The stack.
Definition: global.F90:265
integer, public s_epoch_usec
Definition: global.F90:262
System information (time, memory, sysname)
Definition: loct.F90:117
subroutine, public loct_rm(name)
Definition: loct.F90:318
subroutine, public loct_mkdir(name)
Definition: loct.F90:304
subroutine, public mpi_debug_init(rank, info)
Definition: mpi_debug.F90:196
type(mpi_grp_t), public mpi_world
Definition: mpi.F90:272
type(namespace_t), public global_namespace
Definition: namespace.F90:135
subroutine, public parse_block_string(blk, l, c, res, convert_to_c)
Definition: parser.F90:818
integer function, public parse_block(namespace, name, blk, check_varinfo_)
Definition: parser.F90:623
int true(void)