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

perl -d script.pl args

Debug the given script.

perl -d -e 'CODE'

Debug a one-liner expression.

perl -de 0

Interactive REPL sandbox.

perl -d:Module script.pl

Load Devel::Module as debugger.

perl -d:NYTProf script

Profile via NYTProf plugin.

perl -S -d script.pl

Search $PATH for the script.

-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

v

View a window around the current line.

v LINE

View a window around LINE.

l

List the next window; repeat advances.

l LINE

List just LINE.

l MIN-MAX

List a range.

l SUB

List the first window of SUB.

-

Previous window.

.

Snap view back to the current line.

f FILE

Switch display to FILE (regex against %INC).

/PAT/

Forward search in source.

?PAT?

Backward search.

Motion: s, n, Enter, c, r#

The four essential motion commands:

Command

Effect

s

Step — execute next statement; descend into subroutines.

n

Next — execute next statement; step over subroutines.

<Enter>

Repeat the last s or n.

c

Continue until next breakpoint or end.

c LINE

Continue to a one-shot breakpoint at LINE, then stop.

c SUB

Continue to the first line of SUB.

r

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 the maxTraceLen option.

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

rerun

Restart, replaying no prior commands.

rerun -N

Restart and replay up to N commands back.

rerun N

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

h, h CMD, h h

Help: summary / one command / full.

|CMD

Pipe the output through $ENV{PAGER}.

q

Quit.

v, v LINE

View source around current / LINE.

l, l LINE, l SUB, l MIN-MAX

List.

.

Snap to current line.

-

Previous window.

f FILE

Switch display to FILE.

/PAT/, ?PAT?

Search forward / backward.

s

Step into.

n

Step over.

<Enter>

Repeat last s / n.

r

Return from current sub.

c, c LINE, c SUB

Continue / one-shot breakpoint.

p EXPR

Print (scalar context).

x EXPR

Dump (structured, pass a reference).

T

Stack backtrace.

R

Restart via exec().

rerun [N|-N]

Restart with history replay.

Find out more#

  • breakpointsb, conditionals, actions, watches, timing matrix.

  • inspecting-stateV, X, y, m, M, i, depth controls.

  • tracingt, AutoTrace, line-info output to a file.