Commit 5b391ee5 authored by Derek Cheng's avatar Derek Cheng Committed by Commit Bot

Revert "Use llvm symbolizer for stack script."

This reverts commit b6e038a7.

Reason for revert: Broke stack_tool_with_logcat_dump on WebKit Android (Nexus4): https://uberchromegw.corp.google.com/i/chromium.webkit/builders/WebKit%20Android%20%28Nexus4%29/builds/72002

Original change's description:
> Use llvm symbolizer for stack script.
> 
> llvm symbolizer is a more efficient symbolizer than addr2line, objdump,
> etc.
> 
> In this cl, I 1)created a wrapper instance to interact with
> llvm symbolizer, 2)made the stack script to use llvm symbolizer instance,
> and then 3)added llvm symbolizer into isolated inputs.
> 
> Bug: 774267
> Change-Id: I971fb808b97f3a569eb9615f99efa41e3a56f3cb
> Reviewed-on: https://chromium-review.googlesource.com/789376
> Reviewed-by: agrieve <agrieve@chromium.org>
> Reviewed-by: Dirk Pranke <dpranke@chromium.org>
> Commit-Queue: Zhiling Huang <hzl@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#521820}

TBR=dpranke@chromium.org,agrieve@chromium.org,hzl@chromium.org,bpastene@chromium.org,jbudorick@chromium.org

