OLD | NEW |
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 | 22 import glob |
23 import itertools | 23 import itertools |
24 import os | 24 import os |
25 import re | 25 import re |
26 import subprocess | 26 import subprocess |
27 import zipfile | 27 import zipfile |
28 | 28 |
29 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__)), |
30 os.pardir, os.pardir) | 30 os.pardir, os.pardir, os.pardir, os.pardir) |
31 ANDROID_BUILD_TOP = CHROME_SRC | 31 ANDROID_BUILD_TOP = CHROME_SRC |
32 SYMBOLS_DIR = CHROME_SRC | 32 SYMBOLS_DIR = CHROME_SRC |
33 CHROME_SYMBOLS_DIR = CHROME_SRC | 33 CHROME_SYMBOLS_DIR = CHROME_SRC |
34 | 34 |
35 ARCH = "arm" | 35 ARCH = "arm" |
36 | 36 |
37 TOOLCHAIN_INFO = None | 37 TOOLCHAIN_INFO = None |
38 | 38 |
39 def Uname(): | 39 def Uname(): |
40 """'uname' for constructing prebuilt/<...> and out/host/<...> paths.""" | 40 """'uname' for constructing prebuilt/<...> and out/host/<...> paths.""" |
(...skipping 16 matching lines...) Expand all Loading... |
57 toolchain_prefix = "arm-linux-androideabi" | 57 toolchain_prefix = "arm-linux-androideabi" |
58 ndk = "ndk" | 58 ndk = "ndk" |
59 elif ARCH == "arm64": | 59 elif ARCH == "arm64": |
60 toolchain_source = "aarch64-linux-android-4.9" | 60 toolchain_source = "aarch64-linux-android-4.9" |
61 toolchain_prefix = "aarch64-linux-android" | 61 toolchain_prefix = "aarch64-linux-android" |
62 ndk = "ndk" | 62 ndk = "ndk" |
63 elif ARCH == "x86": | 63 elif ARCH == "x86": |
64 toolchain_source = "x86-4.9" | 64 toolchain_source = "x86-4.9" |
65 toolchain_prefix = "i686-linux-android" | 65 toolchain_prefix = "i686-linux-android" |
66 ndk = "ndk" | 66 ndk = "ndk" |
67 elif ARCH == "x86_64" or ARCH == "x64": | 67 elif ARCH == "x86_64": |
68 toolchain_source = "x86_64-4.9" | 68 toolchain_source = "x86_64-4.9" |
69 toolchain_prefix = "x86_64-linux-android" | 69 toolchain_prefix = "x86_64-linux-android" |
70 ndk = "ndk" | 70 ndk = "ndk" |
71 elif ARCH == "mips": | 71 elif ARCH == "mips": |
72 toolchain_source = "mipsel-linux-android-4.9" | 72 toolchain_source = "mipsel-linux-android-4.9" |
73 toolchain_prefix = "mipsel-linux-android" | 73 toolchain_prefix = "mipsel-linux-android" |
74 ndk = "ndk" | 74 ndk = "ndk" |
75 else: | 75 else: |
76 raise Exception("Could not find tool chain") | 76 raise Exception("Could not find tool chain") |
77 | 77 |
(...skipping 25 matching lines...) Expand all Loading... |
103 ("aarch64-linux-android-" + gcc_version, "aarch64", "aarch64-linux-android
") | 103 ("aarch64-linux-android-" + gcc_version, "aarch64", "aarch64-linux-android
") |
104 ] | 104 ] |
105 elif ARCH == "arm": | 105 elif ARCH == "arm": |
106 known_toolchains = [ | 106 known_toolchains = [ |
107 ("arm-linux-androideabi-" + gcc_version, "arm", "arm-linux-androideabi") | 107 ("arm-linux-androideabi-" + gcc_version, "arm", "arm-linux-androideabi") |
108 ] | 108 ] |
109 elif ARCH =="x86": | 109 elif ARCH =="x86": |
110 known_toolchains = [ | 110 known_toolchains = [ |
111 ("x86-" + gcc_version, "x86", "i686-linux-android") | 111 ("x86-" + gcc_version, "x86", "i686-linux-android") |
112 ] | 112 ] |
113 elif ARCH =="x86_64" or ARCH =="x64": | 113 elif ARCH =="x86_64": |
114 known_toolchains = [ | 114 known_toolchains = [ |
115 ("x86_64-" + gcc_version, "x86_64", "x86_64-linux-android") | 115 ("x86_64-" + gcc_version, "x86_64", "x86_64-linux-android") |
116 ] | 116 ] |
117 elif ARCH == "mips": | 117 elif ARCH == "mips": |
118 known_toolchains = [ | 118 known_toolchains = [ |
119 ("mipsel-linux-android-" + gcc_version, "mips", "mipsel-linux-android") | 119 ("mipsel-linux-android-" + gcc_version, "mips", "mipsel-linux-android") |
120 ] | 120 ] |
121 else: | 121 else: |
122 known_toolchains = [] | 122 known_toolchains = [] |
123 | 123 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 prefix_list: list of path prefixes. | 177 prefix_list: list of path prefixes. |
178 suffix_list: list of path suffixes. | 178 suffix_list: list of path suffixes. |
179 | 179 |
180 Returns: | 180 Returns: |
181 List of paths each of which joins a prefix with a suffix. | 181 List of paths each of which joins a prefix with a suffix. |
182 """ | 182 """ |
183 return [ | 183 return [ |
184 os.path.join(prefix, suffix) | 184 os.path.join(prefix, suffix) |
185 for prefix in prefix_list for suffix in suffix_list ] | 185 for prefix in prefix_list for suffix in suffix_list ] |
186 | 186 |
187 def GetCandidates(filepart, candidate_fun, relative_dirs=None): | 187 def GetCandidates(dirs, filepart, candidate_fun): |
188 """Returns a list of candidate filenames. | 188 """Returns a list of candidate filenames. |
189 | 189 |
190 Args: | 190 Args: |
| 191 dirs: a list of the directory part of the pathname. |
191 filepart: the file part of the pathname. | 192 filepart: the file part of the pathname. |
192 candidate_fun: a function to apply to each candidate, returns a list. | 193 candidate_fun: a function to apply to each candidate, returns a list. |
193 relative_dirs: a list of relative directory names to search from. | |
194 | 194 |
195 Returns: | 195 Returns: |
196 A list of candidate files ordered by modification time, newest first. | 196 A list of candidate files ordered by modification time, newest first. |
197 """ | 197 """ |
198 candidates = [CHROME_SYMBOLS_DIR] | 198 out_dir = os.environ.get('CHROMIUM_OUT_DIR', 'out') |
| 199 out_dir = os.path.join(CHROME_SYMBOLS_DIR, out_dir) |
| 200 buildtype = os.environ.get('BUILDTYPE') |
| 201 if buildtype: |
| 202 buildtype_list = [ buildtype ] |
| 203 else: |
| 204 buildtype_list = [ 'Debug', 'Release' ] |
199 | 205 |
200 # Add the two possible mojo outdirs. | 206 candidates = PathListJoin([out_dir], buildtype_list) + [CHROME_SYMBOLS_DIR] |
201 out_dir = os.path.join(CHROME_SYMBOLS_DIR, 'out') | 207 candidates = PathListJoin(candidates, dirs) |
202 candidates += PathListJoin([out_dir], ['android_Debug', 'android_Release']) | |
203 | |
204 if relative_dirs: | |
205 candidates = PathListJoin(candidates, relative_dirs) | |
206 | |
207 candidates = PathListJoin(candidates, [filepart]) | 208 candidates = PathListJoin(candidates, [filepart]) |
208 candidates = list( | 209 candidates = list( |
209 itertools.chain.from_iterable(map(candidate_fun, candidates))) | 210 itertools.chain.from_iterable(map(candidate_fun, candidates))) |
210 candidates = sorted(candidates, key=os.path.getmtime, reverse=True) | 211 candidates = sorted(candidates, key=os.path.getmtime, reverse=True) |
| 212 # candidates = ['/usr/local/google/home/qsr/programmes/mojo/src/out/android_De
bug/libmojo_shell.so'] |
211 return candidates | 213 return candidates |
212 | 214 |
213 def GetCandidateApks(): | 215 def GetCandidateApks(): |
214 """Returns a list of APKs which could contain the library. | 216 """Returns a list of APKs which could contain the library. |
215 | 217 |
216 Args: | 218 Args: |
217 None | 219 None |
218 | 220 |
219 Returns: | 221 Returns: |
220 list of APK filename which could contain the library. | 222 list of APK filename which could contain the library. |
221 """ | 223 """ |
222 return GetCandidates('*.apk', glob.glob, relative_dirs=['apks']) | 224 return GetCandidates(['apks'], '*.apk', glob.glob) |
223 | 225 |
224 def GetCrazyLib(apk_filename): | 226 def GetCrazyLib(apk_filename): |
225 """Returns the name of the first crazy library from this APK. | 227 """Returns the name of the first crazy library from this APK. |
226 | 228 |
227 Args: | 229 Args: |
228 apk_filename: name of an APK file. | 230 apk_filename: name of an APK file. |
229 | 231 |
230 Returns: | 232 Returns: |
231 Name of the first library which would be crazy loaded from this APK. | 233 Name of the first library which would be crazy loaded from this APK. |
232 """ | 234 """ |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 def GetCandidateLibraries(library_name): | 274 def GetCandidateLibraries(library_name): |
273 """Returns a list of candidate library filenames. | 275 """Returns a list of candidate library filenames. |
274 | 276 |
275 Args: | 277 Args: |
276 library_name: basename of the library to match. | 278 library_name: basename of the library to match. |
277 | 279 |
278 Returns: | 280 Returns: |
279 A list of matching library filenames for library_name. | 281 A list of matching library filenames for library_name. |
280 """ | 282 """ |
281 return GetCandidates( | 283 return GetCandidates( |
282 library_name, | 284 ['', 'lib', 'lib.target'], library_name, |
283 lambda filename: filter(os.path.exists, [filename])) | 285 lambda filename: filter(os.path.exists, [filename])) |
284 | 286 |
285 def TranslatePathFromDeviceToLocal(lib): | 287 def TranslateLibPath(lib): |
286 """Maps a path as seen on the device to a path on the local file system | 288 # SymbolInformation(lib, addr) receives lib as the path from symbols |
287 containing symbols. | 289 # root to the symbols file. This needs to be translated to point to the |
288 | 290 # correct .so path. If the user doesn't explicitly specify which directory to |
289 Args: | 291 # use, then use the most recently updated one in one of the known directories. |
290 lib: library (or executable) pathname from device. | 292 # If the .so is not found somewhere in CHROME_SYMBOLS_DIR, leave it |
291 """ | 293 # untranslated in case it is an Android symbol in SYMBOLS_DIR. |
292 | |
293 # SymbolInformation(lib, addr) receives lib that is either a basename or | |
294 # the path from symbols root to the symbols file. This needs to be translated | |
295 # to point to the correct .so path. If the user doesn't explicitly specify | |
296 # which directory to use, then use the most recently updated one in one of | |
297 # the known directories. | |
298 library_name = os.path.basename(lib) | 294 library_name = os.path.basename(lib) |
299 | 295 |
300 # The filename in the stack trace maybe an APK name rather than a library | 296 # The filename in the stack trace maybe an APK name rather than a library |
301 # name. This happens when the library was loaded directly from inside the | 297 # name. This happens when the library was loaded directly from inside the |
302 # APK. If this is the case we try to figure out the library name by looking | 298 # APK. If this is the case we try to figure out the library name by looking |
303 # for a matching APK file and finding the name of the library in contains. | 299 # for a matching APK file and finding the name of the library in contains. |
304 # The name of the APK file on the device is of the form | 300 # The name of the APK file on the device is of the form |
305 # <package_name>-<number>.apk. The APK file on the host may have any name | 301 # <package_name>-<number>.apk. The APK file on the host may have any name |
306 # so we look at the APK badging to see if the package name matches. | 302 # so we look at the APK badging to see if the package name matches. |
307 if re.search('-[0-9]+[.]apk$', library_name): | 303 if re.search('-[0-9]+[.]apk$', library_name): |
308 mapping = MapDeviceApkToLibrary(library_name) | 304 mapping = MapDeviceApkToLibrary(library_name) |
309 if mapping: | 305 if mapping: |
310 library_name = mapping | 306 library_name = mapping |
311 | 307 |
312 candidate_libraries = GetCandidateLibraries(library_name) | 308 candidate_libraries = GetCandidateLibraries(library_name) |
313 return (candidate_libraries[0] if candidate_libraries else | 309 if not candidate_libraries: |
314 os.path.join(SYMBOLS_DIR, lib)) | 310 return lib |
| 311 |
| 312 library_path = os.path.relpath(candidate_libraries[0], SYMBOLS_DIR) |
| 313 return '/' + library_path |
315 | 314 |
316 def SymbolInformation(lib, addr, get_detailed_info): | 315 def SymbolInformation(lib, addr, get_detailed_info): |
317 """Look up symbol information about an address. | 316 """Look up symbol information about an address. |
318 | 317 |
319 Args: | 318 Args: |
320 lib: library (or executable) pathname containing symbols | 319 lib: library (or executable) pathname containing symbols |
321 addr: string hexidecimal address | 320 addr: string hexidecimal address |
322 | 321 |
323 Returns: | 322 Returns: |
324 A list of the form [(source_symbol, source_location, | 323 A list of the form [(source_symbol, source_location, |
325 object_symbol_with_offset)]. | 324 object_symbol_with_offset)]. |
326 | 325 |
327 If the function has been inlined then the list may contain | 326 If the function has been inlined then the list may contain |
328 more than one element with the symbols for the most deeply | 327 more than one element with the symbols for the most deeply |
329 nested inlined location appearing first. The list is | 328 nested inlined location appearing first. The list is |
330 always non-empty, even if no information is available. | 329 always non-empty, even if no information is available. |
331 | 330 |
332 Usually you want to display the source_location and | 331 Usually you want to display the source_location and |
333 object_symbol_with_offset from the last element in the list. | 332 object_symbol_with_offset from the last element in the list. |
334 """ | 333 """ |
335 lib = TranslatePathFromDeviceToLocal(lib) | 334 lib = TranslateLibPath(lib) |
336 info = SymbolInformationForSet(lib, set([addr]), get_detailed_info) | 335 info = SymbolInformationForSet(lib, set([addr]), get_detailed_info) |
337 return (info and info.get(addr)) or [(None, None, None)] | 336 return (info and info.get(addr)) or [(None, None, None)] |
338 | 337 |
339 | 338 |
340 def SymbolInformationForSet(lib, unique_addrs, get_detailed_info): | 339 def SymbolInformationForSet(lib, unique_addrs, get_detailed_info): |
341 """Look up symbol information for a set of addresses from the given library. | 340 """Look up symbol information for a set of addresses from the given library. |
342 | 341 |
343 Args: | 342 Args: |
344 lib: library (or executable) pathname containing symbols | 343 lib: library (or executable) pathname containing symbols |
345 unique_addrs: set of hexidecimal addresses | 344 unique_addrs: set of hexidecimal addresses |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
416 | 415 |
417 Returns: | 416 Returns: |
418 A dictionary of the form {addr: [(symbol, file:line)]} where | 417 A dictionary of the form {addr: [(symbol, file:line)]} where |
419 each address has a list of associated symbols and locations | 418 each address has a list of associated symbols and locations |
420 or an empty list if no symbol information was found. | 419 or an empty list if no symbol information was found. |
421 | 420 |
422 If the function has been inlined then the list may contain | 421 If the function has been inlined then the list may contain |
423 more than one element with the symbols for the most deeply | 422 more than one element with the symbols for the most deeply |
424 nested inlined location appearing first. | 423 nested inlined location appearing first. |
425 """ | 424 """ |
426 if not lib or not os.path.isfile(lib): | 425 if not lib: |
| 426 return None |
| 427 |
| 428 |
| 429 symbols = SYMBOLS_DIR + lib |
| 430 if not os.path.isfile(symbols): |
427 return None | 431 return None |
428 | 432 |
429 (label, platform, target) = FindToolchain() | 433 (label, platform, target) = FindToolchain() |
430 cmd = [ToolPath("addr2line"), "--functions", "--inlines", | 434 cmd = [ToolPath("addr2line"), "--functions", "--inlines", |
431 "--demangle", "--exe=" + lib] | 435 "--demangle", "--exe=" + symbols] |
432 child = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) | 436 child = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
433 | 437 |
434 result = {} | 438 result = {} |
435 addrs = sorted(unique_addrs) | 439 addrs = sorted(unique_addrs) |
436 for addr in addrs: | 440 for addr in addrs: |
437 child.stdin.write("0x%s\n" % addr) | 441 child.stdin.write("0x%s\n" % addr) |
438 child.stdin.flush() | 442 child.stdin.flush() |
439 records = [] | 443 records = [] |
440 first = True | 444 first = True |
441 while True: | 445 while True: |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
563 process.stdin.write("\n") | 567 process.stdin.write("\n") |
564 process.stdin.close() | 568 process.stdin.close() |
565 demangled_symbol = process.stdout.readline().strip() | 569 demangled_symbol = process.stdout.readline().strip() |
566 process.stdout.close() | 570 process.stdout.close() |
567 return demangled_symbol | 571 return demangled_symbol |
568 | 572 |
569 def FormatSymbolWithOffset(symbol, offset): | 573 def FormatSymbolWithOffset(symbol, offset): |
570 if offset == 0: | 574 if offset == 0: |
571 return symbol | 575 return symbol |
572 return "%s+%d" % (symbol, offset) | 576 return "%s+%d" % (symbol, offset) |
OLD | NEW |