Added an interactive mode to the "crashlog" command so that we can look at multiple crash logs at once and do some data mining. Added an interactive command prompt that allows you to do:

% PYTHONPATH=./build/Debug/LLDB.framework/Resources/Python ; ./build/Debug//LLDB.framework/Resources/Python/lldb/macosx/crashlog.py -i ~/Downloads/crashes2/*.crash ) 

then you get an interactive prompt where you can search for data within all crash logs. For example you can do:

% list

which will list all crash logs

And you can search for all images given an image basename, or full path:

% image LLDB
% image /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB
% image LLDB.framework

Which would all produce an output listing like:

40CD4430-7D27-3248-BE4C-71B1F36FC5D0 (1.132 - 132) /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB, __TEXT=[0x000000011f8bc000 - 0x0000000120d3efbf)
B727A528-FF1F-3B20-9E4F-BBE96C7D922D (1.136 - 136) /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB, __TEXT=[0x000000011e7f7000 - 0x000000011fc7ff87)
4D6F8DC2-5757-39C7-96B0-1A5B5171DC6B (1.137 - 137) /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB, __TEXT=[0x000000012bd7f000 - 0x000000012d1fcfef)
FBF8786F-92B9-31E3-8BCD-A82148338966 (1.137 - 137) /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB, __TEXT=[0x0000000122d78000 - 0x00000001241f5fd7)
7AE082E3-3BB7-3F64-A308-063E559DFC45 (1.143 - 143) /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB, __TEXT=[0x0000000119b8d000 - 0x000000011b02ef5f)
7AE082E3-3BB7-3F64-A308-063E559DFC45 (1.143 - 143) /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB, __TEXT=[0x0000000111497000 - 0x0000000112938f5f)
7AE082E3-3BB7-3F64-A308-063E559DFC45 (1.143 - 143) /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB, __TEXT=[0x0000000116680000 - 0x0000000117b21f5f)

llvm-svn: 156201
This commit is contained in:
Greg Clayton 2012-05-04 20:44:14 +00:00
parent 30e422d7ba
commit c8f73d7b96
2 changed files with 185 additions and 85 deletions

View File

