iOS调试技巧

转载 2016年05月30日 18:27:42

转载自:http://www.hotobear.com/

iOS调试技巧(1)——断点

iOS调试技巧(2)——堆栈
iOS调试技巧(3)—— Attach to Process with Xcode

iOS调试技巧(4)—— LLDB指令


一:断点

断点是最基本的调试技巧之一。很长一段时间内,我都觉得断点毫无技巧可言,随着接触愈深,才发现自己真是“too native sometimes native”!

如何在Xcode里面加断点,以及Step over、Step into等等就不啰嗦了,直接说说其他用法。

自定义断点

Xcode中,断点右键,选择“Edit Breakpoint…”,可以看见如下的视图:

breakpioint-1

  1. Condition:条件断点,用来对付遍历或者循环相当有效
  2. Ignore:忽略前多少次的断点
  3. Action:断点时执行某种操作。右面的“+”“-”可以添加删除其他操作。点开下拉框,一共有六种操作可以选择
    • Debugger Command:Debugger指令,提示可以填入“po foo”之类的指令。Debugger指令高深莫测,具体等以后总结GDB和LLDB时再详细讲讲
    • Log Message:日志输出,一些特殊的日志规则也都有提示。(日志输出可以选择语音哦,识别率很高声音很性感哦)
    • Shell Command:Shell指令,没有具体尝试过,估计对于自动化测试很有效果
    • Sound:声音,和语音日志输出一样,就是用来调戏用的
    • 其他两种,目前没有应用场景,先占位
  4. Options:勾选可以选择执行后自动跳过。话说自动跳过的断点有什么用,其实是用来配合上一项“Action”的,这两项配合得好的话,必杀技无疑了

更多类型的断点

断点的导航视图(快捷键Command+6),详细标明了程序里的断点信息。除了第一种之外,还有两种:异常断点和符号断点,如下图。具体可在左下角的“+”添加。

breakpioint-2

  • 异常断点(Exception Breakpoint)

假如我们执行下面的代码:

NSArray *array = [[NSArray alloc] init];

NSObject *i = [array objectAtIndex:0];

NSLog(@"%@", i);

[array release];

会直接崩溃到main函数数并报异常。虽说可以知道是什么异常,但没法立刻知道具体是哪里抛出了异常,这时,添加一个异常断点就十分有效了。 

添加一个异常断点。“Break”选择“On Throw”,再次运行代码,就可以在第二句代码处抛出异常时断点。

  • 符号断点(Symbolic Breakpoint)

假设此时需要断点在非自己的代码的函数里面,这一项就发挥作用了。

breakpioint-3

  1. Symbol:符号,假如断不到点,可以先确定下是否格式写错了
  2. Module:模块,限制范围用的
  3. 其他选项的和之前的相同

GDB和LLDB

学好GDB和LLDB很重要,在控制台调试程序,给人的感觉就如同大家都在用IDE写代码的时候,你在用记事本敲代码。

GDB和LLDB有很多断点类的指令,具体指令可以看这里,就不解释含义了。

可以说下我的某次应用场景:某种操作下,UIWebView的弹出键盘被莫名其妙地缩回去了。代码里面所有的显示调用resignFirstResponder都查遍了,没有发现可疑的地方,可以断定,是UIWebView内部的操作导致键盘缩回了。
breakpioint-4
如图,断上所有有关resignFirstResponder的调用函数,这时就可以根据断点查出,到底那些代码,引起了网页键盘缩回。



二:堆栈


断点生效之后,按Command+5,可以快速跳到堆栈的导航视图,如下图:

stack-1

  1. Thread 1是主线程,也就是常说的UI线程,能够看到的是一个runloop开始到断点之间的堆栈调用的函数符号,按照调用顺序排列,最底部是从main函数向UIApplication发出的runloop开始的调用,一般来说基本没什么参考意义
  2. 不同的库和framework的堆栈用不同的颜色来表示,蓝色的人头表示的是非系统库的堆栈,紫色的杯子就是UIKit的东西啦
  3. 看得见函数名称的,说明拥有符号表,没有呢就靠猜吧,当然控制台lldb也是可以打印的

主线程被卡住是非常常见的场景,具体表现就是程序不响应任何的UI交互。这时按下调试的暂停按钮,查看堆栈,就可以看到是到底是死锁、死循环还是死等,导致UI线程被卡住。

下面这张图,做过浏览器都不陌生。主要是Thread 4 WebThread抛出的和UI线程进行同步的堆栈现场。Thread 4 WebThread堆栈顶部有着很明显的wait痕迹。这就是所谓的iOS上面webkit的多线程模型——wait until done。

QQ20131026-1@2x


三:Attach to Process with Xcode


断点调试的前提,是gdb或者lldb能够attach上被调试的程序。

Xcode自带lldb和gdb(Xcode 5.0去除了gdb),特别是lldb,对于Objective-C的调试很是方便;同时作为官方IDE,Xcode的图形化界面对于断点和堆栈也很是直观。所以若条件许可,利用Xcode的lldb来调试程序最为方便。

get-task-allow

Code signing Entitlements中有一项很重要的key值,就是get-task-allow,作用如下:

get-task-allow, when signed into an application, allows other processes (like the debugger) to attach to your app. Distribution profiles require that this value be turned off, while development profiles require this value to be turned on (otherwise Xcode would never be able to launch and attach to your app).

也就是说,get-task-allow决定了这个app能否被Xcode调试。一般来说,上架了的程序,这个key值为空,默认为NO。

模拟器

理所当然,既然能build进模拟器,那就说明这个app能够被调试。那么就大胆地启动app,在Xcode菜单里面选择

Debug -> Attach to Process -> [process name]。

选项中会列出Mac OS上所有的进程,有点多,按顺序找找有了。

而对于模拟器上的系统程序(如Safari),默认也都是可调试的,如下图:

Attach Safari sim

真机

(以下内容涉及app crack)

真机上的app,若不是自己build进去的,基本没有调试的权限。原因在于get-task-allow没有开启。若要开启,步骤如下:

以MobileSafari为例。

  1. 在越狱iOS设备上,利用iOS系统文件管理工具(itools,ifunbox),在Applications/MobileSafari.app目录下,找到MobileSafari这个执行文件,拷贝到Mac上。利用ldid将MobileSafari的code sign导出:
    ldid -e MobileSafari >> MobileSafari.xml
  2. 打开MobileSafari.xml细心看看,好多的私有key。不过不必天真,Code signing Entitlements若出现了私有key,是不允许上架的。在MobileSafari.xml添加get-task-allow这个key,并赋值为true,保存退出。
  3. 对MobileSafari进行重签名:
    ldid -SMobileSafari.xml ./MobileSafari 
  4. 将MobileSafari重新拷贝回iOS设备,声明下权限:
    chmod 755 ./MobileSafari 
  5. 启动Safari,Debug -> Attach to Process -> [process name]。
  6. 成功Attach上去之后,点击暂停按钮,在控制台执行指令
    po [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]

打印出App的名称,可以看到Safari,如下图:

Attach Safari (1)

测试环境和工具

  • Xcode 5.0
  • 越狱的iPhone,iOS6.1
  • ifunbox

参考

xcode中的get-task-allow有什么用?


四:LLDB指令



基本操作

 指令  作用
 c  continue,进程继续运行
 r  run,进程重新运行
 kill  run,kill进程
 s  step-in
 n  step-over
 finish  step-out

 

 断点

 指令  作用
 b  打印所有断点
 b -[NSString stringWithFormat:]  Objective-C函数断点
 b objc_msgSend  C/C++函数断点
 b resignFirstResponder  对所有含有resignFirstResponder的函数进行断点
 br del 1  删除某个断点(标号可以通过「打印所有断点」取得)

 

 堆栈

 指令  作用
 bt  打印当前线程堆栈
 bt all  打印所有线程堆栈
 up  跳到上一个调用栈
 down  跳到下一个调用栈

 

 p 和 po

p指令可以打印结构体以及基本类型,有时会出现异常,尝试类型强转,如 p (int)a 。

除了p指令外,LLDB增加了po,如 po self ,用来打印一个OC对象的description。

又如下面这句:

po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]

