plan.org has specified a shadow stack in the dev column since the beginning and nothing had ever built it. A frame is four words on the calling function's own stack: the one it displaced, a pointer to a static description of the function, and two words reserved for its locals. The name and the location travel on the frame, so a backtrace needs no debug information, no symbol table, and nothing from the platform unwinder that plan.org deliberately does not use. The pop is at every ret, the landing block a transfer leaves through included. That is the half that is easy to get wrong: a pop written only on the normal path leaves a dead frame behind every handled error, and the test takes five breaks and resumes all of them by transfer before asking for two frames. (:op "backtrace") answers from a snapshot the stopped thread takes, beside the restarts and for the same reason, and marks which frames belong to the program and which to the evaluation the break is inside. It is refused while the program runs. Measured, interleaved, three pairs of binaries: 29% on 600 frames of sand, 7.6% on a benchmark that is nothing but calls -- 32us per frame of sand, a fifth of a percent of a frame at 60fps. An array with a stack pointer was built and timed as the alternative and is worse on both.
Description
Languages
OCaml
67.2%
Emacs Lisp
15.2%
C
10.4%
HTML
2.9%
Standard ML
2.8%
Other
1.5%