Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(297)

Unified Diff: third_party/android_platform/development/scripts/symbol.py

Issue 18326020: Android: adds a few third-party scripts for stack symbolization. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/android_platform/development/scripts/stack_core.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/android_platform/development/scripts/symbol.py
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 0000000000000000000000000000000000000000..0f58df64cd04e15e08583f044a3c589dc8dbcf2b
--- /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)
« no previous file with comments | « third_party/android_platform/development/scripts/stack_core.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698