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

p $x

A joined / formatted expression

p join ',', sort keys %h

A hash or array

x \%h, x \@a

An object’s fields

x $obj

Only the top level

x 1 $deep_tree

Package variables of Pkg

V Pkg

Lexicals of current sub

y 0

Method list of $obj

m $obj

@ISA chain of $obj

i $obj

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

dumpDepth

Recursion depth (negative = unlimited). Default 4.

arrayDepth

First N array elements only; empty = all.

hashDepth

Same, for hashes.

compactDump

Short arrays on one line.

veryCompact

Tighter yet.

globPrint

Dump glob contents.

DumpReused

Fully expand repeated references (beware cycles).

quote

" or ' or auto (default).

undefPrint

How undef renders.

bareStringify

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.