Delve — Structure#

Layout pattern#

Standard Go Layout (cmd/internal/pkg) with service layer extension

Delve uses the standard cmd/ + pkg/ layout, augmented with a top-level service/ directory that houses the protocol and server layer. This is a disciplined single-binary project: there is one binary (dlv), and the entire codebase is organized around the vertical slices of that binary’s responsibility. The pkg/ vs service/ split cleanly separates reusable process-control libraries from the networking/protocol layer.

Directory map#

delve/
├── cmd/
│   └── dlv/                   # Single binary entry point
│       ├── main.go            # Thin launcher: telemetry init, CGO flags, cobra exec
│       ├── dlv_test.go        # Integration tests for the dlv command
│       ├── tools.go           # tooling imports (go:generate etc.)
│       └── cmds/              # Cobra command tree
│           ├── commands.go    # All subcommands: debug, test, exec, attach, dap, ...
│           └── helphelpers/   # Auto-generated CLI help text
│
├── pkg/                       # Reusable, importable packages
│   ├── proc/                  # CORE: process abstraction + debugger engine (56 files)
│   │   ├── native/            # OS debugging backend (ptrace / Windows APIs) (58 files)
│   │   ├── gdbserial/         # GDB remote protocol client backend (6 files)
│   │   ├── core/              # Core dump reader backend (5 files)
│   │   ├── internal/
│   │   │   └── ebpf/          # eBPF non-stop tracing backend (6 Go files + C code)
│   │   │       ├── bpf/       # C source: trace.bpf.c (compiled via Docker)
│   │   │       └── build/     # Docker build scripts for eBPF objects
│   │   ├── amd64util/         # x86-64 instruction utilities
│   │   ├── linutil/           # Linux-specific utilities (9 files)
│   │   ├── fbsdutil/          # FreeBSD-specific utilities
│   │   ├── macutil/           # macOS-specific utilities
│   │   ├── winutil/           # Windows-specific utilities
│   │   ├── evalop/            # Expression evaluation opcodes (3 files)
│   │   ├── debuginfod/        # debuginfod server client for remote DWARF
│   │   └── test/              # Test helpers (protest pkg) for proc tests
│   │
│   ├── dwarf/                 # Custom DWARF parser (Go-aware extensions)
│   │   ├── godwarf/           # Go-specific DWARF type reading (7 files)
│   │   ├── frame/             # .debug_frame / .eh_frame parser (5 files)
│   │   ├── line/              # .debug_line (line number table) parser (5 files)
│   │   ├── op/                # DWARF expression evaluator (4 files)
│   │   ├── leb128/            # LEB128 integer encoding (5 files)
│   │   ├── loclist/           # .debug_loc location lists
│   │   ├── reader/            # Incremental DWARF entry reader
│   │   ├── regnum/            # DWARF register number mappings per arch (6 files)
│   │   └── dwarfbuilder/      # DWARF builder (for test fixtures)
│   │
│   ├── terminal/              # Interactive CLI / REPL (15 files)
│   │   └── starbind/          # Starlark script binding for terminal (5 files)
│   ├── config/                # .delverc config file loading
│   ├── logflags/              # Structured logging flags
│   ├── gobuild/               # Go build integration (compile + gcflags)
│   ├── locspec/               # Location spec parsing ("file:line", "func")
│   ├── astutil/               # Go AST utilities for expression parsing
│   ├── goversion/             # Go runtime version detection
│   ├── debugdetect/           # Detect debugger presence (7 files, platform-specific)
│   ├── elfwriter/             # ELF file writer (for DWARF injection)
│   ├── version/               # Delve version constants
│   └── internal/              # pkg-internal shared utilities
│
├── service/                   # Protocol servers and high-level debugger API
│   ├── api/                   # Shared JSON-serializable types (8 files)
│   ├── debugger/              # Debugger struct: high-level ops (launch, attach, ...) (4 files)
│   ├── rpc2/                  # JSON-RPC 2.0 server and client
│   ├── dap/                   # Debug Adapter Protocol server (8 files)
│   │   └── daptest/           # DAP test client helpers
│   ├── rpccommon/             # Shared RPC utilities
│   ├── internal/
│   │   └── sameuser/          # Security: verify client is same OS user (4 files)
│   ├── test/                  # Integration tests for the service layer
│   ├── server.go              # Server start-up and listener management
│   ├── client.go              # RPC2 client implementation
│   ├── config.go              # Service-level config types
│   ├── listenerpipe.go        # In-process listener for headless mode
│   └── rpccallback.go         # RPC callback interface
│
├── _fixtures/                 # Test fixture Go source files (compiled during tests)
│   └── <many subdirs>         # One dir per special test scenario (cgo, plugins, asm, etc.)
├── _scripts/                  # Go-based build helper scripts (make.go)
├── Documentation/             # User and internal documentation
│   ├── api/                   # JSON-RPC / DAP API docs and how-to guides
│   ├── cli/                   # Auto-generated CLI reference
│   ├── internal/              # Porting notes, architecture docs
│   ├── installation/          # Platform install guides
│   ├── usage/                 # User-facing usage guides
│   └── AI/                    # AI/IDE integration guidance
├── assets/                    # Logo and static assets
└── vendor/                    # Vendored dependencies

Entry points#

BinaryPathPurpose
dlvcmd/dlv/main.goSingle entry point for the Delve debugger. Initializes Go telemetry, sets CGO_CFLAGS=-O0 -g (disable optimizations on debugged programs), then delegates to Cobra command tree via cmds.New(false).Execute().

There is exactly one binary. All debugger modes (interactive, headless, DAP server, attach, exec, trace, eBPF) are subcommands of dlv.

Package organization#

Internal packages#

PackagePurpose
pkg/proc/internal/ebpfeBPF tracing backend; internal prevents external import of this unstable backend
pkg/internalShared utility types used across pkg/ only
service/internal/sameuserSecurity helper ensuring RPC clients share the OS user; not part of the public API

Public packages (pkg/)#

PackagePurpose
pkg/procCore process abstraction: Process, ProcessInternal, Target, TargetGroup, Thread, Goroutine, variable evaluation, breakpoint management
pkg/proc/nativeOS-native debugging backend: ptrace (Linux), mach exception ports (macOS), Windows debug APIs
pkg/proc/gdbserialGDB remote serial protocol client; enables debugging under LLDB, rr, and hardware targets
pkg/proc/coreCore dump (ELF/Mach-O) reader; read-only process backend for post-mortem analysis
pkg/proc/internal/ebpfeBPF-based non-stop function tracing; unique backend that never halts the target
pkg/dwarf/*Custom DWARF parser family: Go-specific type reading, frame/line/loc parsers, expression evaluator, register number tables
pkg/terminalInteractive REPL: command dispatch, history, Starlark scripting, output formatting
pkg/locspecLocation specification parser: "file:line", "package.Function", "*addr"
pkg/gobuildInvokes the Go toolchain to compile target programs with debug flags
pkg/configReads and writes .delverc user configuration files
pkg/logflagsStructured logger setup (used for internal Delve debug logging)
pkg/goversionDetects and parses the Go runtime version of the target process
pkg/debugdetectDetects whether the process is running under a debugger (anti-debug detection for tests)
pkg/elfwriterWrites ELF files with injected DWARF sections (used for test fixture generation)
pkg/astutilGo AST helpers for parsing expressions typed by the user
pkg/versionVersion string constants for the dlv binary

Layering#

The package dependency graph flows strictly top-down:

cmd/dlv (CLI)
    └── service/debugger → service/api
            ├── service/rpc2   (JSON-RPC 2.0 server)
            └── service/dap    (DAP server)
                    └── pkg/proc  (process abstraction)
                            ├── pkg/proc/native
                            ├── pkg/proc/gdbserial
                            ├── pkg/proc/core
                            ├── pkg/proc/internal/ebpf
                            └── pkg/dwarf/*

This is clean layered architecture: upper layers never import lower-layer internals. pkg/proc and pkg/dwarf are entirely self-contained with no dependency on service/. There are no circular imports.

Build system#

  • Build tool: make (delegating to go run _scripts/make.go) + standard go build
  • Key targets:
    • make build — produces the dlv binary via go run _scripts/make.go build
    • make install — installs dlv to $GOPATH/bin
    • make test — runs go vet then go run _scripts/make.go test
    • make vendor — updates vendored dependencies
    • make build-ebpf-image — builds Docker image with clang-12 for eBPF compilation
    • make build-ebpf-object — compiles pkg/proc/internal/ebpf/bpf/trace.bpf.c inside Docker to produce architecture-specific .o files
  • Docker: Yes, but only for the eBPF backend. The eBPF C code must be compiled with clang-12 (newer versions produce BPF bytecode that fails the kernel verifier). The Docker image pins this exact version. The rest of Delve builds with standard go build.
  • Vendoring: Yes — a vendor/ directory is present and maintained.

Notable structural decisions#

  1. Single binary, multiple modes: All functionality (interactive, headless RPC, DAP server, eBPF tracer, core dump reader) lives in one dlv binary. There are no separate dlv-server / dlv-client binaries. The subcommand tree (cmds/commands.go) determines the mode at runtime.

  2. Four pluggable backends under one interface: pkg/proc/native, pkg/proc/gdbserial, pkg/proc/core, and pkg/proc/internal/ebpf all satisfy the same ProcessInternal interface. This is a textbook application of the strategy pattern at the package level — the pkg/proc layer is completely backend-agnostic.

  3. Custom DWARF parser as a standalone sub-library: pkg/dwarf/ is large enough to be its own library (~30+ files across 9 sub-packages). Rather than vendoring a third-party DWARF library, Delve built its own because stock Go DWARF tooling (even debug/dwarf) doesn’t handle Go-specific extensions (goroutine metadata, interface types, generics). This is a deliberate bet on correctness over convenience.

  4. Platform-specific code isolated at filename level only: There are no if runtime.GOOS switches in the generic files. Instead, every platform-specific implementation is in a file named *_linux.go, *_darwin.go, *_windows.go, *_freebsd.go, regs_amd64.go, etc. This makes portability explicit, readable, and compilable by the toolchain without runtime overhead.

  5. _fixtures/ as compilable test programs (not binaries): Test fixtures are Go source files that are compiled during test execution using pkg/gobuild. This means fixtures are always compiled with the Go version under test, ensuring correct DWARF output for each Go release. Pre-compiled binaries would immediately become stale.

  6. eBPF backend requires Docker for reproducibility: The eBPF C code compilation is containerized to pin clang-12, since the kernel BPF verifier rejects bytecode from newer clang versions. This is an unusual but pragmatic decision that trades build simplicity for correctness.