← back to index

src/framework/MOM_error_handler.F90

portedportable, not yet portedexecuted, not portableexecutable, not hit by this run

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5!> Routines for error handling and I/O management
6module MOM_error_handler
7
8use MOM_coms_infra, only : num_PEs
9use MOM_error_infra, only : MOM_err, is_root_pe, stdlog, stdout, NOTE, WARNING, FATAL
10use posix, only : getpid, getppid, handler_interface
11use posix, only : signal, kill, SIGUSR1
12use posix, only : sigjmp_buf, siglongjmp
13use posix, only : sleep
14
15! MOM_error_infra does not provide stderr . We only use stderr in this module
16! *IF* FMS has not been initialized. Further, stderr is only used internally and
17! not made public. Other modules should obtain stderr from MOM_io.
18use iso_fortran_env, only : stderr=>error_unit
19
20implicit none ; private
21
22! These routines are found in this module.
23public :: MOM_error, MOM_mesg, assert
24public :: MOM_set_verbosity, MOM_get_verbosity, MOM_verbose_enough
25public :: callTree_showQuery, callTree_enter, callTree_leave, callTree_waypoint
26! These routines are simply passed-through from MOM_error_infra
27public :: is_root_pe, stdlog, stdout
28!> Integer parameters encoding the severity of an error message
29public :: NOTE, WARNING, FATAL
30public :: disable_fatal_errors, enable_fatal_errors, set_skip_mpi
31
32integer :: verbosity = 6
33!< Verbosity level:
34!! 0 - FATAL messages only
35!! 1 - FATAL + WARNING messages only
36!! 2 - FATAL + WARNING + NOTE messages only [default]
37!! 3 - above + informational
38!! 4 -
39!! 5 -
40!! 6 - above + call tree
41!! 7 -
42!! 8 -
43!! 9 - anything and everything (also set with DEBUG=True)
44
45! Note that this module default will only hold until the
46! VERBOSITY parameter is parsed and the given default imposed.
47! We set it to 6 here so that the call tree will print before
48! the parser has been initialized
49! Also note that this is a module variable rather than contained in
50! a type passed by argument (preferred for most data) for convenience
51! and to reduce obfuscation of code
52
53integer :: callTreeIndentLevel = 0
54!< The level of calling within the call tree
55
56! Error handling
57
58logical :: ignore_fatal = .false.
59 !< If true, ignore FATAL errors and jump to a prior state.
60integer, parameter :: err_signal = SIGUSR1
61 !< Signal used to trigger the error handler
62integer :: err_pid
63 !< Process ID for the error handler (either self or MPI launcher)
64procedure(handler_interface), pointer :: prior_handler
65 !< The default signal handler used before signal() setup (usually SIG_DFT)
66type(sigjmp_buf) :: prior_env
67 !< Buffer containing the program state to be recovered by longjmp
68logical :: skip_mpi_dep = .false.
69 !< If true, bypass any calls that require FMS (MPI) to have been initialized.
70 !! Use s/r set_skip_mpi() to change this flag. By default, set_skip_mpi() does not
71 !! need to be called and this flag is false so that FMS (and MPI) should be
72 !! initialized.
73
74contains
75
76!> This provides a convenient interface for writing an informative comment, depending
77!! on the model's current verbosity setting and the verbosity level for this message.
7813subroutine MOM_mesg(message, verb, all_print)
79 character(len=*), intent(in) :: message !< A message to write out
80 integer, optional, intent(in) :: verb !< A level of verbosity for this message
81 logical, optional, intent(in) :: all_print !< If present and true, any PEs are
82 !! able to write this message.
83 ! This provides a convenient interface for writing an informative comment.
84 integer :: verb_msg
85 logical :: write_msg
86
8713 if (skip_mpi_dep) then
880 write_msg = .true.
89 else
9013 write_msg = is_root_pe()
91 endif
9213 if (present(all_print)) write_msg = write_msg .or. all_print
93
9413 verb_msg = 2 ; if (present(verb)) verb_msg = verb
9513 if (write_msg .and. (verbosity >= verb_msg)) call loc_MOM_err(NOTE, message)
96
9713end subroutine MOM_mesg
98
99!> Enable error handling, replacing FATALs in MOM_error with err_handler.
1000subroutine disable_fatal_errors(env)
101 type(sigjmp_buf), intent(in) :: env
102 !> Process recovery state after FATAL errors
103
104 integer :: sig
105
1060 ignore_fatal = .true.
107
108 ! TODO: Only need to call this once; move to an init() function?
1090 if (num_PEs() > 1) then
1100 err_pid = getppid()
111 else
1120 err_pid = getpid()
113 endif
114
115 ! Store the program state
1160 prior_env = env
117
118 ! Setup the signal handler
119 ! NOTE: Passing parameters to signal() in GFortran causes a compiler error.
120 ! We avert this by copying err_signal to a variable.
1210 sig = err_signal
122 ! TODO: Use sigaction() in place of signal()
1230 prior_handler => signal(sig, err_handler)
1240end subroutine disable_fatal_errors
125
126!> Disable the error handler and abort on FATAL
1270subroutine enable_fatal_errors()
128 integer :: sig
129 procedure(handler_interface), pointer :: dummy
130
1310 ignore_fatal = .false.
1320 err_pid = -1 ! NOTE: 0 might be safer, since it's unusable.
133
134 ! Restore the original signal handler (usually SIG_DFT).
1350 sig = err_signal
136 ! NOTE: As above, we copy the err_signal to accommodate GFortran.
1370 dummy => signal(sig, prior_handler)
1380end subroutine enable_fatal_errors
139
140!> Enable/disable skipping MPI dependent behaviors
1410subroutine set_skip_mpi(skip)
142 logical, intent(in) :: skip !< State to assign
143
1440 skip_mpi_dep = skip
145
1460end subroutine set_skip_mpi
147
148!> This provides a convenient interface for writing an error message
149!! with run-time filter based on a verbosity and the severity of the error.
1502subroutine MOM_error(level, message, all_print)
151 integer, intent(in) :: level !< The severity level of this message
152 character(len=*), intent(in) :: message !< A message to write out
153 logical, optional, intent(in) :: all_print !< If present and true, any PEs are
154 !! able to write this message.
155 logical :: write_msg
156 integer :: rc
157
1582 if (skip_mpi_dep) then
1590 write_msg = .true.
160 else
1612 write_msg = is_root_pe()
162 endif
1632 if (present(all_print)) write_msg = write_msg .or. all_print
164
1652 select case (level)
166 case (NOTE)
1670 if (write_msg.and.verbosity>=2) call loc_MOM_err(NOTE, message)
168 case (WARNING)
1692 if (write_msg.and.verbosity>=1) call loc_MOM_err(WARNING, message)
170 case (FATAL)
1710 if (ignore_fatal) then
1720 print *, "(FATAL): " // message
1730 rc = kill(err_pid, err_signal)
174 ! NOTE: MPI launchers require, in their words, "a few seconds" to
175 ! propagate the signal to the nodes, so we wait here to avoid
176 ! anomalous FATAL calls.
177 ! In practice, the signal will take control before sleep() completes.
1780 rc = sleep(3)
179 endif
1800 if (verbosity>=0) call loc_MOM_err(FATAL, message)
181 case default
1822 call loc_MOM_err(level, message)
183 end select
1842end subroutine MOM_error
185
186!> A private routine through which all error/warning/note messages are written
187!! by this module.
1886subroutine loc_MOM_err(level, message)
189 integer, intent(in) :: level !< The severity level of this message
190 character(len=*), intent(in) :: message !< A message to write out
191
1926 if (.not. skip_mpi_dep) then
1936 call MOM_err(level, message)
194 else
195 ! FMS (and therefore MPI) have not been initialized
1960 write(stdout(),'(a)') trim(message) ! Send message to stdout
1970 select case (level)
198 case (WARNING)
1990 write(stderr,'("WARNING ",a)') trim(message) ! Additionally send message to stderr
200 case (FATAL)
2010 write(stderr,'("ERROR: ",a)') trim(message) ! Additionally send message to stderr
202 end select
203 endif
204
2056end subroutine loc_MOM_err
206
207!> This subroutine sets the level of verbosity filtering MOM error messages
2081subroutine MOM_set_verbosity(verb)
209 integer, intent(in) :: verb !< A level of verbosity to set
210 character(len=80) :: msg
2111 if (verb>0 .and. verb<10) then
2121 verbosity=verb
213 else
2140 write(msg(1:80),'("Attempt to set verbosity outside of range (0-9). verb=",I0)') verb
2150 call MOM_error(FATAL,msg)
216 endif
2171end subroutine MOM_set_verbosity
218
219!> This subroutine gets the level of verbosity filtering MOM error messages
2202function MOM_get_verbosity()
221 integer :: MOM_get_verbosity
2222 MOM_get_verbosity = verbosity
2232end function MOM_get_verbosity
224
225!> This tests whether the level of verbosity filtering MOM error messages is
226!! sufficient to write a message of verbosity level verb
2270function MOM_verbose_enough(verb)
228 integer, intent(in) :: verb !< A level of verbosity to test
229 logical :: MOM_verbose_enough
2300 MOM_verbose_enough = (verbosity >= verb)
2310end function MOM_verbose_enough
232
233!> Returns True, if the verbosity>=6 indicating to show the call tree
234162function callTree_showQuery()
235 ! Local variables
236 logical :: callTree_showQuery
237162 callTree_showQuery = (verbosity >= 6)
238162end function callTree_showQuery
239
240!> Writes a message about entering a subroutine if call tree reporting is active
24178subroutine callTree_enter(mesg,n)
242 character(len=*), intent(in) :: mesg !< Message to write
243 integer, optional, intent(in) :: n !< An optional integer to write at end of message
244 ! Local variables
245 character(len=8) :: nAsString
24678 callTreeIndentLevel = callTreeIndentLevel + 1
24778 if (verbosity<6) return
2480 if (is_root_pe()) then
2490 nAsString = ''
2500 if (present(n)) then
2510 write(nAsString(1:8),'(i8)') n
2520 call loc_MOM_err(NOTE, 'callTree: '// &
2530 repeat(' ',callTreeIndentLevel-1)//'loop '//trim(mesg)//trim(nAsString))
254 else
2550 call loc_MOM_err(NOTE, 'callTree: '// &
2560 repeat(' ',callTreeIndentLevel-1)//'---> '//trim(mesg))
257 endif
258 endif
25978end subroutine callTree_enter
260
261!> Writes a message about leaving a subroutine if call tree reporting is active
26278subroutine callTree_leave(mesg)
263 character(len=*) :: mesg !< Message to write
2640 if (callTreeIndentLevel<1) write(0,*) 'callTree_leave: error callTreeIndentLevel=',callTreeIndentLevel,trim(mesg)
26578 callTreeIndentLevel = callTreeIndentLevel - 1
26678 if (verbosity<6) return
2670 if (is_root_pe()) call loc_MOM_err(NOTE, 'callTree: '// &
2680 repeat(' ',callTreeIndentLevel)//'<--- '//trim(mesg))
26978end subroutine callTree_leave
270
271!> Writes a message about reaching a milestone if call tree reporting is active
27220subroutine callTree_waypoint(mesg,n)
273 character(len=*), intent(in) :: mesg !< Message to write
274 integer, optional, intent(in) :: n !< An optional integer to write at end of message
275 ! Local variables
276 character(len=8) :: nAsString
2770 if (callTreeIndentLevel<0) write(0,*) 'callTree_waypoint: error callTreeIndentLevel=',callTreeIndentLevel,trim(mesg)
27820 if (verbosity<6) return
2791 if (is_root_pe()) then
2801 nAsString = ''
2811 if (present(n)) then
2820 write(nAsString(1:8),'(i8)') n
2830 call loc_MOM_err(NOTE, 'callTree: '// &
2840 repeat(' ',callTreeIndentLevel)//'loop '//trim(mesg)//trim(nAsString))
285 else
2860 call loc_MOM_err(NOTE, 'callTree: '// &
2871 repeat(' ',callTreeIndentLevel)//'o '//trim(mesg))
288 endif
289 endif
29020end subroutine callTree_waypoint
291
292!> Issues a FATAL error if the assertion fails, i.e. the first argument is false.
293629subroutine assert(logical_arg, msg)
294 logical, intent(in) :: logical_arg !< If false causes a FATAL error
295 character(len=*), intent(in) :: msg !< Message to issue in case of failed assertion
296
297629 if (.not. logical_arg) then
2980 call MOM_error(FATAL, msg)
299 endif
300629end subroutine assert
301
302!> Restore the process state via longjmp after receiving a signal.
3030subroutine err_handler(sig)
304 integer, intent(in) :: sig
305 !< Signal passed to the handler (unused)
3060 call siglongjmp(prior_env, 1)
3070end subroutine
308
309end module MOM_error_handler