LLDB基本操作

前言

LLDB是搭配LLVM的一個調(diào)試工具纬黎,就如同GDB之于GCC。我們?nèi)粘i_發(fā)中幾乎時時刻刻和它在打交道肄渗。下面簡單地從多個方面來了解以及使用LLDB:

  • 1镇眷、BreakPoint和WatchPoint;
  • 2啼肩、程序控制;
  • 3程储、線程狀態(tài)和棧幀狀態(tài)萝挤;

其中BreakPoint也就是我們常說的斷點,它是我們?nèi)粘U{(diào)試中使用過最多的能力,其作用自不必多言;WatchPoint相對來說用的就很少了垦梆,我們可以稱之為“觀察點”他宛。我們可以通過添加觀察點來關注我們指定變量的變化船侧。
程序控制就是指的在我們調(diào)試過程中的單步執(zhí)行、繼續(xù)執(zhí)行厅各、進入子過程內(nèi)部以及從子過程內(nèi)部跳出镜撩。
線程狀態(tài)和棧幀狀態(tài)則是查看當前線程的執(zhí)行狀態(tài),包括過程調(diào)用队塘、變量信息以及寄存器狀態(tài)等等袁梗。
下面提及的所有命令,如果不清楚如何使用都可以使用 help xxx 形式來進行查詢:

help breakpoint
help watchpoint
help memory
...

程序啟動和控制相關

程序啟動大致可以分為兩種憔古,第一種是在啟動lldb的時候指定要debug的程序遮怜;第二種是在啟動lldb之后,再明確指定要啟動的程序:

/// 第一種啟動lldb的時候指定待調(diào)試的程序
?  lldb lldb_demo
/// 第二種先啟動lldb投放,然后指定要啟動的調(diào)試程序
?  lldb
(lldb) run /Users/.../db_demo 1 2 3
/// 第三種創(chuàng)建不同的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里面執(zhí)行shell相關的指令灸芳。可以使用如下方式:

platform shell xxxx拜姿。當然也可以使用command alias為platform shell取一個別名烙样。

下表中的內(nèi)容簡單的列舉了一下相關的指令:

類別 描述 GDB LLDB 備注
程序啟動 啟動指定進程 run/r <args> xxx process launch -- <args>、run/r <args> xxx args啟動過程中需要的參數(shù),對應main函數(shù)中的參數(shù)
展示傳入的參數(shù) show args settings show target.run-args 展示我們設置的參數(shù)
設置環(huán)境變量 set env DEBUG 1 env DEBUG=1 設置環(huán)境變量
程序控制 源碼級別進入執(zhí)行 step/s thread step-in 單步執(zhí)行到指定過程的內(nèi)部(比如子函數(shù)調(diào)用時,進入子函數(shù)內(nèi)部)
指令級別進入執(zhí)行 stepi/si thread step-inst
源碼級別跳過執(zhí)行 next/n thread step-over
指令級別跳過執(zhí)行 nexti/ni hread step-inst-over
結束執(zhí)行 finish thread step-out
///####################################### 展示傳入的參數(shù)
(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

下面列出了我們調(diào)試過程中需要設置斷點和觀察點的方法恶复,以及編輯斷點和觀察點的命令盛末。

類別 描述 GDB LLDB 備注
斷點相關 基于函數(shù)名稱設置斷點 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 這里命令后面帶的數(shù)字,即某一斷點在breakpoint list里面的序號
使指定斷點失效 disable 1 breakpoint disable 1
使指定斷點生效 enable 1 breakpoint enable 1
觀察點相關 設置觀察點 watch ctlptr->len watchpoint set variable ctlptr->len 這里命令后面帶的數(shù)字态坦,即某一觀察點在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

變量和表達式執(zhí)行

使用expr我們可以在debug的時候去執(zhí)行相關的表達式,以達到運行期間修改的目的。比如在運行時我們想要修改某一個View的背景色赔硫,那么我們就可以使用expr來達到這個目的,而不需要重新編譯執(zhí)行盐肃。

類別 描述 GDB LLDB 備注
變量 查看當前幀變量 info args frame variable
展示局部變量 info locals frame variable --no-args 局部變量爪膊,即非外部傳入的變量
打印指定變量 p bar p bar
執(zhí)行表達式 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
 
 ///####################################### 執(zhí)行表達式相關 
 (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

線程狀態(tài)

其中bt是我們使用的最多的一個命令,它可以打印出當前線程的調(diào)用堆棧砸王。

類別 描述 GDB LLDB 備注
Thread State 列舉程序中的線程信息 info threads thread list
選中某個線程 thread 1 thread select 1 序號為thread list中的編號
當前線程調(diào)用棧 bt thread backtrace
所有線程調(diào)用棧 thread apply all bt bt all
當前線程調(diào)用棧前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'
  
 ///####################################### 調(diào)用堆棧
(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

///####################################### 調(diào)用堆棧前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;

內(nèi)存和寄存器

當我們發(fā)生了bad access/類型異常等等錯誤推盛,為了定位到底發(fā)生了什么我們就需要去查看對應地址上的內(nèi)存值。

類別 描述 GDB LLDB 備注
內(nèi)存相關 讀取指定地址的內(nèi)存值 x/10xw 0x0000000100704080 x/10xw 0x0000000100704080

上表中和內(nèi)存讀取相關的指令格式中存在一個緊跟著反斜杠的10bw內(nèi)容谦铃,這里我簡單解釋一下他們的作用:

  • N(Number):需要讀取的個數(shù)耘成,比如這里是10;
  • F(Format):我們讀取的格式,比如這里的b瘪菌。它和我們平時使用printf格式一樣:‘x’(十六進制), ‘d’, ‘u’, ‘o’, ‘t’, ‘a(chǎn)’, ‘c’, ‘f’, ‘s’, ‘m’件豌;
  • U(Unit):上面提到的個數(shù),那么U就是決定每個的展示內(nèi)容的大锌厥取:
    b Bytes:字節(jié)
    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用于確定當前棧的區(qū)域;ip/pc用于確定當前執(zhí)行指令壁顶;lr寄存器保存函數(shù)調(diào)用返回地址等等珠洗。

類別 描述 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.

可執(zhí)行文件和共享庫相關查詢

我們在調(diào)試的時候用到image的場景不是特別常見,它主要用于調(diào)試的時候查看有哪些鏡像若专、以及這些鏡像的起始地址和結束地址许蓖。用于對比發(fā)生異常之后指令是位于哪個鏡像。

類別 描述 GDB LLDB
可執(zhí)行文件和共享庫 展示當前可執(zhí)行文件以及所依賴的所有庫 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腳本的好處就是可以避免人工來做重復的事情调衰,比如命中某個斷點之后我們需要修改一些內(nèi)存膊爪、寄存器、或者代碼的執(zhí)行路徑等等嚎莉。都是可以基于Python腳本來進行的米酬。其大致步驟有:

  • 編寫python腳本文件;
  • 導入腳本文件趋箩;
  • 導入腳本內(nèi)部的命令(這一步可以沒有)赃额;
    其中最主要的是第一步,畢竟導入文件和導入命令就兩個lldb命令的事兒叫确。因此主要來看看腳本文件的編寫跳芳。

Python腳本編寫

由于是編寫基于LLDB的腳本,所以我們必須先導入lldb這個module(這是LLDB API 包含在一個名為 lldb 的 python 模塊中竹勉,完整的API見LLDB Python API ):

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

import lldb

其中我們可能會用到的一些變量包括有(下面表格里面所有提到的內(nèi)容都可以通過 script help(lldb.xxxx) 來獲取詳細地幫助信息):

變量 類型 描述
lldb.debugger lldb.SBDebugger SBTarget.GetDebugger 調(diào)試器
lldb.target lldb.SBTarget SBDebugger.GetSelectedTarget 我們在啟動lldb之后飞盆,可以通過target create來創(chuàng)建不同的target。然后對應run來指定要運行的target
lldb.process lldb.SBProcess SBTarget.GetProcess 進程
lldb.thread lldb.SBThread SBProcess.GetSelectedThread 線程
lldb.frame lldb.SBFrame SBThread.GetSelectedFrame 調(diào)用幀
(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內(nèi)嵌的python解釋器中執(zhí)行相關的代碼得到的結論饶米。
現(xiàn)在我們來編寫Python腳本桨啃。首先是創(chuàng)建一個Target,我們可以根據(jù)debugger來創(chuàng)建:

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

創(chuàng)建好了target檬输,那肯定是要執(zhí)行這個target的照瘾。這里會返回一個process:

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

程序默認執(zhí)行是不會暫停的,我們可以通過腳本來增加指定的斷點:

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

到這里我們先讓這個腳本能RUN起來丧慈,那么第一步肯定是要讓我們這兩個函數(shù)有地方調(diào)用析命。在LLDB中給我們提供了一個函數(shù)入口__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給我們提供斷點被執(zhí)行時的回調(diào)函數(shù)鹃愤。默認是breakpoint_function_wrapper簇搅。但是我們可以在執(zhí)行指令的時候通過--python-function指定我們需要處理的函數(shù)(指定的函數(shù)參數(shù)必須要包含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

'''
-----------------
私有函數(shù)
-----------------
'''
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)

針對函數(shù)原型中提到的幾個參數(shù)软吐,下面做了一下簡單的解釋:

參數(shù)名稱 類型 備注
frame lldb.SBFrame 斷點被命中的當前堆棧幀
bp_loc lldb.SBBreakpointLocation 命中的斷點位置
extra_args lldb.SBStructuredData 暫未使用
internal_dict dict python字典對象

得到的結論類似于這樣:

使用Python腳本給LLDB新增指令

就像我在本文開頭描述的那樣瘩将,我們需要在lldb里面使用shell的命令需要加一個platform shell的前綴。顯然這么麻煩的事情凹耙,并不是我們程序員要干的事兒姿现。需求有了,那就來實現(xiàn)一下肖抱!
首先是定義我們需要使用的指令备典,這里我就以最常用的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)

在定義了對應的函數(shù)之后,我們需要將這些函數(shù)添加到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 回調(diào)時機
-----------------
'''
# debugger: lldb.SBDebugger 調(diào)試器對象
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)

'''
-----------------
私有函數(shù)
-----------------
'''
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

引用

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市荤崇,隨后出現(xiàn)的幾起案子拌屏,更是在濱河造成了極大的恐慌,老刑警劉巖天试,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件槐壳,死亡現(xiàn)場離奇詭異,居然都是意外死亡喜每,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門雳攘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來带兜,“玉大人,你說我怎么就攤上這事吨灭「照眨” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵喧兄,是天一觀的道長无畔。 經(jīng)常有香客問我,道長吠冤,這世上最難降的妖魔是什么浑彰? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮拯辙,結果婚禮上郭变,老公的妹妹穿的比我還像新娘颜价。我一直安慰自己,他們只是感情好诉濒,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布周伦。 她就那樣靜靜地躺著,像睡著了一般未荒。 火紅的嫁衣襯著肌膚如雪专挪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天片排,我揣著相機與錄音寨腔,去河邊找鬼。 笑死划纽,一個胖子當著我的面吹牛脆侮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勇劣,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼靖避,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了比默?” 一聲冷哼從身側響起幻捏,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎命咐,沒想到半個月后篡九,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡醋奠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年榛臼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窜司。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡沛善,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出塞祈,到底是詐尸還是另有隱情金刁,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布议薪,位于F島的核電站尤蛮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏斯议。R本人自食惡果不足惜产捞,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捅位。 院中可真熱鬧轧葛,春花似錦搂抒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衷笋,卻和暖如春芳杏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辟宗。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工爵赵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泊脐。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓空幻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親容客。 傳聞我的和親對象是個殘疾皇子秕铛,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容