forked from OSchip/llvm-project
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:
parent
30e422d7ba
commit
c8f73d7b96
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue