你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Tips:LLDB 基礎

Tips:LLDB 基礎

編輯:IOS開發基礎

t0100784099e0b83abd500.jpg

原文鏈接:6 Basic LLDB tips,原作者:Michal Wojtysiak,譯者:WAMaker(博客)

長話短說

在開發了幾年的iOS應用後,我對LLDB調試器的使用已經趨於最小:

(lldb)?po?data.pressure
98.65814208984375
(lldb)?po?samples.count
28
(lldb)?po?(x?+?y)*z
96

以上幾乎就是我使用的全部了。這並不值得驕傲。設置一個斷點,然後使用po命令。我知道po是'print object(輸出對象)'的意思,並且能用它來計算表達式。我需要更先進的工具來幫我應對不得不處理的復雜難題。

讓我們開始吧!

探究LLDB工作區

你總能夠在調試的時候輸入一個help命令:

(lldb)?help
Debugger?commands:
apropos???????????--?Find?a?list?of?debugger?commands?related?to?a?particular
word/subject.
...

你可能已經注意到就連apropos命令也是分外有趣的。讓我們使用LLDB的'聰明才智'來為我們推薦設置斷點的命令:

(lldb)?apropos?breakpoints
The?following?built-in?commands?may?relate?to?'breakpoints':
breakpoint????????????????--?A?set?of?commands?for?operating?on?breakpoints.
Also?see?_regexp-break.
breakpoint?clear??????????--?Clears?a?breakpoint?or?set?of?breakpoints?in?the
executable.
breakpoint?command?add????--?Add?a?set?of?commands?to?a?breakpoint,?to?be
executed?whenever?the?breakpoint?is?hit.??If?no
breakpoint?is?specified,?adds?the?commands?to
the?last?created?breakpoint.
...

LLDB還不賴嘛,還不賴。

LLDB必須要配合Xcode才能使用嗎?

當然。。。不是咯!我們並不需要依賴Xcode的調試區域(Debug Area)。我們能夠很輕易地從終端啟動LLDB。在此之前我們需要先搭建一個app去模擬相應的環境。在終端切換到你的項目路徑下輸入:

michal$?xcodebuild?-sdk?iphonesimulator9.2

在路徑:project_dir/build/Release-iphonesimulator/appname.app 下你就有了一個為調試而生的 *.app 文件。

現在,在不切換路徑的前提下你就能夠從終端啟動LLDB了:

michal$?xcrun?lldb
(lldb)

接著你需要像這樣創建一個測試目標:

(lldb)?target?create?./build/Release-iphonesimulator/appname.app
Current?executable?set?to?'./build/Release-iphonesimulator/appname.app'?(x86_64).

最後,啟動一個進程來開始我們的任務:

(lldb)?process?launch

這樣我們就和在Xcode中運行調試模式一樣了。

標准的Ctrl+C命令無法讓你退出LLDB,請使用quit命令:

(lldb)?quit
michal$

為什麼我需要在命令行使用LLDB?

如果你浏覽了(lldb) apropos breakpoint給出的結果,你肯定意識到了很多可能的原因。想要獲取更多針對斷點的幫助請輸入:

(lldb) help breakpoint

雖然Xcode有了一些用UI實現的調試特性:

  • Breakpoint Navigator (Command + 7)

  • Debug Navigator (Command + 6)

  • Debug Area (Command + Shift + Y)

  • Debug menu item

但在命令行中使用LLDB我們能獲取到更多更詳細的調試信息。

以斷點列表為例,在Debug Navigator中你會看到一個方法名,斷點處代碼所處行數,以及這個斷點是否有效:

BreakpointNavigator.png

雖然這樣看起來不錯,但通過LLDB命令breakpoint list能帶給你更多像執行次數或者地址一類的信息:

(lldb)?breakpoint?list
Current?breakpoints:
1:?file?=?'/Users/michal/Developer/Swift/Altimeter/Altimeter/ViewController.swift',?line?=?72,?locations?=?1,?resolved?=?1,?hit?count?=?1
1.1:?where?=?Altimeter`Altimeter.ViewController.startAltimeter?(Altimeter.ViewController)()?->?()?+?712?at?ViewController.swift:72,?address?=?0x00000001000e86b4,?resolved,?hit?count?=?1
...

通過添加像-v這樣的參數我們能獲取到更詳盡的輸出結果。要想了解所有的參數請輸入:

(lldb)?help?breakpoint?list

如果你想著能得到更多鍛煉,嘗試與breakpoint enable和breakpoint disable這樣的子命令共舞:

(lldb)?breakpoint?disable?//disables?all
(lldb)?breakpoint?disable?1.1?//disables?just?one?place
(lldb)?breakpoint?enable?//enables?all
...

設置簡單的和復雜的斷點

當使用Xcode時我們習慣在一個指定的文件的某一行設置斷點。命令行的LLDB不僅僅提供了這一種類型的斷點。讓我們來看一下下面這些例子,每一個例子展示了簡短而完整的版本:

  • 在一個文件的指定行設置斷點:

(lldb)?breakpoint?set?--file?ViewController.swift?--line?26
(lldb)?breakpoint?set?-f?ViewController.swift?-l?26
  • 在每個方法處設立斷點:

(lldb)?breakpoint?set?--selector?viewDidLoad
(lldb)?breakpoint?set?-S?viewDidLoad
  • 在每一個方法名設立斷點:

(lldb)?breakpoint?set?--name?stringToDate
(lldb)?breakpoint?set?-n?stringToDate
  • 設置匹配regexp方法名字的斷點:

(lldb)?breakpoint?set?--source-pattern-regexp?'Manager'?-file?ViewController.swift
(lldb)?breakpoint?set?-p?'Manager'?-f?ViewController.swift
  • 設置一個在第一次停止後就刪除的斷點:

(lldb)?breakpoint?set??--one-shot
(lldb)?breakpoint?set??-o

當然,這只是命令行所能做到的一小部分。想了解設置斷點的更多信息請輸入:

(lldb)?help?breakpoint?set

調試會話的導航

在Xcode裡調試的時候你一定能熟練運用'continue','step in','step out'和'step over'按鈕。對於LLDB命令來說要怎麼完成同樣的事呢?

(lldb)?thread?continue
(lldb)?thread?step-in
(lldb)?thread?step-out
(lldb)?thread?step-over

像往常一樣,通過thread的help命令你會發現更多的參數。

其它被選出的小技巧

我們還能通過命令行完成些什麼操作呢?讓我們一探究竟吧。

類型格式化

你可能會對調試區(Debug Area)中的'View Value As比較熟悉。它的作用是快速改變一個給定值的展示樣式:

ViewValueAs.jpg

如果你想一直把布爾變量用二進制格式顯示呢?看看用LLDB是如何完成這個任務的:

(lldb)?print?isAltimeterRunning.value
(Builtin.Int1)?$R2?=?false
(lldb)?type?format?add?--format?decimal?Builtin.Int1
(lldb)?print?isAltimeterRunning.value
(Builtin.Int1)?$R3?=?0

我們能額外了解到的是Swift的bool是Builtin.Int1類型。不得不說LLDB的類型格式化對於Swift的類型來說並不是很友好。類型名字必須完全匹配。類型格式化對於老的Cocoa對象和Obj-C/C支持的更好。

類型摘要

從我們關於輸出調試法(print debugging)的博文中我們知道了如何使用CustomStringConvertible去獲得有意義的調試信息。這同樣能應用在LLDB上。為了讓這看起來簡單,我們用Int來舉例子。這就是我們能在調試區經常看到的樣子:

IntInDebugArea.jpg

讓我們添加一個類型摘要,把它顯示到控制台上:

(lldb)?type?summary?add?-s?"natural=${var.value}?octal=${var.value%o}?hex=${var.value%x}"?Int
(lldb)?frame?variable?integer
(Int)?integer?=?4095?natural=4095?octal=07777?hex=0x00000fff

當我們重新查看調試區時就能看到我們自定義的信息:

CustomIntInDebugArea.jpg

多行表達式模式

當使用LLDB調試時我們手握功能強大的編譯器。開發它的潛力最好的方式是使用expression命令進入多行模式。在LLDB中輸入expression,回車:

(lldb)?expression
Enter?expressions,?then?terminate?with?an?empty?line?to?evaluate:
1?struct?compass{var?direction?=?"N";?var?angle?=?16.5}
2?var?c?=?compass()
3?print(c)
4
(compass?#1)(direction:?"N",?angle:?16.5)
(lldb)

線程返回棧

當調試線程時我們經常使用Debug Navigator (Command + 5):

DebugNavigator.jpg

通過一行命令就能達到同樣的效果,甚至得到更詳盡的線程返回棧的打印:

(lldb)?thread?backtrace?all

錯誤展開

一個額外需要記住的東西是在LLDB中執行的表達式會影響我們執行過的代碼。如果你在LLDB中修改了變量的值,當繼續執行的時候這個變量的值仍是被修改過的。

更有甚者一些表達式可能會引起程序崩潰。通常來說如果造成了崩潰程序的狀態就被清空了。然而有時候我們想要看一下這些狀態。

想象一下減少一個var integer:UInt = 10變量:

(lldb)?expression?while?integer?<=?0?{integer--}
error:?Execution?was?interrupted,?reason:?EXC_BAD_INSTRUCTION?(code=EXC_I386_INVOP,?subcode=0x0).
The?process?has?been?returned?to?the?state?before?expression?evaluation.

我們獲得了一個錯誤但我們依然能夠繼續執行。

要改變這個問題我們給表達式設置一個--unwind-on-error=0參數:

(lldb)?expression?--unwind-on-error=0?--?while?integer?<=?0?{integer--}
error:?Execution?was?interrupted,?reason:?EXC_BAD_INSTRUCTION?(code=EXC_I386_INVOP,?subcode=0x0).
The?process?has?been?left?at?the?point?where?it?was?interrupted,?use?"thread?return?-x"?to?return?to?the?state?before?expression?evaluation.

這樣我們就得到了真正導致的崩潰原因。

查找未格式化的變量

在類型摘要例子中我們盡我們所能得到了一個不錯的格式化輸出,使用了frame variable命令來顯示。而有些時候我們想做的卻恰恰相反-原始的值。讓我們看下這裡面的區別:

(lldb)?frame?variable?self.isAltimeterRunning
(Bool)?self.isAltimeterRunning?=?false
(lldb)?frame?variable?--raw?self.isAltimeterRunning
(Swift.Bool)?self.isAltimeterRunning?=?{
????value?=?0
}

這也解釋了很多Swift的結構體類型是如何設置的。它們有一個value變量。挖掘更深入之後我們能看到Bool類型背後的真實類型:

(lldb)?frame?variable?self.isAltimeterRunning.value
(Builtin.Int1)?self.isAltimeterRunning.value?=?0

它是Int1類型-一個一比特的整型。如果你想要知道更多關於解包Swfit Bool類型和其他類型請看SwiftUnboxed。--raw參數同樣適用於窺探Swift的optional和nested optional。

就是這樣!

希望你注意到了在命令行中使用LLDB命令的優點。這篇文章只是摸索了其中的冰山一角。你能在LLDB Documentation閱讀有關lldb的文章。同樣也去看下他們的教程。

蘋果有一個quick start讓開發者入門。同樣有很多WWDC的視頻教程:

WWDC 2012: Debugging with LLDB

WWDC 2014: Advanced Swift debugging in LLDB

WWDC 2014: Introduction to LLDB and the Swift REPL

WWDC 2015: What’s new in LLDB

最後值得一提的是,objc.io對LLDB做了很好的總結。

2016年1月21日更新

我是否需要一遍遍輸入所有的命令?

如果你對有效的在命令行使用LLDB依舊沒什麼信心,你可能會想看一下.lldbinit。這是一個在你home路徑下的文件。它包含了一系列LLDB命令。要創建你自己的文件在終端中執行以下命令:

michal$?cd?~/
michal$?touch?.lldbinit
michal$?chmod?+x?.lldbinit

然後你就能用一系列每次LLDB啟動時候要運行的命令來填充.lldbinit文件。例如:

breakpoint?set?-n?malloc?-N?memory
breakpoint?set?-n?free?-N?memory
breakpoint?disable?memory

以上命令在malloc和free方法出設置斷點並給他們一個通用的名字'memory'。這些斷點是被禁止的,所以它們並不會干擾到你,但是如果你想調試內存相關的東西時它們盡在掌控。要讓這些斷點生效只需要修改文件最後一行或者在運行時輸入下面這行命令:

(lldb)?breakpoint?enable?memory

請觀看WWDC 2015: What’s new in LLDB視頻獲得更多細節。

更多有用的命令

在上面提到的的WWDC視頻中,你能發現很多很有用的命令,例如:

  • type lookup命令:

(lldb)?type?lookup?CLLocation
@available(iOS?2.0,?*)
@objc?class?CLLocation?:?ObjectiveC.NSObject,?NSCopying,?NSSecureCoding?{
@objc?deinit??{
}
@objc?init(latitude:?CoreLocation.CLLocationDegrees,?longitude:?CoreLocation.CLLocationDegrees)
...
  • 給Objc/Swift語言設置異常斷點:

(lldb)?breakpoint?set?-E?objc
(lldb)?breakpoint?set?-E?swift
  • 給某一個指定類型設置斷點:

(lldb)?breakpoint?set?-E?-O?EnumError
  • 最後給極少數熱愛者一些福利。一個memory命令:

(lldb)?po?locationMgr

(lldb)?memory?read?0x7af76280
0x7af76280:?40?4a?17?00?60?63?f7?7a?00?00?00?00?00?00?00?00??@J..`c.z........
0x7af76290:?33?75?af?37?20?76?af?47?03?00?00?00?00?00?00?00??3u.7?v.G........

它的真正的威力當你查看C數組或char* 字符串才能體現。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved