1 #!/usr/bin/env python 2 # 3 # Copyright 2013 The Chromium Authors. All rights reserved. 4 # Use of this source code is governed by a BSD-style license that can be 5 # found in the LICENSE file. 6 7 """Symbolizes stack traces generated by Chromium for Android. 8 9 Sample usage: 10 adb logcat chromium:V | symbolize.py 11 """ 12 13 import os 14 import re 15 import sys 16 17 from pylib import constants 18 19 # Uses symbol.py from third_party/android_platform, not python's. 20 sys.path.insert(0, 21 os.path.join(constants.DIR_SOURCE_ROOT, 22 'third_party/android_platform/development/scripts')) 23 import symbol 24 25 # Sample output from base/debug/stack_trace_android.cc 26 #00 0x693cd34f /path/to/some/libfoo.so+0x0007434f 27 TRACE_LINE = re.compile('(?P<frame>\#[0-9]+ 0x[0-9a-f]{8,8}) ' 28 '(?P<lib>[^+]+)\+0x(?P<addr>[0-9a-f]{8,8})') 29 30 class Symbolizer(object): 31 def __init__(self, output): 32 self._output = output 33 34 def write(self, data): 35 while True: 36 match = re.search(TRACE_LINE, data) 37 if not match: 38 self._output.write(data) 39 break 40 41 frame = match.group('frame') 42 lib = match.group('lib') 43 addr = match.group('addr') 44 45 # TODO(scherkus): Doing a single lookup per line is pretty slow, 46 # especially with larger libraries. Consider caching strategies such as: 47 # 1) Have Python load the libraries and do symbol lookups instead of 48 # calling out to addr2line each time. 49 # 2) Have Python keep multiple addr2line instances open as subprocesses, 50 # piping addresses and reading back symbols as we find them 51 # 3) Read ahead the entire stack trace until we find no more, then batch 52 # the symbol lookups. 53 # 54 # TODO(scherkus): These results are memoized, which could result in 55 # incorrect lookups when running this script on long-lived instances 56 # (e.g., adb logcat) when doing incremental development. Consider clearing 57 # the cache when modification timestamp of libraries change. 58 sym = symbol.SymbolInformation(lib, addr, False)[0][0] 59 60 if not sym: 61 post = match.end('addr') 62 self._output.write(data[:post]) 63 data = data[post:] 64 continue 65 66 pre = match.start('frame') 67 post = match.end('addr') 68 69 self._output.write(data[:pre]) 70 self._output.write(frame) 71 self._output.write(' ') 72 self._output.write(sym) 73 74 data = data[post:] 75 76 def flush(self): 77 self._output.flush() 78 79 80 def main(): 81 symbolizer = Symbolizer(sys.stdout) 82 for line in sys.stdin: 83 symbolizer.write(line) 84 symbolizer.flush() 85 86 87 if __name__ == '__main__': 88 main() 89