native_heapdump_viewer.py解析app内部so

本文介绍了如何在Android应用中检测和处理native内存泄漏问题,包括使用mallocdebug工具开启调试,获取native堆数据,以及解析native_heapdump_viewer.py脚本的工作原理和常见问题解决方法。
摘要由CSDN通过智能技术生成

当app发生native内存泄漏时,最为头疼,不过也有很成熟的工具来排查,比如malloc debug

一、开启malloc debug:

1、没有root的设备,使用wrap.sh脚本,并放到app/src/resources/lib/下(jniLibs下打包只会打包so文件,sh文件要放到resources下),如下:

2、有root的设备,那就更简单了,直接设置属性并重启应用即可:

adb shell setprop wrap.xxx '"LIBC_DEBUG_MALLOC_OPTIONS=backtrace"' 
adb shell killall xxx
adb shell am start xxx

xxx是你的应用包名 

注:打开malloc debug后应用会非常卡,所以不推荐使用如下方式打开所有应用的debug:

setprop libc.debug.malloc.program app_process
setprop libc.debug.malloc.options backtrace=64

二、获取native堆

adb shell am dumpheap -n xxx /data/local/tmp/xxx.native.txt
adb pull /data/local/tmp/xxx.native.txt

-n参数是重点,否则会dump java的堆

三、解析得数据

前面得到的xxx.native.txt是一堆地址,需要android源码提供一个脚本development/scripts/native_heapdump_viewer.py来解析成可读的txt或者html。

脚本有几个参数:

   $ python native_heapdump_viewer.py [options] native_heap.txt
      [--verbose]: verbose output
      [--html]: interactive html output
      [--reverse]: reverse the backtraces (start the tree from the leaves)
      [--symbols SYMBOL_DIR] SYMBOL_DIR is the directory containing the .so files with symbols.
                 Defaults to $ANDROID_PRODUCT_OUT/symbols
      [--app-symbols SYMBOL_DIR] SYMBOL_DIR is the directory containing the app APK and so files.
                 Defaults to the current directory.

其中:

--symbols是android系统的符号表,比如out/target/product/generic_x86_64/symbols/

--app-symbols很少有资料提到,其实是app自己的库,这个对我们意义更大

如果不传入--app-symbols,那么只能解析系统的符号表,对于app自己的so就无能为力了:

但是原生AOSP的native_heapdump_viewer.py有一个bug(Android 12里面反正没有修复),就是--app-symbols参数怎么传都不对,看help似乎是传一个apk或者so所在的目录,但是真传进去报错:

Traceback (most recent call last):
  File "development/scripts/native_heapdump_viewer.py", line 482, in <module>
    main()
  File "development/scripts/native_heapdump_viewer.py", line 453, in main
    args.app_symboldir)
  File "development/scripts/native_heapdump_viewer.py", line 253, in ParseNativeHeap
    mappings.append(GetMappingFromOffset(Mapping(start, end, offset, name), app_symboldir))
  File "development/scripts/native_heapdump_viewer.py", line 190, in GetMappingFromOffset
    opened_zip = zipfile.ZipFile(zip_name)
  File "/usr/lib/python2.7/zipfile.py", line 793, in __init__
    self._RealGetContents()
  File "/usr/lib/python2.7/zipfile.py", line 834, in _RealGetContents
    raise BadZipfile, "File is not a zip file"
zipfile.BadZipfile: File is not a zip file

看源码中报错的位置:

def GetMappingFromOffset(mapping, app_symboldir):
  """
  If the input mapping is a zip file, translate the contained uncompressed files and add mapping
  entries.

  This is done to handle symbols for the uncompressed .so files inside APKs. With the replaced
  mappings, the script looks up the .so files as separate files.
  """
  basename = os.path.basename(mapping.name)
  zip_name = app_symboldir + basename
  if os.path.isfile(zip_name):
    opened_zip = zipfile.ZipFile(zip_name)
    if opened_zip:
      # For all files in the zip, add mappings for the internal files.
      for file_info in opened_zip.infolist():
        # Only add stored files since it doesn't make sense to have PC into compressed ones.
        if file_info.compress_type == zipfile.ZIP_STORED:
          zip_header_entry_size = 30
          data_offset = (file_info.header_offset
              + zip_header_entry_size
              + len(file_info.filename)
              + len(file_info.extra)
              + len(file_info.comment))
          end_offset = data_offset + file_info.file_size
          if mapping.offset >= data_offset and mapping.offset < end_offset:
            # Round up the data_offset to the nearest page since the .so must be aligned.
            so_file_alignment = 4096
            data_offset += so_file_alignment - 1;
            data_offset -= data_offset % so_file_alignment;
            mapping.name = file_info.filename
            mapping.offset -= data_offset
            break
  return mapping

纳尼,它会根据basename(运行起来后可以发现是so的名称,如libmyapplication.so),那么一组合,是想当把so当成zip来解析?或者原意是希望把so放到一个zip、乃至apk内部?但是跑起来不是这么回事,毕竟参数是解析原始文件中类似:

726d3884e000-726d3884f000 rw-p 00047000 fe:04 8198 /data/app/xxx/lib/x86_64/libmyapplication.so

的内容得到的,感觉始终跟zip文件及后面的查找逻辑对不上,所以不知道怎么构造这个zip才能让它work。

简单改法:

  如果是so文件,直接将mapping.name赋值为basename,即libmyapplication.so,经过验证,可以work了:


对应的是专门写的一个内存泄漏的一段jni代码:

有高人有其他办法,欢迎指点。修改后的脚本在文章中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值