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