| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env 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 """stack symbolizes native crash dumps.""" | 17 """stack symbolizes native crash dumps.""" |
| 18 | 18 |
| 19 import getopt | 19 import getopt |
| 20 import glob | |
| 21 import os | 20 import os |
| 22 import re | 21 import os.path |
| 23 import sys | 22 import sys |
| 24 | 23 |
| 24 import stack_utils |
| 25 import stack_core | 25 import stack_core |
| 26 import subprocess | 26 import subprocess |
| 27 import symbol | 27 import symbol |
| 28 | 28 |
| 29 _DEFAULT_SYMROOT = '/tmp/symbols' | 29 _DEFAULT_SYMROOT = '/tmp/symbols' |
| 30 _DEFAULT_BUILD_DIR = 'out/android_Debug' | 30 _DEFAULT_BUILD_DIR = 'out/android_Debug' |
| 31 _DEFAULT_NDK_DIR = 'third_party/android_tools/ndk' | 31 _DEFAULT_NDK_DIR = 'third_party/android_tools/ndk' |
| 32 | 32 |
| 33 | 33 |
| 34 def PrintUsage(): | 34 def PrintUsage(): |
| (...skipping 29 matching lines...) Expand all Loading... |
| 64 print | 64 print |
| 65 print " FILE should contain a stack trace in it somewhere" | 65 print " FILE should contain a stack trace in it somewhere" |
| 66 print " the tool will find that and re-print it with" | 66 print " the tool will find that and re-print it with" |
| 67 print " source files and line numbers. If you don't" | 67 print " source files and line numbers. If you don't" |
| 68 print " pass FILE, or if file is -, it reads from" | 68 print " pass FILE, or if file is -, it reads from" |
| 69 print " stdin." | 69 print " stdin." |
| 70 print | 70 print |
| 71 # pylint: enable-msg=C6310 | 71 # pylint: enable-msg=C6310 |
| 72 sys.exit(1) | 72 sys.exit(1) |
| 73 | 73 |
| 74 def UnzipSymbols(symbolfile, symdir=None): | |
| 75 """Unzips a file to _DEFAULT_SYMROOT and returns the unzipped location. | |
| 76 | |
| 77 Args: | |
| 78 symbolfile: The .zip file to unzip | |
| 79 symdir: Optional temporary directory to use for extraction | |
| 80 | |
| 81 Returns: | |
| 82 A tuple containing (the directory into which the zip file was unzipped, | |
| 83 the path to the "symbols" directory in the unzipped file). To clean | |
| 84 up, the caller can delete the first element of the tuple. | |
| 85 | |
| 86 Raises: | |
| 87 SymbolDownloadException: When the unzip fails. | |
| 88 """ | |
| 89 if not symdir: | |
| 90 symdir = "%s/%s" % (_DEFAULT_SYMROOT, hash(symbolfile)) | |
| 91 if not os.path.exists(symdir): | |
| 92 os.makedirs(symdir) | |
| 93 | |
| 94 print "extracting %s..." % symbolfile | |
| 95 saveddir = os.getcwd() | |
| 96 os.chdir(symdir) | |
| 97 try: | |
| 98 unzipcode = subprocess.call(["unzip", "-qq", "-o", symbolfile]) | |
| 99 if unzipcode > 0: | |
| 100 os.remove(symbolfile) | |
| 101 raise SymbolDownloadException("failed to extract symbol files (%s)." | |
| 102 % symbolfile) | |
| 103 finally: | |
| 104 os.chdir(saveddir) | |
| 105 | |
| 106 android_symbols = glob.glob("%s/out/target/product/*/symbols" % symdir) | |
| 107 if android_symbols: | |
| 108 return (symdir, android_symbols[0]) | |
| 109 else: | |
| 110 # This is a zip of Chrome symbols, so symbol.CHROME_SYMBOLS_DIR needs to be | |
| 111 # updated to point here. | |
| 112 symbol.CHROME_SYMBOLS_DIR = symdir | |
| 113 return (symdir, symdir) | |
| 114 | |
| 115 | |
| 116 def GetBasenameFromMojoApp(url): | |
| 117 """Used by GetSymbolMapping() to extract the basename from the location the | |
| 118 mojo app was downloaded from. The location is a URL, e.g. | |
| 119 http://foo/bar/x.so.""" | |
| 120 index = url.rfind('/') | |
| 121 return url[(index + 1):] if index != -1 else url | |
| 122 | |
| 123 | |
| 124 def GetSymboledNameForMojoApp(path): | |
| 125 """Used by GetSymbolMapping to get the non-stripped library name for an | |
| 126 installed mojo app.""" | |
| 127 # e.g. tracing.mojo -> libtracing_library.so | |
| 128 name, ext = os.path.splitext(path) | |
| 129 if ext != '.mojo': | |
| 130 return path | |
| 131 return 'lib%s_library.so' % name | |
| 132 | |
| 133 | |
| 134 def GetSymbolMapping(lines): | |
| 135 """Returns a mapping (dictionary) from download file to .so.""" | |
| 136 regex = re.compile('Caching mojo app (\S+) at (\S+)') | |
| 137 mappings = {} | |
| 138 for line in lines: | |
| 139 result = regex.search(line) | |
| 140 if result: | |
| 141 url = GetBasenameFromMojoApp(result.group(1)) | |
| 142 mappings[result.group(2)] = GetSymboledNameForMojoApp(url) | |
| 143 return mappings | |
| 144 | |
| 145 | |
| 146 def _LowestAncestorContainingRelpath(dir_path, relpath): | |
| 147 """Returns the lowest ancestor dir of |dir_path| that contains |relpath|. | |
| 148 """ | |
| 149 cur_dir_path = os.path.abspath(dir_path) | |
| 150 while True: | |
| 151 if os.path.exists(os.path.join(cur_dir_path, relpath)): | |
| 152 return cur_dir_path | |
| 153 | |
| 154 next_dir_path = os.path.dirname(cur_dir_path) | |
| 155 if next_dir_path != cur_dir_path: | |
| 156 cur_dir_path = next_dir_path | |
| 157 else: | |
| 158 return None | |
| 159 | |
| 160 | |
| 161 def _GuessDir(relpath): | |
| 162 """Returns absolute path to location |relpath| in the lowest ancestor of this | |
| 163 file that contains it.""" | |
| 164 lowest_ancestor = _LowestAncestorContainingRelpath( | |
| 165 os.path.dirname(__file__), relpath) | |
| 166 if not lowest_ancestor: | |
| 167 return None | |
| 168 return os.path.join(lowest_ancestor, relpath) | |
| 169 | |
| 170 | 74 |
| 171 | 75 |
| 172 def main(): | 76 def main(): |
| 173 try: | 77 try: |
| 174 options, arguments = getopt.getopt(sys.argv[1:], "", | 78 options, arguments = getopt.getopt(sys.argv[1:], "", |
| 175 ["more-info", | 79 ["more-info", |
| 176 "less-info", | 80 "less-info", |
| 177 "build-dir=", | 81 "build-dir=", |
| 178 "ndk-dir=", | 82 "ndk-dir=", |
| 179 "symbols-dir=", | 83 "symbols-dir=", |
| (...skipping 17 matching lines...) Expand all Loading... |
| 197 elif option == "--build-dir": | 101 elif option == "--build-dir": |
| 198 symbol.BUILD_DIR = value | 102 symbol.BUILD_DIR = value |
| 199 elif option == "--ndk-dir": | 103 elif option == "--ndk-dir": |
| 200 symbol.NDK_DIR = value | 104 symbol.NDK_DIR = value |
| 201 elif option == "--more-info": | 105 elif option == "--more-info": |
| 202 more_info = True | 106 more_info = True |
| 203 elif option == "--less-info": | 107 elif option == "--less-info": |
| 204 more_info = False | 108 more_info = False |
| 205 | 109 |
| 206 if not symbol.BUILD_DIR: | 110 if not symbol.BUILD_DIR: |
| 207 guess = _GuessDir(_DEFAULT_BUILD_DIR) | 111 guess = stack_utils.GuessDir(_DEFAULT_BUILD_DIR) |
| 208 if not guess: | 112 if not guess: |
| 209 print "Couldn't find the build directory, please pass --build-dir." | 113 print "Couldn't find the build directory, please pass --build-dir." |
| 210 return 1 | 114 return 1 |
| 211 print "Inferring the build directory path as " + guess | 115 print "Inferring the build directory path as " + guess |
| 212 symbol.BUILD_DIR = guess | 116 symbol.BUILD_DIR = guess |
| 213 | 117 |
| 214 if not symbol.NDK_DIR: | 118 if not symbol.NDK_DIR: |
| 215 guess = _GuessDir(_DEFAULT_NDK_DIR) | 119 guess = stack_utils.GuessDir(_DEFAULT_NDK_DIR) |
| 216 if not guess: | 120 if not guess: |
| 217 print "Couldn't find the Android NDK, please pass --ndk-dir." | 121 print "Couldn't find the Android NDK, please pass --ndk-dir." |
| 218 return 1 | 122 return 1 |
| 219 print "Inferring the Android NDK path as " + guess | 123 print "Inferring the Android NDK path as " + guess |
| 220 symbol.NDK_DIR = guess | 124 symbol.NDK_DIR = guess |
| 221 | 125 |
| 222 | 126 |
| 223 if len(arguments) > 1: | 127 if len(arguments) > 1: |
| 224 PrintUsage() | 128 PrintUsage() |
| 225 | 129 |
| 226 if not arguments or arguments[0] == "-": | 130 if not arguments or arguments[0] == "-": |
| 227 print "Reading native crash info from stdin" | 131 print "Reading native crash info from stdin" |
| 228 f = sys.stdin | 132 f = sys.stdin |
| 229 else: | 133 else: |
| 230 print "Searching for native crashes in %s" % arguments[0] | 134 print "Searching for native crashes in %s" % arguments[0] |
| 231 f = open(arguments[0], "r") | 135 f = open(arguments[0], "r") |
| 232 | 136 |
| 233 lines = f.readlines() | 137 lines = f.readlines() |
| 234 f.close() | 138 f.close() |
| 235 | 139 |
| 236 rootdir = None | 140 rootdir = None |
| 237 if zip_arg: | 141 if zip_arg: |
| 238 rootdir, symbol.SYMBOLS_DIR = UnzipSymbols(zip_arg) | 142 rootdir, symbol.SYMBOLS_DIR = stack_utils.UnzipSymbols(zip_arg) |
| 239 | 143 |
| 240 if symbol.SYMBOLS_DIR: | 144 if symbol.SYMBOLS_DIR: |
| 241 print "Reading Android symbols from", symbol.SYMBOLS_DIR | 145 print "Reading Android symbols from", symbol.SYMBOLS_DIR |
| 242 | 146 |
| 243 print "Reading Mojo symbols from", symbol.BUILD_DIR | 147 print "Reading Mojo symbols from", symbol.BUILD_DIR |
| 244 stack_core.ConvertTrace(lines, more_info, GetSymbolMapping(lines)) | 148 stack_core.ConvertTrace(lines, more_info, stack_utils.GetSymbolMapping(lines)) |
| 245 | 149 |
| 246 if rootdir: | 150 if rootdir: |
| 247 # be a good citizen and clean up...os.rmdir and os.removedirs() don't work | 151 # be a good citizen and clean up...os.rmdir and os.removedirs() don't work |
| 248 cmd = "rm -rf \"%s\"" % rootdir | 152 cmd = "rm -rf \"%s\"" % rootdir |
| 249 print "\ncleaning up (%s)" % cmd | 153 print "\ncleaning up (%s)" % cmd |
| 250 os.system(cmd) | 154 os.system(cmd) |
| 251 | 155 |
| 252 if __name__ == "__main__": | 156 if __name__ == "__main__": |
| 253 main() | 157 main() |
| 254 | 158 |
| 255 # vi: ts=2 sw=2 | 159 # vi: ts=2 sw=2 |
| OLD | NEW |