Android tombstones memory map分析
是是常见的Android稳定性异常,有内核异常和Android层异常。内核异常也就是常说的“kernel panic”,简称KE异常;Android层异常又分为java层crash和Native层crash,简称JE、NE异常。
上篇文章介绍了JE异常的抓取机制和处理方式,本文再讲一下NE异常。
Native exception
Android中,出于运行性能、代码安全或者跨平台实现等方面的需要,存在很多调用c、c++代码库的情况。这些库在运行时发生的异常统称native exception。
Android是基于Linux开发的,所以所有的native程序,本质上也是一个linux小程.序,在执行异常的时候,系统会处理相应的信号,然后产生一个tombstone文件, tombstone英文的本意是墓碑,会记录进程死亡时的进程号、线程号、死亡地址、死亡现场(堆栈)。
1.异常抓取机制
Native异常发生的时候,CPU通过中断的形式触发异常处理流程。Linux kernel将会处理中断,统一成信号发送,应用进程注册和处理信号。
所有的so都需要通过linker加载,Android上,linker会默认注册信号处理函数,代码位置在bionic/linker/linker_main.cpp,实现的地方在debuggerd_init()中。
发生内存泄漏异常通常会生成墓碑文件, 比如 /data/tombstones/tombstone_xx, 可以通过adb pull到本地进行分析, 如果是user版本没有权限的话可以使用adb bugreport生成bugreport-xxx-xxx.zip文件, 然后通过adb pull /data/user_de/0/com.android.shell/files/bugreports/bugreport-xxx-xxx.zip本地后解压, 墓碑文件保存在FS/data/tombstones/tombstone_xx
内存信息在memory map 条目, 摘取的部分信息如下
memory map (2438 entries):00000000'12c00000-00000000'2abfffff rw- 0 18000000 [anon:dalvik-main space (region space)]00000000'6f8a3000-00000000'6fafcfff rw- 0 25a000 [anon:dalvik-/apex/com.android.art/javalib/boot.art]00000000'6fafd000-00000000'6fb3dfff rw- 0 41000 [anon:dalvik-/apex/com.android.art/javalib/boot-core-libart.art]00000000'6fb3e000-00000000'6fb63fff rw- 0 26000 [anon:dalvik-/apex/com.android.art/javalib/boot-okhttp.art]00000000'6fb64000-00000000'6fb97fff rw- 0 34000 [anon:dalvik-/apex/com.android.art/javalib/boot-bouncycastle.art]00000000'6fb98000-00000000'6fb98fff rw- 0 1000 [anon:dalvik-/apex/com.android.art/javalib/boot-apache-xml.art]00000000'6fb99000-00000000'6fc43fff r-- 0 ab000 /apex/com.android.art/javalib/arm64/boot.oat (BuildId: 64e82acb1c8b0ce1e7555c9bf3f660e0aafd908c)00000000'6fc44000-00000000'6ffa2fff r-x ab000 35f000 /apex/com.android.art/javalib/arm64/boot.oat (BuildId: 64e82acb1c8b0ce1e7555c9bf3f660e0aafd908c)
生产墓碑文件的内存信息的代码如下
@system/core/debuggerd/libdebuggerd/tombstone.cppstatic void dump_all_maps(log_t* log, unwindstack::Unwinder* unwinder, uint64_t addr) {bool print_fault_address_marker = addr;unwindstack::Maps* maps = unwinder->GetMaps();_LOG(log, logtype::MAPS,"\n""memory map (%zu entr%s):",if (map_info->flags() & PROT_WRITE) {line += 'w';} else {line += '-';}if (map_info->flags() & PROT_EXEC) {line += 'x';} else {line += '-';}line += StringPrintf(" %8" PRIx64 " %8" PRIx64, map_info->offset(),map_info->end() - map_info->start());
通过代码分析可以知道每一行代表的含义
00000000'12c00000 - 00000000'2abfffff rw- 0 18000000 [anon:dalvik-main space (region space)]map_info->start map_info->end map_info->offset size(单位是字节Byte)
我这次出现内存不够的时候的内存信息的情况如下
[anon:libwebview reservation] 1G
[anon:cfi shadow] 2G
[anon:dalvik-free list large object space] 512M
[anon:dalvik-main space (region space)] 384M
anon是匿名内存的意思, 主要是libwebview reservation和cfi shadow这两个匿名内存占用很大, 可以通过搜素这两个关键字在Android源码里面找到分配的地方.
anon:libwebview reservation
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
lazyPreload
preloadWebViewFactory.prepareWebViewInZygote();
@frameworks/base/core/java/android/webkit/WebViewFactory.javaWebViewLibraryLoader.reserveAddressSpaceInZygote();
@frameworks/base/core/java/android/webkit/WebViewLibraryLoader.javareserveAddressSpaceInZygotelong addressSpaceToReserve = is64Bit ? 1 * 1024 * 1024 * 1024 : 130 * 1024 * 1024;sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
@frameworks/base/native/webview/loader/loader.cppjboolean DoReserveAddressSpace(jlong size) {void* addr = mmap(NULL, vsize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, addr, vsize, "libwebview reservation");
@system/core/debuggerd/libdebuggerd/tombstone.cpp static void dump_all_maps(log_t* log, unwindstack::Unwinder* unwinder, uint64_t addr) { bool print_fault_address_marker = addr; unwindstack::Maps* maps = unwinder->GetMaps(); _LOG(log, logtype::MAPS, "\n" "memory map (%zu entr%s):", if (map_info->flags() & PROT_WRITE) { line += 'w'; } else { line += '-'; } if (map_info->flags() & PROT_EXEC) { line += 'x'; } else { line += '-'; } line += StringPrintf(" %8" PRIx64 " %8" PRIx64, map_info->offset(), map_info->end() - map_info->start());
00000000'12c00000 - 00000000'2abfffff rw- 0 18000000 [anon:dalvik-main space (region space)] map_info->start map_info->end map_info->offset size(单位是字节Byte)
[anon:libwebview reservation] 1G
[anon:cfi shadow] 2G
[anon:dalvik-free list large object space] 512M
[anon:dalvik-main space (region space)] 384M
anon:libwebview reservation
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java lazyPreload preload WebViewFactory.prepareWebViewInZygote(); @frameworks/base/core/java/android/webkit/WebViewFactory.java WebViewLibraryLoader.reserveAddressSpaceInZygote(); @frameworks/base/core/java/android/webkit/WebViewLibraryLoader.java reserveAddressSpaceInZygote long addressSpaceToReserve = is64Bit ? 1 * 1024 * 1024 * 1024 : 130 * 1024 * 1024; sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve); @frameworks/base/native/webview/loader/loader.cpp jboolean DoReserveAddressSpace(jlong size) { void* addr = mmap(NULL, vsize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, addr, vsize, "libwebview reservation");