1#!/usr/bin/python 2# 3# Copyright (C) 2018 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0(the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17"""Use addr2line to interpret tombstone contents 18 19The defaults should work if this is run after running lunch. 20""" 21 22from __future__ import print_function 23import argparse 24import collections 25import functools 26import multiprocessing 27import os 28import re 29import subprocess 30import sys 31 32 33# Patterns of things that we might want to match. 34 35patterns = [ 36 (re.compile('(.* pc )([0-9a-f]+) +([^ ]+) .*'), 1, 3, 2), 37 (re.compile('(.*)#[0-9]+ 0x[0-9a-f]+ +\((.*)\+0x([0-9a-f]+)\)'), 1, 2, 3)] 38 39 40LookupInfo = collections.namedtuple('LookupInfo', 41 ['line_number', 'details', 'file_name']) 42 43 44def lookup_addr(args, object_path, address): 45 try: 46 if object_path[0] == os.path.sep: 47 object_path = object_path[1:] 48 parms = [args.addr2line, '-e', 49 os.path.join(args.symbols, object_path), address] 50 details = subprocess.check_output(parms).strip().split(':') 51 return LookupInfo( 52 line_number=details[-1], 53 details=details, 54 file_name=':'.join(details[:-1])) 55 except subprocess.CalledProcessError: 56 return None 57 58 59def simple_match(line, info, indent, out_file): 60 print('{} // From {}:{}'.format( 61 line, info.file_name, info.line_number), file=out_file) 62 63 64def source_match(line, info, indent, out_file): 65 source = '' 66 try: 67 with open(info.file_name, 'r') as f: 68 for i in range(int(info.line_number.split(' ')[0])): 69 source = f.readline() 70 # Fall back to the simple formatter on any error 71 except Exception: 72 simple_match(line, info, indent, out_file) 73 return 74 print(line, file=out_file) 75 print('{}// From {}:{}'.format( 76 ' ' * indent, info.file_name, info.line_number), file=out_file) 77 print('{} {}'.format(' ' * indent, ' '.join(source.strip().split())), 78 file=out_file) 79 80 81def process(in_file, out_file, args): 82 for line in in_file: 83 line = line.rstrip() 84 groups = None 85 for p in patterns: 86 groups = p[0].match(line) 87 if groups: 88 break 89 info = None 90 if groups is not None: 91 info = lookup_addr(args, groups.group(p[2]), groups.group(p[3])) 92 if info is None: 93 print(line, file=out_file) 94 continue 95 if args.source: 96 source_match(line, info, len(groups.group(p[1])), out_file) 97 else: 98 simple_match(line, info, len(groups.group(p[1])), out_file) 99 100 101def process_file(path, args): 102 with open(path + args.suffix, 'w') as out_file: 103 with open(path, 'r') as in_file: 104 process(in_file, out_file, args) 105 106 107def common_arg_parser(): 108 parser = argparse.ArgumentParser(description= 109 'Add line information to a tombstone') 110 parser.add_argument('--addr2line', type=str, 111 help='Path to addr2line', 112 default=os.path.join( 113 os.environ.get('ANDROID_TOOLCHAIN', ''), 114 'x86_64-linux-android-addr2line')) 115 parser.add_argument('files', metavar='FILE', type=str, nargs='+', 116 help='a list of files to process') 117 parser.add_argument('--jobs', type=int, default=32, 118 help='Number of parallel jobs to run') 119 parser.add_argument('--source', default=False, action='store_true', 120 help='Attempt to print the source') 121 parser.add_argument('--suffix', type=str, default='.txt', 122 help='Suffix to add to the processed file') 123 return parser 124 125 126 127def process_all(args): 128 multiprocessing.Pool(32).map(functools.partial(process_file, args=args), 129 args.files) 130 131 132if __name__ == '__main__': 133 parser = common_arg_parser() 134 parser.add_argument('--symbols', type=str, 135 help='Path to the symbols', 136 default=os.path.join( 137 os.environ.get('ANDROID_PRODUCT_OUT', ''), 'symbols')) 138 process_all(parser.parse_args()) 139