1 什么是 Chisel
Chisel 是一個 LLDB 命令集, 用于輔助 iOS 程序的調試. 它的 Github 主頁在這里.
要了解 Chisel, 首先需要了解什么是 LLDB. 如果已經了解過相關知識, 請直接跳過 LLDB 相關內容.
2 LLDB 簡介
眾所周知, 一個典型的bug修復周期包括: 修改代碼—>編譯—>運行, 然后祈禱結果是對的. 其實還有更快和更簡單的辦法, 就是利用 LLDB, 但千萬不要只把它當做值的觀察器來使用!
LLDB 是一個開源調試器, 其特點是: 實現了一個"讀取-求值-輸出""循環, 并且擁有眾多的 C++ 和 Python 插件, 并且已經被集成到了 Xcode 中.
LLDB 有眾多的插件, 而 Chisel 就是它的一個利用 Python 語言寫的插件集, 下面先來看看 LLDB 的基本使用.
3 LLDB 的基本使用
首先來看如下的代碼, 當觸發斷點后, LLDB 命令行輸入會出現在界面的左下角:
下面就來看看一些 LLDB 的基本命令的使用.
3.1 help 命令
最簡單的命令是 help
, 它會列出 LLDB 的幫助信息, 如果忘記某個命令的用法, 則可以使用 help <command>
來打印該命令的詳細信息, 比如 help print
或者 help thread
. 當然也可以這樣用 help help
??.
3.2 print 命令
有時希望打印出某個變量的值, 則可以使用 print
命令:
由于 LLDB 會自動進行命令前綴匹配, 則實際上該命令還可以這樣寫: prin
, pri
或 p
. 但不能使用 pr
, 因為它和 process
命令沖突.(LLDB 中規定 p
是對應 print
, 所以請放心使用)
另外結果被自動放入了一個 $0
變量中, 所以接下來還可以使用該變量. 比如 print $0 + 9
, 則會打印 108
.
在 LLDB 中, 任何以 $
符號開頭的變量都可以在接下來的命令中進行使用.
3.3 expression 命令
調試的時候, 有時候想直接為某個變量賦值, 然后觀察后續代碼的行為. 此時就可以用 expression
命令來改變某個變量的值. 注意, 這里改變的并非 LLDB 中變量的值, 而是運行程序中變量的值!
下面就來使用 p
和 e
(分別是 print
和 expression
的縮寫)做些事情.
3.4 print 命令詳解
來看這條 LLDB 語句: p count = 18
. 這條語句會直接改變 count 的值并打印出來. 如果使用 help print
, 就會發現如下輸出:
'print' is an abbreviation for 'expression --'.
其中的"兩道杠"是用來分隔標志位和表達式.
假設實際使用時輸入 e -h +17
, 此時無法分辨到底 -h
指的是標志位, 還是說變量 h
的相反數. 所以需要"兩道杠"分隔符, 這樣就很容易分辨了:
e -h -- +17 中 -h 指的是標志位
e -- -h +17 中 -h 指的是h的相反數
所以 print
是 expression
無標志位時候的縮寫.
3.5 打印程序中的對象
我們調試的時候想打印一個對象, 但是直接執行 p object
的話, 結果往往是??:
(NSString *) $7 = 0x0000000104da4040 @"red balloons"
甚至是??:
(lldb) p @[ @"foo", @"bar" ]
(NSArray *) $8 = 0x00007fdb9b71b3e0 @"2 objects"
實際上我們只想打印該對象的 description
方法所輸出的內容, 這時可以使用 -O
標志:
(lldb) e -O -- $8
<__NSArrayI 0x7fdb9b71b3e0>(
foo,
bar
)
清爽了好多. 該命令的縮寫是 po
(print object), 使用 po
的效果和上面一模一樣:
(lldb) po $8
<__NSArrayI 0x7fdb9b71b3e0>(
foo,
bar
)
3.6 按指定形式打印
使用 print
打印時數字, 默認是10進制:
(lldb) p 16
16
可以使用 print/<fmt>
指定打印的形式, 比如 x
表示十六進制:
(lldb) p/x 16
0x10
使用 t
表示二進制:
(lldb) p/t 16
0b00000000000000000000000000010000
(lldb) p/t (char)16
0b00010000
另外 p/c
表示字符(character), p/s
表示字符串(string, 即以 \0 結尾的 char*).
3.7 變量
LLDB 中的變量必須以 $
符號開頭, 使用 LLDB 變量, 可以簡化很多操作:
(lldb) e int $a = 2
(lldb) p $a * 19
38
(lldb) e NSArray *$array = @[ @"Saturday", @"Sunday", @"Monday" ]
(lldb) p [$array count]
2
(lldb) po [[$array objectAtIndex:0] uppercaseString]
SATURDAY
(lldb) p [[$array objectAtIndex:$a] characterAtIndex:0]
error: no known method '-characterAtIndex:'; cast the message send to the method's return type
error: 1 errors parsing expression
上述代碼中 LLDB 無法區分變量類型, 所以需要給它一些提示:
(lldb) p (char)[[$array objectAtIndex:$a] characterAtIndex:0]
'M'
(lldb) p/d (char)[[$array objectAtIndex:$a] characterAtIndex:0]
77
3.8 流程控制
可以使用 LLDB 命令來控制調試流程, 調試過程中的流程控制命令有如下四個:
- 繼續:
c
- 單步:
n
- 單步進入:
s
- 單步退出:
finish
講了這么多關于 LLDB 的內容, 下面就來看如何安裝和使用 Chisel.
4 安裝 Chisel
可以使用 HomeBrew 來安裝 Chisel, 根據命令行提示完成安裝即可:
brew update
brew install chisel
也可以直接下載 Chisel, 然后添加 ~/.lldbinit 文件, 內容如下:
# ~/.lldbinit
...
command script import /path/to/fblldb.py
注意, 寫這個文檔的時候, chisel是1.5.0版本, 用 homebrew 安裝完成后, 有可能 .lldbinit 文件并沒有被創建, 導致在 Xcode 中無法使用 chisel. 故需要在用戶根目錄手動創建 .lldbinit 文件, 并添加文件內容:
command script import /usr/local/opt/chisel/libexec/fblldb.py
這里該 fblldb.py 文件的位置可能有所不同, 需要根據情況修改.(該文件路徑在安裝 chisel 的時候會有提示)
安裝完成后, 再次啟動 Xcode 時, 就可以在 LLDB 中使用 Chisel 中的所有命令了.
5 如何使用 Chisel
由于 Chisel 是一個 LLDB 命令集插件, 故使用時直接調用其提供的命令即可.
要查詢所有命令, 可以在 LLDB 中執行help
, 或查看 Chisel 官方命令列表: 完整命令列表.
(lldb) help
The following is a list of built-in, permanent debugger commands:
...
The following is a list of your current user-defined commands:
...
下面是一些常用命令:
命令 | 含義 | iOS | OS X |
---|---|---|---|
pviews | Print the recursive view description for the key window. | Yes | Yes |
pvc | Print the recursive view controller description for the key window. | Yes | No |
visualize | Open a UIImage , CGImageRef , UIView , CALayer , NSData (of an image), UIColor , CIColor , or CGColorRef in Preview.app on your Mac. |
Yes | No |
fv | Find a view in the hierarchy whose class name matches the provided regex. | Yes | No |
fvc | Find a view controller in the hierarchy whose class name matches the provided regex. | Yes | No |
show/hide | Show or hide the given view or layer. You don't even have to continue the process to see the changes! | Yes | Yes |
mask/unmask | Overlay a view or layer with a transparent rectangle to visualize where it is. | Yes | No |
border/unborder | Add a border to a view or layer to visualize where it is. | Yes | Yes |
caflush | Flush the render server (equivalent to a "repaint" if no animations are in-flight). | Yes | Yes |
bmessage | Set a symbolic breakpoint on the method of a class or the method of an instance without worrying which class in the hierarchy actually implements the method. | Yes | Yes |
wivar | Set a watchpoint on an instance variable of an object. | Yes | Yes |
presponder | Print the responder chain starting from the given object. | Yes | Yes |
... | ... and many more! |
Chisel 特別在界面調試上有很大用途. 下面僅挑常用的說, 詳細文檔請移步這里.
-
show 和 hide:
比如界面上有一個label, 變量名為 textLabel, 要顯示它, 則使用:
show textLabel
要隱藏, 則使用:
hide textLabel
-
alamborder 和 alamunborder:
alamborder 可以將界面上存在歧義布局(ambiguous layout)的視圖標注出來, 比如標注為紅色邊框, 邊框線寬為2, 則使用(如果指定參數, 則:
alamborder --color=red --width=2
要取消, 則使用:
alamunborder
-
border 和 unborder
將一個視圖用線框包裹起來:
border xyzLabel unborder //隱藏
執行這個命令, 它會使用默認配置: 即紅色邊框將該視圖的矩形框標識出來. 當然也可以指定顏色線寬和其他參數.
-
dismiss
將使用 present: 方法顯示出來的 VC 取消顯示:
dismiss xxxViewController
-
flicker
當看到一個視圖變量, 但在代碼中找很久都找不到它到底對應界面上什么東西的時候, 這個命令就可以出來幫忙了, 比如有一個 xyzLabel 變量, 你只知道它是個label ??, 但如何找到它呢? 就使用如下方法, 這時 xyzLabel 會在界面上快速顯隱兩次, 這樣就知道它在哪里了:
flicker xyzLabel
-
mwarning
模擬一個內存警告.
-
pactions
打印某個 control 的所有 action 和 target:
pactions xxxButton
-
pbcopy
打印指定對象, 且將輸出拷貝到剪貼板.
-
pbundlepath
打印應用程序的 bundle 目錄的地址.
-
pclass
打印某目標對象的類型繼承鏈:
(lldb) pclass self HomeBoard_iPhone | BaseRootBoard_iPhone | | BeeUIBoard | | | UIViewController | | | | UIResponder | | | | | NSObject
-
pdata
將某 NSData 中的內容以字符串的形式打印出來, 并且可以指定編碼, 默認是 utf-8:
pdata valueData
-
pdocspath
打印本應用的 Documents 目錄路徑.
-
pinternals
打印某對象的內部情況:
(lldb) pinternals valueData (_NSInlineData) $705 = { NSData = { NSObject = { isa = _NSInlineData } } _length = 9 }
-
pjson
以 JSON 格式打印字典或數組的內容:
(lldb) pjson @[1, 2, 3] [ 1, 2, 3 ]
-
pmethods
打印指定類或對象的所有類方法和對象方法.
-
pproperties
打印某對象或類型的所有屬性
-
vs
在當前視圖樹中搜索指定的視圖.