Debugging on Metamodule with coding agents (pi, openai codex, tmux, etc)

As mentioned Will the Befaco Oneiroi be coming to the 4ms Meta?, I’ve been setting up a way to debug using an AI agent to assist with the process (as I’m not super familiar with GDB), and thought I’d share. Fair warning, the tool is “YOLO” by default.

Setup

I’ve used a few agentic tools in the past but have recently being enjoying the minimal pi agent. There’s a great article from the developer here, and one example he shows is using gdb to debug a faulty c program using tmux.

It works well if you set up SKILLS for some common tasks, and I found some good starter skills in this repo, including one that teaches the agent how to use tmux: agent-stuff/skills/tmux/SKILL.md at main · mitsuhiko/agent-stuff · GitHub (also good uv skill).

Then I connected my STLINK-V3MINIE probe to the MM board following the instructions here for JTAG (i.e. add freeze jumper and wire up to JTAG header. Then in one window, in source checkout in firmware directory make start-openocd and in a new tmux session in theory we run make debug, or so I thought! In practice I found Ctrl-C commands, which interrupt the running program in gdb, get intercepted by cmake and then cmake quits (ending gdb session) - @danngreen not sure if you can reproduce, and if so maybe we can update docs? Anyway I had to run cmake to build the image then run gdb seperately:

# in a tmux session 
# build only
cmake --build --preset full -- main.uimg
# launch gdb 
/Applications/ArmGNUToolchain/12.3.rel1/arm-none-eabi/bin/arm-none-eabi-gdb --command=flashing/multi.gdbinit 

This will load the MM image into memory directly, and takes about 2 mins each time you want to do it! It will drop you into (gdb) prompt if sucessful.

Debugging with pi

Assuming pi has the tmux skill installed, you can simply run pi in the metamodule checkout and instruct in plain langauge:

Me: Following @docs/ i’ve started openocd with make start-openocd in one window and in tmux i’ve loaded firmware with make debug, tmux session named metamodule_session. can you attach and manipulate gdb? lets test continue and break to start.


tmux capture-pane -p -J -t metamodule_session:0.0 -S -120

tmux send-keys -t metamodule_session:0.0 -- 'continue' Enter; sleep 1; tmux send-keys -t metamodule_session:0.0 C-c; sleep 1; tmux capture-pane -p -J -t metamodule_session:0.0 -S -80

Done — I attached to your existing gdb in tmux session metamodule_session and ran the test sequence.

What I sent

  • continue
  • Ctrl-C (break/interrupt)

Result

GDB resumed and then halted cleanly on interrupt:

  • Program received signal SIGINT, Interrupt.
  • Current stop PC: 0xc2340a8c
  • Current frame:
    std::__atomic_base<bool>::load(...) in atomic_base.h:486

So continue/break control is working from my side.

If you want, next I can:

  • set a breakpoint at a specific function (break <symbol>)
  • do bt / inspect locals
  • continue until that breakpoint and verify hit behavior.

Then from that point on I would try hypotheses for the bug and the agent would place break points etc

(gdb) break OneiroiVCV::process
continue
Breakpoint 1 at 0xc249bfb0: file /Users/ewanhemingway/Documents/Code/VCV/OneiroiMetaModule/befaco-oneiroi/src/Oneiroi.cpp, line 802.
(gdb) continue
Continuing.
target halted in ARM state due to debug-request, current mode: System
cpsr: 0x8000011f pc: 0xc239daf4
MMU: enabled, D-Cache: enabled, I-Cache: enabled

Breakpoint 1, OneiroiVCV::process (this=0xcd546740, args=...) at /Users/ewanhemingway/Documents/Code/VCV/OneiroiMetaModule/befaco-oneiroi/src/Oneiroi.cpp:802
802			if (!patch || !bufferIn || !bufferOut) {
(gdb)

For example here it is dumping out oscillator volumes etc:

tmux send-keys -t metamodule_session:0.0 -- 'frame 0' Enter; tmux send-keys -t metamodule_session:0.0 -- 'print patchCtrls->oscUseWavetable' Enter; tmux send-keys -t metamodule_session:0.0 -- 'print ui->osc2Vol_' Enter; sleep 0.8; tmux capture-pane -p -J -t metamodule_session:0.0 -S -60

Bug solved

In this case, I got silence in a simple patch with just sawtooth up (no filter etc), but would get sound after randomising a few times. We added diagnostics to the plugin, and dumped all parameters to file in working and non-working case. Eventually the agent spotted NaNs in one of the filter sections, and even though filter was completely dry, the NaNs still infected the audio chain. So we worked out it was due to MM 32kHz sample rate and some filter cutoffs over Nyquist, which were promptly fixed!

Any questions happy to answer! I think most of this would work with other setups, key thing is the agent being able to grab the tmux output with capture-pane and send arbitrary commands to tmux via send-keys, the rest it can piece together.

3 Likes

Cool to see this!

:clap::clap::clap:

I haven’t tried any agentic gdb usage yet, but it sounds useful.

Good catch, I hadn’t noticed that (probably because I had only used J-Link and trace32 for debuggers). Running gdb directly like you did is an easy solution, if you dig into the cmake that’s all that make debug is doing anyways. You can make it a one-liner: this is my pattern for other projects:

make && arm-none-eabi-gdb --command=flashing/multi.gdbinit

Another solution is to modify the debug target to run gdb in a tmux session like this (line 61 of flashing/flashing.cmake):

  COMMAND tmux -c "${CMAKE_GDB} --command=flashing/multi.gdbinit"

I’ll add something to the docs about it.

Also, re: speed. J-Link and Trace32 are much faster (around 10x faster to load?), though the tools cost a lot more. You can make loading faster on the low-cost STLINK by reducing binary size. The easiest way to do this is to drop a bunch of built-in brands. There’s a cmake preset for this:

cmake --fresh --preset min-brands
make

This drops all the built-in brands except 4ms and should make firmware loading about 2x faster (the binary goes from 8MB to 5MB). You can fiddle with presets in CMakePresets.json if you want to enable just one brand, or something.

1 Like

Also I would add – if you are interested in playing with the firmware for non-commercial reasons, then the J-Link EDU Mini is a great choice for a debugger.

1 Like

Yes just tested now and 30s to load into memory with J-Link EDU Mini vs 2 mins for STLINK. And both much less for the min-brands. Thanks!