Inspecting state#
This chapter covers the debugger’s data-inspection vocabulary:
x, V, X, y, m, M, i, and the options that control
how deeply aggregate values are dumped. After this chapter you
can ask any running program “what does this variable, this
package, this object actually look like right now?”
Readers arrive here knowing p EXPR prints a scalar. The rest of
the inspection commands earn their keep the moment the answer
“what is this?” requires more than a single value.
x: dump structures#
x EXPR pretty-prints in list context, recursing into references:
DB<1> x \%config
0 HASH(0x5651f9a82358)
'debug' => 1
'dsn' => 'dbi:SQLite::memory:'
'timeout' => 30
'users' => ARRAY(0x5651f9b28a10)
0 'alice'
1 'bob'
2 'carol'
Always pass a reference for aggregates. x %h dumps the flat
key/value list, which hides the structure. x \%h does what you
want.
DB<2> x \@list
0 ARRAY(0x5651f9b28a10)
0 'alice'
1 'bob'
2 'carol'
DB<3> x $obj
0 MY::Class=HASH(0x5651fa01c2b0)
'id' => 42
'prefs' => HASH(0x5651fa01c330)
'theme' => 'dark'
For deep or cyclic structures, cap the recursion:
DB<4> x 2 \%tree # recurse 2 levels deep
DB<5> x 1 $obj # one level
If the first token of the expression is numeric, parenthesise:
DB<6> x (3 + $offset) # without parens, 3 is mis-parsed as depth
V and X: package variables#
V [PKG [VARS]] dumps the globals of a package:
DB<1> V main
$counter = 42
@ARGV = (
0 '--verbose'
1 'input.txt'
)
%ENV = (
'PATH' => '/usr/bin:/bin'
...
)
With a second argument, filter variables:
DB<2> V MyApp $config $last_user # only these
DB<3> V MyApp ~^config # regex: names matching /^config/
DB<4> V MyApp !^_ # regex: names NOT matching /^_/
X is V for the current package:
DB<5> X # dump current package's globals
DB<6> X $foo @bar # filtered
V / X do not see lexicals (my variables). For those,
use y.
y: lexicals in enclosing scope#
y LEVEL [VARS] reads lexical my variables via PadWalker:
DB<1> y 0 # lexicals of the current sub
my $req = HTTP::Request=HASH(0x...)
my $user_id = 42
my @errors = ()
DB<2> y 1 # one frame up (the caller's lexicals)
my $request_count = 118
my $dispatcher = MyApp::Dispatcher=HASH(0x...)
DB<3> y 0 $req $user_id # only those
y requires PadWalker version 0.08 or later. It works only
inside a sub (the outer script body has no pad to walk).
m: methods of an object or class#
m EXPR lists methods callable on an object or class:
DB<1> m $user
via UNIVERSAL: isa can DOES VERSION DESTROY
via MyApp::Model: save load delete update
via MyApp::Role::Auditable: audit_log record_event
DB<2> m 'MyApp::Worker'
via UNIVERSAL: ...
via MyApp::Worker: dispatch retry cancel
Groups methods by the package that provides them — useful for spotting which role or superclass a method comes from.
M and i: modules and inheritance#
M lists loaded modules with versions:
DB<1> M
'DBI' (version 1.643)
'JSON::PP' (version 4.16)
'MyApp::Model' (version undef)
...
i CLASS or i $obj prints the inheritance tree:
DB<2> i $user
MyApp::User 0.12
MyApp::Model 0.42
Moo::Object ?
Useful for “where is this method actually coming from?” when method resolution crosses several roles.
S: subroutine names#
S [[!]PAT] lists sub names matching (or not matching) a
pattern:
DB<1> S ^MyApp::Worker::
MyApp::Worker::cancel
MyApp::Worker::dispatch
MyApp::Worker::retry
DB<2> S !^(main|DB|UNIVERSAL):: # everything except these
S alone lists everything — long.
p vs x: which to use when#
Want |
Use |
|---|---|
A single scalar’s value, plain |
|
A joined / formatted expression |
|
A hash or array |
|
An object’s fields |
|
Only the top level |
|
Package variables of |
|
Lexicals of current sub |
|
Method list of |
|
|
|
Special case: p is a flat print. For a reference, p $ref
prints HASH(0x…). Use x $ref for the contents.
Depth and compactness options#
Dump output is shaped by options set via the o command. The
relevant settings for inspection:
Option |
Effect |
|---|---|
|
Recursion depth (negative = unlimited). Default 4. |
|
First N array elements only; empty = all. |
|
Same, for hashes. |
|
Short arrays on one line. |
|
Tighter yet. |
|
Dump glob contents. |
|
Fully expand repeated references (beware cycles). |
|
|
|
How |
|
Print overload-stringified form. |
Set an option in a session:
DB<1> o dumpDepth=6
DB<2> o compactDump=1
DB<3> o arrayDepth=20
DB<4> o compactDump? # query current value
Set options at startup via PERLDB_OPTS or in .perldb:
PERLDB_OPTS="dumpDepth=6 compactDump=1" perl -d script.pl
Inspecting SV internals#
Devel::Peek exposes the scalar’s internal storage — type flags,
refcount, PV/NV/IV layout, magic chain — when the question is
“what kind of scalar is this really?”:
DB<1> use Devel::Peek; Dump($sv)
Primarily for investigating tied variables, overloaded objects, or performance anomalies where the answer depends on whether a scalar is currently stored as an integer, a string, or both.
Note: Devel::Peek in pperl is a compat layer — it reports a
pperl-specific Sv layout rather than perl5’s SV layout. Fields
like REFCNT, FLAGS, PV, IV, NV have equivalents; the
exact output differs. See the pperl-architecture
guide for layout details.
Evaluating at the prompt#
Anything unrecognised as a command is evaluated as Perl. Use it to drill deeper than a single command:
DB<1> p keys %{ $obj->{prefs} }
theme,locale,tz
DB<2> x [ grep { !defined $_->{parent} } @nodes ]
DB<3> x { map { $_->id => $_->name } @users }
For one-off assertions without stopping:
DB<1> die "unexpected" if ref $obj ne 'MyApp::User'
Program I/O vs debugger I/O#
The debugger’s own input and output go through $DB::IN and
$DB::OUT, which are opened to /dev/tty directly. The program
can redirect STDIN / STDOUT / STDERR freely; the debugger prompt
still shows up. Conversely, p EXPR output does not contaminate
a program’s captured stdout.
Find out more#
breakpoints — stop where you can inspect; this chapter is about what to look at once stopped.
tracing — watch values change over time without stopping at every step.