关于android System.loadLibrary阻塞问题的分析

上周客户反馈SDK在某些早期型号的安卓智能电视上加载有卡死的现象发生,已知可以稳定复现问题的设备型号如下:

长虹 C2000i
长虹 C3000i
长虹 C5000i
创维 Skyworth 8K56 E380S
创维 Skyworth 8K55 E680
联想 ideatv A21
TCL Generic Android on mt5880

我们通过SDK端的上报数据分析,发现出现问题的上报都是只有开始加载so的状态,没有加载完成或加载失败的状态,说明调用System.loadLibrary后长时间没有返回,调用处于阻塞状态,虽然加载so是在子线程中执行,但由于System.loadLibrary为系统调用,当前线程阻塞也会影响其他线程so的正常加载,导致应用功能受到影响,问题比较严重,必须优先解决。

客户反馈大部分设备加载so都是正常的,只是线上用户的个别型号会出现这个问题,并且客户自己的测试环境也无法复现,只提供了采集到的设备信息:

这里写图片描述

根据分析,我们初步怀疑是提供的SDK对于CPU指令集兼容不全,之前我们只提供了针对armeabi,armeabi-v7a指令集的库,在x86,x86_64等指令集平台可能会导致加载异常。但我们收集了对应设备的CPU型号,发现都是ARM平台的,没有x86的,所以基本排除了这个原因。

再分析,发现这些设备都是安卓4.0.4的系统版本,SDK之前已经在其他移动端的客户线上稳定跑了一年多,4.0.4的安卓系统也跑过,客户也没有反馈过相关问题,所以我们怀疑是不是由于智能电视和盒子上的安卓系统被硬件厂商裁剪过,导致系统的某些库不全,针对这种环境是否需要显式设置一些SDK的编译参数?,我们基于这个猜测增加了一些可能的编译参数,也更换过NDK的版本,重新打包SDK给到线上复现问题的用户测试,但用户还是会稳定复现加载so卡死的现象,问题依然没有解决。

我们和客户的测试环境都没有能够复现问题的机型,而联系线上用户协助验证的方式周期长,成本高,所以能尽快搞到相关型号的测试机就比较关键了。由于这些设备型号都比较老,线上早已停售,还好我们的售前同学比较给力,在58上找到了一个石家庄的长虹C2000i卖家,大早上从北京高铁赶往石家庄人肉提货,因为他出色的快递天赋,我们当天就拿到了一台能稳定复现的设备,在这里给冯老湿点赞。

拿到设备能稳定复现问题后,定位问题就快了许多,经过排查,发现问题出在编译SDK链接的STL库上,之前NDK工程Application.mk中设置如下:
这里写图片描述
工程中静态链接的是gnustl_static,这个问题比较诡异,如果程序链接的是gnustl_static,且只要sdk中包含了<iostream>头文件,so在问题设备上加载就会卡死,之前定位问题时也没有怀疑c++ stl标准库竟然能导致这个问题。

为了验证这个问题,我们写了个简单的hello world程序,代码十分简单,注意要包含<iostream>头文件:
这里写图片描述
同时Application.mk中设置APP_STL := gnustl_static
这里写图片描述
Android.mk中设定编译为可执行文件
这里写图片描述
用ndk-build构建编译出可执行文件,adb push到设备上执行即可复现程序卡死。(示例程序下载地址)

奇怪的是,即使链接了gnustl_static,只要不引用<iostream>头文件,就不会发生卡死,strace跟踪程序执行的系统调用,发现程序最后阻塞在recv中,跟踪的系统调用顺序如下:
这里写图片描述

至于为什么程序会阻塞在recv调用上,现在还不清楚深层原因,猜测可能和操作系统本身被裁剪过有关,但是用APP_STL := c++_static编译出来的可执行文件在相同环境下是运行正常的。查了NDK官方文档,提到gnustl在后续的NDK版本将会逐渐被弃用,推荐开发者基于libc++构建。

最终解决方法:不使用gnustl_static,改为链接c++_static,问题解决,这个隐晦的问题,最后发现竟是gnustl的大坑。。
这里写图片描述

发表评论

电子邮件地址不会被公开。 必填项已用*标注