1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*8975f5c5SAndroid Build Coastguard Worker# 3*8975f5c5SAndroid Build Coastguard Worker# Copyright 2013 The Chromium Authors 4*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 5*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file. 6*8975f5c5SAndroid Build Coastguard Worker 7*8975f5c5SAndroid Build Coastguard Worker 8*8975f5c5SAndroid Build Coastguard Workerimport argparse 9*8975f5c5SAndroid Build Coastguard Workerimport collections 10*8975f5c5SAndroid Build Coastguard Workerimport os 11*8975f5c5SAndroid Build Coastguard Workerimport re 12*8975f5c5SAndroid Build Coastguard Workerimport sys 13*8975f5c5SAndroid Build Coastguard Worker 14*8975f5c5SAndroid Build Coastguard Workerfrom pylib import constants 15*8975f5c5SAndroid Build Coastguard Workerfrom pylib.constants import host_paths 16*8975f5c5SAndroid Build Coastguard Worker 17*8975f5c5SAndroid Build Coastguard Worker# pylint: disable=wrong-import-order 18*8975f5c5SAndroid Build Coastguard Worker# Uses symbol.py from third_party/android_platform, not python's. 19*8975f5c5SAndroid Build Coastguard Workerwith host_paths.SysPath( 20*8975f5c5SAndroid Build Coastguard Worker host_paths.ANDROID_PLATFORM_DEVELOPMENT_SCRIPTS_PATH, 21*8975f5c5SAndroid Build Coastguard Worker position=0): 22*8975f5c5SAndroid Build Coastguard Worker import symbol 23*8975f5c5SAndroid Build Coastguard Worker 24*8975f5c5SAndroid Build Coastguard Worker 25*8975f5c5SAndroid Build Coastguard Worker_RE_ASAN = re.compile( 26*8975f5c5SAndroid Build Coastguard Worker r""" 27*8975f5c5SAndroid Build Coastguard Worker (?P<prefix>.*?) 28*8975f5c5SAndroid Build Coastguard Worker (?P<pos>\#\S*?) # position of the call in stack. 29*8975f5c5SAndroid Build Coastguard Worker # escape the char "#" due to the VERBOSE flag. 30*8975f5c5SAndroid Build Coastguard Worker \s+(\S*?)\s+ 31*8975f5c5SAndroid Build Coastguard Worker \( # match the char "(". 32*8975f5c5SAndroid Build Coastguard Worker (?P<lib>.*?) # library path. 33*8975f5c5SAndroid Build Coastguard Worker \+0[xX](?P<addr>.*?) # address of the symbol in hex. 34*8975f5c5SAndroid Build Coastguard Worker # the prefix "0x" is skipped. 35*8975f5c5SAndroid Build Coastguard Worker \) # match the char ")". 36*8975f5c5SAndroid Build Coastguard Worker """, re.VERBOSE) 37*8975f5c5SAndroid Build Coastguard Worker 38*8975f5c5SAndroid Build Coastguard Worker# This named tuple models a parsed Asan log line. 39*8975f5c5SAndroid Build Coastguard WorkerAsanParsedLine = collections.namedtuple('AsanParsedLine', 40*8975f5c5SAndroid Build Coastguard Worker 'prefix,library,pos,rel_address') 41*8975f5c5SAndroid Build Coastguard Worker 42*8975f5c5SAndroid Build Coastguard Worker# This named tuple models an Asan log line. 'raw' is the raw content 43*8975f5c5SAndroid Build Coastguard Worker# while 'parsed' is None or an AsanParsedLine instance. 44*8975f5c5SAndroid Build Coastguard WorkerAsanLogLine = collections.namedtuple('AsanLogLine', 'raw,parsed') 45*8975f5c5SAndroid Build Coastguard Worker 46*8975f5c5SAndroid Build Coastguard Workerdef _ParseAsanLogLine(line): 47*8975f5c5SAndroid Build Coastguard Worker """Parse line into corresponding AsanParsedLine value, if any, or None.""" 48*8975f5c5SAndroid Build Coastguard Worker m = re.match(_RE_ASAN, line) 49*8975f5c5SAndroid Build Coastguard Worker if not m: 50*8975f5c5SAndroid Build Coastguard Worker return None 51*8975f5c5SAndroid Build Coastguard Worker return AsanParsedLine(prefix=m.group('prefix'), 52*8975f5c5SAndroid Build Coastguard Worker library=m.group('lib'), 53*8975f5c5SAndroid Build Coastguard Worker pos=m.group('pos'), 54*8975f5c5SAndroid Build Coastguard Worker rel_address=int(m.group('addr'), 16)) 55*8975f5c5SAndroid Build Coastguard Worker 56*8975f5c5SAndroid Build Coastguard Worker 57*8975f5c5SAndroid Build Coastguard Workerdef _FindASanLibraries(): 58*8975f5c5SAndroid Build Coastguard Worker asan_lib_dir = os.path.join(host_paths.DIR_SOURCE_ROOT, 59*8975f5c5SAndroid Build Coastguard Worker 'third_party', 'llvm-build', 60*8975f5c5SAndroid Build Coastguard Worker 'Release+Asserts', 'lib') 61*8975f5c5SAndroid Build Coastguard Worker asan_libs = [] 62*8975f5c5SAndroid Build Coastguard Worker for src_dir, _, files in os.walk(asan_lib_dir): 63*8975f5c5SAndroid Build Coastguard Worker asan_libs += [os.path.relpath(os.path.join(src_dir, f)) 64*8975f5c5SAndroid Build Coastguard Worker for f in files 65*8975f5c5SAndroid Build Coastguard Worker if f.endswith('.so')] 66*8975f5c5SAndroid Build Coastguard Worker return asan_libs 67*8975f5c5SAndroid Build Coastguard Worker 68*8975f5c5SAndroid Build Coastguard Worker 69*8975f5c5SAndroid Build Coastguard Workerdef _TranslateLibPath(library, asan_libs): 70*8975f5c5SAndroid Build Coastguard Worker for asan_lib in asan_libs: 71*8975f5c5SAndroid Build Coastguard Worker if os.path.basename(library) == os.path.basename(asan_lib): 72*8975f5c5SAndroid Build Coastguard Worker return '/' + asan_lib 73*8975f5c5SAndroid Build Coastguard Worker # pylint: disable=no-member 74*8975f5c5SAndroid Build Coastguard Worker return symbol.TranslateLibPath(library) 75*8975f5c5SAndroid Build Coastguard Worker 76*8975f5c5SAndroid Build Coastguard Worker 77*8975f5c5SAndroid Build Coastguard Workerdef _PrintSymbolized(asan_input, arch): 78*8975f5c5SAndroid Build Coastguard Worker """Print symbolized logcat output for Asan symbols. 79*8975f5c5SAndroid Build Coastguard Worker 80*8975f5c5SAndroid Build Coastguard Worker Args: 81*8975f5c5SAndroid Build Coastguard Worker asan_input: list of input lines. 82*8975f5c5SAndroid Build Coastguard Worker arch: Target CPU architecture. 83*8975f5c5SAndroid Build Coastguard Worker """ 84*8975f5c5SAndroid Build Coastguard Worker asan_libs = _FindASanLibraries() 85*8975f5c5SAndroid Build Coastguard Worker 86*8975f5c5SAndroid Build Coastguard Worker # Maps library -> [ AsanParsedLine... ] 87*8975f5c5SAndroid Build Coastguard Worker libraries = collections.defaultdict(list) 88*8975f5c5SAndroid Build Coastguard Worker 89*8975f5c5SAndroid Build Coastguard Worker asan_log_lines = [] 90*8975f5c5SAndroid Build Coastguard Worker for line in asan_input: 91*8975f5c5SAndroid Build Coastguard Worker line = line.rstrip() 92*8975f5c5SAndroid Build Coastguard Worker parsed = _ParseAsanLogLine(line) 93*8975f5c5SAndroid Build Coastguard Worker if parsed: 94*8975f5c5SAndroid Build Coastguard Worker libraries[parsed.library].append(parsed) 95*8975f5c5SAndroid Build Coastguard Worker asan_log_lines.append(AsanLogLine(raw=line, parsed=parsed)) 96*8975f5c5SAndroid Build Coastguard Worker 97*8975f5c5SAndroid Build Coastguard Worker # Maps library -> { address -> [(symbol, location, obj_sym_with_offset)...] } 98*8975f5c5SAndroid Build Coastguard Worker all_symbols = collections.defaultdict(dict) 99*8975f5c5SAndroid Build Coastguard Worker 100*8975f5c5SAndroid Build Coastguard Worker for library, items in libraries.items(): 101*8975f5c5SAndroid Build Coastguard Worker libname = _TranslateLibPath(library, asan_libs) 102*8975f5c5SAndroid Build Coastguard Worker lib_relative_addrs = set(i.rel_address for i in items) 103*8975f5c5SAndroid Build Coastguard Worker # pylint: disable=no-member 104*8975f5c5SAndroid Build Coastguard Worker symbols_by_library = symbol.SymbolInformationForSet(libname, 105*8975f5c5SAndroid Build Coastguard Worker lib_relative_addrs, 106*8975f5c5SAndroid Build Coastguard Worker True, 107*8975f5c5SAndroid Build Coastguard Worker cpu_arch=arch) 108*8975f5c5SAndroid Build Coastguard Worker if symbols_by_library: 109*8975f5c5SAndroid Build Coastguard Worker all_symbols[library] = symbols_by_library 110*8975f5c5SAndroid Build Coastguard Worker 111*8975f5c5SAndroid Build Coastguard Worker for log_line in asan_log_lines: 112*8975f5c5SAndroid Build Coastguard Worker m = log_line.parsed 113*8975f5c5SAndroid Build Coastguard Worker if (m and m.library in all_symbols and 114*8975f5c5SAndroid Build Coastguard Worker m.rel_address in all_symbols[m.library]): 115*8975f5c5SAndroid Build Coastguard Worker # NOTE: all_symbols[lib][address] is a never-emtpy list of tuples. 116*8975f5c5SAndroid Build Coastguard Worker # NOTE: The documentation for SymbolInformationForSet() indicates 117*8975f5c5SAndroid Build Coastguard Worker # that usually one wants to display the last list item, not the first. 118*8975f5c5SAndroid Build Coastguard Worker # The code below takes the first, is this the best choice here? 119*8975f5c5SAndroid Build Coastguard Worker s = all_symbols[m.library][m.rel_address][0] 120*8975f5c5SAndroid Build Coastguard Worker symbol_name = s[0] 121*8975f5c5SAndroid Build Coastguard Worker symbol_location = s[1] 122*8975f5c5SAndroid Build Coastguard Worker print('%s%s %s %s @ \'%s\'' % 123*8975f5c5SAndroid Build Coastguard Worker (m.prefix, m.pos, hex(m.rel_address), symbol_name, symbol_location)) 124*8975f5c5SAndroid Build Coastguard Worker else: 125*8975f5c5SAndroid Build Coastguard Worker print(log_line.raw) 126*8975f5c5SAndroid Build Coastguard Worker 127*8975f5c5SAndroid Build Coastguard Worker 128*8975f5c5SAndroid Build Coastguard Workerdef main(): 129*8975f5c5SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 130*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('-l', 131*8975f5c5SAndroid Build Coastguard Worker '--logcat', 132*8975f5c5SAndroid Build Coastguard Worker help='File containing adb logcat output with ASan ' 133*8975f5c5SAndroid Build Coastguard Worker 'stacks. Use stdin if not specified.') 134*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--output-directory', 135*8975f5c5SAndroid Build Coastguard Worker help='Path to the root build directory.') 136*8975f5c5SAndroid Build Coastguard Worker parser.add_argument('--arch', default='arm', help='CPU architecture name') 137*8975f5c5SAndroid Build Coastguard Worker args = parser.parse_args() 138*8975f5c5SAndroid Build Coastguard Worker 139*8975f5c5SAndroid Build Coastguard Worker if args.output_directory: 140*8975f5c5SAndroid Build Coastguard Worker constants.SetOutputDirectory(args.output_directory) 141*8975f5c5SAndroid Build Coastguard Worker # Do an up-front test that the output directory is known. 142*8975f5c5SAndroid Build Coastguard Worker constants.CheckOutputDirectory() 143*8975f5c5SAndroid Build Coastguard Worker 144*8975f5c5SAndroid Build Coastguard Worker if args.logcat: 145*8975f5c5SAndroid Build Coastguard Worker asan_input = open(args.logcat, 'r') 146*8975f5c5SAndroid Build Coastguard Worker else: 147*8975f5c5SAndroid Build Coastguard Worker asan_input = sys.stdin 148*8975f5c5SAndroid Build Coastguard Worker 149*8975f5c5SAndroid Build Coastguard Worker _PrintSymbolized(asan_input.readlines(), args.arch) 150*8975f5c5SAndroid Build Coastguard Worker 151*8975f5c5SAndroid Build Coastguard Worker 152*8975f5c5SAndroid Build Coastguard Workerif __name__ == "__main__": 153*8975f5c5SAndroid Build Coastguard Worker sys.exit(main()) 154