Devel::Trepan Debugger evaluation of Perl statements

In the last blog, I started describing features that are new in Devel::Trepan that aren't in perl5db. Here I will continue with one more: the evaluation aspects of the debugger REPL (read, eval, and print loop).

By default, when you type something that isn't a debugger command the debugger just tries to evaluate the string you gave it. If what you typed was really a mistyped debugger command you might get a weird error message. For example:


(trepanpl): stp
Unquoted string "stp" may clash with future reserved word ...

If you don't want this behavior, you can turn off "auto eval". And here is what happens then:

(trepanpl): set auto eval off(trepanpl): stp
** Undefined command: "stp". Try "help".

By the way, bold and underline really is what you get in the debugger if the terminal you are using supports underlining and bold.

Now let's go back and type some Perl commands to be evaluated and let us see what happens.

$ trepan.pl gcd.pl 3 5
-- main::(gcd.pl:18)
die sprintf "Need two integer arguments, got %d", scalar(@ARGV) unless 
     @ARGV == 2
(trepanpl): $0
$DB::D[1] = gcd.pl
 
(trepanpl): @ARGV
$DB::D[2] =
@{[
    [0] 3,
    [1] 5
]}

In the above there is again a little bit to explain. First, As mentioned in the first blog, the code itself is colorized. Second, notice the format of the output in showing the array @ARGV. This is comes from the module Data::Printer, because I happened to have this installed.

Because Data::Printer pulls in a number of dependencies and Devel::Trepan should work in somewhat sparse environments, that module is not a required dependency. Instead Data::Dumper is a dependency and Data::Dumper does not depend on anything. We can also use Data::Dumper::Perltidy if that is installed. Originally that was required instead of Data::Dumper even though it pulls in other modules. However I backed away from that when it learned that on some systems there is a bug that can cause it not to be installed.

To recap. The order of preference that Devel::Trepan uses is first Data::Printer. Failing that, Data::Dumper::Perltidy; and failing that, Data::Dumper which should be around because it is a build dependency. You can specify which type of print output you want using the set display eval debugger command.

Another thing to notice is that the results of evaluation is pushed onto the array @DB::D.

Next, notice that the debugger changed $0 from what it was initially, trepan.pl, to what it would have been if you hadn't been debugging the program, gcd.pl. And thanks to the perlmonks, __FILE__ and __LINE__ are now also handled properly.

The last item worth mentioning is to note that the debugger figured out that @ARGV was an array while $0 is a scalar. How did it get the evaluation context correct? In this case it was simple: it looked at the first non-blank character of what you wanted to evaluate.

In more complex situations such as when you evaluate a function, the debugger's eval code might not be able to figure out whether a scalar context or an array context was intended. In those cases, it is pretty easy to tell the debugger what you mean. Just use the debugger aliases for the eval command @ or $. Here is an example:

(trepanpl): grep {/\d+/} @ARGV # default context is scalar
$DB::D[3] = 2
(trepanpl): $ grep {/\d+/} @ARGV
$DB::D[4] = 2
(trepanpl): @ grep {/\d+/} @ARGV
$DB::D[5] =
@{[
    [0] 3,
    [1] 5
]}

If you were astute, you might have noticed those two aliases in the list of aliases shown in the last blog where I described a little about debugger command aliases.

Some debugger commands such as eval use suffixes on alias names to modify their behavior a little. Another example of this is quit or kill which have aliases with an exclamation point at the end. When the exclamation point is added, these debugger commands don't prompt for confirmation, although by default they normally they would. That is, while quit asks you if you are sure you want to quit (as gdb does), quit! or q! doesn't prompt.

But back to eval. The at sign @ is an alias for the debugger command eval; eval@ is another longer alias for the same thing.

Now that we've been exposed to the debugger command eval, a little more about it. Giving it a string to evaluate ultimately runs Perl's eval(). So in the example above, rather than type "$0", I could have typed the longer "eval $0", or "eval$ $0" and I would have gotten the same results. Similarly for "@ARGV", "@ @ARGV", or "eval@ @ARGV".

But eval has a couple more tricks up its sleeve! Often when stopped inside the debugger you want to see what the upcomming command is going to do. For that, type "eval" without supplying a string. The eval command will then use as its string to evaluate the current source line of the stopped Perl program.

Here is an example that shows that:

$ trepan.pl /tmp/foo.pl
-- main::(/tmp/foo.pl:2)

(trepanpl): list
/tmp/foo.pl [1-5]
  1    	use File::Basename;
  2  ->	$x = basename(__FILE__);
  3    	if ($x =~ /foo.pl/) {
  4    	    print "Fooish here\n";
  5    	}
(trepanpl): $x
$DB::D[1] = 
(trepanpl): eval
$DB::D[1] = "foo.pl"
(trepanpl): $x
$DB::D[2] = foo.pl
In the above, we use the debugger commands, list which gives a source code listing of a small number of lines. Notice that the eval command ran the command and set the variable $x.

But wait, there's more! Sometimes the line you are stopped on isn't a full statement but has a part of it that makes sense to evaluate. For example it might be part of an if statement. Here what you often want to see is what the expression part in the if statement is. For that, use eval?:

-- main::(/tmp/foo.pl:3)
if ($x =~ /foo.pl/) {
(trepanpl): eval?
eval: $x =~ /foo.pl/
$DB::D[3] = 1

Notice in the above the debugger echoes the part of the string in the line of code that it is using so you know which part it picked up.

See the help text for the eval command for more details of what happens when the question-mark suffix is used in an alias. See also an eariler wiki entry where some this material is presented a little bit differently.

Given all of the care above in the evaluation part, you might think people would be happy with this. My own experience is that people still sometimes want to go into a real Devel::REPL shell. If you have that module installed, there is a plugin package called Devel::Trepan::Shell which adds a shell command and alias re.pl.

Leave a comment

About rockyb

user-pic I blog about Perl.