diff options
author | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-10 13:23:12 +0000 |
---|---|---|
committer | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-10 13:23:12 +0000 |
commit | e228d5a20e272bd1f54fd787e2dbbf26dc4eb858 (patch) | |
tree | 6cf561645a1324f7b09c9035a9fe152c7faf0070 /third_party | |
parent | c49414a0fc71df8a014a9fb6abacfc60807ec03e (diff) | |
download | chromium_src-e228d5a20e272bd1f54fd787e2dbbf26dc4eb858.zip chromium_src-e228d5a20e272bd1f54fd787e2dbbf26dc4eb858.tar.gz chromium_src-e228d5a20e272bd1f54fd787e2dbbf26dc4eb858.tar.bz2 |
Android: adds a few third-party scripts for stack symbolization.
Pick a few utility scripts from https://android.googlesource.com/platform/development/+/master/scripts/
These are used for symbolizing crash stacks on android.
BUG=234973
Review URL: https://chromiumcodereview.appspot.com/18326020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@210833 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party')
-rw-r--r-- | third_party/android_platform/LICENSE | 202 | ||||
-rw-r--r-- | third_party/android_platform/OWNERS | 2 | ||||
-rw-r--r-- | third_party/android_platform/README.chromium | 15 | ||||
-rwxr-xr-x | third_party/android_platform/development/scripts/stack | 78 | ||||
-rwxr-xr-x | third_party/android_platform/development/scripts/stack_core.py | 196 | ||||
-rwxr-xr-x | third_party/android_platform/development/scripts/symbol.py | 344 |
6 files changed, 837 insertions, 0 deletions
diff --git a/third_party/android_platform/LICENSE b/third_party/android_platform/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/third_party/android_platform/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/android_platform/OWNERS b/third_party/android_platform/OWNERS new file mode 100644 index 0000000..2b5e2e4 --- /dev/null +++ b/third_party/android_platform/OWNERS @@ -0,0 +1,2 @@ +bulach@chromium.org +cjhopman@chromium.org diff --git a/third_party/android_platform/README.chromium b/third_party/android_platform/README.chromium new file mode 100644 index 0000000..3db1405 --- /dev/null +++ b/third_party/android_platform/README.chromium @@ -0,0 +1,15 @@ +Name: Android Platform engineering tools +Short Name: android platform development +URL: https://android.googlesource.com/platform/development +Version: 0 +Date: 2013/07/03 +Revision: f56a37e +License: Apache 2.0 +License File: NOT_SHIPPED +Security Critical: no + +Description: +Android Platform engineering tools, specifically stack symbolization scripts. + +Local Modifications: +Only picked the few scripts needed by chrome. diff --git a/third_party/android_platform/development/scripts/stack b/third_party/android_platform/development/scripts/stack new file mode 100755 index 0000000..6bb8d0a --- /dev/null +++ b/third_party/android_platform/development/scripts/stack @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""stack symbolizes native crash dumps.""" + +import getopt +import sys + +import stack_core +import symbol + + +def PrintUsage(): + """Print usage and exit with error.""" + # pylint: disable-msg=C6310 + print + print " usage: " + sys.argv[0] + " [options] [FILE]" + print + print " --arch=arm|x86" + print " the target architecture" + 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) + + +def main(): + try: + options, arguments = getopt.getopt(sys.argv[1:], "", + ["arch=", + "help"]) + except getopt.GetoptError, unused_error: + PrintUsage() + + for option, value in options: + if option == "--help": + PrintUsage() + elif option == "--arch": + symbol.ARCH = value + + if len(arguments) > 1: + PrintUsage() + + 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() + + print "Reading symbols from", symbol.SYMBOLS_DIR + stack_core.ConvertTrace(lines) + +if __name__ == "__main__": + main() + +# vi: ts=2 sw=2 diff --git a/third_party/android_platform/development/scripts/stack_core.py b/third_party/android_platform/development/scripts/stack_core.py new file mode 100755 index 0000000..42285d4 --- /dev/null +++ b/third_party/android_platform/development/scripts/stack_core.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""stack symbolizes native crash dumps.""" + +import re + +import symbol + +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.""" + maxlen = max(map(lambda tl: len(tl[2]), value_lines)) + print + print "Stack Data:" + print " ADDR VALUE " + "FUNCTION".ljust(maxlen) + " FILE:LINE" + for vl in value_lines: + (addr, value, symbol_with_offset, location) = vl + print " %8s %8s %s %s" % (addr, value, symbol_with_offset.ljust(maxlen), location) + return + +UNKNOWN = "<unknown>" +HEAP = "[heap]" +STACK = "[stack]" + + +def PrintOutput(trace_lines, value_lines): + if trace_lines: + PrintTraceLines(trace_lines) + if value_lines: + PrintValueLines(value_lines) + +def PrintDivider(): + print + print "-----------------------------------------------------\n" + +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}\-\-\-") + dalvik_jni_thread_line = re.compile("(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)") + dalvik_native_thread_line = re.compile("(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)") + # Note that 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 + # #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so (symbol) + # 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 + # bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so (symbol) + # 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 = [] + last_frame = -1 + + 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') + process_header = process_info_line.search(line) + signal_header = signal_line.search(line) + register_header = register_line.search(line) + thread_header = thread_line.search(line) + dalvik_jni_thread_header = dalvik_jni_thread_line.search(line) + dalvik_native_thread_header = dalvik_native_thread_line.search(line) + if process_header or signal_header or register_header or thread_header \ + or dalvik_jni_thread_header or dalvik_native_thread_header: + if trace_lines or value_lines: + PrintOutput(trace_lines, value_lines) + PrintDivider() + trace_lines = [] + value_lines = [] + last_frame = -1 + if process_header: + print process_header.group(1) + if signal_header: + print signal_header.group(1) + if register_header: + print register_header.group(1) + if thread_header: + print thread_header.group(1) + if dalvik_jni_thread_header: + print dalvik_jni_thread_header.group(1) + if dalvik_native_thread_header: + print dalvik_native_thread_header.group(1) + continue + if trace_line.match(line): + match = trace_line.match(line) + (unused_0, frame, unused_1, + code_addr, area, symbol_present, symbol_name) = match.groups() + + if frame <= last_frame and (trace_lines or value_lines): + PrintOutput(trace_lines, value_lines) + PrintDivider() + trace_lines = [] + value_lines = [] + last_frame = frame + + if area == UNKNOWN or area == HEAP or area == STACK: + trace_lines.append((code_addr, "", 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" + info = symbol.SymbolInformation(area, code_addr) + nest_count = len(info) - 1 + 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: + nest_count = nest_count - 1 + trace_lines.append(("v------>", source_symbol, source_location)) + else: + if not object_symbol_with_offset: + object_symbol_with_offset = source_symbol + 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, symbol_present, symbol_name) = match.groups() + if area == UNKNOWN or area == HEAP or area == STACK or not area: + value_lines.append((addr, value, "", area)) + else: + info = symbol.SymbolInformation(area, value) + (source_symbol, source_location, object_symbol_with_offset) = info.pop() + 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 + value_lines.append((addr, + value, + object_symbol_with_offset, + source_location)) + + PrintOutput(trace_lines, value_lines) + + +# vi: ts=2 sw=2 diff --git a/third_party/android_platform/development/scripts/symbol.py b/third_party/android_platform/development/scripts/symbol.py new file mode 100755 index 0000000..0f58df6 --- /dev/null +++ b/third_party/android_platform/development/scripts/symbol.py @@ -0,0 +1,344 @@ +#!/usr/bin/python +# +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""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() + +ARCH = "arm" + +TOOLCHAIN_INFO = None + +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 = FindToolchain() + (label, platform, target) = toolchain_info + return os.path.join(ANDROID_BUILD_TOP, "prebuilts/gcc", Uname(), platform, 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. + """ + global TOOLCHAIN_INFO + if TOOLCHAIN_INFO is not None: + return TOOLCHAIN_INFO + + ## Known toolchains, newer ones in the front. + if ARCH == "arm": + gcc_version = os.environ["TARGET_GCC_VERSION"] + known_toolchains = [ + ("arm-linux-androideabi-" + gcc_version, "arm", "arm-linux-androideabi"), + ] + elif ARCH =="x86": + known_toolchains = [ + ("i686-android-linux-4.4.3", "x86", "i686-android-linux") + ] + else: + 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 + return toolchain_info + + raise Exception("Could not find tool chain") + +def SymbolInformation(lib, addr): + """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. + """ + 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: + 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 + + addr_to_objdump = CallObjdumpForSet(lib, unique_addrs) + if not addr_to_objdump: + return None + + 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 + + +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.exists(symbols): + return None + + (label, platform, target) = FindToolchain() + cmd = [ToolPath("addr2line"), "--functions", "--inlines", + "--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() + records = [] + first = True + while True: + symbol = child.stdout.readline().strip() + if symbol == "??": + symbol = None + location = child.stdout.readline().strip() + if location == "??:0": + location = None + if symbol is None and location is None: + break + records.append((symbol, location)) + if first: + # Write a blank line as a sentinel so we know when to stop + # reading inlines from the output. + # The blank line will cause addr2line to emit "??\n??:0\n". + child.stdin.write("\n") + first = False + result[addr] = records + child.stdin.close() + child.stdout.close() + 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 + +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_dec = str(StripPC(int(addrs[0], 16))) + stop_addr_dec = str(StripPC(int(addrs[-1], 16)) + 8) + cmd = [ToolPath("objdump"), + "--section=.text", + "--demangle", + "--disassemble", + "--start-address=" + start_addr_dec, + "--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 = StripPC(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) |