一次Android平台native heap内存泄露的追查过程

一次过程Android平台native heap内存泄露的追查过程

前段时间有个客户报了个问题:他们的一个视频播放app在我们的Android 6.0 SDK上出现内存泄露,我用showmap查了下,该app在长时间播放视频过程中出现native heap内存的持续泄露。

原以为只要把libc debug开关打开了,就能很容易查出问题点,却没想到碰到了一些意想不到的情况。

libc debug开关使能之后,在我们的Android6.0 SDK上执行所有命令及程序都会异常崩溃,系统跑不起来了

是的,通过”setprop libc.debug.malloc 1”使能libc debug后,所有命令都跑不了了,客户的app也跑不了了,出现的Fatal信息如下:

215 F DEBUG   : ABI: 'arm'
215 F DEBUG   : pid: 893, tid: 893, name: iptables  >>> /system/bin/iptables <<<
215 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7
614 W NativeCrashListener: Couldn't find ProcessRecord for pid 893
215 F DEBUG   :     r0 ffffffff  r1 0000000d  r2 be97e1a4  r3 00000000
215 E DEBUG   : AM write failed: Broken pipe
215 F DEBUG   :     r4 be97e1a4  r5 0000000d  r6 be97e298  r7 00000000
215 F DEBUG   :     r8 be97e298  r9 00000084  sl b6ed5bbc  fp 00000000
215 F DEBUG   :     ip b6ee606c  sp be97e178  lr b6ecf791  pc b6ecfca2  cpsr 200e0030
215 F DEBUG   : 
215 F DEBUG   : backtrace:
215 F DEBUG   :     #00 pc 00077ca2  /system/lib/libc++.so
215 F DEBUG   :     #01 pc 0007778d  /system/lib/libc++.so
215 F DEBUG   :     #02 pc 0007742d  /system/lib/libc++.so
215 F DEBUG   :     #03 pc 00048a3f  /system/lib/libc++.so (__gxx_personality_v0+290)
215 F DEBUG   :     #04 pc 0001e384  /system/bin/iptables (__gnu_Unwind_Backtrace+160)
215 F DEBUG   :     #05 pc 0001ecb4  /system/bin/iptables (___Unwind_Backtrace+20)

异常发生在libc++库中,头大,查了半天没找到原因。

试用了下valgrind,发现速度奇慢,且无法用在客户app上,用valgrind加载其它系统自带的app也是跑不起来,果断放弃。

还是想办法把Android自带的libc debug用起来。正向查不出来,那就曲线救国吧。

我修改Android5.1 SDK上面的libc++源码,添加必要函数后编译完成,替换掉样机上原来的libc++.so(样机上跑的是Android6.0系统),居然能工作正常,而且打开libc debug开关后,执行命令也不会崩溃了。

使能libc debug后,打开app,结果又出状况了…

libc debug使能后,客户app无法工作了

在没有打开libc debug时候,客户的app是能正常工作的,但通过以下命令使能libc debug之后:

setprop libc.debug.malloc 1
stop
start

然后打开客户app,一播放视频就会异常退出,异常LOG如下:

ABI: 'arm'
pid: 4011, tid: 4104, name: AsyncTask #1  >>> com.hikvision.stabilitytesttool <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x59a70ecf
    r0 00000001  r1 00004080  r2 59a70ecf  r3 00000008
    r4 00004080  r5 9edf3b60  r6 00000080  r7 00000000
    r8 9edf361c  r9 9edf36e0  sl 80000000  fp 00000005
    ip 9f488158  sp 9edf34f0  lr 9f651971  pc 9f6514d8  cpsr 240b1c30

