Stacktrace
Explore stack traces
Description
This set of example programs demonstrates stack traces: all call sequences result in a run-time error to evoke the error output including the stack traces.
-
Without kernel:
-
Stacktr0: a plain sequence of procedure calls;
-
Stacktr1: a sequence of procedure calls, exceptions, and exception handlers, with the exception handlers also calling further procedures, where the run-time error is triggered;
-
Stacktr2: a procedure call followed by a sequence of exception handlers, with the run-time error triggered in the last exception handler.
-
-
With kernel-v1: as above, but running as kernel thread:
- StacktrK0
- StacktrK1
- StacktrK2
Since kernel threads use the Process Stack Pointer (PSP), the kernel programs demonstrate the switch from the Main Stack Pointer (MSP) used for exception handling to the PSP while building the stack trace across two stacks.
On the RP2040 and RP2350, the programs run on both cores to test the independence of run-time error handling and stack traces per core. The program on core 0 triggers a compiler-inserted run-time check error, on core 1 a hardware fault.
On the STM32 MCUs, you can set Case to either Error or Fault to see both stack traces.
Example Error Output
run-time error in handler mode: 7 core: 0
integer divided by zero or negative divisor
StacktrK1.error addr: 100061B6H line: 40
stacked registers:
xpsr: 6100002BH
pc: 100061B6H
lr: 10006213H
r12: 0A0B0C0DH
r3: 00000002H
r2: 1000257CH
r1: 00000000H
r0: 00000000H
sp: 2002FD00H
trace: code addr ln frame addr fsz
StacktrK1.error 100061B6H 40
StacktrK1.i2 1000620EH 49 2002FD28H 8
StacktrK1.i1 1000624AH 58 2002FD30H 4
StacktrK1.i0 10006256H 63 2002FD34H 20
--- exc ---
StacktrK1.h2 10006260H 2002FD6CH 4
StacktrK1.h1 10006290H 80 2002FD70H 8
StacktrK1.h0 1000629EH 85 2002FD78H 20
--- exc ---
StacktrK1.p1 100062B4H 2002F378H 4
StacktrK1.p0 100062E0H 107 2002F37CH 260
StacktrK1.run 100062FEH 112 2002F480H 4
StacktrK1.t0c 1000631EH 119 2002F484H 4
A full run-time error output shows, from the top:
- the type of error: run-time error (via a compiler-inserted check) or MCU fault (via a hardware exception)
- if it occurred in thread or handler mode
- a description of the error
- the error location, indicating the faulting procedure, the absolute code address, and, if available, the source code line number
- the registers as stacked by the hardware at exception entry (note the marker/sentinel value in register
r12as set in the test programs), plus the value of the stack pointer after stacking - the trace, ie. the chain of procedure calls and exceptions that led up to the error
- exceptions are annotated with
exc; the procedure right above is the exception handler - the absolute code address and, if available, the source code line number where the call occurred; when interrupted by an exception, the source line number is unknown
- the address and size of the corresponding frame on the stack
- exceptions are annotated with
This output is printed by module RuntimeErrorsOut, which interprets the error data collected by module RuntimeErrors, and uses module Stacktrace to create the stack trace. RuntimeErrors and RuntimeErrorsOut are installed at start-up by module Main.
Creating the Stack Trace
-
The tracing algorithm loops over the stack starting from the error exception stack frame, then determining the top of each procedure stack frame, where the stacked link register (LR) value is stored, pushed there by the procedure's prologue (including exception handlers).
-
That is, the tracing algorithm emulates a frame pointer.
-
It determines the size of each frame on the stack by inspecting the corresponding procedure's prologue. The prologue address is found using the binary program meta data appended to each Astrobe program.
-
Such found stacked LR value can be either:
-
an
EXC_RETURNvalue, which signifies that there is a hardware-created exception stack frame right above in the stack address range, which must be skipped when continuing; theEXC_RETURNvalue also indicates if the exception stack frame is on the process stack (PSP), and thus the tracing algorithm must continue there; in any case, the current procedure is an exception handler, and its data are recorded as trace point (eg.h0andi0in the example output); -
a regular LR value stemming from a procedure call via
blorblx; the LR value is an address in the calling procedure, and the call is registered as trace point (eg.p0orh1in the above output).
-
-
The trace stops when either all trace points in the trace data structure are used, or when the first LR value on the stack is encountered. Since all program code of an Oberon program is executed as procedure, with a corresponding prologue, there is always an LR value at the base of the stack.
Stay tuned for a more in-depth description of this algorithm in an updated version of Stacktraces, which still describes the formerly used approach to scan the whole stack to determine potential pushed LR values based on heuristics. The new algorithm finds LR values deterministically.
Further Reading
Repository
<repo>/examples/v3.1/rpi/pico/Stacktrace<repo>/examples/v3.1/rpi/pico2/Stacktrace<repo>/examples/v3.1/stm/u585i-iot/Stacktrace
Last updated: 18 May 2026