| #!/usr/bin/env python |
| |
| import lldb |
| import optparse |
| import shlex |
| import string |
| import sys |
| |
| |
| class DumpLineTables: |
| command_name = "dump-line-tables" |
| short_description = ( |
| "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_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 |
| |
| 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 -o -c %s.DumpLineTables %s" |
| % (__name__, DumpLineTables.command_name) |
| ) |
| debugger.HandleCommand( |
| "command script add -o -c %s.DumpFiles %s" % (__name__, DumpFiles.command_name) |
| ) |
| print( |
| 'The "%s" and "%s" commands have been installed.' |
| % (DumpLineTables.command_name, DumpFiles.command_name) |
| ) |