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.