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

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

Issue 401003003: Stack trace support for libraries loaded directly from APKs (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix typo in comment. Created 6 years, 5 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/README.chromium ('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
index df7f2c4b138ee1e6b2570d2f59eceb83e2c16855..44d0238391329d49e8fc952c79883172e0f24729 100755
--- a/third_party/android_platform/development/scripts/symbol.py
+++ b/third_party/android_platform/development/scripts/symbol.py
@@ -19,9 +19,12 @@
The information can include symbol names, offsets, and source locations.
"""
+import glob
+import itertools
import os
import re
import subprocess
+import zipfile
CHROME_SRC = os.path.join(os.path.realpath(os.path.dirname(__file__)),
os.pardir, os.pardir, os.pardir, os.pardir)
@@ -130,6 +133,158 @@ def FindToolchain():
raise Exception("Could not find tool chain")
+def GetAapt():
+ """Returns the path to aapt.
+
+ Args:
+ None
+
+ Returns:
+ the pathname of the 'aapt' executable.
+ """
+ sdk_home = os.path.join('third_party', 'android_tools', 'sdk')
+ sdk_home = os.environ.get('SDK_HOME', sdk_home)
+ aapt_exe = glob.glob(os.path.join(sdk_home, 'build-tools', '*', 'aapt'))
+ if not aapt_exe:
+ return None
+ return sorted(aapt_exe, key=os.path.getmtime, reverse=True)[0]
+
+def ApkMatchPackageName(aapt, apk_path, package_name):
+ """Returns true the APK's package name matches package_name.
+
+ Args:
+ aapt: pathname for the 'aapt' executable.
+ apk_path: pathname of the APK file.
+ package_name: package name to match.
+
+ Returns:
+ True if the package name matches or aapt is None, False otherwise.
+ """
+ if not aapt:
+ # Allow false positives
+ return True
+ aapt_output = subprocess.check_output(
+ [aapt, 'dump', 'badging', apk_path]).split('\n')
+ package_name_re = re.compile(r'package: .*name=\'(\S*)\'')
+ for line in aapt_output:
+ match = package_name_re.match(line)
+ if match:
+ return package_name == match.group(1)
+ return False
+
+def PathListJoin(prefix_list, suffix_list):
+ """Returns each prefix in prefix_list joined with each suffix in suffix list.
+
+ Args:
+ prefix_list: list of path prefixes.
+ suffix_list: list of path suffixes.
+
+ Returns:
+ List of paths each of which joins a prefix with a suffix.
+ """
+ return [
+ os.path.join(prefix, suffix)
+ for prefix in prefix_list for suffix in suffix_list ]
+
+def GetCandidates(dirs, filepart, candidate_fun):
+ """Returns a list of candidate filenames.
+
+ Args:
+ dirs: a list of the directory part of the pathname.
+ filepart: the file part of the pathname.
+ candidate_fun: a function to apply to each candidate, returns a list.
+
+ Returns:
+ A list of candidate files ordered by modification time, newest first.
+ """
+ out_dir = os.environ.get('CHROMIUM_OUT_DIR', 'out')
+ out_dir = os.path.join(CHROME_SYMBOLS_DIR, out_dir)
+ buildtype = os.environ.get('BUILDTYPE')
+ if buildtype:
+ buildtype_list = [ buildtype ]
+ else:
+ buildtype_list = [ 'Debug', 'Release' ]
+
+ candidates = PathListJoin([out_dir], buildtype_list) + [CHROME_SYMBOLS_DIR]
+ candidates = PathListJoin(candidates, dirs)
+ candidates = PathListJoin(candidates, [filepart])
+ candidates = list(
+ itertools.chain.from_iterable(map(candidate_fun, candidates)))
+ candidates = sorted(candidates, key=os.path.getmtime, reverse=True)
+ return candidates
+
+def GetCandidateApks():
+ """Returns a list of APKs which could contain the library.
+
+ Args:
+ None
+
+ Returns:
+ list of APK filename which could contain the library.
+ """
+ return GetCandidates(['apks'], '*.apk', glob.glob)
+
+def GetCrazyLib(apk_filename):
+ """Returns the name of the first crazy library from this APK.
+
+ Args:
+ apk_filename: name of an APK file.
+
+ Returns:
+ Name of the first library which would be crazy loaded from this APK.
+ """
+ zip_file = zipfile.ZipFile(apk_filename, 'r')
+ for filename in zip_file.namelist():
+ match = re.match('lib/[^/]*/crazy.(lib.*[.]so)', filename)
+ if match:
+ return match.group(1)
+
+def GetMatchingApks(device_apk_name):
+ """Find any APKs which match the package indicated by the device_apk_name.
+
+ Args:
+ device_apk_name: name of the APK on the device.
+
+ Returns:
+ A list of APK filenames which could contain the desired library.
+ """
+ match = re.match('(.*)-[0-9]+[.]apk$', device_apk_name)
+ if not match:
+ return None
+ package_name = match.group(1)
+ return filter(
+ lambda candidate_apk:
+ ApkMatchPackageName(GetAapt(), candidate_apk, package_name),
+ GetCandidateApks())
+
+def MapDeviceApkToLibrary(device_apk_name):
+ """Provide a library name which corresponds with device_apk_name.
+
+ Args:
+ device_apk_name: name of the APK on the device.
+
+ Returns:
+ Name of the library which corresponds to that APK.
+ """
+ matching_apks = GetMatchingApks(device_apk_name)
+ for matching_apk in matching_apks:
+ crazy_lib = GetCrazyLib(matching_apk)
+ if crazy_lib:
+ return crazy_lib
+
+def GetCandidateLibraries(library_name):
+ """Returns a list of candidate library filenames.
+
+ Args:
+ library_name: basename of the library to match.
+
+ Returns:
+ A list of matching library filenames for library_name.
+ """
+ return GetCandidates(
+ ['lib', 'lib.target'], library_name,
+ lambda filename: filter(os.path.exists, [filename]))
+
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
@@ -138,21 +293,20 @@ def TranslateLibPath(lib):
# 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)
- out_dir = os.environ.get('CHROMIUM_OUT_DIR', 'out')
- candidate_dirs = ['.',
- os.path.join(out_dir, 'Debug', 'lib'),
- os.path.join(out_dir, 'Debug', 'lib.target'),
- os.path.join(out_dir, 'Release', 'lib'),
- os.path.join(out_dir, '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)
+ # The filename in the stack trace maybe an APK name rather than a library
+ # name. This happens when the library was loaded directly from inside the
+ # APK. If this is the case we try to figure out the library name by looking
+ # for a matching APK file and finding the name of the library in contains.
+ # The name of the APK file on the device is of the form
+ # <package_name>-<number>.apk. The APK file on the host may have any name
+ # so we look at the APK badging to see if the package name matches.
+ if re.search('-[0-9]+[.]apk$', library_name):
+ mapping = MapDeviceApkToLibrary(library_name)
+ if mapping:
+ library_name = mapping
+
+ candidate_libraries = GetCandidateLibraries(library_name)
if not candidate_libraries:
return lib
« no previous file with comments | « third_party/android_platform/README.chromium ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698