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.)