ESL — Engineering Software LabEngineering evidence, not guesses
Hands-on extension to “Beyond Breakpoints”

From breakpoints
to evidence.

A repeatable C++ lab that turns a delayed heap-use-after-free into a verified cause, a minimal ownership fix, and a permanent regression check.

ESLPurpose
Why we built it

Make the debugging workflow tangible.

Vivek Bhadra’s article explains why one tool cannot answer every debugging question. We turned that principle into a public lab where each tool has a precise job.

01

Reproduce

Make undefined behavior visible without pretending that one observed output is guaranteed.

02

Explain

Separate the late symptom from the earlier lifetime-ending event that caused it.

03

Verify

Prove the smallest correction with sanitizers and regression tests—not confidence alone.

ESLThe defect
A realistic ownership mistake

The pointer survives.
The object does not.

1 · Borrowed pointer escapes into the cache
const Reading* cached_reading = nullptr;

void cache_for_dashboard(const Reading& reading)
{
    cached_reading = &reading;
}
2 · Owner retires the object; cache still points there
void retire_reading(std::unique_ptr<Reading>& reading)
{
    reading.reset();
}

// later, elsewhere...
const Reading& reading = *cached_reading;

A non-null address proves only that bits remain in the pointer. It does not prove that a live object still occupies that address.

ESLThe misleading symptom
Correct-looking output is not correctness

Undefined behavior can look plausible.

42

Expected record

The dashboard cache should retain the original reactor-inlet reading.

9001

Observed record

A later allocation can reuse the same heap address, making unrelated data appear in the dashboard.

cached object at 0x603000000040
new object at    0x603000000040
cached reading: id=9001, value=-273.15, label=unrelated-record
ESLThe repeatable workflow
Five stages, one command

Move deliberately between forms of evidence.

1 · BuildDebug symbols, frame pointers, useful warnings.
2 · ObserveCapture the visible symptom without over-interpreting it.
3 · DiagnoseAddressSanitizer identifies the invalid memory operation.
4 · InspectGDB stops at the delayed use and exposes program state.
5 · VerifyCorrected sanitizer target and regression tests must pass.
C:\amp-demos\cpp-debugging-demo> run_demo.bat
...
SUCCESS: symptom, root-cause evidence, debugger inspection, and fix verified.
ESLAddressSanitizer
The causal timeline

ASan connects the symptom to its history.

FirstA
Allocation in main()Where the object’s valid lifetime begins.
ThenF
Deallocation in retire_reading()The causal event: unique_ptr::reset() ends the object’s lifetime.
LaterR
Invalid read in render_dashboard()The visible symptom occurs away from the cause.

That allocation → free → invalid-read chain is stronger than guessing from the crash line.

ESLGDB
Interactive inspection

GDB shows why “not null” is not enough.

break cache_for_dashboard
break retire_reading
break render_dashboard
run
print cached_reading
continue
backtrace full
info locals
Before retirementThe cache and owner refer to the same live object.
After retirementThe cached pointer still contains the same address.
At the symptomThe address exists, but the object lifetime has ended.
ESLThe correction
Fix ownership, not the symptom

The cache must own what it retains.

Before · borrowed lifetime
const Reading* cached_reading;

cached_reading = &reading;

// Owner can destroy reading independently.
After · cache-owned value
std::optional<Reading> cached_reading;

cached_reading = reading;

// Cache retains a valid independent value.

Original ID retained

The corrected program prints id=42.

Sanitizer clean

The corrected ASan target has no lifetime finding.

Regression protected

CTest makes the expected behavior repeatable.

ESLWhat our work adds
From article to runnable artifact

A practical companion—not a replacement.

MIT

Public source

A small C++17 project readers can inspect, modify, and teach from.

One-click runner

A Windows batch file orchestrates the WSL build and all five verification stages.

Repeatable state

Every run rebuilds the diagnostic targets, so the demonstration can be repeated safely.

UI

Findings dashboard

A local browser view presents progress, confirmed findings, code changes, and terminal evidence.

CI

Executable acceptance

The runner fails if expected sanitizer evidence or corrected tests are missing.

Symptom vs. cause

The lab explicitly teaches that the crash location and causal event may be far apart.

ESLOne-click dashboard
The easiest way to present the demo

One click runs the full investigation.

Completed debugging dashboard with five verified stages and live findings
The completed run turns raw tool output into a five-stage debugging story.
1 · LaunchDouble-click start_dashboard.bat. It selects a free local port and opens the browser—no WSL window is required.
2 · RunClick Run complete demo and watch build, symptom, ASan, GDB, and verification stages update live.
3 · Present againUse Run again to repeat the same controlled investigation from a clean build.
ESLDashboard evidence
Cause, correction, and proof in one view

The dashboard explains what the run proved.

Dashboard showing borrowed-pointer defect, owned-value correction, and passing tests
The ownership change appears beside the GDB evidence and passing regression tests.
DefectA borrowed pointer remains in the cache after the pointed-to object’s lifetime ends.
Correctionstd::optional<Reading> gives the cache ownership of the retained value.
ProofThe terminal preserves debugger observations, clean sanitizer execution, and two passing regression tests.
TakeawayThe audience sees the complete evidence chain—not only a final “fixed” message.
ESLRun it yourself
Public, open, repeatable

What you need—and what to run.

Prerequisites

Windows + WSLWith a Linux distribution available.
Build toolsCMake, GCC or Clang, and GDB.
Optional dashboardPython 3.9 or newer.
git clone https://github.com/zuwasi/cpp-debugging-demo.git
cd cpp-debugging-demo

run_demo.bat

# or launch the visual dashboard
start_dashboard.bat
ESLClosing principle
The engineering lesson
AI may propose.
The engineering toolchain must verify.

The durable result of debugging is not merely an explanation. It is evidence, a justified correction, and an automated check that prevents rediscovery.

← Swipe to navigate →
1 / 13