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

I am coding a content engine which parses my files into valid perl block and then evals them into anonymous subroutines. Basically, the files have API defintions which are compiled into a hash of anon subs.

The problem is, if there is an error that is not a syntax error Apache emits an error to the error log indicating the source as "(eval xxx)", where xxx is a number.

Is there any way to determine, after an eval has taken place, what the internal eval counter is in perl so I can maintain a table of eval numbers to english names so when I trap thse Apache errors I can replace (eval xxx) with something that makes a bit more sense?

I have been searching for this information for the past couple hours, so hopefully someone here can help me out with this problem!

I am running mod_perl under Apache if that is relevant.

Thanks for any assistance with this problem! :)
  • Comment on Is it possible to determine the eval block accumulator?

Replies are listed 'Best First'.
Re: Is it possible to determine the eval block accumulator?
by Abigail-II (Bishop) on Feb 14, 2003 at 12:37 UTC
    Peeking in the source, it turns out there's a C variable that holds the count. (Not very surprising...). Here's an Inline::C program giving you access to it:
    #!/usr/bin/perl use strict; use warnings; use Inline 'C'; eval 'print "Hello, world\n"'; printf "Sequence number: %d\n", gimmi (); eval 'print "Hello, earthlings\n"'; printf "Sequence number: %d\n", gimmi (); __END__ __C__ int gimmi () { return (PL_evalseq); }

    That that if you run this the first time, you'll get high numbers, but that's because Inline and/or Inline::C will do some evalling as well. After that, the count starts at 2, probably also because of some evalling inside Inline and/or Inline::C.

    Abigail

      Bingo Abigail, your answer is exactly what I was looking for! :)

      Now, to try and coerce this to work with mod_perl! :}


      For everyone else, what I am doing is parsing code snippets from multiple files, doing some regexp magic on the code snippets to replace special markup with legal perl and then creating an anonymous subroutine from the code, eval'ing it into a code reference and storing that coderef in a hash for later execution.

      Thanks to everyone for all your help and suggestions, especially Abigail for this solution! :)
Re: Is it possible to determine the eval block accumulator?
by Juerd (Abbot) on Feb 14, 2003 at 08:05 UTC

    Is there any way to determine, after an eval has taken place, what the internal eval counter is in perl so I can maintain a table of eval numbers to english names so when I trap thse Apache errors I can replace (eval xxx) with something that makes a bit more sense?

    Use the #line meta-comment:

    eval "#line 1 foo\ndie"; print $@;

    Juerd
    - http://juerd.nl/
    - spamcollector_perlmonks@juerd.nl (do not use).
    

      This does not appear to solve my problem...

      Died at foo line 1.
      That is the result of the code snippet you provided, regardless of how many eval's have occured.

      What I am after is the *total* number of eval blocks the perl interpreter has parsed since it's been launched so that after I execute an eval block I can create a table to match the eval accumulator with a reference to the created code object.

      This way when an error occurs I can replace the obscure "(eval xxx)" references with something much more helpful for debugging.

        AFAIK the eval counter begins at 1 and increments by 1 in the order of compilation, so you should be able to find the block in question easily enough by looking at the order in which they are compiled. You might also store the code snippets in their text form inside an array starting at index 1. The real question though is why you're storing the code in raw text? Why can't you place those subroutines in another file that you require? Also, unless you have strict control over what gets submitted to you in the form of text files, you may want to run this eval'd code inside a Safe.


        "The dead do not recognize context" -- Kai, Lexx

        Died at foo line 1. That is the result of the code snippet you provided, regardless of how many eval's have occured.

        Change foo to something that will help you debug. This does of course require eval EXPR instead of eval BLOCK.

        Juerd
        - http://juerd.nl/
        - spamcollector_perlmonks@juerd.nl (do not use).
        

Re: Is it possible to determine the eval block accumulator?
by steves (Curate) on Feb 14, 2003 at 10:29 UTC

    Expanding on what Juerd suggested a little, consider that you can do this sort of thing:

    use strict; my $line = 1; my $sub = 'foo'; eval "#line $line $sub\ndie"; print "1: $@"; $line = 2; $sub = 'bar'; eval "#line $line $sub\ndie"; print "2: $@";
    produces:
    1: Died at foo line 1. 2: Died at bar line 2.

    Also consider the fact that even though you're building blocks of code, those blocks must eventually get parsed as eval EXPR type eval's unless I'm not understanding something. The only time I see those eval(xxx) messages is when I use expressions. When I use blocks I get the file name and line number the block is in. I believe that's because eval blocks are compiled at compile time where file names and line numbers are set. Expressions are compiled at run time and have no file name/line number context unless given, as in Juerd's examples that I expanded on. My reasoning may be incorrect, but testing shows the difference. This code in a file named evalt

    use strict; eval { die; }; print "1: $@"; eval "die"; print "2: $@";
    produces:
    1: Died at evalt line 7. 2: Died at (eval 1) line 1.
Re: Is it possible to determine the eval block accumulator?
by gmpassos (Priest) on Feb 16, 2003 at 05:11 UTC
    The Abigail node is perfect, since it returns the value without modify it!

    But if you want a pure Perl function for that:

    sub eval_x { eval("die") ; my ($eval_x) = ( $@ =~ /\(\s*eval\s+(\d+)\s*\)/si ); return( $eval_x ) ; }
    The problem is that you make an eval to get the eval_x!

    About the idea of "fever", to follow the order of the eval()s that you make to get the number, don't do this. Because if some eval() has another eval() inside, you can't follow any more!

    Graciliano M. P.
    "The creativity is the expression of the liberty".