blob: d709f7e221784e09d20299243a3a9481dd691f7f [file] [log] [blame]
Ben Murdoch097c5b22016-05-18 11:27:45 +01001#!/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
8import collections
9import optparse
10import os
11import re
12import sys
13
14from pylib import constants
15from pylib.constants import host_paths
16
17# Uses symbol.py from third_party/android_platform, not python's.
18with host_paths.SysPath(
19 host_paths.ANDROID_PLATFORM_DEVELOPMENT_SCRIPTS_PATH,
20 position=0):
21 import symbol
22
23
24_RE_ASAN = re.compile(r'(.*?)(#\S*?)\s+(\S*?)\s+\((.*?)\+(.*?)\)')
25
26def _ParseAsanLogLine(line):
27 m = re.match(_RE_ASAN, line)
28 if not m:
29 return None
30 return {
31 'prefix': m.group(1),
32 'library': m.group(4),
33 'pos': m.group(2),
34 'rel_address': '%08x' % int(m.group(5), 16),
35 }
36
37
38def _FindASanLibraries():
39 asan_lib_dir = os.path.join(host_paths.DIR_SOURCE_ROOT,
40 'third_party', 'llvm-build',
41 'Release+Asserts', 'lib')
42 asan_libs = []
43 for src_dir, _, files in os.walk(asan_lib_dir):
44 asan_libs += [os.path.relpath(os.path.join(src_dir, f))
45 for f in files
46 if f.endswith('.so')]
47 return asan_libs
48
49
50def _TranslateLibPath(library, asan_libs):
51 for asan_lib in asan_libs:
52 if os.path.basename(library) == os.path.basename(asan_lib):
53 return '/' + asan_lib
54 # pylint: disable=no-member
55 return symbol.TranslateLibPath(library)
56
57
58def _Symbolize(asan_input):
59 asan_libs = _FindASanLibraries()
60 libraries = collections.defaultdict(list)
61 asan_lines = []
62 for asan_log_line in [a.rstrip() for a in asan_input]:
63 m = _ParseAsanLogLine(asan_log_line)
64 if m:
65 libraries[m['library']].append(m)
66 asan_lines.append({'raw_log': asan_log_line, 'parsed': m})
67
68 all_symbols = collections.defaultdict(dict)
69 for library, items in libraries.iteritems():
70 libname = _TranslateLibPath(library, asan_libs)
71 lib_relative_addrs = set([i['rel_address'] for i in items])
72 # pylint: disable=no-member
73 info_dict = symbol.SymbolInformationForSet(libname,
74 lib_relative_addrs,
75 True)
76 if info_dict:
77 all_symbols[library]['symbols'] = info_dict
78
79 for asan_log_line in asan_lines:
80 m = asan_log_line['parsed']
81 if not m:
82 print asan_log_line['raw_log']
83 continue
84 if (m['library'] in all_symbols and
85 m['rel_address'] in all_symbols[m['library']]['symbols']):
86 s = all_symbols[m['library']]['symbols'][m['rel_address']][0]
87 print '%s%s %s %s' % (m['prefix'], m['pos'], s[0], s[1])
88 else:
89 print asan_log_line['raw_log']
90
91
92def main():
93 parser = optparse.OptionParser()
94 parser.add_option('-l', '--logcat',
95 help='File containing adb logcat output with ASan stacks. '
96 'Use stdin if not specified.')
97 parser.add_option('--output-directory',
98 help='Path to the root build directory.')
99 options, _ = parser.parse_args()
100
101 if options.output_directory:
102 constants.SetOutputDirectory(options.output_directory)
103 # Do an up-front test that the output directory is known.
104 constants.CheckOutputDirectory()
105
106 if options.logcat:
107 asan_input = file(options.logcat, 'r')
108 else:
109 asan_input = sys.stdin
110 _Symbolize(asan_input.readlines())
111
112
113if __name__ == "__main__":
114 sys.exit(main())