vscode + codelldb + python 增强调试能力
在最近分析 SQLITE3 的源码的时候,发现查询 SQLITE3 VDBE 虚拟机的执行指令很不方便,指令都是定义的类似如下的宏
1
2
3
4
5
6
#define OP_Savepoint 0
#define OP_AutoCommit 1
#define OP_Transaction 2
#define OP_Checkpoint 3
#define OP_JournalMode 4
...
那么在调试的过程中,指令的名字只是一个数字,对于理解指令集很麻烦,只能去 opcodes.h 中进行查询,不是很方便。
记得在原来查看 codelldb 的介绍页面的时候,看到了一个很酷炫的图就是用 python 来读取数据进行绘制。于是我就想可以用 python 来进行转换。查询了一些资料终于实现了一下版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/usr/bin/env python3
import lldb
import debugger
opcode_map = {
0:"OP_Savepoint",
1:"OP_AutoCommit",
2:"OP_Transaction",
3:"OP_Checkpoint",
4:"OP_JournalMode"
...
}
def print_vdbe_aop(addr):
print("print_vdbe_aop")
"""Command to print the contents of the aOp array in a Vdbe struct"""
addr = debugger.unwrap(addr)
vdbe_ptr = 0
if addr.TypeIsPointerType():
vdbe_ptr = addr.GetValueAsUnsigned()
else:
vdbe_ptr = addr.AddressOf().GetValueAsUnsigned()
# 获取 aOp 和 nOp
aop_expr = f"((Vdbe*){vdbe_ptr})->aOp"
nop_expr = f"((Vdbe*){vdbe_ptr})->nOp"
aOp = lldb.frame.EvaluateExpression(aop_expr) # 获取 aOp 数组的指针
nOp = lldb.frame.EvaluateExpression(nop_expr) # 获取 nOp 数量
if aOp.IsValid() and nOp.IsValid():
# 获取 aOp 数组的长度
nOp_val = nOp.GetValueAsSigned()
table = "<table border=1><tr><th>Op</th><th>Opcode</th><th>P1</th><th>P2</th><th>P3</th><th>P4</th><th>P5</th></tr>"
print(f"Number of operations (nOp): {nOp_val}")
for i in range(nOp_val):
# 读取每个 Op 的 opcode 值
op_expr = f"((Op*)({aOp.GetValue()} + sizeof(Op) * {i}))->opcode"
opcode = lldb.frame.EvaluateExpression(op_expr).GetValueAsUnsigned()
# 获取 P1, P2, P3, P4, P5 参数
p1_expr = f"((Op*)({aOp.GetValue()} + sizeof(Op) * {i}))->p1"
p2_expr = f"((Op*)({aOp.GetValue()} + sizeof(Op) * {i}))->p2"
p3_expr = f"((Op*)({aOp.GetValue()} + sizeof(Op) * {i}))->p3"
p4_expr = f"((Op*)({aOp.GetValue()} + sizeof(Op) * {i}))->p3"
p5_expr = f"((Op*)({aOp.GetValue()} + sizeof(Op) * {i}))->p3"
p1 = lldb.frame.EvaluateExpression(p1_expr).GetValueAsSigned()
p2 = lldb.frame.EvaluateExpression(p2_expr).GetValueAsSigned()
p3 = lldb.frame.EvaluateExpression(p3_expr).GetValueAsSigned()
p4 = lldb.frame.EvaluateExpression(p4_expr).GetValueAsSigned()
p5 = lldb.frame.EvaluateExpression(p5_expr).GetValueAsSigned()
# 将 opcode 转换为字符串
opcode_name = opcode_map.get(opcode, f"Unknown Opcode ({opcode})")
print(f"Op[{i}]: {opcode_name} (P1: {p1}, P2: {p2}, P3: {p3}, P4: {p4}, P5: {p5})")
# create a html table
#
table += f"<tr><td>{i}</td><td>{opcode_name}</td><td>{p1}</td><td>{p2}</td><td>{p3}</td><td>{p4}</td><td>{p5}</td> </tr>"
table += "</table>"
debugger.display_html(table,position=2)
else:
print("Failed to retrieve aOp or nOp")
这里可以参考 codelldb 的 README 文档,其中实现了对部分 lldb python 接口的包装.
代码的主要思路就是获取变量的地址,然后执行表达式,最后将结果使用 html 进行输出,这样就可以在 vscode 中直接查看结果了.
具体的操作方式:
加载 python 文件
在 vscode 的
launch.json中加入如下内容,加载 python 脚本1 2 3
"initCommands": [ "command script import ~/code/c/sqlite/vdbe_print.py" ],
- 在
sqlite3VdbeExec添加一个条件断点,内容如下:1
/py vdbe_print.print_vdbe_aop($p) if 1==1 else False
这里的 p 是 Vdbe 变量.
- 运行程序查看结果
截图如下:
本文由作者按照 CC BY 4.0 进行授权
