The interactive debugger#
This chapter covers perl -d: launching a program under the
debugger, the prompt, the essential motion and inspection
commands, and the stack-navigation vocabulary. After this chapter
you can investigate any running program interactively.
Readers start from knowing print / warn work but are not
enough: you need to stop, look around, peek at variables, walk the
call stack, then resume. That is what this chapter delivers.
Launching#
For any script app.pl, prepend -d:
perl -d app.pl --verbose input.txt
Arguments after the script name pass to the script as normal.
Common launch forms:
Form |
Purpose |
|---|---|
|
Debug the given script. |
|
Debug a one-liner expression. |
|
Interactive REPL sandbox. |
|
Load |
|
Profile via NYTProf plugin. |
|
Search |
-d causes the compiler to emit per-statement hook points; the
interpreter preloads perl5db.pl before the first runtime line.
Never pipe input to perl -d and never use /dev/stdin — the
debugger needs a TTY to read commands from. Launch from a real
terminal.
The prompt#
Loading DB routines from perl5db.pl version 1.77
Editor support available.
Enter h or 'h h' for help, or 'man perldebug' for more help.
main::(app.pl:4): my $key = 'welcome';
DB<1>
The line main::(app.pl:4): shows the next line about to
execute — nothing in the program has run yet. Compile-time code
(BEGIN, use) already ran; the first prompt comes before the
first runtime statement.
DB<1> — the number is a command counter that increments on each
command. !N recalls command number N. DB<<N>> with double
brackets means you are inside a nested debugger (one expression
you stepped into called another breakable expression).
The debugger always shows the line about to execute, never the line just executed.
Quitting#
DB<1> q
Not quit, not exit. Typing exit runs Perl’s
exit inside the debugger’s
eval, which is not the same. ^D at the prompt works on most
terminals.
Help#
DB<1> h # one-screen summary
DB<1> h h # full help (long — use the pager)
DB<1> h b # help on one command
DB<1> |h h # pipe full help through $ENV{PAGER}
Where am I?#
v views a window around the current line. l lists a specified
range:
DB<1> v
1 #!/usr/bin/perl
2 use v5.40;
3
4==> my $key = 'welcome';
5: my %data = (
6 'this' => qw(that),
7 'tom' => qw(and jerry),
8 'welcome' => q(Hello World),
9 'zip' => q(welcome),
10 );
==>marks the next line to execute.:marks executable lines — you can set a breakpoint there.Lines without
:are not breakable (comments, blanks, continuations of a multi-line statement).
Navigation:
Command |
Effect |
|---|---|
|
View a window around the current line. |
|
View a window around LINE. |
|
List the next window; repeat advances. |
|
List just LINE. |
|
List a range. |
|
List the first window of SUB. |
|
Previous window. |
|
Snap view back to the current line. |
|
Switch display to FILE (regex against |
|
Forward search in source. |
|
Backward search. |
Motion: s, n, Enter, c, r#
The four essential motion commands:
Command |
Effect |
|---|---|
|
Step — execute next statement; descend into subroutines. |
|
Next — execute next statement; step over subroutines. |
|
Repeat the last |
|
Continue until next breakpoint or end. |
|
Continue to a one-shot breakpoint at LINE, then stop. |
|
Continue to the first line of SUB. |
|
Return from the current sub; stop at the caller. |
DB<1> s # descend into the call
DB<2> n # stay at this level
DB<3> # <Enter> repeats the last motion
DB<4> c 42 # run to line 42 once
DB<5> r # pop the current frame
A multi-line statement steps atomically — one s executes a whole
multi-line hash assignment.
On a line that calls no subroutines, s and n are
indistinguishable.
Printing values: p and x#
DB<1> p $key
welcome
DB<2> p join ',', sort keys %data
this,tom,welcome,zip
p EXPR evaluates EXPR in the program’s current package and
prints the scalar-context result followed by a newline. p is
flat — for a reference it prints HASH(0x...), not contents.
For structures, use x:
DB<3> x \%data
0 HASH(0x5651f9a82358)
'this' => 'that'
'tom' => 'and'
'welcome' => 'jerry'
'zip' => 'Hello World'
Always pass a reference (x \%h, x \@a) — x %h dumps the
flat key/value list, which hides the structure.
See inspecting-state for the full inspection
vocabulary (V, X, y, m, M, i, depth controls).
The call stack: T, s, r#
T prints a backtrace — the chain of sub calls that led to the
current line:
DB<1> T
$ = main::handle_request(ref(HTTP::Request)) called from file 'app.pl' line 47
$ = main::dispatch('GET', '/users/42') called from file 'app.pl' line 19
@ = main::main() called from file 'app.pl' line 12
$— called in scalar context.@— called in list context..— void context.Arguments are shown with
Data::Dumper-style truncation; the exact length cap is themaxTraceLenoption.
Navigate the stack with s (into a call) and r (out of the
current frame). There is no “frame up / frame down” command:
re-step if you go too deep, or use b to set a breakpoint at the
caller and c to reach it.
To inspect an outer frame’s lexicals, use y LEVEL from
inspecting-state — it reads via
PadWalker and shows the enclosing scope’s my variables.
Evaluating Perl at the prompt#
Anything the debugger does not recognise as a command is eval’d
as Perl code in package DB at the current lexical scope:
DB<1> $counter = 0 # mutate state
DB<2> @ARGV = qw(new args) # mutate @ARGV
DB<3> $obj->reset # call a method
DB<4> m/(\w+)/ && p $1 # match against $_
A fresh my $x at the prompt vanishes when the command returns —
the prompt has its own lexical scope. Use a bareword, our, or
chain on one line:
DB<1> our $tmp = compute(); # survives
DB<2> $tmp = compute(); p $tmp # one-line chain
Force Perl interpretation when an expression looks like a debugger command:
DB<1> ; h EXPR # ; prefix forces eval
DB<2> + h EXPR # + prefix works too
Restart and rerun#
R restarts the program via exec(). History, breakpoints,
actions, options, and the command line are preserved:
DB<1> R
rerun re-executes without leaving the debugger, preserving
state:
Form |
Effect |
|---|---|
|
Restart, replaying no prior commands. |
|
Restart and replay up to N commands back. |
|
Restart from history entry N. |
Redefining a sub mid-session#
Paste the entire sub NAME { ... } at the prompt. Or edit a
replacement file and load it:
DB<1> do 'fixed.pl'
Subsequent calls use the new definition. Existing stack frames continue with the old body.
Commands so far#
Command |
Effect |
|---|---|
|
Help: summary / one command / full. |
|
Pipe the output through |
|
Quit. |
|
View source around current / LINE. |
|
List. |
|
Snap to current line. |
|
Previous window. |
|
Switch display to FILE. |
|
Search forward / backward. |
|
Step into. |
|
Step over. |
|
Repeat last |
|
Return from current sub. |
|
Continue / one-shot breakpoint. |
|
Print (scalar context). |
|
Dump (structured, pass a reference). |
|
Stack backtrace. |
|
Restart via |
|
Restart with history replay. |
Find out more#
breakpoints —
b, conditionals, actions, watches, timing matrix.inspecting-state —
V,X,y,m,M,i, depth controls.tracing —
t,AutoTrace, line-info output to a file.