Duang~ Android堆栈慘遭毁容?精神哥揭露毁容真相!

最近精神哥接到多个小伙伴的投诉,说无法看懂Bugly崩溃克星页面上显示的堆栈信息!精神哥赶紧把正研究的Top Crash崩溃和心爱的鸡爪放下,开始着手跟进。经分析发现,大家误会Bugly崩溃克星了,这一切都是Proguard搞的鬼!

下面请容精神哥一一道来!

问题描述

先看看图一中显示的正常堆栈内容,正常堆栈中每一个调用帧(Frame),都会有3个元素组成:

  • 类名(Full Class Name,红线标注);
  • 方法名(Method,蓝线标注);
  • 源文件及行号(SourceFile:LineNum,绿线标注)。

我们再看看图二显示的异常堆栈内容,可以发现栈中的某一调用帧(Frame),一个类名下会存在多个方法名,而且第一个方法尾部的源文件及行号是(Unknown Source),根本看不出源文件和行号。

但,有经验的同学童鞋应该能看出来,这个堆栈是被Proguard还原过的!

怎么看?很简单,出错时JVM生成的堆栈中每一个"方法描述",只有“方法名”而没有“方法返回类型”及“方法参数”。但被Proguard还原过的堆栈,应该有“方法返回类型”及“方法参数”。

那么这里有两个问题:

  1. 为什么堆栈会是Unknown Source?是Bugly崩溃克星没有上报吗?
  2. 为什么com.xx.a.a会被还原成多个方法(图2示例中就被还原了5个方法)?

精神哥继续给大家分析分析!

为什么堆栈会是Unknown Source?

是Bugly崩溃克星忘记上报了吗?不是!那是因为,你代码编译的姿势不对!

有经验的童鞋应该会发现,开发阶段上报Bugly的Crash崩溃堆栈都是有源码及行号的,但发布后就变成了Unknown Source,为什么?

如图3所示,我们发布时源码信息会先经过javac编译,再经过proguard混淆,才被打包进发布的apk中,最终Crash崩溃后Bugly获取到的堆栈中有木有源码及行号就要看这两步了。

所以想让Crash崩溃堆栈不再Unknown Source,需要两个保证:

保证一:javac编译保留源文件名及行号

【TODO】javac编译保留源文件名及行号 源文件名、行号、变量名称,都存在class文件的debug信息中,javac编译时可以选择是否保留debug信息,那么我们肯定是要保留的!下面是使用不同编译方法时保存源文件名及行号的解决方法示例。

  • 命令行javac编译的解决方法
javac -g:{lines,source} XXX.java
  • 使用Ant编译的解决方法
  • 使用Eclipse编译的解决方法

保证二:Proguard混淆中保留原文件名及行号

【TODO】Proguard中keep住源文件及行号

-keepattributes SourceFile,LineNumberTable

为什么com.xx.a.a被还原成多个方法了?

如图六,大家看到Class1_Promoted类下面的两个方法都被混淆成了a方法。

那么问题来了:假如堆栈中有 proguard.retrace.a.a(Unknown Source),应该被还原成神马呢?

因为它可以是proguard.retrace.Class_Promoted类下的方法void doClass1(int),也可以是void doClass1()。所以Proguard还原工具直接把这两个都给你列出来都作为你的还原结果,所以:

proguard.retrace.a.a(Unknown Source) 

被还原成

proguard.retrace.Class_Promoted.
void doClass1(int)
void doClass1()

图二中的5个方法也就是这么来的。

真不能怪Proguard还原工具,谁让Java堆栈中不给出方法的完整描述或签名(返回值,方法名,方法参数),而只有方法名,但Java语法又允许方法名相同。但这是有解决办法的!

Proguard还原工具其实还能根据行号进行区分,如果你的堆栈已经解决了UnknownSource问题,那么你的还原Mapping文件就不一样了。如图7,大家看到mapping文件中多了“数字:数字”这类内容,这个就是行号,也就是说proguard.retrace.a类中24行到26行属于void doClass(int)方法 ,29到31行属于void doClass1()方法。

假如堆栈中有proguard.retrace.a.a(Demo.java:25),就知道要被还原成proguard.retrace.Class_Promoted.void doClass1(int)了。

精神哥总结

Proguard作为非常优秀的工具,让我们安装包体积变小了,代码混淆更安全了,代码裁剪优化速度更快了,但确实也埋下了一些坑,增加了我们定位崩溃时的成本。跟着精神哥的这篇文章把原文件名和行号补上后,绝大部分的堆栈问题都可以被解决了!

那么堆栈问题就没了吗?有人曾问我:堆栈里显示A方法调用了D方法,跟着D方法崩溃了,但实际查看代码A并没有调用D方法啊?

精神哥汗了,不知道Progurad会做代码优化的么?认真看看是不是有A->B->C->D ?如果有!那是因为Proguard大神很喜欢把你没用的->B->C给干掉,直接A->D了!

原文发布于微信公众号 - 腾讯Bugly(weixinBugly)

原文发表时间:2015-03-05

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏linux驱动个人学习

Linux CFS调度器之队列操作--Linux进程的管理与调度(二十七)

完全公平调度器CFS中有两个函数可用来增删队列的成员:enqueue_task_fair和dequeue_task_fair分别用来向CFS就绪队列中添加或者删...

1532
来自专栏SAP最佳业务实践

想学FM系列(22)-SAP FM模块:派生规则推导策略(5)-派生规则推导使用

4.2 派生规则推导的操作及测试 在派生规则具体维护时其操作界面通常如下: ? ① 显示\修改派生规则。 ② 选中某一条步骤,点击查看该步骤的定义。 ...

5297
来自专栏向治洪

Android性能优化之TraceView和Lint使用详解

Android lint工具是Android studio中集成的一个代码提示工具,它主要负责对你的代码进行优化提示,包括xml和java文件,很强大。编写完代...

2516
来自专栏大数据钻研

9 个让 JavaScript 调试更简单的 Console 命令

一、显示信息的命令 <!DOCTYPE html> <html> <head> <title>常用console命令</title> ...

34010
来自专栏友弟技术工作室

vim精简版教程

vim编辑器 ? vim trree 编辑器的分类 文本编辑器,ASCII码 字处理器:word 全称 vi:Visual interface vim: Vis...

2295
来自专栏用户2442861的专栏

百度2014软件开发工程师笔试题详解

1.有一个数据A = [a_1,a_2,a_3.....a_n],n的大小不定,请设计算法将A中的所有数据组合进行输出

2982
来自专栏流媒体

Makefile文件编写

make 的参数有很多, 可以通过 make -h 去查看, 下面只介绍几个我认为比较有用的。

1183
来自专栏大史住在大前端

webpack4.0各个击破(7)—— plugin篇

plugin机制是webpack中另一个核心概念,它基于事件流框架tapable,你可以参考浏览器环境中的【DOM事件模型】,【SPA模型中的生命周期钩子】或是...

1652
来自专栏FreeBuf

Phpcms v9漏洞分析

最近研究源码审计相关知识,会抓起以前开源的CMS漏洞进行研究,昨天偶然看见了这个PHPCMS的漏洞,就准备分析研究一番,最开始本来想直接从源头对代码进行静态分析...

2997
来自专栏崔庆才的专栏

用Flask+Aiohttp+Redis维护动态代理池

5255

扫码关注云+社区

领取腾讯云代金券