# How to Debug Native Code Using DAP in Oh My Pi (omp): A Complete Guide

> Debug native code in Oh My Pi (omp) using the Debug Adapter Protocol (DAP). Connect to gdb and lldb-dap directly from your command line.

- Repository: [Can Bölük/oh-my-pi](https://github.com/can1357/oh-my-pi)
- Tags: how-to-guide
- Published: 2026-05-21

---

**Oh My Pi (omp) provides a built-in debug tool that speaks the Debug Adapter Protocol (DAP), allowing you to launch, attach to, and control native debuggers like gdb and lldb-dap directly from the command line or TUI.**

The omp repository includes a sophisticated debugging interface that bridges the gap between your CLI environment and native debuggers. By implementing the Debug Adapter Protocol, omp enables seamless debugging of C, C++, and other compiled binaries without leaving your terminal. This guide explains how to leverage the DAP integration to debug native code using the exact source implementation found in the can1357/oh-my-pi codebase.

## Architecture of the DAP Integration

The debugging system consists of three core layers that handle everything from CLI parsing to low-level DAP message exchange.

**Debug Tool CLI** ([`packages/coding-agent/src/tools/debug.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/tools/debug.ts)) parses JSON parameters and routes commands to the session manager. The `DebugTool.execute()` method implements a switch-case covering every DAP action including `launch`, `set_breakpoint`, and `continue`.

**DAP Session Manager** ([`packages/coding-agent/src/dap/session.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/dap/session.ts)) maintains a single active debugging session. It handles the initialization handshake, sends requests, and listens for events like `stopped` or `terminated`. Key methods include `launch()`, `attach()`, and `continue()`, each coordinating with the internal event loop to track execution state.

**Adapter Resolution** ([`packages/coding-agent/src/dap/config.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/dap/config.ts)) automatically selects the appropriate native debugger. The `selectLaunchAdapter()` function inspects file extensions and executable markers, preferring `gdb` or `lldb-dap` for extension-less binaries while respecting user overrides via the `adapter` parameter.

## Launching Native Binaries

To start debugging a native executable, use the `launch` action with the path to your binary. The tool automatically resolves the working directory and selects the correct debugger adapter based on the file type.

```json
{
  "action": "launch",
  "program": "./build/myapp",
  "args": ["--verbose"],
  "cwd": "/home/user/project"
}

```

From the CLI:

```bash
omp debug '{"action":"launch","program":"./build/myapp","args":["--verbose"]}'

```

When you execute this command, `selectLaunchAdapter()` in [`packages/coding-agent/src/dap/config.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/dap/config.ts) (lines 27-34) detects that `./build/myapp` has no file extension and selects **gdb** (or **lldb-dap** on Darwin systems). The session manager then spawns the adapter via `DapClient.spawn()`, sends the `initialize` request, and completes the configuration handshake with `configurationDone` before issuing the actual `launch` request. The launch arguments are assembled at [`packages/coding-agent/src/tools/debug.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/tools/debug.ts) (lines 654-662), merging user-provided values with the adapter's default configuration from [`packages/coding-agent/src/dap/defaults.json`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/dap/defaults.json).

## Setting Breakpoints

Set source-level breakpoints using the `set_breakpoint` action, which supports optional conditions.

```json
{
  "action": "set_breakpoint",
  "file": "src/main.c",
  "line": 42,
  "condition": "i > 10"
}

```

CLI example:

```bash
omp debug '{"action":"set_breakpoint","file":"src/main.c","line":42,"condition":"i > 10"}'

```

Internally, `DebugTool.execute()` routes this to `dapSessionManager.setBreakpoint()`. This method builds a DAP `setBreakpoints` request containing the normalized source path and breakpoint parameters. The implementation at [`packages/coding-agent/src/dap/session.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/dap/session.ts) (lines 44-50) deduplicates existing breakpoints for the same line, sorts them by line number, and maps the response back to internal state to track verified status.

## Controlling Execution

Once stopped, control program flow using standard debugging commands. Each action maps directly to a DAP request sent through the active session.

**Continue execution**:

```bash
omp debug '{"action":"continue"}'

```

**Step over**:

```bash
omp debug '{"action":"step_over"}'

```

The `continue()` method in [`packages/coding-agent/src/dap/session.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/dap/session.ts) (lines 4-22) first resolves the current thread ID, clears stale stop state, and subscribes to a stop outcome before sending the request. This guarantees that the manager captures the next `stopped` event rather than racing with it. Similarly, `stepOver()` (lines 111-121) prepares the outcome listener, issues the `next` request, and awaits the corresponding stop event.

## Inspecting Program State

Examine threads, stack frames, and variables using dedicated inspection actions. Each query returns formatted output suitable for terminal display.

**List active threads**:

```bash
omp debug '{"action":"threads"}'

```

**Get stack trace**:

```bash
omp debug '{"action":"stack_trace","levels":10}'

```

**Inspect variables in a frame**:

```bash
omp debug '{"action":"scopes","frame_id":3}'
omp debug '{"action":"variables","variable_ref":5}'

```

These actions delegate to `dapSessionManager.threads()`, `stackTrace()`, `scopes()`, and `variables()`, which issue the corresponding DAP requests. For low-level debugger interaction, use the `evaluate` action with `context: "repl"` to send raw commands to the underlying debugger:

```bash
omp debug '{"action":"evaluate","expression":"info registers","context":"repl"}'

```

## Advanced Usage Scenarios

### Forcing a Specific Debugger Adapter

While omp auto-selects adapters, you can force a specific debugger by providing the `adapter` field:

```bash
omp debug '{"action":"launch","adapter":"lldb-dap","program":"./mybinary"}'

```

This bypasses the automatic selection logic in `selectLaunchAdapter()` and uses the specified adapter configuration from [`defaults.json`](https://github.com/can1357/oh-my-pi/blob/main/defaults.json).

### Attaching to Running Processes

For remote debugging scenarios (such as Delve debugging a Go process), use the `attach` action:

```json
{
  "action": "attach",
  "port": 2345
}

```

The `selectAttachAdapter` logic recognizes the `port` parameter and selects the `dlv` adapter when appropriate.

### Pausing and Terminating Sessions

Pause a hung process:

```bash
omp debug '{"action":"pause"}'

```

Cleanly terminate the current session:

```bash
omp debug '{"action":"terminate"}'

```

List all active sessions (useful when managing multiple debug targets):

```bash
omp debug '{"action":"sessions"}'

```

## Summary

- **omp debug** exposes full DAP capabilities through JSON actions, wrapping complex debugger initialization into simple CLI commands.
- The system automatically selects **gdb** or **lldb-dap** for native binaries based on file heuristics, but supports explicit adapter overrides.
- All standard debugging operations—breakpoint management, stepping, stack inspection, and variable evaluation—are available as discrete actions routed through `DapSessionManager`.
- The implementation guarantees event coherence by preparing stop outcomes before sending continuation commands, preventing lost debugger events.
- Native debugger commands can be passed through using the `evaluate` action with `context: "repl"`, enabling access to platform-specific features like register inspection.

## Frequently Asked Questions

### Which native debuggers does omp support out of the box?

According to the [`defaults.json`](https://github.com/can1357/oh-my-pi/blob/main/defaults.json) configuration in [`packages/coding-agent/src/dap/defaults.json`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/dap/defaults.json), omp ships with built-in adapter definitions for **gdb**, **lldb-dap**, **dlv** (Delve for Go), and **debugpy**. You can extend support by modifying the configuration or providing explicit command paths via the adapter selection API.

### How does omp choose between gdb and lldb-dap automatically?

The `selectLaunchAdapter()` function in [`packages/coding-agent/src/dap/config.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/dap/config.ts) examines the target program's file extension and executable markers. For binaries without extensions (typical for native executables), it prefers **gdb** on Linux systems and **lldb-dap** on Darwin, returning the first match from the sorted adapter list. You can override this behavior by specifying the `adapter` parameter in your launch request.

### Can I debug remote processes or attach to existing PIDs?

Yes. Use the `attach` action with either a `processId` for local attachment or a `port` for remote debugging. When a port is specified, the adapter resolution logic selects appropriate remote debugging adapters (such as `dlv` for Go remote debugging). The session manager handles the attach handshake identically to a launch session, setting up the event loop before returning control.

### Why might my breakpoint not be verified or hit?

Breakpoint verification depends on the path matching between your source file and the debug information embedded in the binary. The `setBreakpoint()` implementation normalizes paths and deduplicates entries, but if the source in [`packages/coding-agent/src/dap/session.ts`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/dap/session.ts) cannot reconcile the path with what the debugger expects, the breakpoint may remain unverified. Ensure your `cwd` and file paths match the compilation context, or use the `evaluate` action with `"context": "repl"` to send raw debugger commands for manual breakpoint verification.