Change-Id: Ieebbcb9527dafd25a0fc74d2826a31162495c86e
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 774267
Reviewed-on: https://chromium-review.googlesource.com/809941Reviewed-by: default avatarDerek Cheng <imcheng@chromium.org>
Commit-Queue: Derek Cheng <imcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521898}
parent 51357dc1
...@@ -13,5 +13,9 @@ group("stack_py") { ...@@ -13,5 +13,9 @@ group("stack_py") {
sources = _py_files sources = _py_files
data = sources data = sources
data += [ "//third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer" ] data += [
"${android_tool_prefix}addr2line",
"${android_tool_prefix}objdump",
"${android_tool_prefix}c++filt",
]
} }
...@@ -40,10 +40,6 @@ Added code to capture java stderr for better handling of native->java crashes. ...@@ -40,10 +40,6 @@ Added code to capture java stderr for better handling of native->java crashes.
Fixed invalid using decl in logging header debug.h Fixed invalid using decl in logging header debug.h
Only attempt to symbolize with ELF libraries. Only attempt to symbolize with ELF libraries.
Changed the stack script to use llvm symbolizer instead of addr2line,
objdump, etc, since llvm symbolizer is more efficient in finding
function names, line numbers etc.
Android relocation packing tool details: Android relocation packing tool details:
Copy sources from AOSP bionic/tools/relocation_packer Copy sources from AOSP bionic/tools/relocation_packer
Remove scripts that regenerate golden test data (not relevant here) Remove scripts that regenerate golden test data (not relevant here)
......
...@@ -31,13 +31,8 @@ import sys ...@@ -31,13 +31,8 @@ import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), sys.path.insert(0, os.path.join(os.path.dirname(__file__),
os.pardir, os.pardir, os.pardir, os.pardir, os.pardir, os.pardir, os.pardir, os.pardir,
'build', 'android')) 'build', 'android'))
from pylib import constants from pylib import constants
sys.path.insert(0, os.path.join(os.path.dirname(__file__),
os.pardir, os.pardir, os.pardir, os.pardir,
'tools', 'python'))
import llvm_symbolizer
DEFAULT_SYMROOT='/tmp/symbols' DEFAULT_SYMROOT='/tmp/symbols'
# From: https://source.android.com/source/build-numbers.html # From: https://source.android.com/source/build-numbers.html
...@@ -230,12 +225,10 @@ def main(argv): ...@@ -230,12 +225,10 @@ def main(argv):
print ("Reading Android symbols from: " print ("Reading Android symbols from: "
+ os.path.normpath(symbol.SYMBOLS_DIR)) + os.path.normpath(symbol.SYMBOLS_DIR))
chrome_search_path = symbol.GetLibrarySearchPaths() chrome_search_path = symbol.GetLibrarySearchPaths()
print ("Searching for Chrome symbols from within: "
with llvm_symbolizer.LLVMSymbolizer() as symbolizer: + ':'.join((os.path.normpath(d) for d in chrome_search_path)))
print ("Searching for Chrome symbols from within: " stack_core.ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome,
+ ':'.join((os.path.normpath(d) for d in chrome_search_path))) arch_defined)
stack_core.ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome,
arch_defined, symbolizer)
if rootdir: if rootdir:
# be a good citizen and clean up...os.rmdir and os.removedirs() don't work # be a good citizen and clean up...os.rmdir and os.removedirs() don't work
......
...@@ -158,7 +158,7 @@ def PrintDivider(): ...@@ -158,7 +158,7 @@ def PrintDivider():
print print
print '-----------------------------------------------------\n' print '-----------------------------------------------------\n'
def ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome, arch_defined, llvm_symbolizer): def ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome, arch_defined):
"""Convert strings containing native crash to a stack.""" """Convert strings containing native crash to a stack."""
InitWidthRelatedLineMatchers() InitWidthRelatedLineMatchers()
...@@ -189,7 +189,7 @@ def ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome, arch_define ...@@ -189,7 +189,7 @@ def ConvertTrace(lines, load_vaddrs, more_info, fallback_monochrome, arch_define
print ('Find ABI:' + arch) print ('Find ABI:' + arch)
symbol.ARCH = arch symbol.ARCH = arch
ResolveCrashSymbol(list(useful_log), more_info, llvm_symbolizer) ResolveCrashSymbol(list(useful_log), more_info)
end = time.time() end = time.time()
logging.debug('Finished resolving symbols. Elapsed time: %.4fs', logging.debug('Finished resolving symbols. Elapsed time: %.4fs',
(end - start)) (end - start))
...@@ -304,7 +304,7 @@ class PreProcessLog: ...@@ -304,7 +304,7 @@ class PreProcessLog:
useful_log.append(line) useful_log.append(line)
return useful_log, self._so_dirs return useful_log, self._so_dirs
def ResolveCrashSymbol(lines, more_info, llvm_symbolizer): def ResolveCrashSymbol(lines, more_info):
"""Convert unicode strings which contains native crash to a stack """Convert unicode strings which contains native crash to a stack
""" """
...@@ -313,6 +313,39 @@ def ResolveCrashSymbol(lines, more_info, llvm_symbolizer): ...@@ -313,6 +313,39 @@ def ResolveCrashSymbol(lines, more_info, llvm_symbolizer):
last_frame = -1 last_frame = -1
pid = -1 pid = -1
# It is faster to get symbol information with a single call rather than with
# separate calls for each line. Since symbol.SymbolInformation caches results,
# we can extract all the addresses that we will want symbol information for
# from the log and call symbol.SymbolInformation so that the results are
# cached in the following lookups.
code_addresses = {}
# Collects all java exception lines, keyed by pid for later output during
# native crash handling.
java_stderr_by_pid = {}
for line in lines:
lib, address = None, None
match = _TRACE_LINE.match(line) or _DEBUG_TRACE_LINE.match(line)
if match:
address, lib = match.group('address', 'lib')
match = _VALUE_LINE.match(line)
if match and not _CODE_LINE.match(line):
(_0, _1, address, lib, _2, _3) = match.groups()
if lib:
code_addresses.setdefault(lib, set()).add(address)
java_stderr_match = _JAVA_STDERR_LINE.search(line)
if java_stderr_match:
pid, msg = java_stderr_match.groups()
java_stderr_by_pid.setdefault(pid, []).append(msg)
for lib in code_addresses:
symbol.SymbolInformationForSet(
symbol.TranslateLibPath(lib), code_addresses[lib], more_info)
for line in lines: for line in lines:
# AndroidFeedback adds zero width spaces into its crash reports. These # AndroidFeedback adds zero width spaces into its crash reports. These
# should be removed or the regular expresssions will fail to match. # should be removed or the regular expresssions will fail to match.
...@@ -377,19 +410,25 @@ def ResolveCrashSymbol(lines, more_info, llvm_symbolizer): ...@@ -377,19 +410,25 @@ def ResolveCrashSymbol(lines, more_info, llvm_symbolizer):
logging.debug('Identified lib: %s' % area) logging.debug('Identified lib: %s' % area)
# If a calls b which further calls c and c is inlined to b, we want to # If a calls b which further calls c and c is inlined to b, we want to
# display "a -> b -> c" in the stack trace instead of just "a -> c" # display "a -> b -> c" in the stack trace instead of just "a -> c"
# To use llvm symbolizer, the hexadecimal address has to start with 0x. info = symbol.SymbolInformation(area, code_addr, more_info)
info = llvm_symbolizer.GetSymbolInformation(
os.path.join(symbol.SYMBOLS_DIR, symbol.TranslateLibPath(area)),
'0x' + code_addr)
logging.debug('symbol information: %s' % info) logging.debug('symbol information: %s' % info)
nest_count = len(info) - 1 nest_count = len(info) - 1
for source_symbol, source_location in info: for (source_symbol, source_location, object_symbol_with_offset) in info:
if not source_symbol:
if symbol_present:
source_symbol = symbol.CallCppFilt(symbol_name)
else:
source_symbol = UNKNOWN
if not source_location:
source_location = area
if nest_count > 0: if nest_count > 0:
nest_count = nest_count - 1 nest_count = nest_count - 1
trace_lines.append(('v------>', source_symbol, source_location)) trace_lines.append(('v------>', source_symbol, source_location))
else: else:
if not object_symbol_with_offset:
object_symbol_with_offset = source_symbol
trace_lines.append((code_addr, trace_lines.append((code_addr,
source_symbol, object_symbol_with_offset,
source_location)) source_location))
match = _VALUE_LINE.match(line) match = _VALUE_LINE.match(line)
if match: if match:
...@@ -397,14 +436,20 @@ def ResolveCrashSymbol(lines, more_info, llvm_symbolizer): ...@@ -397,14 +436,20 @@ def ResolveCrashSymbol(lines, more_info, llvm_symbolizer):
if area == UNKNOWN or area == HEAP or area == STACK or not area: if area == UNKNOWN or area == HEAP or area == STACK or not area:
value_lines.append((addr, value, '', area)) value_lines.append((addr, value, '', area))
else: else:
info = llvm_symbolizer.GetSymbolInformation( info = symbol.SymbolInformation(area, value, more_info)
os.path.join(symbol.SYMBOLS_DIR, symbol.TranslateLibPath(area)), (source_symbol, source_location, object_symbol_with_offset) = info.pop()
'0x' + code_addr) if not source_symbol:
source_symbol, source_location = info.pop() if symbol_present:
source_symbol = symbol.CallCppFilt(symbol_name)
else:
source_symbol = UNKNOWN
if not source_location:
source_location = area
if not object_symbol_with_offset:
object_symbol_with_offset = source_symbol
value_lines.append((addr, value_lines.append((addr,
value, value,
source_symbol, object_symbol_with_offset,
source_location)) source_location))
java_lines = [] java_lines = []
......
...@@ -69,6 +69,91 @@ def Uname(): ...@@ -69,6 +69,91 @@ def Uname():
return "linux-x86" return "linux-x86"
return uname return uname
def ToolPath(tool, toolchain_info=None):
"""Return a full qualified path to the specified tool"""
# ToolPath looks for the tools in the completely incorrect directory.
# This looks in the checked in android_tools.
if ARCH == "arm":
toolchain_source = "arm-linux-androideabi-4.9"
toolchain_prefix = "arm-linux-androideabi"
ndk = "ndk"
elif ARCH == "arm64":
toolchain_source = "aarch64-linux-android-4.9"
toolchain_prefix = "aarch64-linux-android"
ndk = "ndk"
elif ARCH == "x86":
toolchain_source = "x86-4.9"
toolchain_prefix = "i686-linux-android"
ndk = "ndk"
elif ARCH == "x86_64" or ARCH == "x64":
toolchain_source = "x86_64-4.9"
toolchain_prefix = "x86_64-linux-android"
ndk = "ndk"
elif ARCH == "mips":
toolchain_source = "mipsel-linux-android-4.9"
toolchain_prefix = "mipsel-linux-android"
ndk = "ndk"
else:
raise Exception("Could not find tool chain for " + ARCH)
toolchain_subdir = (
"third_party/android_tools/%s/toolchains/%s/prebuilt/linux-x86_64/bin" %
(ndk, toolchain_source))
return os.path.join(CHROME_SRC,
toolchain_subdir,
toolchain_prefix + "-" + tool)
def FindToolchain():
"""Look for the latest available toolchain
Args:
None
Returns:
A pair of strings containing toolchain label and target prefix.
"""
global TOOLCHAIN_INFO
if TOOLCHAIN_INFO is not None:
return TOOLCHAIN_INFO
## Known toolchains, newer ones in the front.
gcc_version = "4.9"
if ARCH == "arm64":
known_toolchains = [
("aarch64-linux-android-" + gcc_version, "aarch64", "aarch64-linux-android")
]
elif ARCH == "arm":
known_toolchains = [
("arm-linux-androideabi-" + gcc_version, "arm", "arm-linux-androideabi")
]
elif ARCH =="x86":
known_toolchains = [
("x86-" + gcc_version, "x86", "i686-linux-android")
]
elif ARCH =="x86_64" or ARCH =="x64":
known_toolchains = [
("x86_64-" + gcc_version, "x86_64", "x86_64-linux-android")
]
elif ARCH == "mips":
known_toolchains = [
("mipsel-linux-android-" + gcc_version, "mips", "mipsel-linux-android")
]
else:
known_toolchains = []
logging.debug('FindToolcahin: known_toolchains=%s' % known_toolchains)
# Look for addr2line to check for valid toolchain path.
for (label, platform, target) in known_toolchains:
toolchain_info = (label, platform, target);
if os.path.exists(ToolPath("addr2line", toolchain_info)):
TOOLCHAIN_INFO = toolchain_info
print ("Using toolchain from: "
+ os.path.normpath(ToolPath("", TOOLCHAIN_INFO)))
return toolchain_info
raise Exception("Could not find tool chain")
def GetAapt(): def GetAapt():
"""Returns the path to aapt. """Returns the path to aapt.
...@@ -279,7 +364,260 @@ def TranslateLibPath(lib): ...@@ -279,7 +364,260 @@ def TranslateLibPath(lib):
library_path = os.path.relpath(candidate_libraries[0], SYMBOLS_DIR) library_path = os.path.relpath(candidate_libraries[0], SYMBOLS_DIR)
logging.debug('TranslateLibPath: library_path=%s' % library_path) logging.debug('TranslateLibPath: library_path=%s' % library_path)
return library_path return '/' + library_path
def SymbolInformation(lib, addr, get_detailed_info):
"""Look up symbol information about an address.
Args:
lib: library (or executable) pathname containing symbols
addr: string hexidecimal address
Returns:
A list of the form [(source_symbol, source_location,
object_symbol_with_offset)].
If the function has been inlined then the list may contain
more than one element with the symbols for the most deeply
nested inlined location appearing first. The list is
always non-empty, even if no information is available.
Usually you want to display the source_location and
object_symbol_with_offset from the last element in the list.
"""
lib = TranslateLibPath(lib)
info = SymbolInformationForSet(lib, set([addr]), get_detailed_info)
return (info and info.get(addr)) or [(None, None, None)]
def SymbolInformationForSet(lib, unique_addrs, get_detailed_info):
"""Look up symbol information for a set of addresses from the given library.
Args:
lib: library (or executable) pathname containing symbols
unique_addrs: set of hexidecimal addresses
Returns:
A dictionary of the form {addr: [(source_symbol, source_location,
object_symbol_with_offset)]} where each address has a list of
associated symbols and locations. The list is always non-empty.
If the function has been inlined then the list may contain
more than one element with the symbols for the most deeply
nested inlined location appearing first. The list is
always non-empty, even if no information is available.
Usually you want to display the source_location and
object_symbol_with_offset from the last element in the list.
"""
if not lib:
return None
addr_to_line = CallAddr2LineForSet(lib, unique_addrs)
if not addr_to_line:
return None
if get_detailed_info:
addr_to_objdump = CallObjdumpForSet(lib, unique_addrs)
if not addr_to_objdump:
return None
else:
addr_to_objdump = dict((addr, ("", 0)) for addr in unique_addrs)
result = {}
for addr in unique_addrs:
source_info = addr_to_line.get(addr)
if not source_info:
source_info = [(None, None)]
if addr in addr_to_objdump:
(object_symbol, object_offset) = addr_to_objdump.get(addr)
object_symbol_with_offset = FormatSymbolWithOffset(object_symbol,
object_offset)
else:
object_symbol_with_offset = None
result[addr] = [(source_symbol, source_location, object_symbol_with_offset)
for (source_symbol, source_location) in source_info]
return result
class MemoizedForSet(object):
def __init__(self, fn):
self.fn = fn
self.cache = {}
def __call__(self, lib, unique_addrs):
lib_cache = self.cache.setdefault(lib, {})
no_cache = filter(lambda x: x not in lib_cache, unique_addrs)
if no_cache:
lib_cache.update((k, None) for k in no_cache)
result = self.fn(lib, no_cache)
if result:
lib_cache.update(result)
return dict((k, lib_cache[k]) for k in unique_addrs if lib_cache[k])
@MemoizedForSet
def CallAddr2LineForSet(lib, unique_addrs):
"""Look up line and symbol information for a set of addresses.
Args:
lib: library (or executable) pathname containing symbols
unique_addrs: set of string hexidecimal addresses look up.
Returns:
A dictionary of the form {addr: [(symbol, file:line)]} where
each address has a list of associated symbols and locations
or an empty list if no symbol information was found.
If the function has been inlined then the list may contain
more than one element with the symbols for the most deeply
nested inlined location appearing first.
"""
if not lib:
return None
symbols = SYMBOLS_DIR + lib
if not os.path.splitext(symbols)[1] in ['', '.so', '.apk']:
return None
if not os.path.isfile(symbols):
return None
addrs = sorted(unique_addrs)
result = {}
def _Callback(sym, addr):
records = []
while sym: # Traverse all the inlines following the |inlined_by| chain.
if sym.source_path and sym.source_line:
location = '%s:%d' % (sym.source_path, sym.source_line)
else:
location = None
records += [(sym.name, location)]
sym = sym.inlined_by
result[addr] = records
(label, platform, target) = FindToolchain()
symbolizer = elf_symbolizer.ELFSymbolizer(
elf_file_path=symbols,
addr2line_path=ToolPath("addr2line"),
callback=_Callback,
inlines=True)
for addr in addrs:
symbolizer.SymbolizeAsync(int(addr, 16), addr)
symbolizer.Join()
return result
def StripPC(addr):
"""Strips the Thumb bit a program counter address when appropriate.
Args:
addr: the program counter address
Returns:
The stripped program counter address.
"""
global ARCH
if ARCH == "arm":
return addr & ~1
return addr
@MemoizedForSet
def CallObjdumpForSet(lib, unique_addrs):
"""Use objdump to find out the names of the containing functions.
Args:
lib: library (or executable) pathname containing symbols
unique_addrs: set of string hexidecimal addresses to find the functions for.
Returns:
A dictionary of the form {addr: (string symbol, offset)}.
"""
if not lib:
return None
symbols = SYMBOLS_DIR + lib
if not os.path.exists(symbols):
return None
symbols = SYMBOLS_DIR + lib
if not os.path.exists(symbols):
return None
result = {}
# Function lines look like:
# 000177b0 <android::IBinder::~IBinder()+0x2c>:
# We pull out the address and function first. Then we check for an optional
# offset. This is tricky due to functions that look like "operator+(..)+0x2c"
func_regexp = re.compile("(^[a-f0-9]*) \<(.*)\>:$")
offset_regexp = re.compile("(.*)\+0x([a-f0-9]*)")
# A disassembly line looks like:
# 177b2: b510 push {r4, lr}
asm_regexp = re.compile("(^[ a-f0-9]*):[ a-f0-0]*.*$")
for target_addr in unique_addrs:
start_addr_dec = str(StripPC(int(target_addr, 16)))
stop_addr_dec = str(StripPC(int(target_addr, 16)) + 8)
cmd = [ToolPath("objdump"),
"--section=.text",
"--demangle",
"--disassemble",
"--start-address=" + start_addr_dec,
"--stop-address=" + stop_addr_dec,
symbols]
current_symbol = None # The current function symbol in the disassembly.
current_symbol_addr = 0 # The address of the current function.
stream = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout
for line in stream:
# Is it a function line like:
# 000177b0 <android::IBinder::~IBinder()>:
components = func_regexp.match(line)
if components:
# This is a new function, so record the current function and its address.
current_symbol_addr = int(components.group(1), 16)
current_symbol = components.group(2)
# Does it have an optional offset like: "foo(..)+0x2c"?
components = offset_regexp.match(current_symbol)
if components:
current_symbol = components.group(1)
offset = components.group(2)
if offset:
current_symbol_addr -= int(offset, 16)
# Is it an disassembly line like:
# 177b2: b510 push {r4, lr}
components = asm_regexp.match(line)
if components:
addr = components.group(1)
i_addr = int(addr, 16)
i_target = StripPC(int(target_addr, 16))
if i_addr == i_target:
result[target_addr] = (current_symbol, i_target - current_symbol_addr)
stream.close()
return result
def CallCppFilt(mangled_symbol):
cmd = [ToolPath("c++filt")]
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
process.stdin.write(mangled_symbol)
process.stdin.write("\n")
process.stdin.close()
demangled_symbol = process.stdout.readline().strip()
process.stdout.close()
return demangled_symbol
def FormatSymbolWithOffset(symbol, offset): def FormatSymbolWithOffset(symbol, offset):
if offset == 0: if offset == 0:
......
# Copyright 2017 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.
import logging
import os
import re
import subprocess
import threading
_CHROME_SRC = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
_LLVM_SYMBOLIZER_PATH = os.path.join(
_CHROME_SRC, 'third_party', 'llvm-build', 'Release+Asserts', 'bin',
'llvm-symbolizer')
_BINARY = re.compile(r'0b[0,1]+')
_HEX = re.compile(r'0x[0-9,a-e]+')
_OCTAL = re.compile(r'0[0-7]+')
_UNKNOWN = '<UNKNOWN>'
def _CheckValidAddr(addr):
"""
Check whether the addr is valid input to llvm symbolizer.
Valid addr has to be octal, binary, or hex number.
Args:
addr: addr to be entered to llvm symbolizer.
Returns:
whether the addr is valid input to llvm symbolizer.
"""
return _HEX.match(addr) or _OCTAL.match(addr) or _BINARY.match(addr)
class LLVMSymbolizer(object):
def __init__(self):
"""Create a LLVMSymbolizer instance that interacts with the llvm symbolizer.
The purpose of the LLVMSymbolizer is to get function names and line
numbers of an address from the symbols library.
"""
self._llvm_symbolizer_subprocess = None
# Allow only one thread to call GetSymbolInformation at a time.
self._lock = threading.Lock()
def Start(self):
"""Start the llvm symbolizer subprocess.
Create a subprocess of the llvm symbolizer executable, which will be used
to retrieve function names etc.
"""
if os.path.isfile(_LLVM_SYMBOLIZER_PATH):
self._llvm_symbolizer_subprocess = subprocess.Popen(
[_LLVM_SYMBOLIZER_PATH], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
else:
logging.error('Cannot find llvm_symbolizer here: %s.' %
_LLVM_SYMBOLIZER_PATH)
self._llvm_symbolizer_subprocess = None
def Close(self):
"""Close the llvm symbolizer subprocess.
Close the subprocess by closing stdin, stdout and killing the subprocess.
"""
with self._lock:
if self._llvm_symbolizer_subprocess:
self._llvm_symbolizer_subprocess.kill()
self._llvm_symbolizer_subprocess = None
def __enter__(self):
"""Start the llvm symbolizer subprocess."""
self.Start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Close the llvm symbolizer subprocess."""
self.Close()
def GetSymbolInformation(self, lib, addr):
"""Return the corresponding function names and line numbers.
Args:
lib: library to search for info.
addr: address to look for info.
Returns:
A list of (function name, line numbers) tuple.
"""
if (self._llvm_symbolizer_subprocess is None or not lib
or not _CheckValidAddr(addr) or not os.path.isfile(lib)):
return [(_UNKNOWN, lib)]
with self._lock:
self._llvm_symbolizer_subprocess.stdin.write('%s %s\n' % (lib, addr))
self._llvm_symbolizer_subprocess.stdin.flush()
result = []
# Read till see new line, which is a symbol of end of output.
# One line of function name is always followed by one line of line number.
while True:
line = self._llvm_symbolizer_subprocess.stdout.readline()
if line != '\n':
line_numbers = self._llvm_symbolizer_subprocess.stdout.readline()
result.append(
(line[:-1],
line_numbers[:-1]))
else:
return result
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment