Systematic Debugging — amd-smi
Random fixes waste time and create new bugs. Quick patches mask underlying issues.
Core principle: ALWAYS find the root cause before attempting a fix. Symptom fixes are failure.
Violating the letter of this process is violating the spirit of debugging.
The Iron Law
NO FIXES WITHOUT ROOT-CAUSE INVESTIGATION FIRST
If you haven't completed Phase 1, you cannot propose a fix.
When to Use
Use for any technical issue:
- Test failures (C++ GTest, Python unit, CLI)
- Bugs reported in
amdsmi_* output
- Unexpected CLI output
- Build/CMake failures
- Packaging failures (RPM/DEB postinst)
- Wrapper-regen mismatches
- Performance regressions
Especially when:
- Under time pressure (emergencies make guessing tempting)
- "Just one quick fix" seems obvious
- You've already tried 2+ fixes
- The previous fix didn't work
- You don't fully understand the issue
The Four Phases
Complete each phase before proceeding to the next.
Phase 1: Root-Cause Investigation
Read error messages carefully
- Read the full stack trace, every frame
- Note exact line numbers, file paths, error codes
- For
amdsmi_status_t errors: which enum value? Where is it set?
Reproduce consistently
- Exact command, exact environment
- Same hardware? Same install context (system vs pip)?
- If not reproducible → gather more data, don't guess
Check recent changes
git log --oneline -20 -- <affected files>
git diff HEAD~5 -- <affected file>
- New CMake options? New dependency? Wrapper regenerated?
Gather evidence across cascade layers
amd-smi bugs often span layers. Add instrumentation at every boundary:
# Layer 1: CLI argparse
echo "argparse args: $@"
# Layer 2: Python interface
python3 -c "import amdsmi; print(amdsmi.amdsmi_get_lib_version())"
# Layer 3: C wrapper loaded
python3 -c "from amdsmi import amdsmi_wrapper; print(amdsmi_wrapper.libamd_smi._name)"
# Layer 4: Actual C call return value
strace -e trace=openat python3 -c "import amdsmi; amdsmi.amdsmi_init()" 2>&1 | grep libamd_smi
This reveals which layer breaks, not just that something breaks.
Trace data flow backward
- Where does the bad value originate?
- What called this with the bad value?
- Keep tracing up the cascade until you find the source
- Fix at the source, not at the symptom
Phase 2: Pattern Analysis
- Find a working example — locate the closest-working similar function/command/test
- Compare against the reference — read it completely, line by line; don't skim
- List every difference — however small. Don't assume "that can't matter"
- Understand dependencies — what config, environment, build flags does the working version need?
Phase 3: Hypothesis and Testing
- Form a single hypothesis — "I think X is the root cause because Y." Write it down.
- Test minimally — the smallest possible change. One variable at a time. No "while I'm here" fixes.
- Verify before continuing — worked? Phase 4. Didn't? Form a NEW hypothesis. Don't pile fixes.
- When you don't know — say "I don't understand X". Don't pretend. Ask the user or research more.
Phase 4: Implementation
Create a failing test that reproduces the bug
- Use the
test-driven-development skill
- The test MUST fail before the fix and pass after
Implement a single fix — address the root cause. One change. No bundled refactors.
Verify
- Bug test passes
- No other tests broken
- Issue is actually resolved end-to-end (use
verification-before-completion)
If the fix doesn't work — STOP. Count attempts.
- < 3 attempts → return to Phase 1 with the new information
- ≥ 3 attempts → STOP and question the architecture (next item)
If 3+ fixes failed: question the architecture
Pattern indicating an architectural problem:
- Each fix reveals new shared state / coupling in a different place
- Fixes require "massive refactoring" to implement
- Each fix creates new symptoms elsewhere
Stop. Discuss with the user. This is not a failed hypothesis — this is a wrong architecture.
Red Flags — STOP and Follow Process
- "Quick fix for now, investigate later"
- "Just try changing X and see if it works"
- "Skip the test, I'll manually verify"
- "It's probably X, let me fix that"
- "I don't fully understand but this might work"
- "Pattern says X but I'll adapt it differently"
- Proposing solutions before tracing data flow
- "One more fix attempt" after 2+ failures
- Each fix reveals a new problem in a different place
All of these mean: stop, return to Phase 1.
If 3+ fixes failed: question the architecture (Phase 4 step 5).
Common Rationalizations
| Excuse |
Reality |
| "Issue is simple, no process needed" |
Simple issues have root causes too. Process is fast for simple bugs. |
| "Emergency, no time for process" |
Systematic debugging is FASTER than guess-and-check thrashing. |
| "Just try this, then investigate" |
First fix sets the pattern. Do it right from the start. |
| "I'll write the test after the fix works" |
Untested fixes don't stick. Test-first proves the fix actually addresses the bug. |
| "Multiple fixes at once saves time" |
Can't isolate what worked. Causes new bugs. |
| "Reference is too long, I'll adapt the pattern" |
Partial understanding guarantees more bugs. Read it fully. |
| "I see the problem, let me fix it" |
Seeing the symptom ≠ understanding the cause. |
| "One more fix attempt" (after 2+ failures) |
3+ failures = architectural problem. Question the pattern. |
Quick Reference
| Phase |
Activities |
Done When |
| 1. Root cause |
Read errors, reproduce, check changes, instrument layers |
You understand WHAT and WHY |
| 2. Pattern |
Find working examples, compare differences |
You can list every relevant difference |
| 3. Hypothesis |
State theory, test minimally |
Confirmed, or new hypothesis formed |
| 4. Implementation |
Failing test → single fix → verify |
Bug resolved, tests green |
amd-smi-Specific Investigation Tips
- API cascade bugs: Use the cascade Quick Check (
grep across header + cc + wrapper + interface + CLI) — gaps are often the root cause
- Wrapper mismatch: If a function's Python signature differs from the C header, run
tools/update_wrapper.sh — the wrapper drifted
- Install context bugs: Reproduce in BOTH system install and pip install before claiming the fix works (see
amdsmi-build-install skill)
- CMake bugs: Always
rm -rf build between attempts — stale CMake cache hides real causes
- Test flakiness: Run the test in isolation, then alone in the suite, then with the full suite — find the polluting test
Required Companion Skills
test-driven-development — Phase 4 Step 1 (failing test)
verification-before-completion — Phase 4 Step 3 (verify the fix)
dispatching-parallel-agents — when there are 2+ independent failures
1---2name: systematic-debugging3description: Use when encountering any bug, test failure, build failure, or unexpected behavior in amd-smi — before proposing any fix. Enforces root-cause investigation before symptom patching.4---56# Systematic Debugging — amd-smi78Random fixes waste time and create new bugs. Quick patches mask underlying issues.910**Core principle:** ALWAYS find the root cause before attempting a fix. Symptom fixes are failure.1112**Violating the letter of this process is violating the spirit of debugging.**1314## The Iron Law1516```17NO FIXES WITHOUT ROOT-CAUSE INVESTIGATION FIRST18```1920If you haven't completed Phase 1, you cannot propose a fix.2122## When to Use2324Use for any technical issue:2526- Test failures (C++ GTest, Python unit, CLI)27- Bugs reported in `amdsmi_*` output28- Unexpected CLI output29- Build/CMake failures30- Packaging failures (RPM/DEB postinst)31- Wrapper-regen mismatches32- Performance regressions3334**Especially when:**35- Under time pressure (emergencies make guessing tempting)36- "Just one quick fix" seems obvious37- You've already tried 2+ fixes38- The previous fix didn't work39- You don't fully understand the issue4041## The Four Phases4243Complete each phase before proceeding to the next.4445### Phase 1: Root-Cause Investigation46471. **Read error messages carefully**48 - Read the full stack trace, every frame49 - Note exact line numbers, file paths, error codes50 - For `amdsmi_status_t` errors: which enum value? Where is it set?51522. **Reproduce consistently**53 - Exact command, exact environment54 - Same hardware? Same install context (system vs pip)?55 - If not reproducible → gather more data, don't guess56573. **Check recent changes**58 - `git log --oneline -20 -- <affected files>`59 - `git diff HEAD~5 -- <affected file>`60 - New CMake options? New dependency? Wrapper regenerated?61624. **Gather evidence across cascade layers**6364 amd-smi bugs often span layers. Add instrumentation at every boundary:6566 ```bash67 # Layer 1: CLI argparse68 echo "argparse args: $@"6970 # Layer 2: Python interface71 python3 -c "import amdsmi; print(amdsmi.amdsmi_get_lib_version())"7273 # Layer 3: C wrapper loaded74 python3 -c "from amdsmi import amdsmi_wrapper; print(amdsmi_wrapper.libamd_smi._name)"7576 # Layer 4: Actual C call return value77 strace -e trace=openat python3 -c "import amdsmi; amdsmi.amdsmi_init()" 2>&1 | grep libamd_smi78 ```7980 This reveals **which layer breaks**, not just that something breaks.81825. **Trace data flow backward**83 - Where does the bad value originate?84 - What called this with the bad value?85 - Keep tracing up the cascade until you find the source86 - Fix at the source, not at the symptom8788### Phase 2: Pattern Analysis89901. **Find a working example** — locate the closest-working similar function/command/test912. **Compare against the reference** — read it completely, line by line; don't skim923. **List every difference** — however small. Don't assume "that can't matter"934. **Understand dependencies** — what config, environment, build flags does the working version need?9495### Phase 3: Hypothesis and Testing96971. **Form a single hypothesis** — "I think X is the root cause because Y." Write it down.982. **Test minimally** — the smallest possible change. One variable at a time. No "while I'm here" fixes.993. **Verify before continuing** — worked? Phase 4. Didn't? Form a NEW hypothesis. Don't pile fixes.1004. **When you don't know** — say "I don't understand X". Don't pretend. Ask the user or research more.101102### Phase 4: Implementation1031041. **Create a failing test** that reproduces the bug105 - Use the `test-driven-development` skill106 - The test MUST fail before the fix and pass after1072. **Implement a single fix** — address the root cause. One change. No bundled refactors.1083. **Verify**109 - Bug test passes110 - No other tests broken111 - Issue is actually resolved end-to-end (use `verification-before-completion`)1124. **If the fix doesn't work** — STOP. Count attempts.113 - < 3 attempts → return to Phase 1 with the new information114 - **≥ 3 attempts → STOP and question the architecture** (next item)1155. **If 3+ fixes failed: question the architecture**116117 Pattern indicating an architectural problem:118 - Each fix reveals new shared state / coupling in a different place119 - Fixes require "massive refactoring" to implement120 - Each fix creates new symptoms elsewhere121122 Stop. Discuss with the user. This is not a failed hypothesis — this is a wrong architecture.123124## Red Flags — STOP and Follow Process125126- "Quick fix for now, investigate later"127- "Just try changing X and see if it works"128- "Skip the test, I'll manually verify"129- "It's probably X, let me fix that"130- "I don't fully understand but this might work"131- "Pattern says X but I'll adapt it differently"132- Proposing solutions before tracing data flow133- **"One more fix attempt" after 2+ failures**134- **Each fix reveals a new problem in a different place**135136**All of these mean: stop, return to Phase 1.**137138If 3+ fixes failed: question the architecture (Phase 4 step 5).139140## Common Rationalizations141142| Excuse | Reality |143|--------|---------|144| "Issue is simple, no process needed" | Simple issues have root causes too. Process is fast for simple bugs. |145| "Emergency, no time for process" | Systematic debugging is FASTER than guess-and-check thrashing. |146| "Just try this, then investigate" | First fix sets the pattern. Do it right from the start. |147| "I'll write the test after the fix works" | Untested fixes don't stick. Test-first proves the fix actually addresses the bug. |148| "Multiple fixes at once saves time" | Can't isolate what worked. Causes new bugs. |149| "Reference is too long, I'll adapt the pattern" | Partial understanding guarantees more bugs. Read it fully. |150| "I see the problem, let me fix it" | Seeing the symptom ≠ understanding the cause. |151| "One more fix attempt" (after 2+ failures) | 3+ failures = architectural problem. Question the pattern. |152153## Quick Reference154155| Phase | Activities | Done When |156|-------|-----------|-----------|157| 1. Root cause | Read errors, reproduce, check changes, instrument layers | You understand WHAT and WHY |158| 2. Pattern | Find working examples, compare differences | You can list every relevant difference |159| 3. Hypothesis | State theory, test minimally | Confirmed, or new hypothesis formed |160| 4. Implementation | Failing test → single fix → verify | Bug resolved, tests green |161162## amd-smi-Specific Investigation Tips163164- **API cascade bugs:** Use the cascade Quick Check (`grep` across header + cc + wrapper + interface + CLI) — gaps are often the root cause165- **Wrapper mismatch:** If a function's Python signature differs from the C header, run `tools/update_wrapper.sh` — the wrapper drifted166- **Install context bugs:** Reproduce in BOTH system install and pip install before claiming the fix works (see `amdsmi-build-install` skill)167- **CMake bugs:** Always `rm -rf build` between attempts — stale CMake cache hides real causes168- **Test flakiness:** Run the test in isolation, then alone in the suite, then with the full suite — find the polluting test169170## Required Companion Skills171172- **`test-driven-development`** — Phase 4 Step 1 (failing test)173- **`verification-before-completion`** — Phase 4 Step 3 (verify the fix)174- **`dispatching-parallel-agents`** — when there are 2+ independent failures