adamant-debugging
Skill4 starsChanged 3 months agoApache-2.0View on GitHub
Debug Adamant software at three levels -- interactive GDB on unit tests and assemblies (breakpoints, stepping, Ada catchpoints), post-mortem crash triage (Last Chance Handler packets, stack-trace symbolization, Renode attach), and target/compiler-level pitfalls (works-on-Linux-traps-on-target, codegen bugs, stale generated code). Use when a test fails and assertions/prints are not enough, when the FSW crashes or a test suite hangs, when decoding an exception traceback, or when behavior differs between Linux and the flight target.
In this repository27 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- adamant-assembly-devSkill
- adamant-build-systemSkill
- adamant-code-reviewSkill
- adamant-component-devSkill
- adamant-cosmos-integrationSkill
- adamant-cosmos-suite-resultsSkill
- adamant-cosmos-testingSkill
- adamant-cosmos-tool-creationSkill
- adamant-formal-verificationSkill
- adamant-framework-componentsSkill
- adamant-framework-internalsSkill
- adamant-generator-devSkill
- adamant-project-setupSkill
- adamant-regression-suiteSkill
- adamant-skill-campaignSkill
- adamant-skill-creationSkill
- adamant-skill-selectorSkill
- adamant-styleSkill
- adamant-subassembliesSkill
- adamant-testingSkill
- adamant-toolsSkill
- adamant-type-systemSkill
- high-assurance-designSkill
- knowledge-acquisitionSkill
- task-planningSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 3 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 55
No flags, no rebuild-for-debug: Linux test binaries are always compiled -O0 -g -gnata -gnatVa and never stripped, so source-level debugging …
- Very specific: line 169
When an exception escapes on the flight target, GNAT's __gnat_last_chance_handler hook fires and Adamant's LCH pattern captures a …
- Distinctive: line 189
Check whether housekeeping sequence counts froze.
- Distinctive: line 228
Prefer the project's own scripts -- they encode target-specific details (ports, ELF paths, register sets) this skill keeps generic.
- Very specific: line 308
Keep it generic: No project names, component or symbol names, memory addresses, platform files, or naming motifs from the program you were …
- Distinctive: line 313
Extend, don't duplicate: Search this file and the references for an existing section on the topic first and sharpen it in place.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
adamant-debugging
Three debugging layers, in escalation order: interactive GDB (a failing test in front of you), post-mortem triage (a crash already happened -- decode what the flight software left behind), and target/compiler-level (the bug only exists on the cross target). The framework already builds debuggable artifacts and ships crash-reporting components -- the skill is knowing which level you are at and the Ada-specific moves at each.
Quick Start (debug a failing unit test)
From the host, build with admt test <component>/test as usual; gdb
itself must run inside the activated container shell -- enter one with
admt env login (the redo/gdb lines below are what you run there).
cd src/components/<component_name>/test
redo test # builds build/bin/Linux_Test/test.elf
gdb build/bin/Linux_Test/test.elfcatch exception # break wherever any Ada exception is raised
run # runs the whole AUnit suite
bt # on catch: raise point <- test proc <- AUnit callerThe same session runs headless for reproducible, pasteable evidence -- the form to prefer when the finding goes in a report:
gdb -batch -ex 'catch exception' -ex run -ex bt -ex 'info locals' \
build/bin/Linux_Test/test.elf(-ex 'break file.adb:N' and print commands chain the same way; note
catch assert hooks GNAT's assert-failure routine -- pragma Assert /
Ada.Assertions / SPARK contracts under -gnata -- so it does NOT fire
on AUnit/Adamant test assertions, which raise their own
Assertion_Error as an ordinary exception. catch exception DOES stop
at those; catch assert does not.)
Diagnosis standard: a root cause is demonstrated, not inferred. Reading source produces a hypothesis; the diagnosis is complete only when the mechanism has been shown at runtime -- the actual values at the defect (a catchpoint stop, a breakpoint print, a watchpoint transition). This is what separates "the bug is probably X" from a report a reviewer can act on, and it routinely falsifies convincing source-reading theories.
No flags, no rebuild-for-debug: Linux test binaries are always compiled
-O0 -g -gnata -gnatVa and never stripped, so source-level debugging works
on the artifact redo test already built.
Build targets: which artifacts are debuggable
Debug readiness is decided by the build TARGET, not a flag:
| Target family | Switches | Steppable? |
Linux (= Linux_Debug), Linux_Test, Linux_Coverage | -O0 -g -fstack-check -gnato -gnata -gnatVa | Yes -- honest line-by-line |
Bareboard <Platform>_Debug / _Test variants | -g3 -ggdb -O0 -gnata -gnatVa | Yes |
Bareboard _Production / _Development | -g3 -ggdb plus -O2 | Loadable; stepping jumpy, locals may be optimized out |
- There is no production Linux mode --
Linuxis a rename ofLinux_Debug. - Bareboard ELFs keep symbols in every mode, so a production ELF loads in
gdb -- but for honest stepping build the
-O0variant:redo build/bin/<Platform>_Debug/main.elf. DEBUG=1 redo ...is build verbosity (prints gprbuild/gcc command lines), not debug symbols -- the target already provides those.- Executables land at
<src_dir>/build/bin/<TARGET>/<test|main>.elf; test dirs force the_Testsuffix automatically viaenv.py.redo targetslists every target.
Ada-specific GDB moves
Set breakpoints by
file:line, not Ada name -- the expression form (break My_Package.My_Proc) frequently fails to resolve:break <unit_name>-implementation.adb:326For symbolic breakpoints use the linker name (Ada mangles with double underscores):
nm test.elf | grep -i <proc_name>then break on<unit>__implementation__<proc>.catch exceptionis the failing-test power tool ---gnatameans a failingpragma AssertraisesADA.ASSERTIONS.ASSERTION_ERROR; the catchpoint stops at the raise with the full stack:catch exception # all Ada exceptions catch exception unhandled # only ones that would kill the test catch assert # only failed assertions- SPARK contracts are assertions at runtime. On Debug/Test targets
(checks on), a failing SPARK pre/postcondition raises like a
pragma Assert--catch exception/catch assertstops at it the same way. Static proof is a separate GNATprove analyze target, not part of the gdb loop (seeadamant-formal-verification); do not confuse a proof failure (compile/analyze time) with a runtime contract violation (what you debug here). - No single-test filter -- the AUnit harness runs the whole suite;
break in the specific
Test_*procedure andcontinuepast the rest. - Validity checks change what you see --
-gnatVa+ Initialize_Scalars means uninitialized scalars hold recognizable invalid patterns; an extreme value ininfo localsusually means "never assigned", not "corrupted".
Debugging environment
- gdb ships with the GNAT toolchain and is on PATH only in the activated
environment (container login via the project's env tooling); a bare
docker execshell will not find it. - DWARF records build-environment paths. Inside the container everything
matches; a host gdb works on Linux-target binaries if launched from the
source dir (cwd resolution) or with
set substitute-path <container_prefix> <host_checkout>. - Rebuild before debugging -- "Source file is more recent than executable" means line numbers are lying.
- Assembly binaries on Linux: same story without the
_Testsuffix --redo build/bin/Linux/main.elf, thengdbit (redo run= build + run).
Cross targets: Renode and JTAG
Cross ELFs cannot execute on the host. Attach a cross gdb
(riscv32-elf-gdb, arm-eabi-gdb) to a GDB server:
Renode: the project's
.rescloads the ELF (sysbus LoadELF) and startsmachine StartGdbServer 3333; attach with the ELF as symbols only -- noload(Renode already loaded it):<cross>-gdb build/bin/<Platform>/main.elf -ex 'target remote :3333'Ctrl-C halts the live target;
monitor <cmd>passes to the Renode console. Projects wrap this inredo renode/debug_renode.shscripts beside the assembly ortest_renodedirs -- discover them before hand-rolling. Server in a different container: attach viahost.docker.internal:3333.- JTAG/SWD hardware: the probe's GDB server runs on the host (USB);
gdb attaches (
target [extended-]remote host.docker.internal:<port>from a container,localhost:<port>on host) and owns the bring-up -- script it (gdb -x bringup.gdb). Server variants differ in monitor syntax:- OpenOCD (open example: adamant_example's Raspberry Pi Pico --
src/assembly/pico/main/{debug.sh,config.gdb,program.sh}and its README): serveropenocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg, port 3333; bring-upmonitor reset init,load,continue; one-shot flash-without-debug viaopenocd ... -c "program <elf> verify reset exit". - SEGGER J-Link: port 2331 typical; bring-up
monitor reset,monitor halt,file <elf>,load(needs a RAM-loader link), then break/continue. Start the server with its all-interfaces flag or a containerized gdb cannot reach it. A serial/UART cable beside the probe carries the console/telemetry channel -- it is the capture path, not the debug transport.
- OpenOCD (open example: adamant_example's Raspberry Pi Pico --
Post-mortem: crashes and the Last Chance Handler
When an exception escapes on the flight target, GNAT's
__gnat_last_chance_handler hook fires and Adamant's LCH pattern captures a
Packed_Exception_Occurrence (exception name, message, and a stack-trace
address array) and broadcasts it as a telemetry packet until watchdog reset.
Framework components involved (all open, in src/components/):
| Component | Role in debugging |
zero_divider | Commandable, magic-number-protected crash trigger -- the LCH test fixture; also supplies the LCH packet definition |
last_chance_manager | Dumps/clears the non-volatile copy of the last exception by command; data product doubles as an "LCH fired" flag |
stack_monitor | Per-task stack + secondary-stack usage percent, per tick |
cpu_monitor / queue_monitor | Per-task CPU windows / queue high-water marks |
memory_dumper | Command-driven dump of configured memory regions |
logger + gnd/bin/decode_event_log.py | Post-mortem circular-buffer event dump -> decoded event list |
Triage rules (each learned from a real incident -- details and the full
runbook in references/post-mortem-and-lch.md):
- A hung test suite IS a crash until proven otherwise. The LCH stops telemetry; downstream waits spin forever, so the failure never reports. Check whether housekeeping sequence counts froze.
- Symbolize against the same build. Generate a symbol table at build
time (
<cross>-nm -C -n main.elf > main.nm); resolve trace addresses by nearest-symbol-below, or<cross>-addr2line -f -C -e main.elf <addr>for file:line. A symbol file from any other build resolves plausible garbage. - The crashed target is still attachable: Renode's GDB server works while the CPU spins in the LCH -- capture the exception occurrence and registers via a batch gdb script, no reproduction needed.
- Interrupt-context traps record only ONE frame (the unwinder cannot cross the trap handler); recover the real chain by re-running paused with a hardware breakpoint at the LCH entry.
scripts/symbolize_traceback.py (in this skill) resolves pasted traceback
addresses against an nm dump or ELF -- see the reference for usage.
Target/compiler-level debugging
"Works on Linux, traps on the flight target" is a class, not a fluke:
undefined behavior is target-dependent, so cross-run the unit tests on the
flight target (redo test_renode where the project provides it) rather
than trusting host green. The known pitfall families -- alignment-erroneous
overlays, scalar-storage-order codegen bugs, per-flag workarounds pinned in
gpr files, and the post-rebase stale-generated-code signatures -- are in
references/target-pitfalls.md with their proof techniques (bit-pattern
analysis, disassembly around the faulting PC, RISC-V trap-register
decoding).
Project tooling discovery
Closed-source projects commonly ship debug helpers the framework does not: symbolizers, per-task stack maps, UART/log capture scripts, scripted gdb bring-ups. Before hand-rolling, look in the project's ground tooling and assembly directories:
ls gnd/bin/ | grep -iE 'stack|trace|decode|symbol|log'
ls src/assembly/<name>/main/*.sh src/assembly/<name>/main/*.gdbPrefer the project's own scripts -- they encode target-specific details (ports, ELF paths, register sets) this skill keeps generic.
Checklist
- Pick the layer: failing test in hand -> interactive gdb; crash/hang already happened -> post-mortem; target-only misbehavior -> pitfalls ref
- Rebuild so symbols match source; pick the
-O0target if stepping - Interactive:
catch exceptionbeforerun; breakpoints byfile:line. A hung/stalled/parked task, a mystery write, or a packed-layout mismatch means readreferences/gdb-advanced-inspection.md-- generic gdb gets you a backtrace; the reference has the Adamant-specific moves (for a hang,thread apply all bt+ mapping the parked thread to its component by the tick-handler frame --info tasksis unreliable for Adamant tasks; see the reference), so read it rather than improvising - Cross: server first (Renode/JTAG), then cross gdb;
loadonly on JTAG - Post-mortem: capture the LCH output (telemetry, NV dump via
last_chance_manager, UART log, or gdb attach to the spinning target) - Symbolize addresses against the SAME build's nm dump or ELF
- Suite hang: check for frozen housekeeping counters before debugging the test script
- Target-only bug: reproduce under Renode; a PROGRAM_ERROR raise with no
message usually means a runtime CHECK fired, not a trap (catch the
exception or the
__gnat_rcheck_*call -- see the pitfalls reference); decode trap registers only for genuine traps, and read CSRs through the emulator monitor if the GDB stub does not expose them
Common Errors
break Package.Proc"not defined" -- usefile:lineor the mangledpkg__procname fromnm.- Breakpoint never hits / lines mismatch -- stale binary; gdb warned
"Source file is more recent than executable"; rebuild.
More generally, a reproduction or nm/objdump signature that contradicts
the source means stale objects until proven otherwise:
redo clean_all(not a shallowredo clean) at the component, rebuild, re-verify -- see the stale-objects section ofreferences/target-pitfalls.md. gdb: command not foundin the container -- bare shell; enter the activated environment (gdb lives in the toolchain, not/usr/bin).- No source listing on host gdb -- DWARF has container paths;
cdto the source dir orset substitute-path. - Stepping jumps around on a bareboard ELF -- attached to an
-O2image; build and load the_Debugvariant. loadfails/hangs against Renode -- Renode alreadyLoadELFed; attach with symbols only (loadbelongs to the JTAG flow).- Expected a debug build from
DEBUG=1-- that is build verbosity; the target selects switches, and tests/Linux are already debug. - Cannot reach the GDB server from the container -- server listening
on another host/container's localhost; use
host.docker.internal:<port>and all-interfaces server flags. - Symbolized trace names look wrong / nonsensical -- symbol file and ELF are from different builds; regenerate the nm dump from the exact ELF that was running.
- Suite "hangs" with no failure -- likely an LCH crash upstream: telemetry died, waits spin. Triage as a crash (post-mortem reference), not as a slow test.
- Single-frame stack trace from an interrupt trap -- expected; the unwinder stops at the trap boundary. Use the paused-emulation + hardware-breakpoint recovery in the post-mortem reference.
- Assertions "fixed" by disabling them -- never ship
Assertion_Policy (Ignore)to hide an LCH: the code then runs on the bad input and produces garbage instead of a diagnosable crash. Fix the dependency (fetch + early-return on non-Success).
Improving this skill in the field
Everything in this skill was demonstrated on a real program, and field use has more than once falsified content that was merely plausible. Hold the skill itself to the same bar, while the evidence is still in front of you:
- When a debugging task goes beyond what is written here -- a failure family, tool behavior, target quirk, or workaround this skill does not cover, or a documented move that did not work as described -- capture it. Silently working around a gap leaves it for the next debugger.
- New content meets the diagnosis standard: demonstrated, not inferred. Record what you observed (the command, the output, the failure signature and its resolution), never what you believe should happen. If you falsified a claim here, correct it and state what you observed instead -- a wrong confident claim is worse than a gap.
- Keep it generic. No project names, component or symbol names,
memory addresses, platform files, or naming motifs from the program
you were debugging -- placeholders only (
<addr>,<component>, "the project's LCH"). Project-specific guidance belongs in that project's own skill, not here. - Extend, don't duplicate. Search this file and the references for an existing section on the topic first and sharpen it in place; put quick moves here and depth in the layer-appropriate reference, and add a routing cue (References blurb, selector row) so the new content is findable.
- If your task does not permit editing skills (some validation and review tasks forbid it), put the proposed change in your report verbatim -- the gap, the observed evidence, and suggested wording -- so a maintainer can fold it in. Version control of skill edits belongs to the orchestrator or maintainer either way.
References
references/gdb-advanced-inspection.md-- watchpoints (who mutates this?), Ada task/thread triage (info tasks,thread apply all bt, live attach), packed-record vs raw-memory views, conditional/scripted stops, signals vs cross-target traps, core-file handoff, Renode monitor passthrough. Read when breakpoints + catchpoints are not enough to demonstrate a mechanism.references/post-mortem-and-lch.md-- LCH anatomy and wire format, capture paths (telemetry / NV dump / UART / gdb attach), symbolization pipeline + script usage (including extracting a trace from a raw binary capture), interrupt-context trap recovery, RISC-V trap register decoding, crash root-cause checklist, suite-hang signatures, and the headless-emulator bench-operations runbook (launch form, port hygiene, kill patterns). Read when a crash or hang already happened, or before any scripted emulator session.references/target-pitfalls.md-- works-on-Linux-traps-on-target families with reproducers and proof techniques; compiler-bug workaround flag pinning; post-rebase stale-codegen signature table; runtime debug-variant builds; monitor components for resource debugging. Read when behavior differs between host and flight target, or after a rebase produces baffling compile errors.scripts/symbolize_traceback.py-- stdlib-only resolver: traceback addresses + (nm dump | ELF + binutils prefix) -> symbol+offset and optional file:line.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

