File Coverage

blib/lib/Fred/Fish/DBUG/Signal.pm
Criterion Covered Total %
statement 299 349 85.6
branch 108 164 65.8
condition 52 89 58.4
subroutine 43 44 97.7
pod 3 15 20.0
total 505 661 76.4


line stmt bran cond sub pod time code
1             ###
2             ### Copyright (c) 2007 - 2025 Curtis Leach. All rights reserved.
3             ###
4             ### Signal processinng enhancememnt to the Fred Fish Dbug module.
5             ###
6             ### Module: Fred::Fish::DBUG::Signal
7             ###
8             ### Note: All methods starting with 'on_' are calls to stubs that call
9             ### unexported functions defined in Fred::Fish::DBUG::ON ...
10             ###
11             ### Note: All methods starting with '_' are unexported local functions.
12              
13             =head1 NAME
14              
15             Fred::Fish::DBUG::Signal - Fred Fish library extension to trap Signals.
16              
17             =head1 SYNOPSIS
18              
19             use Fred::Fish::DBUG::Signal;
20             or
21             require Fred::Fish::DBUG::Signal;
22              
23             =head1 DESCRIPTION
24              
25             F is a pure Perl extension to the F
26             module. Using this module allows you to trap the requested signal and write
27             the event to your fish logs. Kept separate since not all OS support Signal
28             handling. Also the list of Signals supported varry by OS.
29              
30             You are not required to use this module when trapping signals, but it's useful
31             for logging in B that a signal was trapped and where in the code the
32             signal was trigereed when seeing how a caught signal affects your code.
33              
34             =head1 FUNCTIONS
35              
36             =over 4
37              
38             =cut
39              
40             package Fred::Fish::DBUG::Signal;
41              
42              
43 39     39   134744 use strict;
  39         86  
  39         1471  
44 39     39   456 use warnings;
  39         72  
  39         2553  
45              
46 39     39   247 use vars qw( @ISA @EXPORT @EXPORT_OK $VERSION );
  39         87  
  39         3164  
47 39     39   248 use Exporter;
  39         88  
  39         2333  
48              
49 39     39   933 use Fred::Fish::DBUG::ON 2.10;
  39         1080  
  39         7488  
50              
51 39     39   18566 use Perl::AtEndOfScope;
  39         15652  
  39         1688  
52 39     39   253 use Config qw( %Config );
  39         81  
  39         1728  
53              
54 39     39   198 use Perl::OSType ':all';
  39         67  
  39         5041  
55 39     39   256 use FileHandle;
  39         77  
  39         291  
56 39     39   12801 use File::Basename;
  39         93  
  39         3010  
57 39     39   309 use Cwd 'abs_path';
  39         92  
  39         2170  
58 39     39   265 use Sub::Identify 'sub_fullname';
  39         68  
  39         4631  
59              
60             $VERSION = "2.10";
61             @ISA = qw( Exporter );
62              
63             @EXPORT = qw( DBUG_TRAP_SIGNAL DBUG_FIND_CURRENT_TRAPS DBUG_DIE_CONTEXT
64              
65             DBUG_SIG_ACTION_EXIT13 DBUG_SIG_ACTION_EXIT_SIGNUM
66             DBUG_SIG_ACTION_LOG DBUG_SIG_ACTION_DIE
67             DBUG_SIG_ACTION_REMOVE
68             );
69              
70             @EXPORT_OK = qw( );
71              
72             # Constants to use to tell what to do with the trapped signals ... (never use 0)
73 39     39   238 use constant DBUG_SIG_ACTION_EXIT13 => 1;
  39         89  
  39         3700  
74 39     39   230 use constant DBUG_SIG_ACTION_EXIT_SIGNUM => 2;
  39         123  
  39         2288  
75 39     39   219 use constant DBUG_SIG_ACTION_LOG => 3;
  39         82  
  39         2064  
76 39     39   207 use constant DBUG_SIG_ACTION_DIE => 4;
  39         92  
  39         2094  
77 39     39   258 use constant DBUG_SIG_ACTION_REMOVE => 55;
  39         93  
  39         2069  
78 39     39   238 use constant DBUG_SIG_ACTION_UNKNOWN => 99; # Not exposed!
  39         76  
  39         4507  
79              
80              
81             # These hash variables holds all the global variables used by this module.
82             my %dbug_signal_vars; # The signal vars can cross fish frames.
83              
84             # --------------------------------
85             # This BEGIN block handles the initialization of the signal trapping logic!
86             # --------------------------------
87             BEGIN
88             {
89             # All fish frames will share the same signal info.
90 39     39   138 my (%details, %defaults);
91 39         102 $dbug_signal_vars{recursion} = 0;
92 39         95 $dbug_signal_vars{forward_signals} = \%details;
93 39         84 $dbug_signal_vars{original_signal_action} = \%defaults;
94 39         169658 return;
95             }
96              
97             # --------------------------------
98             # END is automatically called when this module goes out of scope!
99             # --------------------------------
100             END
101             {
102 39     39   2144 DBUG_ENTER_FUNC (@_);
103              
104             # Clear any signals trapped by this module ...
105 39         228 my $pkg = __PACKAGE__ . "::";
106 39         194 my $clr_sig_flg = 0;
107 39         3038 foreach ( sort keys %SIG ) {
108 2689 100 100     13907 if ( defined $SIG{$_} && $SIG{$_} =~ m/^${pkg}/ ) {
109             on_dbug_print_pkg_tag ( DBUG_FILTER_LEVEL_INFO,
110 77         539 "Clearing Signal (%s) [%s]", $_, $SIG{$_} );
111              
112             # Reset to what the signal was originally ....
113 77         513 $SIG{$_} = $dbug_signal_vars{original_signal_action}->{$_};
114 77         389 $clr_sig_flg = 1;
115             }
116             }
117              
118 39 50       460 on_printing ("\n") if ( $clr_sig_flg );
119              
120 39         201 DBUG_VOID_RETURN ();
121             }
122              
123              
124             # ------------------------------------------------------------------------------
125             # Set up to call non-exposed methods in Fred::Fish::DBUG:ON.
126             # ------------------------------------------------------------------------------
127 775     775 0 2102 sub on_printing { return ( Fred::Fish::DBUG::ON::_printing (@_) ); }
128 736     736 0 1910 sub on_indent { return ( Fred::Fish::DBUG::ON::_indent (@_) ); }
129 81     81 0 404 sub on_get_func_info { return ( Fred::Fish::DBUG::ON::_get_func_info (@_) ); }
130 0     0 0 0 sub on_eval_depth { return ( Fred::Fish::DBUG::ON::_eval_depth (@_) ); }
131 176     176 0 589 sub on_dbug_called_by { return ( Fred::Fish::DBUG::ON::_dbug_called_by (@_) ); }
132 633     633 0 2103 sub on_dbug_hack { return ( Fred::Fish::DBUG::ON::_dbug_hack (@_) ); }
133 481     481 0 1279 sub on_get_global_var { return ( Fred::Fish::DBUG::ON::_get_global_var (@_) ); }
134 292     292 0 3918 sub on_set_global_var { return ( Fred::Fish::DBUG::ON::_set_global_var (@_) ); }
135 378     378 0 1443 sub on_dbug_print_pkg_tag { return ( Fred::Fish::DBUG::ON::_dbug_print_pkg_tag (@_) ); }
136 98     98 0 345 sub on_get_filter_color { return ( Fred::Fish::DBUG::ON::_get_filter_color (@_) ); }
137 98     98 0 318 sub on_filter_on { return ( Fred::Fish::DBUG::ON::_filter_on (@_) ); }
138 114     114 0 458 sub on_dbug_level { return ( Fred::Fish::DBUG::ON::dbug_level (@_) ); }
139              
140             # ==============================================================================
141             # Start of Signal Handling Extenstion to this module ...
142             # ==============================================================================
143              
144             # Only for use by Fred::Fish::DBUG::SignalKiller to flag that we've replaced
145             # Perl's core 'die' with a custom version.
146             # Must be called from Fred::Fish::DBUG::SignalKiller::_custom_fish_die(),
147             # not from it's BEGIN block so can detect if you overrode the override!
148             sub _dbug_enable_signal_suicide
149             {
150 3     3   9 $dbug_signal_vars{LOG_NOW_WORKS_IN_DIE} = 1;
151 3         8 return;
152             }
153              
154              
155             # ==================================================================
156             # Do we need to disable fish tracing in the END blocks?
157             # When calling an untrapped die, we won't be calling DBUG_LEAVE to
158             # handle this for us!
159             # Triggered by action: DBUG_SIG_ACTION_DIE
160             # ------------------------------------------------------------------
161             sub _dbug_turn_off_end_while_dying
162             {
163 169     169   320 my $special_flag = shift; # Called by a trapped die(2)/warn(1) signal?
164             # 0 - for all other signals.
165              
166             # It's a no-op unless fish is on & we requested suppressing fish in END.
167 169 50 66     512 return unless ( on_get_global_var('on') && on_get_global_var('no_end') );
168              
169             # Don't disable fish if we're just going to call DBUG's custom die next ...
170 0 0 0     0 if ( $dbug_signal_vars{die_trapped} && $special_flag != 2 ) {
171 0   0     0 my $s = $SIG{__DIE__} || "";
172 0         0 my $ref = $dbug_signal_vars{forward_signals}->{__DIE__};
173 0 0       0 return if ( $s eq $ref->{SAVE_SIG} );
174             }
175              
176             # --- Now lets disable fish in the END blocks ...
177              
178             # There is always at least one "eval" around most called signals ...
179 0         0 my $eval_cnt = on_eval_depth ();
180              
181             # DBUG_PRINT ("TURN END OFF", "Count: %d (%d) [Flag: %d]", $eval_cnt, $^S, $special_flag);
182              
183             # Should we shut fish down immediately?
184             # Checking if we are processing an untrapped die request!
185 0 0       0 if ( $special_flag ) {
186 0 0       0 on_set_global_var('on', 0) if ( $eval_cnt <= 0 ); # Die/Warn ...
187             } else {
188 0 0       0 on_set_global_var('on', 0) if ( $eval_cnt <= 1 ); # Other ...
189             }
190              
191 0         0 return;
192             }
193              
194              
195             # --------------------------------
196             # See DBUG_TRAP_SIGNAL() for more info on what the args mean ...
197             # Returns:
198             # -2 : No such context. Request ignored.
199             # -1 : No such action. Request ignored.
200             # 0 : No such signal. Request ignored.
201             # 1 : Signal is now trapped! Forwarding info in %dbug_signal_vars.
202             # --------------------------------
203             # %action_to_take hash has 5 keys:
204             # 1) ACTION - The type of action to take.
205             # 2) EXIT - The exit status for the program to use.
206             # 3) FUNC - undef or an array of funcs to call as code ref.
207             # 4) NAME - undef or an array of fully qualified string of func names.
208             # 5) CONTEXT - Will be 0 unless the FUNC array contain 1 or more entries.
209             # Then it tells what to do if these FUNCs call die! (1 or 2)
210             # The REMOVE action is handled elsewhere!
211             # --------------------------------
212             sub _dbug_log_signal
213             {
214 145     145   296 my $sig = shift; # The signal to trap ...
215 145   50     404 my $action = shift || DBUG_SIG_ACTION_UNKNOWN;
216 145         256 my $context = shift; # 1 or 2 ... what to do if die is called via @flst!
217 145         316 my @flst = @_; # The list of funcs to forward to.
218              
219 145 50       359 return (0) unless ( $sig );
220              
221 145         275 $sig = uc ($sig);
222              
223             # These signals are special cases not in %SIG by default!
224             # They are also not in $Config{sig_name} or $Config{sig_num} arrays!
225 145   100     615 my $special_signals = ( $sig eq "__DIE__" || $sig eq "__WARN__" );
226              
227             # Disallow signals not in %SIG! (or one of the special cases)
228 145 50 66     428 return (0) unless ( $special_signals || exists $SIG{$sig} );
229              
230             # Save whatever action the signal currently does.
231 145         468 my $saved_signal = $SIG{$sig};
232              
233             # ----------------------------------------------------------
234             # Calculate the signal number to use as the exit status ...
235             # ----------------------------------------------------------
236 145         295 my $exit_sts = 13;
237 145 50 66     2069 if ( $special_signals && $action == DBUG_SIG_ACTION_EXIT_SIGNUM ) {
    50 66        
    50 66        
238             # It's not in $Config{sig_name} or $Config{sig_num} arrays!
239             # So use some really big numbers for the exit status!
240             # So the numbers are unique for all OS. (255 is biggest allowed.)
241 0 0       0 $exit_sts = ($sig eq "__DIE__") ? 240 : 241;
242              
243             } elsif ( $action == DBUG_SIG_ACTION_EXIT_SIGNUM ) {
244 0         0 $exit_sts = -1;
245 0         0 my @numbers = split (" ", $Config{sig_num});
246 0         0 my @names = split (" ", $Config{sig_name});
247 0         0 foreach (0..$#names) {
248 0 0       0 if ( $names[$_] eq $sig ) {
249 0         0 $exit_sts = $numbers[$_];
250 0         0 last;
251             }
252             }
253              
254 0 0       0 return (0) if ( $exit_sts == -1 ); # No such signal, shouldn't happen.
255              
256             } elsif ( $action == DBUG_SIG_ACTION_EXIT13 ||
257             $action == DBUG_SIG_ACTION_LOG ||
258             $action == DBUG_SIG_ACTION_DIE ) {
259             ;
260              
261             } else {
262 0         0 return (-1); # Unknown action ... Trap request ignored!
263             }
264              
265             # ----------------------------------------------------------
266             # Now set up the requested action to take ...
267             # ----------------------------------------------------------
268 145         259 my %action_to_take;
269 145         464 $action_to_take{ACTION} = $action;
270 145         318 $action_to_take{CONTEXT} = 0; # Assume no funcs to forward to ...
271              
272 145 50 33     567 if ( $action == DBUG_SIG_ACTION_EXIT13 ||
273             $action == DBUG_SIG_ACTION_EXIT_SIGNUM ) {
274 0         0 $action_to_take{EXIT} = $exit_sts; # Abort program with this status.
275             } else {
276 145         317 $action_to_take{EXIT} = 0;
277             }
278              
279 145         256 my (@codes, @names);
280 145         292 my $drop = 0;
281              
282             # ---------------------------------------------------------------------------
283             # Will we be forwarding any trapped signal(s) ???
284             # All strings are converted to code references for reliable execution logic.
285             # Anything that can't be converted to a code reference will be tossed!
286             # ---------------------------------------------------------------------------
287 145         368 foreach my $func ( @flst ) {
288 147         258 my ( $code, $name );
289              
290 147 50       811 unless ( $func ) {
    100          
291 66         165 next; # It's a no-op ...
292              
293 0 50       0 } elsif ( $func eq "IGNORE" || $func eq "DEFAULT" ) {
294 0         0 next; # It's another no-op ...
295              
296             } else {
297 81         417 ( $code, $name ) = on_get_func_info ( $func, "forward ${sig} to" );
298             }
299              
300             # We found the code reference to use ...
301 81 50 33     578 if ( $code && $name ) {
302 81         356 push (@codes, $code);
303 81         297 push (@names, $name);
304              
305             } else {
306             # warn ("Unknown function [$func]. Tossing it as a custom forwarding function!\n");
307 0         0 ++$drop;
308             }
309             } # end foreach $func loop ...
310              
311             # Save the forwarding information ...
312 145 100       784 if ( $#codes != -1 ) {
    50          
    50          
313 67         245 $action_to_take{FUNC} = \@codes;
314 67         222 $action_to_take{NAME} = \@names;
315 67         159 $action_to_take{CONTEXT} = $context;
316 67 50 33     654 return (-2) unless ( 1 <= $context && $context <= 3 );
317             } elsif ( $drop == 1 ) {
318 0         0 warn ( "The passed function name was not defined properly.\n",
319             "So nothing will be forwarded.\n" );
320             } elsif ( $drop > 1 ) {
321 0         0 warn ( "None of the passed function names were defined properly.\n",
322             "So nothing will be forwarded.\n" );
323             }
324              
325             # -----------------------------------------------------------
326             # Save the signal results ... No errors allowed after this!
327             # -----------------------------------------------------------
328 145         613 $dbug_signal_vars{forward_signals}->{$sig} = \%action_to_take;
329 145 100       538 unless ( exists $dbug_signal_vars{original_signal_action}->{$sig} ) {
330 79         610 $dbug_signal_vars{original_signal_action}->{$sig} = $saved_signal;
331             }
332              
333             # -----------------------------------------------------------
334             # Now update the signal hash itself ... We're ready to go!
335             # -----------------------------------------------------------
336 145 100 33     1049 unless ( $special_signals ) {
    100          
337 13         204 $SIG{$sig} = __PACKAGE__ . "::_dbug_normal_signals";
338              
339 0         0 } elsif ( $sig eq "__DIE__" ) {
340             # return (-1) if ( $action == DBUG_SIG_ACTION_LOG ); # Since doesn't work as expected!
341 49         576 $SIG{$sig} = __PACKAGE__ . "::_dbug_trap_die_call";
342              
343             } elsif ( $sig eq "__WARN__" ) {
344             $SIG{$sig} = __PACKAGE__ . "::_dbug_trap_warn_call";
345             }
346              
347 145         570 return (1); # Signal trapped ...
348             }
349              
350              
351             =item DBUG_TRAP_SIGNAL ( $signal, $action [, @forward_to] )
352              
353             This function instructs I to trap the requested signal and write the
354             event to B. If the signal name isn't in the %SIG hash, or the I<$action>
355             is invalid, then the request will be ignored! Just be warned that not all
356             signals are trappable, and the list of signals may vary per OS. Also note that
357             if the untrapped signal causes your program to terminate, any clean up done in
358             any B blocks will also be ignored.
359              
360             This signal is trapped even when B is turned off. So the behavior of
361             your program doesn't change when B is turned on and off. Except that
362             nothing is writen to B when B is turned off.
363              
364             If called multiple times for the same signal, only the info for the last time
365             called is active. It returns B<1> if the signal is now trapped or removed.
366             It returns B<0> if it had issues that prevented it from taking the requested
367             action against the signal.
368              
369             Never call this function in a BEGIN block of code. If you do so and hit
370             compile time issues, it can make your life very difficult trying to debug
371             things.
372              
373             The I<$action> flag tells what this module is to do with this signal once it
374             is trapped and logged to B. See below for this list of valid action
375             constants! If the action is a negative value, it will call abs() before
376             it's validated.
377              
378             =over 4
379              
380             B - Remove this I<$signal> from the list of signals
381             trapped. Then restore $SIG{$signal} to it's original setting. It does
382             nothing if the signal wasn't trapped by this module.
383              
384             B - Exit your program with the status of B<13>.
385             (Recommended for most signals.)
386              
387             B - Exit your program with the signal number
388             associated with the trapped signal as your exit status. For signals DIE
389             and WARN, they will exit with B<240>/B<241> since they have no signal numbers!
390              
391             B - Call B, it's trappable by I or
392             I. (Recommended for signal DIE, using anything else for DIE could
393             break a lot of code.)
394              
395             B - Just log to B and return control to your code.
396             (Recommended for signal WARN) Doesn't work for signal DIE, so can't use this
397             action to try to avoid the need for I or I blocks in your code.
398              
399             =back
400              
401             Just be aware that trapping certain signals and returning control back to your
402             program can sometimes cause strange behavior.
403              
404             If action B was used for your B signal, it will
405             also call B's list of functions if B was also trapped by this module.
406             But if you trap B outside of this module this may trigger an unexpected
407             duplicate call to your custom B routine. So in this case it's best to
408             leave B untrapped or trapped via this module as well.
409              
410             If you provided I<@forward_to> it will assume you wish to call those function(s)
411             in the order specified after the signal has been logged to fish, but before the
412             specified I<$action> is taken. This array may contain zero or more entries in
413             it! Each entry in this array may be a reference to a function or a fully
414             qualified function name as a string.
415              
416             When called, these functions will be passed one argument. For most signals its
417             the name of the trapped signal. But if called by trapping B or B,
418             it is the message printed by die or warn. Just be aware that calls to die and
419             warn with multiple arguments have them joined together before the signal is
420             generated. Any return value will be ignored.
421              
422             But what happens if you call B in one of these I functions? In
423             that case the die request is ignored. It's treated as an instruction to the
424             signal handler that it is to stop calling additional I function(s)
425             in this list. It will not override the action selected. If you really want
426             to terminate your program in one of these functions, write your message to
427             STDERR and call B or I instead!
428              
429             NOTE: If you really, really want B to work for B,
430             see module L and immediately forget you asked
431             about this.
432              
433             =cut
434              
435             # ==============================================================
436             sub DBUG_TRAP_SIGNAL
437             {
438 149   50 149 1 599182 my $sig = uc (shift || ""); # The signal name, not the number!
439 149   100     533 my $action = abs (shift || DBUG_SIG_ACTION_UNKNOWN);
440 149         485 my @funcs = @_;
441              
442             # Named after the function ...
443 149         564 my $clr = "::DBUG_TRAP_SIGNAL";
444              
445             # Reset if a non-numeric action was given ... (to avoids warnings!)
446 149 50       950 $action = DBUG_SIG_ACTION_UNKNOWN unless ( $action =~ m/^\d+$/ );
447              
448 149         297 my $status = 0; # Assume failure ...
449              
450 149 100 100     817 if ( $sig eq "DIE" || $sig eq "WARN" ) {
451 6         23 $sig = "__${sig}__"; # Convert to __DIE__ or __WARN__.
452             }
453              
454             # Did we ask to remove any previousy trapped signal ???
455 149 100       397 if ( $action == DBUG_SIG_ACTION_REMOVE ) {
456 2 50       14 if ( exists $dbug_signal_vars{forward_signals}->{$sig} ) {
457 2         22 $SIG{$sig} = $dbug_signal_vars{original_signal_action}->{$sig};
458 2         12 delete $dbug_signal_vars{forward_signals}->{$sig};
459 2         6 delete $dbug_signal_vars{original_signal_action}->{$sig};
460 2 50       9 delete $dbug_signal_vars{die_trapped} if ( $sig eq "__DIE__" );
461 2         4 $status = 1; # Successfully removed the trapped signal!
462             } else {
463 0         0 on_dbug_print_pkg_tag ( DBUG_FILTER_LEVEL_WARN, $clr,
464             "The signal [%s] wasn't already trapped! %s",
465             $sig, on_dbug_called_by (0) );
466             }
467 2         10 return ( $status );
468             }
469              
470             # TO DO: Figure out a way to set this die action flag dynamically to 1 or 2!
471 147         256 my $context = 2; # Should be: 1 or 2 ... See @die_action_msg for meaning!
472              
473 147         252 my $res = -1; # An invalid action requested ...
474 147 100 33     1083 if ( $action == DBUG_SIG_ACTION_EXIT13 ||
      66        
      100        
475             $action == DBUG_SIG_ACTION_EXIT_SIGNUM ||
476             $action == DBUG_SIG_ACTION_LOG ||
477             $action == DBUG_SIG_ACTION_DIE ) {
478 145         482 $res = _dbug_log_signal ($sig, $action, $context, @funcs);
479             }
480              
481 147 50       751 if ( $res == -2 ) {
    100          
    50          
482 0         0 on_dbug_print_pkg_tag ( DBUG_FILTER_LEVEL_WARN, $clr,
483             "No such context [%s] for signal [%s] %s",
484             $action, $sig, on_dbug_called_by (0) );
485             } elsif ( $res == -1 ) {
486 2         10 on_dbug_print_pkg_tag ( DBUG_FILTER_LEVEL_WARN, $clr,
487             "No such action [%s] for signal [%s] %s",
488             $action, $sig, on_dbug_called_by (0) );
489             } elsif ( $res == 0 ) {
490 0         0 on_dbug_print_pkg_tag ( DBUG_FILTER_LEVEL_WARN, $clr,
491             "No such signal [%s] %s",
492             $sig, on_dbug_called_by (0) );
493             } else {
494 145         262 $status = 1; # Success!
495 145         352 my $ref = $dbug_signal_vars{forward_signals}->{$sig};
496 145         521 $ref->{SAVE_SIG} = $SIG{$sig};
497 145 100       1068 $dbug_signal_vars{die_trapped} = 1 if ( $sig eq "__DIE__" );
498             }
499              
500 147         39564 return ( $status );
501             }
502              
503              
504             =item DBUG_FIND_CURRENT_TRAPS ( $signal )
505              
506             This function tells if the requested I<$signal> is currently being trapped by
507             this module via I or not.
508              
509             It can return one or more values: ($action_taken [, $func1 [, $func2 [, ...]]])
510              
511             If the signal isn't being trapped by this module it returns (undef).
512              
513             If the signal is being trapped, but not forwarded it returns ($action_taken).
514             As an FYI, no valid action has a value of zero.
515              
516             Otherwise it returns the I<$action_taken> and the list of functions it is
517             scheduled to call if the signal gets trapped.
518              
519             But if called in scalar mode it just returns I<$action_taken> and tosses the
520             list of functions.
521              
522             Each function will be returned as a B reference. Even if it was
523             originally passed to I as a string containing the function
524             to call.
525              
526             If someone reset $SIG{$signal} manually, this function will detect that and
527             return (-1 * $action) for the action. To make it easy to detect this problem.
528              
529             =cut
530              
531             # ==============================================================
532             sub DBUG_FIND_CURRENT_TRAPS
533             {
534 92   50 92 1 7349 my $sig = uc (shift || ""); # The signal name, not the number!
535              
536 92 50 33     617 if ( $sig eq "DIE" || $sig eq "WARN" ) {
537 0         0 $sig = "__${sig}__"; # Convert to __DIE__ or __WARN__.
538             }
539              
540 92         270 my $ref = $dbug_signal_vars{forward_signals}->{$sig};
541              
542             # If the signal wansn't trapped by this module ...
543 92 100       421 return (0) unless (defined $ref);
544              
545             # Both NAME & FUNC are always populated!
546 24         79 my $act = $ref->{ACTION};
547 24         179 my $func = $ref->{FUNC}; # As an array of code referece ...
548             # my $name = $ref->{NAME}; # As an array of strings ...
549              
550             # Did someone redirect where $SIG{$sig} pointed to???
551 24   50     172 my $s = $SIG{$sig} || "";
552 24 100       97 $act = -$act if ( $s ne $ref->{SAVE_SIG} );
553              
554 24 100       92 return ($act) unless (defined $func);
555              
556 15 50       50 return (wantarray ? ($act, @{$func}) : $act);
  15         98  
557             }
558              
559              
560             =item DBUG_DIE_CONTEXT ( )
561              
562             This is a helper method to I. For use when your custom die
563             routine(s) are called by the signal handler on a die request. You call this
564             method for the context of the die request being handled.
565              
566             It returns ( $fish_managed, $eval_trap, $original_signal, $rethrown, $die_action ).
567              
568             If called in scalar mode it returns ( $fish_managed ).
569              
570             =over 4
571              
572             I<$fish_managed> - A boolean flag that tells if F managed
573             the call to your custom die function via a trapped B<__DIE__> signal.
574              
575             I<$eval_trap> - Boolean value telling if this call to B is going to be
576             trapped by an B block of code and/or a B block of code.
577             Otherwise it is false if it will be terminating your program when you return
578             to the signal handler. It has no way of telling what the caller will do once
579             it catches the result.
580              
581             I<$original_signal> - Tells which signal triggered the call to die. If a
582             non-die signal was trapped using action DBUG_SIG_ACTION_DIE, it returns the
583             name of the trapped signal. It returns B<__DIE__> if triggered directly.
584             Otherwise it returns the empty string if the call wasn't managed by B.
585              
586             I<$rethrown> - A boolean flag that attempts to detect if your code caught a
587             previous call to B in an B or B block and then called B
588             again with the same mesage. It's set to B<1> if the previous B
589             was the same as the current B. Else it's set to B<0>. So if
590             you change the messsage before rethrowing the error again, this flag can't
591             help you. Many times you'd like to do nothing on a rethrown die signal.
592              
593             I<$die_action> - A code that tells what the die handler will do when it catches
594             any die thrown by your custom function. B<1> - Die is ignored. B<2> - Tells
595             the DIE handler not to call any more custom DIE function(s). B<0> - The context
596             function doesn't know the answer.
597              
598             =back
599              
600             =cut
601              
602             # ==============================================================
603             # $in_eval flag doesn't count the eval block in _dbug_forward_trapped_signal().
604             # Since that eval block controls what happens if your custom routine
605             # calls die itself!
606              
607             sub DBUG_DIE_CONTEXT
608             {
609             # Only set in _dbug_trap_die_call() before calling the custom functions.
610 149 100   149 1 1364 my $managed = $dbug_signal_vars{die_context_managed} ? 1 : 0;
611 149         364 my $in_eval = $dbug_signal_vars{die_context_eval};
612 149   100     728 my $sig = $dbug_signal_vars{chained_die} || ($managed ? "__DIE__" : "");
613 149   100     681 my $rethrown = $dbug_signal_vars{same_die_message} || 0;
614 149         356 my $die_action = $dbug_signal_vars{die_context_die};
615              
616             # Perl's special variable for Current State of the Interpreter.
617             # $^S = (undef - parsing, 1 - in an eval block, 0 - otherwise)
618             # Only used if $managed is "0"! So OK if detecting the call
619             # to this function was inside an eval/try block of code!
620 149 100       533 $in_eval = $^S unless ( defined $in_eval );
621              
622             # Rule to use if not managing the die request ...
623 149 100       363 $die_action = 0 unless ( $managed );
624              
625 149 50       1006 return ( wantarray ? ( $managed, $in_eval, $sig, $rethrown, $die_action ) : $managed );
626             }
627              
628              
629             # ====================================================================
630             # Undocumented signal handling functions ...
631             # Never called directly by anyone's code ...
632             # --------------------------------------------------------------------
633             # "$^S" - Perl's special var for Current State of the Interpreter.
634             # $^S = (undef - parsing, 1 - in an eval, 0 - otherwise)
635             # We may have to test this var in the future for DIE & WARN traps
636             # since both signals can be triggered during the parsing phase
637             # where maybe these 2 traps shouldn't be doing anything?
638             # Need to experiment with it a bit before ever using this var.
639             # So trapping these signals in a BEGIN block might be problematic
640             # at times.
641             # ====================================================================
642              
643             #---------------------------------------------------------------------
644             # Generic TRAP function for DBUG (can't use for __DIE__ or __WARN__!)
645             # Perl calls it like ==>
646             # Fred::Fish::DBUG::Signal::_dbug_normal_signals ( $signal );
647             # Do not call it directly from your code!
648             #---------------------------------------------------------------------
649             # On UNIX, the trapped signal call is always wrapped by an "eval" block!
650             # The same is true on Windows when not called in hack mode for self tests!
651             # This affects how _dbug_trap_die_call() will handle things.
652             #---------------------------------------------------------------------
653             sub _dbug_normal_signals
654             {
655 26     26   1652 my $sig = shift; # The Signal Name in upper case!
656 26         420 local $SIG{$sig} = "IGNORE"; # Prevents recursive signals!
657              
658 26         131 my $signal_msg = "Signal \"${sig}\" has been trapped and logged!" . on_dbug_called_by (0, 0);
659              
660 26         105 my $sig_info = $dbug_signal_vars{forward_signals}->{$sig};
661              
662 26 50       115 unless (defined $sig_info) {
663 0         0 local $SIG{__WARN__} = "IGNORE";
664 0         0 warn ($signal_msg . "\n",
665             "But an improper hack of the DBUG module's Signal Handling has been detected.\n",
666             "Aborting your program with an exit status of 13!\n");
667 0         0 DBUG_LEAVE (13);
668             }
669              
670 26 100       93 if ( DBUG_ACTIVE () ) {
671 18         101 _dbug_stack_trace (1, $signal_msg); # Writes stack to INFO level
672             } else {
673 8         40 on_dbug_hack (pause => 0, \&on_dbug_print_pkg_tag, DBUG_FILTER_LEVEL_INFO, "::${sig}", $signal_msg);
674             }
675              
676 26 50       146 if ( $sig_info->{CONTEXT} ) {
677 26         117 local $dbug_signal_vars{chained_die} = ${sig};
678 26         66 my $func = $sig_info->{FUNC};
679 26         74 _dbug_forward_trapped_signal ( $sig, "", $sig_info->{CONTEXT}, @{$func} );
  26         207  
680             }
681              
682             # What to do after processing the signal ...
683 26 50 33     319 if ( $sig_info->{ACTION} == DBUG_SIG_ACTION_EXIT13 ||
    100          
    50          
684             $sig_info->{ACTION} == DBUG_SIG_ACTION_EXIT_SIGNUM ) {
685 0         0 DBUG_LEAVE ( $sig_info->{EXIT} );
686              
687             } elsif ( $sig_info->{ACTION} == DBUG_SIG_ACTION_LOG ) {
688 2         37 return;
689              
690             } elsif ( $sig_info->{ACTION} == DBUG_SIG_ACTION_DIE ) {
691 24         96 local $dbug_signal_vars{chained_die} = ${sig};
692 24         95 _dbug_turn_off_end_while_dying (0);
693 24         626 die ("Calling die due to caught Signal [$sig]\n");
694 0         0 return;
695             }
696              
697             # Should never get here ...
698             on_dbug_print_pkg_tag ( DBUG_FILTER_LEVEL_WARN,
699             "Unexpected Signal Action Specified for signal %s! (%d)\n%s",
700 0         0 $sig, $sig_info->{ACTION}, " Just logging it!");
701              
702 0         0 return;
703             }
704              
705              
706              
707             # --------------------------------------------------------------
708             # Can only be called when $SIG{__WARN__} was trapped & triggered!
709             # Doesn't do a stack trace.
710             # --------------------------------------------------------------
711              
712             sub _dbug_trap_warn_call
713             {
714 15     15   369 my $msg = shift;
715              
716 15         104 local $SIG{__WARN__} = "IGNORE";
717              
718 15         47 my $sig_info = $dbug_signal_vars{forward_signals}->{__WARN__};
719              
720 15         30 my $level = DBUG_FILTER_LEVEL_WARN;
721              
722             # Write the warning to the screen?
723 15 100       68 if ( $sig_info->{ACTION} != DBUG_SIG_ACTION_DIE ) {
724             # unless ( DBUG_EXECUTE ( $level ) == -1 ) {
725 13         659 print STDERR $msg;
726             # }
727             }
728              
729             # Check if the warning message already tells where it came from!
730             # If not, we'll only add it to the end of the messge written to fish!
731 15         167 my ($line, $filename) = (caller(0))[2,1];
732 15         78 my $extra = "at ${filename} line ${line}.";
733 15 100 66     682 if ( $msg =~ m/ at (.+) line ${line}[.]/ && $1 eq $filename) {
734 6         19 $extra = ""; # No need to repeat this info!
735             }
736              
737 15         132 on_dbug_hack (pause => 0, \&on_dbug_print_pkg_tag, $level, "::__WARN__", "%s%s", $msg, $extra);
738              
739 15 50       73 if ( $sig_info->{CONTEXT} ) {
740 15         65 local $dbug_signal_vars{chained_die} = "__WARN__";
741 15         56 my $funcs = $sig_info->{FUNC};
742 15         62 _dbug_forward_trapped_signal ( "__WARN__", $msg, $sig_info->{CONTEXT}, @{$funcs} );
  15         67  
743             }
744              
745             # What to do after processing the signal ...
746 15 50 33     236 if ( $sig_info->{ACTION} == DBUG_SIG_ACTION_EXIT13 ||
    100          
    50          
747             $sig_info->{ACTION} == DBUG_SIG_ACTION_EXIT_SIGNUM ) {
748 0         0 DBUG_LEAVE ( $sig_info->{EXIT} );
749              
750             } elsif ( $sig_info->{ACTION} == DBUG_SIG_ACTION_LOG ) {
751 13         134 return;
752              
753             } elsif ( $sig_info->{ACTION} == DBUG_SIG_ACTION_DIE ) {
754 2         9 local $dbug_signal_vars{chained_die} = "__WARN__";
755 2         8 my $stack = "Called \"warn\" for a stack trace ... " . on_dbug_called_by(1);
756 2         11 _dbug_stack_trace (1, $stack);
757 2         10 _dbug_turn_off_end_while_dying (1);
758 2         34 die ( $msg );
759 0         0 return;
760             }
761              
762             # Should never get here ...
763             on_dbug_print_pkg_tag ( DBUG_FILTER_LEVEL_WARN,
764             "Unexpected Signal Action Specified for signal %s! (%d)\n%s",
765 0         0 "__WARN__", $sig_info->{ACTION}, " Just logging it!");
766              
767 0         0 return;
768             }
769              
770              
771             # --------------------------------------------------------------
772             # Can only be called when $SIG{__DIE__} was trapped & triggered!
773             # Other signals can get here indirectly when using the DIE action.
774             # --------------------------------------------------------------
775             # Question: Do we really want to call the custom forward function(s)
776             # if "die" was called inside an "eval" block?
777             #
778             # Right now the answer is "YES".
779             # DBUG_DIE_CONTEXT() was added to allow the developer to
780             # decide if this is the right answer or not.
781             # --------------------------------------------------------------
782              
783             sub _dbug_trap_die_call
784             {
785 146     146   4203 my $msg = shift; # Always ends in "\n"!
786              
787 146         732 local $SIG{__DIE__} = "IGNORE";
788              
789             # So fish won't try to rebalance fish while processing the die request ...
790 146         927 my $runWhenOutOfScope = Perl::AtEndOfScope->new ( \&on_set_global_var,
791             'skip_eval_fix', on_get_global_var ('skip_eval_fix') );
792 146         1731 on_set_global_var ('skip_eval_fix', 1);
793              
794 146         387 my $sig_info = $dbug_signal_vars{forward_signals}->{__DIE__};
795 146 50       463 unless ( $sig_info ) {
796 0         0 my %tmp;
797 0         0 $tmp{ACTION} = DBUG_SIG_ACTION_DIE;
798 0         0 $sig_info = \%tmp;
799             }
800              
801 146         341 my $funcs = $sig_info->{FUNC};
802              
803 146         294 my $trapped_by_eval = 1; # Assume true for now ...
804 146         234 my $rethrown = 0; # Assume not trapping a rethrown exception.
805 146         511 my $stack_msg = "Called \"die\" for a stack trace ... " . on_dbug_called_by (1);
806              
807             # Was this call triggered via another signal handler calling die?
808 146 100 100     1009 if ( $dbug_signal_vars{chained_die} ) {
    100          
    100          
809             # Set so when the "eval" block around the original signal catches
810             # this "die", it will still remember next time that the request is
811             # a duplicate of this one and not repeat any work when it rethrows
812             # the "die" again! (IE print the stack trace or calling the custom
813             # "die" function(s) a 2nd time for the same event.)
814 26         74 $dbug_signal_vars{expect_duplicate_rethrown_request} = 1;
815 26         73 delete $dbug_signal_vars{last_die_message};
816              
817             # Stack trace was already printed out by the other signal calling die ...
818 26         102 $trapped_by_eval = $^S; # Perl's special var tells if trapped by eval!
819              
820             # Was "die" called again when a "eval" block rethrew the original forwarded
821             # die message from another signal again?
822             # If so, we don't want to forward things to the custom functions again!
823             } elsif ( $dbug_signal_vars{expect_duplicate_rethrown_request} ) {
824 26         87 delete $dbug_signal_vars{expect_duplicate_rethrown_request};
825              
826             # Just verifying my assumption ...
827 26 100       83 $rethrown = ($msg eq $dbug_signal_vars{last_die_message} ) ? 1 : 0;
828              
829 26         50 my $prt_sts; # Unknown state ...
830 26 100       74 if ( $rethrown ) {
831 24         52 $funcs = undef;
832 24         44 $prt_sts = 0; # Turn it off ...
833             }
834              
835 26 100       151 on_dbug_print_pkg_tag ( DBUG_FILTER_LEVEL_INTERNAL, "%s\n%s",
836             "Caught the expected rethrown die request!",
837             $rethrown ? "The messages were the same."
838             : "The messages were different!");
839              
840             # Should we turn printing off? Or leave it in it's current state?
841 26         99 $trapped_by_eval = on_dbug_hack ( on => $prt_sts, \&_dbug_stack_trace, 1, $stack_msg );
842              
843             # Did the normal die get trapped by an 'eval' and rethrown again with
844             # the same message? This is not 100% reliable since people tend to
845             # rethrow the 'die' with a new message in their own code!
846             } elsif ( $dbug_signal_vars{last_die_message} &&
847             $dbug_signal_vars{last_die_message} eq $msg ) {
848 42         88 $rethrown = 1;
849              
850             # Do we want to call the custom functions on a rethrow ???
851             # Currently using DBUG_DIE_CONTEXT() so the user can decide for himself!
852             # $funcs = undef; # Uncomment if the answer is no!
853              
854             # Assumes we don't want a stack trace again ...
855 42         126 $trapped_by_eval = $^S; # Perl's special var tells if trapped by eval!
856              
857             # Just a normal 1st time call to die!
858             } else {
859 52         206 $trapped_by_eval = _dbug_stack_trace (1, $stack_msg);
860             }
861              
862             # ------------------------------------------------------------------
863             # Write the die messsage to fish ...
864             # ------------------------------------------------------------------
865 146 100       431 if ( $rethrown == 0 ) {
866 80         298 on_dbug_hack (pause => 0, \&on_dbug_print_pkg_tag, DBUG_FILTER_LEVEL_ERROR, "::Signal::__DIE__", $msg);
867             }
868              
869             # So can detect if die was trapped by an eval stmt & rethrown again!
870 146         434 $dbug_signal_vars{last_die_message} = $msg;
871              
872             # ------------------------------------------------------------------
873             # Let's call the custom die routines ...
874             # And manage future calls to DBUG_DIE_CONTEXT() by any of them!
875             # ------------------------------------------------------------------
876 146 50 66     671 if ( $funcs && $sig_info->{CONTEXT} ) {
877             # {chained_die} was set elsewhere if needed ...
878 73         257 local $dbug_signal_vars{die_context_managed} = 1;
879 73         176 local $dbug_signal_vars{same_die_message} = $rethrown;
880              
881 73         161 my $context_action = $trapped_by_eval;
882 73 50 33     519 if ( $sig_info->{ACTION} == DBUG_SIG_ACTION_EXIT13 ||
883             $sig_info->{ACTION} == DBUG_SIG_ACTION_EXIT_SIGNUM ) {
884 0         0 $context_action = 0; # Ignore any eval/try attempts ...
885             }
886 73         206 local $dbug_signal_vars{die_context_eval} = $context_action;
887              
888             # Must either be 1 or 2 in this instance!
889 73         198 local $dbug_signal_vars{die_context_die} = $sig_info->{CONTEXT};
890              
891 73         166 _dbug_forward_trapped_signal ( "__DIE__", $msg, $sig_info->{CONTEXT}, @{$funcs} );
  73         297  
892             }
893              
894             # ------------------------------------------------------------------
895             # What to do after processing the signal ...
896             # ------------------------------------------------------------------
897 146 50 33     1210 if ( $sig_info->{ACTION} == DBUG_SIG_ACTION_EXIT13 ||
    100          
    50          
898             $sig_info->{ACTION} == DBUG_SIG_ACTION_EXIT_SIGNUM ) {
899 0         0 print STDERR $msg;
900 0         0 DBUG_LEAVE ( $sig_info->{EXIT} );
901              
902             } elsif ( $sig_info->{ACTION} == DBUG_SIG_ACTION_LOG ) {
903 4 100       17 if ( $dbug_signal_vars{LOG_NOW_WORKS_IN_DIE} ) {
904 3         9 delete $dbug_signal_vars{LOG_NOW_WORKS_IN_DIE};
905 3         113 print STDERR join ("", $msg);
906 3         10 $@ = ""; # So the catch block of eval or try won't be triggered!
907 3         27 return; # Returns control to program ignoring "eval"/"try" logic.
908             }
909              
910 1 50       10 unless ( $rethrown ) {
911 1         30 print STDERR
912             "Ha ha! I already told you using DBUG_SIG_ACTION_LOG for trapping __DIE__\n",
913             "doesn't work! It still calls die anyway!\n";
914             }
915 1         7 _dbug_turn_off_end_while_dying (2);
916 1         6 return; # This return triggers an automatic die!
917              
918             } elsif ( $sig_info->{ACTION} == DBUG_SIG_ACTION_DIE ) {
919 142         501 _dbug_turn_off_end_while_dying (2);
920              
921             # This die will write the die message to the screen!
922             # Unless it's trapped by an eval ...
923 142         1273 die ( $msg );
924             }
925              
926             # Should never get here ...
927             on_dbug_print_pkg_tag ( DBUG_FILTER_LEVEL_WARN,
928             "Unexpected Signal Action Specified for signal %s! (%d)\n%s",
929 0         0 "__DIE__", $sig_info->{ACTION}, " Just logging it!");
930              
931 0         0 return;
932             }
933              
934             # --------------------------------------------------------------------
935             # Prints out a stack trace (Not necessarily the fish function trace.)
936             # Never called when __WARN__ is trapped.
937             # --------------------------------------------------------------------
938             # Returns: The numer of eval's detected in the stack! (>= 0)
939             # --------------------------------------------------------------------
940             sub _dbug_stack_trace
941             {
942 98     98   198 my $skip = shift; # How many functions on the stack to skip over ...
943 98         195 my $msg = shift; # What message to start the stack trace with ...
944              
945 98         205 my $eval_found = 0; # Assume not trapped by an eval block.
946              
947             # Filter based on INFO level ...
948 98         190 my $level = DBUG_FILTER_LEVEL_INFO;
949              
950             # Color based on INTERNAL level ...
951 98         281 my @colors = on_get_filter_color (DBUG_FILTER_LEVEL_INTERNAL);
952              
953 98         271 my $print_flg = on_filter_on ( $level );
954              
955 98 50       577 on_dbug_print_pkg_tag ( $level, $msg ) if ( $msg );
956              
957             # Not using DBUG_PRINT on purpose ...
958 98         299 my ($c, $idx) = ("", $skip + 1);
959 98         819 while ( $c = (caller (${idx}++))[3] ) {
960 638 50       2530 on_printing $colors[0], on_indent ("Stack Trace"), " --> $c ()", $colors[1], "\n" if ( $print_flg );
961 638 100       4164 ++$eval_found if ( $c eq "(eval)" ); # Eval block in stack found!
962             }
963              
964 98 50       404 on_printing $colors[0], on_indent ("Stack Trace"), " --> main ()", $colors[1], "\n" if ( $print_flg );
965              
966 98         463 return ($eval_found); # Count of eval levels detected ...
967             }
968              
969              
970             # =============================================================================
971             # Calls all the custom signal functions.
972             # But what happens if the custom signal function itself calls die?
973             # $die_action tells which @die_action_msg to use ... (1 or 2)
974              
975             # Using action "3" causes issues, so no longer supported!
976              
977             my @die_action_msg = ( "One of the custom signal functions called die!",
978             "But we're ignoring any die requests here.",
979             "So we're not calling the remaining custom function(s)!",
980             # "So we're honoring the request by rethrowing the error."
981             );
982              
983             sub _dbug_forward_trapped_signal
984             {
985 114     114   259 my $sig = shift; # The current signal being thrown!
986 114         265 my $msg = shift; # Only provided with DIE/WARN signals!
987 114         225 my $die_action = shift; # Action/Context if die is called in custom signal function.
988 114         388 my @func_lst = @_; # The list of functions to call ... shouldn't be empty.
989              
990             # Did someone try to loop recursively back to me again ???
991 114 50       423 return if ( $dbug_signal_vars{recursion} ); # Yes ...
992              
993 114         272 ++$dbug_signal_vars{recursion};
994              
995             # Determine which value to use for the arguments ...
996 114 100       318 my $arg = $msg ? $msg : $sig;
997              
998             # Build an alternate stack so the forward functions can't mess fish up!
999 114         198 my @alt_stack;
1000 114     114   790 my $func = sub { my $lvl = shift; foreach (1..$lvl) { DBUG_ENTER_BLOCK ("***** Fish Mistake in Custom Signal function *****"); } return; };
  114         233  
  114         509  
  542         1663  
  114         651  
1001 114         520 on_dbug_hack (on => 0, who_called => 0, functions => \@alt_stack, $func, on_dbug_level() + 1);
1002              
1003 114         369 foreach my $fn ( @func_lst ) {
1004 195         466 eval {
1005 195         1233 local $SIG{__DIE__} = "DEFAULT";
1006 195         1051 on_dbug_hack ( who_called => 0, \&DBUG_ENTER_BLOCK,
1007             __PACKAGE__ . "::Forwarding-Trapped-Signal-${sig}" );
1008              
1009             # Indirectly calling the custom signal function with own fish stack ...
1010 195         647 my @own_stack = @alt_stack;
1011 195         588 my $stop = on_dbug_hack ( functions => \@own_stack, $fn, $arg );
1012              
1013 123         447 DBUG_VOID_RETURN ();
1014             # last if ( $stop );
1015             };
1016              
1017 195 100       2107 if ( $@ ) {
1018 72         366 on_dbug_print_pkg_tag ( DBUG_FILTER_LEVEL_INTERNAL, "%s\n%s\n%s",
1019             $die_action_msg[0], $die_action_msg[$die_action], $@ );
1020 72         354 DBUG_CATCH ();
1021              
1022 72 50       211 next if ( $die_action == 1 ); # Ignore the die!
1023 72         170 last; # Skip remaining custom funcs.
1024             }
1025             } # End foreach $fn loop ...
1026              
1027 114         326 --$dbug_signal_vars{recursion};
1028              
1029 114         1702 return;
1030             }
1031              
1032              
1033             =back
1034              
1035             =head1 CREDITS
1036              
1037             Thanks to Fred Fish for developing the basic algorithm and putting it into the
1038             public domain! Any bugs in its implementation are purely my fault.
1039              
1040             =head1 SEE ALSO
1041              
1042             L The controling Fred Fish DBUG module.
1043              
1044             L The live version of the DBUG module.
1045             Used to log fish calls made by this module.
1046              
1047             L The stub version of the DBUG module.
1048              
1049             L - Allows you to trap and log STDOUT/STDERR to B.
1050              
1051             L - Allows you to implement action
1052             DBUG_SIG_ACTION_LOG for B. Really dangerous to use. Will break most
1053             code bases.
1054              
1055             L - A L wrapper to redirect test results to
1056             B.
1057              
1058             L - Sample code demonstrating using DBUG module.
1059              
1060             =head1 COPYRIGHT
1061              
1062             Copyright (c) 2007 - 2025 Curtis Leach. All rights reserved.
1063              
1064             This program is free software. You can redistribute it and/or modify it
1065             under the same terms as Perl itself.
1066              
1067             =cut
1068              
1069             # ============================================================
1070             #required if module is included w/ require command;
1071             1;
1072