標籤:
breakpoints、lldb 和 chisel 的詳解
Breakpoints
BreakPoint分類
breakpoint也是有分類的,我這裡的文章內大致按使用的方式分為了
- Normal Breakpoint,
- Exception Breakpoint,
- OpenGL ES Error breakpoint,
- Symbolic Breakpoint,
- Test Failure Breakpoint,
- WatchPoints。
可以按具體的情景使用不同類型的breakpoint,解決問題為根本。
Normal Breakpoint
添加普通斷點就不多說了,在原始碼的右側點擊一下即可。或者,使用快速鍵:command + \ 來添加和刪除。這兩種方式添加的breakpoints在Xcode上面是可以通過UI看到的。
還有可以通過下面兩個LLDB命令直接在運行時添加斷點,但是這種方式需要注意的是一方面無法通過UI直接看到斷點,另外一方面只存在於本次運行,下一次啟動模擬器重新啟動並執行時候,這些斷點就不生效了。
如,通過“br li”命令列印所有的breakpoint,可以看到一共有3個breakpoint,第一個是通過Xcode的UI添加的,後面兩個分別是通過下面兩個命令添加的:
“breakpoint set -f XXX.m -l XX” 和 “b XXX.m:XX”。
Exception Breakpoint
可以通過中Xcode的UI添加Exception Breakpoint。有時候,比如數組越界或者設定一個Null 物件等問題,都會拋出一個異常,但是這種類型的錯誤非常難以定位,這個時候就可以使用Exception Breakpoint來進行調試,在異常發生時可以捕捉到並停止程式的執行。OC中的異常是一個常被忽略的地方,但實際上系統架構內這個使用非常廣泛,大部分這種錯誤資訊,系統架構都會以異常的形式throw出來,所以善用這種breakpoint的話,我們能大大減少尋找錯誤的時間。
例如,當我們添加如下Exception Breakpoint之後(bt 命令後文中會講解,這個命令的作用是在斷點觸發時,列印回調棧資訊):
類似下面這樣的數組越界的問題,我們可以很容易就定位到問題所在,不用再毫無頭緒找來找去了:
當斷點暫停執行時,我們可以通過Xcode的UI中查看調用棧資訊:
或者查看bt命令列印的調用棧資訊:
還有類似如下的錯誤可以通過這種斷點很容易定位到:
,不過這種問題,可以通過使用setValue:forKey:代替來避免。
OpenGL ES Error Breakpoint
同中,在Xcode的breakpoint navigator的下部添加按鈕,選擇”Add OpenGL ES Error Breakpoint”即可。這個breakpoint主要是用來在OpenGL ES發生錯誤時停止程式的運行。
Symbolic Breakpoint
通過Xcode的UI添加symbolic breakpoint的方式同exception breakpoint,彈出框如下:
Symbolic breakpoints 在某個特定的函數或者方法開始執行的時候,暫停程式的執行,通過這種方式添加斷點,我們就不需要知道在源檔案中添加,也不需要知道斷點設定在檔案的第幾行。
中,最主要的設定是Symbol的內容,可以有如下幾種:
- 1. A method name,方法名稱,例如 pathsMatchingExtensions: 這樣的方法名稱,會對所有類的這個方法都起作用。
- 2. A method of a particular class. 特定類的某個方法。例如 ,[SKLine drawHandlesInView],或者 people::Person::name()
- 3. A function name。函數名稱。例如 ,_objc_msgForward 這樣C函數。
另外,也可以通過命令列的方式添加 Symbolic breakpoints。對C函數添加斷點:
對OC的方法添加斷點:
常用的這個類型的斷點有,objc_exception_throw可以用來代替 Exception Breakpoint,還有一個-[NSObject doesNotRecognizeSelector:] 也比較常用,用於檢測方法調用失敗。
Test Failure Breakpoint
通過Xcode的UI添加方法同上。這個類型的break point 會在 test assertion 失敗的時候暫停程式的執行。
Watchpoints
Watuchpoints是一個用來監聽變數的值的變化或者記憶體位址的變化的工具,發生變化時會在debugger中觸發一個暫停。對於那些不知道如何準確跟蹤的狀態問題,可以利用這個工具來解決。要設定watchpoint的話,在程式運行到stack frame包含有你想觀察的變數時,讓debugger暫停運行,這個時候變數在當前stack frame的scope內,這個時候才能對該變數設定watchpoint。
你可以在Xcode的GUI中設定watchpoint,在xcode的 Variables View中,把你想觀察的變數保留出來,然後右鍵設定“Watch XXX”。例如,觀察self的title變數,點擊 Watch “_button1ClickCount” 即可。
命令列
或者也可以通過命令列來設定watchpoint:watch set variable _button1ClickCount,詳細命令可以參考:http://lldb.llvm.org/lldb-gdb.html,有好幾種命令可以達到同樣的效果。
上面是對變數進行觀察,實際上我們可以對任意記憶體位址進行觀察,命令如下:watchpoint set expression — 0x123456,參考:http://stackoverflow.com/questions/21063995/watch-points-on-memory-address
需要注意的是,watchpoint是分類型的,包括read,write或者read_write類型,這個非常容易理解,在讀,寫或者讀寫變數或記憶體的時候,watchpoint是否被觸發。read,write或read_write跟著-w參數後面表示類型。另外,命令列中,watchpoint還有一些簡寫,set簡寫為s,watch簡寫為wa,variable簡寫為v。
下面的樣本是來自 http://www.dreamingwish.com/article/lldb-usage-a.html 網站的幾個命令:
第一個命令是監聽_abc4變數的記憶體位址write的變化,第二個是監聽_abc4變數read的變化,第三個是監聽_abc3變數read_write的變化。
需要注意的是,通過Xcode的GUI添加的watchpoint為預設類型,即write類型,如果想要添加讀寫都watch的watchpoint,則只能通過命令列工具進行添加了。
使用watchpoint modify -c ‘(XXX==XX)’,則修改watchpoint之後在某個值的時候才會監聽。
編輯選項
BreakPoint Condition
當我們通過Xcode對breakpoint進行編輯時,可以發現normal breakpoint和symbolic breakpoint都有一個”Condition”輸入選項,這個的作用很容易理解,只有在設定的condition運算式為YES的情況下這些斷點才會起作用。
例如,中的breakpoint在判斷字串相等的時候才會停止運行:
可以注意到這裡使用stringWithUTF8Stirng:方法,原因在於lldb的expression parser有一個bug,不相容非ASCII字元,需要處理一下才行,否則會報錯“An Objective-C constant string‘s string initializer is not an array”,參考:http://stackoverflow.com/questions/17192505/error-in-breakpoint-condition
更加簡單一些的例子就不說了,比如 i == 99之類的簡單比較,只要運算式的結果為BOOL類型即可。
Breakpoint Actions
可以看到上面的每種breakpoint編輯選項中基本上都有“Add Action”選項,當breakpoint被觸發時,都首先會執行我們設定的這些action,然後我們才能得到控制權,即Xcode上面才會顯示程式停止執行的UI。這個Action通過例子比較好理解,我們通過上面那個setObject:forKey:的異常來說明。代碼如下:
設定Breakpoint:
可以看到中,我們一共設定了3個action。第一個action,用來列印exception的詳細資料,用法參考:http://stackoverflow.com/questions/17238673/xcode-exception-breakpoint-doesnt-print-details-of-the-exception-being-thrown。
第二個action,我們使用shell命令“say”,讓電腦發聲,把一段文字讀出來。
第三個action,我們使用“bt”命令來列印調用棧資訊
設定完成之後,當異常發生時,我們會聽到電腦發聲念中的英文,然後在log中可以看到如下資訊,第一行是Exception的描述資訊,下面是呼叫堆疊:
Continuing after Evaluation
看一下breakpoint的編輯彈窗,我們可以發現有一個 “Automatically continue after evaluation actions” checkbox選項。當我們勾選這個checkbox之後,debugger會執行breakpoint中添加的所有的actions,然後繼續執行程式。對於我們來說,除了觸發一大堆command並且執行時間很長的情況之外,程式會很快跳過這個breakpoint,所以我們可能根本不會注意到這個breakpoint的存在。所以,這個選項的功能相當於在執行的最後一個action之後,直接輸入continue命令繼續執行。
有了這個很強大的功能,我們可以直接通過breakpoints來單獨對我們的程式進行修改。在某行代碼時停止執行,使用”expression”命令來直接修改程式的某個變數設定直接修改UI,然後繼續執行。expression / call 配合這個選項的時候,會非常強大,可以很方便實現很多很強大的功能。
例如,我們實現一個如下的功能,把tableview的第一個cell的selectBackgroundView的背景色改為紅色:
action的內容為“expression [[cell selectedBackgroundView] setBackgroundColor:[UIColor redColor]]”,這裡的運算式先不用關心,我們後面LLDB章節會講到,修改之後,當我們點擊cell的時候,cell的背景就會如一樣變紅:
使用這種方式,我們在不需要修改一行代碼的情況下,只需要通過修改breakpoint,就可以實現對UI的各種調試效果。
iOS開發——測試篇&breakpoints、lldb 和 chisel 的詳解