Android libc debug

Android在bionic libc中实现了一种方法用于定位堆内存(heap)泄露的位置以及内存越界(此处我们只关注内存泄露问题)

开启libc debug

在Android系统中,通过属性来控制libc debug开关。不同的Android版本,所使用的属性会有所不同:

Android6.0以及更早的版本

  • 目标进程从zygote派生

    1
    2
    3
    setprop libc.debug.malloc 1
    stop
    start
  • 目标进程不是从zygote派生

    1
    2
    setprop lib.malloc.debug 1
    kill -9 [目标进程ID]

Android7.0及以后的版本

  • 目标进程从zygote派生

    1
    2
    3
    4
    setprop libc.debug.malloc.program [目标进程名称]
    setprop libc.debug.malloc.options backtrace
    stop
    start
  • 目标进程不是从zygote派生

    1
    2
    3
    setprop libc.debug.malloc.program [目标进程名称]
    setprop libc.debug.malloc.options backtrace
    kill -9 [目标进程ID]
  • 用signal控制libc debug

    1
    2
    3
    4
    setprop libc.debug.malloc.options backtrace_enable_on_signal
    stop
    start
    kill -45 <目标进程>

    backtrace这种options不同的地方,就是backtrace_enable_on_signal可以允许你在合适的时机再开启目标进程的libc debug功能。

获取heap信息

对不同类型的进程,在打开了该进程的libc debug开关之后,就可以使用对应的方法来获得该进程的heap信息

派生自Zygote的进程

派生自Zygote的进程,可以通过am dumpheap命令获得heap内存信息

1
am dumpheap -n <PID> [/save/path]

所得到的heap信息如下

1
2
3
4
5
6
7
8
9
10
Android Native Heap Dump v1.2

Total memory: 295944
Allocation records: 4
Backtrace size: 16

z 0 sz 4096 num 66 bt 57e2e6b204 768185a854
z 0 sz 4096 num 6 bt
z 0 sz 1024 num 1 bt
z 0 sz 8 num 1 bt

注意:Android 7/8 版本的heap信息中,并不会合并同类项(即backtrace相同的项),使得heap信息看起来很长;而Android9虽然有合并同类项,但解析脚本又按照旧的方式解析(未合并同类项的方式),所得到的解析后的数据是有问题的。这些问题在HeapSnap里面都有解决。

mediaserver进程

1
dumpsys media.player -m

其它进程

除了派生自Zygote的进程,以及mediaserver进程之外,其它的进程Android不支持获取heap信息,但我们可以通过第三方的工具实现,比如:HeapSnap

1
2
/data/local/tmp/heapsnap -p <pid> -l /data/local/tmp/libheapsnap.so
kill -21 <pid>

heap信息被保存在/sdcard/heap_snap/,其内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
Heap Snapshot v1.0

Total memory: 33800
Allocation records: 3
Backtrace size: 32

z 0 sz 4096 num 8 bt 0000007f9a2b0e14 0000007f9a37f534 00000055828338bc 000000558283379c 0000007f9a37f6b0 0000005582833810
#00 pc 0000000000008e14 /system/lib64/libc_malloc_debug_leak.so (leak_malloc+408)
#01 pc 0000000000019534 /system/lib64/libc.so (malloc+24)
#02 pc 00000000000008bc /system/bin/leak_test
#03 pc 000000000000079c /system/bin/leak_test (main+28)
#04 pc 00000000000196b0 /system/lib64/libc.so (__libc_init+104)
#05 pc 0000000000000810 /system/bin/leak_test

解析Heap信息

使用am dumpheap命令所得到的heap信息中,backtrace是未经解析的地址,需要使用Android SDK工程中的脚本进行解析;用heapsnap工具得到的heap信息,已经有做了部分解析,如果想要更详细的解析,也可以使用Android SDK工程中的脚本对它进行解析。

注意:Android SDK工程代码需要跟所测试设备一致,否则很多地址会解析异常

  1. 把前面所保存的heap信息文件复制到android sdk中

  2. 使用脚本解析heap文件

    1
    development/scripts/native_heapdump_viewer.py heap.txt > heap_info.txt

    也可以保存成html文件:

    1
    development/scripts/native_heapdump_viewer.py --html heap.txt > heap_info.html

    android6.0及更早版本:

    1
    development/scripts/stack heap.txt > heap_info.txt

解析出来的信息如下:

1
2
3
4
5
6
7
8
9
10
11
 BYTES %TOTAL %PARENT    COUNT    ADDR LIBRARY FUNCTION LOCATION
0 0.00% 0.00% 0 APP

107808 100.00% 100.00% 198 ZYGOTE
107800 99.99% 99.99% 197 5d1b2c0678 /system/bin/leak_test do_arm64_start external/heapsnap.git/leak_test.c:?
107800 99.99% 100.00% 197 77cd006594 /system/lib64/libc.so __libc_init /proc/self/cwd/bionic/libc/bionic/libc_init_dynamic.cpp:109
98304 91.18% 91.19% 24 5d1b2c073c /system/bin/leak_test foo /proc/self/cwd/external/heapsnap.git/leak_test.c:9 (discriminator 1)
8472 7.86% 7.86% 172 5d1b2c0764 /system/bin/leak_test main /proc/self/cwd/external/heapsnap.git/leak_test.c:22
8472 7.86% 100.00% 172 77cd02a084 /system/lib64/libc.so sleep /proc/self/cwd/bionic/libc/upstream-freebsd/lib/libc/gen/sleep.c:58
8472 7.86% 100.00% 172 77cd05756c /system/lib64/libc.so nanosleep /proc/self/cwd/bionic/libc/arch-arm64/syscalls/nanosleep.S:7
8472 7.86% 100.00% 172 77cd2206bc [vdso] ??? ???