Skip to content

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

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:

task build
# or: for d in cmd/*/; do go build -o bin/$(basename $d) ./$d; done

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:

  1. "Can this be unit-tested without a terminal?" If yes, keep it out of any package that imports the termforge engine root.
  2. "Would the stock dashboard want this?" If yes, it belongs upstream in termforge, not here.