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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/android_platform/README.chromium ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # 2 #
3 # Copyright (C) 2013 The Android Open Source Project 3 # Copyright (C) 2013 The Android Open Source Project
4 # 4 #
5 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License. 6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at 7 # You may obtain a copy of the License at
8 # 8 #
9 # http://www.apache.org/licenses/LICENSE-2.0 9 # http://www.apache.org/licenses/LICENSE-2.0
10 # 10 #
11 # Unless required by applicable law or agreed to in writing, software 11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS, 12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and 14 # See the License for the specific language governing permissions and
15 # limitations under the License. 15 # limitations under the License.
16 16
17 """Module for looking up symbolic debugging information. 17 """Module for looking up symbolic debugging information.
18 18
19 The information can include symbol names, offsets, and source locations. 19 The information can include symbol names, offsets, and source locations.
20 """ 20 """
21 21
22 import glob
23 import itertools
22 import os 24 import os
23 import re 25 import re
24 import subprocess 26 import subprocess
27 import zipfile
25 28
26 CHROME_SRC = os.path.join(os.path.realpath(os.path.dirname(__file__)), 29 CHROME_SRC = os.path.join(os.path.realpath(os.path.dirname(__file__)),
27 os.pardir, os.pardir, os.pardir, os.pardir) 30 os.pardir, os.pardir, os.pardir, os.pardir)
28 ANDROID_BUILD_TOP = CHROME_SRC 31 ANDROID_BUILD_TOP = CHROME_SRC
29 SYMBOLS_DIR = CHROME_SRC 32 SYMBOLS_DIR = CHROME_SRC
30 CHROME_SYMBOLS_DIR = CHROME_SRC 33 CHROME_SYMBOLS_DIR = CHROME_SRC
31 34
32 ARCH = "arm" 35 ARCH = "arm"
33 36
34 TOOLCHAIN_INFO = None 37 TOOLCHAIN_INFO = None
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
123 # Look for addr2line to check for valid toolchain path. 126 # Look for addr2line to check for valid toolchain path.
124 for (label, platform, target) in known_toolchains: 127 for (label, platform, target) in known_toolchains:
125 toolchain_info = (label, platform, target); 128 toolchain_info = (label, platform, target);
126 if os.path.exists(ToolPath("addr2line", toolchain_info)): 129 if os.path.exists(ToolPath("addr2line", toolchain_info)):
127 TOOLCHAIN_INFO = toolchain_info 130 TOOLCHAIN_INFO = toolchain_info
128 print "Using toolchain from :" + ToolPath("", TOOLCHAIN_INFO) 131 print "Using toolchain from :" + ToolPath("", TOOLCHAIN_INFO)
129 return toolchain_info 132 return toolchain_info
130 133
131 raise Exception("Could not find tool chain") 134 raise Exception("Could not find tool chain")
132 135
136 def GetAapt():
137 """Returns the path to aapt.
138
139 Args:
140 None
141
142 Returns:
143 the pathname of the 'aapt' executable.
144 """
145 sdk_home = os.path.join('third_party', 'android_tools', 'sdk')
146 sdk_home = os.environ.get('SDK_HOME', sdk_home)
147 aapt_exe = glob.glob(os.path.join(sdk_home, 'build-tools', '*', 'aapt'))
148 if not aapt_exe:
149 return None
150 return sorted(aapt_exe, key=os.path.getmtime, reverse=True)[0]
151
152 def ApkMatchPackageName(aapt, apk_path, package_name):
153 """Returns true the APK's package name matches package_name.
154
155 Args:
156 aapt: pathname for the 'aapt' executable.
157 apk_path: pathname of the APK file.
158 package_name: package name to match.
159
160 Returns:
161 True if the package name matches or aapt is None, False otherwise.
162 """
163 if not aapt:
164 # Allow false positives
165 return True
166 aapt_output = subprocess.check_output(
167 [aapt, 'dump', 'badging', apk_path]).split('\n')
168 package_name_re = re.compile(r'package: .*name=\'(\S*)\'')
169 for line in aapt_output:
170 match = package_name_re.match(line)
171 if match:
172 return package_name == match.group(1)
173 return False
174
175 def PathListJoin(prefix_list, suffix_list):
176 """Returns each prefix in prefix_list joined with each suffix in suffix list.
177
178 Args:
179 prefix_list: list of path prefixes.
180 suffix_list: list of path suffixes.
181
182 Returns:
183 List of paths each of which joins a prefix with a suffix.
184 """
185 return [
186 os.path.join(prefix, suffix)
187 for prefix in prefix_list for suffix in suffix_list ]
188
189 def GetCandidates(dirs, filepart, candidate_fun):
190 """Returns a list of candidate filenames.
191
192 Args:
193 dirs: a list of the directory part of the pathname.
194 filepart: the file part of the pathname.
195 candidate_fun: a function to apply to each candidate, returns a list.
196
197 Returns:
198 A list of candidate files ordered by modification time, newest first.
199 """
200 out_dir = os.environ.get('CHROMIUM_OUT_DIR', 'out')
201 out_dir = os.path.join(CHROME_SYMBOLS_DIR, out_dir)
202 buildtype = os.environ.get('BUILDTYPE')
203 if buildtype:
204 buildtype_list = [ buildtype ]
205 else:
206 buildtype_list = [ 'Debug', 'Release' ]
207
208 candidates = PathListJoin([out_dir], buildtype_list) + [CHROME_SYMBOLS_DIR]
209 candidates = PathListJoin(candidates, dirs)
210 candidates = PathListJoin(candidates, [filepart])
211 candidates = list(
212 itertools.chain.from_iterable(map(candidate_fun, candidates)))
213 candidates = sorted(candidates, key=os.path.getmtime, reverse=True)
214 return candidates
215
216 def GetCandidateApks():
217 """Returns a list of APKs which could contain the library.
218
219 Args:
220 None
221
222 Returns:
223 list of APK filename which could contain the library.
224 """
225 return GetCandidates(['apks'], '*.apk', glob.glob)
226
227 def GetCrazyLib(apk_filename):
228 """Returns the name of the first crazy library from this APK.
229
230 Args:
231 apk_filename: name of an APK file.
232
233 Returns:
234 Name of the first library which would be crazy loaded from this APK.
235 """
236 zip_file = zipfile.ZipFile(apk_filename, 'r')
237 for filename in zip_file.namelist():
238 match = re.match('lib/[^/]*/crazy.(lib.*[.]so)', filename)
239 if match:
240 return match.group(1)
241
242 def GetMatchingApks(device_apk_name):
243 """Find any APKs which match the package indicated by the device_apk_name.
244
245 Args:
246 device_apk_name: name of the APK on the device.
247
248 Returns:
249 A list of APK filenames which could contain the desired library.
250 """
251 match = re.match('(.*)-[0-9]+[.]apk$', device_apk_name)
252 if not match:
253 return None
254 package_name = match.group(1)
255 return filter(
256 lambda candidate_apk:
257 ApkMatchPackageName(GetAapt(), candidate_apk, package_name),
258 GetCandidateApks())
259
260 def MapDeviceApkToLibrary(device_apk_name):
261 """Provide a library name which corresponds with device_apk_name.
262
263 Args:
264 device_apk_name: name of the APK on the device.
265
266 Returns:
267 Name of the library which corresponds to that APK.
268 """
269 matching_apks = GetMatchingApks(device_apk_name)
270 for matching_apk in matching_apks:
271 crazy_lib = GetCrazyLib(matching_apk)
272 if crazy_lib:
273 return crazy_lib
274
275 def GetCandidateLibraries(library_name):
276 """Returns a list of candidate library filenames.
277
278 Args:
279 library_name: basename of the library to match.
280
281 Returns:
282 A list of matching library filenames for library_name.
283 """
284 return GetCandidates(
285 ['lib', 'lib.target'], library_name,
286 lambda filename: filter(os.path.exists, [filename]))
287
133 def TranslateLibPath(lib): 288 def TranslateLibPath(lib):
134 # SymbolInformation(lib, addr) receives lib as the path from symbols 289 # SymbolInformation(lib, addr) receives lib as the path from symbols
135 # root to the symbols file. This needs to be translated to point to the 290 # root to the symbols file. This needs to be translated to point to the
136 # correct .so path. If the user doesn't explicitly specify which directory to 291 # correct .so path. If the user doesn't explicitly specify which directory to
137 # use, then use the most recently updated one in one of the known directories. 292 # use, then use the most recently updated one in one of the known directories.
138 # If the .so is not found somewhere in CHROME_SYMBOLS_DIR, leave it 293 # If the .so is not found somewhere in CHROME_SYMBOLS_DIR, leave it
139 # untranslated in case it is an Android symbol in SYMBOLS_DIR. 294 # untranslated in case it is an Android symbol in SYMBOLS_DIR.
140 library_name = os.path.basename(lib) 295 library_name = os.path.basename(lib)
141 out_dir = os.environ.get('CHROMIUM_OUT_DIR', 'out')
142 candidate_dirs = ['.',
143 os.path.join(out_dir, 'Debug', 'lib'),
144 os.path.join(out_dir, 'Debug', 'lib.target'),
145 os.path.join(out_dir, 'Release', 'lib'),
146 os.path.join(out_dir, 'Release', 'lib.target'),
147 ]
148 296
149 candidate_libraries = map( 297 # The filename in the stack trace maybe an APK name rather than a library
150 lambda d: ('%s/%s/%s' % (CHROME_SYMBOLS_DIR, d, library_name)), 298 # name. This happens when the library was loaded directly from inside the
151 candidate_dirs) 299 # APK. If this is the case we try to figure out the library name by looking
152 candidate_libraries = filter(os.path.exists, candidate_libraries) 300 # for a matching APK file and finding the name of the library in contains.
153 candidate_libraries = sorted(candidate_libraries, 301 # The name of the APK file on the device is of the form
154 key=os.path.getmtime, reverse=True) 302 # <package_name>-<number>.apk. The APK file on the host may have any name
303 # so we look at the APK badging to see if the package name matches.
304 if re.search('-[0-9]+[.]apk$', library_name):
305 mapping = MapDeviceApkToLibrary(library_name)
306 if mapping:
307 library_name = mapping
155 308
309 candidate_libraries = GetCandidateLibraries(library_name)
156 if not candidate_libraries: 310 if not candidate_libraries:
157 return lib 311 return lib
158 312
159 library_path = os.path.relpath(candidate_libraries[0], SYMBOLS_DIR) 313 library_path = os.path.relpath(candidate_libraries[0], SYMBOLS_DIR)
160 return '/' + library_path 314 return '/' + library_path
161 315
162 def SymbolInformation(lib, addr, get_detailed_info): 316 def SymbolInformation(lib, addr, get_detailed_info):
163 """Look up symbol information about an address. 317 """Look up symbol information about an address.
164 318
165 Args: 319 Args:
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 process.stdin.write("\n") 568 process.stdin.write("\n")
415 process.stdin.close() 569 process.stdin.close()
416 demangled_symbol = process.stdout.readline().strip() 570 demangled_symbol = process.stdout.readline().strip()
417 process.stdout.close() 571 process.stdout.close()
418 return demangled_symbol 572 return demangled_symbol
419 573
420 def FormatSymbolWithOffset(symbol, offset): 574 def FormatSymbolWithOffset(symbol, offset):
421 if offset == 0: 575 if offset == 0:
422 return symbol 576 return symbol
423 return "%s+%d" % (symbol, offset) 577 return "%s+%d" % (symbol, offset)
OLDNEW
« 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