1#!/usr/bin/env python 2 3import lldb 4import optparse 5import shlex 6import string 7import sys 8 9 10class DumpLineTables: 11 command_name = "dump-line-tables" 12 short_description = "Dumps full paths to compile unit files and optionally all line table files." 13 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.' 14 usage = "usage: %prog [options] MODULE1 [MODULE2 ...]" 15 def create_options(self): 16 self.parser = optparse.OptionParser( 17 description=self.description, 18 prog=self.command_name, 19 usage=self.usage) 20 21 self.parser.add_option( 22 '-v', 23 '--verbose', 24 action='store_true', 25 dest='verbose', 26 help='Display verbose output.', 27 default=False) 28 29 def get_short_help(self): 30 return self.short_description 31 32 def get_long_help(self): 33 return self.help_string 34 35 def __init__(self, debugger, unused): 36 self.create_options() 37 self.help_string = self.parser.format_help() 38 39 def __call__(self, debugger, command, exe_ctx, result): 40 # Use the Shell Lexer to properly parse up command options just like a 41 # shell would 42 command_args = shlex.split(command) 43 44 try: 45 (options, args) = self.parser.parse_args(command_args) 46 except: 47 # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit 48 # (courtesy of OptParse dealing with argument errors by throwing SystemExit) 49 result.SetError("option parsing failed") 50 return 51 52 # Always get program state from the SBExecutionContext passed in as exe_ctx 53 target = exe_ctx.GetTarget() 54 if not target.IsValid(): 55 result.SetError("invalid target") 56 return 57 58 for module_path in args: 59 module = target.module[module_path] 60 if not module: 61 result.SetError('no module found that matches "%s".' % (module_path)) 62 return 63 num_cus = module.GetNumCompileUnits() 64 print('Module: "%s"' % (module.file.fullpath), end=' ', file=result) 65 if num_cus == 0: 66 print('no debug info.', file=result) 67 continue 68 print('has %u compile units:' % (num_cus), file=result) 69 for cu_idx in range(num_cus): 70 cu = module.GetCompileUnitAtIndex(cu_idx) 71 print(' Compile Unit: %s' % (cu.file.fullpath), file=result) 72 for line_idx in range(cu.GetNumLineEntries()): 73 line_entry = cu.GetLineEntryAtIndex(line_idx) 74 start_file_addr = line_entry.addr.file_addr 75 end_file_addr = line_entry.end_addr.file_addr 76 # If the two addresses are equal, this line table entry 77 # is a termination entry 78 if options.verbose: 79 if start_file_addr != end_file_addr: 80 result.PutCString( 81 ' [%#x - %#x): %s' % 82 (start_file_addr, end_file_addr, line_entry)) 83 else: 84 if start_file_addr == end_file_addr: 85 result.PutCString(' %#x: END' % 86 (start_file_addr)) 87 else: 88 result.PutCString( 89 ' %#x: %s' % 90 (start_file_addr, line_entry)) 91 if start_file_addr == end_file_addr: 92 result.PutCString("\n") 93 94 95class DumpFiles: 96 command_name = "dump-files" 97 short_description = "Dumps full paths to compile unit files and optionally all line table files." 98 usage = "usage: %prog [options] MODULE1 [MODULE2 ...]" 99 description = '''This class adds a dump-files command to the LLDB interpreter. 100 101This command will dump all compile unit file paths found for each source file 102for the binaries specified as arguments in the current target. Specify the 103--support-files or -s option to see all file paths that a compile unit uses in 104its lines tables. This is handy for troubleshooting why breakpoints aren't 105working in IDEs that specify full paths to source files when setting file and 106line breakpoints. Sometimes symlinks cause the debug info to contain the symlink 107path and an IDE will resolve the path to the actual file and use the resolved 108path when setting breakpoints. 109''' 110 def create_options(self): 111 # Pass add_help_option = False, since this keeps the command in line with lldb commands, 112 # and we wire up "help command" to work by providing the long & short help methods below. 113 self.parser = optparse.OptionParser( 114 description = self.description, 115 prog = self.command_name, 116 usage = self.usage, 117 add_help_option = False) 118 119 self.parser.add_option( 120 '-s', 121 '--support-files', 122 action = 'store_true', 123 dest = 'support_files', 124 help = 'Dumps full paths to all files used in a compile unit.', 125 default = False) 126 127 def get_short_help(self): 128 return self.short_description 129 130 def get_long_help(self): 131 return self.help_string 132 133 def __init__(self, debugger, unused): 134 self.create_options() 135 self.help_string = self.parser.format_help() 136 137 def __call__(self, debugger, command, exe_ctx, result): 138 # Use the Shell Lexer to properly parse up command options just like a 139 # shell would 140 command_args = shlex.split(command) 141 142 try: 143 (options, args) = self.parser.parse_args(command_args) 144 except: 145 # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit 146 # (courtesy of OptParse dealing with argument errors by throwing SystemExit) 147 result.SetError("option parsing failed") 148 return 149 150 # Always get program state from the SBExecutionContext passed in as exe_ctx 151 target = exe_ctx.GetTarget() 152 if not target.IsValid(): 153 result.SetError("invalid target") 154 return 155 156 if len(args) == 0: 157 result.SetError("one or more executable paths must be specified") 158 return 159 160 for module_path in args: 161 module = target.module[module_path] 162 if not module: 163 result.SetError('no module found that matches "%s".' % (module_path)) 164 return 165 num_cus = module.GetNumCompileUnits() 166 print('Module: "%s"' % (module.file.fullpath), end=' ', file=result) 167 if num_cus == 0: 168 print('no debug info.', file=result) 169 continue 170 print('has %u compile units:' % (num_cus), file=result) 171 for i in range(num_cus): 172 cu = module.GetCompileUnitAtIndex(i) 173 print(' Compile Unit: %s' % (cu.file.fullpath), file=result) 174 if options.support_files: 175 num_support_files = cu.GetNumSupportFiles() 176 for j in range(num_support_files): 177 path = cu.GetSupportFileAtIndex(j).fullpath 178 print(' file[%u]: %s' % (j, path), file=result) 179 180 181def __lldb_init_module(debugger, dict): 182 # This initializer is being run from LLDB in the embedded command interpreter 183 184 # Add any commands contained in this module to LLDB 185 debugger.HandleCommand( 186 'command script add -o -c %s.DumpLineTables %s' % (__name__, 187 DumpLineTables.command_name)) 188 debugger.HandleCommand( 189 'command script add -o -c %s.DumpFiles %s' % (__name__, DumpFiles.command_name)) 190 print('The "%s" and "%s" commands have been installed.' % (DumpLineTables.command_name, 191 DumpFiles.command_name)) 192