LLDB是Xcode默认的调试器,它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。平时用Xcode运行程序,实际走的都是LLDB。熟练使用LLDB,可以让你debug事半功倍。

LLDB 命令还是不少的,但有些都集成在 Xcode 的图形化界面中,没必要再去手动操作,这里主要说一下使用起来更加高效的一些命令。

Features

help

和大多数命令一样,help 命令会显示出所有的命令列表,对于相关的操作可以直接查看。 若想查看某一条命令的话,直接在 help 后面加上对应的命令名称。如:

help print

p & call & po

先说 p 和 call, 二者都是 expression – 的别名, p 为 print 的简写,同时可以写为 pri,打印某个东西,可以i是变量和表达式; call 为调用某个方法,输出变量也是可以的。

po 一般用于打印对象,是 expression -O – 的别名。

p 和 po 的区别在于使用 po 只会输出对应的值,而 p 则会返回值的类型以及命令结果的引用名。如:

(lldb) p self.model.number
(float) $ =
(lldb) p self.model.name
(NSString *) $ = nil
(lldb) po self.model.number (lldb) po self.model.words
Hello

expression

expression 命令的作用是执行一个表达式,并将表达式返回的结果输出。expression的完整语法是这样的:

expression (cmd-options) -- (expr)

说明下参数:

(cmd-options):命令选项,一般情况下使用默认的即可,不需要特别标明。
– –: 命令选项结束符,表示所有的命令选项已经设置完毕,如果没有命令选项,–可以省略。
(expr): 要执行的表达式

说 expression 是LLDB里面最重要的命令都不为过。因为他能实现2个功能。

  • 执行某个表达式。 我们在代码运行过程中,可以通过执行某个表达式来动态改变程序运行的轨迹。 假如我们在运行过程中,突然想把 self.view 颜色改成红色,看看效果。我们不必写下代码,重新run,只需暂停程序,用expression改变颜色,再刷新一下界面,就能看到效果
// 改变颜色
(lldb) expression -- self.view.backgroundColor = [UIColor redColor]
// 刷新界面
(lldb) expression -- (void)[CATransaction flush]
  • 将返回值输出。 也就是说我们可以通过expression来打印东西。 假如我们想打印 self.view :
(lldb) expression -- self.view
(UIView *) $ = 0x00007fe322c18a10

thread

BACKTRACE & BT

此命令一般用于将线程的堆栈打印出来,一般在程序出现 crash的时候调用。如;

(lldb) thread backtrace
* thread #: tid = 0xdd42, 0x000000010afb380b libobjc.A.dylib`objc_msgSend + , queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #: 0x000000010afb380b libobjc.A.dylib`objc_msgSend +
* frame #: 0x000000010aa9f75e TLLDB`-[ViewController viewDidLoad](self=0x00007fa270e1f440, _cmd="viewDidLoad") + at ViewController.m:
frame #: 0x000000010ba67f98 UIKit`-[UIViewController loadViewIfRequired] +
frame #: 0x000000010ba682e7 UIKit`-[UIViewController view] +
frame #: 0x000000010b93eab0 UIKit`-[UIWindow addRootViewControllerViewIfPossible] +
frame #: 0x000000010b93f199 UIKit`-[UIWindow _setHidden:forced:] +
frame #: 0x000000010b950c2e UIKit`-[UIWindow makeKeyAndVisible] +
bt 为 thread backtrace 的别名,直接使用 bt 和使用上面那一长串是一个效果。

THREAD RETURN

Debug的时候,也许会因为各种原因,我们不想让代码执行某个方法,或者要直接返回一个想要的值。这时候就该thread return上场了。

thread return [expr]

thread return可以接受一个表达式,调用命令之后直接从当前的堆栈中返回表达式的值。

e.g: 我们有一个 someMethod 方法,默认情况下是返回YES。我们想要让他返回NO

我们只需在方法的开始位置加一个断点,当程序中断的时候,输入命令即可:

(lldb) thread return NO

效果相当于在断点位置直接调用 return NO;,不会执行断点后面的代码.

target

对于target这个命令,我们用得最多的可能就是 target modules lookup。由于 LLDB 给 target modules 取了个别名 image,所以这个命令我们又可以写成 image lookup

IMAGE LOOKUP –ADDRESS

当我们有一个地址,想查找这个地址具体对应的文件位置,可以使用 image lookup –address,简写为 image lookup -a。 e.g: 当我们发生一个crash

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 1 beyond bounds for empty NSArray'
*** First throw call stack:
(
CoreFoundation 0x000000010accde65 __exceptionPreprocess +
libobjc.A.dylib 0x000000010a746deb objc_exception_throw +
CoreFoundation 0x000000010ac7c395 -[__NSArray0 objectAtIndex:] +
TLLDB 0x000000010a1c3e36 -[ViewController viewDidLoad] +
UIKit 0x000000010b210f98 -[UIViewController loadViewIfRequired] +
UIKit 0x000000010b2112e7 -[UIViewController view] +

我们可以看到是由于-[__NSArray0 objectAtIndex:]:超出边界而导致的crash,但是objectAtIndex:的代码到底在哪儿呢?

(lldb) image lookup -a 0x000000010a1c3e36
Address: TLLDB[0x0000000100000e36] (TLLDB.__TEXT.__text + )
Summary: TLLDB`-[ViewController viewDidLoad] + at ViewController.m:

根据0x000000010a1c3e36 -[ViewController viewDidLoad]里面的地址,使用image lookup –address查找,我们可以看到代码位置在ViewController.m里面的32行。

IMAGE LOOKUP –NAME

当我们想查找一个方法或者符号的信息,比如所在文件位置等。我们可以使用 image lookup –name ,简写为 image lookup -n

e.g: 刚刚遇到的真问题,某个第三方SDK用了一个我们项目里原有的第三方库,库里面对 NSDictionary 添加了 category 。也就是有2个 class 对 NSDictionary 添加了名字相同的 category,项目中调用自己的 category 的地方实际走到了第三方SDK里面去了。最大的问题是,这2个同名 category 方法行为并不一致,导致出现 bug

现在问题来了,怎么寻找到底是哪个第三方SDK?方法完全包在.a里面。

其实只需使用image lookup -n即可:

(lldb) image lookup -n dictionaryWithXMLString:
matches found in /Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo:
Address: BaiduIphoneVideo[0x00533a7c] (BaiduIphoneVideo.__TEXT.__text + )
Summary: BaiduIphoneVideo`+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:] at XmlDictionary.m
Module: file = "/Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo", arch = "armv7"
CompileUnit: id = {0x00000000}, file = "/Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m", language = "Objective-C"
Function: id = {0x23500000756}, name = "+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:]", range = [0x005a6a7c-0x005a6b02)
FuncType: id = {0x23500000756}, decl = XmlDictionary.m:, clang_type = "NSDictionary *(NSString *)"
Blocks: id = {0x23500000756}, range = [0x005a6a7c-0x005a6b02)
LineEntry: [0x005a6a7c-0x005a6a98): /Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m
Symbol: id = {0x0000f2d5}, range = [0x005a6a7c-0x005a6b04), name="+[NSDictionary(SAPIXmlDictionary) dictionaryWithXMLString:]"
Variable: id = {0x23500000771}, name = "self", type = "Class", location = [sp+], decl =
Variable: id = {0x2350000077e}, name = "_cmd", type = "SEL", location = [sp+], decl =
Variable: id = {0x2350000078b}, name = "string", type = "NSString *", location = [sp+], decl = XmlDictionary.m:
Variable: id = {0x23500000799}, name = "data", type = "NSData *", location = [sp+], decl = XmlDictionary.m:
Address: BaiduIphoneVideo[0x012ee160] (BaiduIphoneVideo.__TEXT.__text + )
Summary: BaiduIphoneVideo`+[NSDictionary(XMLDictionary) dictionaryWithXMLString:] at XMLDictionary.m
Module: file = "/Users/jiangliancheng/Library/Developer/Xcode/DerivedData/VideoIphone-aivsnqmlwjhxapdlvmdmrubbdxpq/Build/Products/Debug-iphoneos/BaiduIphoneVideo.app/BaiduIphoneVideo", arch = "armv7"
CompileUnit: id = {0x00000000}, file = "/Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m", language = "Objective-C"
Function: id = {0x79900000b02}, name = "+[NSDictionary(XMLDictionary) dictionaryWithXMLString:]", range = [0x01361160-0x0136119a)
FuncType: id = {0x79900000b02}, decl = XMLDictionary.m:, clang_type = "NSDictionary *(NSString *)"
Blocks: id = {0x79900000b02}, range = [0x01361160-0x0136119a)
LineEntry: [0x01361160-0x01361164): /Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m
Symbol: id = {0x0003a1e9}, range = [0x01361160-0x0136119c), name="+[NSDictionary(XMLDictionary) dictionaryWithXMLString:]"
Variable: id = {0x79900000b1e}, name = "self", type = "Class", location = r0, decl =
Variable: id = {0x79900000b2c}, name = "_cmd", type = "SEL", location = r1, decl =
Variable: id = {0x79900000b3a}, name = "string", type = "NSString *", location = r2, decl = XMLDictionary.m:
Variable: id = {0x79900000b4a}, name = "data", type = "NSData *", location = r2, decl = XMLDictionary.m:

东西有点多,我们只需关注里面的file这一行:

CompileUnit: id = {0x00000000}, file = "/Users/jiangliancheng/Development/Work/iOS_ShareLib/SharedLib/Srvcs/BDPassport4iOS/BDPassport4iOS/SAPI/Extensive/ThirdParty/XMLDictionary/XmlDictionary.m", language = "Objective-C"
CompileUnit: id = {0x00000000}, file = "/Users/wingle/Workspace/qqlive4iphone/iphone_4.0_fabu_20150601/Common_Proj/mobileTAD/VIDEO/Library/Third Party/XMLDictionary/XMLDictionary.m", language = "Objective-C"

可以清晰的看到,LLDB给我们找出来了这个方法的位置。 当然这个命令也可以找到方法的其他相关信息,比如参数等.

IMAGE LOOKUP –TYPE

当我们想查看一个类型的时候,可以使用 image lookup –type,简写为image lookup -t:

e.g: 我们来看看Model的类型:

(lldb) image lookup -t Model
Best match found in /Users/jiangliancheng/Library/Developer/Xcode/DerivedData/TLLDB-beqoowskwzbttrejseahdoaivpgq/Build/Products/Debug-iphonesimulator/TLLDB.app/TLLDB:
id = {0x30000002f}, name = "Model", byte-size = , decl = Modek.h:, clang_type = "@interface Model : NSObject{
NSString * _bb;
NSString * _cc;
NSString * _name;
}
@property ( getter = name,setter = setName:,readwrite,nonatomic ) NSString * name;
@end
"

可以看到,LLDB把Model这个class的所有属性和成员变量都打印了出来,当我们想了解某个类的时候,直接使用 image lookup -t 即可。

其他

可以直接使用LLDB打开模拟器位置:

(lldb) po NSHomeDirectory()
/Users/mfw/Library/Developer/CoreSimulator/Devices/EAFE74A5-4C53-42CE-8B40-141380D73A6D/data/Containers/Data/Application/B4C48D8B-BD8B--B9D7-15FEC3CA8662 (lldb) platform shell open /Users/mfw/Library/Developer/CoreSimulator/Devices/EAFE74A5-4C53-42CE-8B40-141380D73A6D/data/Containers/Data/Application/B4C48D8B-BD8B--B9D7-15FEC3CA8662

常用的Debug快捷键

debug的时候,使用快捷键是一个很好的习惯,我简单列举了几个debug的快捷键

功能 命令
暂停/继续 cmd + ctrl + Y
断点失效/生效 cmd + Y
控制台显示/隐藏 cmd + shift + Y
光标切换到控制台 cmd + shift + C
清空控制台 cmd + K
step over F6
step into F7
step out F8
工程导航器 Command+1
显示/隐藏导航器面板 Command+0
显示/隐藏实用工具面板 Command+Option+0
打开Assistant Editor 项目导航器中选中文件执行Option+左键点击操作
展示方法列表 Control+6(键入方法/变量名+Enter跳转
快速打开 Command + Shift + O (字母O)
文档和参考 Command + Shift + 0 (数字0)
快速帮助 在类或者方法名上执行Option + Left-click操作
展示当前你在工程导航器中打开的文件 Command + Shift + J
迷你窗口,可任意选择位置 Command + Option + Shift + Left-click

End

这是我比较常用的一些命令,不全但是有效,像那些 breakpoint 的功能,若不是使用 lldb调试可执行文件的话,直接使用 Xcode 的功能效果会更加显著一些。若想使用一些高级命令,可结合 python 脚本使用。

参考:

与调试器共舞 - LLDB 的华尔兹

熟练使用 LLDB,让你调试事半功倍

LLDB使用篇(上)

使用LLDB调试程序

Xcode - LLDB调试技巧的更多相关文章

  1. iOS开发之Xcode常用调试技巧总结

    转载自:iOS开发之Xcode常用调试技巧总结 最近在面试,面试过程中问到了一些Xcode常用的调试技巧问题.平常开发过程中用的还挺顺手的,但你要突然让我说,确实一脸懵逼.Debug的技巧很多,比如最 ...

  2. xcode的调试技巧

    转自:http://www.cnblogs.com/daiweilai/p/4421340.html#biyouji 目录 前言逼优鸡知己知彼 百战不殆抽刀断Bug 普通操作 全局断点(Global ...

  3. Xcode 常用调试技巧总结

    NSLog,po命令和普通断点调试相信每个iOS开发者都会,这里就不作介绍了. 一.Memory Graph Xcode8新增:Memory Graph解决闭包引用循环问题 有很多叹号说明就有问题了. ...

  4. 5个Xcode开发调试技巧

    转自Joywii的博客,原文:Four Tips for Debugging in XCode Like a Bro    1.Enable NSZombie Objects(开启僵尸对象) Enab ...

  5. iOS 5个Xcode开发调试技巧

    转自Joywii的博客,原文:Four Tips for Debugging in XCode Like a Bro    1.Enable NSZombie Objects(开启僵尸对象) Enab ...

  6. (转)5个Xcode开发调试技巧

    1.Enable NSZombie Objects(开启僵尸对象) Enable NSZombie Objects可能是整个Xcode开发环境中最有用的调试技巧.这个技巧非常非常容易追踪到重复释放的问 ...

  7. ios逆向过程中lldb调试技巧-po篇

    假如你准备在模拟器里面运行这个,你可以在"(lldb)"提示的后面输入下面的: (lldb) po $eax LLDB在xcode4.3或者之后的版本里面是默认的调试器.假如你正在 ...

  8. ios逆向过程中lldb调试技巧

    在ios逆向过程中,善于运用lldb,会给逆向带来很大的方便 一般的命令: 1.image list -o -f  看看各个模块在内存中的基址 2.register read r0  读取寄存器r0的 ...

  9. 【工作总结】LLDB调试技巧 - 篇一

    备忘命令 : 命令“l”可以查看程序当前运行的位置 (lldb) l } - (void)rightBarButtonAction { 命令“bt”也能查看程序运行的调用栈 (lldb) bt * t ...

随机推荐

  1. ESPCN处理彩色图像代码

    原来仅处理了Y通道,输出的灰度图像. Super-Resolution/ESPCN at master · wangxuewen99/Super-Resolution · GitHub https:/ ...

  2. ITIL之“变更管理”

    首先要说明的是ITIL的变更是指“上线系统的变更”,而不是指系统建设的变更. ITIL的变更的流程如下: 整个变更管理在实际操作中有几个注意点: 1. 现存的企业中,变更咨询委员会(CAB)可能只有信 ...

  3. PHP 数组current和next用法

    1.current   当前数组 <?php $transport = array('foot', 'bike', 'car', 'plane'); $mode = current($trans ...

  4. 申请LINE 帐号的所有方法

    如果你打算用LINE 交朋友或是做行销,你就必须先要拥有一个甚至许多个LINE 帐号.在本篇文章中,将分享给你目前申请LINE 帐号的所有可能方法. 目前LINE 公司允许大家使用以下二种身分申请LI ...

  5. js获取图片原始大小

    摘要: 浏览器中显示的图片大小未必是他真实的高和宽,比如像下面这样,我们给他加上宽和高的样式 <img src="IE.png" style="width:25px ...

  6. JSP面试知识

    JSP方面 1. JSP四种范围是什么?区别是什么? Page:指单单一页jsp page的范围: Request:的范围只在一jsp页发出请求到另一页之间,随后这个属性失效: Session:范围是 ...

  7. Go之简单并发

    func Calculate(id int) { fmt.Println(id) } 使用go来实现并发 func main() { for i := 0; i < 100; i++ { go ...

  8. C 修改命令行文本颜色

    #include <Windows.h> #include <stdio.h> int main() { HANDLE h = GetStdHandle(STD_OUTPUT_ ...

  9. 第二十二篇:基于UDP的一对回射客户/服务器程序

    前言 之前曾经学习过一对回射客户/服务器程序的例子,不过那个是基于TCP协议的.本文将讲解另一对回射客户/服务器程序,该程序基于UDP协议. 由于使用的协议不同,因此编写出的程序也有本质上的区别,应将 ...

  10. Elasticsearch 5.4.3 聚合分组

    第一个分析需求:计算每个tag下的商品数量 GET /ecommerce/product/_search { "aggs": { "group_by_tags" ...