This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Delve is a debugger for the Go programming language. This is a complex, multi-layered system that requires understanding of debugging internals, DWARF format, OS-specific process control, and Go runtime internals.
make build # Build dlv binary
make install # Install dlv to system
make uninstall # Remove dlv from systemmake test # Run all tests with vetting
make vet # Run Go vet with architecture tags
go test -run TestName ./pkg/... # Run specific test by name
go test ./pkg/proc # Run all pkg/proc tests
go test ./service/test # Run all integration testsmake build-ebpf-image # Build Docker image for eBPF compilation
make build-ebpf-object # Compile eBPF C code to object filesThe eBPF backend uses Docker to compile C code
(pkg/proc/internal/ebpf/bpf/trace.bpf.c) in a controlled environment,
producing architecture-specific .o files.
eBPF tests (TestTraceEBPF*) require elevated capabilities (CAP_BPF, CAP_PERFMON, CAP_SYS_RESOURCE) and must be run with sudo:
# Run specific eBPF test
sudo go test -v -run TestTraceEBPF3 -count 1 ./cmd/dlv
# Run all eBPF tests
sudo go test -v -run TestTraceEBPF -count 1 ./cmd/dlv
# Run eBPF tests in Docker (useful when host lacks capabilities or for CI)
docker run --privileged -v "$(pwd)":/delve -w /delve \
-e GOFLAGS="-buildvcs=false" golang:1.24-bookworm \
go test -v -run TestTraceEBPF -count 1 ./cmd/dlv
# Suppress debug output
sudo go test -v -run TestTraceEBPF3 -count 1 ./cmd/dlv 2>&1 | \
grep -v "^DEBUG"dlv debug # Compile and debug current package
dlv test # Compile and debug tests
dlv attach <pid> # Attach to running process
dlv exec <binary> # Debug pre-compiled binary
dlv dap # Start Debug Adapter Protocol serverCRITICAL: All code changes MUST follow test-driven development (TDD). This is not optional.
- RED - Write a failing test first (must fail for the right reason)
- GREEN - Write minimum code to pass the test
- REFACTOR - Clean up while keeping tests green
- VERIFY - Run full test suite before committing
- NEVER write implementation code without a failing test first
- NEVER commit code without tests
- NEVER skip tests or mark them as skipped to make CI pass
- NEVER disable existing tests - fix them or fix the code
- Each test should verify ONE specific behavior
- Test names should describe the scenario being tested
# RED - Write failing test
go test -run TestEvaluateComplexType ./pkg/proc
# Output: FAIL - undefined: evaluateComplexType (EXPECTED)
# GREEN - Implement minimum code
go test -run TestEvaluateComplexType ./pkg/proc
# Output: PASS
# REFACTOR - Clean up
go test -run TestEvaluateComplexType ./pkg/proc
# Output: PASS (must stay green)
# VERIFY - Full suite
make test- Unit tests: Co-locate with code (e.g.,
pkg/proc/variables_test.goforvariables.go) - Integration tests:
service/test/for end-to-end scenarios - Platform-specific tests: Use build tags (e.g.,
//go:build linux) - Backend-specific tests: Test each backend separately
- Test fixtures: Source code in
_fixtures/(compiled during tests, not pre-compiled binaries) - Finding fixtures directory: Use
protest.FindFixturesDir()fromgithub.com/go-delve/delve/pkg/proc/testinstead of writing custom fixture directory lookup code
- Test behavior, not implementation
- One assertion per test when possible (easier diagnosis)
- Clear naming:
Test<What>_<Scenario>_<ExpectedResult> - Use existing test utilities in
*_test.gofiles - Tests must be deterministic (no random values or race conditions)
Difficulty writing tests first indicates: (1) function doing too much, (2) tightly coupled dependencies, or (3) unclear requirements. DO NOT skip TDD - the difficulty signals a design problem.
Delve uses a layered architecture with clear separation of concerns:
CLI (cmd/dlv) → Cobra commands
↓
Service Layer (service/) → RPC2, DAP protocol implementations
↓
Debugger (service/debugger) → High-level debugging operations
↓
Process Abstraction (pkg/proc) → TargetGroup, Target, ProcessInternal
↓
Backend Implementations:
- pkg/proc/native/ → Direct OS debugging (ptrace, Windows APIs)
- pkg/proc/gdbserial/ → GDB remote protocol client
- pkg/proc/core/ → Core dump analysis
- pkg/proc/internal/ebpf/ → eBPF-based tracing (non-stop debugging)
Core of Delve's architecture with two-level interface design:
Process- Read-only public interfaceProcessInternal- Internal interface with state-modifying operationsTarget- WrapsProcessInternalwith debugging stateTargetGroup- Manages multiple processes (for exec following)Thread- OS thread abstractionMemoryReadWriter- Memory access abstraction
Backends are pluggable via ProcessInternal interface. When modifying
backends, changes usually apply to ALL implementations.
debugger/-Debuggerstruct wrappingTargetGroupwith high-level operations (launch, attach, breakpoints, stepping, variable evaluation)rpc2/- JSON-RPC 2.0 server for remote clientsdap/- Debug Adapter Protocol for IDE integrationapi/- Shared type definitions
pkg/dwarf/- Custom DWARF parser with Delve-specific featurespkg/gobuild/- Go build system integrationpkg/proc/BinaryInfo- Debug symbols, function entries, line tables
Uses filename-based conditional compilation:
- OS-specific:
*_linux.go,*_darwin.go,*_windows.go,*_freebsd.go - Architecture-specific:
regs_amd64.go,regs_arm64.go, etc. - DO NOT put platform-specific code in generic files
- In most cases platform-specific code should only be added to the
pkg/proc/nativeandpkg/proc/gdbserialbackends.
Supported: amd64, arm64, 386, ppc64le, riscv64, loong64
-
OS/arch separation: Use Go's filename-based conditional compilation. Never put platform-specific code in generic files.
-
Breakpoint types: Two kinds exist:
LogicalBreakpoint- User-visible (set at file:line or function)Breakpoint- Physical per target (multiple per logical breakpoint)
-
Testing: See "Test-Driven Development (MANDATORY)" section above.
- Parsing in
pkg/dwarf/with custom reader utilities - Operations (expressions) in
pkg/dwarf/op/ - Variable evaluation issues: check type reading
(
pkg/proc/variables.go), DWARF expression evaluation (pkg/dwarf/op/), and memory reading (Process.Memory())
pkg/proc/internal/ebpf/ is fundamentally different: uses uprobes
to trace function calls without stopping execution. C code compiles to
eBPF bytecode using Docker for reproducible builds. Manages goroutines for
event processing - be careful with shutdown.
Modifying eBPF code:
- Edit
pkg/proc/internal/ebpf/bpf/trace.bpf.c - Run
make build-ebpf-object - Go code loads
.ofiles using cilium/ebpf library
Critical - Function Prologues: Set breakpoints/uprobes at
FirstPCAfterPrologue, not fn.Entry (unless explicitly requested). Go's
stack-check prologue will re-trigger if set at fn.Entry.
Use subsystem-based format from CONTRIBUTING.md:
<subsystem>: <what changed>
<why this change was made>
Fixes #<issue>
Subsystems: proc, service, terminal, dwarf, native, dap, rpc2,
cmd/dlv, etc.
Subject line: Max 70 characters, Body: Wrap at 80 characters
Example:
proc/internal/ebpf: fix goroutine leak and shutdown sequence
The eBPF event processing goroutines were not being properly cleaned up
on debugger shutdown, causing resource leaks. This adds proper context
cancellation and wait groups.
Fixes #1234
-
Don't modify Process state directly - Use
ProcessInternalmethods for locking and state consistency -
Breakpoint insertion is backend-specific -
nativeuses software breakpoints (INT3),gdbserialsends packets,ebpfuses uprobes -
Thread vs Goroutine - Delve tracks both OS threads (
Thread) and Go goroutines (Gstruct). Most operations work on goroutines, not threads. -
Memory safety - Always handle errors when reading process memory. Process may die, memory may be unmapped, or addresses invalid.
-
Don't reparse DWARF sections -
loadDebugInfoMapsalready iterates all compile units. Add per-CU checks there instead of making separate passes overdebug_info.
delve/
├── cmd/dlv/ # CLI entry point and commands
├── pkg/
│ ├── proc/ # Core process abstraction
│ │ ├── native/ # OS-level debugging backends
│ │ ├── gdbserial/ # GDB remote protocol
│ │ ├── core/ # Core dump support
│ │ ├── internal/ebpf/ # eBPF tracing backend
│ │ └── *.go # Process interfaces and common logic
│ ├── dwarf/ # DWARF parsing and manipulation
│ ├── terminal/ # Interactive CLI
│ ├── config/ # Configuration (.delverc)
│ └── ... # Other utilities
├── service/
│ ├── debugger/ # High-level debugger API
│ ├── rpc2/ # JSON-RPC 2.0 server
│ ├── dap/ # Debug Adapter Protocol
│ └── api/ # Shared type definitions
├── _fixtures/ # Test source files (compiled during tests)
├── _scripts/ # Build helper scripts
└── Documentation/ # User and internal documentation
Key external dependencies (see go.mod):
github.com/cilium/ebpf- eBPF program loadinggithub.com/google/go-dap- Debug Adapter Protocolgithub.com/spf13/cobra- CLI frameworkgolang.org/x/arch- CPU architecture utilitiesgolang.org/x/sys- System calls and OS interfaces