How does the perl debugger find the source lines

Discussion in 'Perl Misc' started by Peter J. Holzer, Dec 26, 2013.

  1. I'm currently writing a plugin for qpsmtpd, which means that both the
    plugin and the tests are read from a file, massaged a bit, compiled via
    eval() and finally the test framework calls the test methods.

    This seems to confuse the debugger a bit, because it looks like this:

    | Qpsmtpd::plugin::run_tests(/home/hjp/wrk/qpsmtpd/t/Test/Qpsmtpd/Plugin.pm:36):
    | 36: $plugin->$method();
    | DB<2> s
    | Qpsmtpd::plugin::no_bcc::pass1(t/plugin_tests/no_bcc:9):
    | 9:
    | DB<2> n
    | Qpsmtpd::plugin::no_bcc::pass1(t/plugin_tests/no_bcc:11):
    | 11:
    | DB<2> n
    | Qpsmtpd::plugin::no_bcc::pass1(t/plugin_tests/no_bcc:12):
    | 12:
    | DB<2>

    The interesting thing is that it shows the correct source file
    ("t/plugin_tests/no_bcc") and the correct line numbers (9, 11, 12), but
    not the contents of the lines, which look like this:

    9 my ($self) = @_;
    10
    11 my $qp = $self->qp;
    12 $qp->command("rset");

    So, if the debugger knows the file and the line, why can't it display
    the content?

    hp
     
    Peter J. Holzer, Dec 26, 2013
    #1
    1. Advertisements

  2. The simple answer seems to be 'because the array perl uses to locate
    source lines' (assuming the path to the source file was a/b/c.pl, the
    corresponding array would be

    @_<a/b/c.pl

    in package main) doesn't contain the code.

    Example showing how to access that:

    ,----
    | [rw@sable]/tmp#perl -d ab/a.pl
    |
    | Loading DB routines from perl5db.pl version 1.32
    | Editor support available.
    |
    | Enter h or `h h' for help, or `man perldebug' for more help.
    |
    | main::(ab/a.pl:1): print 1;
    | DB<1> $a = *{"_<ab/a.pl"}{ARRAY}
    |
    | DB<2> p @$a
    | BEGIN { require 'perl5db.pl' };
    | print 1;
    | print 2;
    `----

    It is possible to load the source code lines into this array (offsetted
    by 1) and the debugger then happily uses it.
    "Devel::REPL - a modern perl interactive shell"

    As usual, modern means "It prints 'Hello, world!'" but it needs six
    order of magnitude more infrastructure code than a simple

    perl -e 'print "Hello, World!\n"'

    would require" ...
     
    Rainer Weikusat, Dec 29, 2013
    #2
    1. Advertisements

  3. I played around with it a bit more, but I am just becoming more
    confused. It's not like eval never fills in @{"_<$filename"}. In this
    simple test script:

    #!/usr/bin/perl
    use v5.10;

    eval ('
    #line 1 "3a"
    use v5.10;

    sub foo {
    say "bla";
    }
    ');

    foo();
    say;

    @{"_<3a"} is filled with the correct lines and I can singlestep into foo().

    So, sometimes the lines are there and sometimes they aren't.

    I'll try to come up with a minimal script which exhibits the faulty
    behaviour.

    hp
     
    Peter J. Holzer, Dec 30, 2013
    #3
  4. I've actually casually read through the 'core' of the implementation
    which is 123 lines of code and seems to be little more than a wrapper
    around Term::Readline (the perl debugger will use that as well if
    available) with a moose chained to its ankle because someone considered
    that a fashionably pet in 2007. If the author is really convinced to
    have solved any 'hard problems' (minus getting his head around the issue
    in the first place) in there, I seriously hope that he doesn't ever
    encounter a hard problem as this will probably blow a fuse in his head.

    OTOH, even the most trivial problem can be made to look very commanding
    by a suitably obfuscated solution, eg use

    sub wrap_as_sub {
    my ($self, $line, %args) = @_;
    return qq!sub {\n!. ( $args{no_mangling} ? $line : $self->mangle_line($line) ).qq!\n}\n!;
    }

    instead of

    sprintf('sub { %s }', $args{no_mangling} ? $line : $self->mangle_line($line));

    minus any of the fancy features (including sensible handling of
    exceptions thrown by the executed code), this is

    ----------
    use Term::ReadLine;

    sub run
    {
    eval($_[0]);
    print STDERR ("XX: $@") if $@;
    }

    my ($term, $line);

    $term = Term::ReadLine->new('Perl ERPL');
    print(run($line), "\n") while defined($line = $term->readline('Perl ERPL>'));
    print("\n");
     
    Rainer Weikusat, Dec 30, 2013
    #4
  5. [...]
    The 2nd line was a 'last second' addition to make this print compiler
    errors encountered during eval which eats the return value of the
    former. Slightly more complicated demo which deals with that:

    ----------
    sub dorun
    {
    eval($_[0]);
    }

    sub run
    {
    my @r;

    @r = &dorun;
    return @r unless $@;

    print STDERR ("XX: $@");
    return;
    }
    -----------

    A supposed-to-be-reusable class providing such a feature should probably
    take some more pain in order to provide a clean execution environment
    than just using an 'empty sub', eg, it should localize all or at least
    some of the global variables affecting execution of Perl code in order
    to avoid leaking parts of the 'executor' environment into the executed
    code and the same in the opposite direction (especially @_ and $_).
     
    Rainer Weikusat, Dec 30, 2013
    #5
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.