C++ debugging workflow for investigating complex issues...
An interactive workflow for methodically debugging C++ issues through hypothesis elimination, test-driven investigation, and documented reasoning.
1. PARSE TICKET → Read DEBUG_TICKET.md for issue details
2. INVESTIGATE → Review code, headers, and existing tests
3. ELIMINATE → Rule out causes with evidence
4. PROPOSE → Suggest diagnostic tests
5. IMPLEMENT → Write and run tests (GTest/Catch2)
6. CHECKPOINT → Get human feedback, iterate or resolve
Before beginning, look for DEBUG_TICKET.md in the project root or current directory. This file contains the structured problem description from the user.
If no ticket exists, ask the user to create one using the template in assets/DEBUG_TICKET_TEMPLATE.md.
Required ticket sections:
Create a debug log file to track the investigation:
mkdir -p .debug-sessions
SESSION_ID=$(date +%Y%m%d_%H%M%S)
cp DEBUG_TICKET.md .debug-sessions/debug_${SESSION_ID}.md
Append investigation sections to the log. See references/debug-log-template.md for the full format.
In addition to the debug session log, create or resume an iteration log for tracking build-test cycles:
docs/investigations/{feature-name}/iteration-log.md.claude/templates/iteration-log.md.templateIf the log already exists (from a previous session), read it fully before making any changes. This is critical for avoiding repeated approaches.
Relationship to debug log: The debug log tracks hypotheses and reasoning. The iteration log tracks build-test cycle results and is used for circle detection. Both are maintained in parallel.
For each file/class/function identified in the ticket:
@pre, @post, @throws annotationsstatic_assert, concept, or SFINAE constraints#define guards and macro usage# Find existing tests
find . -name "*test*.cpp" -o -name "*_test.cpp" -o -name "test_*.cpp" | head -20
# Check for test framework
grep -r "gtest\|catch\|doctest\|boost.test" --include="CMakeLists.txt" .
# If using gcov/lcov:
# lcov --capture --directory . --output-file coverage.info
# lcov --list coverage.info | grep "suspect_file"
Document in debug log:
For each potential cause, document:
### Hypothesis: [Brief description]
**Status:** ⏳ Investigating | ✅ Eliminated | ❌ Confirmed as cause | 🔄 Needs more info
**Evidence gathered:**
- [What was checked]
- [What was found]
**Conclusion:** [Why this is/isn't the cause]
Elimination methods:
Based on investigation, propose tests that will:
Test proposal format:
### Proposed Test: [Name]
**Purpose:** What this test will prove or disprove
**Target:** Which component/function/behavior
**Type:** Unit | Integration | Regression
**Expected outcome:** What we expect to learn
**Test outline:**
- Setup: [preconditions]
- Action: [what to test]
- Assert: [expected results]
Present proposals to user before implementing.
After user approval, implement tests using the project's test framework.
# Check CMakeLists.txt for test framework
grep -E "gtest|GTest|catch|Catch2|doctest|Boost.*Test" CMakeLists.txt
# Common locations
ls tests/ test/ unittest/ 2>/dev/null
// tests/debug_investigation_test.cpp
#include <gtest/gtest.h>
#include "suspect_class.h"
// Link to hypothesis H1 in debug log
TEST(DebugInvestigation, H1_BoundaryCondition) {
SuspectClass obj;
// Test the specific condition from hypothesis
EXPECT_EQ(obj.method(edge_case_input), expected_output);
}
TEST(DebugInvestigation, H2_NullHandling) {
SuspectClass obj;
EXPECT_NO_THROW(obj.method(nullptr));
}
// tests/debug_investigation_test.cpp
#include <catch2/catch_test_macros.hpp>
#include "suspect_class.h"
TEST_CASE("Debug H1: Boundary condition", "[debug][h1]") {
SuspectClass obj;
REQUIRE(obj.method(edge_case_input) == expected_output);
}
TEST_CASE("Debug H2: Null handling", "[debug][h2]") {
SuspectClass obj;
REQUIRE_NOTHROW(obj.method(nullptr));
}
# CMake build
mkdir -p build && cd build
cmake .. -DBUILD_TESTING=ON
cmake --build . --target debug_investigation_test
# Run specific test
./debug_investigation_test --gtest_filter="DebugInvestigation.*"
# or for Catch2:
./debug_investigation_test "[debug]"
# Rebuild with sanitizers for deeper investigation
cmake .. -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -g"
cmake --build .
./debug_investigation_test
Update debug log with:
After each build+test cycle (whether diagnostic tests pass or fail), follow this protocol:
1. Record Iteration Entry
Append a new entry to docs/investigations/{feature-name}/iteration-log.md:
### Iteration N — {YYYY-MM-DD HH:MM}
**Commit**: {short SHA}
**Hypothesis**: {Why this change was made — what problem it's solving}
**Changes**:
- `path/to/file.cpp`: {description of change}
**Build Result**: PASS / FAIL ({details if fail})
**Test Result**: {pass}/{total} — {list of new failures or fixes vs previous iteration}
**Impact vs Previous**: {+N passes, -N regressions, net change}
**Assessment**: {Does this move us forward? Any unexpected side effects?}
2. Auto-Commit
After each successful build+test cycle, commit all changes:
git add {changed files} {iteration-log.md}
git commit -m "investigate: iteration {N} — {one-line summary}
{ticket-name}
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
3. Circle Detection — Before Next Change
Before making the next change, read the iteration log and check for:
If a circle is detected:
At each checkpoint, present:
## Debug Session Checkpoint
### Progress Summary
- Hypotheses eliminated: [count]
- Hypotheses confirmed: [count]
- Hypotheses pending: [count]
### Key Findings
[Most important discoveries]
### Current Best Theory
[What we think is happening and why]
### Recommended Next Steps
1. [Option A - describe and estimate effort]
2. [Option B - describe and estimate effort]
### Questions for You
- [Specific questions that would help narrow investigation]
Wait for human feedback before:
If tests reveal the cause:
If tests are inconclusive:
If human redirects investigation:
# View current debug session
cat .debug-sessions/debug_*.md | tail -100
# Search for patterns in codebase
grep -rn "pattern" --include="*.cpp" --include="*.h" --include="*.hpp" .
# Find recent changes to suspect files
git log --oneline -20 -- path/to/file.cpp
# Show class/function usages
grep -rn "ClassName\|function_name" --include="*.cpp" --include="*.h" .
# Find all includes of a header
grep -rn '#include.*"suspect.h"' --include="*.cpp" .
# Build with debug symbols
cmake -DCMAKE_BUILD_TYPE=Debug ..
# Run with Valgrind (memory errors)
valgrind --leak-check=full --track-origins=yes ./executable
# Run with AddressSanitizer output
ASAN_OPTIONS=detect_leaks=1:print_stats=1 ./executable
# Generate coverage report
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory coverage_report
# Check for undefined behavior
./executable # if built with -fsanitize=undefined
# Examine core dump
gdb ./executable core
Common C++ issues to check: