1# This implements the "diagnose-unwind" command, usually installed 2# in the debug session like 3# command script import lldb.diagnose 4# it is used when lldb's backtrace fails -- it collects and prints 5# information about the stack frames, and tries an alternate unwind 6# algorithm, that will help to understand why lldb's unwind algorithm 7# did not succeed. 8 9import optparse 10import lldb 11import re 12import shlex 13 14# Print the frame number, pc, frame pointer, module UUID and function name 15# Returns the SBModule that contains the PC, if it could be found 16 17 18def backtrace_print_frame(target, frame_num, addr, fp): 19 process = target.GetProcess() 20 addr_for_printing = addr 21 addr_width = process.GetAddressByteSize() * 2 22 if frame_num > 0: 23 addr = addr - 1 24 25 sbaddr = lldb.SBAddress() 26 try: 27 sbaddr.SetLoadAddress(addr, target) 28 module_description = "" 29 if sbaddr.GetModule(): 30 module_filename = "" 31 module_uuid_str = sbaddr.GetModule().GetUUIDString() 32 if module_uuid_str is None: 33 module_uuid_str = "" 34 if sbaddr.GetModule().GetFileSpec(): 35 module_filename = sbaddr.GetModule().GetFileSpec().GetFilename() 36 if module_filename is None: 37 module_filename = "" 38 if module_uuid_str != "" or module_filename != "": 39 module_description = '%s %s' % ( 40 module_filename, module_uuid_str) 41 except Exception: 42 print('%2d: pc==0x%-*x fp==0x%-*x' % (frame_num, addr_width, addr_for_printing, addr_width, fp)) 43 return 44 45 sym_ctx = target.ResolveSymbolContextForAddress( 46 sbaddr, lldb.eSymbolContextEverything) 47 if sym_ctx.IsValid() and sym_ctx.GetSymbol().IsValid(): 48 function_start = sym_ctx.GetSymbol().GetStartAddress().GetLoadAddress(target) 49 offset = addr - function_start 50 print('%2d: pc==0x%-*x fp==0x%-*x %s %s + %d' % (frame_num, addr_width, addr_for_printing, addr_width, fp, module_description, sym_ctx.GetSymbol().GetName(), offset)) 51 else: 52 print('%2d: pc==0x%-*x fp==0x%-*x %s' % (frame_num, addr_width, addr_for_printing, addr_width, fp, module_description)) 53 return sbaddr.GetModule() 54 55# A simple stack walk algorithm that follows the frame chain. 56# Returns a two-element list; the first element is a list of modules 57# seen and the second element is a list of addresses seen during the backtrace. 58 59 60def simple_backtrace(debugger): 61 target = debugger.GetSelectedTarget() 62 process = target.GetProcess() 63 cur_thread = process.GetSelectedThread() 64 65 initial_fp = cur_thread.GetFrameAtIndex(0).GetFP() 66 67 # If the pseudoreg "fp" isn't recognized, on arm hardcode to r7 which is 68 # correct for Darwin programs. 69 if initial_fp == lldb.LLDB_INVALID_ADDRESS and target.triple[0:3] == "arm": 70 for reggroup in cur_thread.GetFrameAtIndex(1).registers: 71 if reggroup.GetName() == "General Purpose Registers": 72 for reg in reggroup: 73 if reg.GetName() == "r7": 74 initial_fp = int(reg.GetValue(), 16) 75 76 module_list = [] 77 address_list = [cur_thread.GetFrameAtIndex(0).GetPC()] 78 this_module = backtrace_print_frame( 79 target, 0, cur_thread.GetFrameAtIndex(0).GetPC(), initial_fp) 80 print_stack_frame(process, initial_fp) 81 print("") 82 if this_module is not None: 83 module_list.append(this_module) 84 if cur_thread.GetNumFrames() < 2: 85 return [module_list, address_list] 86 87 cur_fp = process.ReadPointerFromMemory(initial_fp, lldb.SBError()) 88 cur_pc = process.ReadPointerFromMemory( 89 initial_fp + process.GetAddressByteSize(), lldb.SBError()) 90 91 frame_num = 1 92 93 while cur_pc != 0 and cur_fp != 0 and cur_pc != lldb.LLDB_INVALID_ADDRESS and cur_fp != lldb.LLDB_INVALID_ADDRESS: 94 address_list.append(cur_pc) 95 this_module = backtrace_print_frame(target, frame_num, cur_pc, cur_fp) 96 print_stack_frame(process, cur_fp) 97 print("") 98 if this_module is not None: 99 module_list.append(this_module) 100 frame_num = frame_num + 1 101 next_pc = 0 102 next_fp = 0 103 if target.triple[ 104 0:6] == "x86_64" or target.triple[ 105 0:4] == "i386" or target.triple[ 106 0:3] == "arm": 107 error = lldb.SBError() 108 next_pc = process.ReadPointerFromMemory( 109 cur_fp + process.GetAddressByteSize(), error) 110 if not error.Success(): 111 next_pc = 0 112 next_fp = process.ReadPointerFromMemory(cur_fp, error) 113 if not error.Success(): 114 next_fp = 0 115 # Clear the 0th bit for arm frames - this indicates it is a thumb frame 116 if target.triple[0:3] == "arm" and (next_pc & 1) == 1: 117 next_pc = next_pc & ~1 118 cur_pc = next_pc 119 cur_fp = next_fp 120 this_module = backtrace_print_frame(target, frame_num, cur_pc, cur_fp) 121 print_stack_frame(process, cur_fp) 122 print("") 123 if this_module is not None: 124 module_list.append(this_module) 125 return [module_list, address_list] 126 127 128def print_stack_frame(process, fp): 129 if fp == 0 or fp == lldb.LLDB_INVALID_ADDRESS or fp == 1: 130 return 131 addr_size = process.GetAddressByteSize() 132 addr = fp - (2 * addr_size) 133 i = 0 134 outline = "Stack frame from $fp-%d: " % (2 * addr_size) 135 error = lldb.SBError() 136 try: 137 while i < 5 and error.Success(): 138 address = process.ReadPointerFromMemory( 139 addr + (i * addr_size), error) 140 outline += " 0x%x" % address 141 i += 1 142 print(outline) 143 except Exception: 144 return 145 146 147def diagnose_unwind(debugger, command, result, dict): 148 """ 149 Gather diagnostic information to help debug incorrect unwind (backtrace) 150 behavior in lldb. When there is a backtrace that doesn't look 151 correct, run this command with the correct thread selected and a 152 large amount of diagnostic information will be printed, it is likely 153 to be helpful when reporting the problem. 154 """ 155 156 command_args = shlex.split(command) 157 parser = create_diagnose_unwind_options() 158 try: 159 (options, args) = parser.parse_args(command_args) 160 except: 161 return 162 target = debugger.GetSelectedTarget() 163 if target: 164 process = target.GetProcess() 165 if process: 166 thread = process.GetSelectedThread() 167 if thread: 168 lldb_versions_match = re.search( 169 r'[lL][lL][dD][bB]-(\d+)([.](\d+))?([.](\d+))?', 170 debugger.GetVersionString()) 171 lldb_version = 0 172 lldb_minor = 0 173 if len(lldb_versions_match.groups() 174 ) >= 1 and lldb_versions_match.groups()[0]: 175 lldb_major = int(lldb_versions_match.groups()[0]) 176 if len(lldb_versions_match.groups() 177 ) >= 5 and lldb_versions_match.groups()[4]: 178 lldb_minor = int(lldb_versions_match.groups()[4]) 179 180 modules_seen = [] 181 addresses_seen = [] 182 183 print('LLDB version %s' % debugger.GetVersionString()) 184 print('Unwind diagnostics for thread %d' % thread.GetIndexID()) 185 print("") 186 print("=============================================================================================") 187 print("") 188 print("OS plugin setting:") 189 debugger.HandleCommand( 190 "settings show target.process.python-os-plugin-path") 191 print("") 192 print("Live register context:") 193 thread.SetSelectedFrame(0) 194 debugger.HandleCommand("register read") 195 print("") 196 print("=============================================================================================") 197 print("") 198 print("lldb's unwind algorithm:") 199 print("") 200 frame_num = 0 201 for frame in thread.frames: 202 if not frame.IsInlined(): 203 this_module = backtrace_print_frame( 204 target, frame_num, frame.GetPC(), frame.GetFP()) 205 print_stack_frame(process, frame.GetFP()) 206 print("") 207 if this_module is not None: 208 modules_seen.append(this_module) 209 addresses_seen.append(frame.GetPC()) 210 frame_num = frame_num + 1 211 print("") 212 print("=============================================================================================") 213 print("") 214 print("Simple stack walk algorithm:") 215 print("") 216 (module_list, address_list) = simple_backtrace(debugger) 217 if module_list and module_list is not None: 218 modules_seen += module_list 219 if address_list and address_list is not None: 220 addresses_seen = set(addresses_seen) 221 addresses_seen.update(set(address_list)) 222 223 print("") 224 print("=============================================================================================") 225 print("") 226 print("Modules seen in stack walks:") 227 print("") 228 modules_already_seen = set() 229 for module in modules_seen: 230 if module is not None and module.GetFileSpec().GetFilename() is not None: 231 if not module.GetFileSpec().GetFilename() in modules_already_seen: 232 debugger.HandleCommand( 233 'image list %s' % 234 module.GetFileSpec().GetFilename()) 235 modules_already_seen.add( 236 module.GetFileSpec().GetFilename()) 237 238 print("") 239 print("=============================================================================================") 240 print("") 241 print("Disassembly ofaddresses seen in stack walks:") 242 print("") 243 additional_addresses_to_disassemble = addresses_seen 244 for frame in thread.frames: 245 if not frame.IsInlined(): 246 print("--------------------------------------------------------------------------------------") 247 print("") 248 print("Disassembly of %s, frame %d, address 0x%x" % (frame.GetFunctionName(), frame.GetFrameID(), frame.GetPC())) 249 print("") 250 if target.triple[ 251 0:6] == "x86_64" or target.triple[ 252 0:4] == "i386": 253 debugger.HandleCommand( 254 'disassemble -F att -a 0x%x' % frame.GetPC()) 255 else: 256 debugger.HandleCommand( 257 'disassemble -a 0x%x' % 258 frame.GetPC()) 259 if frame.GetPC() in additional_addresses_to_disassemble: 260 additional_addresses_to_disassemble.remove( 261 frame.GetPC()) 262 263 for address in list(additional_addresses_to_disassemble): 264 print("--------------------------------------------------------------------------------------") 265 print("") 266 print("Disassembly of 0x%x" % address) 267 print("") 268 if target.triple[ 269 0:6] == "x86_64" or target.triple[ 270 0:4] == "i386": 271 debugger.HandleCommand( 272 'disassemble -F att -a 0x%x' % address) 273 else: 274 debugger.HandleCommand('disassemble -a 0x%x' % address) 275 276 print("") 277 print("=============================================================================================") 278 print("") 279 additional_addresses_to_show_unwind = addresses_seen 280 for frame in thread.frames: 281 if not frame.IsInlined(): 282 print("--------------------------------------------------------------------------------------") 283 print("") 284 print("Unwind instructions for %s, frame %d" % (frame.GetFunctionName(), frame.GetFrameID())) 285 print("") 286 debugger.HandleCommand( 287 'image show-unwind -a "0x%x"' % frame.GetPC()) 288 if frame.GetPC() in additional_addresses_to_show_unwind: 289 additional_addresses_to_show_unwind.remove( 290 frame.GetPC()) 291 292 for address in list(additional_addresses_to_show_unwind): 293 print("--------------------------------------------------------------------------------------") 294 print("") 295 print("Unwind instructions for 0x%x" % address) 296 print("") 297 debugger.HandleCommand( 298 'image show-unwind -a "0x%x"' % address) 299 300 301def create_diagnose_unwind_options(): 302 usage = "usage: %prog" 303 description = '''Print diagnostic information about a thread backtrace which will help to debug unwind problems''' 304 parser = optparse.OptionParser( 305 description=description, 306 prog='diagnose_unwind', 307 usage=usage) 308 return parser 309 310def __lldb_init_module(debugger, internal_dict): 311 debugger.HandleCommand( 312 'command script add -o -f %s.diagnose_unwind diagnose-unwind' % 313 __name__) 314 print('The "diagnose-unwind" command has been installed, type "help diagnose-unwind" for detailed help.') 315