blob: 7320bd56c535cdd8e43c13884e1f55556958f258 [file] [log] [blame]
Greg Hartmane85dd632018-11-13 18:34:13 -08001#!/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())