I recently unified the codepaths for recording data, evaluating expressions "currently", and evaluating them historically. This was primarily so that I could finalize conditional breakpoints with arbitrary expressions.
Because they're unified, it made it straightforward to allow conditions to mix and match current and historical data, e.g. in this video val > val @ 0 is true when the current value is greater than the value at sample 0.
Previously, the assumption was that recording would always use be really simple expressions (e.g. variables, pointer derefs, struct member access etc), and that all the full expression work would be historical.

The new conditions play nicely with other major recent additions to &WhiteBox: breakpoint actions to enable/disable other breakpoints/inspections, and (auto-layouting) hardware breakpoints.

These solve the main things I find frustrating with data breakpoints in other debuggers that I've tried:

  • Data breakpoint disable between runs (presumably due to ASLR & not knowing where to replace them).
    • Solution: auto-enable them at specific points in the code, reevaluating the expressions for addresses then.
  • Data breakpoints on local vars trigger in some completely different callstack that you don't care about after the function finishes.
    • Solution: auto-disable them either at particular points in the code or when the function returns.

In the video:

  • I place a sampling breakpoint both as a place to enable/disable the data breakpoint, and as a fixed point of reference for comparing val > val @ 0
  • The data breakpoint is set to sample data and then continue execution
  • I rerun multiple times with different conditions (including historical self-reference) and get a fresh timeline showing different all the times val was written to when the condition was true.
  • (You can see all the changes for local vars on multiple levels of the stack, as well as watch expressions currently evaluating at the top level.)
View full feed

Motivation

Programming is difficult.

Our squishy human brains struggle to simulate what the computer will do when it runs our code. As a result we make lots of errors, many of them small, requiring us to divert our attention from the main problem we're working on to fix minor implementation details. This both takes up time and makes us lose focus on the high-level design.

The computer is going to run our code anyway, so why not do so immediately to check what it does?

Tool Overview

WhiteBox runs the code as it is written, capturing details about the execution. It can then give you live feedback about the run, showing you how variables change over time and other details.

This is intended to improve the developer experience by helping you to understand your code faster, and consequently:

  • catch and fix bugs earlier, before they cause major issues
  • iterate more often over your design, giving more opportunity for refinement

Tool Internals

WhiteBox is written in C and C++ with few external libraries:

  • Clang/LLVM is used for lazily JIT compiling code
  • Dear ImGui is used for creating the UI
  • A few single-header libraries are used for various utilities (STB, Mattias Gustavsson, Randy Gaul's cute_headers)

Just about everything else is written from scratch, including:

  • the hash table implementation
  • DWARF debug info parsing
  • plugins for code editors
  • ...

Project Details

WhiteBox has most of the initial set of features implemented and is nearly ready for alpha release.

Supported languages: C (and a small subset of C++) Supported platforms: Windows x64, Linux x64 (tested on Ubuntu 20.04) Supported code editors: 4coder, Emacs, Notepad++, Sublime Text, Vim/Neovim, Visual Studio, VSCode, (plus workarounds that should work for most other editors)

Support for more of each of these is planned or currently in-development.

Project Links