Shadow call stack, filled in by BUILD=FastDebug.
MIPS GCC does not keep a walkable chain of frame pointers: even with -fno-omit-frame-pointer, where a function saves its caller's frame pointer depends on the size of its frame. So the FastDebug build type compiles everything with -finstrument-functions, and psyqo provides the __cyg_profile_func_enter and __cyg_profile_func_exit hooks, which push and pop the call site of every instrumented function onto a small array. Reading that array from anywhere gives the chain of return addresses that led there, which mipsel-none-elf-addr2line -f -e program.elf turns back into functions and lines. Each entry is a return address, 8 bytes past the jal, so subtract 8 to get the line of the call itself. The hooks cost two calls per function call.
FastDebug also defines PSYQO_SHADOW_STACK. Without it, the hooks are not compiled at all, enabled is false, and the accessors below return zero.
Entry 0 is the outermost call. The stack holds c_capacity entries; deeper calls are counted in depth() but their call sites are not stored, so the pops stay balanced and the innermost frames are the ones that go missing.
Rules for code running under FastDebug:
- Interrupt handlers may be instrumented. A push reserves its slot before writing it, and an interrupt pops everything it pushed before it returns, so an interrupt landing in the middle of a hook leaves the interrupted stack intact. Anything sampling the stack from an interrupt sees the interrupt's own frames on top of the interrupted ones.
- A function that leaves through anything but a return, such as a callback that ends in
syscall_returnFromException, leaks its entry, and has to be marked __attribute__((no_instrument_function)).
- Code that switches stacks has to switch shadow stacks with it, using
switchTo. The stackful coroutines do this already.
- Hot helpers that are called often enough to drown everything else can opt out with
__attribute__((no_instrument_function)).