# Tracing: watching execution flow This chapter covers traces that record what a program did without stopping it: the debugger's `t` command and trace options, the `Devel::Trace` module family, and the regex-engine tracer. Use tracing when the bug is "which code path actually ran?" rather than "what value does X have at line N?". Readers coming in know how to set breakpoints and inspect state. A trace is the complement — "follow every step and tell me about it" rather than "stop at one interesting place". ## `t`: trace inside the debugger `t` toggles per-statement tracing. Every breakable line prints a location marker before executing: ``` DB<1> t Trace = on DB<2> c main::(app.pl:5): my $key = 'welcome'; main::(app.pl:6): my %data = ( main::handle_request(app.pl:42): my $user = load_user($req); main::load_user(app.pl:87): my ($req) = @_; main::load_user(app.pl:88): return unless $req; ... ``` Restricting the depth: ``` DB<3> t 2 # toggle with maximum sub-depth 2 DB<4> t 0 my_work() # trace one expression only ``` The `frame` option controls what the trace prints at sub calls and returns: | Bit | Flag | Effect | |-----|--------|------------------------------------------------------| | 1 | enter | Print on sub entry. | | 2 | exit | Print on sub exit. | | 4 | ctx+args | Include call context and arguments. | | 8 | tied/ref | Mark tied-variable and reference sub calls. | | 16 | return | Print the return value. | Set `frame` via `o`: ``` DB<5> o frame=15 # enter + exit + ctx/args + tied/ref ``` Common combinations: - `frame=2`: just entry/exit traces, one line per call — the call-tree view. - `frame=6`: add caller context and argument dump — full trace. - `frame=30`: like `frame=6` plus return values. Trace output goes to the debugger's output stream. Redirect it to a file so it does not drown the interactive session: ``` DB<6> o LineInfo=trace.out ``` Read the file in another terminal: ``` tail -f trace.out ``` ## Non-interactive tracing: `NonStop` + `AutoTrace` For "record a full execution trace with no interaction", combine `NonStop=1` (no prompts) and `AutoTrace=1` (tracing on from start): ``` PERLDB_OPTS="NonStop=1 AutoTrace=1 frame=2 LineInfo=trace.out" \ perl -d script.pl ``` The program runs straight through; `trace.out` captures every breakable line and every sub entry. Ideal for CI-mode failure capture, headless servers, or any environment where a TTY prompt is unavailable. Add `inhibit_exit=0` to let the program fall off the end without the "Debugged program terminated" prompt. ## `Devel::Trace`: every line, without the debugger `Devel::Trace` traces every line of every file to STDERR. No command-line interaction. No breakpoints. Simple and loud: ``` perl -d:Trace script.pl 2> trace.out ``` ``` >> app.pl:4: my $key = 'welcome'; >> app.pl:5: my %data = ( >> app.pl:11: print "$data{$key}\n"; ``` Run it once, grep the output for the puzzle. Output size is proportional to program run length — use on a minimal reproducer, not the whole test suite. Variants: - `Devel::DumpTrace` — same plus per-line variable values. - `Devel::TraceUse` — "which modules were loaded, in what order, by whom": ``` perl -d:TraceUse script.pl ``` - `Devel::TraceINC` — narrower: just the `require` chain with timing. - `Devel::Trace::Subs` — call-flow and stack-trace generator for "which subs actually ran". ## Tracing a specific region The `$DB::trace` variable toggles tracing from Perl code: ```perl $DB::trace = 1; interesting_region(); $DB::trace = 0; ``` Assigning to `$DB::trace` when not under `-d` autovivifies the variable harmlessly — the code is safe to commit and runs as a no-op outside the debugger. Same pattern for single-stepping a region: ```perl $DB::single = 1; # break here when next statement runs under -d ``` ## Regex tracing: `use re 'debug'` The regex engine has its own tracer. Enable lexically inside a region, or on the command line: ```perl { use re 'debug'; $str =~ /(\w+)\s+(\w+)/; } ``` ``` perl -Mre=debug -e '"hello world" =~ /(\w+)\s+(\w+)/' ``` Compile-phase output shows what the optimiser did: - `Compiling REx 'PATTERN'` — the source pattern. - `size N` — internal node-table size. - `anchored 'SUB' at K` / `floating 'SUB' at K..LIMIT` — which substring the engine will look for as a pre-filter. - `stclass TYPE` — the starting-character class. - `minlen N` — no match possible on input shorter than N. - Node listing — the compiled program. Runtime output shows each step: ``` POS | NODE_NUM: OPNAME ``` Indentation reflects backtracking depth; `failed...` and `failed, try continuation...` mark backtracks; `Match successful!` / `Match failed.` close the trace. If no runtime output appears at all, the optimiser decided the match without entering the engine — either `minlen` pre-filtered or the pattern was folded to a constant test. pperl's regex engine is a native reimplementation, not a port of perl5's. The trace is functionally equivalent but the exact node names and field formats are not byte-identical to upstream. Parsing the trace programmatically (few tools do) will need adjustment. Partial switches narrow the output: ```perl use re 'debugcolor'; # ANSI colour use re debug => 'EXECUTE'; # runtime only, no compile output ``` ## `Devel::TraceUse` for module-load bugs A program that runs slowly at startup, or a "why is this module loaded at all?", is almost always a module-graph question. `Devel::TraceUse` prints the full `use`/`require` tree: ``` perl -d:TraceUse script.pl Modules used from script.pl: 1. strict 1.12, script.pl line 2 [main] 2. warnings 1.58, script.pl line 2 [main] 3. DBI 1.643, script.pl line 5 [main] 4. Carp 1.52, DBI line 13 [DBI] 5. DynaLoader 1.47, DBI line 14 [DBI] ... ``` Indentation shows the dependency chain. Cross-reference with the line number in the parent file to see where each load happens. ## Combining traces with the debugger Trace to a file, step interactively: ``` PERLDB_OPTS="frame=6 LineInfo=trace.out" perl -d app.pl ``` The debugger prompt appears normally; `trace.out` accumulates the call-tree as you step and continue. Useful when a call chain is too deep to follow by stepping but you still want to stop at specific points to inspect. ## Find out more - [breakpoints](breakpoints) — stopping at a specific point rather than recording every step. - [inspecting-state](inspecting-state) — the commands that answer "what is this value?".