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