blob: 3e8fab505ac6779d45d44516489616efa7537d1a [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
Greg Clayton223e8082012-01-21 04:26:24 +000036import shlex
Greg Clayton01f7c962012-01-20 03:15:45 +000037import sys
38import time
Greg Claytoncd793122012-01-20 06:12:47 +000039import uuid
40
Greg Clayton01f7c962012-01-20 03:15:45 +000041
42PARSE_MODE_NORMAL = 0
43PARSE_MODE_THREAD = 1
44PARSE_MODE_IMAGES = 2
45PARSE_MODE_THREGS = 3
46PARSE_MODE_SYSTEM = 4
47
48class CrashLog:
49 """Class that does parses darwin crash logs"""
50 thread_state_regex = re.compile('^Thread ([0-9]+) crashed with')
51 thread_regex = re.compile('^Thread ([0-9]+)([^:]*):(.*)')
Greg Clayton8077a532012-01-20 03:32:35 +000052 frame_regex = re.compile('^([0-9]+) +([^ ]+) *\t(0x[0-9a-fA-F]+) +(.*)')
53 image_regex_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^<]+)<([-0-9a-fA-F]+)> (.*)');
54 image_regex_no_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^/]+)/(.*)');
Greg Clayton01f7c962012-01-20 03:15:45 +000055 empty_line_regex = re.compile('^$')
56
57 class Thread:
58 """Class that represents a thread in a darwin crash log"""
59 def __init__(self, index):
60 self.index = index
61 self.frames = list()
62 self.registers = dict()
63 self.reason = None
64 self.queue = None
65
66 def dump(self, prefix):
67 print "%sThread[%u] %s" % (prefix, self.index, self.reason)
68 if self.frames:
69 print "%s Frames:" % (prefix)
70 for frame in self.frames:
71 frame.dump(prefix + ' ')
72 if self.registers:
73 print "%s Registers:" % (prefix)
74 for reg in self.registers.keys():
75 print "%s %-5s = %#16.16x" % (prefix, reg, self.registers[reg])
76
77 def did_crash(self):
78 return self.reason != None
79
80 def __str__(self):
81 s = "Thread[%u]" % self.index
82 if self.reason:
83 s += ' %s' % self.reason
84 return s
85
86
87 class Frame:
88 """Class that represents a stack frame in a thread in a darwin crash log"""
89 def __init__(self, index, pc, details):
90 self.index = index
91 self.pc = pc
92 self.sym_ctx = None
93 self.details = details
94
95 def __str__(self):
96 return "[%2u] %#16.16x %s" % (self.index, self.pc, self.details)
97
98 def dump(self, prefix):
99 print "%s%s" % (prefix, self)
100
101
102 class Image:
103 """Class that represents a binary images in a darwin crash log"""
Greg Clayton8077a532012-01-20 03:32:35 +0000104 dsymForUUIDBinary = os.path.expanduser('~rc/bin/dsymForUUID')
Greg Claytoncd793122012-01-20 06:12:47 +0000105 if not os.path.exists(dsymForUUIDBinary):
106 dsymForUUIDBinary = commands.getoutput('which dsymForUUID')
107
108 dwarfdump_uuid_regex = re.compile('UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*')
Greg Clayton8077a532012-01-20 03:32:35 +0000109
Greg Clayton01f7c962012-01-20 03:15:45 +0000110 def __init__(self, text_addr_lo, text_addr_hi, ident, version, uuid, path):
111 self.text_addr_lo = text_addr_lo
112 self.text_addr_hi = text_addr_hi
113 self.ident = ident
114 self.version = version
Greg Claytoncd793122012-01-20 06:12:47 +0000115 self.arch = None
Greg Clayton01f7c962012-01-20 03:15:45 +0000116 self.uuid = uuid
117 self.path = path
Greg Claytoncd793122012-01-20 06:12:47 +0000118 self.resolved_path = None
119 self.dsym = None
Greg Clayton01f7c962012-01-20 03:15:45 +0000120 self.module = None
Greg Clayton01f7c962012-01-20 03:15:45 +0000121
122 def dump(self, prefix):
123 print "%s%s" % (prefix, self)
124
125 def __str__(self):
Greg Claytoncd793122012-01-20 06:12:47 +0000126 return "%#16.16x %s %s" % (self.text_addr_lo, self.uuid, self.get_resolved_path())
Greg Clayton01f7c962012-01-20 03:15:45 +0000127
Greg Claytoncd793122012-01-20 06:12:47 +0000128 def get_resolved_path(self):
129 if self.resolved_path:
130 return self.resolved_path
131 elif self.path:
132 return self.path
133 return None
134
135 def get_resolved_path_basename(self):
136 path = self.get_resolved_path()
137 if path:
138 return os.path.basename(path)
139 return None
140
141 def dsym_basename(self):
142 if self.dsym:
143 return os.path.basename(self.dsym)
Greg Clayton01f7c962012-01-20 03:15:45 +0000144 return None
145
146 def fetch_symboled_executable_and_dsym(self):
Greg Claytoncd793122012-01-20 06:12:47 +0000147 if self.resolved_path:
148 # Don't load a module twice...
149 return 0
150 print 'Locating %s %s...' % (self.uuid, self.path),
Greg Clayton8077a532012-01-20 03:32:35 +0000151 if os.path.exists(self.dsymForUUIDBinary):
152 dsym_for_uuid_command = '%s %s' % (self.dsymForUUIDBinary, self.uuid)
Greg Clayton8077a532012-01-20 03:32:35 +0000153 s = commands.getoutput(dsym_for_uuid_command)
154 if s:
155 plist_root = plistlib.readPlistFromString (s)
156 if plist_root:
Greg Claytoncd793122012-01-20 06:12:47 +0000157 plist = plist_root[self.uuid]
158 if plist:
159 if 'DBGArchitecture' in plist:
160 self.arch = plist['DBGArchitecture']
161 if 'DBGDSYMPath' in plist:
162 self.dsym = os.path.realpath(plist['DBGDSYMPath'])
163 if 'DBGSymbolRichExecutable' in plist:
164 self.resolved_path = os.path.expanduser (plist['DBGSymbolRichExecutable'])
165 if not self.resolved_path and os.path.exists(self.path):
166 dwarfdump_cmd_output = commands.getoutput('dwarfdump --uuid "%s"' % self.path)
167 self_uuid = uuid.UUID(self.uuid)
168 for line in dwarfdump_cmd_output.splitlines():
169 match = self.dwarfdump_uuid_regex.search (line)
170 if match:
171 dwarf_uuid_str = match.group(1)
172 dwarf_uuid = uuid.UUID(dwarf_uuid_str)
173 if self_uuid == dwarf_uuid:
174 self.resolved_path = self.path
175 self.arch = match.group(2)
176 break;
177 if not self.resolved_path:
178 print "error: file %s '%s' doesn't match the UUID in the installed file" % (self.uuid, self.path)
179 return 0
180 if (self.resolved_path and os.path.exists(self.resolved_path)) or (self.path and os.path.exists(self.path)):
181 print 'ok'
182 if self.path != self.resolved_path:
183 print ' exe = "%s"' % self.resolved_path
184 if self.dsym:
185 print ' dsym = "%s"' % self.dsym
186 return 1
Greg Clayton01f7c962012-01-20 03:15:45 +0000187 else:
Greg Claytoncd793122012-01-20 06:12:47 +0000188 return 0
Greg Clayton01f7c962012-01-20 03:15:45 +0000189
190 def load_module(self):
Greg Claytone9ee5502012-01-20 19:25:32 +0000191 if not lldb.target:
192 return 'error: no target'
Greg Clayton01f7c962012-01-20 03:15:45 +0000193 if self.module:
194 text_section = self.module.FindSection ("__TEXT")
195 if text_section:
Greg Claytone9ee5502012-01-20 19:25:32 +0000196 error = lldb.target.SetSectionLoadAddress (text_section, self.text_addr_lo)
Greg Clayton01f7c962012-01-20 03:15:45 +0000197 if error.Success():
Greg Clayton8077a532012-01-20 03:32:35 +0000198 #print 'Success: loaded %s.__TEXT = 0x%x' % (self.basename(), self.text_addr_lo)
Greg Clayton01f7c962012-01-20 03:15:45 +0000199 return None
200 else:
201 return 'error: %s' % error.GetCString()
202 else:
Greg Claytoncd793122012-01-20 06:12:47 +0000203 return 'error: unable to find "__TEXT" section in "%s"' % self.get_resolved_path()
Greg Clayton01f7c962012-01-20 03:15:45 +0000204 else:
205 return 'error: invalid module'
206
Greg Claytone9ee5502012-01-20 19:25:32 +0000207 def create_target(self):
Greg Clayton01f7c962012-01-20 03:15:45 +0000208 if self.fetch_symboled_executable_and_dsym ():
Greg Claytoncd793122012-01-20 06:12:47 +0000209 resolved_path = self.get_resolved_path();
210 path_spec = lldb.SBFileSpec (resolved_path)
Greg Clayton01f7c962012-01-20 03:15:45 +0000211 #result.PutCString ('plist[%s] = %s' % (uuid, self.plist))
212 error = lldb.SBError()
Greg Claytone9ee5502012-01-20 19:25:32 +0000213 lldb.target = lldb.debugger.CreateTarget (resolved_path, self.arch, None, False, error);
214 if lldb.target:
215 self.module = lldb.target.FindModule (path_spec)
Greg Clayton01f7c962012-01-20 03:15:45 +0000216 if self.module:
217 err = self.load_module()
218 if err:
219 print err
220 else:
221 return None
222 else:
Greg Claytoncd793122012-01-20 06:12:47 +0000223 return 'error: unable to get module for (%s) "%s"' % (self.arch, resolved_path)
Greg Clayton01f7c962012-01-20 03:15:45 +0000224 else:
Greg Claytoncd793122012-01-20 06:12:47 +0000225 return 'error: unable to create target for (%s) "%s"' % (self.arch, resolved_path)
Greg Claytone9ee5502012-01-20 19:25:32 +0000226 else:
227 return 'error: unable to locate main executable (%s) "%s"' % (self.arch, self.path)
Greg Clayton01f7c962012-01-20 03:15:45 +0000228
Greg Claytone9ee5502012-01-20 19:25:32 +0000229 def add_target_module(self):
230 if lldb.target:
Greg Clayton01f7c962012-01-20 03:15:45 +0000231 if self.fetch_symboled_executable_and_dsym ():
Greg Claytoncd793122012-01-20 06:12:47 +0000232 resolved_path = self.get_resolved_path();
233 path_spec = lldb.SBFileSpec (resolved_path)
234 #print 'target.AddModule (path="%s", arch="%s", uuid=%s)' % (resolved_path, self.arch, self.uuid)
Greg Claytone9ee5502012-01-20 19:25:32 +0000235 self.module = lldb.target.AddModule (resolved_path, self.arch, self.uuid)
Greg Clayton01f7c962012-01-20 03:15:45 +0000236 if self.module:
237 err = self.load_module()
238 if err:
239 print err;
240 else:
241 return None
242 else:
Greg Claytoncd793122012-01-20 06:12:47 +0000243 return 'error: unable to get module for (%s) "%s"' % (self.arch, resolved_path)
Greg Clayton01f7c962012-01-20 03:15:45 +0000244 else:
245 return 'error: invalid target'
246
247 def __init__(self, path):
248 """CrashLog constructor that take a path to a darwin crash log file"""
Greg Clayton223e8082012-01-21 04:26:24 +0000249 self.path = os.path.expanduser(path);
Greg Clayton01f7c962012-01-20 03:15:45 +0000250 self.info_lines = list()
251 self.system_profile = list()
252 self.threads = list()
253 self.images = list()
254 self.idents = list() # A list of the required identifiers for doing all stack backtraces
255 self.crashed_thread_idx = -1
256 self.version = -1
Greg Clayton223e8082012-01-21 04:26:24 +0000257 self.error = None
Greg Clayton01f7c962012-01-20 03:15:45 +0000258 # With possible initial component of ~ or ~user replaced by that user's home directory.
Greg Clayton223e8082012-01-21 04:26:24 +0000259 try:
260 f = open(self.path)
261 except IOError:
262 self.error = 'error: cannot open "%s"' % self.path
263 return
264
Greg Clayton01f7c962012-01-20 03:15:45 +0000265 self.file_lines = f.read().splitlines()
266 parse_mode = PARSE_MODE_NORMAL
267 thread = None
268 for line in self.file_lines:
269 # print line
270 line_len = len(line)
271 if line_len == 0:
272 if thread:
273 if parse_mode == PARSE_MODE_THREAD:
274 if thread.index == self.crashed_thread_idx:
275 thread.reason = ''
276 if self.thread_exception:
277 thread.reason += self.thread_exception
278 if self.thread_exception_data:
279 thread.reason += " (%s)" % self.thread_exception_data
280 self.threads.append(thread)
281 thread = None
282 else:
283 # only append an extra empty line if the previous line
284 # in the info_lines wasn't empty
285 if len(self.info_lines) > 0 and len(self.info_lines[-1]):
286 self.info_lines.append(line)
287 parse_mode = PARSE_MODE_NORMAL
288 # print 'PARSE_MODE_NORMAL'
289 elif parse_mode == PARSE_MODE_NORMAL:
290 if line.startswith ('Process:'):
291 (self.process_name, pid_with_brackets) = line[8:].strip().split()
292 self.process_id = pid_with_brackets.strip('[]')
293 elif line.startswith ('Path:'):
294 self.process_path = line[5:].strip()
295 elif line.startswith ('Identifier:'):
296 self.process_identifier = line[11:].strip()
297 elif line.startswith ('Version:'):
298 (self.process_version, compatability_version) = line[8:].strip().split()
299 self.process_compatability_version = compatability_version.strip('()')
300 elif line.startswith ('Parent Process:'):
301 (self.parent_process_name, pid_with_brackets) = line[15:].strip().split()
302 self.parent_process_id = pid_with_brackets.strip('[]')
303 elif line.startswith ('Exception Type:'):
304 self.thread_exception = line[15:].strip()
305 continue
306 elif line.startswith ('Exception Codes:'):
307 self.thread_exception_data = line[16:].strip()
308 continue
309 elif line.startswith ('Crashed Thread:'):
310 self.crashed_thread_idx = int(line[15:].strip().split()[0])
311 continue
312 elif line.startswith ('Report Version:'):
313 self.version = int(line[15:].strip())
314 continue
315 elif line.startswith ('System Profile:'):
316 parse_mode = PARSE_MODE_SYSTEM
317 continue
318 elif (line.startswith ('Interval Since Last Report:') or
319 line.startswith ('Crashes Since Last Report:') or
320 line.startswith ('Per-App Interval Since Last Report:') or
321 line.startswith ('Per-App Crashes Since Last Report:') or
322 line.startswith ('Sleep/Wake UUID:') or
323 line.startswith ('Anonymous UUID:')):
324 # ignore these
325 continue
326 elif line.startswith ('Thread'):
327 thread_state_match = self.thread_state_regex.search (line)
328 if thread_state_match:
329 thread_state_match = self.thread_regex.search (line)
330 thread_idx = int(thread_state_match.group(1))
331 parse_mode = PARSE_MODE_THREGS
332 thread = self.threads[thread_idx]
333 else:
334 thread_match = self.thread_regex.search (line)
335 if thread_match:
336 # print 'PARSE_MODE_THREAD'
337 parse_mode = PARSE_MODE_THREAD
338 thread_idx = int(thread_match.group(1))
339 thread = CrashLog.Thread(thread_idx)
340 continue
341 elif line.startswith ('Binary Images:'):
342 parse_mode = PARSE_MODE_IMAGES
343 continue
344 self.info_lines.append(line.strip())
345 elif parse_mode == PARSE_MODE_THREAD:
346 frame_match = self.frame_regex.search(line)
347 if frame_match:
348 ident = frame_match.group(2)
349 if not ident in self.idents:
350 self.idents.append(ident)
351 thread.frames.append (CrashLog.Frame(int(frame_match.group(1)), int(frame_match.group(3), 0), frame_match.group(4)))
352 else:
Greg Clayton8077a532012-01-20 03:32:35 +0000353 print 'error: frame regex failed for line: "%s"' % line
Greg Clayton01f7c962012-01-20 03:15:45 +0000354 elif parse_mode == PARSE_MODE_IMAGES:
355 image_match = self.image_regex_uuid.search (line)
356 if image_match:
357 image = CrashLog.Image (int(image_match.group(1),0),
358 int(image_match.group(2),0),
359 image_match.group(3).strip(),
360 image_match.group(4).strip(),
361 image_match.group(5),
362 image_match.group(6))
363 self.images.append (image)
364 else:
365 image_match = self.image_regex_no_uuid.search (line)
366 if image_match:
367 image = CrashLog.Image (int(image_match.group(1),0),
368 int(image_match.group(2),0),
369 image_match.group(3).strip(),
370 image_match.group(4).strip(),
371 None,
372 image_match.group(5))
373 self.images.append (image)
374 else:
375 print "error: image regex failed for: %s" % line
376
377 elif parse_mode == PARSE_MODE_THREGS:
378 stripped_line = line.strip()
379 reg_values = stripped_line.split(' ')
380 for reg_value in reg_values:
381 (reg, value) = reg_value.split(': ')
382 thread.registers[reg.strip()] = int(value, 0)
383 elif parse_mode == PARSE_MODE_SYSTEM:
384 self.system_profile.append(line)
385 f.close()
386
387 def dump(self):
388 print "Crash Log File: %s" % (self.path)
389 print "\nThreads:"
390 for thread in self.threads:
391 thread.dump(' ')
392 print "\nImages:"
393 for image in self.images:
394 image.dump(' ')
395
396 def find_image_with_identifier(self, ident):
397 for image in self.images:
398 if image.ident == ident:
399 return image
400 return None
401
Greg Claytone9ee5502012-01-20 19:25:32 +0000402 def create_target(self):
403 if not self.images:
404 return 'error: no images in crash log'
405 exe_path = self.images[0].get_resolved_path()
406 err = self.images[0].create_target ()
407 if not err:
408 return None # success
409 # We weren't able to open the main executable as, but we can still symbolicate
410 if self.idents:
Sean Callananf7fb7332012-01-20 19:27:48 +0000411 for ident in self.idents:
Greg Claytone9ee5502012-01-20 19:25:32 +0000412 image = self.find_image_with_identifier (ident)
413 if image:
414 err = image.create_target ()
415 if not err:
416 return None # success
417 for image in self.images:
418 err = image.create_target ()
419 if not err:
420 return None # success
421 return 'error: unable to locate any executables from the crash log'
422
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000423def disassemble_instructions (instructions, pc, options, non_zeroeth_frame):
Greg Clayton01f7c962012-01-20 03:15:45 +0000424 lines = list()
425 pc_index = -1
426 comment_column = 50
427 for inst_idx, inst in enumerate(instructions):
Greg Claytone9ee5502012-01-20 19:25:32 +0000428 inst_pc = inst.GetAddress().GetLoadAddress(lldb.target);
Greg Clayton01f7c962012-01-20 03:15:45 +0000429 if pc == inst_pc:
430 pc_index = inst_idx
Greg Claytone9ee5502012-01-20 19:25:32 +0000431 mnemonic = inst.GetMnemonic (lldb.target)
432 operands = inst.GetOperands (lldb.target)
433 comment = inst.GetComment (lldb.target)
434 #data = inst.GetData (lldb.target)
Greg Clayton01f7c962012-01-20 03:15:45 +0000435 lines.append ("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands))
436 if comment:
437 line_len = len(lines[-1])
438 if line_len < comment_column:
439 lines[-1] += ' ' * (comment_column - line_len)
440 lines[-1] += "; %s" % comment
441
442 if pc_index >= 0:
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000443 # If we are disassembling the non-zeroeth frame, we need to backup the PC by 1
444 if non_zeroeth_frame and pc_index > 0:
445 pc_index = pc_index - 1
446 if options.disassemble_before == -1:
Greg Clayton01f7c962012-01-20 03:15:45 +0000447 start_idx = 0
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000448 else:
449 start_idx = pc_index - options.disassemble_before
450 if start_idx < 0:
451 start_idx = 0
452 if options.disassemble_before == -1:
453 end_idx = inst_idx
454 else:
455 end_idx = pc_index + options.disassemble_after
Greg Clayton01f7c962012-01-20 03:15:45 +0000456 if end_idx > inst_idx:
457 end_idx = inst_idx
458 for i in range(start_idx, end_idx+1):
459 if i == pc_index:
460 print ' -> ', lines[i]
461 else:
462 print ' ', lines[i]
463
464def print_module_section_data (section):
465 print section
466 section_data = section.GetSectionData()
467 if section_data:
468 ostream = lldb.SBStream()
469 section_data.GetDescription (ostream, section.GetFileAddress())
470 print ostream.GetData()
471
472def print_module_section (section, depth):
473 print section
474
475 if depth > 0:
476 num_sub_sections = section.GetNumSubSections()
477 for sect_idx in range(num_sub_sections):
478 print_module_section (section.GetSubSectionAtIndex(sect_idx), depth - 1)
479
480def print_module_sections (module, depth):
481 for sect in module.section_iter():
482 print_module_section (sect, depth)
483
484def print_module_symbols (module):
485 for sym in module:
486 print sym
487
488def usage():
489 print "Usage: lldb-symbolicate.py [-n name] executable-image"
490 sys.exit(0)
491
492def Symbolicate(debugger, command, result, dict):
Greg Clayton223e8082012-01-21 04:26:24 +0000493 try:
494 SymbolicateCrashLog (shlex.split(command))
495 except:
496 result.PutCString ("error: python exception %s" % sys.exc_info()[0])
497
Greg Clayton01f7c962012-01-20 03:15:45 +0000498def SymbolicateCrashLog(command_args):
Greg Claytona3698c62012-01-21 00:37:19 +0000499 usage = "usage: %prog [options] <FILE> [FILE ...]"
500 description='''Symbolicate one or more darwin crash log files to provide source file and line information,
501inlined stack frames back to the concrete functions, and disassemble the location of the crash
502for the first frame of the crashed thread.
503If this script is imported into the LLDB command interpreter, a "crashlog" command will be added to the interpreter
504for use at the LLDB command line. After a crash log has been parsed and symbolicated, a target will have been
505created that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows
506you to explore the program as if it were stopped at the locations described in the crash log and functions can
507be disassembled and lookups can be performed using the addresses found in the crash log.'''
508 parser = optparse.OptionParser(description=description, prog='crashlog.py',usage=usage)
Greg Clayton01f7c962012-01-20 03:15:45 +0000509 parser.add_option('--platform', type='string', metavar='platform', dest='platform', help='specify one platform by name')
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000510 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
Greg Clayton01f7c962012-01-20 03:15:45 +0000511 parser.add_option('--no-images', action='store_false', dest='show_images', help='don\'t show images in stack frames', default=True)
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000512 parser.add_option('-a', '--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 +0000513 parser.add_option('--image-list', action='store_true', dest='dump_image_list', help='show image list', default=False)
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000514 parser.add_option('-g', '--debug-delay', type='int', dest='debug_delay', metavar='NSEC', help='pause for NSEC seconds for debugger', default=0)
515 parser.add_option('-c', '--crashed-only', action='store_true', dest='crashed_only', help='only symbolicate the crashed thread', default=False)
516 parser.add_option('-d', '--disasm-depth', type='int', dest='disassemble_depth', help='set the depth in stack frames that should be disassembled (default is 1)', default=1)
517 parser.add_option('-D', '--disasm-all', action='store_true', dest='disassemble_all_threads', help='enabled disassembly of frames on all threads (not just the crashed thread)', default=False)
518 parser.add_option('-B', '--disasm-before', type='int', dest='disassemble_before', help='the number of instructions to disassemble before the frame PC', default=4)
519 parser.add_option('-A', '--disasm-after', type='int', dest='disassemble_after', help='the number of instructions to disassemble after the frame PC', default=4)
Greg Clayton01f7c962012-01-20 03:15:45 +0000520 loaded_addresses = False
Greg Clayton223e8082012-01-21 04:26:24 +0000521 try:
522 (options, args) = parser.parse_args(command_args)
523 except:
524 return
525
Greg Clayton01f7c962012-01-20 03:15:45 +0000526 if options.verbose:
Greg Clayton223e8082012-01-21 04:26:24 +0000527 print 'command_args = %s' % command_args
Greg Clayton01f7c962012-01-20 03:15:45 +0000528 print 'options', options
Greg Clayton223e8082012-01-21 04:26:24 +0000529 print 'args', args
530
Greg Clayton01f7c962012-01-20 03:15:45 +0000531 if options.debug_delay > 0:
532 print "Waiting %u seconds for debugger to attach..." % options.debug_delay
533 time.sleep(options.debug_delay)
Greg Clayton01f7c962012-01-20 03:15:45 +0000534 error = lldb.SBError()
Greg Claytone9ee5502012-01-20 19:25:32 +0000535 if args:
536 for crash_log_file in args:
Greg Clayton01f7c962012-01-20 03:15:45 +0000537 crash_log = CrashLog(crash_log_file)
Greg Clayton223e8082012-01-21 04:26:24 +0000538 if crash_log.error:
539 print crash_log.error
540 return
Greg Clayton8077a532012-01-20 03:32:35 +0000541 if options.verbose:
542 crash_log.dump()
Greg Claytona3698c62012-01-21 00:37:19 +0000543 if not crash_log.images:
544 print 'error: no images in crash log'
545 return
546
547 err = crash_log.create_target ()
548 if err:
549 print err
550 return
551
552 exe_module = lldb.target.GetModuleAtIndex(0)
553 images_to_load = list()
554 loaded_image_paths = list()
555 if options.load_all_images:
556 # --load-all option was specified, load everything up
557 for image in crash_log.images:
558 images_to_load.append(image)
559 else:
560 # Only load the images found in stack frames for the crashed threads
561 for ident in crash_log.idents:
562 image = crash_log.find_image_with_identifier (ident)
563 if image:
564 images_to_load.append(image)
565 else:
566 print 'error: can\'t find image for identifier "%s"' % ident
567
568 for image in images_to_load:
569 if image.path in loaded_image_paths:
570 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 +0000571 else:
Greg Claytona3698c62012-01-21 00:37:19 +0000572 err = image.add_target_module ()
573 if err:
574 print err
575 else:
576 loaded_image_paths.append(image.path)
577
578 for line in crash_log.info_lines:
579 print line
580
581 # Reconstruct inlined frames for all threads for anything that has debug info
582 for thread in crash_log.threads:
583 if options.crashed_only and thread.did_crash() == False:
584 continue
585 # start a new frame list that we will fixup for each thread
586 new_thread_frames = list()
587 # Iterate through all concrete frames for a thread and resolve
588 # any parent frames of inlined functions
589 for frame_idx, frame in enumerate(thread.frames):
590 # Resolve the frame's pc into a section + offset address 'pc_addr'
591 pc_addr = lldb.target.ResolveLoadAddress (frame.pc)
592 # Check to see if we were able to resolve the address
593 if pc_addr:
594 # We were able to resolve the frame's PC into a section offset
595 # address.
596
597 # Resolve the frame's PC value into a symbol context. A symbol
598 # context can resolve a module, compile unit, function, block,
599 # line table entry and/or symbol. If the frame has a block, then
600 # we can look for inlined frames, which are represented by blocks
601 # that have inlined information in them
602 frame.sym_ctx = lldb.target.ResolveSymbolContextForAddress (pc_addr, lldb.eSymbolContextEverything);
603
604 # dump if the verbose option was specified
605 if options.verbose:
606 print "frame.pc = %#16.16x (file_addr = %#16.16x)" % (frame.pc, pc_addr.GetFileAddress())
607 print "frame.pc_addr = ", pc_addr
608 print "frame.sym_ctx = "
609 print frame.sym_ctx
610 print
611
612 # Append the frame we already had from the crash log to the new
613 # frames list
614 new_thread_frames.append(frame)
615
616 new_frame = CrashLog.Frame (frame.index, -1, None)
617
618 # Try and use the current frame's symbol context to calculate a
619 # parent frame for an inlined function. If the curent frame is
620 # inlined, it will return a valid symbol context for the parent
621 # frame of the current inlined function
622 parent_pc_addr = lldb.SBAddress()
623 new_frame.sym_ctx = frame.sym_ctx.GetParentOfInlinedScope (pc_addr, parent_pc_addr)
624
625 # See if we were able to reconstruct anything?
626 while new_frame.sym_ctx:
627 # We have a parent frame of an inlined frame, create a new frame
628 # Convert the section + offset 'parent_pc_addr' to a load address
629 new_frame.pc = parent_pc_addr.GetLoadAddress(lldb.target)
630 # push the new frame onto the new frame stack
631 new_thread_frames.append (new_frame)
632 # dump if the verbose option was specified
633 if options.verbose:
634 print "new_frame.pc = %#16.16x (%s)" % (new_frame.pc, parent_pc_addr)
635 print "new_frame.sym_ctx = "
636 print new_frame.sym_ctx
637 print
638 # Create another new frame in case we have multiple inlined frames
639 prev_new_frame = new_frame
640 new_frame = CrashLog.Frame (frame.index, -1, None)
641 # Swap the addresses so we can try another inlined lookup
642 pc_addr = parent_pc_addr;
643 new_frame.sym_ctx = prev_new_frame.sym_ctx.GetParentOfInlinedScope (pc_addr, parent_pc_addr)
644 # Replace our thread frames with our new list that includes parent
645 # frames for inlined functions
646 thread.frames = new_thread_frames
647 # Now iterate through all threads and display our richer stack backtraces
648 for thread in crash_log.threads:
649 this_thread_crashed = thread.did_crash()
650 if options.crashed_only and this_thread_crashed == False:
651 continue
652 print "%s" % thread
653 prev_frame_index = -1
654 for frame_idx, frame in enumerate(thread.frames):
655 details = ' %s' % frame.details
656 module = frame.sym_ctx.GetModule()
657 instructions = None
658 if module:
659 module_basename = module.GetFileSpec().GetFilename();
660 function_start_load_addr = -1
661 function_name = None
662 function = frame.sym_ctx.GetFunction()
663 block = frame.sym_ctx.GetBlock()
664 line_entry = frame.sym_ctx.GetLineEntry()
665 symbol = frame.sym_ctx.GetSymbol()
666 inlined_block = block.GetContainingInlinedBlock();
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000667 disassemble = (this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth;
Greg Claytona3698c62012-01-21 00:37:19 +0000668 if inlined_block:
669 function_name = inlined_block.GetInlinedName();
670 block_range_idx = inlined_block.GetRangeIndexForBlockAddress (lldb.target.ResolveLoadAddress (frame.pc))
671 if block_range_idx < lldb.UINT32_MAX:
672 block_range_start_addr = inlined_block.GetRangeStartAddress (block_range_idx)
673 function_start_load_addr = block_range_start_addr.GetLoadAddress (lldb.target)
Greg Clayton01f7c962012-01-20 03:15:45 +0000674 else:
Greg Claytona3698c62012-01-21 00:37:19 +0000675 function_start_load_addr = frame.pc
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000676 if disassemble:
Greg Claytona3698c62012-01-21 00:37:19 +0000677 instructions = function.GetInstructions(lldb.target)
678 elif function:
679 function_name = function.GetName()
680 function_start_load_addr = function.GetStartAddress().GetLoadAddress (lldb.target)
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000681 if disassemble:
Greg Claytona3698c62012-01-21 00:37:19 +0000682 instructions = function.GetInstructions(lldb.target)
683 elif symbol:
684 function_name = symbol.GetName()
685 function_start_load_addr = symbol.GetStartAddress().GetLoadAddress (lldb.target)
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000686 if disassemble:
Greg Claytona3698c62012-01-21 00:37:19 +0000687 instructions = symbol.GetInstructions(lldb.target)
Greg Clayton01f7c962012-01-20 03:15:45 +0000688
Greg Claytona3698c62012-01-21 00:37:19 +0000689 if function_name:
690 # Print the function or symbol name and annotate if it was inlined
691 inline_suffix = ''
692 if inlined_block:
693 inline_suffix = '[inlined] '
694 else:
695 inline_suffix = ' '
696 if options.show_images:
697 details = "%s%s`%s" % (inline_suffix, module_basename, function_name)
698 else:
699 details = "%s" % (function_name)
700 # Dump the offset from the current function or symbol if it is non zero
701 function_offset = frame.pc - function_start_load_addr
702 if function_offset > 0:
703 details += " + %u" % (function_offset)
704 elif function_offset < 0:
705 defaults += " %i (invalid negative offset, file a bug) " % function_offset
706 # Print out any line information if any is available
707 if line_entry.GetFileSpec():
708 details += ' at %s' % line_entry.GetFileSpec().GetFilename()
709 details += ':%u' % line_entry.GetLine ()
710 column = line_entry.GetColumn()
711 if column > 0:
712 details += ':%u' % column
Greg Clayton01f7c962012-01-20 03:15:45 +0000713
714
Greg Claytona3698c62012-01-21 00:37:19 +0000715 # Only print out the concrete frame index if it changes.
716 # if prev_frame_index != frame.index:
717 # print "[%2u] %#16.16x %s" % (frame.index, frame.pc, details)
718 # else:
719 # print " %#16.16x %s" % (frame.pc, details)
720 print "[%2u] %#16.16x %s" % (frame.index, frame.pc, details)
721 prev_frame_index = frame.index
722 if instructions:
723 print
Greg Clayton2c1fdd02012-01-21 05:10:20 +0000724 disassemble_instructions (instructions, frame.pc, options, frame.index > 0)
Greg Claytona3698c62012-01-21 00:37:19 +0000725 print
Greg Clayton01f7c962012-01-20 03:15:45 +0000726
Greg Claytona3698c62012-01-21 00:37:19 +0000727 print
Greg Clayton01f7c962012-01-20 03:15:45 +0000728
Greg Claytona3698c62012-01-21 00:37:19 +0000729 if options.dump_image_list:
730 print "Binary Images:"
731 for image in crash_log.images:
732 print image
733
Greg Clayton01f7c962012-01-20 03:15:45 +0000734
735if __name__ == '__main__':
Greg Claytone9ee5502012-01-20 19:25:32 +0000736 # Create a new debugger instance
737 lldb.debugger = lldb.SBDebugger.Create()
738 SymbolicateCrashLog (sys.argv)
739elif lldb.debugger:
740 lldb.debugger.HandleCommand('command script add -f crashlog.Symbolicate crashlog')
741 print '"crashlog" command installed, type "crashlog --help" for detailed help'
Greg Clayton01f7c962012-01-20 03:15:45 +0000742