iOS 常用除錯方法:LLDB命令

QiShare發表於2019-03-11

級別: ★☆☆☆☆
標籤:「Xcode控制檯除錯」「iOS 除錯臺」「iOS Console」
作者: Xs·H
審校: QiShare團隊


在iOS專案開發過程中,常用到靜態分析(Analyze)、斷點(Breakpoint)和控制檯(Console)進行程式碼除錯,其中控制檯除錯最核心的的就是LLDB命令。本篇文章介紹作者在專案中最常用到的LLDB命令除錯方法。

一、簡介

LLDB是新一代高效能偵錯程式。 它構建為一組可重用的元件,可以高度利用較大的LLVM專案中的現有庫,例如Clang表示式解析器和LLVM反彙編程式。 LLDB是Mac OS X上Xcode的預設偵錯程式,支援在桌面和iOS裝置和模擬器上除錯C,Objective-C和C ++。 LLDB專案中的所有程式碼都是在標準LLVM許可證下提供的,這是一種開源的“BSD風格”許可證。 更多介紹可從LLDB Homepage獲取。

二、幫助

LLDB命令格式如下: <命令名稱> <命令動作> [-可選項 [可選項的值]] [引數1 [引數2...]]

LLDB命令的各部分由空格分割,如果引數中包含空格,則需要使用雙引號括起引數,如果引數中包含雙引號或者反斜槓,則需要使用反斜槓進行轉義。

LLDB命令有非常多的功能,完全背下來不太容易,也沒必要。開發者可以使用help命令檢視相關命令的用法,甚至可以檢視help命令的用法。

(lldb) help help
     Show a list of all debugger commands, or give details about a specific
     command.

Syntax: help [<cmd-name>]

Command Options Usage:
  help [-ahu] [<cmd-name> [<cmd-name> [...]]]

       -a ( --hide-aliases )
            Hide aliases in the command list.

       -h ( --show-hidden-commands )
            Include commands prefixed with an underscore.

       -u ( --hide-user-commands )
            Hide user-defined commands from the list.
     
     This command takes options and free-form arguments.  If your arguments
     resemble option specifiers (i.e., they start with a - or --), you must use
     ' -- ' between the end of the command options and the beginning of the
     arguments.
複製程式碼

三、執行

在LLDB中,執行命令expression是最基礎的命令,簡寫為expr或者e,語法為:expression <cmd-options> -- <expr>,它的作用是執行一個表示式,並輸出表示式返回的結果。示例如下。

iOS 常用除錯方法:LLDB命令

//! 輸出count的值
(lldb) expression count
(NSUInteger) $0 = 10

//! 執行string的拼接字串方法
(lldb) expression [string stringByAppendingString:@"732"]
(__NSCFString *) $1 = 0x00006000006ac870 @"QiShare732"

//! 修改view的顏色
(lldb) expression self.view.backgroundColor = [UIColor redColor]
(UICachedDeviceRGBColor *) $2 = 0x0000600001d74780
(lldb) expression [CATransaction flush]
//!< 因為斷點會終止UI執行緒,執行[CATransaction flush]命令可以渲染出修改後的介面
複製程式碼

四、列印

列印命令print是最常用的命令,簡寫為pri或者p,語法為:print <expr>,它的作用是列印變數或者表示式。通過help print會發現print其實是expression --命令的簡寫:'print' is an abbreviation for 'expression --',其中--標識選項的結束和引數的開始。 同樣常用的expression簡寫命令還有pocall。其中po表示print object,用來列印物件,call用來呼叫某個方法。示例如下:

iOS 常用除錯方法:LLDB命令

(lldb) expression -- self.view
(UIView *) $4 = 0x00007f8ca8401690

(lldb) e self.view
(UIView *) $5 = 0x00007f8ca8401690

(lldb) p self.view
(UIView *) $6 = 0x00007f8ca8401690

(lldb) po self.view
<UIView: 0x7f8ca8401690; frame = (0 0; 375 812); autoresize = W+H; layer = <CALayer: 0x6000008a1dc0>>

(lldb) call self.view
(UIView *) $8 = 0x00007f8ca8401690
複製程式碼