@ -28,16 +28,19 @@
import lldb
import commands
import cmd
import glob
import optparse
import os
import plistlib
import pprint # pp = pprint.PrettyPrinter(indent=4); pp.pprint(command_args)
import re
import shlex
import string
import sys
import time
import uuid
import lldb.utils.symbolication
from lldb.utils import symbolication
PARSE_MODE_NORMAL = 0
PARSE_MODE_THREAD = 1
@ -45,7 +48,7 @@ PARSE_MODE_IMAGES = 2
PARSE_MODE_THREGS = 3
PARSE_MODE_SYSTEM = 4
class CrashLog(lldb.utils.symbolication.Symbolicator):
class CrashLog(symbolication.Symbolicator):
"""Class that does parses darwin crash logs"""
thread_state_regex = re.compile('^Thread ([0-9]+) crashed with')
thread_regex = re.compile('^Thread ([0-9]+)([^:]*):(.*)')
@ -100,7 +103,7 @@ class CrashLog(lldb.utils.symbolication.Symbolicator):
def dump(self, prefix):
print "%s%s" % (prefix, str(self))
class DarwinImage(lldb.utils.symbolication.Image):
class DarwinImage(symbolication.Image):
"""Class that represents a binary images in a darwin crash log"""
dsymForUUIDBinary = os.path.expanduser('~rc/bin/dsymForUUID')
if not os.path.exists(dsymForUUIDBinary):
@ -109,8 +112,8 @@ class CrashLog(lldb.utils.symbolication.Symbolicator):
dwarfdump_uuid_regex = re.compile('UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*')
def __init__(self, text_addr_lo, text_addr_hi, identifier, version, uuid, path):
lldb.utils.symbolication.Image.__init__(self, path, uuid);
self.add_section (lldb.utils.symbolication.Section(text_addr_lo, text_addr_hi, "__TEXT"))
symbolication.Image.__init__(self, path, uuid);
self.add_section (symbolication.Section(text_addr_lo, text_addr_hi, "__TEXT"))
self.identifier = identifier
self.version = version
@ -161,7 +164,7 @@ class CrashLog(lldb.utils.symbolication.Symbolicator):
def __init__(self, path):
"""CrashLog constructor that take a path to a darwin crash log file"""
lldb.utils.symbolication.Symbolicator.__init__(self);
symbolication.Symbolicator.__init__(self);
self.path = os.path.expanduser(path);
self.info_lines = list()
self.system_profile = list()
@ -316,7 +319,7 @@ class CrashLog(lldb.utils.symbolication.Symbolicator):
def create_target(self):
#print 'crashlog.create_target()...'
target = lldb.utils.symbolication.Symbolicator.create_target(self)
target = symbolication.Symbolicator.create_target(self)
if target:
return target
# We weren't able to open the main executable as, but we can still symbolicate
@ -342,6 +345,101 @@ def usage():
print "Usage: lldb-symbolicate.py [-n name] executable-image"
sys.exit(0)
class Interactive(cmd.Cmd):
'''Interactive prompt for analyzing one or more Darwin crash logs, type "help" to see a list of supported commands.'''
image_option_parser = None
def __init__(self, crash_logs):
cmd.Cmd.__init__(self)
self.intro = 'Interactive crashlogs prompt, type "help" to see a list of supported commands.'
self.crash_logs = crash_logs
self.prompt = '% '
def default(self, line):
'''Catch all for unknown command, which will exit the interpreter.'''
print "uknown command: %s" % line
return True
def do_q(self, line):
'''Quit command'''
return True
def do_quit(self, line):
'''Quit command'''
return True
def do_list(self, line=None):
'''Dump a list of all crash logs that are currently loaded.
USAGE: list'''
print '%u crash logs are loaded:' % len(self.crash_logs)
for (crash_log_idx, crash_log) in enumerate(self.crash_logs):
print '[%u] = %s' % (crash_log_idx, crash_log.path)
def do_image(self, line):
'''Dump information about an image in the crash log given an image basename.
USAGE: image <basename>'''
usage = "usage: %prog [options] <PATH> [PATH ...]"
description='''Dump information about one or more images in all crash logs. The <PATH>
can be a full path or a image basename.'''
command_args = shlex.split(line)
if not self.image_option_parser:
self.image_option_parser = optparse.OptionParser(description=description, prog='image',usage=usage)
self.image_option_parser.add_option('-a', '--all', action='store_true', help='show all images', default=False)
try:
(options, args) = self.image_option_parser.parse_args(command_args)
except:
return
for image_path in args:
fullpath_search = image_path[0] == '/'
for crash_log in self.crash_logs:
matches_found = 0
for (image_idx, image) in enumerate(crash_log.images):
if fullpath_search:
if image.get_resolved_path() == image_path:
matches_found += 1
print image
else:
image_basename = image.get_resolved_path_basename()
if image_basename == image_path:
matches_found += 1
print image
if matches_found == 0:
for (image_idx, image) in enumerate(crash_log.images):
if string.find(image.get_resolved_path(), image_path) >= 0:
print image
return False
def interactive_crashlogs(options, args):
crash_log_files = list()
for arg in args:
for resolved_path in glob.glob(arg):
crash_log_files.append(resolved_path)
crash_logs = list();
for crash_log_file in crash_log_files:
#print 'crash_log_file = "%s"' % crash_log_file
crash_log = CrashLog(crash_log_file)
if crash_log.error:
print crash_log.error
continue
if options.verbose:
crash_log.dump()
if not crash_log.images:
print 'error: no images in crash log "%s"' % (crash_log)
continue
else:
crash_logs.append(crash_log)
interpreter = Interactive(crash_logs)
# List all crash logs that were imported
interpreter.do_list()
interpreter.cmdloop()
def Symbolicate(debugger, command, result, dict):
try:
SymbolicateCrashLog (shlex.split(command))
@ -358,19 +456,17 @@ for use at the LLDB command line. After a crash log has been parsed and symbolic
created that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows
you to explore the program as if it were stopped at the locations described in the crash log and functions can
be disassembled and lookups can be performed using the addresses found in the crash log.'''
parser = optparse.OptionParser(description=description, prog='crashlog.py',usage=usage)
parser.add_option('--platform', type='string', metavar='platform', dest='platform', help='specify one platform by name')
parser = optparse.OptionParser(description=description, prog='crashlog',usage=usage)
parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
parser.add_option('--no-images', action='store_false', dest='show_images', help='don\'t show images in stack frames', default=True)
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)
parser.add_option('--image-list', action='store_true', dest='dump_image_list', help='show image list', default=False)
parser.add_option('--images', action='store_true', dest='dump_image_list', help='show image list', default=False)
parser.add_option('-g', '--debug-delay', type='int', dest='debug_delay', metavar='NSEC', help='pause for NSEC seconds for debugger', default=0)
parser.add_option('-c', '--crashed-only', action='store_true', dest='crashed_only', help='only symbolicate the crashed thread', default=False)
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)
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)
parser.add_option('-B', '--disasm-before', type='int', dest='disassemble_before', help='the number of instructions to disassemble before the frame PC', default=4)
parser.add_option('-A', '--disasm-after', type='int', dest='disassemble_after', help='the number of instructions to disassemble after the frame PC', default=4)
loaded_addresses = False
parser.add_option('-i', '--interactive', action='store_true', help='parse all crash logs and enter interactive mode', default=False)
try:
(options, args) = parser.parse_args(command_args)
except:
@ -385,85 +481,89 @@ be disassembled and lookups can be performed using the addresses found in the cr
print "Waiting %u seconds for debugger to attach..." % options.debug_delay
time.sleep(options.debug_delay)
error = lldb.SBError()
if args:
for crash_log_file in args:
crash_log = CrashLog(crash_log_file)
if options.interactive:
interactive_crashlogs(options, args)
else:
for crash_log_file in args:
crash_log = CrashLog(crash_log_file)
#pp = pprint.PrettyPrinter(indent=4); pp.pprint(args)
if crash_log.error:
print crash_log.error
return
if options.verbose:
crash_log.dump()
if not crash_log.images:
print 'error: no images in crash log'
return
#pp = pprint.PrettyPrinter(indent=4); pp.pprint(args)
if crash_log.error:
print crash_log.error
return
if options.verbose:
crash_log.dump()
if not crash_log.images:
print 'error: no images in crash log'
return
target = crash_log.create_target ()
if not target:
return
exe_module = target.GetModuleAtIndex(0)
images_to_load = list()
loaded_images = list()
if options.load_all_images:
# --load-all option was specified, load everything up
for image in crash_log.images:
images_to_load.append(image)
else:
# Only load the images found in stack frames for the crashed threads
for ident in crash_log.idents:
images = crash_log.find_images_with_identifier (ident)
if images:
for image in images:
images_to_load.append(image)
else:
print 'error: can\'t find image for identifier "%s"' % ident
for image in images_to_load:
if image in loaded_images:
print "warning: skipping %s loaded at %#16.16x duplicate entry (probably commpage)" % (image.path, image.text_addr_lo)
target = crash_log.create_target ()
if not target:
return
exe_module = target.GetModuleAtIndex(0)
images_to_load = list()
loaded_images = list()
if options.load_all_images:
# --load-all option was specified, load everything up
for image in crash_log.images:
images_to_load.append(image)
else:
err = image.add_module (target)
if err:
print err
else:
#print 'loaded %s' % image
loaded_images.append(image)
# Only load the images found in stack frames for the crashed threads
for ident in crash_log.idents:
images = crash_log.find_images_with_identifier (ident)
if images:
for image in images:
images_to_load.append(image)
else:
print 'error: can\'t find image for identifier "%s"' % ident
for thread in crash_log.threads:
this_thread_crashed = thread.did_crash()
if options.crashed_only and this_thread_crashed == False:
continue
print "%s" % thread
#prev_frame_index = -1
for frame_idx, frame in enumerate(thread.frames):
disassemble = (this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth;
symbolicated_frame_addresses = crash_log.symbolicate (frame.pc)
if symbolicated_frame_addresses:
symbolicated_frame_address_idx = 0
for symbolicated_frame_address in symbolicated_frame_addresses:
print '[%3u] %s' % (frame_idx, symbolicated_frame_address)
if symbolicated_frame_address_idx == 0:
if disassemble:
instructions = symbolicated_frame_address.get_instructions()
if instructions:
print
lldb.utils.symbolication.disassemble_instructions (target,
instructions,
frame.pc,
options.disassemble_before,
options.disassemble_after, frame.index > 0)
print
symbolicated_frame_address_idx += 1
for image in images_to_load:
if image in loaded_images:
print "warning: skipping %s loaded at %#16.16x duplicate entry (probably commpage)" % (image.path, image.text_addr_lo)
else:
print frame
print
err = image.add_module (target)
if err:
print err
else:
#print 'loaded %s' % image
loaded_images.append(image)
for thread in crash_log.threads:
this_thread_crashed = thread.did_crash()
if options.crashed_only and this_thread_crashed == False:
continue
print "%s" % thread
#prev_frame_index = -1
for frame_idx, frame in enumerate(thread.frames):
disassemble = (this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth;
symbolicated_frame_addresses = crash_log.symbolicate (frame.pc)
if symbolicated_frame_addresses:
symbolicated_frame_address_idx = 0
for symbolicated_frame_address in symbolicated_frame_addresses:
print '[%3u] %s' % (frame_idx, symbolicated_frame_address)
if symbolicated_frame_address_idx == 0:
if disassemble:
instructions = symbolicated_frame_address.get_instructions()
if instructions:
print
symbolication.disassemble_instructions (target,
instructions,
frame.pc,
options.disassemble_before,
options.disassemble_after, frame.index > 0)
print
symbolicated_frame_address_idx += 1
else:
print frame
print
if options.dump_image_list:
print "Binary Images:"
for image in crash_log.images:
print image
if options.dump_image_list:
print "Binary Images:"
for image in crash_log.images:
print image
if __name__ == '__main__':
# Create a new debugger instance

View File

@ -206,7 +206,7 @@ class Image:
print "%s%s" % (prefix, self)
def __str__(self):
s = "%s %s" % (self.get_uuid(), self.get_resolved_path())
s = "%s %s %s" % (self.get_uuid(), self.version, self.get_resolved_path())
for section_info in self.section_infos:
s += ", %s" % (section_info)
if self.slide != None: