LLDB Python 脚本入门
LLDB Python脚本编程入门与自定义命令开发
LLDB Python 脚本入门
环境准备
检查 LLDB Python 支持
bash
1
2
3
4
5
$ lldb
(lldb) script
>>> import lldb
>>> lldb.debugger
Debugger (instance: "debugger_1", id: 1)
必备工具
- LLDB
- Python 3.x
- 文本编辑器
基础概念
核心类
| 类名 | 描述 |
|---|---|
| SBDebugger | 调试器主入口点 |
| SBTarget | 被调试的目标程序 |
| SBProcess | 目标进程 |
| SBThread | 线程 |
| SBFrame | 栈帧 |
| SBBreakpoint | 断点 |
获取调试器实例
python
1
2
3
4
5
6
7
8
9
10
# 在 LLDB 脚本环境中
debugger = lldb.debugger
# 创建新的调试器实例
debugger = lldb.SBDebugger.Create()
debugger.SetAsync(False)
# 通过当前进程获取
target = debugger.GetSelectedTarget()
process = target.GetProcess()
快速开始
内联执行
bash
1
2
3
4
(lldb) script
>>> print("Hello from Python!")
>>> target = lldb.debugger.GetSelectedTarget()
>>> print(f"Target: {target}")
外部脚本
创建 my_script.py:
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import lldb
def print_registers(debugger, command, result, internal_dict):
frame = debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
if not frame.IsValid():
result.AppendMessage("No valid frame selected")
return
registers = frame.GetRegisters()
result.AppendMessage("Register values:")
for regs in registers:
result.AppendMessage(f" {regs.GetName()}:")
for reg in regs:
result.AppendMessage(f" {reg.GetName():10} = {reg.GetValue()}")
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand('command script add -f my_script.print_registers preg')
加载脚本:
bash
1
(lldb) command script import my_script.py
核心 API
目标程序管理
python
1
2
3
4
5
6
7
8
def target_info(debugger, command, result, internal_dict):
target = debugger.GetSelectedTarget()
if target.IsValid():
result.AppendMessage(f"Target: {target}")
result.AppendMessage(f"Executable: {target.GetExecutable()}")
result.AppendMessage(f"Modules: {target.GetNumModules()}")
else:
result.AppendMessage("No target selected")
断点管理
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def set_conditional_breakpoint(debugger, command, result, internal_dict):
args = command.split()
if len(args) < 2:
result.AppendMessage("Usage: cb <location> <condition>")
return
location = args[0]
condition = " ".join(args[1:])
target = debugger.GetSelectedTarget()
breakpoint = target.BreakpointCreateByLocation(location)
breakpoint.SetCondition(condition)
result.AppendMessage(f"Breakpoint set at {location} with condition: {condition}")
内存操作
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def examine_memory(debugger, command, result, internal_dict):
args = command.split()
if len(args) < 2:
result.AppendMessage("Usage: xm <address> <count>")
return
address = int(args[0], 0)
count = int(args[1])
target = debugger.GetSelectedTarget()
process = target.GetProcess()
error = lldb.SBError()
memory = process.ReadMemory(address, count, error)
if error.Success():
for i in range(0, len(memory), 16):
hex_str = " ".join(f"{b:02x}" for b in memory[i:i+16])
result.AppendMessage(f" 0x{address+i:016x}: {hex_str}")
实用脚本
崩溃分析
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
def analyze_crash(debugger, command, result, internal_dict):
target = debugger.GetSelectedTarget()
process = target.GetProcess()
if process.GetState() == lldb.eStateStopped:
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()
result.AppendMessage("=== Crash Analysis ===")
result.AppendMessage(f"Stop Reason: {thread.GetStopReason()}")
# 寄存器
result.AppendMessage("\nRegisters:")
registers = frame.GetRegisters()
for regs in registers:
for reg in regs:
if reg.GetValue():
result.AppendMessage(f" {reg.GetName()}: {reg.GetValue()}")
# 局部变量
result.AppendMessage("\nLocal variables:")
variables = frame.GetVariables(True, True, True, True)
for var in variables:
result.AppendMessage(f" {var.GetName()}: {var.GetValue()}")
# 栈回溯
result.AppendMessage("\nBacktrace:")
for i in range(thread.GetNumFrames()):
frame = thread.GetFrameAtIndex(i)
result.AppendMessage(f" #{i}: {frame}")
性能分析
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
import time
class PerformanceProfiler:
def __init__(self):
self.breakpoint_times = {}
self.start_time = None
def start_profiling(self, debugger, command, result, internal_dict):
self.start_time = time.time()
self.breakpoint_times = {}
target = debugger.GetSelectedTarget()
functions = ["malloc", "free", "pthread_create"]
for func in functions:
bp = target.BreakpointCreateByName(func)
bp.SetScriptCallbackFunction("performance_profiler.breakpoint_hit")
result.AppendMessage("Profiling started")
def breakpoint_hit(self, frame, bp_loc, dict):
current_time = time.time() - self.start_time
func_name = frame.GetFunctionName()
if func_name not in self.breakpoint_times:
self.breakpoint_times[func_name] = []
self.breakpoint_times[func_name].append(current_time)
return False
def show_results(self, debugger, command, result, internal_dict):
result.AppendMessage("=== Performance Profile ===")
for func, times in self.breakpoint_times.items():
result.AppendMessage(f"{func}: {len(times)} calls")
profiler = PerformanceProfiler()
def start_profiling(debugger, command, result, internal_dict):
profiler.start_profiling(debugger, command, result, internal_dict)
def show_results(debugger, command, result, internal_dict):
profiler.show_results(debugger, command, result, internal_dict)
def breakpoint_hit(frame, bp_loc, dict):
return profiler.breakpoint_hit(frame, bp_loc, dict)
数据结构可视化
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
def visualize_linked_list(debugger, command, result, internal_dict):
frame = debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
head_ptr = frame.FindVariable("list_head")
if not head_ptr.IsValid():
result.AppendMessage("List head not found")
return
result.AppendMessage("Linked List:")
current = head_ptr
count = 0
max_nodes = 20
while current.IsValid() and count < max_nodes:
data = current.GetChildMemberWithName("data")
next_ptr = current.GetChildMemberWithName("next")
if data.IsValid():
result.AppendMessage(f" -> [{data.GetValue()}]")
if not next_ptr.IsValid() or next_ptr.GetValueAsUnsigned() == 0:
result.AppendMessage(" -> NULL")
break
current = next_ptr.Dereference()
count += 1
高级功能
事件处理
python
1
2
3
4
5
6
def handle_stop_event(debugger, command, result, internal_dict):
def internal_handler(event, breakpoint):
if event.GetType() == lldb.SBProcess.eStateStopped:
result.AppendMessage("Process stopped")
listener = debugger.GetListener()
导出调试信息
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
import json
def export_debug_info(debugger, command, result, internal_dict):
target = debugger.GetSelectedTarget()
process = target.GetProcess()
debug_info = {
"target": str(target),
"process_id": process.GetProcessID(),
"threads": []
}
for thread in process:
thread_info = {
"id": thread.GetThreadID(),
"frames": []
}
for frame in thread:
frame_info = {
"function": frame.GetFunctionName(),
"file": frame.GetLineEntry().GetFileSpec().GetFilename(),
"line": frame.GetLineEntry().GetLine()
}
thread_info["frames"].append(frame_info)
debug_info["threads"].append(thread_info)
with open("debug_info.json", "w") as f:
json.dump(debug_info, f, indent=2)
result.AppendMessage("Debug info exported")
调试脚本
python
1
2
3
4
5
6
7
def debug_script(debugger, command, result, internal_dict):
try:
result.AppendMessage("Script executed successfully")
except Exception as e:
result.AppendMessage(f"Error: {e}")
import traceback
result.AppendMessage(traceback.format_exc())
最佳实践
- 错误处理:始终检查 SBError 和对象有效性
- 资源管理:及时释放不需要的资源
- 性能考虑:避免在热路径中执行复杂操作
- 代码组织:将大型脚本拆分为模块
- 文档化:为自定义命令提供清晰的帮助文本
总结
LLDB Python 脚本提供了强大的调试自动化能力:
- 核心 API:SBDebugger、SBTarget、SBProcess 等
- 自定义命令:通过
__lldb_init_module注册命令 - 实用脚本:崩溃分析、性能分析、数据可视化
- 高级功能:事件处理、外部集成
通过这些工具,可以显著提高调试效率。