另外,開發者可以按照print/<fmt>的語法為print命令指定列印格式:

p/x  //!< 以十六進位制列印整數
p/d  //!< 以帶符號的十進位制列印整數
p/u  //!< 以無符號的十進位制列印整數
p/o  //!< 以八進位制列印整數
p/t  //!< 以二進位制列印整數
p/a  //!< 以十六進位制列印地址
p/c  //!< 列印字元常量
p/f  //!< 列印浮點數
p/s  //!< 列印字串
p/r  //!< 格式化列印
複製程式碼

示例如下:

iOS 常用除錯方法:LLDB命令

(lldb) p count
(NSUInteger) $0 = 10

(lldb) p/t count
(NSUInteger) $1 = 0b0000000000000000000000000000000000000000000000000000000000001010

(lldb) p/o count
(NSUInteger) $2 = 012

(lldb) p/x count
(NSUInteger) $3 = 0x000000000000000a

(lldb) p/x string
(__NSCFConstantString *) $4 = 0x000000010416a0b8 @"QiShare"

(lldb) p/c string
(__NSCFConstantString *) $5 = \xb8\xa0\x16\x04\x01\0\0\0 @"QiShare"

(lldb) p/s string
(__NSCFConstantString *) $6 = @"QiShare"

(lldb) p/a string
(__NSCFConstantString *) $7 = 0x000000010416a0b8 @"QiShare" @"QiShare"
複製程式碼

五、執行緒

thread是執行緒相關的命令,語法為thread <subcommand> [<subcommand-options>],它可以接受不同的可選引數,以實現豐富的功能。其中thread listthread backtracethread return較為常用。 開發者可以使用thread list命令檢視當前的所有執行緒,示例如下:

(lldb) thread list
Process 4031 stopped
* thread #1: tid = 0x25cac, 0x0000000104167e23 QiDebugDemo`-[QiConsoleViewController testLLDBCommands](self=0x00007f850df0bbf0, _cmd="testLLDBCommands") at QiConsoleViewController.m:34, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
  thread #2: tid = 0x25d2f, 0x00000001079ff28a libsystem_kernel.dylib`__workq_kernreturn + 10
  thread #3: tid = 0x25d30, 0x00000001079ff28a libsystem_kernel.dylib`__workq_kernreturn + 10
  thread #4: tid = 0x25d31, 0x00000001079ff28a libsystem_kernel.dylib`__workq_kernreturn + 10
  thread #5: tid = 0x25d32, 0x00000001079ff28a libsystem_kernel.dylib`__workq_kernreturn + 10
  thread #6: tid = 0x25d33, 0x00000001079ff28a libsystem_kernel.dylib`__workq_kernreturn + 10
  thread #7: tid = 0x25d3e, 0x00000001079f520a libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
複製程式碼

thread backtrace命令可以方便地供開發者檢視執行緒堆疊資訊,簡寫為bt。比如,當程式崩潰的時候,開發者可以檢視堆疊呼叫列表。示例如下。

iOS 常用除錯方法:LLDB命令

(lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
    frame #0: 0x0000000104cc2705 libobjc.A.dylib`objc_exception_throw
    frame #1: 0x00000001056704ec CoreFoundation`_CFThrowFormattedException + 194
    frame #2: 0x00000001057a6b00 CoreFoundation`-[__NSArrayI objectAtIndexedSubscript:] + 96
  * frame #3: 0x00000001043a1df7 QiDebugDemo`-[QiConsoleViewController testLLDBCommands](self=0x00007fadc7c50400, _cmd="testLLDBCommands") at QiConsoleViewController.m:33
    frame #4: 0x00000001043a1d5a QiDebugDemo`-[QiConsoleViewController viewDidLoad](self=0x00007fadc7c50400, _cmd="viewDidLoad") at QiConsoleViewController.m:26
...
    frame #18: 0x00000001056830be CoreFoundation`__CFRunLoopDoObservers + 430
    frame #19: 0x0000000105683751 CoreFoundation`__CFRunLoopRun + 1537
    frame #20: 0x0000000105682e11 CoreFoundation`CFRunLoopRunSpecific + 625
    frame #21: 0x000000010ddd51dd GraphicsServices`GSEventRunModal + 62
    frame #22: 0x000000010a1db81d UIKitCore`UIApplicationMain + 140
    frame #23: 0x00000001043a2450 QiDebugDemo`main(argc=1, argv=0x00007ffeeb85df90) at main.m:7
    frame #24: 0x0000000107858575 libdyld.dylib`start + 1
複製程式碼

在除錯過程中,開發者可以使用thread return命令終端某個方法並返回一個想要的值。示例如下。

iOS 常用除錯方法:LLDB命令

(lldb) thread return string
(lldb) continue
2019-02-27 17:22:47.323225+0800 QiDebugDemo[5071:222700] resultString: Qi_Share
複製程式碼

六、斷點

作者在iOS 除錯方法:斷點這篇文章中介紹過斷點的用法。其實,視覺化的斷點都可以使用LLDB語法來實現。比如下圖中的1、2、3、4、5都能用LLDB命令表達。

iOS 常用除錯方法:LLDB命令

  1. 啟用/禁用斷點(breakpoint enable/disable)
  2. 繼續執行程式(continue)
  3. 執行下一步(next)
  4. 進入方法(step)
  5. 跳出方法(finish)

在斷點相關操作中,因為Xcode已經整合了視覺化的斷點操作工具,所以breakpoint命令並不被常用。但是,breakpoint命令擁有著十分強大的功能,語法為:breakpoint <subcommand> [<command-options>],主要命令示例如下。

//! 檢視所有斷點
(lldb) breakpoint list

//! 為所有類中的viewDidAppear:設定斷點
(lldb) breakpoint set -n viewDidAppear:

//! 為QiConsoleViewController.m檔案中的testLLDBCommands方法設定斷點
(lldb) breakpoint set -f QiConsoleViewController.m -n testLLDBCommands

//! 為QiConsoleViewController.m檔案中的第32行程式碼設定斷點
(lldb) breakpoint set -f QiConsoleViewController.m -l 32

//! 為handleString:方法設定條件斷點,條件為string != nil
(lldb) breakpoint set - handleString: -c string != nil
複製程式碼

七、觀察點

想比較於breakpoint是對方法生效的斷點,watchpoint則是對地址生效的斷點。watchpoint類似於KVO的工作原理,當觀察到屬性地址裡面的東西改變時,就讓程式中斷,其語法為:watchpoint <subcommand> [<command-options>]。其應用場景示例如下。

iOS 常用除錯方法:LLDB命令

(lldb) watchpoint set variable string
Watchpoint created: Watchpoint 1: addr = 0x7ffeef497360 size = 8 state = enabled type = w
    declare @ '/Users/huangxianshuai/Desktop/Products/QiShare/QiDebugDemo/QiDebugDemo/QiConsoleViewController.m:33'
    watchpoint spec = 'string'
    new value: 0x00000001007670b8

(lldb) next

Watchpoint 1 hit:
old value: 0x00000001007670b8
new value: 0x0000000100767138

(lldb) image lookup -a 0x00000001007670b8
      Address: QiDebugDemo[0x00000001000040b8] (QiDebugDemo.__DATA.__cfstring + 32)
      Summary: @"QiShare"
(lldb) image lookup -a 0x0000000100767138
      Address: QiDebugDemo[0x0000000100004138] (QiDebugDemo.__DATA.__cfstring + 160)
      Summary: @"huang"
複製程式碼

image lookup -aimage lookup -address的簡寫,可以檢視引數地址的內容。

總結

文章僅列舉了作者平常使用到的一些LLDB命令,這些命令的確能提高除錯效率。而更多的LLDB命令可以從LLDB Homepage或者其他網路資源中獲取。


小編微信:可加並拉入《QiShare技術交流群》。

iOS 常用除錯方法:LLDB命令

關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)

推薦文章:
iOS訊息轉發
iOS 自定義拖拽式控制元件:QiDragView
iOS 自定義卡片式控制元件:QiCardView
iOS Wireshark抓包
iOS Charles抓包
奇舞週刊

相關文章