Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Comment on

( #3333=superdoc: print w/ replies, xml ) Need Help??

Perl's Warn and Die Signals


Introduction:


This is a tutorial on using Perl's warn and die signals. It is based on a post I made some time ago, here, but covers more of the details and raises more of the pitfalls. In this tutorial I attempt to separate the warn and die signals from the OS signals, explain how they are used and what they can be used for, and explore the problems that can come up. At the end you will find a list of additional documents you should read. Note that I write this under the risky assumption that you are using Perl 5.6. This does not exclude you if you have an older version of Perl, as I believe everything holds for Perl 5.0 with the exception of -W and the warnings pragma.

How are sigwarn and sigdie different from OS signals?


And why does it matter? OS signals originate from the operating system. A good example is INT, the interupt signal, which can be caused by any number of scenarios including the user hitting control-C. Different builds of Perl behave rather differently when they receive these signals because the signals themselves are platform dependent. Perl's warn and die signals (henceforth referred to as sigwarn and sigdie) are manipulated by the programmer the same way that the OS signals are manipulated, and this can lead to the confusion that they are the same type of signal. They are not. Perl itself generates sigwarn and sigdie, and does so in a reliable and consistent fashion. This means that you, the programmer, have much less to fear while writing your own signal handler for them. I caution you that while implementing signal handlers for other signals may look the same as what I am about to show you, it is not. Furthermore, it involves much more then I am covering here. No further disclaimers on this will be made.

How are sigwarn and sigdie useful?


The fun begins.

When a program (or Perl) calls die (or warn) a signal is sent. If a signal handling routine has been defined (and is in scope) for that signal, then it is executed, otherwise Perl catches it and handles it accordingly. These signals can be generated from a variety of sources, including -w, -W, use warnings, warn, die, carp, cluck, croak, confess, and more. <emph>What's important is to realize that you, the programmer, can exercise some control over the way your program behaves when these occur.</emph>

How?

You can write handlers. Lets examine some simple scenarios where a signal handler could be useful. Say I've got a program that tests the subroutines in a module. It calls each routine and prints something to STDOUT signifying the result. To truly test the routine, the test also throws the "unexpected" at it, intentionally trying to raise a warning. Warnings get printed to STDERR though, and for my test I want everything to go to STDOUT. There are two solutions, one is to redirect STDERR to STDOUT. No problem, the docs for open tell me how to do that, but then I don't get to know where the warning came from, only the output. Worse, if the warning happened around a normal print then I can't control which shows up first. (And I want the output to be consistent so that I don't actually have to read it, I can just compare it against a previous run.) So instead I decide to write a sigwarn handler that will print the warning to STDOUT (instead of STDERR) and give me the exact offending line (which Perl does not always do. Playing with caller is even more important with die though).
$SIG{__WARN__} = sub { my @loc = caller(1); print STDOUT "Warning generated at line $loc[2] in $loc[1]:\n", @_ +, "\n"; return 1; };
Some things to note: I have assigned an anonymous subroutine as the signal handler. I could have defined the sub, and then set $SIG{__WARN__} equal to a subref. One important thing about this is the semi-colon. Since the %SIG assignment takes 6 lines, it's easy to forget that it was an assignment, not a code block. As your handlers get more complicated I recommend writing them as named subroutines, although that carries additional risk which I will address in a later section. Next, my routine explicitly returns 1. Remember that all Perl subroutines return something. If I hadn't returned 1, I would have been returning 1 or the empty string, depending on the success of the print statement. Yes, print can fail. Should you check your prints? Yes... if you are writing to a file. This is as easy as print( "hello world" ) or die $!; but can be even more fun with the help of OO file handles like IO::File. OO things are good because they have DESTROY methods which get called by Perl when you finish, exit, or die. But I digress. My point was that your subroutines should always explicitly return something. If the subroutine is a 'void' method, have it return undef.

So that covers the how, and some of the why, but doesn't give you the full potential. When a program runs into a bad situation, the programmer often has it call die. This causes Perl to send out sigdie, and if a handler is defined it gets called. One last thing on warnings before we delve head first into sigdie: Make Warnings Fatal Everywhere You Can! You can do this at compile time in 5.6 with the use warnings pragma:

