LLVM 开发指南 / 第 16 章:LLDB 调试器
第 16 章:LLDB 调试器
“好的调试器让你看见程序运行的真相。”
16.1 LLDB 概述
LLDB 是 LLVM 项目的调试器,基于 LLVM 和 Clang 构建。
| 特性 | 说明 |
|---|---|
| 高性能 | 使用 LLVM 反汇编器和 Clang 表达式解析 |
| 模块化 | 基于库设计,可嵌入 |
| Python 脚本 | 完整的 Python 脚本化 API |
| 兼容 GDB | 兼容大部分 GDB 命令 |
| 跨平台 | macOS, Linux, Windows, FreeBSD |
16.2 基础命令
# 启动 LLDB
lldb ./program
lldb -c core # 分析 core dump
lldb -p <pid> # 附加到进程
# GDB 兼容模式
lldb --no-use-colors ./program
16.2.1 常用命令
# 启动和运行
(lldb) run # 运行程序
(lldb) run arg1 arg2 # 带参数运行
(lldb) process launch -- arg1 # 另一种方式
(lldb) quit # 退出
# 断点
(lldb) break set -n main # 函数断点
(lldb) break set -f test.c -l 10 # 文件行断点
(lldb) break set -n foo -c 'x > 5' # 条件断点
(lldb) break set -a 0x1234 # 地址断点
(lldb) break set -n foo --count 5 # 忽略前 5 次
(lldb) break list # 列出断点
(lldb) break delete 1 # 删除断点
(lldb) break disable 1 # 禁用断点
(lldb) break enable 1 # 启用断点
# 执行控制
(lldb) step # 单步进入 (s)
(lldb) next # 单步跳过 (n)
(lldb) stepi # 指令级单步
(lldb) nexti # 指令级跳过
(lldb) continue # 继续执行 (c)
(lldb) finish # 运行到函数返回
# 查看变量
(lldb) frame variable # 当前帧变量
(lldb) frame variable x # 特定变量
(lldb) frame variable -r 3 # 递归 3 层展开
(lldb) expression x # 表达式求值
(lldb) expression x = 42 # 修改变量
(lldb) po x # 打印对象 (ObjC/Swift)
(lldb) p x # 打印值
# 调用栈
(lldb) bt # 回溯 (backtrace)
(lldb) frame select 3 # 选择栈帧
(lldb) thread list # 线程列表
(lldb) thread select 2 # 选择线程
# 内存和寄存器
(lldb) memory read 0x1234 # 读内存
(lldb) memory read -fx -c 64 $rsp # 读栈
(lldb) register read # 读寄存器
(lldb) register read $rax # 读特定寄存器
# 断点命令
(lldb) watch set var x # 变量监视
(lldb) watch set expression -- *(int*)0x1234 # 地址监视
16.3 Python 脚本化
16.3.1 Python 命令
# my_script.py — 自定义 LLDB 命令
import lldb
def print_registers(debugger, command, result, internal_dict):
"""打印所有通用寄存器"""
target = debugger.GetSelectedTarget()
process = target.GetProcess()
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()
regs = ["rax", "rbx", "rcx", "rdx", "rsi", "rdi",
"rbp", "rsp", "r8", "r9", "r10", "r11",
"r12", "r13", "r14", "r15", "rip"]
for reg_name in regs:
reg = frame.FindRegister(reg_name)
if reg.IsValid():
print(f" {reg_name}: {reg.GetValue()}")
def my_backtrace(debugger, command, result, internal_dict):
"""自定义回溯"""
target = debugger.GetSelectedTarget()
process = target.GetProcess()
thread = process.GetSelectedThread()
print(f"线程 {thread.GetIndexID()}: {thread.GetName()}")
for frame in thread:
name = frame.GetFunctionName()
line = frame.GetLineEntry().GetLine()
file = frame.GetLineEntry().GetFileSpec().GetFilename()
print(f" #{frame.GetFrameID()} {name} at {file}:{line}")
# 注册命令
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand(
'command script add -f my_script.print_registers regs')
debugger.HandleCommand(
'command script add -f my_script.my_backtrace mybt')
print("自定义命令已加载: regs, mybt")
# 在 LLDB 中加载脚本
(lldb) command script import my_script.py
(lldb) regs
(lldb) mybt
16.3.2 条件断点脚本
# 在断点触发时执行 Python 代码
def breakpoint_handler(frame, bp_loc, dict):
"""断点命中时打印调用参数"""
func = frame.GetFunctionName()
args = frame.GetFunction().GetArguments()
print(f"=== 命中断点: {func} ===")
for arg in args:
print(f" {arg.GetName()}: {arg.GetValue()}")
return True # 继续执行
# 在 LLDB 中使用
(lldb) break set -n my_function
(lldb) break command add -F my_script.breakpoint_handler 1
16.3.3 自定义数据格式化
# 为自定义类型定义显示格式
class MyVectorSyntheticProvider:
"""显示 std::vector 内容"""
def __init__(self, valobj, dict):
self.valobj = valobj
self.update()
def update(self):
self.start = self.valobj.GetChildMemberWithName('_start')
self.finish = self.valobj.GetChildMemberWithName('_finish')
def num_children(self):
start_addr = self.start.GetValueAsUnsigned()
finish_addr = self.finish.GetValueAsUnsigned()
return int((finish_addr - start_addr) / 4) # sizeof(int)
def get_child_at_index(self, index):
offset = index * 4
return self.start.CreateChildAtOffset(
f'[{index}]', offset, self.start.GetType())
def get_child_index(self, name):
return int(name.strip('[]'))
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand(
'type synthetic add -l my_script.MyVectorSyntheticProvider MyVector')
16.4 与 GDB 对比
| 特性 | LLDB | GDB |
|---|---|---|
| 表达式解析 | Clang(支持 C++17+) | 内置解析器 |
| 脚本语言 | Python | Python |
| 架构 | 模块化、库化 | 单体 |
| macOS | 原生支持 | 有限 |
| Rust | 通过插件 | 通过插件 |
| 启动速度 | 快 | 中 |
| 社区 | Apple/LLVM | GNU |
16.5 LLDB 扩展
16.5.1 自定义 LLDB 插件
// MyLLDBPlugin.cpp
#include "lldb/API/LLDB.h"
#include "lldb/API/SBDebugger.h"
#include "lldb/API/SBCommandInterpreter.h"
class MyPlugin : public lldb::SBCommandPluginInterface {
public:
bool DoExecute(lldb::SBDebugger debugger, char **command,
lldb::SBCommandReturnObject &result) override {
result.AppendMessage("Hello from MyPlugin!");
result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
return true;
}
};
16.5.2 core dump 分析
# 生成 core dump
ulimit -c unlimited
./program # 崩溃后生成 core
# 分析 core dump
lldb -c core ./program
(lldb) bt
(lldb) frame variable
(lldb) thread list
(lldb) image list # 加载的模块
16.6 本章小结
| 概念 | 要点 |
|---|---|
| 基础命令 | run, break, step, next, bt, frame |
| Python 脚本 | command script add/import |
| 断点处理 | -F 回调函数 |
| 数据格式化 | type synthetic add |
| GDB 兼容 | 大部分 GDB 命令可直接使用 |
扩展阅读
- LLDB Documentation — LLDB 官方文档
- LLDB Python Reference — Python API
- LLDB Tutorial — 教程
- GDB to LLDB — GDB 命令映射
下一章: 第 17 章:MLIR — 学习 MLIR 多级 IR 框架。