LLDB基本操作

前言

LLDB是搭配LLVM的一个调试工具,就如同GDB之于GCC。我们日常开发中几乎时时刻刻和它在打交道。下面简单地从多个方面来了解以及使用LLDB:

  • 1、BreakPoint和WatchPoint;
  • 2、程序控制;
  • 3、线程状态和栈帧状态;

其中BreakPoint也就是我们常说的断点,它是我们日常调试中使用过最多的能力,其作用自不必多言;WatchPoint相对来说用的就很少了,我们可以称之为“观察点”。我们可以通过添加观察点来关注我们指定变量的变化。
程序控制就是指的在我们调试过程中的单步执行、继续执行、进入子过程内部以及从子过程内部跳出。
线程状态和栈帧状态则是查看当前线程的执行状态,包括过程调用、变量信息以及寄存器状态等等。
下面提及的所有命令,如果不清楚如何使用都可以使用 help xxx 形式来进行查询:

help breakpoint
help watchpoint
help memory
...

程序启动和控制相关

程序启动大致可以分为两种,第一种是在启动lldb的时候指定要debug的程序;第二种是在启动lldb之后,再明确指定要启动的程序:

/// 第一种启动lldb的时候指定待调试的程序
➜  lldb lldb_demo
/// 第二种先启动lldb,然后指定要启动的调试程序
➜  lldb
(lldb) run /Users/.../db_demo 1 2 3
/// 第三种创建不同的target,在lldb里面随意的启动
➜  lldb 
(lldb) target create /Users/xxx/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo
Current executable set to '/Users/xxx/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo' (x86_64).
(lldb) target list
Current targets:
* target #0: /Users/xxx/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo ( arch=x86_64-apple-macosx11.5.0, platform=host )
(lldb) r 0
Process 30082 launched: '/Users/xxx/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo' (x86_64)

一个小提示:我们在target create的时候需要知道某一个路径的话,就需要lldb里面执行shell相关的指令。可以使用如下方式:

platform shell xxxx。当然也可以使用command alias为platform shell取一个别名。

下表中的内容简单的列举了一下相关的指令:

类别 描述 GDB LLDB 备注
程序启动 启动指定进程 run/r <args> xxx process launch -- <args>、run/r <args> xxx args启动过程中需要的参数,对应main函数中的参数
展示传入的参数 show args settings show target.run-args 展示我们设置的参数
设置环境变量 set env DEBUG 1 env DEBUG=1 设置环境变量
程序控制 源码级别进入执行 step/s thread step-in 单步执行到指定过程的内部(比如子函数调用时,进入子函数内部)
指令级别进入执行 stepi/si thread step-inst
源码级别跳过执行 next/n thread step-over
指令级别跳过执行 nexti/ni hread step-inst-over
结束执行 finish thread step-out
///####################################### 展示传入的参数
(lldb) settings show target.run-args
target.run-args (array of strings) =
  [0]: "/Users/xxx/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo"
  [1]: "1"
  [2]: "2"
  [3]: "3"

BreakPoint和WatchPoint

下面列出了我们调试过程中需要设置断点和观察点的方法,以及编辑断点和观察点的命令。

类别 描述 GDB LLDB 备注
断点相关 基于函数名称设置断点 break main breakpoint set --name main
基于代码文件以及某一行设置断点 break Controller.cpp:11 breakpoint set --file Controller.cpp --line 11
基于方法名设置断点 breakpoint set --method pfb_lldb::Controller::increase
基于Objective C相关断点设置 breakpoint set --name "-[Model setController:]"\breakpoint set --selector count
获取当前设置的断点信息 info break breakpoint list
删除指定断点 delete 1 breakpoint delete 1 这里命令后面带的数字,即某一断点在breakpoint list里面的序号
使指定断点失效 disable 1 breakpoint disable 1
使指定断点生效 enable 1 breakpoint enable 1
观察点相关 设置观察点 watch ctlptr->len watchpoint set variable ctlptr->len 这里命令后面带的数字,即某一观察点在watchpoint list里面的序号
查看所有观察点 info break watchpoint list
删除观察点 delete 1 watchpoint delete 1
///####################################### 基于C++方法设置断点
(lldb) breakpoint set --method pfb_lldb::Controller::increase

///####################################### 获取当前所有的断点
(lldb) breakpoint list
Current breakpoints:
1: name = '-[Model setController:]', locations = 1
  1.1: where = lldb_demo`-[Model setController:] + 20 at Model.mm:14:5, address = lldb_demo[0x0000000100003de4], unresolved, hit count = 0 

2: name = 'main', locations = 10
  2.1: where = lldb_demo`main + 25 at main.mm:14:22, address = lldb_demo[0x0000000100003b49], unresolved, hit count = 0 
  2.2: where = Foundation`-[NSBlockOperation main], address = Foundation[0x00007fff21177335], unresolved, hit count = 0 
  2.3: where = Foundation`-[NSThread main], address = Foundation[0x00007fff2118e53d], unresolved, hit count = 0 
  2.4: where = Foundation`-[NSFilesystemItemRemoveOperation main], address = Foundation[0x00007fff2118f067], unresolved, hit count = 0 
  2.5: where = Foundation`-[NSInvocationOperation main], address = Foundation[0x00007fff211a34a8], unresolved, hit count = 0 
  2.6: where = Foundation`-[NSOperation main], address = Foundation[0x00007fff211a3c97], unresolved, hit count = 0 
  2.7: where = Foundation`-[NSFilesystemItemMoveOperation main], address = Foundation[0x00007fff211e2a9f], unresolved, hit count = 0 
  2.8: where = Foundation`-[NSDirectoryTraversalOperation main], address = Foundation[0x00007fff2122aa96], unresolved, hit count = 0 
  2.9: where = Foundation`-[_NSBarrierOperation main], address = Foundation[0x00007fff212ef78b], unresolved, hit count = 0 
  2.10: where = Security`Security::OSXCode::main(), address = Security[0x00007fff22450154], unresolved, hit count = 0 

3: name = 'pfb_lldb::Controller::increase', locations = 1
  3.1: where = lldb_demo`pfb_lldb::Controller::increase() + 12 at Controller.cpp:11:8, address = lldb_demo[0x0000000100003d9c], unresolved, hit count = 0 

///####################################### 设置观察点
(lldb) watchpoint set variable ctlptr->len
Watchpoint created: Watchpoint 1: addr = 0x100704080 size = 4 state = enabled type = w
    declare @ '/Users/xxx/WorkStation/iOS/exec/lldb_demo/lldb_demo/main.mm:17'
    watchpoint spec = 'ctlptr->len'
    new value: 0

变量和表达式执行

使用expr我们可以在debug的时候去执行相关的表达式,以达到运行期间修改的目的。比如在运行时我们想要修改某一个View的背景色,那么我们就可以使用expr来达到这个目的,而不需要重新编译执行。

类别 描述 GDB LLDB 备注
变量 查看当前帧变量 info args frame variable
展示局部变量 info locals frame variable --no-args 局部变量,即非外部传入的变量
打印指定变量 p bar p bar
执行表达式 print (int) printf ("Print nine: %d.", 4 + 5) expr (int) printf ("Print nine: %d.", 4 + 5)
打印OC相关的对象 po [SomeClass returnAnObject] po [SomeClass returnAnObject]\expr -o -- [SomeClass returnAnObject]
///####################################### 查看当前变量
(lldb) frame variable
(int) argc = 1
(const char **) argv = 0x00007ffeefbff4d0
(NSRunLoop *) loop = 0x0000000100405d80
(pfb_lldb::Controller *) ctlptr = 0x0000000100704080
(Model *) mdl = nil
 
 ///####################################### 查看当前局部变量
 (lldb) frame variable --no-args
(NSRunLoop *) loop = 0x0000000100405d80
(pfb_lldb::Controller *) ctlptr = 0x0000000100704080
(Model *) mdl = nil
 
 ///####################################### 执行表达式相关 
 (lldb) expr ctlptr->len = 10
(int) $0 = 10
(lldb) expr ctlptr->len++
(int) $1 = 10
(lldb) po ctlptr->len
11
(lldb) expr -o -- ctlptr->len
11

线程状态

其中bt是我们使用的最多的一个命令,它可以打印出当前线程的调用堆栈。

类别 描述 GDB LLDB 备注
Thread State 列举程序中的线程信息 info threads thread list
选中某个线程 thread 1 thread select 1 序号为thread list中的编号
当前线程调用栈 bt thread backtrace
所有线程调用栈 thread apply all bt bt all
当前线程调用栈前2帧 bt 2 thread backtrace -c 2
选中某第1帧 frame 1 frame select 1
(lldb) thread list
Process 50356 stopped
* thread #1: tid = 0x50aac7, 0x0000000100003c1b lldb_demo`main(argc=1, argv=0x00007ffeefbff4d0) at main.mm:18:9, queue = 'com.apple.main-thread', stop reason = step over
  thread #2: tid = 0x50afa5, 0x00007fff2050f420 libsystem_pthread.dylib`start_wqthread
  thread #3: tid = 0x50e012, 0x00007fff22d7fcd2 libsystem_notify.dylib`___lldb_unnamed_symbol29$$libsystem_notify.dylib, queue = 'com.apple.root.user-initiated-qos'
  
 ///####################################### 调用堆栈
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
  * frame #0: 0x0000000100003d9c lldb_demo`pfb_lldb::Controller::increase(this=0x0000000100704080) at Controller.cpp:11:8
    frame #1: 0x0000000100003c24 lldb_demo`main(argc=1, argv=0x00007ffeefbff4d0) at main.mm:18:17
    frame #2: 0x00007fff2052ef3d libdyld.dylib`start + 1

///####################################### 调用堆栈前2帧
(lldb) bt 2
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
  * frame #0: 0x0000000100003d9c lldb_demo`pfb_lldb::Controller::increase(this=0x0000000100704080) at Controller.cpp:11:8
    frame #1: 0x0000000100003c24 lldb_demo`main(argc=1, argv=0x00007ffeefbff4d0) at main.mm:18:17

/// 选中指定帧
(lldb) frame select 1
frame #1: 0x0000000100003c24 lldb_demo`main(argc=1, argv=0x00007ffeefbff4d0) at main.mm:18:17
   15           NSRunLoop *loop = [NSRunLoop currentRunLoop];
   16           [loop addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
   17           Controller *ctlptr = new Controller();
-> 18           ctlptr->increase();
   19           Model *mdl = [Model new];
   20           [mdl setController:ctlptr];
   21           delete ctlptr;

内存和寄存器

当我们发生了bad access/类型异常等等错误,为了定位到底发生了什么我们就需要去查看对应地址上的内存值。

类别 描述 GDB LLDB 备注
内存相关 读取指定地址的内存值 x/10xw 0x0000000100704080 x/10xw 0x0000000100704080

上表中和内存读取相关的指令格式中存在一个紧跟着反斜杠的10bw内容,这里我简单解释一下他们的作用:

  • N(Number):需要读取的个数,比如这里是10;
  • F(Format):我们读取的格式,比如这里的b。它和我们平时使用printf格式一样:‘x’(十六进制), ‘d’, ‘u’, ‘o’, ‘t’, ‘a’, ‘c’, ‘f’, ‘s’, ‘m’;
  • U(Unit):上面提到的个数,那么U就是决定每个的展示内容的大小:
    b Bytes:字节
    h Halfwords (2 bytes):半字
    w Words (4 bytes):字
    g Giant words (8 bytes):巨字
(lldb) frame variable --no-args
(NSRunLoop *) loop = 0x0000000100405d80
(pfb_lldb::Controller *) ctlptr = 0x0000000100704080
(Model *) mdl = nil
 
(lldb) x/10xw 0x0000000100704080
0x100704080: 0x0000000b 0x00000000 0x00000000 0x00000000
0x100704090: 0x00000000 0x00000000 0x00000000 0x00000000
0x1007040a0: 0x00000000 0x00000000

在进行DEBUG的时候获取寄存器的值,对我们定位问题有着至关重要的作用。对于通用目的寄存器来说,排查问题比较大的帮助的有bp/fp、sp用于确定当前栈的区域;ip/pc用于确定当前执行指令;lr寄存器保存函数调用返回地址等等。

类别 描述 GDB LLDB 备注
寄存器相关 展示当前线程通用目的寄存器 info registers register read
展示所有寄存器的值 info all-registers register read --all
修改指定寄存器的值 register write rax 123
(lldb) register read
General Purpose Registers:
       rbx = 0x0000000000000000
       rbp = 0x00007ffeefbff4b0
       rsp = 0x00007ffeefbff430
       r12 = 0x0000000000000000
       r13 = 0x0000000000000000
       r14 = 0x0000000000000000
       r15 = 0x0000000000000000
       rip = 0x0000000100003c24  lldb_demo`main + 244 at main.mm:18:17
13 registers were unavailable.

(lldb) register read --all
General Purpose Registers:
       rbx = 0x0000000000000000
       rbp = 0x00007ffeefbff4b0
       rsp = 0x00007ffeefbff430
       r12 = 0x0000000000000000
       r13 = 0x0000000000000000
       r14 = 0x0000000000000000
       r15 = 0x0000000000000000
       rip = 0x0000000100003c24  lldb_demo`main + 244 at main.mm:18:17
       ebx = 0x00000000
       ebp = 0xefbff420
       esp = 0xefbff420
        sp = 0xf420
61 registers were unavailable.

Floating Point Registers:
50 registers were unavailable.

Exception State Registers:
3 registers were unavailable.

可执行文件和共享库相关查询

我们在调试的时候用到image的场景不是特别常见,它主要用于调试的时候查看有哪些镜像、以及这些镜像的起始地址和结束地址。用于对比发生异常之后指令是位于哪个镜像。

类别 描述 GDB LLDB
可执行文件和共享库 展示当前可执行文件以及所依赖的所有库 info shared image list
查找指定地址相关信息 info symbol 0x1ec4 image lookup --address 0x1ec4
dump所有的sections maintenance info sections image dump sections
dump指定image的sections image dump sections a.out
dump指定image的符号表 image dump symtab lldb_demo
(lldb) image lookup --address 0x0000000100003b30
      Address: lldb_demo[0x0000000100003b30] (lldb_demo.__TEXT.__text + 0)
      Summary: lldb_demo`main at main.mm:12
      

(lldb) image dump section lldb_demo
Sections for '/Users/wangwang/Library/Developer/Xcode/DerivedData/lldb_demo-bcdczbxhxshevwakdquvexpupggc/Build/Products/Debug/lldb_demo' (x86_64):
  SectID     Type             Load Address                             Perm File Off.  File Size  Flags      Section Name
  ---------- ---------------- ---------------------------------------  ---- ---------- ---------- ---------- ----------------------------
  0x00000100 container        [0x0000000000000000-0x0000000100000000)* ---  0x00000000 0x00000000 0x00000000 lldb_demo.__PAGEZERO
  0x00000200 container        [0x0000000100000000-0x0000000100004000)  r-x  0x00000000 0x00004000 0x00000000 lldb_demo.__TEXT
  0x00000001 code             [0x0000000100003b30-0x0000000100003e3e)  r-x  0x00003b30 0x0000030e 0x80000400 lldb_demo.__TEXT.__text
  0x00000002 code             [0x0000000100003e3e-0x0000000100003e74)  r-x  0x00003e3e 0x00000036 0x80000408 lldb_demo.__TEXT.__stubs
  0x00000003 code             [0x0000000100003e74-0x0000000100003eca)  r-x  0x00003e74 0x00000056 0x80000400 lldb_demo.__TEXT.__stub_helper
  0x00000004 regular          [0x0000000100003ecc-0x0000000100003f08)  r-x  0x00003ecc 0x0000003c 0x00000000 lldb_demo.__TEXT.__gcc_except_tab
  0x00000005 data-cstr        [0x0000000100003f08-0x0000000100003f5c)  r-x  0x00003f08 0x00000054 0x00000002 lldb_demo.__TEXT.__objc_methname
  0x00000006 data-cstr        [0x0000000100003f5c-0x0000000100003f61)  r-x  0x00003f5c 0x00000005 0x00000002 lldb_demo.__TEXT.__cstring
  0x00000007 data-cstr        [0x0000000100003f61-0x0000000100003f67)  r-x  0x00003f61 0x00000006 0x00000002 lldb_demo.__TEXT.__objc_classname
  0x00000008 data-cstr        [0x0000000100003f67-0x0000000100003f95)  r-x  0x00003f67 0x0000002e 0x00000002 lldb_demo.__TEXT.__objc_methtype
  0x00000009 compact-unwind   [0x0000000100003f98-0x0000000100003ff4)  r-x  0x00003f98 0x0000005c 0x00000000 lldb_demo.__TEXT.__unwind_info
  0x00000300 container        [0x0000000100004000-0x0000000100008000)  rw-  0x00004000 0x00004000 0x00000010 lldb_demo.__DATA_CONST
  0x0000000a data-ptrs        [0x0000000100004000-0x0000000100004028)  rw-  0x00004000 0x00000028 0x00000006 lldb_demo.__DATA_CONST.__got
  0x0000000b data-ptrs        [0x0000000100004028-0x0000000100004030)  rw-  0x00004028 0x00000008 0x10000000 lldb_demo.__DATA_CONST.__objc_classlist
  0x0000000c regular          [0x0000000100004030-0x0000000100004038)  rw-  0x00004030 0x00000008 0x00000000 lldb_demo.__DATA_CONST.__objc_imageinfo
  0x00000400 container        [0x0000000100008000-0x000000010000c000)  rw-  0x00008000 0x00004000 0x00000000 lldb_demo.__DATA
  0x0000000d data-ptrs        [0x0000000100008000-0x0000000100008048)  rw-  0x00008000 0x00000048 0x00000007 lldb_demo.__DATA.__la_symbol_ptr
  0x0000000e data-ptrs        [0x0000000100008048-0x0000000100008168)  rw-  0x00008048 0x00000120 0x00000000 lldb_demo.__DATA.__objc_const
  0x0000000f data-cstr-ptr    [0x0000000100008168-0x0000000100008190)  rw-  0x00008168 0x00000028 0x10000005 lldb_demo.__DATA.__objc_selrefs
  0x00000010 data-ptrs        [0x0000000100008190-0x00000001000081a8)  rw-  0x00008190 0x00000018 0x10000000 lldb_demo.__DATA.__objc_classrefs
  0x00000011 regular          [0x00000001000081a8-0x00000001000081b0)  rw-  0x000081a8 0x00000008 0x00000000 lldb_demo.__DATA.__objc_ivar
  0x00000012 data-ptrs        [0x00000001000081b0-0x0000000100008200)  rw-  0x000081b0 0x00000050 0x00000000 lldb_demo.__DATA.__objc_data
  0x00000013 data             [0x0000000100008200-0x0000000100008208)  rw-  0x00008200 0x00000008 0x00000000 lldb_demo.__DATA.__data
  0x00000500 container        [0x000000010000c000-0x0000000100014000)  r--  0x0000c000 0x00005c10 0x00000000 lldb_demo.__LINKEDIT

使用Python脚本搭配LLDB

使用Python脚本的好处就是可以避免人工来做重复的事情,比如命中某个断点之后我们需要修改一些内存、寄存器、或者代码的执行路径等等。都是可以基于Python脚本来进行的。其大致步骤有:

  • 编写python脚本文件;
  • 导入脚本文件;
  • 导入脚本内部的命令(这一步可以没有);
    其中最主要的是第一步,毕竟导入文件和导入命令就两个lldb命令的事儿。因此主要来看看脚本文件的编写。

Python脚本编写

由于是编写基于LLDB的脚本,所以我们必须先导入lldb这个module(这是LLDB API 包含在一个名为 lldb 的 python 模块中,完整的API见LLDB Python API ):

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import lldb

其中我们可能会用到的一些变量包括有(下面表格里面所有提到的内容都可以通过 script help(lldb.xxxx) 来获取详细地帮助信息):

变量 类型 描述
lldb.debugger lldb.SBDebugger SBTarget.GetDebugger 调试器
lldb.target lldb.SBTarget SBDebugger.GetSelectedTarget 我们在启动lldb之后,可以通过target create来创建不同的target。然后对应run来指定要运行的target
lldb.process lldb.SBProcess SBTarget.GetProcess 进程
lldb.thread lldb.SBThread SBProcess.GetSelectedThread 线程
lldb.frame lldb.SBFrame SBThread.GetSelectedFrame 调用帧
(lldb) script
Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
>>> print(lldb.debugger)
Debugger (instance: "debugger_1", id: 1)
>>> print(lldb.process)
SBProcess: pid = 30104, state = stopped, threads = 1, executable = lldb_demo
>>> print(lldb.thread)
thread #1: tid = 0x61552d, 0x0000000100003b49 lldb_demo`main(argc=2, argv=0x00007ffeefbff468) at main.mm:14:22, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
>>> print(lldb.frame)
frame #0: 0x0000000100003b49 lldb_demo`main(argc=2, argv=0x00007ffeefbff468) at main.mm:14:22
>>> print(lldb.target)
lldb_demo

这些是我们在lldb内嵌的python解释器中执行相关的代码得到的结论。
现在我们来编写Python脚本。首先是创建一个Target,我们可以根据debugger来创建:

def pfbCreateTarget():
    # triple = "x86_64-apple-macosx"
    # platform_name = None
    # add_dependents = False
    path = os.getcwd() + "/lldb_demo"
    demo_target = lldb.debugger.CreateTargetWithFileAndArch (path, lldb.LLDB_ARCH_DEFAULT)
    return demo_target

创建好了target,那肯定是要执行这个target的。这里会返回一个process:

def pfbRunTarget(target):
    process = target.LaunchSimple(None, None, os.getcwd())
    return process

程序默认执行是不会暂停的,我们可以通过脚本来增加指定的断点:

def pfbCreateMainBreakpoint(target):
    breakpoint = target.BreakpointCreateByName("main", None)
    breakpoint.enable = True
    return breakpoint

到这里我们先让这个脚本能RUN起来,那么第一步肯定是要让我们这两个函数有地方调用。在LLDB中给我们提供了一个函数入口__lldb_init_module:

def __lldb_init_module(debugger, internal_dict):
    print("LLDB INIT MODULE")
    pfbTarget = pfbCreateTarget()
    print(pfbTarget)
    pfbMainBreakPoint = pfbCreateMainBreakpoint(pfbTarget)
    pfbProcess = pfbRunTarget(pfbTarget)

将脚本添加到lldb中:

(lldb) command script import ~/WorkStation/exec/llvm/lldb/script/python/search_fmp.py

可以得到如下结果,程序在指定的位置(断点处)暂停了:

如果是我们需要在脚本文件中处理断点相关的debug操作的话,LLDB给我们提供断点被执行时的回调函数。默认是breakpoint_function_wrapper。但是我们可以在执行指令的时候通过--python-function指定我们需要处理的函数(指定的函数参数必须要包含frame、bp_loc):

def breakpoint_function_wrapper(frame, bp_loc, extra_args, internal_dict):
    # Your code goes here
    print("breakpoint_function_wrapper1 begin:")
    print(frame)
    print("breakpoint_function_wrapper1 end")
    return True

'''
-----------------
私有函数
-----------------
'''
def pfbEnableBreakpointHook(target):
    # breakpoint command add -s python xxx --python-function search_fmp.breakpoint_function_wrapper
    bnums = target.GetNumBreakpoints()
    print("Enable Breakpoint nums: " + str(bnums))
    for i in range(0, bnums):
        brkpt = target.GetBreakpointAtIndex(i)
        cmd = "breakpoint command add -s python " + str(brkpt.id) + " --python-function search_fmp.breakpoint_function_wrapper"
        print("Enable Breakpoint: " + cmd)
        lldb.debugger.HandleCommand(cmd)

针对函数原型中提到的几个参数,下面做了一下简单的解释:

参数名称 类型 备注
frame lldb.SBFrame 断点被命中的当前堆栈帧
bp_loc lldb.SBBreakpointLocation 命中的断点位置
extra_args lldb.SBStructuredData 暂未使用
internal_dict dict python字典对象

得到的结论类似于这样:

使用Python脚本给LLDB新增指令

就像我在本文开头描述的那样,我们需要在lldb里面使用shell的命令需要加一个platform shell的前缀。显然这么麻烦的事情,并不是我们程序员要干的事儿。需求有了,那就来实现一下!
首先是定义我们需要使用的指令,这里我就以最常用的ls和pwd来举例:

def ls(debugger, command, exe_ctx, result, internal_dict):
    ret = subprocess.check_output(["ls", "-l"])
    print(ret)

def pwd(debugger, command, exe_ctx, result, internal_dict):
    ret = subprocess.check_output(["pwd"])
    print(ret)

def pfbShell(debugger, command, result, dict):
    command_args = shlex.split(command)
    ret = subprocess.check_output(command_args)
    print(ret)

在定义了对应的函数之后,我们需要将这些函数添加到command中:

debugger.HandleCommand("command script add -f search_fmp.pfbShell pfb")
debugger.HandleCommand("command script add -f search_fmp.pwd pwd")
debugger.HandleCommand("command script add -f search_fmp.ls ls")

Python脚本搭配LLDB还有很多的玩法,我就列出了简单的使用场景。

完整的代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# https://lldb.llvm.org/use/python-reference.html

import lldb
# python2 command, python3 subprocess
import subprocess
import optparse
import shlex
import os


'''
-----------------
LLDB 回调时机
-----------------
'''
# debugger: lldb.SBDebugger 调试器对象
def __lldb_init_module(debugger, internal_dict):
    print("LLDB INIT MODULE")
    pfbTarget = pfbCreateTarget()
    pfbMainBreakPoint = pfbCreateMainBreakpoint(pfbTarget)
    pfbEnableBreakpointHook(pfbTarget)
    pfbProcess = pfbRunTarget(pfbTarget)
    # lldb.target
    # debugger.HandleCommand("")
    debugger.HandleCommand("command script add -f search_fmp.pfbShell pfb")
    debugger.HandleCommand("command script add -f search_fmp.pwd pwd")
    debugger.HandleCommand("command script add -f search_fmp.ls ls")

# frame: lldb.SBFrame 断点被命中的当前堆栈帧
# bp_loc: lldb.SBBreakpointLocation 命中的断点位置
# extra_args: lldb.SBStructuredData
# internal_dict: dict
def breakpoint_function_wrapper(frame, bp_loc, extra_args, internal_dict):
    # Your code goes here
    print("breakpoint_function_wrapper1 begin:")
    print(frame)
    print("breakpoint_function_wrapper1 end")
    return True

'''
-----------------
command
-----------------
'''
def ls(debugger, command, exe_ctx, result, internal_dict):
    ret = subprocess.check_output(["ls", "-l"])
    print(ret)

def pwd(debugger, command, exe_ctx, result, internal_dict):
    ret = subprocess.check_output(["pwd"])
    print(ret)

def pfbShell(debugger, command, result, dict):
    command_args = shlex.split(command)
    ret = subprocess.check_output(command_args)
    print(ret)

'''
-----------------
私有函数
-----------------
'''
def pfbEnableBreakpointHook(target):
    # breakpoint command add -s python xxx --python-function search_fmp.breakpoint_function_wrapper
    bnums = target.GetNumBreakpoints()
    print("Enable Breakpoint nums: " + str(bnums))
    for i in range(0, bnums):
        brkpt = target.GetBreakpointAtIndex(i)
        cmd = "breakpoint command add -s python " + str(brkpt.id) + " --python-function search_fmp.breakpoint_function_wrapper"
        print("Enable Breakpoint: " + cmd)
        lldb.debugger.HandleCommand(cmd)
    

def pfbCreateMainBreakpoint(target):
    breakpoint = target.BreakpointCreateByName("main", None)
    breakpoint.enable = True
    return breakpoint

def pfbCreateTarget():
    triple = "x86_64-apple-macosx11.5.0"
    platform_name = None
    add_dependents = False
    path = os.getcwd() + "/lldb_demo"
    print("Target Create: " + path)
    # https://lldb.llvm.org/use/symbolication.html#using-python-api-to-symbolicate
    target = lldb.debugger.CreateTarget(path, triple, platform_name, add_dependents, lldb.SBError())
    #target = lldb.debugger.CreateTargetWithFileAndArch (path, lldb.LLDB_ARCH_DEFAULT)
    return target

def pfbRunTarget(target):
    process = target.LaunchSimple(None, None, os.getcwd())
    return process

引用

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,565评论 6 525
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,696评论 3 406
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,935评论 0 370
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,327评论 1 303
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,338评论 6 401
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,760评论 1 316
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,085评论 3 431
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,091评论 0 280
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,656评论 1 327
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,657评论 3 348
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,767评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,360评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,088评论 3 341
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,493评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,654评论 1 278
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,374评论 3 383
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,841评论 2 367

推荐阅读更多精彩内容

  • 你是否曾经苦恼于理解你的代码,而去尝试打印一个变量的值? NSLog(@"%@", whatIsInsideThi...
    paraneaeee阅读 1,198评论 0 7
  • 你是否曾经苦恼于理解你的代码,而去尝试打印一个变量的值? NSLog(@"%@", whatIsInsideThi...
    木易林1阅读 958评论 0 4
  • 转载 与调试器共舞 - LLDB 的华尔兹: https://objccn.io/issue-19-2/ 推荐:i...
    F麦子阅读 3,339评论 0 10
  • 你是否曾经苦恼于理解你的代码,而去尝试打印一个变量的值? NSLog(@"%@", whatIsInsideThi...
    F麦子阅读 1,254评论 1 2
  • 你是否曾经苦恼于理解你的代码,而去尝试打印一个变量的值? NSLog(@"%@", whatIsInsideThi...
    我是啊梁阅读 808评论 1 1