内容
# llmdb
> 从终端或AI代理调试任何Mac或iOS模拟器应用程序。
封装了`lldb-dap`(Apple的LLDB调试适配器协议)和一个结构化、面向会话的调试器,供AI代理使用。启动二进制文件、设置断点、逐步执行代码、检查局部变量、评估表达式 — 所有操作均返回JSON,所有操作均由命令行或MCP驱动。
## 看看实际操作
一个完整的调试会话 — 启动、断开、继续、检查、评估、逐步执行 — 共计七个调用。自己运行它,针对捆绑的Swift固定装置(先运行`swift build`)。
### 1. 启动 — 在调试器下启动二进制文件
```bash
llmdb launch ./.build/debug/llmdb-fixture quick
{
"sessionId" : "2649sk",
"state" : "stopped",
"stopReason" : { "reason" : "exception", "description" : "signal SIGSTOP", ... }
}
```
守护进程在第一次调用时自动启动。 二进制文件在进入时停止,以便下一个调用有稳定的目标。
### 2. 设置断点并运行直到断点命中 — 一次调用
```bash
llmdb run-until ./Sources/Fixture/main.swift:35
{
"breakpoint" : { "id" : 1, "line" : 35, "verified" : true, ... },
"snapshot" : {
"state" : "stopped",
"stopReason" : { "reason" : "breakpoint", "hitBreakpointIDs" : [1], ... }
}
}
```
`run-until`组合了`break set` + `continue`,适用于最常见的代理流程。 当您想在两者之间进行检查时,请单独使用它们。 对于交互式UI应用程序,用户可能需要任意时间触发断点,请传递`--wait none`以立即触发并返回,然后调用`llmdb wait`以阻塞直到停止到达。
### 3. 检查 — 调用堆栈和类型化的局部变量
```bash
llmdb bt --depth 3
{ "frames" : [
{ "name" : "compute(x:y:)", "line" : 35, "source" : ".../main.swift", ... },
{ "name" : "llmdb_fixture_main", "line" : 63, ... },
{ "name" : "start", "source" : "/usr/lib/dyld`start", ... }
]}
```
```bash
llmdb locals
{ "locals" : [
{ "name" : "x", "type" : "Int", "value" : "3" },
{ "name" : "y", "type" : "Int", "value" : "4" },
{ "name" : "sum", "type" : "Int", "value" : "7" },
{ "name" : "product", "type" : "Int", "value" : "12" },
{ "name" : "diff", "type" : "Int", "value" : "1" },
{ "name" : "total", "type" : "Int", "value" : "20" }
]}
```
值是lldb格式化的字符串 — 代理可以直接读取它们而无需解析内存布局。 具有非零`variablesReference`的局部变量是结构化的(结构体、数组、对象)— 使用`expand`深入了解:
```bash
llmdb expand 4
{ "children" : [
{ "name" : "[0]", "type" : "String", "value" : "\"alpha\"", "variablesReference" : 9 },
{ "name" : "[1]", "type" : "String", "value" : "\"beta\"", "variablesReference" : 10 }
]}
```
每个子项都带有自己的`variablesReference`,因此您可以深入了解嵌套值。
### 4. 评估 — 向lldb提问
```bash
llmdb expr "sum + diff"
{ "type" : "Int", "value" : "8", "variablesReference" : 0 }
```
`expr`在当前帧的上下文中运行。 当`locals`不够用时使用它 — 用于属性访问(`self.state.count`)、方法调用或局部变量的算术运算。
### 5. 逐步执行 — 并验证您已移动
```bash
llmdb step --over
{ "state" : "stopped", "stopReason" : { "reason" : "step", "description" : "step over", ... } }
```
```bash
llmdb bt --depth 1
{ "frames" : [{ "name" : "llmdb_fixture_main", "line" : 62, ... }] }
```
`--over` / `--in` / `--out`是互斥标志;默认是`--over`。
### 6. 关闭
```bash
llmdb stop
{ "ok" : true }
```
## 安装
### Homebrew
```bash
brew install alexmx/tools/llmdb
```
### Mise
```bash
mise use --global github:alexmx/llmdb
```
## 要求
- macOS 15 或更高版本
- Xcode 或命令行工具(提供`lldb-dap`)
- 可调试的目标 — 调试构建,`get-task-allow=true`(Xcode 默认)。具有强化运行时的发布构建无法在不重新签名情况下附加。
运行`llmdb doctor`验证工具链和守护进程套接字:
```bash
llmdb doctor
{ "checks" : [
{ "name" : "lldb-dap", "ok" : true, "detail" : "/Applications/Xcode.app/.../lldb-dap" },
{ "name" : "socket-dir", "ok" : true, "detail" : "~/Library/Caches/llmdb" },
{ "name" : "daemon", "ok" : true, "detail" : "~/Library/Caches/llmdb/llmdbd.sock" }
]}
```
如果任何检查失败,则退出状态为非零,因此它适用于 shell 脚本。
## 更多示例
```bash
# 附加到正在运行的进程,PID 为 12345
llmdb attach --pid 12345
# 附加到已启动的 iOS 模拟器中的 SwiftUI 应用程序
llmdb attach --app com.example.MyApp
# 列出与此守护进程通信的所有代理的所有活动会话
llmdb sessions
# 管理断点
llmdb break set ./Sources/Fixture/main.swift:34
llmdb break set ./Sources/Fixture/main.swift:49 --condition "index == 2"
llmdb break list
llmdb break delete 1
# 暂停正在运行的会话
llmdb interrupt
# 检查线程
llmdb threads
```
## 命令参考
所有命令默认返回 JSON (`--format json`)。 当有多个会话活动时,请传递 `--session <id>`。
### 生命周期
| 命令 | 描述 | 关键选项 |
|---|---|---|
| `launch <binary> [-- args…]` | 在 `lldb-dap` 下启动二进制文件或 `.app` 包。 在入口处停止。 | `.app` 包(或其中的路径)通过 LaunchServices 路由,以便应用注册到 AppKit — 需要 accessibility/UI-automation 工具看到进程 |
| `attach` | 附加到正在运行的进程或模拟器应用 | `--pid N` 或 `--app <bundle-id>`(两者之一) |
| `stop` | 断开/终止会话 | `--session ID` |
| `sessions` | 列出活动调试会话 | — |
### 断点
| 命令 | 描述 | 关键选项 |
|---|---|---|
| `break set <file>:<line>` | 设置源代码断点;返回已验证的 BP 和会话快照 | `--condition <expr>`, `--hit-condition <expr>` |
| `break list` | 列出会话中的断点 | — |
| `break delete <id>` | 按 ID 删除断点;返回幸存者 | — |
### 执行
所有四个阻塞动词都接受 `--wait <seconds|none>`。 默认超时:`continue` 60s, `step` 30s, `interrupt` 10s, `run-until` 60s。 `--wait none`(或 `--wait 0`)是即刻生效 — 目标开始运行,调用立即返回;与 `wait` 配对以稍后阻塞。
| 命令 | 描述 | 关键选项 |
|---|---|---|
| `continue` | 继续执行,直到下一次停止 | `--wait <seconds\|none>` |
| `run-until <file>:<line>` | `break set` + `continue` 合二为一 | `--wait <seconds\|none>` |
| `step` | 单步执行一个源代码行 | `--over`(默认), `--in`, `--out`; `--wait` |
| `interrupt` | 暂停正在运行的会话 | `--wait <seconds\|none>` |
| `wait` | 阻塞直到会话退出 `running` 状态 | `--timeout <seconds>`(默认 60) |
### 检查
| 命令 | 描述 | 关键选项 |
|---|---|---|
| `bt` | 停止线程的结构化调用堆栈 | `--thread N`, `--depth N` |
| `locals` | 栈帧的类型化局部变量 | `--frame N`(默认 0) |
| `expand <ref>` | 通过其 `variablesReference` 深入了解结构化值 | — |
| `threads` | 会话中的线程列表 | — |
| `expr <expression>` | 在帧上下文中评估 | `--frame N` |
### 系统
| 命令 | 描述 | 关键选项 |
|---|---|---|
| `doctor` | 验证 lldb-dap、套接字目录、守护进程可达性。 在新机器上为 `daemon: ok+idle`; 只有无法访问的过时套接字才算作失败。 如果任何检查失败,则退出状态为 1。 | — |
| `daemon` | 运行 `llmdbd`(通常自动启动)。 | `--socket PATH` 覆盖 |
| `mcp` | 启动 stdio MCP 服务器。 | `--setup` 打印客户端配置片段 |
## MCP 服务器
llmdb 作为 stdio MCP 服务器运行。 每个 CLI 动词都作为 `llmdb_*` 工具公开(`llmdb_launch`、`llmdb_break_set`、`llmdb_run_until`,...),与 CLI 1:1 映射。
```bash
llmdb mcp --setup # 打印 Claude Code、Cursor、Codex CLI 等的配置
```
手动配置:
```json
{
"mcpServers": {
"llmdb": { "command": "llmdb", "args": ["mcp"] }
}
}
```
### 多代理隔离
两个同时驱动 llmdb 的代理默认共享一个守护进程 — 方便,但它们会看到对方的会话,并且“只有一个活动会话时的默认会话”快捷方式将停止工作。 通过 `LLMDB_SOCKET_PATH` 环境变量为每个代理提供自己的守护进程:
```json
{
"mcpServers": {
"llmdb": {
"command": "llmdb",
"args": ["mcp"],
"env": { "LLMDB_SOCKET_PATH": "/tmp/llmdb-agent-a.sock" }
}
}
}
```
为每个代理设置不同的路径 — 自动启动会拾取覆盖并为每个代理获取一个完全隔离的守护进程、会话和 `lldb-dap` 子项。
## 许可证
在 [MIT License](LICENSE) 下发布。