手把手教你如何分析 iOS 系统栈 crash

先上栈,这个 crash 是我们目前开发产品的 top5 crash

第一步

对于死在 ojbc _ msgSend 的函数(不仅仅是 msgSend, objc_retain 等一切没有创建栈帧的都需要注意),请先检查 crash 上报的寄存器信息

一般来说,lr 肯定不等于第一个栈。 目前的 crash上报功能,丢失了最顶层的栈。因为 objc_msgSend 并没有创建栈帧。

这样,我们就得根据 lr,来计算真实的最后一个栈了。

栈帧介绍

栈帧,保存当前函数返回地址,以及上级栈帧地址。 这样,通过枚举栈帧,即可得到函数的调用栈。


第二步

在模块列表查找,lr 是那个模块的

找到了,计算绝对偏移,找出对应函数地址。(函数绝对偏移 = lr - 模块基址)同样,反过来就可以在本机或者 IDA 查找函数了)

libAVFAudio.dylib`AVAudioSessionPropertyListener(void, unsigned int, unsigned int, void const) + 1796

好了,终于找到调用 objc_msgSend 的点了。


第三步

运行程序,找到野掉的对象到底是个什么

AVAudioSessionRouteDescription (这里需要根据 selector 再次核实上一步骤的调用点是否正确。crash 时 selector 存放在 x1 寄存器, 有时候上报平台会打印出 x-selector detect, 对比下 selector 是否一致,一致则说明上一步得到的地址没有问题)


第四步

这个对象是哪里来的

需要调试神器 lzmalloc 命令(话说,这个命令实在是太好用了。) lzmalloc 为我们自己开发的调试器辅助命令,用于打印对象分配以及释放点的堆栈信息。 下面为 lzmalloc 结果


第五步

到这里为止,首先排查了自己代码内部对于 AVAudioSessionRouteDescription 确定不存在过度释放的问题,不得已,只有逆向了。(最蛋疼的步骤了)

首先确定野掉的 AVAudioSessionRouteDescription 来源于 libAVFAudio.dylib`AVAudioSessionPropertyListener(void, unsigned int, unsigned int, void const) + 1768

而此行是调用函数 -[AVAudioSession privateConfigureRouteDescription:]

而 privateConfigureRouteDescription 从lzmalloc 结论来看,内部是调用 +[AVAudioSessionRouteDescription privateCreateOrConfigure:withRawDescription:]


第六步

首先逆向 +[AVAudioSessionRouteDescription privateCreateOrConfigure:withRawDescription:]

函数逻辑大概如下

test config

if(!change) return orgDes;

else  release(orgDes); alloc newDes。 newDes retain autorelease

从这个逻辑,可以看出来,如果是 new 出来的对象,那是绝对不可能野的。 所以,对象只可能是返回了 orgDes。


第七步

逆向 -[AVAudioSession privateConfigureRouteDescription:]

函数逻辑大概如下

lock
{
   get orgDes
   newdes = call privateCreateOrConfigure:withRawDescription:
   return newdes
}

发现问题了吗? 如果 newdes = orgdes 呢。 而函数返回后,刚好另一个线程执行了 privateCreateOrConfigure:withRawDescription: 而这个时候,config 又恰好变动呢。 orgDes 会被释放!! 哎,这个锁算是白加了。


第八步

问题原因可能猜到了。但是如何修改呢?

hook -[AVAudioSession privateConfigureRouteDescription:] 内部调用原函数之后加上 retain autorelease? 似乎挺理想,但是仔细想想,还是没什么用啊,照样阻止不了其他线程 privateCreateOrConfigure:withRawDescription:的调用。当然这个可以很大降低概率,因为间隔代码很少。

so,换种思路,根据之前动态调试的结果 privateCreateOrConfigure:withRawDescription: 触发时机,有两个,一个是系统耳机插拔通知的时候,另一个就是我们自己调用 audiosession.currentroute 的时候。 而系统通知只在 audio 线程调用。所以呢,既然如此,那我们自己干脆不调用了,在系统通知的时候,在回调里面保存最新的。 当需要访问 audiosession.currentroute 直接返回我们保存的值。 这样,冲突不就没了


第九步

修改外发 很幸运,已经消灭了这个问题。


第十步

总结下

最近发现不少苹果的内存问题。 不知道为什么苹果自己代码很多都不使用 arc,也许这样做很 cool!!

不过,连苹果这么牛这么自信的开发,都弄出了这么多难缠的问题。我们还是不要向他学习,老老实实的用好 ARC 吧。


本文系腾讯Bugly独家内容,转载请在文章开头显眼处注明注明作者和出处“腾讯Bugly(http://bugly.qq.com)”

腾讯Bugly 最专业的质量跟踪平台

精神哥、小萝莉,为您定期分享应用崩溃解决方案

本文分享自微信公众号 - 腾讯Bugly(weixinBugly)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2015-11-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏微服务生态

Akka简单的性能测试

这种方案是采用MQ作为中间的媒介,在服务端采用线程池异步处理任务,处理完成之后将结果发送到MQ中,客户端采用侦听的方式得到结果继续进行处理。

24410
来自专栏机器学习算法与Python学习

干货 | Python 爬虫的工具列表大全

源 | 伯乐头条 | 小象 这个列表包含与网页抓取和数据处理的Python库。 网络 通用 urllib -网络库(stdlib)。 requests -网络...

36090
来自专栏信安之路

漏洞分析之Typecho二连爆

这段时间 Typecho 在十几天之内连续爆了两个最高可 getshell 的洞,先是 SSRF 可打内网,再是反序列化直接前台 getshell ……安全性这...

24700
来自专栏逍遥剑客的游戏开发

GameEngineArchitecture读书笔记(二)

17450
来自专栏王清培的专栏

WebAPi的可视化输出模式(RabbitMQ、消息补偿相关)——所有webapi似乎都缺失的一个功能

最近的工作我在做一个有关于消息发送和接受封装工作。大概流程是这样的,消息中间件是采用rabbitmq,为了保证消息的绝对无丢失,我们需要在发送和接受前对消息进行...

13200
来自专栏北京马哥教育

干货 | 史上最全的 Python 爬虫工具列表大全

来源:伯乐在线 这个列表包含与网页抓取和数据处理的Python库。 网络 通用 urllib -网络库(stdlib)。 requests -网络库。 gra...

868140
来自专栏web前端教室

前端开发就是这样,“看似简单的东西,反而会很复杂。”

今天的零基础前端课讲到了一个tab地址切换的菜单,就下面这个东西, ? 第一眼看起来超级简单,无非是点击上面的title显示下面的菜单,然后点省市区把内容选上去...

23460
来自专栏牛客网

阿里一面面经C++

【每日一语】绝对不要做你的敌人希望你做的事情,原因很简单,因为敌人希望你这样做。——拿破仑

9720
来自专栏静晴轩

Gulp折腾之路(II)

前段时间折腾Gulp,主要是搜寻一些插件,组合之以优化前端开发流程。这段折腾历程除了达成所愿外,给予最大的收获是:只要你想实现某功能,基本就已有对应插件供使用;...

38350
来自专栏ChaMd5安全团队

zctf web100的简单分析

zctf web100的简单分析 From ChaMd5安全团队核心成员 Pcat web100 xctf2017第二站的zctf,web100的链接点开之后...

382150

扫码关注云+社区

领取腾讯云代金券