– [UIView recursiveDescription] 可以用来打印一个UIView的层次结构。在Reveal还没有出来之前,这个方法是用来快速查看UIView的层次结构。

事实上,p 和 po 指令是用来执行代码,并输出返回值结果。用来打印输出只是返回结果罢了。所以,在断点处输入

po [self.view setBackgroundColor:[UIColor whiteColor]]

是可以实时生效的。

 

image

image指令可以操作符号相关的东西。

NSArray *array = [[NSArray alloc] init];
NSObject *i = [array objectAtIndex:0];

比如说上面的代码,运行时崩溃,控制台会输入如下:

QQ20141228-2@2x

通过控制台输出,可以猜测崩溃在我们的程序「test」中的某处(上图标红处),此时通过指令

image lookup −a 0x00000001036d58e7

可以看出,程序崩溃在ViewController.m的第38行。QQ20141228-4@2x

另外,还有一个非常好用的指令,可以用来打印和某关键字相关的符号,如下:

image lookup −r −n UIWebView

 

参考

The LLDB Debugger





xcode中的get-task-allow

get-task-allow, when signed into an application, allows other processes (like the debugger) to attac...
  • laiguo
  • laiguo
  • 2012年08月15日 14:11
  • 4276

企业版IDP的申请及“In House”发布

网上关于个人版/公司版IDP申请流程,以及将应用发布到苹果商店的介绍有很多,但关于企业版IDP以及In House应用的讨论非常之少(尤其国内)。下面,笔者将对这两方面的问题进行一个全面细致的介绍。...
  • kmyhy
  • kmyhy
  • 2011年05月13日 21:25
  • 95285

Xcode中如何在即将启动的APP中设置断点调试

在实际的App应用中,我们可能会碰到这么一种情况:我们设计一个APP,它可能会在外界发送某种消息或满足某种条件时,被动的启动加载,并根据外界传入的一些参数进行初始化工作。 这里由于程序是由其他程序启动...
  • u013378438
  • u013378438
  • 2015年04月13日 21:33
  • 2370

Xcode 调试方法(5种方法)

Xcode 调试方法
  • u010079532
  • u010079532
  • 2014年02月19日 19:32
  • 1392

iOS逆向----调试之艰辛路

从5月12日到今天,一直在反反复复的测试
  • joosonmao
  • joosonmao
  • 2014年05月21日 11:40
  • 11027

【Unity3D】Android和ios真机调试测Profiler

U3D中的Profile也是可以直接在链接安卓设备运行游戏下查看的,导出真机链接U3D的Profile看数据,这样能更好的测试具体原因。 大概看了下官方的做法,看了几张帖子顺带把做法记录下来。 参考:...
  • swj524152416
  • swj524152416
  • 2016年12月05日 16:17
  • 5640

Xcode 调试方法总结

前言:编写代码过程中出现错误、异常是不可避免的。通常我们都需要进行大量的调试去寻找、解决问题。这时,熟练掌握调试技巧将很大程度上的提高工作效率。接下来就说说开发过程中Xcode的调试方法。...
  • u013822374
  • u013822374
  • 2016年03月23日 14:40
  • 5611

iOS调试 — 基本技巧

感谢原创 在程序中,无论是你想弄清楚为什么数组中有3个对象而不是5个,或者为什么一个新的玩家开始之后,游戏在倒退——调试在这些处理过程中是比较重要的一部分。通过本文的学习,我们将知道在程序中,可以使...
  • nogodoss
  • nogodoss
  • 2013年07月02日 09:55
  • 4579

iOS调试技巧

转载自:http://www.hotobear.com/ iOS调试技巧(1)——断点 iOS调试技巧(2)——堆栈 iOS调试技巧(3)—— Attach to Process with Xc...
  • u012411480
  • u012411480
  • 2016年05月30日 18:27
  • 1155

创建Entitlements文件与编译iOS APP

编译用于临时发布的APP程序之前,需要将一个特殊的代码签名Entitlements文件添加到项目中。在Xcode中,依次选择File > New File,然后从显示的窗口中的iOS > Code S...
  • a351945755
  • a351945755
  • 2015年07月17日 22:18
  • 20040
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:iOS调试技巧
举报原因:
原因补充:

(最多只允许输入30个字)