| #!/usr/bin/env python |
| from __future__ import print_function |
| |
| import lldb |
| import optparse |
| import shlex |
| import string |
| import sys |
| |
| |
| class DumpLineTables: |
| command_name = "dump-line-tables" |
| short_decription = "Dumps full paths to compile unit files and optionally all line table files." |
| description = 'Dumps all line tables from all compile units for any modules specified as arguments. Specifying the --verbose flag will output address ranges for each line entry.' |
| usage = "usage: %prog [options] MODULE1 [MODULE2 ...]" |
| def create_options(self): |
| self.parser = optparse.OptionParser( |
| description=self.description, |
| prog=self.command_name, |
| usage=self.usage) |
| |
| self.parser.add_option( |
| '-v', |
| '--verbose', |
| action='store_true', |
| dest='verbose', |
| help='Display verbose output.', |
| default=False) |
| |
| def get_short_help(self): |
| return self.short_decription |
| |
| def get_long_help(self): |
| return self.help_string |
| |
| def __init__(self, debugger, unused): |
| self.create_options() |
| self.help_string = self.parser.format_help() |
| |
| def __call__(self, debugger, command, exe_ctx, result): |
| # Use the Shell Lexer to properly parse up command options just like a |
| # shell would |
| command_args = shlex.split(command) |
| |
| try: |
| (options, args) = self.parser.parse_args(command_args) |
| except: |
| # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit |
| # (courtesy of OptParse dealing with argument errors by throwing SystemExit) |
| result.SetError("option parsing failed") |
| return |
| |
| # Always get program state from the SBExecutionContext passed in as exe_ctx |
| target = exe_ctx.GetTarget() |
| if not target.IsValid(): |
| result.SetError("invalid target") |
| return |
| |
| for module_path in args: |
| module = target.module[module_path] |
| if not module: |
| result.SetError('no module found that matches "%s".' % (module_path)) |
| return |
| num_cus = module.GetNumCompileUnits() |
| print('Module: "%s"' % (module.file.fullpath), end=' ', file=result) |
| if num_cus == 0: |
| print('no debug info.', file=result) |
| continue |
| print('has %u compile units:' % (num_cus), file=result) |
| for cu_idx in range(num_cus): |
| cu = module.GetCompileUnitAtIndex(cu_idx) |
| print(' Compile Unit: %s' % (cu.file.fullpath), file=result) |
| for line_idx in range(cu.GetNumLineEntries()): |
| line_entry = cu.GetLineEntryAtIndex(line_idx) |
| start_file_addr = line_entry.addr.file_addr |
| end_file_addr = line_entry.end_addr.file_addr |
| # If the two addresses are equal, this line table entry |
| # is a termination entry |
| if options.verbose: |
| if start_file_addr != end_file_addr: |
| result.PutCString( |
| ' [%#x - %#x): %s' % |
| (start_file_addr, end_file_addr, line_entry)) |
| else: |
| if start_file_addr == end_file_addr: |
| result.PutCString(' %#x: END' % |
| (start_file_addr)) |
| else: |
| result.PutCString( |
| ' %#x: %s' % |
| (start_file_addr, line_entry)) |
| if start_file_addr == end_file_addr: |
| result.PutCString("\n") |
| |
| |
| class DumpFiles: |
| command_name = "dump-files" |
| short_description = "Dumps full paths to compile unit files and optionally all line table files." |
| usage = "usage: %prog [options] MODULE1 [MODULE2 ...]" |
| description = '''This class adds a dump-files command to the LLDB interpreter. |
| |
| This command will dump all compile unit file paths found for each source file |
| for the binaries specified as arguments in the current target. Specify the |
| --support-files or -s option to see all file paths that a compile unit uses in |
| its lines tables. This is handy for troubleshooting why breakpoints aren't |
| working in IDEs that specify full paths to source files when setting file and |
| line breakpoints. Sometimes symlinks cause the debug info to contain the symlink |
| path and an IDE will resolve the path to the actual file and use the resolved |
| path when setting breakpoints. |
| ''' |
| def create_options(self): |
| # Pass add_help_option = False, since this keeps the command in line with lldb commands, |
| # and we wire up "help command" to work by providing the long & short help methods below. |
| self.parser = optparse.OptionParser( |
| description = self.description, |
| prog = self.command_name, |
| usage = self.usage, |
| add_help_option = False) |
| |
| self.parser.add_option( |
| '-s', |
| '--support-files', |
| action = 'store_true', |
| dest = 'support_files', |
| help = 'Dumps full paths to all files used in a compile unit.', |
| default = False) |
| |
| def get_short_help(self): |
| return self.short_description |
| |
| def get_long_help(self): |
| return self.help_string |
| |
| def __init__(self, debugger, unused): |
| self.create_options() |
| self.help_string = self.parser.format_help() |
| |
| def __call__(self, debugger, command, exe_ctx, result): |
| # Use the Shell Lexer to properly parse up command options just like a |
| # shell would |
| command_args = shlex.split(command) |
| |
| try: |
| (options, args) = self.parser.parse_args(command_args) |
| except: |
| # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit |
| # (courtesy of OptParse dealing with argument errors by throwing SystemExit) |
| result.SetError("option parsing failed") |
| return |
| |
| # Always get program state from the SBExecutionContext passed in as exe_ctx |
| target = exe_ctx.GetTarget() |
| if not target.IsValid(): |
| result.SetError("invalid target") |
| return |
| |
| if len(args) == 0: |
| result.SetError("one or more executable paths must be specified") |
| return |
| |
| for module_path in args: |
| module = target.module[module_path] |
| if not module: |
| result.SetError('no module found that matches "%s".' % (module_path)) |
| return |
| num_cus = module.GetNumCompileUnits() |
| print('Module: "%s"' % (module.file.fullpath), end=' ', file=result) |
| if num_cus == 0: |
| print('no debug info.', file=result) |
| continue |
| print('has %u compile units:' % (num_cus), file=result) |
| for i in range(num_cus): |
| cu = module.GetCompileUnitAtIndex(i) |
| print(' Compile Unit: %s' % (cu.file.fullpath), file=result) |
| if options.support_files: |
| num_support_files = cu.GetNumSupportFiles() |
| for j in range(num_support_files): |
| path = cu.GetSupportFileAtIndex(j).fullpath |
| print(' file[%u]: %s' % (j, path), file=result) |
| |
| |
| def __lldb_init_module(debugger, dict): |
| # This initializer is being run from LLDB in the embedded command interpreter |
| |
| # Add any commands contained in this module to LLDB |
| debugger.HandleCommand( |
| 'command script add -c %s.DumpLineTables %s' % (__name__, |
| DumpLineTables.command_name)) |
| debugger.HandleCommand( |
| 'command script add -c %s.DumpFiles %s' % (__name__, DumpFiles.command_name)) |
| print('The "%s" and "%s" commands have been installed.' % (DumpLineTables.command_name, |
| DumpFiles.command_name)) |