RISC OS Build EnvironmentROBE

Appendix58Appendix X: Pyromaniac tracing

Overview

Pyromaniac includes a detailed tracing and diagnostics system which can report instruction execution, SWI arguments, function entry, recent blocks, exception state, watchpoints, and tracepoints. This appendix describes the structure of that output so that it can be used as a primary reference when diagnosing runtime problems.

Trace output reference

Enabling tracing

Tracing is usually enabled with --debug during initialisation, --boot-debug after boot, or within the running system through the PyromaniacDebug command. The debug names can be listed with --list-debug. Multiple debug names may be combined in a single invocation by separating them with commas, for example --debug trace,traceswiargs.

Common trace-related names include:

Debug nameMeaning
traceStep-by-step instruction tracing, optionally with disassembly support.
traceswiShow SWI execution with register state.
traceswiargsDecode SWI argument registers using known SWI definitions.
traceregionfuncDetect entry to functions and regions and report them before execution.
startTrace internal startup and command-line processing.
executeTrace entry and exit conditions for ARM code execution.

Instruction trace structure

An instruction trace line shows the current processor mode, stack nesting level, program counter, disassembled instruction, operands, and any decoded source-register state that can be interpreted before the instruction executes.

The main fields are:

FieldMeaning
Mode markerThe current processor mode, such as S for SVC, blank for USR, or A for abort mode.
Stack barsA visual indicator of nesting depth, which grows as stack frames accumulate and shrinks as they are removed.
PCThe execution address currently being logged.
InstructionThe mnemonic for the instruction. Some forms may be normalised, for example PUSH and POP for stack instructions.
ArgumentsThe operands for the instruction, including any decoded constants or addresses.
StateA decoding of source register content, which may include strings, blocks, decimal values, or character interpretations where they are recognised.

When a SWI is traced, the instruction normally appears once as disassembly and again as an actual SWI call with decoded entry and exit registers. Known SWIs may show only the registers that matter, together with decoded strings or blocks where those values are recognised.

Exception reports

When an exception occurs, Pyromaniac emits an exception report containing the exception type, general-purpose registers, stack pointer, link register, program counter, and processor status flags. When the processor is in a privileged mode, the saved program status register may also be shown.

The CPSR value is shown both as a raw hexadecimal word and as decoded mode, instruction-set, interrupt-mask, and condition-flag states. Lower-case flag letters indicate a clear flag and upper-case letters a set flag.

If instruction stepping is not enabled, but block-level tracing is active, the exception report may also include the recently executed blocks of code that led to the failure, with the last block being the one known to contain the failing instruction.

Function and region entry tracing

With traceregionfunc, Pyromaniac recognises entry into module regions and C functions and prints a region banner before instruction execution continues. Where available, the trace can name the module, command entry, function name, and the current register set on entry.

This is particularly useful when following CMHG veneers into C code, because the log can show both the module command entry and the later named function that the veneer branches to.

Repeated blocks

Repeated execution of the same basic block can make a trace huge and expensive to render. Pyromaniac therefore elides repeated blocks after a threshold and replaces the repeated instruction listing with a single REPEAT line giving the address range and the number of further executions.

The threshold is a fixed 16 consecutive executions of the exact same block (the same start address and size, immediately following each other with nothing else in between); this is not currently configurable. The first 16 executions are shown normally; from the 17th execution onward they are suppressed, and when the loop finally moves to a different block, a single REPEAT line is printed reporting how many further executions beyond the first 16 were suppressed.

Eliding only happens when block-level tracking is active: enabling trace on its own does not elide anything, even for a tight single-instruction spin loop executed hundreds of times. One of traceregion, traceregionfunc, or traceblockregs must also be enabled alongside trace for elision to take effect. A worked example, tracing a small loop that counts down from 25 to 0 before exiting:

pyro --debug trace,traceregion loop,ff8
8080: MOV r0, #&19 ; #25
8084: SUBS r0, r0, #1 ; R0 = &00000019
8088: BNE &00008084
8084: SUBS r0, r0, #1 ; R0 = &00000018
8088: BNE &00008084
...
8084: SUBS r0, r0, #1 ; R0 = &0000000a
8088: BNE &00008084
808c: REPEAT &00008084 - &00008088 9 times
808c: SWI OS_Exit

Here the loop body (the two-instruction block at &8084 to &8088) executes 25 times in total. The first 16 executions (counting down from 25 to 10) are shown individually; the remaining 9 executions (counting down from 9 to 1) are suppressed and reported together once the loop exits to the following instruction at &808c, giving 9 times in the REPEAT line. This matches 25 - 16 = 9: the count reported is always the number of executions beyond the first 16 shown, not the total repeat count.

This keeps loop-heavy traces readable while still indicating that a block was executed many times.

Watchpoints

Watchpoints trap reads or writes to selected addresses or watched regions. When triggered they report the access type, address, value, data width, and the current registers. They can be used even when full instruction stepping is not enabled, which makes them useful for catching corruption with less tracing overhead.

Dynamic-area heap administration and guard regions can also be placed under watchpoint control, which helps detect overwrites and underwrites of heap metadata and buffer guards.

Tracepoints

Tracepoints trap execution at selected addresses and report the register set, decoded code locations, a lightweight C backtrace where one can be inferred, and the recently executed code blocks that led to the point of interest.

They are useful when a specific function or address is suspected but full instruction tracing would be too noisy or too slow.

The backtrace is inferred purely from the APCS frame-pointer chain (the stack-limit, stack-pointer, and frame-pointer registers), not from debug symbols. If the current register state does not look like a valid APCS frame (for example, code built without frame pointers, or execution in a privileged mode the checker does not recognise), no backtrace can be inferred at all, and the C backtrace: heading and its lines are omitted entirely from the report rather than shown empty.

Trace workflows

Starting a traced command run

This pattern enables instruction tracing together with decoded SWI arguments.

pyro --debug trace,traceswiargs --command "Help Modules"

Tracing function and region entry

This is useful when you want a lighter trace that still names entered functions and modules.

pyro --debug traceregionfunc --command "*MyCommand"