Directory Structure¶
This document maps the gdbforge repository packages to their responsibilities.
Companion docs: ARCHITECTURE.md · DEPENDENCIES.md · DEVELOPER_GUIDE.md
Table of contents¶
- Framework vs application
- Repository tree
- Command entry points
- internal/gdbforge
- internal/mcp
- internal/serialmux
- internal/gdb
- internal/dlv
- docs
- Dependency graph
- What belongs where
Framework vs application¶
The terminal UI framework lives in a separate module, termforge. Everything in this repository is the debugger application.
| Kind | Where | Notes |
|---|---|---|
| Framework | github.com/yairgd/termforge (+ /platform, /commands, /collections, /ptyx, /execcli, /devport) |
Reusable TUI — see termforge docs |
| Application | internal/app, internal/gdb, internal/dlv, internal/mcp, internal/gdbforge/* |
Debugger-only |
| Entry point | cmd/gdbforge/main.go |
Version stamp + argv dispatch only — no application logic |
| App events | internal/gdbforge/events |
GdbOutputMsg (MI bridge) |
| App state | internal/gdbforge/debugstate |
Debugger session fields |
| App DTOs | internal/gdbforge/models, parse, mitext |
Break/thread/stack types + MI parsers / string helpers |
| Script host | internal/luahost |
Generic Lua VM; debugger bindings come from internal/gdbforge/luadebug |
Import guardrails: task check-imports.
Repository tree¶
gdbforge/
├── cmd/
│ ├── gdbforge/ # main.go only — version, argv dispatch, exit codes
│ ├── docserve/ # Documentation HTTP server
│ └── flowdoc/ # Code-flow catalog generator (build-time)
├── internal/
│ ├── app/ # Debugger app — composition root (DebuggerApp + *Ctl)
│ ├── ttyhold/ # --hold-inferior-tty helper (re-executed binary)
│ ├── gdbforge/ # Debugger app layer
│ │ ├── models/ # Break/thread/stack DTOs
│ │ ├── parse/ # MI parsers
│ │ ├── mitext/ # MI string unescape / prompt tokens
│ │ ├── debugstate/ # Debugger session state
│ │ ├── events/ # GdbOutputMsg (MI bridge)
│ │ ├── domain/ # DebugDomain surface for MCP
│ │ ├── debugger/ # Backend-facing debugger interfaces
│ │ ├── backend/ # backend.Backend — GDB/Delve policy surface
│ │ ├── layout/ # Named workspace builders
│ │ ├── luadebug/ # Debugger Lua bindings
│ │ ├── persist/ # Breakpoint + history YAML
│ │ └── widgets/ # Debugger panes (no gdb/mcp imports)
│ ├── gdb/ # GDB MI2 backend
│ ├── dlv/ # Delve backend (rpc2 + CLI PTY)
│ ├── mcp/ # HTTP/MCP surface
│ ├── luahost/ # Lua VM + generic script API
│ └── serialmux/ # UART ↔ PTY mux (kgdb one-cable)
│ # Tests: *_test.go next to each package
├── docs/ # gdbforge documentation
├── lua/ # Shipped Lua workflows (embedded via lua/fs.go)
├── examples/ # Sample programs to debug
│ └── zephyr_cortex_r5/ # Zephyr app for kv260_r5 (RTT or UART0 console)
├── scripts/ # check_imports.sh, zynqmp-park-el3.sh (ZynqMP JTAG prep)
├── go.mod # requires github.com/yairgd/termforge
├── Taskfile.yml
└── CONTRIBUTING.md
The UI framework is not in this tree — it is the termforge module. See
termforge: package layout.
Command entry points¶
| Path | Binary | Purpose |
|---|---|---|
cmd/gdbforge/main.go |
gdbforge |
Thin entry point: version stamp, argv dispatch, exit codes — all logic is in internal/app |
cmd/docserve/main.go |
docserve |
Serves docs/ as HTML with Mermaid |
cmd/flowdoc/ |
flowdoc |
Generates and validates docs/flows/flows.json |
A framework showcase binary lives in the termforge repository at
cmd/demo.
internal/app layout¶
DebuggerApp is a composition root: it wires backend.Backend and host-backed *Ctl controllers (initControllers). Domain state lives on controllers; orchestration (stop pipeline, modes, layouts) stays on the app. See facade.go.
cmd/gdbforge/main.go only stamps version, dispatches -version and
--hold-inferior-tty (internal/ttyhold), then calls app.ParseFlags and
app.NewDebuggerApp. Everything below lives in internal/app; the package is
still one Go package, so controllers keep their unexported host interfaces.
| File | Responsibility |
|---|---|
flags.go |
ParseFlags, SessionConfig, -g gdb\|dlv |
app.go |
DebuggerApp — embeds LayoutShell + DebugSession; NewDebuggerApp, Close |
facade.go |
Composition-root comment (layers + hosts) |
debug_session.go |
DebugSession — backend init, GDB widgets, debug *Ctl lifecycle |
layout_host.go |
layoutHost + adapters for LayoutShell |
lua_host.go / dlv_host.go |
luaHost / dlvHost + adapters |
controllers.go |
initControllers, host compile checks, adapter forwards |
setup.go |
InitB — initLayoutShell, mode handlers, cmdline |
builtins.go |
Shell builtins + DebugSession.init |
gdb_console.go |
consoleCtl — GDB/Delve submit / paint / quit / suspend |
io_console.go |
inferiorIOCtl — Inferior PTY bridge + OutputWidget intents |
console_wire.go |
Shared wireConsole / SetOn* for GDB / IO / Exec |
breakpoints.go |
breakCtl — BP sync / toggle / delete / Code+Asm gutters; YAML restore |
assembly.go |
asmCtl — Assembly widget, :b asm, preferAsm / autoAsm |
buffers.go |
bufferCtl — per-path CodeWidgets, :b / :edit |
debug_info.go |
debugInfoCtl — Thread / call-stack view sync + activate |
completion.go |
completionCtl — CompletionMenu → CompletionView |
search.go |
searchCtl — / n/N */# on focused pane |
dlv_ctl.go |
dlvCtl — Delve confirm gate; frame-nav / suppress-stop bookkeeping |
coalesce.go |
coalesceRunner for BP / debug-info refresh bursts |
command_tree.go |
ExapData colon-command DSL |
keybindings.go |
InitKeyBindings (n/s/c, Space, …) |
actions.go |
Command actions (focus, split, quit, :! Exec, …) |
input.go |
HandleInterrupt (thin dispatch), mode keys, global Ctrl-Z |
layout.go |
:layout (+ optional asm); layout builders |
layout_behavior.go |
Per-layout normal-mode key policy |
focus.go |
Focus introspection (focusedCode, …) |
workspace.go |
LayoutShell — pane marks; embedded on app |
workspace_policy.go |
Code/GDB/last activation, FocusCode |
workspace_place.go |
placeCodeInSlot, logo slot, sticky-GDB swap / JumpBack |
workspace_layout.go |
ApplyLayout mounts layout WidgetTree onto Tab |
code_nav.go |
Thin Workspace delegates; activeCodeWidget; sendGdbExec via Backend.MapExec |
inferior_tty.go |
:set inferior-tty (GDB live / DLV restart); builds the internal/ttyhold command line |
events.go |
Debugger domain events (BreakpointsChangedMsg) |
stopped.go |
Stop pipeline; presentLocation (Code vs autoAsm); thread/frame select |
lua.go |
luaCtl — ModeLua; :lua / embedded script builtins |
debug_domain.go |
appDebugDomain → domain.DebugDomain for MCP |
internal/ttyhold¶
--hold-inferior-tty helper. gdbforge re-executes its own binary inside the
external terminal; the helper keeps that window open and releases its pts
(TIOCNOTTY) so the inferior can claim it as a controlling terminal. It is
argv-level plumbing dispatched from main(), holds no application state, and
must not import internal/app (enforced by scripts/check_imports.sh).
Build all commands:
internal/gdbforge¶
gdbforge application layer — backend policy, shared models, layout builders, and debugger views.
| Path | Responsibility |
|---|---|
backend/ |
Backend iface — semantic debugger ops + capability flags; GDB vs Delve policy |
backend/gdb_backend.go |
GDBBackend — wraps *gdb.GDBClient; MI strings internal |
backend/dlv_backend.go |
DLVBackend — wraps *dlv.Client; rpc2 + CLI |
backend/ops.go |
Shared Exec, frame/thread select, navigation helpers |
backend/break_cmds.go |
Breakpoint semantic commands |
backend/dlv_rpc.go |
Delve rpc2-backed queries and ops |
backend/refresh.go |
Shared threads/stack query helpers |
debugger/ |
Cross-backend stop/console types — StopInfo, ConsoleUpdate, InferiorIO |
debugger/stop.go |
Stop pipeline input |
debugger/update.go |
Console update from backend parsers |
debugger/inferior_io.go |
Inferior routing interface (InferiorInternal / external) |
models/breakpoints.go |
BreakpointList — shared BP model (GUI + MCP) |
models/types.go |
BreakInfo, BreakGutter, GuttersByLine / GuttersByAddr |
models/threads.go |
ThreadList — stop snapshot |
models/callstack.go |
CallStack — frame snapshot |
models/assembly.go |
AssemblyList / AsmLine |
parse/disassemble.go |
Disassembly parse for Assembly pane |
persist/breakpoints.go |
./.gdbforge/breakpoints.yaml save/load |
domain/domain.go |
DebugDomain — peer-controller surface (AI now; future Lua) |
layout/ |
Named workspace trees (default, panels, classic, wide) — geometry only |
layout/default.go |
Multi-pane: Code/GDB left; IO / BP / Threads / Callstack right |
layout/panels.go |
Code/GDB left; IO over (Threads|Callstack) over Breakpoints |
layout/classic.go |
Original cgdb: full-width Code over GDB |
widgets/code_widget.go |
Source view; Space → break toggle; gutters via BreakGutter |
widgets/assembly_widget.go |
:b asm; addr breakpoints; AssemblyHost |
widgets/break_paint.go |
Shared gutter colors (disabled / conditional / enabled) |
widgets/breakpoint_widget.go |
:b breakpoint; embeds TableWidget; BreakpointHost |
widgets/thread_widget.go |
:b threads; embeds TableWidget; ThreadHost |
widgets/callstack_widget.go |
:b callstack; embeds TableWidget; CallStackHost |
widgets/file_list_widget.go |
:edit picker; embeds TableWidget (# · File); FileListHost |
widgets/output_widget.go |
:b io; CompositeTerminal + WireInferior |
widgets/about_widget.go |
Built-in About page (singleton via :b about) |
widgets/help_widget.go |
Viewport user manual (:help / :b help) |
widgets/logo_widget.go |
Startup splash in the code leaf until source loads |
widgets/gdb_widget.go |
GDB/Delve terminal — CompositeTerminal + WireCLI |
widgets/exec_widget.go |
Exec/shell terminal — CompositeTerminal + WireExec |
widgets/lua_widget.go |
Lua script panes |
internal/mcp¶
In-process GDB tool service for AI / MCP (same Session as the UI).
| File | Responsibility |
|---|---|
gdb_service.go |
GdbMcpService — GdbCommand under WithWrite + output capture |
tools.go |
LLM tool dispatch → gdbforge/domain.DebugDomain |
break_list.go |
Parse -break-list / pending BPs into BreakInfo |
thread_info.go |
Parse -thread-info into ThreadInfo |
stack_frames.go |
Parse -stack-list-frames into StackFrame |
agent.go |
:AI LLM loop (Anthropic / OpenAI) with domain tools + gdb_command |
internal/serialmux¶
Shared UART mux for kgdb on one serial cable — bridges hardware UART to virtual PTY legs.
| File | Responsibility |
|---|---|
mux.go |
Mux — devport.Open (UART) + ptyx.Open (console + gdb legs); owner routing |
registry.go |
One mux per device path |
termios_ioctl_*.go |
Raw mode on PTY masters (not the UART) |
See PTY_ARCHITECTURE.md and KERNEL_KGDB.md.
internal/gdb¶
GDB MI2 backend. Owns GDB PTY + inferior TTY; parses MI. Implements ptyx.Session.
| File | Responsibility |
|---|---|
gdb_client.go |
GDBClient — CLI + MI + inferior *ptyx.TTY; new-ui mi2 bootstrap |
mi.go |
MI string decode, field extraction, tab expansion |
mi_msg.go |
Batch line parser → structured MiMsg (helper / tests) |
mi_state.go |
Stream splitter: PushRaw → MiUpdate per complete MI line |
Rule: no imports from termforge. GDB MI → GdbOutputMsg → parser; inferior/CLI bytes → WireTTY → CompositeTerminal.
Application orchestration for gdbforge lives in internal/app (DebuggerApp embeds termforge.App and implements termforge.AppApi); cmd/gdbforge/main.go only wires argv to it.
internal/dlv¶
Delve backend (peer of internal/gdb). Headless dlv exec + rpc2 + dlv connect CLI PTY; inferior via --tty.
| File | Responsibility |
|---|---|
client.go |
Client — headless child, rpc2 dial, dlv connect PTY, inferior TTY |
rpc_dial.go |
DialRPC, PickListenAddr |
rpc_convert.go |
Delve api.* → models.* row types |
input_state.go |
Stream splitter: PushRaw → Update (stops, prompts, [Y/n]?, BP notifies) |
confirm.go |
ConfirmGate for Delve yes/no prompts (suspended breakpoint after exit) |
complete.go |
Console Tab: command names + funcs ^<prefix> locspec completion |
parse.go |
Text parsers for CLI fallback scrape → MCP row types |
Selected with gdbforge -g dlv. See DEBUGGER_INTEGRATION.md.
docs¶
| Path | Purpose |
|---|---|
README.md |
Documentation index |
OVERVIEW.md |
Vision and comparison |
ARCHITECTURE.md |
High-level architecture |
PTY_ARCHITECTURE.md |
Dual PTY master/slave, :b io, external tty, Delve TCP |
| (moved to termforge) | Widget/canvas/grid details |
WINDOW_MANAGEMENT.md |
Splits, tabs, CmdLine |
| (moved to termforge) | Grid, cells, diff rendering |
INPUT.md |
Keyboard, modes, commands |
COMMAND_SYSTEM.md |
Command tree, DSL, rest-args |
EXEC_SHELL.md |
:! exec panes, jump list |
DEBUGGER_INTEGRATION.md |
GDB MI / Delve details (see also PTY_ARCHITECTURE) |
PLUGINS.md |
Lua extensibility plans |
DIRECTORY_STRUCTURE.md |
This file |
DEPENDENCIES.md |
Go module + internal package rules |
ROADMAP.md |
Status and plans |
DEVELOPER_GUIDE.md |
Contributor onboarding |
HOSTING.md |
Docs server |
diagrams/*.mermaid |
Standalone diagram sources |
www/ |
Browser viewer assets |
serve.sh |
Launch docs server |
Dependency graph¶
Full detail: DEPENDENCIES.md.
flowchart BT
subgraph ext["external module: termforge"]
tf["termforge<br/>(engine root · needs tcell)"]
tfplatform["termforge/platform"]
tfptyx["termforge/ptyx"]
end
widgets["internal/gdbforge/widgets"]
layoutpkg["internal/gdbforge/layout"]
backend["internal/gdbforge/backend"]
gdb["internal/gdb"]
dlv["internal/dlv"]
app["internal/app"]
mainpkg["cmd/gdbforge/main.go"]
widgets --> tf
layoutpkg --> tf
gdb --> tfptyx
gdb --> tfplatform
dlv --> tfptyx
backend --> gdb
backend --> dlv
mainpkg --> app
app --> tf
app --> widgets
app --> layoutpkg
app --> backend
gdb -.->|"must NOT import"| tf
dlv -.->|"must NOT import"| tf
widgets -.->|"must NOT import"| gdb
Only internal/app, internal/gdbforge/widgets, and internal/gdbforge/layout touch
the termforge engine root. Backends reach the headless subpackages only, so they stay
testable without a terminal.
What belongs where¶
| Question | Package |
|---|---|
| Application model (domain state)? | internal/gdbforge/models |
| Peer control surface (AI / Lua)? | internal/gdbforge/domain (+ internal/app/debug_domain.go impl) |
| Service (external I/O)? | internal/gdb, internal/dlv, or a new backend package |
| GDB MI parsing? | internal/gdb + internal/gdbforge/parse |
| Debugger pane (view of a model)? | internal/gdbforge/widgets |
| Named workspace preset? | internal/gdbforge/layout |
| Key binding in normal mode? | internal/app/keybindings.go + input.go |
| Debugger session state? | internal/gdbforge/debugstate |
| Breakpoint / history persistence? | internal/gdbforge/persist |
| Debugger Lua binding? | internal/gdbforge/luadebug |
| Colon command for the debugger? | internal/app/command_tree.go |
| Compose backends + controllers + UI? | internal/app/setup.go |
| Split pane layout / window manager? | termforge — not this repo |
| Generic widget, scroll primitive, box borders? | termforge — not this repo |
| Interaction mode plumbing? | termforge (platform.AppState via App) |
Two questions settle most cases:
- "Can this be unit-tested without a terminal?" If yes, keep it out of any package that imports the termforge engine root.
- "Would the stock dashboard want this?" If yes, it belongs upstream in termforge, not here.
Related documentation¶
- DEVELOPER_GUIDE.md — file walk order
- ARCHITECTURE.md — subsystem overview