diff options
Diffstat (limited to 'lldb/examples/python/symbolication.py')
-rwxr-xr-x | lldb/examples/python/symbolication.py | 355 |
1 files changed, 221 insertions, 134 deletions
diff --git a/lldb/examples/python/symbolication.py b/lldb/examples/python/symbolication.py index 88846c99b3a..b655ad0d179 100755 --- a/lldb/examples/python/symbolication.py +++ b/lldb/examples/python/symbolication.py @@ -11,13 +11,13 @@ # "crashlog" command installed, type "crashlog --help" for detailed help # (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash # -# The benefit of running the crashlog command inside lldb in the -# embedded python interpreter is when the command completes, there +# The benefit of running the crashlog command inside lldb in the +# embedded python interpreter is when the command completes, there # will be a target with all of the files loaded at the locations # described in the crash log. Only the files that have stack frames # in the backtrace will be loaded unless the "--load-all" option # has been specified. This allows users to explore the program in the -# state it was in right at crash time. +# state it was in right at crash time. # # On MacOSX csh, tcsh: # ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash ) @@ -37,16 +37,23 @@ import sys import time import uuid + class Address: """Class that represents an address that will be symbolicated""" + def __init__(self, target, load_addr): self.target = target - self.load_addr = load_addr # The load address that this object represents - self.so_addr = None # the resolved lldb.SBAddress (if any), named so_addr for section/offset address - self.sym_ctx = None # The cached symbol context for this address - self.description = None # Any original textual description of this address to be used as a backup in case symbolication fails - self.symbolication = None # The cached symbolicated string that describes this address + self.load_addr = load_addr # The load address that this object represents + # the resolved lldb.SBAddress (if any), named so_addr for + # section/offset address + self.so_addr = None + self.sym_ctx = None # The cached symbol context for this address + # Any original textual description of this address to be used as a + # backup in case symbolication fails + self.description = None + self.symbolication = None # The cached symbolicated string that describes this address self.inlined = False + def __str__(self): s = "%#16.16x" % (self.load_addr) if self.symbolication: @@ -58,18 +65,19 @@ class Address: return s def resolve_addr(self): - if self.so_addr == None: - self.so_addr = self.target.ResolveLoadAddress (self.load_addr) + if self.so_addr is None: + self.so_addr = self.target.ResolveLoadAddress(self.load_addr) return self.so_addr def is_inlined(self): return self.inlined - + def get_symbol_context(self): - if self.sym_ctx == None: + if self.sym_ctx is None: sb_addr = self.resolve_addr() if sb_addr: - self.sym_ctx = self.target.ResolveSymbolContextForAddress (sb_addr, lldb.eSymbolContextEverything) + self.sym_ctx = self.target.ResolveSymbolContextForAddress( + sb_addr, lldb.eSymbolContextEverything) else: self.sym_ctx = lldb.SBSymbolContext() return self.sym_ctx @@ -82,9 +90,9 @@ class Address: return function.GetInstructions(self.target) return sym_ctx.GetSymbol().GetInstructions(self.target) return None - - def symbolicate(self, verbose = False): - if self.symbolication == None: + + def symbolicate(self, verbose=False): + if self.symbolication is None: self.symbolication = '' self.inlined = False sym_ctx = self.get_symbol_context() @@ -101,27 +109,32 @@ class Address: block = sym_ctx.GetBlock() line_entry = sym_ctx.GetLineEntry() symbol = sym_ctx.GetSymbol() - inlined_block = block.GetContainingInlinedBlock(); + inlined_block = block.GetContainingInlinedBlock() if function: self.symbolication += function.GetName() if inlined_block: self.inlined = True - self.symbolication += ' [inlined] ' + inlined_block.GetInlinedName(); - block_range_idx = inlined_block.GetRangeIndexForBlockAddress (self.so_addr) + self.symbolication += ' [inlined] ' + \ + inlined_block.GetInlinedName() + block_range_idx = inlined_block.GetRangeIndexForBlockAddress( + self.so_addr) if block_range_idx < lldb.UINT32_MAX: - block_range_start_addr = inlined_block.GetRangeStartAddress (block_range_idx) - function_start_load_addr = block_range_start_addr.GetLoadAddress (self.target) + block_range_start_addr = inlined_block.GetRangeStartAddress( + block_range_idx) + function_start_load_addr = block_range_start_addr.GetLoadAddress( + self.target) if function_start_load_addr == -1: - function_start_load_addr = function.GetStartAddress().GetLoadAddress (self.target) + function_start_load_addr = function.GetStartAddress().GetLoadAddress(self.target) elif symbol: self.symbolication += symbol.GetName() - function_start_load_addr = symbol.GetStartAddress().GetLoadAddress (self.target) + function_start_load_addr = symbol.GetStartAddress().GetLoadAddress(self.target) else: self.symbolication = '' return False - # Dump the offset from the current function or symbol if it is non zero + # Dump the offset from the current function or symbol if it + # is non zero function_offset = self.load_addr - function_start_load_addr if function_offset > 0: self.symbolication += " + %u" % (function_offset) @@ -135,38 +148,44 @@ class Address: self.symbolication += ' at %s' % line_entry.GetFileSpec() else: self.symbolication += ' at %s' % line_entry.GetFileSpec().GetFilename() - self.symbolication += ':%u' % line_entry.GetLine () + self.symbolication += ':%u' % line_entry.GetLine() column = line_entry.GetColumn() if column > 0: self.symbolication += ':%u' % column return True return False + class Section: """Class that represents an load address range""" sect_info_regex = re.compile('(?P<name>[^=]+)=(?P<range>.*)') addr_regex = re.compile('^\s*(?P<start>0x[0-9A-Fa-f]+)\s*$') - range_regex = re.compile('^\s*(?P<start>0x[0-9A-Fa-f]+)\s*(?P<op>[-+])\s*(?P<end>0x[0-9A-Fa-f]+)\s*$') + range_regex = re.compile( + '^\s*(?P<start>0x[0-9A-Fa-f]+)\s*(?P<op>[-+])\s*(?P<end>0x[0-9A-Fa-f]+)\s*$') - def __init__(self, start_addr = None, end_addr = None, name = None): + def __init__(self, start_addr=None, end_addr=None, name=None): self.start_addr = start_addr self.end_addr = end_addr self.name = name - + @classmethod def InitWithSBTargetAndSBSection(cls, target, section): sect_load_addr = section.GetLoadAddress(target) if sect_load_addr != lldb.LLDB_INVALID_ADDRESS: - obj = cls(sect_load_addr, sect_load_addr + section.size, section.name) + obj = cls( + sect_load_addr, + sect_load_addr + + section.size, + section.name) return obj else: return None - + def contains(self, addr): - return self.start_addr <= addr and addr < self.end_addr; - + return self.start_addr <= addr and addr < self.end_addr + def set_from_string(self, s): - match = self.sect_info_regex.match (s) + match = self.sect_info_regex.match(s) if match: self.name = match.group('name') range_str = match.group('range') @@ -192,22 +211,24 @@ class Section: print '<name>=<base>-<end> __TEXT=0x123000-0x124000 Section from base address and end address' print '<name>=<base>+<size> __TEXT=0x123000+0x1000 Section from base address and size' return False - + def __str__(self): if self.name: - if self.end_addr != None: - if self.start_addr != None: - return "%s=[0x%16.16x - 0x%16.16x)" % (self.name, self.start_addr, self.end_addr) + if self.end_addr is not None: + if self.start_addr is not None: + return "%s=[0x%16.16x - 0x%16.16x)" % ( + self.name, self.start_addr, self.end_addr) else: - if self.start_addr != None: + if self.start_addr is not None: return "%s=0x%16.16x" % (self.name, self.start_addr) return self.name return "<invalid>" - + + class Image: """A class that represents an executable image and any associated data""" - - def __init__(self, path, uuid = None): + + def __init__(self, path, uuid=None): self.path = path self.resolved_path = None self.resolved = False @@ -220,7 +241,7 @@ class Image: self.module = None self.symfile = None self.slide = None - + @classmethod def InitWithSBTargetAndSBModule(cls, target, module): '''Initialize this Image object with a module from a target.''' @@ -229,15 +250,16 @@ class Image: obj.resolved = True obj.arch = module.triple for section in module.sections: - symb_section = Section.InitWithSBTargetAndSBSection(target, section) + symb_section = Section.InitWithSBTargetAndSBSection( + target, section) if symb_section: - obj.section_infos.append (symb_section) + obj.section_infos.append(symb_section) obj.arch = module.triple obj.module = module obj.symfile = None obj.slide = None return obj - + def dump(self, prefix): print "%s%s" % (prefix, self) @@ -254,7 +276,7 @@ class Image: print 'module = %s' % (self.module) print 'symfile = "%s"' % (self.symfile) print 'slide = %i (0x%x)' % (self.slide, self.slide) - + def __str__(self): s = '' if self.uuid: @@ -268,15 +290,15 @@ class Image: s += "%s " % (resolved_path) for section_info in self.section_infos: s += ", %s" % (section_info) - if self.slide != None: + if self.slide is not None: s += ', slide = 0x%16.16x' % self.slide - return s - + return s + def add_section(self, section): - #print "added '%s' to '%s'" % (section, self.path) - self.section_infos.append (section) - - def get_section_containing_load_addr (self, load_addr): + # print "added '%s' to '%s'" % (section, self.path) + self.section_infos.append(section) + + def get_section_containing_load_addr(self, load_addr): for section_info in self.section_infos: if section_info.contains(load_addr): return section_info @@ -299,13 +321,13 @@ class Image: if self.symfile: return os.path.basename(self.symfile) return None - + def has_section_load_info(self): - return self.section_infos or self.slide != None - + return self.section_infos or self.slide is not None + def load_module(self, target): if self.unavailable: - return None # We already warned that we couldn't find this module, so don't return an error string + return None # We already warned that we couldn't find this module, so don't return an error string # Load this module into "target" using the section infos to # set the section load addresses if self.has_section_load_info(): @@ -315,9 +337,11 @@ class Image: num_sections_loaded = 0 for section_info in self.section_infos: if section_info.name: - section = self.module.FindSection (section_info.name) + section = self.module.FindSection( + section_info.name) if section: - error = target.SetSectionLoadAddress (section, section_info.start_addr) + error = target.SetSectionLoadAddress( + section, section_info.start_addr) if error.Success(): num_sections_loaded += 1 else: @@ -325,11 +349,13 @@ class Image: else: return 'error: unable to find the section named "%s"' % section_info.name else: - return 'error: unable to find "%s" section in "%s"' % (range.name, self.get_resolved_path()) + return 'error: unable to find "%s" section in "%s"' % ( + range.name, self.get_resolved_path()) if num_sections_loaded == 0: return 'error: no sections were successfully loaded' else: - err = target.SetModuleLoadAddress(self.module, self.slide) + err = target.SetModuleLoadAddress( + self.module, self.slide) if err.Fail(): return err.GetCString() return None @@ -339,30 +365,33 @@ class Image: return 'error: invalid target' else: return 'error: no section infos' - + def add_module(self, target): '''Add the Image described in this object to "target" and load the sections if "load" is True.''' if target: - # Try and find using UUID only first so that paths need not match up + # Try and find using UUID only first so that paths need not match + # up uuid_str = self.get_normalized_uuid_string() if uuid_str: - self.module = target.AddModule (None, None, uuid_str) + self.module = target.AddModule(None, None, uuid_str) if not self.module: - self.locate_module_and_debug_symbols () + self.locate_module_and_debug_symbols() if self.unavailable: return None resolved_path = self.get_resolved_path() - self.module = target.AddModule (resolved_path, self.arch, uuid_str, self.symfile) + self.module = target.AddModule( + resolved_path, self.arch, uuid_str, self.symfile) if not self.module: - return 'error: unable to get module for (%s) "%s"' % (self.arch, self.get_resolved_path()) + return 'error: unable to get module for (%s) "%s"' % ( + self.arch, self.get_resolved_path()) if self.has_section_load_info(): return self.load_module(target) else: - return None # No sections, the module was added to the target, so success + return None # No sections, the module was added to the target, so success else: return 'error: invalid target' - - def locate_module_and_debug_symbols (self): + + def locate_module_and_debug_symbols(self): # By default, just use the paths that were supplied in: # self.path # self.resolved_path @@ -371,7 +400,7 @@ class Image: # Subclasses can inherit from this class and override this function self.resolved = True return True - + def get_uuid(self): if not self.uuid and self.module: self.uuid = uuid.UUID(self.module.GetUUIDString()) @@ -387,12 +416,13 @@ class Image: if self.unavailable: return None - if self.locate_module_and_debug_symbols (): - resolved_path = self.get_resolved_path(); - path_spec = lldb.SBFileSpec (resolved_path) + if self.locate_module_and_debug_symbols(): + resolved_path = self.get_resolved_path() + path_spec = lldb.SBFileSpec(resolved_path) #result.PutCString ('plist[%s] = %s' % (uuid, self.plist)) error = lldb.SBError() - target = lldb.debugger.CreateTarget (resolved_path, self.arch, None, False, error); + target = lldb.debugger.CreateTarget( + resolved_path, self.arch, None, False, error) if target: self.module = target.FindModule(path_spec) if self.has_section_load_info(): @@ -405,20 +435,21 @@ class Image: else: print 'error: unable to locate main executable (%s) "%s"' % (self.arch, self.path) return None - + + class Symbolicator: def __init__(self): """A class the represents the information needed to symbolicate addresses in a program""" self.target = None - self.images = list() # a list of images to be used when symbolicating + self.images = list() # a list of images to be used when symbolicating self.addr_mask = 0xffffffffffffffff - + @classmethod def InitWithSBTarget(cls, target): obj = cls() obj.target = target - obj.images = list(); + obj.images = list() triple = target.triple if triple: arch = triple.split('-')[0] @@ -429,7 +460,7 @@ class Symbolicator: image = Image.InitWithSBTargetAndSBModule(target, module) obj.images.append(image) return obj - + def __str__(self): s = "Symbolicator:\n" if self.target: @@ -441,7 +472,7 @@ class Symbolicator: for image in self.images: s += ' %s\n' % (image) return s - + def find_images_with_identifier(self, identifier): images = list() for image in self.images: @@ -454,20 +485,20 @@ class Symbolicator: if regex.match(image.identifier): images.append(image) return images - + def find_image_containing_load_addr(self, load_addr): for image in self.images: - if image.get_section_containing_load_addr (load_addr): + if image.get_section_containing_load_addr(load_addr): return image return None - + def create_target(self): if self.target: return self.target if self.images: for image in self.images: - self.target = image.create_target () + self.target = image.create_target() if self.target: if self.target.GetAddressByteSize() == 4: triple = self.target.triple @@ -477,9 +508,8 @@ class Symbolicator: self.addr_mask = 0xfffffffffffffffe return self.target return None - - - def symbolicate(self, load_addr, verbose = False): + + def symbolicate(self, load_addr, verbose=False): if not self.target: self.create_target() if self.target: @@ -493,51 +523,60 @@ class Symbolicator: # that a load address belongs to and lazily load its module in the # target, but we shouldn't do any of this if we have a live process if not live_process: - image = self.find_image_containing_load_addr (load_addr) + image = self.find_image_containing_load_addr(load_addr) if image: - image.add_module (self.target) + image.add_module(self.target) symbolicated_address = Address(self.target, load_addr) - if symbolicated_address.symbolicate (verbose): + if symbolicated_address.symbolicate(verbose): if symbolicated_address.so_addr: symbolicated_addresses = list() symbolicated_addresses.append(symbolicated_address) # See if we were able to reconstruct anything? - while 1: + while True: inlined_parent_so_addr = lldb.SBAddress() - inlined_parent_sym_ctx = symbolicated_address.sym_ctx.GetParentOfInlinedScope (symbolicated_address.so_addr, inlined_parent_so_addr) + inlined_parent_sym_ctx = symbolicated_address.sym_ctx.GetParentOfInlinedScope( + symbolicated_address.so_addr, inlined_parent_so_addr) if not inlined_parent_sym_ctx: break if not inlined_parent_so_addr: break - symbolicated_address = Address(self.target, inlined_parent_so_addr.GetLoadAddress(self.target)) + symbolicated_address = Address( + self.target, inlined_parent_so_addr.GetLoadAddress( + self.target)) symbolicated_address.sym_ctx = inlined_parent_sym_ctx symbolicated_address.so_addr = inlined_parent_so_addr - symbolicated_address.symbolicate (verbose) - + symbolicated_address.symbolicate(verbose) + # push the new frame onto the new frame stack - symbolicated_addresses.append (symbolicated_address) - + symbolicated_addresses.append(symbolicated_address) + if symbolicated_addresses: return symbolicated_addresses else: print 'error: no target in Symbolicator' return None - - -def disassemble_instructions (target, instructions, pc, insts_before_pc, insts_after_pc, non_zeroeth_frame): + + +def disassemble_instructions( + target, + instructions, + pc, + insts_before_pc, + insts_after_pc, + non_zeroeth_frame): lines = list() pc_index = -1 comment_column = 50 for inst_idx, inst in enumerate(instructions): - inst_pc = inst.GetAddress().GetLoadAddress(target); + inst_pc = inst.GetAddress().GetLoadAddress(target) if pc == inst_pc: pc_index = inst_idx - mnemonic = inst.GetMnemonic (target) - operands = inst.GetOperands (target) - comment = inst.GetComment (target) + mnemonic = inst.GetMnemonic(target) + operands = inst.GetOperands(target) + comment = inst.GetComment(target) #data = inst.GetData (target) - lines.append ("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands)) + lines.append("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands)) if comment: line_len = len(lines[-1]) if line_len < comment_column: @@ -545,7 +584,8 @@ def disassemble_instructions (target, instructions, pc, insts_before_pc, insts_a lines[-1] += "; %s" % comment if pc_index >= 0: - # If we are disassembling the non-zeroeth frame, we need to backup the PC by 1 + # If we are disassembling the non-zeroeth frame, we need to backup the + # PC by 1 if non_zeroeth_frame and pc_index > 0: pc_index = pc_index - 1 if insts_before_pc == -1: @@ -560,81 +600,128 @@ def disassemble_instructions (target, instructions, pc, insts_before_pc, insts_a end_idx = pc_index + insts_after_pc if end_idx > inst_idx: end_idx = inst_idx - for i in range(start_idx, end_idx+1): + for i in range(start_idx, end_idx + 1): if i == pc_index: print ' -> ', lines[i] else: print ' ', lines[i] -def print_module_section_data (section): + +def print_module_section_data(section): print section section_data = section.GetSectionData() if section_data: ostream = lldb.SBStream() - section_data.GetDescription (ostream, section.GetFileAddress()) + section_data.GetDescription(ostream, section.GetFileAddress()) print ostream.GetData() -def print_module_section (section, depth): + +def print_module_section(section, depth): print section if depth > 0: num_sub_sections = section.GetNumSubSections() for sect_idx in range(num_sub_sections): - print_module_section (section.GetSubSectionAtIndex(sect_idx), depth - 1) + print_module_section( + section.GetSubSectionAtIndex(sect_idx), depth - 1) + -def print_module_sections (module, depth): +def print_module_sections(module, depth): for sect in module.section_iter(): - print_module_section (sect, depth) + print_module_section(sect, depth) -def print_module_symbols (module): + +def print_module_symbols(module): for sym in module: print sym + def Symbolicate(command_args): - + usage = "usage: %prog [options] <addr1> [addr2 ...]" - description='''Symbolicate one or more addresses using LLDB's python scripting API..''' - parser = optparse.OptionParser(description=description, prog='crashlog.py',usage=usage) - parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) - parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') - parser.add_option('-f', '--file', type='string', metavar='file', dest='file', help='Specify a file to use when symbolicating') - parser.add_option('-a', '--arch', type='string', metavar='arch', dest='arch', help='Specify a architecture to use when symbolicating') - parser.add_option('-s', '--slide', type='int', metavar='slide', dest='slide', help='Specify the slide to use on the file specified with the --file option', default=None) - parser.add_option('--section', type='string', action='append', dest='section_strings', help='specify <sect-name>=<start-addr> or <sect-name>=<start-addr>-<end-addr>') + description = '''Symbolicate one or more addresses using LLDB's python scripting API..''' + parser = optparse.OptionParser( + description=description, + prog='crashlog.py', + usage=usage) + parser.add_option( + '-v', + '--verbose', + action='store_true', + dest='verbose', + help='display verbose debug info', + default=False) + parser.add_option( + '-p', + '--platform', + type='string', + metavar='platform', + dest='platform', + help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') + parser.add_option( + '-f', + '--file', + type='string', + metavar='file', + dest='file', + help='Specify a file to use when symbolicating') + parser.add_option( + '-a', + '--arch', + type='string', + metavar='arch', + dest='arch', + help='Specify a architecture to use when symbolicating') + parser.add_option( + '-s', + '--slide', + type='int', + metavar='slide', + dest='slide', + help='Specify the slide to use on the file specified with the --file option', + default=None) + parser.add_option( + '--section', + type='string', + action='append', + dest='section_strings', + help='specify <sect-name>=<start-addr> or <sect-name>=<start-addr>-<end-addr>') try: (options, args) = parser.parse_args(command_args) except: return symbolicator = Symbolicator() - images = list(); + images = list() if options.file: - image = Image(options.file); + image = Image(options.file) image.arch = options.arch - # Add any sections that were specified with one or more --section options + # Add any sections that were specified with one or more --section + # options if options.section_strings: for section_str in options.section_strings: section = Section() - if section.set_from_string (section_str): - image.add_section (section) + if section.set_from_string(section_str): + image.add_section(section) else: sys.exit(1) - if options.slide != None: + if options.slide is not None: image.slide = options.slide symbolicator.images.append(image) - + target = symbolicator.create_target() if options.verbose: print symbolicator if target: for addr_str in args: addr = int(addr_str, 0) - symbolicated_addrs = symbolicator.symbolicate(addr, options.verbose) + symbolicated_addrs = symbolicator.symbolicate( + addr, options.verbose) for symbolicated_addr in symbolicated_addrs: print symbolicated_addr print else: print 'error: no target for %s' % (symbolicator) - + if __name__ == '__main__': # Create a new debugger instance lldb.debugger = lldb.SBDebugger.Create() - Symbolicate (sys.argv[1:]) + Symbolicate(sys.argv[1:]) |