前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >手把手教你如何分析 iOS 系统栈 crash

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

作者头像
腾讯Bugly
发布2018-03-23 10:02:22
1.9K1
发布2018-03-23 10:02:22
举报
文章被收录于专栏:腾讯Bugly的专栏腾讯Bugly的专栏

先上栈,这个 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:]

函数逻辑大概如下

代码语言:javascript
复制
test config

if(!change) return orgDes;

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

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


第七步

逆向 -[AVAudioSession privateConfigureRouteDescription:]

函数逻辑大概如下

代码语言:javascript
复制
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 最专业的质量跟踪平台

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2015-11-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯Bugly 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第一步
  • 第二步
  • 第三步
  • 第四步
  • 第五步
  • 第六步
  • 第七步
  • 第八步
  • 第九步
  • 第十步
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档