summaryrefslogtreecommitdiffstats
path: root/third_party
diff options
context:
space:
mode:
authorbulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-10 13:23:12 +0000
committerbulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-10 13:23:12 +0000
commite228d5a20e272bd1f54fd787e2dbbf26dc4eb858 (patch)
tree6cf561645a1324f7b09c9035a9fe152c7faf0070 /third_party
parentc49414a0fc71df8a014a9fb6abacfc60807ec03e (diff)
downloadchromium_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/LICENSE202
-rw-r--r--third_party/android_platform/OWNERS2
-rw-r--r--third_party/android_platform/README.chromium15
-rwxr-xr-xthird_party/android_platform/development/scripts/stack78
-rwxr-xr-xthird_party/android_platform/development/scripts/stack_core.py196
-rwxr-xr-xthird_party/android_platform/development/scripts/symbol.py344
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)