Tencent / tinker

Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstall apk.
Other
17.17k stars 3.34k forks source link

问个关于 unable to open DEX file 的问题 #925

Closed cantalou closed 5 years ago

cantalou commented 6 years ago

异常类型:app运行时异常

手机型号:OPPO,VIVO,HUAWEI等各机型都会

手机系统版本:4.X版本

tinker版本:如:1.9.8

gradle版本:3.5 android plugin 版本: 2.3.3 buildTools 版本: 25.0.3

是否使用热更新SDK:否

系统:如:win7

堆栈/日志: W/dalvikvm( 12513): DexOpt: --- END '' --- status=0x000e, process failed E/dalvikvm( 12513): Unable to extract+optimize DEX from '/data/data//tinker/patch-56eee707/dex/classes.dex.jar' Suppressed: java.io.IOException: unable to open DEX file at dalvik.system.DexFile.openDexFileNative(Native Method) at dalvik.system.DexFile.openDexFile(DexFile.java:296) at dalvik.system.DexFile.(DexFile.java:111) at dalvik.system.DexFile.loadDex(DexFile.java:151)

在个别用户的手机上会出现如下的错误日志, 但是我们自己的测试机器无法复现该问题, 并且只在4.X的版本才会出现这个错误. 想问下我该如何去排查这个问题? 是classes.dex.jar文件损坏? 还是我们的业务代码有什么问题?

tys282000 commented 6 years ago

W/dalvikvm( 12513): DexOpt: --- END '' --- status=0x000e, process failed E/dalvikvm( 12513): Unable to extract+optimize DEX from '/data/data//tinker/patch-56eee707/dex/classes.dex.jar'

关键是这两行,dexopt失败了,所以opendex肯定也跟着失败了。如果没法复现的话,一般就是问题机器的存储空间或者内存不够导致的,估计没啥好办法能进一步排查了。

flyeek commented 6 years ago

遇到了同样的问题。status=0x000e,这个应该是dexopt进程收到了SIGALRM信号后异常退出了,没有找到进一步原因

cantalou commented 6 years ago

根据用户设备统计上来的发生“status=0x000e, process failed”的手机,有些手机存储空间都还是挺大的,但部分确实是存储空间有问题。在存储空间充足的情况下,删掉文件重下或者重命名文件尝试是有概率成功的(同文件失败重试基本无法成功)

cantalou commented 6 years ago

关于返回码“status=0x000e”, 除了如楼上说的SIGALRM信号的问题, 不知还有啥资料或者方向呢?

xiabodan commented 5 years ago

我也遇到了 有跟进原因吗?

CaptainJno commented 5 years ago

你们的问题都解决了吗?项目中我去除tinker后,还是有这个问题。说明tinker无关。 出现问题的机型:vivo Y613 ,等少部分机型

cantalou commented 5 years ago

这个问题和tinker没关系, 即使不用tinker也会有可能出现这个问题. 来这里提问主要是想看下大神有没有遇到过或者有没相关解决思路和排查方法. 目前从一些日志上来看 X5的webview使用插件的方式也会有这个错误, 就是不知道他们新版有没有解决.

xiabodan commented 5 years ago

是的,我的也是和tinker没有关系,只会在几个4.4的手机上出现,我的jar包大概有5w个method。 将jar包拆分为两个jar同时加载,可以加载成功,暂时只能这么解决。

xiabodan commented 5 years ago

原因我也找了一下,没找到什么线索,我将会出问题的机器上的libdvm和dexopt, dump到其他的4.4机器上居然可以正常加载,怀疑是dvm哪里有个参数设置,或者触发了什么bug,没有找到。

CaptainJno commented 5 years ago

我的问题解决了,从报错分析,表面原因是一些低端机(主要是4.4,4.2)在执行DexOpt的过程中失败,经验证,可能和非主dex的大小有关系(非主dex较大)。本人目前是通过精简依赖,减小了非主dex的体积和方法数(体积减小了3M左右,方法数由5W减小到1W个Method)。在低端机上已经运行正常。 更底层的原因尚未分析...

cantalou commented 5 years ago

我倒是有给Google的哥们提过问题, 不过烂尾了 关于方法数的问题, 我有做过灰度, 我测试的结果和方法数的关系不大(也可能方式有误) 单dex文件方法数1w, 3w, 4w, 5.2w(大小不超过7.8m) 的方法数我都试过, 但是都会有概率出现失败的情况。甚至同一台机器, 同一个方法数, 前一次失败, 再试一次就成功的也有出现。最后我通过重命名的方式, 同一时刻成功的概率比较大。如: 下载时是plugin.jar 优化失败, 接着重命名成plugin.zip/plugin.apk 然后莫明的就成功了

