blob: bab338c22c951ed3ccbaecb8a466cd6d5477ad2d [file] [log] [blame]
Greg Clayton01f7c962012-01-20 03:15:45 +00001#!/usr/bin/python
2
3#----------------------------------------------------------------------
4# Be sure to add the python path that points to the LLDB shared library.
Greg Claytona3698c62012-01-21 00:37:19 +00005#
6# To use this in the embedded python interpreter using "lldb":
7#
8# cd /path/containing/crashlog.py
9# lldb
10# (lldb) script import crashlog
11# "crashlog" command installed, type "crashlog --help" for detailed help
12# (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash
13#
14# The benefit of running the crashlog command inside lldb in the
15# embedded python interpreter is when the command completes, there
16# will be a target with all of the files loaded at the locations
17# described in the crash log. Only the files that have stack frames
18# in the backtrace will be loaded unless the "--load-all" option
19# has been specified. This allows users to explore the program in the
20# state it was in right at crash time.
21#
Greg Clayton01f7c962012-01-20 03:15:45 +000022# On MacOSX csh, tcsh:
Greg Claytona3698c62012-01-21 00:37:19 +000023# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash )
24#
Greg Clayton01f7c962012-01-20 03:15:45 +000025# On MacOSX sh, bash:
Greg Claytona3698c62012-01-21 00:37:19 +000026# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash
Greg Clayton01f7c962012-01-20 03:15:45 +000027#----------------------------------------------------------------------
28
29import lldb
Greg Claytona3698c62012-01-21 00:37:19 +000030import commands
Greg Clayton01f7c962012-01-20 03:15:45 +000031import optparse
32import os
33import plistlib
Greg Claytona3698c62012-01-21 00:37:19 +000034#import pprint # pp = pprint.PrettyPrinter(indent=4); pp.pprint(command_args)
Greg Clayton01f7c962012-01-20 03:15:45 +000035import re
36import sys
37import time
Greg Claytoncd793122012-01-20 06:12:47 +000038import uuid
39
Greg Clayton01f7c962012-01-20 03:15:45 +000040
41PARSE_MODE_NORMAL = 0
42PARSE_MODE_THREAD = 1
43PARSE_MODE_IMAGES = 2
44PARSE_MODE_THREGS = 3
45PARSE_MODE_SYSTEM = 4
46
47class CrashLog:
48 """Class that does parses darwin crash logs"""
49 thread_state_regex = re.compile('^Thread ([0-9]+) crashed with')
50 thread_regex = re.compile('^Thread ([0-9]+)([^:]*):(.*)')
Greg Clayton8077a532012-01-20 03:32:35 +000051 frame_regex = re.compile('^([0-9]+) +([^ ]+) *\t(0x[0-9a-fA-F]+) +(.*)')
52 image_regex_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^<]+)<([-0-9a-fA-F]+)> (.*)');
53 image_regex_no_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^/]+)/(.*)');
Greg Clayton01f7c962012-01-20 03:15:45 +000054 empty_line_regex = re.compile('^$')
55
56 class Thread:
57 """Class that represents a thread in a darwin crash log"""
58 def __init__(self, index):
59 self.index = index
60 self.frames = list()
61 self.registers = dict()
62 self.reason = None
63 self.queue = None
64
65 def dump(self, prefix):
66 print "%sThread[%u] %s" % (prefix, self.index, self.reason)
67 if self.frames:
68 print "%s Frames:" % (prefix)
69 for frame in self.frames:
70 frame.dump(prefix + ' ')
71 if self.registers:
72 print "%s Registers:" % (prefix)
73 for reg in self.registers.keys():
74 print "%s %-5s = %#16.16x" % (prefix, reg, self.registers[reg])
75
76 def did_crash(self):
77 return self.reason != None
78
79 def __str__(self):
80 s = "Thread[%u]" % self.index
81 if self.reason:
82 s += ' %s' % self.reason
83 return s
84
85
86 class Frame:
87 """Class that represents a stack frame in a thread in a darwin crash log"""
88 def __init__(self, index, pc, details):
89 self.index = index
90 self.pc = pc
91 self.sym_ctx = None
92 self.details = details
93
94 def __str__(self):
95 return "[%2u] %#16.16x %s" % (self.index, self.pc, self.details)
96
97 def dump(self, prefix):
98 print "%s%s" % (prefix, self)
99
100
101 class Image:
102 """Class that represents a binary images in a darwin crash log"""
Greg Clayton8077a532012-01-20 03:32:35 +0000103 dsymForUUIDBinary = os.path.expanduser('~rc/bin/dsymForUUID')
Greg Claytoncd793122012-01-20 06:12:47 +0000104 if not os.path.exists(dsymForUUIDBinary):
105 dsymForUUIDBinary = commands.getoutput('which dsymForUUID')
106
107 dwarfdump_uuid_regex = re.compile('UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*')
Greg Clayton8077a532012-01-20 03:32:35 +0000108
Greg Clayton01f7c962012-01-20 03:15:45 +0000109 def __init__(self, text_addr_lo, text_addr_hi, ident, version, uuid, path):
110 self.text_addr_lo = text_addr_lo
111 self.text_addr_hi = text_addr_hi
112 self.ident = ident
113 self.version = version
Greg Claytoncd793122012-01-20 06:12:47 +0000114 self.arch = None
Greg Clayton01f7c962012-01-20 03:15:45 +0000115 self.uuid = uuid
116 self.path = path
Greg Claytoncd793122012-01-20 06:12:47 +0000117 self.resolved_path = None
118 self.dsym = None
Greg Clayton01f7c962012-01-20 03:15:45 +0000119 self.module = None
Greg Clayton01f7c962012-01-20 03:15:45 +0000120
121 def dump(self, prefix):
122 print "%s%s" % (prefix, self)
123
124 def __str__(self):
Greg Claytoncd793122012-01-20 06:12:47 +0000125 return "%#16.16x %s %s" % (self.text_addr_lo, self.uuid, self.get_resolved_path())
Greg Clayton01f7c962012-01-20 03:15:45 +0000126
Greg Claytoncd793122012-01-20 06:12:47 +0000127 def get_resolved_path(self):
128 if self.resolved_path:
129 return self.resolved_path
130 elif self.path:
131 return self.path
132 return None
133
134 def get_resolved_path_basename(self):
135 path = self.get_resolved_path()
136 if path:
137 return os.path.basename(path)
138 return None
139
140 def dsym_basename(self):
141 if self.dsym:
142 return os.path.basename(self.dsym)
Greg Clayton01f7c962012-01-20 03:15:45 +0000143 return None
144
145 def fetch_symboled_executable_and_dsym(self):
Greg Claytoncd793122012-01-20 06:12:47 +0000146 if self.resolved_path:
147 # Don't load a module twice...
148 return 0
149 print 'Locating %s %s...' % (self.uuid, self.path),
Greg Clayton8077a532012-01-20 03:32:35 +0000150 if os.path.exists(self.dsymForUUIDBinary):
151 dsym_for_uuid_command = '%s %s' % (self.dsymForUUIDBinary, self.uuid)
Greg Clayton8077a532012-01-20 03:32:35 +0000152 s = commands.getoutput(dsym_for_uuid_command)
153 if s:
154 plist_root = plistlib.readPlistFromString (s)
155 if plist_root:
Greg Claytoncd793122012-01-20 06:12:47 +0000156 plist = plist_root[self.uuid]
157 if plist:
158 if 'DBGArchitecture' in plist:
159 self.arch = plist['DBGArchitecture']
160 if 'DBGDSYMPath' in plist:
161 self.dsym = os.path.realpath(plist['DBGDSYMPath'])
162 if 'DBGSymbolRichExecutable' in plist:
163 self.resolved_path = os.path.expanduser (plist['DBGSymbolRichExecutable'])
164 if not self.resolved_path and os.path.exists(self.path):
165 dwarfdump_cmd_output = commands.getoutput('dwarfdump --uuid "%s"' % self.path)
166 self_uuid = uuid.UUID(self.uuid)
167 for line in dwarfdump_cmd_output.splitlines():
168 match = self.dwarfdump_uuid_regex.search (line)
169 if match:
170 dwarf_uuid_str = match.group(1)
171 dwarf_uuid = uuid.UUID(dwarf_uuid_str)
172 if self_uuid == dwarf_uuid:
173 self.resolved_path = self.path
174 self.arch = match.group(2)
175 break;
176 if not self.resolved_path:
177 print "error: file %s '%s' doesn't match the UUID in the installed file" % (self.uuid, self.path)
178 return 0
179 if (self.resolved_path and os.path.exists(self.resolved_path)) or (self.path and os.path.exists(self.path)):
180 print 'ok'
181 if self.path != self.resolved_path:
182 print ' exe = "%s"' % self.resolved_path
183 if self.dsym:
184 print ' dsym = "%s"' % self.dsym
185 return 1
Greg Clayton01f7c962012-01-20 03:15:45 +0000186 else:
Greg Claytoncd793122012-01-20 06:12:47 +0000187 return 0
Greg Clayton01f7c962012-01-20 03:15:45 +0000188
189 def load_module(self):
Greg Claytone9ee5502012-01-20 19:25:32 +0000190 if not lldb.target:
191 return 'error: no target'
Greg Clayton01f7c962012-01-20 03:15:45 +0000192 if self.module:
193 text_section = self.module.FindSection ("__TEXT")
194 if text_section:
Greg Claytone9ee5502012-01-20 19:25:32 +0000195 error = lldb.target.SetSectionLoadAddress (text_section, self.text_addr_lo)
Greg Clayton01f7c962012-01-20 03:15:45 +0000196 if error.Success():
Greg Clayton8077a532012-01-20 03:32:35 +0000197 #print 'Success: loaded %s.__TEXT = 0x%x' % (self.basename(), self.text_addr_lo)
Greg Clayton01f7c962012-01-20 03:15:45 +0000198 return None
199 else:
200 return 'error: %s' % error.GetCString()
201 else:
Greg Claytoncd793122012-01-20 06:12:47 +0000202 return 'error: unable to find "__TEXT" section in "%s"' % self.get_resolved_path()
Greg Clayton01f7c962012-01-20 03:15:45 +0000203 else:
204 return 'error: invalid module'
205
Greg Claytone9ee5502012-01-20 19:25:32 +0000206 def create_target(self):
Greg Clayton01f7c962012-01-20 03:15:45 +0000207 if self.fetch_symboled_executable_and_dsym ():
Greg Claytoncd793122012-01-20 06:12:47 +0000208 resolved_path = self.get_resolved_path();
209 path_spec = lldb.SBFileSpec (resolved_path)
Greg Clayton01f7c962012-01-20 03:15:45 +0000210 #result.PutCString ('plist[%s] = %s' % (uuid, self.plist))
211 error = lldb.SBError()
Greg Claytone9ee5502012-01-20 19:25:32 +0000212 lldb.target = lldb.debugger.CreateTarget (resolved_path, self.arch, None, False, error);
213 if lldb.target:
214 self.module = lldb.target.FindModule (path_spec)
Greg Clayton01f7c962012-01-20 03:15:45 +0000215 if self.module:
216 err = self.load_module()
217 if err:
218 print err
219 else:
220 return None
221 else:
Greg Claytoncd793122012-01-20 06:12:47 +0000222 return 'error: unable to get module for (%s) "%s"' % (self.arch, resolved_path)
Greg Clayton01f7c962012-01-20 03:15:45 +0000223 else:
Greg Claytoncd793122012-01-20 06:12:47 +0000224 return 'error: unable to create target for (%s) "%s"' % (self.arch, resolved_path)
Greg Claytone9ee5502012-01-20 19:25:32 +0000225 else:
226 return 'error: unable to locate main executable (%s) "%s"' % (self.arch, self.path)
Greg Clayton01f7c962012-01-20 03:15:45 +0000227
Greg Claytone9ee5502012-01-20 19:25:32 +0000228 def add_target_module(self):
229 if lldb.target:
Greg Clayton01f7c962012-01-20 03:15:45 +0000230 if self.fetch_symboled_executable_and_dsym ():
Greg Claytoncd793122012-01-20 06:12:47 +0000231 resolved_path = self.get_resolved_path();
232 path_spec = lldb.SBFileSpec (resolved_path)
233 #print 'target.AddModule (path="%s", arch="%s", uuid=%s)' % (resolved_path, self.arch, self.uuid)
Greg Claytone9ee5502012-01-20 19:25:32 +0000234 self.module = lldb.target.AddModule (resolved_path, self.arch, self.uuid)
Greg Clayton01f7c962012-01-20 03:15:45 +0000235 if self.module:
236 err = self.load_module()
237 if err:
238 print err;
239 else:
240 return None
241 else:
Greg Claytoncd793122012-01-20 06:12:47 +0000242 return 'error: unable to get module for (%s) "%s"' % (self.arch, resolved_path)
Greg Clayton01f7c962012-01-20 03:15:45 +0000243 else:
244 return 'error: invalid target'
245
246 def __init__(self, path):
247 """CrashLog constructor that take a path to a darwin crash log file"""
248 self.path = path;
249 self.info_lines = list()
250 self.system_profile = list()
251 self.threads = list()
252 self.images = list()
253 self.idents = list() # A list of the required identifiers for doing all stack backtraces
254 self.crashed_thread_idx = -1
255 self.version = -1
256 # With possible initial component of ~ or ~user replaced by that user's home directory.
257 f = open(os.path.expanduser(self.path))
258 self.file_lines = f.read().splitlines()
259 parse_mode = PARSE_MODE_NORMAL
260 thread = None
261 for line in self.file_lines:
262 # print line
263 line_len = len(line)
264 if line_len == 0:
265 if thread:
266 if parse_mode == PARSE_MODE_THREAD:
267 if thread.index == self.crashed_thread_idx:
268 thread.reason = ''
269 if self.thread_exception:
270 thread.reason += self.thread_exception
271 if self.thread_exception_data:
272 thread.reason += " (%s)" % self.thread_exception_data
273 self.threads.append(thread)
274 thread = None
275 else:
276 # only append an extra empty line if the previous line
277 # in the info_lines wasn't empty
278 if len(self.info_lines) > 0 and len(self.info_lines[-1]):
279 self.info_lines.append(line)
280 parse_mode = PARSE_MODE_NORMAL
281 # print 'PARSE_MODE_NORMAL'
282 elif parse_mode == PARSE_MODE_NORMAL:
283 if line.startswith ('Process:'):
284 (self.process_name, pid_with_brackets) = line[8:].strip().split()
285 self.process_id = pid_with_brackets.strip('[]')
286 elif line.startswith ('Path:'):
287 self.process_path = line[5:].strip()
288 elif line.startswith ('Identifier:'):
289 self.process_identifier = line[11:].strip()
290 elif line.startswith ('Version:'):
291 (self.process_version, compatability_version) = line[8:].strip().split()
292 self.process_compatability_version = compatability_version.strip('()')
293 elif line.startswith ('Parent Process:'):
294 (self.parent_process_name, pid_with_brackets) = line[15:].strip().split()
295 self.parent_process_id = pid_with_brackets.strip('[]')
296 elif line.startswith ('Exception Type:'):
297 self.thread_exception = line[15:].strip()
298 continue
299 elif line.startswith ('Exception Codes:'):
300 self.thread_exception_data = line[16:].strip()
301 continue
302 elif line.startswith ('Crashed Thread:'):
303 self.crashed_thread_idx = int(line[15:].strip().split()[0])
304 continue
305 elif line.startswith ('Report Version:'):
306 self.version = int(line[15:].strip())
307 continue
308 elif line.startswith ('System Profile:'):
309 parse_mode = PARSE_MODE_SYSTEM
310 continue
311 elif (line.startswith ('Interval Since Last Report:') or
312 line.startswith ('Crashes Since Last Report:') or
313 line.startswith ('Per-App Interval Since Last Report:') or
314 line.startswith ('Per-App Crashes Since Last Report:') or
315 line.startswith ('Sleep/Wake UUID:') or
316 line.startswith ('Anonymous UUID:')):
317 # ignore these
318 continue
319 elif line.startswith ('Thread'):
320 thread_state_match = self.thread_state_regex.search (line)
321 if thread_state_match:
322 thread_state_match = self.thread_regex.search (line)
323 thread_idx = int(thread_state_match.group(1))
324 parse_mode = PARSE_MODE_THREGS
325 thread = self.threads[thread_idx]
326 else:
327 thread_match = self.thread_regex.search (line)
328 if thread_match:
329 # print 'PARSE_MODE_THREAD'
330 parse_mode = PARSE_MODE_THREAD
331 thread_idx = int(thread_match.group(1))
332 thread = CrashLog.Thread(thread_idx)
333 continue
334 elif line.startswith ('Binary Images:'):
335 parse_mode = PARSE_MODE_IMAGES
336 continue
337 self.info_lines.append(line.strip())
338 elif parse_mode == PARSE_MODE_THREAD:
339 frame_match = self.frame_regex.search(line)
340 if frame_match:
341 ident = frame_match.group(2)
342 if not ident in self.idents:
343 self.idents.append(ident)
344 thread.frames.append (CrashLog.Frame(int(frame_match.group(1)), int(frame_match.group(3), 0), frame_match.group(4)))
345 else:
Greg Clayton8077a532012-01-20 03:32:35 +0000346 print 'error: frame regex failed for line: "%s"' % line
Greg Clayton01f7c962012-01-20 03:15:45 +0000347 elif parse_mode == PARSE_MODE_IMAGES:
348 image_match = self.image_regex_uuid.search (line)
349 if image_match:
350 image = CrashLog.Image (int(image_match.group(1),0),
351 int(image_match.group(2),0),
352 image_match.group(3).strip(),
353 image_match.group(4).strip(),
354 image_match.group(5),
355 image_match.group(6))
356 self.images.append (image)
357 else:
358 image_match = self.image_regex_no_uuid.search (line)
359 if image_match:
360 image = CrashLog.Image (int(image_match.group(1),0),
361 int(image_match.group(2),0),
362 image_match.group(3).strip(),
363 image_match.group(4).strip(),
364 None,
365 image_match.group(5))
366 self.images.append (image)
367 else:
368 print "error: image regex failed for: %s" % line
369
370 elif parse_mode == PARSE_MODE_THREGS:
371 stripped_line = line.strip()
372 reg_values = stripped_line.split(' ')
373 for reg_value in reg_values:
374 (reg, value) = reg_value.split(': ')
375 thread.registers[reg.strip()] = int(value, 0)
376 elif parse_mode == PARSE_MODE_SYSTEM:
377 self.system_profile.append(line)
378 f.close()
379
380 def dump(self):
381 print "Crash Log File: %s" % (self.path)
382 print "\nThreads:"
383 for thread in self.threads:
384 thread.dump(' ')
385 print "\nImages:"
386 for image in self.images:
387 image.dump(' ')
388
389 def find_image_with_identifier(self, ident):
390 for image in self.images:
391 if image.ident == ident:
392 return image
393 return None
394
Greg Claytone9ee5502012-01-20 19:25:32 +0000395 def create_target(self):
396 if not self.images:
397 return 'error: no images in crash log'
398 exe_path = self.images[0].get_resolved_path()
399 err = self.images[0].create_target ()
400 if not err:
401 return None # success
402 # We weren't able to open the main executable as, but we can still symbolicate
403 if self.idents:
Sean Callananf7fb7332012-01-20 19:27:48 +0000404 for ident in self.idents:
Greg Claytone9ee5502012-01-20 19:25:32 +0000405 image = self.find_image_with_identifier (ident)
406 if image:
407 err = image.create_target ()
408 if not err:
409 return None # success
410 for image in self.images:
411 err = image.create_target ()
412 if not err:
413 return None # success
414 return 'error: unable to locate any executables from the crash log'
415
416def disassemble_instructions (instructions, pc, insts_before_pc, insts_after_pc):
Greg Clayton01f7c962012-01-20 03:15:45 +0000417 lines = list()
418 pc_index = -1
419 comment_column = 50
420 for inst_idx, inst in enumerate(instructions):
Greg Claytone9ee5502012-01-20 19:25:32 +0000421 inst_pc = inst.GetAddress().GetLoadAddress(lldb.target);
Greg Clayton01f7c962012-01-20 03:15:45 +0000422 if pc == inst_pc:
423 pc_index = inst_idx
Greg Claytone9ee5502012-01-20 19:25:32 +0000424 mnemonic = inst.GetMnemonic (lldb.target)
425 operands = inst.GetOperands (lldb.target)
426 comment = inst.GetComment (lldb.target)
427 #data = inst.GetData (lldb.target)
Greg Clayton01f7c962012-01-20 03:15:45 +0000428 lines.append ("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands))
429 if comment:
430 line_len = len(lines[-1])
431 if line_len < comment_column:
432 lines[-1] += ' ' * (comment_column - line_len)
433 lines[-1] += "; %s" % comment
434
435 if pc_index >= 0:
436 if pc_index >= insts_before_pc:
437 start_idx = pc_index - insts_before_pc
438 else:
439 start_idx = 0
440 end_idx = pc_index + insts_after_pc
441 if end_idx > inst_idx:
442 end_idx = inst_idx
443 for i in range(start_idx, end_idx+1):
444 if i == pc_index:
445 print ' -> ', lines[i]
446 else:
447 print ' ', lines[i]
448
449def print_module_section_data (section):
450 print section
451 section_data = section.GetSectionData()
452 if section_data:
453 ostream = lldb.SBStream()
454 section_data.GetDescription (ostream, section.GetFileAddress())
455 print ostream.GetData()
456
457def print_module_section (section, depth):
458 print section
459
460 if depth > 0:
461 num_sub_sections = section.GetNumSubSections()
462 for sect_idx in range(num_sub_sections):
463 print_module_section (section.GetSubSectionAtIndex(sect_idx), depth - 1)
464
465def print_module_sections (module, depth):
466 for sect in module.section_iter():
467 print_module_section (sect, depth)
468
469def print_module_symbols (module):
470 for sym in module:
471 print sym
472
473def usage():
474 print "Usage: lldb-symbolicate.py [-n name] executable-image"
475 sys.exit(0)
476
477def Symbolicate(debugger, command, result, dict):
478 SymbolicateCrashLog (command.split())
479
480def SymbolicateCrashLog(command_args):
Greg Claytone9ee5502012-01-20 19:25:32 +0000481 print 'command_args = %s' % command_args
Greg Claytona3698c62012-01-21 00:37:19 +0000482 usage = "usage: %prog [options] <FILE> [FILE ...]"
483 description='''Symbolicate one or more darwin crash log files to provide source file and line information,
484inlined stack frames back to the concrete functions, and disassemble the location of the crash
485for the first frame of the crashed thread.
486If this script is imported into the LLDB command interpreter, a "crashlog" command will be added to the interpreter
487for use at the LLDB command line. After a crash log has been parsed and symbolicated, a target will have been
488created that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows
489you to explore the program as if it were stopped at the locations described in the crash log and functions can
490be disassembled and lookups can be performed using the addresses found in the crash log.'''
491 parser = optparse.OptionParser(description=description, prog='crashlog.py',usage=usage)
Greg Clayton01f7c962012-01-20 03:15:45 +0000492 parser.add_option('--platform', type='string', metavar='platform', dest='platform', help='specify one platform by name')
493 parser.add_option('--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
Greg Clayton01f7c962012-01-20 03:15:45 +0000494 parser.add_option('--no-images', action='store_false', dest='show_images', help='don\'t show images in stack frames', default=True)
Greg Claytona3698c62012-01-21 00:37:19 +0000495 parser.add_option('--load-all', action='store_true', dest='load_all_images', help='load all executable images, not just the images found in the crashed stack frames', default=False)
Greg Clayton01f7c962012-01-20 03:15:45 +0000496 parser.add_option('--image-list', action='store_true', dest='dump_image_list', help='show image list', default=False)
497 parser.add_option('--debug-delay', type='int', dest='debug_delay', metavar='NSEC', help='pause for NSEC seconds for debugger', default=0)
Greg Claytone9ee5502012-01-20 19:25:32 +0000498 parser.add_option('--crashed-only', action='store_true', dest='crashed_only', help='only symbolicate the crashed thread', default=False)
Greg Clayton01f7c962012-01-20 03:15:45 +0000499 loaded_addresses = False
500 (options, args) = parser.parse_args(command_args)
501 if options.verbose:
502 print 'options', options
Greg Clayton01f7c962012-01-20 03:15:45 +0000503 if options.debug_delay > 0:
504 print "Waiting %u seconds for debugger to attach..." % options.debug_delay
505 time.sleep(options.debug_delay)
Greg Clayton01f7c962012-01-20 03:15:45 +0000506 error = lldb.SBError()
Greg Claytone9ee5502012-01-20 19:25:32 +0000507 if args:
508 for crash_log_file in args:
Greg Clayton01f7c962012-01-20 03:15:45 +0000509 crash_log = CrashLog(crash_log_file)
Greg Clayton8077a532012-01-20 03:32:35 +0000510 if options.verbose:
511 crash_log.dump()
Greg Claytona3698c62012-01-21 00:37:19 +0000512 if not crash_log.images:
513 print 'error: no images in crash log'
514 return
515
516 err = crash_log.create_target ()
517 if err:
518 print err
519 return
520
521 exe_module = lldb.target.GetModuleAtIndex(0)
522 images_to_load = list()
523 loaded_image_paths = list()
524 if options.load_all_images:
525 # --load-all option was specified, load everything up
526 for image in crash_log.images:
527 images_to_load.append(image)
528 else:
529 # Only load the images found in stack frames for the crashed threads
530 for ident in crash_log.idents:
531 image = crash_log.find_image_with_identifier (ident)
532 if image:
533 images_to_load.append(image)
534 else:
535 print 'error: can\'t find image for identifier "%s"' % ident
536
537 for image in images_to_load:
538 if image.path in loaded_image_paths:
539 print "warning: skipping %s loaded at %#16.16x duplicate entry (probably commpage)" % (image.path, image.text_addr_lo)
Greg Clayton01f7c962012-01-20 03:15:45 +0000540 else:
Greg Claytona3698c62012-01-21 00:37:19 +0000541 err = image.add_target_module ()
542 if err:
543 print err
544 else:
545 loaded_image_paths.append(image.path)
546
547 for line in crash_log.info_lines:
548 print line
549
550 # Reconstruct inlined frames for all threads for anything that has debug info
551 for thread in crash_log.threads:
552 if options.crashed_only and thread.did_crash() == False:
553 continue
554 # start a new frame list that we will fixup for each thread
555 new_thread_frames = list()
556 # Iterate through all concrete frames for a thread and resolve
557 # any parent frames of inlined functions
558 for frame_idx, frame in enumerate(thread.frames):
559 # Resolve the frame's pc into a section + offset address 'pc_addr'
560 pc_addr = lldb.target.ResolveLoadAddress (frame.pc)
561 # Check to see if we were able to resolve the address
562 if pc_addr:
563 # We were able to resolve the frame's PC into a section offset
564 # address.
565
566 # Resolve the frame's PC value into a symbol context. A symbol
567 # context can resolve a module, compile unit, function, block,
568 # line table entry and/or symbol. If the frame has a block, then
569 # we can look for inlined frames, which are represented by blocks
570 # that have inlined information in them
571 frame.sym_ctx = lldb.target.ResolveSymbolContextForAddress (pc_addr, lldb.eSymbolContextEverything);
572
573 # dump if the verbose option was specified
574 if options.verbose:
575 print "frame.pc = %#16.16x (file_addr = %#16.16x)" % (frame.pc, pc_addr.GetFileAddress())
576 print "frame.pc_addr = ", pc_addr
577 print "frame.sym_ctx = "
578 print frame.sym_ctx
579 print
580
581 # Append the frame we already had from the crash log to the new
582 # frames list
583 new_thread_frames.append(frame)
584
585 new_frame = CrashLog.Frame (frame.index, -1, None)
586
587 # Try and use the current frame's symbol context to calculate a
588 # parent frame for an inlined function. If the curent frame is
589 # inlined, it will return a valid symbol context for the parent
590 # frame of the current inlined function
591 parent_pc_addr = lldb.SBAddress()
592 new_frame.sym_ctx = frame.sym_ctx.GetParentOfInlinedScope (pc_addr, parent_pc_addr)
593
594 # See if we were able to reconstruct anything?
595 while new_frame.sym_ctx:
596 # We have a parent frame of an inlined frame, create a new frame
597 # Convert the section + offset 'parent_pc_addr' to a load address
598 new_frame.pc = parent_pc_addr.GetLoadAddress(lldb.target)
599 # push the new frame onto the new frame stack
600 new_thread_frames.append (new_frame)
601 # dump if the verbose option was specified
602 if options.verbose:
603 print "new_frame.pc = %#16.16x (%s)" % (new_frame.pc, parent_pc_addr)
604 print "new_frame.sym_ctx = "
605 print new_frame.sym_ctx
606 print
607 # Create another new frame in case we have multiple inlined frames
608 prev_new_frame = new_frame
609 new_frame = CrashLog.Frame (frame.index, -1, None)
610 # Swap the addresses so we can try another inlined lookup
611 pc_addr = parent_pc_addr;
612 new_frame.sym_ctx = prev_new_frame.sym_ctx.GetParentOfInlinedScope (pc_addr, parent_pc_addr)
613 # Replace our thread frames with our new list that includes parent
614 # frames for inlined functions
615 thread.frames = new_thread_frames
616 # Now iterate through all threads and display our richer stack backtraces
617 for thread in crash_log.threads:
618 this_thread_crashed = thread.did_crash()
619 if options.crashed_only and this_thread_crashed == False:
620 continue
621 print "%s" % thread
622 prev_frame_index = -1
623 for frame_idx, frame in enumerate(thread.frames):
624 details = ' %s' % frame.details
625 module = frame.sym_ctx.GetModule()
626 instructions = None
627 if module:
628 module_basename = module.GetFileSpec().GetFilename();
629 function_start_load_addr = -1
630 function_name = None
631 function = frame.sym_ctx.GetFunction()
632 block = frame.sym_ctx.GetBlock()
633 line_entry = frame.sym_ctx.GetLineEntry()
634 symbol = frame.sym_ctx.GetSymbol()
635 inlined_block = block.GetContainingInlinedBlock();
636 if inlined_block:
637 function_name = inlined_block.GetInlinedName();
638 block_range_idx = inlined_block.GetRangeIndexForBlockAddress (lldb.target.ResolveLoadAddress (frame.pc))
639 if block_range_idx < lldb.UINT32_MAX:
640 block_range_start_addr = inlined_block.GetRangeStartAddress (block_range_idx)
641 function_start_load_addr = block_range_start_addr.GetLoadAddress (lldb.target)
Greg Clayton01f7c962012-01-20 03:15:45 +0000642 else:
Greg Claytona3698c62012-01-21 00:37:19 +0000643 function_start_load_addr = frame.pc
644 if this_thread_crashed and frame_idx == 0:
645 instructions = function.GetInstructions(lldb.target)
646 elif function:
647 function_name = function.GetName()
648 function_start_load_addr = function.GetStartAddress().GetLoadAddress (lldb.target)
649 if this_thread_crashed and frame_idx == 0:
650 instructions = function.GetInstructions(lldb.target)
651 elif symbol:
652 function_name = symbol.GetName()
653 function_start_load_addr = symbol.GetStartAddress().GetLoadAddress (lldb.target)
654 if this_thread_crashed and frame_idx == 0:
655 instructions = symbol.GetInstructions(lldb.target)
Greg Clayton01f7c962012-01-20 03:15:45 +0000656
Greg Claytona3698c62012-01-21 00:37:19 +0000657 if function_name:
658 # Print the function or symbol name and annotate if it was inlined
659 inline_suffix = ''
660 if inlined_block:
661 inline_suffix = '[inlined] '
662 else:
663 inline_suffix = ' '
664 if options.show_images:
665 details = "%s%s`%s" % (inline_suffix, module_basename, function_name)
666 else:
667 details = "%s" % (function_name)
668 # Dump the offset from the current function or symbol if it is non zero
669 function_offset = frame.pc - function_start_load_addr
670 if function_offset > 0:
671 details += " + %u" % (function_offset)
672 elif function_offset < 0:
673 defaults += " %i (invalid negative offset, file a bug) " % function_offset
674 # Print out any line information if any is available
675 if line_entry.GetFileSpec():
676 details += ' at %s' % line_entry.GetFileSpec().GetFilename()
677 details += ':%u' % line_entry.GetLine ()
678 column = line_entry.GetColumn()
679 if column > 0:
680 details += ':%u' % column
Greg Clayton01f7c962012-01-20 03:15:45 +0000681
682
Greg Claytona3698c62012-01-21 00:37:19 +0000683 # Only print out the concrete frame index if it changes.
684 # if prev_frame_index != frame.index:
685 # print "[%2u] %#16.16x %s" % (frame.index, frame.pc, details)
686 # else:
687 # print " %#16.16x %s" % (frame.pc, details)
688 print "[%2u] %#16.16x %s" % (frame.index, frame.pc, details)
689 prev_frame_index = frame.index
690 if instructions:
691 print
692 disassemble_instructions (instructions, frame.pc, 4, 4)
693 print
Greg Clayton01f7c962012-01-20 03:15:45 +0000694
Greg Claytona3698c62012-01-21 00:37:19 +0000695 print
Greg Clayton01f7c962012-01-20 03:15:45 +0000696
Greg Claytona3698c62012-01-21 00:37:19 +0000697 if options.dump_image_list:
698 print "Binary Images:"
699 for image in crash_log.images:
700 print image
701
Greg Clayton01f7c962012-01-20 03:15:45 +0000702
703if __name__ == '__main__':
Greg Claytone9ee5502012-01-20 19:25:32 +0000704 # Create a new debugger instance
705 lldb.debugger = lldb.SBDebugger.Create()
706 SymbolicateCrashLog (sys.argv)
707elif lldb.debugger:
708 lldb.debugger.HandleCommand('command script add -f crashlog.Symbolicate crashlog')
709 print '"crashlog" command installed, type "crashlog --help" for detailed help'
Greg Clayton01f7c962012-01-20 03:15:45 +0000710