Your skill will accomplish
what the force of many cannot
Using the debugger's R command to debug "hard" problemsby pemungkah (Priest)
|on Jun 07, 2012 at 22:18 UTC||Need Help??|
The problemIf 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 commandReading 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 TechniqueTo 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.
ExampleHere'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.
Starting up the debugger:
Load the module you want to set the breakpoint in:
Print its @INC entry to figure out what file you just loaded (needed to do the 'f' command):
Switch to that file with 'f':
We want to break in new(), so find it:
Find the first executable statement in the sub via 'l':
Set a breakpoint at this line:
Restart the debugger:
Run the program, and you break at the point you wanted.
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.