Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Using the debugger's R command to debug "hard" problems

by pemungkah (Priest)
on Jun 07, 2012 at 22:18 UTC ( #975057=CUFP: print w/ replies, xml ) Need Help??

The problem

If you use the debugger, you've probably noted that you don't get control of the program being debugged until it reaches the first executable line of the main program. If you really wanted to debug a module's import() method or a BEGIN block, this isn't a lot of help.

Since you don't get control in the debugger until after all that has executed, there's no way for you to set a breakpoint in any of that...unless you cheat.

The R command

Reading the documentation for the debugger's R (restart) command, it says
Pure-man-restart of debugger, some of debugger state and command-line options may be lost. Currently the following settings are preserved: history, breakpoints and actions, debugger Options and the following command-line options: -w, -I, -e.
Note that the breakpoints are preserved. The debugger will happily let you set a breakpoint in already-executed code; it doesn't care that the code has been run at all. This means that we can combine the two to set a the breakpoint in the code that already executed, then restart the program from the beginning. This will cause it to stop executing and drop into the debugger at the first breakpoint it finds, which will be in that code that executes before the first line of the main program.

The Technique

To take advantage of this, we need to be able to locate the place we want to set the breakpoint efficiently. The easiest way to do this is to take advantage of the debugger's f command, which allows you to switch the file that the debugger is "viewing", and %INC, which maps module names (almost) to the files they were loaded from.

First, let's see how to find the file a module came from. If we list the keys of %INC, we'll see that the keys are in the form 'Some/Module/Name.pm', which maps back to Some::Module::Name. The value associated with that key will be file actual filename that the module was loaded from - and that's the argument we need to hand to the f command to change the file that the debugger is currently "looking" at.

Once we have that filename and have done f /home/user/Some/Module/Name.pm, we can then use the debugger's commands that let us look through the file - l to list the lines, and /string to search for a string. This lets us zero in on the code where we want to set breakpoints. Note that we can always l 1 to start at the top of the file, letting us find BEGIN, import(), and so on.

Example

Here's a contrived example. For this sample program, we want to break when we reach new() in LWP::UserAgent. Obviously, we could step through until we reached it, but let's use a combination of the R command and the f command to quickly find the place to set the breakpoint, and then R and c to jump right to that point.
#/usr/bin/perl -d use strict; use warnings; use WWW::Mechanize; my $agent = WWW::Mechanize->new();
Starting up the debugger:
bash$ perl -d sample.pl --whatever-arguments Loading DB routines from perl5db.pl version 1.37 Editor support available. Enter h or 'h h' for help, or 'man perldebug' for more help. main::(use_mech.pl:7): my $foo = WWW::Mechanize::Pluggable->new();
Load the module you want to set the breakpoint in:
DB<1> use LWP::UserAgent
Print its @INC entry to figure out what file you just loaded (needed to do the 'f' command):
DB<2> p $INC{"LWP/UserAgent.pm"} /Users/joemcmahon/perl5/perlbrew/perls/perl-5.16.0/lib/site_perl/5.16. +0/LWP/UserAgent.pm
Switch to that file with 'f':
DB<3> f /Users/joemcmahon/perl5/perlbrew/perls/perl-5.16.0/lib/site_ +perl/5.16.0/LWP/UserAgent.pm 1 package LWP::UserAgent; 2 3: use strict; 4: use vars qw(@ISA $VERSION); 5 6: require LWP::MemberMixin; 7: @ISA = qw(LWP::MemberMixin); 8: $VERSION = "6.04"; 9 10: use HTTP::Request ();
We want to break in new(), so find it:
DB<4> /new 20: sub new
Find the first executable statement in the sub via 'l':
DB<5> l 20 sub new 21 { 22 # Check for common user mistake 23: Carp::croak("Options to LWP::UserAgent should be key/value pai +rs, not hash reference") 24 if ref($_[1]) eq 'HASH'; 25 26: my($class, %cnf) = @_; 27 28: my $agent = delete $cnf{agent}; 29: my $from = delete $cnf{from};
Set a breakpoint at this line:
DB<5> b 23
Restart the debugger:
DB<6> R Warning: some settings and command-line options may be lost! Loading DB routines from perl5db.pl version 1.37 Editor support available. Enter h or 'h h' for help, or 'man perldebug' for more help. main::(use_mech.pl:7): my $foo = WWW::Mechanize::Pluggable->new();
Run the program, and you break at the point you wanted.
DB<6> c LWP::UserAgent::new(/Users/joemcmahon/perl5/perlbrew/perls/perl-5.16.0 +/lib/site_perl/5.16.0/LWP/UserAgent.pm:23): 23: Carp::croak("Options to LWP::UserAgent should be key/value pai +rs, not hash reference") 24: if ref($_[1]) eq 'HASH'; DB<6>
This technique is particularly handy for debugging import() problems and Attribute::Handlers code; you can load the module that's having trouble. set a breakpoint in its import(), and then restart via R. The debugger will stop in import() for the module you're interested in.

And of course, it's handy to retry your program when you've painstakingly walked through debugging something and then stepped too far past it. As long as your program hasn't actually terminated, you can use this trick, possibly deleting some no-longer-needed breakpoints and setting one in the too-late-it-already-ran code. This will work as long as your program hasn't actually terminated; if it has, you can't retroactively set more breakpoints - you'll have to restart, find the place to set them via looking at %INC and the f command, then restart again.

Comment on Using the debugger's R command to debug "hard" problems
Select or Download Code
Re: Using the debugger's R command to debug "hard" problems (l,b w/ ::)
by tye (Cardinal) on Jun 08, 2012 at 01:15 UTC

    Use "l LWP::UserAgent::new" and/or "b LWP::UserAgent::new" to save yourself a lot of typing:

    % perl -del Loading DB routines from perl5db.pl version 1.33 ... DB<1> use LWP::UserAgent DB<2> l LWP::UserAgent::new Switching to file '.../lib/LWP/UserAgent.pm'. 31 { 32 # Check for common user mistake 33: Carp::croak("Options to LWP::UserAgent should be key/value + pairs, no t hash reference") 34 if ref($_[1]) eq 'HASH'; 35 36: my($class, %cnf) = @_; 37 38: my $agent = delete $cnf{agent}; 39: my $from = delete $cnf{from}; 40: my $def_headers = delete $cnf{default_headers}; DB<3> l 30 30 sub new DB<4> b LWP::UserAgent::new DB<5> L .../lib/LWP/UserAgent.pm: 33: Carp::croak("Options to LWP::UserAgent should be key/value + pairs, not hash reference") break if (1) DB<6>

    - tye        

      Yep, totally agree. This was a contrived example - I should have used something that really did have an import() so I could demo the stop in import().
Re: Using the debugger's R command to debug "hard" problems (.perldb)
by tye (Cardinal) on Jun 08, 2012 at 02:16 UTC

    I think your 'R' usage is quite handy. Here's another approach that can sometimes be useful:

    % echo 'sub afterinit { $DB::single = 1 }' >> .perldb

    That changes the Perl debugger's default to instead stop before running anything. See:

    % cat .perldb sub afterinit { $DB::single = 1 } % cat debugMod.pl use LWP::UserAgent; warn "Done"; % perl -d debugMod.pl Loading DB routines from perl5db.pl version 1.33 ... main::CODE(0x43050c)(debugMod.pl:1): 1: use LWP::UserAgent; DB<1> s LWP::UserAgent::CODE(0x26c274c)(.../lib/LWP/UserAgent.pm:3): 3: use strict; DB<1> T $ = LWP::UserAgent::BEGIN() called from file `.../lib/LWP/UserAgent.pm +' line 3 $ = eval {...} called from file `.../lib/LWP/UserAgent.pm' line 3 $ = require 'LWP/UserAgent.pm' called from file `debugMod.pl' line 1 $ = main::BEGIN() called from ... $ = eval {...} called from ... DB<1>

    Skipping over the loading of modules can be a pain, so I wouldn't recommend leaving this particular .perldb in place most of the time.

    (FYI, I couldn't get this to work under Windows w/o first fixing some bugs in perl5db.pl or else I'd show how the file has to be named perldb.ini and other required differences there.)

    - tye        

Reaped: Re: Using the debugger's R command to debug "hard" problems
by NodeReaper (Curate) on Jun 10, 2012 at 13:03 UTC
Re: Using the debugger's R command to debug "hard" problems
by patcat88 (Deacon) on Jun 11, 2012 at 17:24 UTC
    Too complicated.
    BEGIN{ $DB::single = 1;} use Anything qw( importme ); print "JAPH";
    Now you can use the debugger in the require part and import part of Anything module.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: CUFP [id://975057]
Approved by ww
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (8)
As of 2014-12-27 14:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (177 votes), past polls