一.摘要
我們在分析fd泄漏問題的時候一般的通用方法是在/proc/pid/fd下不斷的ls -al | wc -l統計fd數量,並且查看哪個fd不斷的增多,然後再去代碼中排查對應的代碼,有時候光看fd並不容易找到泄漏的地方。本文章將介紹高通的leakdetect方法。
二.leakdetect使用介紹
默認情況下手機已經包含了fd泄漏的檢測代碼,具體路徑在:bionic/libc/malloc_debug/,它可以監控:file(1),socket(2),mmap(3)
具體原理就不多說了,有興趣的自己看源碼吧,下邊介紹使用方法:
1.adb root
2.adb shell setprop libc.debug.leakdetect 1(1代表監控file,2代表監控socket,3代表監控mmap)
3.adb shell setprop libc.debug.leakdetect.program app_process(安卓進程填app_process,native進程填對應的進程名字)
4.adb shell stop
5.adb shell start
6.adb shell kill -28 pid(你要監控的進程)
7.當你的fd不斷增加的時候再次輸入:adb shell kill -28 pid
你會在logcat中看到對應的結果:O機器關鍵字:fd_leak_debug Q機器關鍵字:malloc_debug
三.結果展示
file泄漏檢測結果:
06-02 11:59:34.040 5534 17218 E fd_leak_debug: Leak Type: file, count = 1, fd = 173, path = anon_inode:dmabuf
06-02 11:59:34.040 5534 17218 E fd_leak_debug: #00 pc 0000000000004bd8 /system/lib64/libc_leak_detector.so (dup+124)
06-02 11:59:34.040 5534 17218 E fd_leak_debug: #01 pc 00000000000070f0 /system/lib64/libcutils.so (native_handle_clone+144)
06-02 11:59:34.040 5534 17218 E fd_leak_debug: #02 pc 0000000000005e84 /vendor/lib64/hw/[email protected] (android::hardware::graphics::mapper::V2_0::implementation::GrallocMapper::importBuffer(android::hardware::hidl_handle const&, std::__1::function<void (android::hardware::graphics::mapper::V2_0::Error, void*)>)+80)
06-02 11:59:34.040 5534 17218 E fd_leak_debug: #03 pc 000000000001896c /system/lib64/[email protected]
06-02 11:59:34.040 5534 17218 E fd_leak_debug: #04 pc 0000000000014600 /system/lib64/libui.so (android::Gralloc2::Mapper::importBuffer(android::hardware::hidl_handle const&, native_handle const**) const+88)
06-02 11:59:34.040 5534 17218 E fd_leak_debug: #05 pc 0000000000017dc4 /system/lib64/libui.so (android::GraphicBufferMapper::importBuffer(native_handle const*, native_handle const**)+108)
06-02 11:59:34.040 5534 17218 E fd_leak_debug: #06 pc 0000000000016a04 /system/lib64/libui.so (android::GraphicBuffer::unflatten(void const*&, unsigned long&, int const*&, unsigned long&)+604)
06-02 11:59:34.040 5534 17218 E fd_leak_debug: #07 pc 00000000001373c8 /system/lib64/libandroid_runtime.so
06-02 11:59:34.040 5534 17218 E fd_leak_debug: #08 pc 000000000006b9a4 /system/lib64/libbinder.so (android::Parcel::read(android::Parcel::FlattenableHelperInterface&) const+464)
06-02 11:59:34.040 5534 17218 E fd_leak_debug: #09 pc 0000000000136e1c /system/lib64/libandroid_runtime.so
mmap泄漏檢測結果:
06-01 20:47:01.342 17087 19660 E malloc_debug: Leak Type: mmap, count = 1, addr = 0x794fee8000
06-01 20:47:01.342 17087 19660 E malloc_debug: #00 pc 000000000000084c /system/lib64/libc_leak_detector.so (mmap+180)
06-01 20:47:01.342 17087 19660 E malloc_debug: #01 pc 0000000000007bcc /apex/com.android.runtime/lib64/libartbase.so (art::MemMap::MapInternal(void*, unsigned long, int, int, int, long, bool)+88)
06-01 20:47:01.342 17087 19660 E malloc_debug: #02 pc 0000000000007844 /apex/com.android.runtime/lib64/libartbase.so (art::MemMap::MapAnonymous(char const*, unsigned char*, unsigned long, int, bool, bool, art::MemMap*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*, bool)+280)
06-01 20:47:01.342 17087 19660 E malloc_debug: #03 pc 000000000017aa84 /apex/com.android.runtime/lib64/libart.so (art::IndirectReferenceTable::IndirectReferenceTable(unsigned long, art::IndirectRefKind, art::IndirectReferenceTable::ResizableCapacity, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*)+176)
06-01 20:47:01.342 17087 19660 E malloc_debug: #04 pc 0000000000258890 /apex/com.android.runtime/lib64/libart.so (art::JNIEnvExt::JNIEnvExt(art::Thread*, art::JavaVMExt*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*)+52)
06-01 20:47:01.342 17087 19660 E malloc_debug: #05 pc 00000000002587fc /apex/com.android.runtime/lib64/libart.so (art::JNIEnvExt::Create(art::Thread*, art::JavaVMExt*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*)+52)
06-01 20:47:01.342 17087 19660 E malloc_debug: #06 pc 00000000003ce91c /apex/com.android.runtime/lib64/libart.so (art::Thread::CreateNativeThread(_JNIEnv*, _jobject*, unsigned long, bool)+400)
06-01 20:47:01.342 17087 19660 E malloc_debug: #07 pc 0000000000308f28 /apex/com.android.runtime/lib64/libart.so
06-01 20:47:01.342 17087 19660 E malloc_debug: #08 pc 0000000000007fec /system/framework/arm64/boot.oat
06-01 20:47:01.342 17087 19660 E malloc_debug: #09 pc 00000000003dcbf5 /apex/com.android.runtime/javalib/core-oj.jar
四.hprof輔助分析
我們有了調用棧後還可以看下是哪個對象持有了這個fd,那麼我們就需要抓一個hprof輔助分析,抓取方法:adb shell am dumpheap com.android.settings /data/local/tmp/settings.hprof,然後將settings.hprof pull到電腦上並打開,我們這裏以“file泄漏檢測結果”中的“file泄漏檢測結果“來舉例,之前我們已經看到了是和GraphicBuffer相關,那麼我們在hprof中找到GraphicBuffer,我們發現它的引用關係是:ActivityManager->TaskSnapshot->mSnapshot
繼續搜索:TaskSnapshot
我們發現這裏創建了很多TaskSnapshot,因爲TaskSnapshot中又包含了一個GraphicBuffer,所以這個時候我們就去排查TaskSnapshot爲什麼被一直被創建並且沒有被回收
五.總結
有了調用棧之後我們再去分析那就簡單了很多了,不需要翻遍源碼到處找在哪裏泄漏了,直接從調用棧出發查問題就好了