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

Application logs

First-class debugging tools should reduce developers' reliance on bespoke logging, but logging code in applications captures and abstracts information important to developers, and will no doubt remain useful. Pernosco leverages logging by capturing log output and presenting it in a dedicated log view, using built-in knowledge of logging frameworks used by our customers. When log output is categorized by "log modules", Pernosco indexes on those modules to support efficient filtering by module. Clicking on log output jumps to where and when it was produced.

For applications like Firefox, enabling all logging is prohibitively expensive, so most logs are disabled by default and developers opt into collecting subsets of logs. This is cumbersome, often requiring multiple iterations of enabling logs, running tests, and investigating results. Worse, each iteration may behave differently, exhibiting different bugs or no bug at all, either because the application is nondeterministic or because the logging itself perturbs the application. Pernosco fixes this by recording test execution with all logging disabled and then capturing log output during a specially instrumented replay. During this replay, whenever the application tests whether logging is enabled, Pernosco forks the program state and executes ahead as if logging is enabled, capturing any log results. Once control returns to the main execution path, the forked state is discarded and replay continues normally. Each "diversion" needs to be very efficient because they can occur at a very high rate.

This approach is not perfect. For example, sometimes logging code will capture some data (e.g. a timestamp) if logging is enabled, then do some work, and then log results based on the previously captured data. With our approach, after the first step the captured data will be thrown away and the actual log output will fail. Nevertheless it generally works well in practice.