1. Overview
  2. Debugging workflow
  3. Debugging workflow: CI
  4. Basic interface
  5. Call stacks
  6. Explaining dataflow
  7. Multiprocess
  8. Search box
  9. Source files
  10. Condition and print expressions
  11. Toolbox
  12. Alerts
  13. Application logs
  14. Callees
  15. View operators
  16. Notebook
  17. Instruction execution
  18. Javascript
  19. Browser UI integration
  20. Screenshots
  21. Additional views
  22. GDB
  23. System debug info
  24. Compiler issues
  25. The Pernosco vision
  26. Related work

Call stacks

Call stacks are useful because they summarize control flow leading to a program point, in terms of abstractions chosen by developers.

Traditional debuggers produce call stacks by inspecting stack memory to extract stack frames. This approach is defeated by stack corruption, optimizations such as tail calls that eliminate stack frames, and code such as JITted code that doesn't use standard calling conventions and doesn't provide debuginfo needed to interpret stack memory. When debugging applications containing JITted code, such as Firefox, gdb will normally only be able to unwind the stack up to the nearest JITted code frame and the rest of the stack is lost.

Pernosco instead looks at the history of the thread to determine the set of function calls that have not yet returned. Pernosco stacks depend only on the application code using regular call instructions (or jmp instructions to function symbols) to call functions. In particular Pernosco produces correct stacks in the presence of tail-call optimizations and unknown JITted code. Of course Pernosco still relies on the presence of debuginfo to obtain function names and parameter values for stack frames. Pernosco stack frames show the values parameters had when a function was called, not the "current" values, because we find that more useful.

<< Basic interface Explaining dataflow >>