Is it possible to run only particular sub under debugger?

Viewed 96

I want to print stacktrace only of particular subroutine. Is it possible ? Script is running under perl without -d option.

Simple example:

#!/usr/bin/env perl

foo();

sub foo {
    print "Hello world\n";
    # enable debugger from this moment with PERLDB_OPTS='NonStop frame=1'
    bar();
    # disable debugger from this moment
    return;
}

sub bar {
    print "Just another Perl hacker\n";
    return;
}

Expected output:

$ perl test.pl 
Hello world
    entering main::bar
Just another Perl hacker

I tried to play with $ENV{PERL5DB} but no effect:

$ENV{PERL5DB}='sub DB::DB {} sub sub {print ++$i, " $sub\n"; &$sub}';
bar();
$ENV{PERL5DB} = undef;
3 Answers

You can just set $DB::frame directly from within your code:

#!/usr/bin/env perl

foo();

sub foo {
    print "Hello world\n";
    $DB::frame = 1;
    bar();
    $DB::frame = 0;
    return;
}

sub bar {
    print "Just another Perl hacker\n";
    return;
}

This can be run as follows:

$ PERLDB_OPTS=NonStop perl -d try.pl
Hello world
   entering main::bar
Just another Perl hacker

That's it.


If you wanted to do it manually and define your own custom debugger, you could do it as follows:

#!/usr/bin/env perl

foo();

sub foo {
    print "Hello world\n";
    $DB::xtrace = 1;
    bar();
    $DB::xtrace = 0;
    return;
}

sub bar {
    print "Just another Perl hacker\n";
    return;
}
$ PERL5DB='BEGIN { package DB; sub DB {} sub sub { print STDERR "    entering $sub\n" if $xtrace; &$sub } }' perl -d try.pl
Hello world
    entering main::bar
Just another Perl hacker

Is this just a question about how to produce a stack trace? You don't need the debugger for that.

use Carp;
sub bar {
    Carp::cluck("entering main::bar");
    ...
}

You would benefit from converting your script to a modulino.

The problem you are experiencing is that foo() is called directly as soon as Perl enters runtime. But this is not the behavior you desire from a debugging perspective.

The purpose of a modulino is to allow the bulk of the code to be treated as an embedded module, and to allow the startup or kickoff code to only run if the script is run directly, but to not be run if the script is instead loaded as a module. By doing this you're able to write a test suite against the modulino code, leaving only minimal kickoff code outside the reach of unit tests.

One of several writeups on this topic exists here: https://perlmaven.com/modulino-both-script-and-module

For your purposes, the modulino lets you be more selective about what you run with the Perl debugger.

If you structured your code as follows, you would be more successful:

package MyModulino;
use parent 'Exporter';
our @EXPORT = qw(foo bar);

sub foo {
    print "Hello world\n";
    # enable debugger from this moment with PERLDB_OPTS='NonStop frame=1'
    bar();
    # disable debugger from this moment
    return;
}

sub bar {
    print "Just another Perl hacker\n";
    return;
}

package main;

if(!caller) {
    MyModulino->import();
    foo();
}

Now you can do this:

perl -I/some/path/to/script -e 'do "scriptname"; MyModulino::bar()'

In other words, you can now treat scriptname, whatever it is, as a module with a package named MyModulino, and you can call bar() directly rather than relying on foo() to call it. The call to foo() only happens if caller() returns false, which it will do if you run the program directly, but will not do if you invoke it as a module.

By treating scriptname as a module, the foo() call doesn't run implicitly, and your debugger code can invoke bar() from the MyModulino package.

This one liner should drop you into the debugger without having called foo().

perl -d -I/path/to/script -e 'do "scriptname"; MyModulino::bar()'

You can combine this technique along with Test::MockModule to mock the portions of the call stack you don't care about when testing bar() also. But when you start going down that path you really should invest the time to implement these strategies in an automated test script. The 'modulino' advice still applies in that case.

Related