development: copy stack tool over from vendor/google/tools
The stack tool is not really proprietary, and is needed by vendors and
third-party developers working on native code.
Change-Id: I37f34b0681a0063ecf71f5a078d2c4a1ba622973
Signed-off-by: Iliyan Malchev <malchev@google.com>
diff --git a/scripts/stack b/scripts/stack
new file mode 100755
index 0000000..6750752
--- /dev/null
+++ b/scripts/stack
@@ -0,0 +1,418 @@
+#!/usr/bin/env python
+#
+# Copyright 2006 Google Inc. All Rights Reserved.
+
+"""stack symbolizes native crash dumps."""
+
+import getopt
+import getpass
+import glob
+import os
+import re
+import subprocess
+import sys
+import urllib
+
+import symbol
+
+
+def PrintUsage():
+ """Print usage and exit with error."""
+ # pylint: disable-msg=C6310
+ print
+ print " usage: " + sys.argv[0] + " [options] [FILE]"
+ print
+ print " --symbols-dir=path"
+ print " the path to a symbols dir, such as =/tmp/out/target/product/dream/symbols"
+ print
+ print " --symbols-zip=path"
+ print " the path to a symbols zip file, such as =dream-symbols-12345.zip"
+ print
+ print " --auto"
+ print " attempt to:"
+ print " 1) automatically find the build number in the crash"
+ print " 2) if it's an official build, download the symbols "
+ print " from the build server, and use them"
+ print
+ print " FILE should contain a stack trace in it somewhere"
+ print " the tool will find that and re-print it with"
+ print " source files and line numbers. If you don't"
+ print " pass FILE, or if file is -, it reads from"
+ print " stdin."
+ print
+ # pylint: enable-msg=C6310
+ sys.exit(1)
+
+
+class SSOCookie(object):
+ """Creates a cookie file so we can download files from the build server."""
+
+ def __init__(self, cookiename=".sso.cookie", keep=False):
+ self.sso_server = "login.corp.google.com"
+ self.name = cookiename
+ self.keeper = keep
+ if not os.path.exists(self.name):
+ user = os.environ["USER"]
+ print "\n%s, to access the symbols, please enter your LDAP " % user,
+ sys.stdout.flush()
+ password = getpass.getpass()
+ params = urllib.urlencode({"u": user, "pw": password})
+ url = "https://%s/login?ssoformat=CORP_SSO" % self.sso_server
+ # login to SSO
+ curlcmd = ["/usr/bin/curl",
+ "--cookie", self.name,
+ "--cookie-jar", self.name,
+ "--silent",
+ "--location",
+ "--data", params,
+ "--output", "/dev/null",
+ url]
+ subprocess.check_call(curlcmd)
+ if os.path.exists(self.name):
+ os.chmod(self.name, 0600)
+ else:
+ print "Could not log in to SSO"
+ sys.exit(1)
+
+ def __del__(self):
+ """Clean up."""
+ if not self.keeper:
+ os.remove(self.name)
+
+
+class NoBuildIDException(Exception):
+ pass
+
+
+def FindBuildFingerprint(lines):
+ """Searches the given file (array of lines) for the build fingerprint."""
+ fingerprint_regex = re.compile("^.*Build fingerprint:\s'(?P<fingerprint>.*)'")
+ for line in lines:
+ fingerprint_search = fingerprint_regex.match(line.strip())
+ if fingerprint_search:
+ return fingerprint_search.group("fingerprint")
+
+ return None # didn't find the fingerprint string, so return none
+
+
+class SymbolDownloadException(Exception):
+ pass
+
+
+DEFAULT_SYMROOT = "/tmp/symbols"
+
+
+def DownloadSymbols(fingerprint, cookie):
+ """Attempts to download the symbols from the build server.
+
+ If successful, extracts them, and returns the path.
+
+ Args:
+ fingerprint: build fingerprint from the input stack trace
+ cookie: SSOCookie
+
+ Returns:
+ tuple (None, None) if no fingerprint is provided. Otherwise
+ tuple (root directory, symbols directory).
+
+ Raises:
+ SymbolDownloadException: Problem downloading symbols for fingerprint
+ """
+ if fingerprint is None:
+ return (None, None)
+ symdir = "%s/%s" % (DEFAULT_SYMROOT, hash(fingerprint))
+ if not os.path.exists(symdir):
+ os.makedirs(symdir)
+ # build server figures out the branch based on the CL
+ params = {
+ "op": "GET-SYMBOLS-LINK",
+ "fingerprint": fingerprint,
+ }
+ print "url: http://android-build/buildbot-update?" + urllib.urlencode(params)
+ url = urllib.urlopen("http://android-build/buildbot-update?",
+ urllib.urlencode(params)).readlines()[0]
+ if not url:
+ raise SymbolDownloadException("Build server down? Failed to find syms...")
+
+ regex_str = (r"(?P<base_url>http\:\/\/android-build\/builds\/.*\/[0-9]+)"
+ r"(?P<img>.*)")
+ url_regex = re.compile(regex_str)
+ url_match = url_regex.match(url)
+ if url_match is None:
+ raise SymbolDownloadException("Unexpected results from build server URL...")
+
+ base_url = url_match.group("base_url")
+ img = url_match.group("img")
+ symbolfile = img.replace("-img-", "-symbols-")
+ symurl = base_url + symbolfile
+ localsyms = symdir + symbolfile
+
+ if not os.path.exists(localsyms):
+ print "downloading %s ..." % symurl
+ curlcmd = ["/usr/bin/curl",
+ "--cookie", cookie.name,
+ "--silent",
+ "--location",
+ "--write-out", "%{http_code}",
+ "--output", localsyms,
+ symurl]
+ p = subprocess.Popen(curlcmd,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ close_fds=True)
+ code = p.stdout.read()
+ err = p.stderr.read()
+ if err:
+ raise SymbolDownloadException("stderr from curl download: %s" % err)
+ if code != "200":
+ raise SymbolDownloadException("Faied to download %s" % symurl)
+ else:
+ print "using existing cache for symbols"
+
+ return UnzipSymbols(localsyms, symdir)
+
+
+def UnzipSymbols(symbolfile, symdir=None):
+ """Unzips a file to DEFAULT_SYMROOT and returns the unzipped location.
+
+ Args:
+ symbolfile: The .zip file to unzip
+ symdir: Optional temporary directory to use for extraction
+
+ Returns:
+ A tuple containing (the directory into which the zip file was unzipped,
+ the path to the "symbols" directory in the unzipped file). To clean
+ up, the caller can delete the first element of the tuple.
+
+ Raises:
+ SymbolDownloadException: When the unzip fails.
+ """
+ if not symdir:
+ symdir = "%s/%s" % (DEFAULT_SYMROOT, hash(symbolfile))
+ if not os.path.exists(symdir):
+ os.makedirs(symdir)
+
+ print "extracting %s..." % symbolfile
+ saveddir = os.getcwd()
+ os.chdir(symdir)
+ try:
+ unzipcode = subprocess.call(["unzip", "-qq", "-o", symbolfile])
+ if unzipcode > 0:
+ os.remove(symbolfile)
+ raise SymbolDownloadException("failed to extract symbol files (%s)."
+ % symbolfile)
+ finally:
+ os.chdir(saveddir)
+
+ return (symdir, glob.glob("%s/out/target/product/*/symbols" % symdir)[0])
+
+
+def PrintTraceLines(trace_lines):
+ """Print back trace."""
+ maxlen = max(map(lambda tl: len(tl[1]), trace_lines))
+ print
+ print "Stack Trace:"
+ print " RELADDR " + "FUNCTION".ljust(maxlen) + " FILE:LINE"
+ for tl in trace_lines:
+ (addr, symbol_with_offset, location) = tl
+ print " %8s %s %s" % (addr, symbol_with_offset.ljust(maxlen), location)
+ return
+
+
+def PrintValueLines(value_lines):
+ """Print stack data values."""
+ print
+ print "Stack Data:"
+ print " ADDR VALUE FILE:LINE/FUNCTION"
+ for vl in value_lines:
+ (addr, value, symbol_with_offset, location) = vl
+ print " " + addr + " " + value + " " + location
+ if location:
+ print " " + symbol_with_offset
+ return
+
+UNKNOWN = "<unknown>"
+HEAP = "[heap]"
+STACK = "[stack]"
+
+
+def ConvertTrace(lines):
+ """Convert strings containing native crash to a stack."""
+ process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)")
+ signal_line = re.compile("(signal [0-9]+ \(.*\).*)")
+ register_line = re.compile("(([ ]*[0-9a-z]{2} [0-9a-f]{8}){4})")
+ thread_line = re.compile("(.*)(\-\-\- ){15}\-\-\-")
+ # Note taht both trace and value line matching allow for variable amounts of
+ # whitespace (e.g. \t). This is because the we want to allow for the stack
+ # tool to operate on AndroidFeedback provided system logs. AndroidFeedback
+ # strips out double spaces that are found in tombsone files and logcat output.
+ #
+ # Examples of matched trace lines include lines from tombstone files like:
+ # #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
+ # Or lines from AndroidFeedback crash report system logs like:
+ # 03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
+ # Please note the spacing differences.
+ trace_line = re.compile("(.*)\#([0-9]+)[ \t]+(..)[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)( \((.*)\))?") # pylint: disable-msg=C6310
+ # Examples of matched value lines include:
+ # bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
+ # 03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
+ # Again, note the spacing differences.
+ value_line = re.compile("(.*)([0-9a-f]{8})[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)")
+ # Lines from 'code around' sections of the output will be matched before
+ # value lines because otheriwse the 'code around' sections will be confused as
+ # value lines.
+ #
+ # Examples include:
+ # 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
+ # 03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
+ code_line = re.compile("(.*)[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[ \r\n]") # pylint: disable-msg=C6310
+
+ trace_lines = []
+ value_lines = []
+
+ for ln in lines:
+ # AndroidFeedback adds zero width spaces into its crash reports. These
+ # should be removed or the regular expresssions will fail to match.
+ line = unicode(ln, errors='ignore')
+ header = process_info_line.search(line)
+ if header:
+ print header.group(1)
+ continue
+ header = signal_line.search(line)
+ if header:
+ print header.group(1)
+ continue
+ header = register_line.search(line)
+ if header:
+ print header.group(1)
+ continue
+ if trace_line.match(line):
+ match = trace_line.match(line)
+ (unused_0, unused_1, unused_2,
+ code_addr, area, symbol_present, symbol_name) = match.groups()
+
+ if area == UNKNOWN or area == HEAP or area == STACK:
+ trace_lines.append((code_addr, area, area))
+ else:
+ # 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"
+ (source_symbol,
+ source_location,
+ object_symbol_with_offset) = symbol.SymbolInformation(area, code_addr)
+ 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 not object_symbol_with_offset:
+ object_symbol_with_offset = source_symbol
+ if not object_symbol_with_offset.startswith(source_symbol):
+ trace_lines.append(("v------>", source_symbol, source_location))
+ trace_lines.append((code_addr,
+ object_symbol_with_offset,
+ source_location))
+ else:
+ trace_lines.append((code_addr,
+ object_symbol_with_offset,
+ source_location))
+ if code_line.match(line):
+ # Code lines should be ignored. If this were exluded the 'code around'
+ # sections would trigger value_line matches.
+ continue;
+ if value_line.match(line):
+ match = value_line.match(line)
+ (unused_, addr, value, area) = match.groups()
+ if area == UNKNOWN or area == HEAP or area == STACK or not area:
+ value_lines.append((addr, value, area, ""))
+ else:
+ (source_symbol,
+ source_location,
+ object_symbol_with_offset) = symbol.SymbolInformation(area, value)
+ if not source_location:
+ source_location = ""
+ if not object_symbol_with_offset:
+ object_symbol_with_offset = UNKNOWN
+ value_lines.append((addr,
+ value,
+ object_symbol_with_offset,
+ source_location))
+ header = thread_line.search(line)
+ if header:
+ if trace_lines:
+ PrintTraceLines(trace_lines)
+
+ if value_lines:
+ PrintValueLines(value_lines)
+ trace_lines = []
+ value_lines = []
+ print
+ print "-----------------------------------------------------\n"
+
+ if trace_lines:
+ PrintTraceLines(trace_lines)
+
+ if value_lines:
+ PrintValueLines(value_lines)
+
+
+def main():
+ try:
+ options, arguments = getopt.getopt(sys.argv[1:], "",
+ ["auto",
+ "symbols-dir=",
+ "symbols-zip=",
+ "help"])
+ except getopt.GetoptError, unused_error:
+ PrintUsage()
+
+ zip_arg = None
+ auto = False
+ fingerprint = None
+ for option, value in options:
+ if option == "--help":
+ PrintUsage()
+ elif option == "--symbols-dir":
+ symbol.SYMBOLS_DIR = os.path.expanduser(value)
+ elif option == "--symbols-zip":
+ zip_arg = os.path.expanduser(value)
+ elif option == "--auto":
+ auto = True
+
+ if len(arguments) > 1:
+ PrintUsage()
+
+ if auto:
+ cookie = SSOCookie(".symbols.cookie")
+
+ if not arguments or arguments[0] == "-":
+ print "Reading native crash info from stdin"
+ f = sys.stdin
+ else:
+ print "Searching for native crashes in %s" % arguments[0]
+ f = open(arguments[0], "r")
+
+ lines = f.readlines()
+ f.close()
+
+ rootdir = None
+ if auto:
+ fingerprint = FindBuildFingerprint(lines)
+ print "fingerprint:", fingerprint
+ rootdir, symbol.SYMBOLS_DIR = DownloadSymbols(fingerprint, cookie)
+ elif zip_arg:
+ rootdir, symbol.SYMBOLS_DIR = UnzipSymbols(zip_arg)
+
+ print "Reading symbols from", symbol.SYMBOLS_DIR
+ ConvertTrace(lines)
+
+ if rootdir:
+ # be a good citizen and clean up...os.rmdir and os.removedirs() don't work
+ cmd = "rm -rf \"%s\"" % rootdir
+ print "\ncleaning up (%s)" % cmd
+ os.system(cmd)
+
+if __name__ == "__main__":
+ main()
+
+# vi: ts=2 sw=2
diff --git a/scripts/symbol.py b/scripts/symbol.py
new file mode 100755
index 0000000..e36304c
--- /dev/null
+++ b/scripts/symbol.py
@@ -0,0 +1,287 @@
+#!/usr/bin/python
+#
+# Copyright 2006 Google Inc. All Rights Reserved.
+
+"""Module for looking up symbolic debugging information.
+
+The information can include symbol names, offsets, and source locations.
+"""
+
+import os
+import re
+import subprocess
+
+ANDROID_BUILD_TOP = os.environ["ANDROID_BUILD_TOP"]
+if not ANDROID_BUILD_TOP:
+ ANDROID_BUILD_TOP = "."
+
+def FindSymbolsDir():
+ saveddir = os.getcwd()
+ os.chdir(ANDROID_BUILD_TOP)
+ try:
+ cmd = ("CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core "
+ "SRC_TARGET_DIR=build/target make -f build/core/config.mk "
+ "dumpvar-abs-TARGET_OUT_UNSTRIPPED")
+ stream = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).stdout
+ return os.path.join(ANDROID_BUILD_TOP, stream.read().strip())
+ finally:
+ os.chdir(saveddir)
+
+SYMBOLS_DIR = FindSymbolsDir()
+
+def Uname():
+ """'uname' for constructing prebuilt/<...> and out/host/<...> paths."""
+ uname = os.uname()[0]
+ if uname == "Darwin":
+ proc = os.uname()[-1]
+ if proc == "i386" or proc == "x86_64":
+ return "darwin-x86"
+ return "darwin-ppc"
+ if uname == "Linux":
+ return "linux-x86"
+ return uname
+
+def ToolPath(tool, toolchain_info=None):
+ """Return a full qualified path to the specified tool"""
+ if not toolchain_info:
+ toolchain_info = TOOLCHAIN_INFO
+ (label, target) = toolchain_info
+ return os.path.join(ANDROID_BUILD_TOP, "prebuilt", Uname(), "toolchain", label, "bin",
+ target + "-" + tool)
+
+def FindToolchain():
+ """Look for the latest available toolchain
+
+ Args:
+ None
+
+ Returns:
+ A pair of strings containing toolchain label and target prefix.
+ """
+
+ ## Known toolchains, newer ones in the front.
+ known_toolchains = [
+ ("arm-linux-androideabi-4.4.x", "arm-linux-androideabi"),
+ ("arm-eabi-4.4.3", "arm-eabi"),
+ ("arm-eabi-4.4.0", "arm-eabi"),
+ ("arm-eabi-4.3.1", "arm-eabi"),
+ ("arm-eabi-4.2.1", "arm-eabi")
+ ]
+
+ # Look for addr2line to check for valid toolchain path.
+ for (label, target) in known_toolchains:
+ toolchain_info = (label, target);
+ if os.path.exists(ToolPath("addr2line", toolchain_info)):
+ return toolchain_info
+
+ raise Exception("Could not find tool chain")
+
+TOOLCHAIN_INFO = FindToolchain()
+
+def SymbolInformation(lib, addr):
+ """Look up symbol information about an address.
+
+ Args:
+ lib: library (or executable) pathname containing symbols
+ addr: string hexidecimal address
+
+ Returns:
+ For a given library and address, return tuple of: (source_symbol,
+ source_location, object_symbol_with_offset) the values may be None
+ if the information was unavailable.
+
+ source_symbol may not be a prefix of object_symbol_with_offset if
+ the source function was inlined in the object code of another
+ function.
+
+ usually you want to display the object_symbol_with_offset and
+ source_location, the source_symbol is only useful to show if the
+ address was from an inlined function.
+ """
+ info = SymbolInformationForSet(lib, set([addr]))
+ return (info and info.get(addr)) or (None, None, None)
+
+
+def SymbolInformationForSet(lib, unique_addrs):
+ """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:
+ For a given library and set of addresses, returns a dictionary of the form
+ {addr: (source_symbol, source_location, object_symbol_with_offset)}. The
+ values may be None if the information was unavailable.
+
+ For a given address, source_symbol may not be a prefix of
+ object_symbol_with_offset if the source function was inlined in the
+ object code of another function.
+
+ Usually you want to display the object_symbol_with_offset and
+ source_location; the source_symbol is only useful to show if the
+ address was from an inlined function.
+ """
+ if not lib:
+ return None
+
+ addr_to_line = CallAddr2LineForSet(lib, unique_addrs)
+ if not addr_to_line:
+ return None
+
+ addr_to_objdump = CallObjdumpForSet(lib, unique_addrs)
+ if not addr_to_objdump:
+ return None
+
+ result = {}
+ for addr in unique_addrs:
+ (source_symbol, source_location) = addr_to_line.get(addr, (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)
+
+ return result
+
+
+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)}. The values may
+ be (None, None) if the address could not be looked up.
+ """
+ if not lib:
+ return None
+
+
+ symbols = SYMBOLS_DIR + lib
+ if not os.path.exists(symbols):
+ return None
+
+ (label, target) = TOOLCHAIN_INFO
+ cmd = [ToolPath("addr2line"), "--functions", "--demangle", "--exe=" + symbols]
+ child = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+
+ result = {}
+ addrs = sorted(unique_addrs)
+ for addr in addrs:
+ child.stdin.write("0x%s\n" % addr)
+ child.stdin.flush()
+ symbol = child.stdout.readline().strip()
+ if symbol == "??":
+ symbol = None
+ location = child.stdout.readline().strip()
+ if location == "??:0":
+ location = None
+ result[addr] = (symbol, location)
+ child.stdin.close()
+ child.stdout.close()
+ return result
+
+
+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
+
+ addrs = sorted(unique_addrs)
+ start_addr_hex = addrs[0]
+ stop_addr_dec = str(int(addrs[-1], 16) + 8)
+ cmd = [ToolPath("objdump"),
+ "--section=.text",
+ "--demangle",
+ "--disassemble",
+ "--start-address=0x" + start_addr_hex,
+ "--stop-address=" + stop_addr_dec,
+ symbols]
+
+ # 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]*.*$")
+
+ current_symbol = None # The current function symbol in the disassembly.
+ current_symbol_addr = 0 # The address of the current function.
+ addr_index = 0 # The address that we are currently looking for.
+
+ stream = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout
+ result = {}
+ 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)
+ target_addr = addrs[addr_index]
+ i_addr = int(addr, 16)
+ i_target = int(target_addr, 16)
+ if i_addr == i_target:
+ result[target_addr] = (current_symbol, i_target - current_symbol_addr)
+ addr_index += 1
+ if addr_index >= len(addrs):
+ break
+ 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):
+ if offset == 0:
+ return symbol
+ return "%s+%d" % (symbol, offset)