#!/usr/bin/python
#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0(the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Use addr2line to interpret tombstone contents
The defaults should work if this is run after running lunch.
"""
from __future__ import print_function
import argparse
import collections
import functools
import multiprocessing
import os
import re
import subprocess
import sys
# Patterns of things that we might want to match.
patterns = [
(re.compile('(.* pc )([0-9a-f]+) +([^ ]+) .*'), 1, 3, 2),
(re.compile('(.*)#[0-9]+ 0x[0-9a-f]+ +\((.*)\+0x([0-9a-f]+)\)'), 1, 2, 3)]
LookupInfo = collections.namedtuple('LookupInfo',
['line_number', 'details', 'file_name'])
def lookup_addr(args, object_path, address):
try:
if object_path[0] == os.path.sep:
object_path = object_path[1:]
parms = [args.addr2line, '-e',
os.path.join(args.symbols, object_path), address]
details = subprocess.check_output(parms).strip().split(':')
return LookupInfo(
line_number=details[-1],
details=details,
file_name=':'.join(details[:-1]))
except subprocess.CalledProcessError:
return None
def simple_match(line, info, indent, out_file):
print('{} // From {}:{}'.format(
line, info.file_name, info.line_number), file=out_file)
def source_match(line, info, indent, out_file):
source = ''
try:
with open(info.file_name, 'r') as f:
for i in range(int(info.line_number.split(' ')[0])):
source = f.readline()
# Fall back to the simple formatter on any error
except Exception:
simple_match(line, info, indent, out_file)
return
print(line, file=out_file)
print('{}// From {}:{}'.format(
' ' * indent, info.file_name, info.line_number), file=out_file)
print('{} {}'.format(' ' * indent, ' '.join(source.strip().split())),
file=out_file)
def process(in_file, out_file, args):
for line in in_file:
line = line.rstrip()
groups = None
for p in patterns:
groups = p[0].match(line)
if groups:
break
info = None
if groups is not None:
info = lookup_addr(args, groups.group(p[2]), groups.group(p[3]))
if info is None:
print(line, file=out_file)
continue
if args.source:
source_match(line, info, len(groups.group(p[1])), out_file)
else:
simple_match(line, info, len(groups.group(p[1])), out_file)
def process_file(path, args):
with open(path + args.suffix, 'w') as out_file:
with open(path, 'r') as in_file:
process(in_file, out_file, args)
def common_arg_parser():
parser = argparse.ArgumentParser(description=
'Add line information to a tombstone')
parser.add_argument('--addr2line', type=str,
help='Path to addr2line',
default=os.path.join(
os.environ.get('ANDROID_TOOLCHAIN', ''),
'x86_64-linux-android-addr2line'))
parser.add_argument('files', metavar='FILE', type=str, nargs='+',
help='a list of files to process')
parser.add_argument('--jobs', type=int, default=32,
help='Number of parallel jobs to run')
parser.add_argument('--source', default=False, action='store_true',
help='Attempt to print the source')
parser.add_argument('--suffix', type=str, default='.txt',
help='Suffix to add to the processed file')
return parser
def process_all(args):
multiprocessing.Pool(32).map(functools.partial(process_file, args=args),
args.files)
if __name__ == '__main__':
parser = common_arg_parser()
parser.add_argument('--symbols', type=str,
help='Path to the symbols',
default=os.path.join(
os.environ.get('ANDROID_PRODUCT_OUT', ''), 'symbols'))
process_all(parser.parse_args())