大家可以https://issuetracker.google.com/issues/113346924 去催催看有没有结果

7heaven commented 5 years ago

关于返回码“status=0x000e”, 除了如楼上说的SIGALRM信号的问题, 不知还有啥资料或者方向呢?

https://www.ibm.com/support/knowledgecenter/en/SSB23S_1.1.0.15/gtpc2/cpp_waitpid.html 可以看一下waitpid函数的定义,status是由这个函数赋值的

cantalou commented 5 years ago
int status = 0x000e;
LOGI("status 0x%04x", status);
if (WIFEXITED(status)) {
    LOGI("WIFEXITED %d, code %d", WIFEXITED(status), WEXITSTATUS(status));
} else {
    LOGI("WIFEXITED  not match %d", WIFEXITED(status));
}
if (WIFSTOPPED(status)) {
    LOGI("WIFSTOPPED %d, code %d", WIFSTOPPED(status), WEXITSTATUS(status));
} else {
    LOGI("WIFSTOPPED not match %d", WIFSTOPPED(status));
}
if (WIFSIGNALED(status)) {
    LOGI("WIFSIGNALED %d, code %d", WIFSIGNALED(status), WTERMSIG(status));
} else {
    LOGI("WIFSIGNALED not match %d", WIFSIGNALED(status));
}

I/NativeTool: WIFEXITED not match 0 I/NativeTool: WIFSTOPPED not match 0 I/NativeTool: WIFSIGNALED 1, code 14 我自己写的小Demo, 返回值是命中 WIFSIGNALED的 @7heaven 是否我写的有误?

7heaven commented 5 years ago
int status = 0x000e;
LOGI("status 0x%04x", status);
if (WIFEXITED(status)) {
    LOGI("WIFEXITED %d, code %d", WIFEXITED(status), WEXITSTATUS(status));
} else {
    LOGI("WIFEXITED  not match %d", WIFEXITED(status));
}
if (WIFSTOPPED(status)) {
    LOGI("WIFSTOPPED %d, code %d", WIFSTOPPED(status), WEXITSTATUS(status));
} else {
    LOGI("WIFSTOPPED not match %d", WIFSTOPPED(status));
}
if (WIFSIGNALED(status)) {
    LOGI("WIFSIGNALED %d, code %d", WIFSIGNALED(status), WTERMSIG(status));
} else {
    LOGI("WIFSIGNALED not match %d", WIFSIGNALED(status));
}

I/NativeTool: WIFEXITED not match 0 I/NativeTool: WIFSTOPPED not match 0 I/NativeTool: WIFSIGNALED 1, code 14 我自己写的小Demo, 返回值是命中 WIFSIGNALED的 @7heaven 是否我写的有误?

昨天看反了,前面的WIFEXITED返回0没错。

passionli commented 5 years ago

目前看只有失败重试这种方案了?

douniwan5788 commented 5 years ago

我的问题解决了,从报错分析,表面原因是一些低端机(主要是4.4,4.2)在执行DexOpt的过程中失败,经验证,可能和非主dex的大小有关系(非主dex较大)。本人目前是通过精简依赖,减小了非主dex的体积和方法数(体积减小了3M左右,方法数由5W减小到1W个Method)。在低端机上已经运行正常。 更底层的原因尚未分析...

可能是"蜥蜴之尾" 病毒导致的,中了这个病毒后fork的进程在2s中后会收到SIGALRM http://blogs.360.cn/post/analysis_of_fakedebuggerd_d.html

flyeek commented 5 years ago
  1. dex的尺寸会影响出错的概率,但是dex降到多少,才完全不会发生,应该无法判断。毕竟dexopt在同一个机器,对同一个dex的处理时间,本身就不是固定的。
  2. 对dexopt的失败,进行检测,然后进行重试。只要重试足够的次数,应该也是可以解决的,实际情况应该是重试1~2次绝大部分机器都可以成功。

上述两个方案,都用上,应该可以完全消除这个crash。

simonjava commented 5 years ago

W/dalvikvm( 12513): DexOpt: --- END '' --- status=0x000e, process failed E/dalvikvm( 12513): Unable to extract+optimize DEX from '/data/data//tinker/patch-56eee707/dex/classes.dex.jar'

关键是这两行,dexopt失败了,所以opendex肯定也跟着失败了。如果没法复现的话,一般就是问题机器的存储空间或者内存不够导致的,估计没啥好办法能进一步排查了。

遇到同样问题了,在我加入某个jar包后 触发了class上限导致的,可能是导致总的class过多导致的,删除掉某些库后能恢复。在4.4的手机上复现,因为4.4 没有oat优化。 目前没有找到根本原因

simonjava commented 5 years ago

我的问题解决了,从报错分析,表面原因是一些低端机(主要是4.4,4.2)在执行DexOpt的过程中失败,经验证,可能和非主dex的大小有关系(非主dex较大)。本人目前是通过精简依赖,减小了非主dex的体积和方法数(体积减小了3M左右,方法数由5W减小到1W个Method)。在低端机上已经运行正常。 更底层的原因尚未分析...

如何精简这么多

simonjava commented 5 years ago

这个问题和tinker没关系, 即使不用tinker也会有可能出现这个问题. 来这里提问主要是想看下大神有没有遇到过或者有没相关解决思路和排查方法. 目前从一些日志上来看 X5的webview使用插件的方式也会有这个错误, 就是不知道他们新版有没有解决.

我也是加入x5以后突然报了这个错,x5去掉就ok。后面我把x5加上又去掉了几个库,也能通过。所以是某个 dex 过大导致的

douniwan5788 commented 5 years ago

这个问题和tinker没关系, 即使不用tinker也会有可能出现这个问题. 来这里提问主要是想看下大神有没有遇到过或者有没相关解决思路和排查方法. 目前从一些日志上来看 X5的webview使用插件的方式也会有这个错误, 就是不知道他们新版有没有解决.

我也是加入x5以后突然报了这个错,x5去掉就ok。后面我把x5加上又去掉了几个库,也能通过。所以是某个 dex 过大导致的

可以对比一下系统分区有没有中毒文件,这个病毒分布挺广的,遇到几台机器都是它引起的,表象是dex过大就失败,根本原因是中毒后dexopt运行超过2s就会被SIGALRM。https://github.com/Tencent/tinker/issues/925#issuecomment-483927335

douniwan5788 commented 5 years ago

这个病毒通过修改系统so实现开机启动注入zygote,清除后会导致无法开机,建议刷机。通过strace观察,每次fork后注册了一个2s的SIGALRM,可以看handler的地址结合maps定位病毒so文件。可以通过Runtime.getRuntime().exec 执行任意elf并观察2s后是否被SIGALRM简单判断是否中毒,strace这个过程可以定位病毒文件

cantalou commented 5 years ago

关于dex文件大小的问题, 我们App里面有个只包含单个dex文件、2W方法数、dex大小1M的插件,也是会有失败的情况

@douniwan5788 关于你说的这个病毒情况, 有没有检测的Demo? 或者执行exec后如何strace? 由于只能是在用户手机上面抓日志,不是那么好处理。

douniwan5788 commented 5 years ago

关于dex文件大小的问题, 我们App里面有个只包含单个dex文件、2W方法数、dex大小1M的插件,也是会有失败的情况

@douniwan5788 关于你说的这个病毒情况, 有没有检测的Demo? 或者执行exec后如何strace? 由于只能是在用户手机上面抓日志,不是那么好处理。

简单的检测可以exec ping 127.0.0.1,正常情况会一直ping下去,中毒的机器几秒钟后就被SIGALRM结束进程了。精确的可以自己写个elf下发,通过sigaction获取SIGALRM的oldact,并在/proc/self/maps中定位handler的地址确定so文件上报。还可以通过alarm(0)防止被kill,病毒是通过pthread_atfork达到fork后执行的,也可以考虑在这方面规避

cantalou commented 5 years ago

让出现这个问题的用户安装了 360安全卫士和360急救箱 , 没有扫描出木马 image @douniwan5788

douniwan5788 commented 5 years ago

让出现这个问题的用户安装了 360安全卫士和360急救箱 , 没有扫描出木马 image @douniwan5788

好像是扫不出来了,因为没法清除,清除会导致进不了系统,最好的办法是刷机… 也可能是变种或其他的问题导致的

cantalou commented 5 years ago

根据 @douniwan5788 在 https://github.com/Tencent/tinker/issues/925#issuecomment-500123970 提示解决了。 代码我放在 https://github.com/cantalou/DexOptFix 有需要的直接引用就好了