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 |
index 0f58df64cd04e15e08583f044a3c589dc8dbcf2b..0f1cf647a485822bfcc9e3df807cafaad6ad8579 100755 |
--- a/third_party/android_platform/development/scripts/symbol.py |
+++ b/third_party/android_platform/development/scripts/symbol.py |
@@ -23,23 +23,11 @@ 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() |
+CHROME_SRC = os.path.join(os.path.realpath(os.path.dirname(__file__)), |
+ os.pardir, os.pardir, os.pardir, os.pardir) |
+ANDROID_BUILD_TOP = CHROME_SRC |
+SYMBOLS_DIR = CHROME_SRC |
+CHROME_SYMBOLS_DIR = CHROME_SRC |
ARCH = "arm" |
@@ -59,11 +47,22 @@ def 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) |
+ # ToolPath looks for the tools in the completely incorrect directory. |
+ # This looks in the checked in android_tools. |
+ if ARCH == "arm": |
+ toolchain_source = "arm-linux-androideabi-4.6" |
+ toolchain_prefix = "arm-linux-androideabi" |
+ else: |
+ toolchain_source = "x86-4.6" |
+ toolchain_prefix = "i686-android-linux" |
+ |
+ toolchain_subdir = ( |
+ "third_party/android_tools/ndk/toolchains/%s/prebuilt/linux-x86_64/bin" % |
+ toolchain_source) |
+ |
+ return os.path.join(CHROME_SRC, |
+ toolchain_subdir, |
+ toolchain_prefix + "-" + tool) |
def FindToolchain(): |
"""Look for the latest available toolchain |
@@ -80,9 +79,8 @@ def FindToolchain(): |
## 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"), |
+ ("arm-linux-androideabi-4.6", "arm", "arm-linux-androideabi"), |
] |
elif ARCH =="x86": |
known_toolchains = [ |
@@ -100,7 +98,35 @@ def FindToolchain(): |
raise Exception("Could not find tool chain") |
-def SymbolInformation(lib, addr): |
+def TranslateLibPath(lib): |
+ # SymbolInformation(lib, addr) receives lib as the path from symbols |
+ # root to the symbols file. This needs to be translated to point to the |
+ # correct .so path. If the user doesn't explicitly specify which directory to |
+ # use, then use the most recently updated one in one of the known directories. |
+ # If the .so is not found somewhere in CHROME_SYMBOLS_DIR, leave it |
+ # untranslated in case it is an Android symbol in SYMBOLS_DIR. |
+ library_name = os.path.basename(lib) |
+ candidate_dirs = ['.', |
+ 'out/Debug/lib', |
+ 'out/Debug/lib.target', |
+ 'out/Release/lib', |
+ 'out/Release/lib.target', |
+ ] |
+ |
+ candidate_libraries = map( |
+ lambda d: ('%s/%s/%s' % (CHROME_SYMBOLS_DIR, d, library_name)), |
+ candidate_dirs) |
+ candidate_libraries = filter(os.path.exists, candidate_libraries) |
+ candidate_libraries = sorted(candidate_libraries, |
+ key=os.path.getmtime, reverse=True) |
+ |
+ if not candidate_libraries: |
+ return lib |
+ |
+ library_path = os.path.relpath(candidate_libraries[0], SYMBOLS_DIR) |
+ return '/' + library_path |
+ |
+def SymbolInformation(lib, addr, get_detailed_info): |
"""Look up symbol information about an address. |
Args: |
@@ -119,11 +145,12 @@ def SymbolInformation(lib, addr): |
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])) |
+ lib = TranslateLibPath(lib) |
+ info = SymbolInformationForSet(lib, set([addr]), get_detailed_info) |
return (info and info.get(addr)) or [(None, None, None)] |
-def SymbolInformationForSet(lib, unique_addrs): |
+def SymbolInformationForSet(lib, unique_addrs, get_detailed_info): |
"""Look up symbol information for a set of addresses from the given library. |
Args: |
@@ -150,9 +177,12 @@ def SymbolInformationForSet(lib, unique_addrs): |
if not addr_to_line: |
return None |
- addr_to_objdump = CallObjdumpForSet(lib, unique_addrs) |
- if not addr_to_objdump: |
- return None |
+ if get_detailed_info: |
+ addr_to_objdump = CallObjdumpForSet(lib, unique_addrs) |
+ if not addr_to_objdump: |
+ return None |
+ else: |
+ addr_to_objdump = dict((addr, ("", 0)) for addr in unique_addrs) |
result = {} |
for addr in unique_addrs: |
@@ -171,6 +201,25 @@ def SymbolInformationForSet(lib, unique_addrs): |
return result |
+class MemoizedForSet(object): |
+ def __init__(self, fn): |
+ self.fn = fn |
+ self.cache = {} |
+ |
+ def __call__(self, lib, unique_addrs): |
+ lib_cache = self.cache.setdefault(lib, {}) |
+ |
+ no_cache = filter(lambda x: x not in lib_cache, unique_addrs) |
+ if no_cache: |
+ lib_cache.update((k, None) for k in no_cache) |
+ result = self.fn(lib, no_cache) |
+ if result: |
+ lib_cache.update(result) |
+ |
+ return dict((k, lib_cache[k]) for k in unique_addrs if lib_cache[k]) |
+ |
+ |
+@MemoizedForSet |
def CallAddr2LineForSet(lib, unique_addrs): |
"""Look up line and symbol information for a set of addresses. |
@@ -244,6 +293,7 @@ def StripPC(addr): |
return addr & ~1 |
return addr |
+@MemoizedForSet |
def CallObjdumpForSet(lib, unique_addrs): |
"""Use objdump to find out the names of the containing functions. |
@@ -265,16 +315,7 @@ def CallObjdumpForSet(lib, unique_addrs): |
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] |
+ result = {} |
# Function lines look like: |
# 000177b0 <android::IBinder::~IBinder()+0x2c>: |
@@ -284,46 +325,51 @@ def CallObjdumpForSet(lib, unique_addrs): |
offset_regexp = re.compile("(.*)\+0x([a-f0-9]*)") |
# A disassembly line looks like: |
- # 177b2: b510 push {r4, lr} |
+ # 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) |
+ for target_addr in unique_addrs: |
+ start_addr_dec = str(StripPC(int(target_addr, 16))) |
+ stop_addr_dec = str(StripPC(int(target_addr, 16)) + 8) |
+ cmd = [ToolPath("objdump"), |
+ "--section=.text", |
+ "--demangle", |
+ "--disassemble", |
+ "--start-address=" + start_addr_dec, |
+ "--stop-address=" + stop_addr_dec, |
+ symbols] |
+ |
+ current_symbol = None # The current function symbol in the disassembly. |
+ current_symbol_addr = 0 # The address of the current function. |
+ |
+ stream = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout |
+ for line in stream: |
+ # Is it a function line like: |
+ # 000177b0 <android::IBinder::~IBinder()>: |
+ components = func_regexp.match(line) |
+ if components: |
+ # This is a new function, so record the current function and its address. |
+ current_symbol_addr = int(components.group(1), 16) |
+ current_symbol = components.group(2) |
+ |
+ # Does it have an optional offset like: "foo(..)+0x2c"? |
+ components = offset_regexp.match(current_symbol) |
+ if components: |
+ current_symbol = components.group(1) |
+ offset = components.group(2) |
+ if offset: |
+ current_symbol_addr -= int(offset, 16) |
+ |
+ # Is it an disassembly line like: |
+ # 177b2: b510 push {r4, lr} |
+ components = asm_regexp.match(line) |
if components: |
- 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() |
+ addr = components.group(1) |
+ i_addr = int(addr, 16) |
+ i_target = StripPC(int(target_addr, 16)) |
+ if i_addr == i_target: |
+ result[target_addr] = (current_symbol, i_target - current_symbol_addr) |
+ stream.close() |
return result |