Oberon RTK

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 r12 as 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

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_RETURN value, which signifies that there is a hardware-created exception stack frame right above in the stack address range, which must be skipped when continuing; the EXC_RETURN value 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. h0 and i0 in the example output);

    • a regular LR value stemming from a procedure call via bl or blx; the LR value is an address in the calling procedure, and the call is registered as trace point (eg. p0 or h1 in 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