summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/build/android/asan_symbolize.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/build/android/asan_symbolize.py')
-rwxr-xr-xthird_party/libwebrtc/build/android/asan_symbolize.py151
1 files changed, 151 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/android/asan_symbolize.py b/third_party/libwebrtc/build/android/asan_symbolize.py
new file mode 100755
index 0000000000..60b00d0049
--- /dev/null
+++ b/third_party/libwebrtc/build/android/asan_symbolize.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python3
+#
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from __future__ import print_function
+
+import collections
+import optparse
+import os
+import re
+import sys
+
+from pylib import constants
+from pylib.constants import host_paths
+
+# pylint: disable=wrong-import-order
+# Uses symbol.py from third_party/android_platform, not python's.
+with host_paths.SysPath(
+ host_paths.ANDROID_PLATFORM_DEVELOPMENT_SCRIPTS_PATH,
+ position=0):
+ import symbol
+
+
+_RE_ASAN = re.compile(
+ r"""
+ (?P<prefix>.*?)
+ (?P<pos>\#\S*?) # position of the call in stack.
+ # escape the char "#" due to the VERBOSE flag.
+ \s+(\S*?)\s+
+ \( # match the char "(".
+ (?P<lib>.*?) # library path.
+ \+0[xX](?P<addr>.*?) # address of the symbol in hex.
+ # the prefix "0x" is skipped.
+ \) # match the char ")".
+ """, re.VERBOSE)
+
+# This named tuple models a parsed Asan log line.
+AsanParsedLine = collections.namedtuple('AsanParsedLine',
+ 'prefix,library,pos,rel_address')
+
+# This named tuple models an Asan log line. 'raw' is the raw content
+# while 'parsed' is None or an AsanParsedLine instance.
+AsanLogLine = collections.namedtuple('AsanLogLine', 'raw,parsed')
+
+def _ParseAsanLogLine(line):
+ """Parse line into corresponding AsanParsedLine value, if any, or None."""
+ m = re.match(_RE_ASAN, line)
+ if not m:
+ return None
+ return AsanParsedLine(prefix=m.group('prefix'),
+ library=m.group('lib'),
+ pos=m.group('pos'),
+ rel_address='%08x' % int(m.group('addr'), 16))
+
+
+def _FindASanLibraries():
+ asan_lib_dir = os.path.join(host_paths.DIR_SOURCE_ROOT,
+ 'third_party', 'llvm-build',
+ 'Release+Asserts', 'lib')
+ asan_libs = []
+ for src_dir, _, files in os.walk(asan_lib_dir):
+ asan_libs += [os.path.relpath(os.path.join(src_dir, f))
+ for f in files
+ if f.endswith('.so')]
+ return asan_libs
+
+
+def _TranslateLibPath(library, asan_libs):
+ for asan_lib in asan_libs:
+ if os.path.basename(library) == os.path.basename(asan_lib):
+ return '/' + asan_lib
+ # pylint: disable=no-member
+ return symbol.TranslateLibPath(library)
+
+
+def _PrintSymbolized(asan_input, arch):
+ """Print symbolized logcat output for Asan symbols.
+
+ Args:
+ asan_input: list of input lines.
+ arch: Target CPU architecture.
+ """
+ asan_libs = _FindASanLibraries()
+
+ # Maps library -> [ AsanParsedLine... ]
+ libraries = collections.defaultdict(list)
+
+ asan_log_lines = []
+ for line in asan_input:
+ line = line.rstrip()
+ parsed = _ParseAsanLogLine(line)
+ if parsed:
+ libraries[parsed.library].append(parsed)
+ asan_log_lines.append(AsanLogLine(raw=line, parsed=parsed))
+
+ # Maps library -> { address -> [(symbol, location, obj_sym_with_offset)...] }
+ all_symbols = collections.defaultdict(dict)
+
+ for library, items in libraries.items():
+ libname = _TranslateLibPath(library, asan_libs)
+ lib_relative_addrs = set([i.rel_address for i in items])
+ # pylint: disable=no-member
+ info_dict = symbol.SymbolInformationForSet(libname,
+ lib_relative_addrs,
+ True,
+ cpu_arch=arch)
+ if info_dict:
+ all_symbols[library] = info_dict
+
+ for log_line in asan_log_lines:
+ m = log_line.parsed
+ if (m and m.library in all_symbols and
+ m.rel_address in all_symbols[m.library]):
+ # NOTE: all_symbols[lib][address] is a never-emtpy list of tuples.
+ # NOTE: The documentation for SymbolInformationForSet() indicates
+ # that usually one wants to display the last list item, not the first.
+ # The code below takes the first, is this the best choice here?
+ s = all_symbols[m.library][m.rel_address][0]
+ print('%s%s %s %s' % (m.prefix, m.pos, s[0], s[1]))
+ else:
+ print(log_line.raw)
+
+
+def main():
+ parser = optparse.OptionParser()
+ parser.add_option('-l', '--logcat',
+ help='File containing adb logcat output with ASan stacks. '
+ 'Use stdin if not specified.')
+ parser.add_option('--output-directory',
+ help='Path to the root build directory.')
+ parser.add_option('--arch', default='arm',
+ help='CPU architecture name')
+ options, _ = parser.parse_args()
+
+ if options.output_directory:
+ constants.SetOutputDirectory(options.output_directory)
+ # Do an up-front test that the output directory is known.
+ constants.CheckOutputDirectory()
+
+ if options.logcat:
+ asan_input = open(options.logcat, 'r')
+ else:
+ asan_input = sys.stdin
+
+ _PrintSymbolized(asan_input.readlines(), options.arch)
+
+
+if __name__ == "__main__":
+ sys.exit(main())