| 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 |