diff options
Diffstat (limited to 'scripts/native_heapdump_viewer.py')
-rwxr-xr-x | scripts/native_heapdump_viewer.py | 59 |
1 files changed, 51 insertions, 8 deletions
diff --git a/scripts/native_heapdump_viewer.py b/scripts/native_heapdump_viewer.py index b6339e884..b5fad3e25 100755 --- a/scripts/native_heapdump_viewer.py +++ b/scripts/native_heapdump_viewer.py @@ -21,6 +21,7 @@ import os.path import re import subprocess import sys +import zipfile class Args: _usage = """ @@ -41,6 +42,8 @@ Usage: [--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. This outputs a file with lines of the form: 5831776 29.09% 100.00% 10532 71b07bc0b0 /system/lib64/libandroid_runtime.so Typeface_createFromArray frameworks/base/core/jni/android/graphics/Typeface.cpp:68 @@ -60,13 +63,17 @@ Usage: self.symboldir = product_out + "/symbols" else: self.symboldir = "./symbols" + self.app_symboldir = "" i = 1 extra_args = [] while i < len(sys.argv): if sys.argv[i] == "--symbols": i += 1 - self.symboldir = args[i] + self.symboldir = sys.argv[i] + "/" + elif sys.argv[i] == "--app-symbols": + i += 1 + self.app_symboldir = sys.argv[i] + "/" elif sys.argv[i] == "--verbose": self.verbose = True elif sys.argv[i] == "--html": @@ -166,7 +173,37 @@ def GetNumFieldValid(native_heap): else: return True -def ParseNativeHeap(native_heap, reverse_frames, num_field_valid): +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: + mapping.name = file_info.filename + mapping.offset = data_offset - mapping.offset + break + return mapping + +def ParseNativeHeap(native_heap, reverse_frames, num_field_valid, app_symboldir): """Parse the native heap into backtraces, maps. Returns two lists, the first is a list of all of the backtraces, the @@ -198,12 +235,14 @@ def ParseNativeHeap(native_heap, reverse_frames, num_field_valid): # 720de01000-720ded7000 r-xp 00000000 fd:00 495 /system/lib64/libc.so m = re_map.match(line) if m: + # Offset of mapping start start = int(m.group('start'), 16) + # Offset of mapping end end = int(m.group('end'), 16) + # Offset within file that is mapped offset = int(m.group('offset'), 16) name = m.group('name') - mappings.append(Mapping(start, end, offset, name)) - + mappings.append(GetMappingFromOffset(Mapping(start, end, offset, name), app_symboldir)) return backtraces, mappings def FindMapping(mappings, addr): @@ -227,7 +266,7 @@ def FindMapping(mappings, addr): return mappings[mid] -def ResolveAddrs(html_output, symboldir, backtraces, mappings): +def ResolveAddrs(html_output, symboldir, app_symboldir, backtraces, mappings): """Resolve address libraries and offsets. addr_offsets maps addr to .so file offset @@ -259,7 +298,9 @@ def ResolveAddrs(html_output, symboldir, backtraces, mappings): print "Resolving symbols using directory %s..." % symboldir for lib in addrs_by_lib: - sofile = symboldir + lib + sofile = app_symboldir + lib + if not os.path.isfile(sofile): + sofile = symboldir + lib if os.path.isfile(sofile): file_offset = 0 result = subprocess.check_output(["objdump", "-w", "-j", ".text", "-h", sofile]) @@ -393,9 +434,11 @@ def main(): num_field_valid = GetNumFieldValid(args.native_heap) - backtraces, mappings = ParseNativeHeap(args.native_heap, args.reverse_frames, num_field_valid) + backtraces, mappings = ParseNativeHeap(args.native_heap, args.reverse_frames, num_field_valid, + args.app_symboldir) # Resolve functions and line numbers - resolved_addrs = ResolveAddrs(args.html_output, args.symboldir, backtraces, mappings) + resolved_addrs = ResolveAddrs(args.html_output, args.symboldir, args.app_symboldir, backtraces, + mappings) app = AddrInfo("APP") zygote = AddrInfo("ZYGOTE") |