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 |