backtrace:
    #00 pc 0027e4d8  /data/app/com.hikvision.stabilitytesttool-1/lib/arm/libPlayCtrl.so
    #01 pc 0027e96d  /data/app/com.hikvision.stabilitytesttool-1/lib/arm/libPlayCtrl.so
    #02 pc 0027eb61  /data/app/com.hikvision.stabilitytesttool-1/lib/arm/libPlayCtrl.so
    #03 pc 002722d5  /data/app/com.hikvision.stabilitytesttool-1/lib/arm/libPlayCtrl.so
    #04 pc 00008a03  /system/lib/libc_malloc_debug_leak.so (_Unwind_Backtrace+130)
    #05 pc 000060ef  /system/lib/libc_malloc_debug_leak.so
    #06 pc 00006b69  /system/lib/libc_malloc_debug_leak.so (leak_malloc+84)
    #07 pc 00272851  /data/app/com.hikvision.stabilitytesttool-1/lib/arm/libPlayCtrl.so (_Znwj+12)
    #08 pc 000b51bf  /data/app/com.hikvision.stabilitytesttool-1/lib/arm/libPlayCtrl.so (_ZN9CRenderer16InitVideoDisplayEi+102)
    #09 pc 000b4d61  /data/app/com.hikvision.stabilitytesttool-1/lib/arm/libPlayCtrl.so (_ZN9CRenderer14SetVideoWindowEPvii+122)

因为打开了libc debug,因此在malloc时候会保存内存分配的调用路径,从上面异常backtrace可以看到在获得该调用路径时候崩溃了。崩溃点发生在客户的库文件中。

由于没有客户app源码,无法正向去查这个异常崩溃问题,所以,依然走曲线救国的道路…

我修改libc,在反查调用路径时候,把客户app中会导致异常的线程都过滤掉,之后,客户app终于能够工作了 ^_^

使用heapsnap工具获得heap的backtrace信息

通过前面的更改,终于能用libc debug了,接下来就是用heapsnap工具抓取客户app的heap详细信息了。
heapsnap工具可以参考这篇:《HeapSnap工具原理及其应用》
以查明内存泄露的地方,很快查到了两个异常的内存泄露点:

size       12, dup 214822
....
size       12, dup 53687, 0xb4ce3e20, 0xb6d26fa8, 0xb4bcdb8a, 0xb4bbf944, 0xb4bc78e0, 0xb4ba2a96, 0xb4acb512, 0xb49732f4, 0xb6e2770c, 0xb6e27780, 0xb6bb4aee, 0xb6bad77a, 0xb6bb2e8c, 0xb6bc3cf2, 0xb5c71588, 0xb5c70200, 0xb5c73516, 0xb5bf6e1c, 0xb5bf6c84, 0xb5bf8c4e, 0xb5bf768e, 0xb6ec8854, 0xb6cb27e6, 0xb6c8a332
          #00  pc 00006e20  /system/lib/libc_malloc_debug_leak.so (leak_malloc+83)
          #01  pc 00049fa8  /system/lib/libc++.so (operator new(unsigned int)+15)
          #02  pc 0034cb8a  /system/lib/libart.so (art::ThreadList::Register(art::Thread*)+257)
          #03  pc 0033e944  /system/lib/libart.so (art::Thread::Init(art::ThreadList*, art::JavaVMExt*, art::JNIEnvExt*)+487)
          #04  pc 003468e0  /system/lib/libart.so (art::Thread::Attach(char const*, bool, _jobject*, bool)+135)
          #05  pc 00321a96  /system/lib/libart.so (art::Runtime::AttachCurrentThread(char const*, bool, _jobject*, bool)+13)
          #06  pc 0024a512  /system/lib/libart.so
          #07  pc 000f22f4  /system/lib/libart.so
          #08  pc 0008870c  /system/lib/libandroid_runtime.so (android::JNISurfaceTextureContext::getJNIEnv(bool*)+47)
          #09  pc 00088780  /system/lib/libandroid_runtime.so (android::JNISurfaceTextureContext::onFrameAvailable(android::BufferItem const&)+11)
          #10  pc 00032aee  /system/lib/libgui.so (android::ConsumerBase::onFrameAvailable(android::BufferItem const&)+93)
          #11  pc 0002b77a  /system/lib/libgui.so (android::BufferQueue::ProxyConsumerListener::onFrameAvailable(android::BufferItem const&)+45)
          #12  pc 00030e8c  /system/lib/libgui.so (android::BufferQueueProducer::queueBuffer(int, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*)+1803)
          #13  pc 00041cf2  /system/lib/libgui.so (android::Surface::queueBuffer(ANativeWindowBuffer*, int)+641)
          #14  pc 0006b588  /system/lib/libstagefright.so (android::ACodec::BaseState::onOutputBufferDrained(android::sp<android::AMessage> const&)+483)
          #15  pc 0006a200  /system/lib/libstagefright.so (android::ACodec::BaseState::onMessageReceived(android::sp<android::AMessage> const&)+619)
          #16  pc 0006d516  /system/lib/libstagefright.so (android::ACodec::ExecutingState::onMessageReceived(android::sp<android::AMessage> const&)+481)
          #17  pc 0000be1c  /system/lib/libstagefright_foundation.so (android::AHierarchicalStateMachine::handleMessage(android::sp<android::AMessage> const&)+63)
          #18  pc 0000bc84  /system/lib/libstagefright_foundation.so (android::AHandler::deliverMessage(android::sp<android::AMessage> const&)+15)
          #19  pc 0000dc4e  /system/lib/libstagefright_foundation.so (android::AMessage::deliver()+53)
          #20  pc 0000c68e  /system/lib/libstagefright_foundation.so (android::ALooper::loop()+221)
          #21  pc 00010854  /system/lib/libutils.so (android::Thread::_threadLoop(void*)+111)
          #22  pc 000417e6  /system/lib/libc.so
          #23  pc 00019332  /system/lib/libc.so

