markov has asked for the wisdom of the Perl Monks concerning the following question:

Is there a clean and generic way to differentiate between the following three causes which fill $@?

eval "die()"; # implementor wants to terminate eval "xyz*"; # parser gives up on it eval "4/0"; # internal runtime error

The latter two need a different treatment, more like syslog levels critical or alert. The first is "expected" by the programmer, syslog level error.

Replies are listed 'Best First'.
Re: why did i die?
by BrowserUk (Patriarch) on Apr 02, 2014 at 09:20 UTC

    Are you looking for some differentiator other than the contents of $@?

    eval{ die() }; print $@;; Died at (eval 13) line 1, <STDIN> line 5. eval{ xyz* }; print $@;; Unquoted string "xyz" may clash with future reserved word at (eval 14) + line 1, <STDIN> line 6. eval{ 4/0 }; print $@;; Illegal division by zero at (eval 15) line 1, <STDIN> line 7.

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      The difference (in my opinion) would be between

      eval 1/0 # compile-time error

      and

      my $d= 0; eval 1/$d;

      Maybe that would be a solution to discern between compile- and runtime-errors - wrap the code in question in a subroutine. If the code compiles, then we don't have a compile-time error:

      my $code; my $ok= eval sprintf q{ $code= sub { %s }; 1 }, $string; if(! $ok) { my $err= $@; warn "Compile error on $string: $err"; }; $ok= eval { $code->(); 1; }; if( ! $ok) { my $err= $@; warn "Runtime error on $string: $err"; };

      Update: Upon testing, I now realize that 1/0 does not generate a compile-time error. But at least, that way one could find out whether there is a compile-time error or a runtime error.

        In specific cases, this may work. In the generic case (I have to solve the generic case), the eval'ed code is "anything perl can offer", so may (probably will) contain dynamicly required extra code.

        Above eval is implementing a try block, calling a code-ref which provided by the application.

        I hope there is something indicating the state of the perl parser or the state of the VM after the eval.

      Yes: I do not want to keep a table of all errors which Perl can produce... The "expected" die() messages are not under my control.

      I am looking for some special variable or trick (probably other than __DIE__) to keep these three causes apart.

        Can you instruct the coders that expected die messages should be prefixed (or contain) some specific piece of text. Then you only need check for that.

        Perhaps, tell them that deliberate exits should be done with just die(); and look for the "Died at" text.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The "expected" die() messages are not under my control.

        :) So keep a table of messages which are under your control?

        See what diagnostics does (perldiag)

Re: why did i die?
by LanX (Saint) on Apr 02, 2014 at 09:17 UTC
    maybe

    my $flag=""; local $SIG{__DIE__} = sub { $flag = "implementor wants to terminate"; ... }; eval "$code"; if ( $flag eq ... and so on...

    untested

    update

    die

    You can arrange for a callback to be run just before th +e "die" does its deed, by setting the $SIG{__DIE__} hook. The associated handler will be called with the error text a +nd can change the error message, if it sees fit, by calling "d +ie" again. See "$SIG{expr}" in perlvar for details on sett +ing %SIG entries, and "eval BLOCK" for some examples. Although +this feature was to be run only right before your program wa +s to exit, this is not currently the case--the $SIG{__DIE__} + hook is currently called even inside eval()ed blocks/strings!

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      Tested:

      perl -we 'my $x = 0;eval "local \$SIG{__DIE__} = sub {\$x = 123};4/0"; + print "$x: $@"' 123: Illegal division by zero at (eval 1) line 1.

      The run-time internal error also calls __DIE__.

      Nastier: I do not know what code is ran inside the eval. It may (probably will) install its own __DIE__ handler.

        you could check something like the errno in '$!' to distinguish.

        > I do not know what code is ran inside the eval.

        well then you'll probably also need a block-eval around the string eval.

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: why did i die?
by markov (Scribe) on Apr 02, 2014 at 09:52 UTC

    I do have a partial solution. When my own __DIE__ handler is being called (but I think I cannot guarantee that), then the runtime error will have no caller() info.

    So, when the eval'ed code does not mess with the __DIE__ handler, I can differentiate the three:

    my $cause = 'PARSER'; eval { local $SIG{__DIE__} = sub { $cause = caller ? 'EXPLICIT' : 'INTERNAL'}; $do_something(); }; print $cause if $@;

    Now a solution without the volatile __DIE__

Re: why did i die?
by zentara (Cardinal) on Apr 02, 2014 at 10:02 UTC
Re: why did i die?
by LanX (Saint) on Apr 02, 2014 at 10:35 UTC
    Maybe you just want to override 'die()' ?

    lanx@nc10-ubuntu:/tmp$ perl -e 'use subs "die"; sub die { print "expec +ted\n" }; eval "4/0"' lanx@nc10-ubuntu:/tmp$ perl -e 'use subs "die"; sub die { print "expec +ted\n" }; eval "die()"' expected

    but '&CORE::die' will of course still call the original...

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      That's not a generic solution. The code I run in the eval can be everything Perl has to offer... for instance runtime requiring any module on CPAN.

        Another option I see¹ is to inspect the source-code.

        Every internal error-msg comes with parsable file and line infos. You could try to match for "die" there.

        Saying this I have to tell you that file and line infos can be manipulated.

        OTOH combining all shown approaches should make it rather difficult to fool your monitoring solution.

        Cheers Rolf

        ( addicted to the Perl Programming Language)

        ¹) beside hacking CORE::die  with XS (which of course can be sabotaged by modules hacking CORE::die  ...)

        So what?

        You have to realize that you won't be able to find a pure Perl solution which can't be sabotaged by evaled pure Perl.

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: why did i die?
by locked_user sundialsvc4 (Abbot) on Apr 02, 2014 at 13:01 UTC

    Consider Exception::Class, for example.   It is a little-known fact that die() also accepts an object as a parameter.   Therefore, you can “throw,” and “catch,” a fully self-describing data structure.   (In cases where something might throw a string, wrap that with a catcher that throws an object containing that string.)

      You do not answer my question: I am not looking for an exception framework. I already have one! And that one does already wrap the string into an object... the problem is that I want to improve that object. The answer to my question would help me with that.