use warnings FATAL => qw( all );
You can replace 'all' in there with a more specific list if the need arises, which is why I use qw(). But this pragma is scoped (so is strict, btw) so it only makes YOUR warnings fatal. So, and this works for older versions of Perl, catch sigwarn and call die:
$SIG{__WARN__} = sub { CORE::die "Warning:\n", @_, "\n" };
Yeah, you probably wanted line numbers, but I already showed you how to add that. Note that by specifying CORE::die I out run any attempt to replace the die function. (Yes, some modules redefine die instead of implementing a signal handler.) So now onto sigdie.

SigDie

Catching the signal generated from die is good for one thing, and one thing only: debugging. Don't use it to clean up stuff. Use END routines and DESTROY methods for that. (If all this OO talk has thrown you for a loop, read Tom's perltoot and the docs for bless. I believe there are a few more OO resources in the monastery as well. At this point I'll simply say that if an instaniated blessed object has a DESTROY method then it gets called when that object goes out of scope.) Don't even try to recover from death abusing sigdie... once Perl decides to die, the games over. (Ok, that's only partly true. See my discussion on eval below.) So then, the purpose of catching sigdie is to gain more insight into why the program died. This is generally done by printing the punctuation ($!, $^E, $@, $?) and the call stack. Somewhere in the code catacombs I printed a "trick" for getting a call stack easily. I'll repeat it here:
use Carp; sub CallStack { local $@; eval { confess( '' ) }; my @stack = split m/\n/, $@; shift @stack for 1..3; # Cover our tracks. return wantarray ? @stack : join "\n", @stack; }
Important side note: You shouldn't make your signal handlers complicated. Things are falling down everywhere, so don't make things complicated with subroutine calls and system activity. Right? Sort of. Sigdie is special in that it is a Perl signal, not an OS signal. So things aren't crashing (yet). This means we can still do stuff, but beware - now that your signal has been called, it has been disabled! If you die while handling sigdie, you really die! This prevents recursive doom, but it also means you walk a fine line of loosing this precious debug info at any time. So then, print all this info out everywhere you can think of. Get it on the screen first, then try to write it to a file (that could fail) or write it to an OS log (that can also fail).

eval

By now you must have wondered, "what happens if die gets called inside an eval block?" Your signal handler gets called. That is good, and bad. You need to be aware of this potential and decide how you want to handle it. You might want to check $^S at some point in your handler and adjust based on what state the code is in: Compile ($^S eq undef), Normal ($^S eq ''), or Run ($^S eq 1). When in doubt, and you have a particular eval block where you don't want the handler to be invoked, then turn it off using local:
sub Something { local $@; # Don't step on other code. # try eval{ local $SIG{__DIE__}; # No sigdie handler # do stuff that might die } # catch HandleEvalError( $@ ) if $@; }

Potential Problems


This section is more of a recap of my pitfalls of SIGDIE post. Things you should realize before making a sigdie-mess:
  1. Declare your handler subroutine before setting the handler.
    BEGIN{ $SIG{__DIE__} = \&FatalErr } # Real problem if compile fails before getting here. sub FatalErr { # do stuff, maybe print @_ or something. }
    Why does moving the sub up work? Well... BEGIN is actually just another sub routine, except that it gets called as soon as it's compiled. INIT, by the way, is also a simillarly special sub routine, except that it gets called as the first step of runtime.
  2. Functions called with an ampersand and no argument list are passed the current values of @_. This means that your sigdie handler gets the same argument list that die got. One thing to note is that the file/line number appending has already taken place by the time your handler gets @_. And if you had a file handle open then $. is in there too.
  3. Your handler only gets called if someone uses die. This means that if you aren't checking your system calls and using die like C's Assert, then you won't get any added functionality.

Recommended reading:



I hope that this tutorial has been a help to you, and that you find good, safe, and useful places for sigwarn and sigdie. - Adam

A few of my past posts on the subject:
Fun with $SIG{__DIE__}      Code timing      END failed--call queue aborted      And yet more $SIG{__DIE__} fun...      SIGDIE


In reply to Perl's Warn and Die Signals by Adam

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • Outside of code tags, you may need to use entities for some characters:
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others rifling through the Monastery: (2)
    As of 2014-09-21 20:12 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      How do you remember the number of days in each month?











      Results (175 votes), past polls