上述信息中,size后面数值是申请内存的大小,dup后面数值是申请的次数。
这两处泄露点都是12字节的内存泄露,其中一个申请了214822次,另一个申请了53687次。前面一处的heap信息没有输出backtrace,是因为我们修改libc把异常线程过滤掉的缘故;而后面的heap信息中就带有backtrace了。

通过解读这个backtrace,可以看到libart库中的art::ThreadList::Register函数中申请了内存,但没有释放掉。

最后

最后,修改libcxx/include/list中的remove函数,重新编译art推送到样机上,再用heapsnap抓取heap信息,前面发现的那两处heap泄露点都已经不存在。至此,内存泄露问题终于解决了。

如果没有libc debug,还有哪些方式可以查这个问题呢?

### 如何彻底卸载金山毒霸及类似安全软件 #### 卸载前准备 为了确保能够顺利并彻底地卸载金山毒霸或其他类似的杀毒软件,在操作之前建议备份重要数据。某些情况下,不当的操作可能会引起系统不稳定或者丢失部分设置。 #### 使用官方工具进行卸载 许多厂商提供了专门用于清理其产品的残留文件和注册表项的小程序。对于金山毒霸而言,可以访问官方网站寻找是否有提供此类服务的应用程序下载链接[^1]。通过这些专业的清除工具往往能更干净地移除产品而不留痕迹。 #### 利用Windows自带功能执行标准流程 如果无法获取到特定品牌的专用卸载器,则可以通过控制面板中的“程序和特性”选项来进行常规的卸载过程: 1. 打开 **控制面板** 2. 选择 **程序 -> 程序和功能** 3. 在列表里找到要删除的安全防护应用(例如:Kingsoft Antivirus) 4. 右键点击该条目并选取 **卸载** 此方法适用于大多数情况下的正常安装版本,并且操作系统会尝试尽可能多地清理关联组件。 #### 清理剩余项目 即使经过上述步骤之后,仍可能存在一些孤立的对象未被处理掉。这时可以考虑借助第三方维护类软件来进一步扫描整个硬盘以及注册表内的遗留记录,比如 CCleaner 或者 Revo Uninstaller 这样的工具可以帮助识别并消除那些顽固不化的碎片化信息。 需要注意的是,在采取任何行动以前务必确认目标确实是不再需要的服务或应用程序,以免误删造成不必要的麻烦。 ```bash # 示例命令行指令 (仅作演示用途) msconfig # 调整启动项配置以禁用相关联的服务 tasklist /FI "IMAGENAME eq kxetray.exe" # 查看进程是否存在 ``` #### 安全提示 在整个过程中应当保持警惕,尤其是在涉及到修改系统核心部位如注册表编辑的时候;如果不熟悉具体操作的话最好寻求专业人士的帮助。另外也要注意从可靠渠道获得辅助性的实用程序以防引入新的安全隐患[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值