1061da546Spatrick# This implements the "diagnose-unwind" command, usually installed 2061da546Spatrick# in the debug session like 3061da546Spatrick# command script import lldb.diagnose 4061da546Spatrick# it is used when lldb's backtrace fails -- it collects and prints 5061da546Spatrick# information about the stack frames, and tries an alternate unwind 6061da546Spatrick# algorithm, that will help to understand why lldb's unwind algorithm 7061da546Spatrick# did not succeed. 8061da546Spatrick 9061da546Spatrickimport optparse 10061da546Spatrickimport lldb 11061da546Spatrickimport re 12061da546Spatrickimport shlex 13061da546Spatrick 14061da546Spatrick# Print the frame number, pc, frame pointer, module UUID and function name 15061da546Spatrick# Returns the SBModule that contains the PC, if it could be found 16061da546Spatrick 17061da546Spatrick 18061da546Spatrickdef backtrace_print_frame(target, frame_num, addr, fp): 19061da546Spatrick process = target.GetProcess() 20061da546Spatrick addr_for_printing = addr 21061da546Spatrick addr_width = process.GetAddressByteSize() * 2 22061da546Spatrick if frame_num > 0: 23061da546Spatrick addr = addr - 1 24061da546Spatrick 25061da546Spatrick sbaddr = lldb.SBAddress() 26061da546Spatrick try: 27061da546Spatrick sbaddr.SetLoadAddress(addr, target) 28061da546Spatrick module_description = "" 29061da546Spatrick if sbaddr.GetModule(): 30061da546Spatrick module_filename = "" 31061da546Spatrick module_uuid_str = sbaddr.GetModule().GetUUIDString() 32061da546Spatrick if module_uuid_str is None: 33061da546Spatrick module_uuid_str = "" 34061da546Spatrick if sbaddr.GetModule().GetFileSpec(): 35061da546Spatrick module_filename = sbaddr.GetModule().GetFileSpec().GetFilename() 36061da546Spatrick if module_filename is None: 37061da546Spatrick module_filename = "" 38061da546Spatrick if module_uuid_str != "" or module_filename != "": 39061da546Spatrick module_description = '%s %s' % ( 40061da546Spatrick module_filename, module_uuid_str) 41061da546Spatrick except Exception: 42061da546Spatrick print('%2d: pc==0x%-*x fp==0x%-*x' % (frame_num, addr_width, addr_for_printing, addr_width, fp)) 43061da546Spatrick return 44061da546Spatrick 45061da546Spatrick sym_ctx = target.ResolveSymbolContextForAddress( 46061da546Spatrick sbaddr, lldb.eSymbolContextEverything) 47061da546Spatrick if sym_ctx.IsValid() and sym_ctx.GetSymbol().IsValid(): 48061da546Spatrick function_start = sym_ctx.GetSymbol().GetStartAddress().GetLoadAddress(target) 49061da546Spatrick offset = addr - function_start 50061da546Spatrick 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)) 51061da546Spatrick else: 52061da546Spatrick print('%2d: pc==0x%-*x fp==0x%-*x %s' % (frame_num, addr_width, addr_for_printing, addr_width, fp, module_description)) 53061da546Spatrick return sbaddr.GetModule() 54061da546Spatrick 55061da546Spatrick# A simple stack walk algorithm that follows the frame chain. 56061da546Spatrick# Returns a two-element list; the first element is a list of modules 57061da546Spatrick# seen and the second element is a list of addresses seen during the backtrace. 58061da546Spatrick 59061da546Spatrick 60061da546Spatrickdef simple_backtrace(debugger): 61061da546Spatrick target = debugger.GetSelectedTarget() 62061da546Spatrick process = target.GetProcess() 63061da546Spatrick cur_thread = process.GetSelectedThread() 64061da546Spatrick 65061da546Spatrick initial_fp = cur_thread.GetFrameAtIndex(0).GetFP() 66061da546Spatrick 67061da546Spatrick # If the pseudoreg "fp" isn't recognized, on arm hardcode to r7 which is 68061da546Spatrick # correct for Darwin programs. 69061da546Spatrick if initial_fp == lldb.LLDB_INVALID_ADDRESS and target.triple[0:3] == "arm": 70061da546Spatrick for reggroup in cur_thread.GetFrameAtIndex(1).registers: 71061da546Spatrick if reggroup.GetName() == "General Purpose Registers": 72061da546Spatrick for reg in reggroup: 73061da546Spatrick if reg.GetName() == "r7": 74061da546Spatrick initial_fp = int(reg.GetValue(), 16) 75061da546Spatrick 76061da546Spatrick module_list = [] 77061da546Spatrick address_list = [cur_thread.GetFrameAtIndex(0).GetPC()] 78061da546Spatrick this_module = backtrace_print_frame( 79061da546Spatrick target, 0, cur_thread.GetFrameAtIndex(0).GetPC(), initial_fp) 80061da546Spatrick print_stack_frame(process, initial_fp) 81061da546Spatrick print("") 82061da546Spatrick if this_module is not None: 83061da546Spatrick module_list.append(this_module) 84061da546Spatrick if cur_thread.GetNumFrames() < 2: 85061da546Spatrick return [module_list, address_list] 86061da546Spatrick 87061da546Spatrick cur_fp = process.ReadPointerFromMemory(initial_fp, lldb.SBError()) 88061da546Spatrick cur_pc = process.ReadPointerFromMemory( 89061da546Spatrick initial_fp + process.GetAddressByteSize(), lldb.SBError()) 90061da546Spatrick 91061da546Spatrick frame_num = 1 92061da546Spatrick 93061da546Spatrick while cur_pc != 0 and cur_fp != 0 and cur_pc != lldb.LLDB_INVALID_ADDRESS and cur_fp != lldb.LLDB_INVALID_ADDRESS: 94061da546Spatrick address_list.append(cur_pc) 95061da546Spatrick this_module = backtrace_print_frame(target, frame_num, cur_pc, cur_fp) 96061da546Spatrick print_stack_frame(process, cur_fp) 97061da546Spatrick print("") 98061da546Spatrick if this_module is not None: 99061da546Spatrick module_list.append(this_module) 100061da546Spatrick frame_num = frame_num + 1 101061da546Spatrick next_pc = 0 102061da546Spatrick next_fp = 0 103061da546Spatrick if target.triple[ 104061da546Spatrick 0:6] == "x86_64" or target.triple[ 105061da546Spatrick 0:4] == "i386" or target.triple[ 106061da546Spatrick 0:3] == "arm": 107061da546Spatrick error = lldb.SBError() 108061da546Spatrick next_pc = process.ReadPointerFromMemory( 109061da546Spatrick cur_fp + process.GetAddressByteSize(), error) 110061da546Spatrick if not error.Success(): 111061da546Spatrick next_pc = 0 112061da546Spatrick next_fp = process.ReadPointerFromMemory(cur_fp, error) 113061da546Spatrick if not error.Success(): 114061da546Spatrick next_fp = 0 115061da546Spatrick # Clear the 0th bit for arm frames - this indicates it is a thumb frame 116061da546Spatrick if target.triple[0:3] == "arm" and (next_pc & 1) == 1: 117061da546Spatrick next_pc = next_pc & ~1 118061da546Spatrick cur_pc = next_pc 119061da546Spatrick cur_fp = next_fp 120061da546Spatrick this_module = backtrace_print_frame(target, frame_num, cur_pc, cur_fp) 121061da546Spatrick print_stack_frame(process, cur_fp) 122061da546Spatrick print("") 123061da546Spatrick if this_module is not None: 124061da546Spatrick module_list.append(this_module) 125061da546Spatrick return [module_list, address_list] 126061da546Spatrick 127061da546Spatrick 128061da546Spatrickdef print_stack_frame(process, fp): 129061da546Spatrick if fp == 0 or fp == lldb.LLDB_INVALID_ADDRESS or fp == 1: 130061da546Spatrick return 131061da546Spatrick addr_size = process.GetAddressByteSize() 132061da546Spatrick addr = fp - (2 * addr_size) 133061da546Spatrick i = 0 134061da546Spatrick outline = "Stack frame from $fp-%d: " % (2 * addr_size) 135061da546Spatrick error = lldb.SBError() 136061da546Spatrick try: 137061da546Spatrick while i < 5 and error.Success(): 138061da546Spatrick address = process.ReadPointerFromMemory( 139061da546Spatrick addr + (i * addr_size), error) 140061da546Spatrick outline += " 0x%x" % address 141061da546Spatrick i += 1 142061da546Spatrick print(outline) 143061da546Spatrick except Exception: 144061da546Spatrick return 145061da546Spatrick 146061da546Spatrick 147061da546Spatrickdef diagnose_unwind(debugger, command, result, dict): 148061da546Spatrick """ 149061da546Spatrick Gather diagnostic information to help debug incorrect unwind (backtrace) 150061da546Spatrick behavior in lldb. When there is a backtrace that doesn't look 151061da546Spatrick correct, run this command with the correct thread selected and a 152061da546Spatrick large amount of diagnostic information will be printed, it is likely 153061da546Spatrick to be helpful when reporting the problem. 154061da546Spatrick """ 155061da546Spatrick 156061da546Spatrick command_args = shlex.split(command) 157061da546Spatrick parser = create_diagnose_unwind_options() 158061da546Spatrick try: 159061da546Spatrick (options, args) = parser.parse_args(command_args) 160061da546Spatrick except: 161061da546Spatrick return 162061da546Spatrick target = debugger.GetSelectedTarget() 163061da546Spatrick if target: 164061da546Spatrick process = target.GetProcess() 165061da546Spatrick if process: 166061da546Spatrick thread = process.GetSelectedThread() 167061da546Spatrick if thread: 168061da546Spatrick lldb_versions_match = re.search( 169061da546Spatrick r'[lL][lL][dD][bB]-(\d+)([.](\d+))?([.](\d+))?', 170061da546Spatrick debugger.GetVersionString()) 171061da546Spatrick lldb_version = 0 172061da546Spatrick lldb_minor = 0 173061da546Spatrick if len(lldb_versions_match.groups() 174061da546Spatrick ) >= 1 and lldb_versions_match.groups()[0]: 175061da546Spatrick lldb_major = int(lldb_versions_match.groups()[0]) 176061da546Spatrick if len(lldb_versions_match.groups() 177061da546Spatrick ) >= 5 and lldb_versions_match.groups()[4]: 178061da546Spatrick lldb_minor = int(lldb_versions_match.groups()[4]) 179061da546Spatrick 180061da546Spatrick modules_seen = [] 181061da546Spatrick addresses_seen = [] 182061da546Spatrick 183061da546Spatrick print('LLDB version %s' % debugger.GetVersionString()) 184061da546Spatrick print('Unwind diagnostics for thread %d' % thread.GetIndexID()) 185061da546Spatrick print("") 186061da546Spatrick print("=============================================================================================") 187061da546Spatrick print("") 188061da546Spatrick print("OS plugin setting:") 189061da546Spatrick debugger.HandleCommand( 190061da546Spatrick "settings show target.process.python-os-plugin-path") 191061da546Spatrick print("") 192061da546Spatrick print("Live register context:") 193061da546Spatrick thread.SetSelectedFrame(0) 194061da546Spatrick debugger.HandleCommand("register read") 195061da546Spatrick print("") 196061da546Spatrick print("=============================================================================================") 197061da546Spatrick print("") 198061da546Spatrick print("lldb's unwind algorithm:") 199061da546Spatrick print("") 200061da546Spatrick frame_num = 0 201061da546Spatrick for frame in thread.frames: 202061da546Spatrick if not frame.IsInlined(): 203061da546Spatrick this_module = backtrace_print_frame( 204061da546Spatrick target, frame_num, frame.GetPC(), frame.GetFP()) 205061da546Spatrick print_stack_frame(process, frame.GetFP()) 206061da546Spatrick print("") 207061da546Spatrick if this_module is not None: 208061da546Spatrick modules_seen.append(this_module) 209061da546Spatrick addresses_seen.append(frame.GetPC()) 210061da546Spatrick frame_num = frame_num + 1 211061da546Spatrick print("") 212061da546Spatrick print("=============================================================================================") 213061da546Spatrick print("") 214061da546Spatrick print("Simple stack walk algorithm:") 215061da546Spatrick print("") 216061da546Spatrick (module_list, address_list) = simple_backtrace(debugger) 217061da546Spatrick if module_list and module_list is not None: 218061da546Spatrick modules_seen += module_list 219061da546Spatrick if address_list and address_list is not None: 220061da546Spatrick addresses_seen = set(addresses_seen) 221061da546Spatrick addresses_seen.update(set(address_list)) 222061da546Spatrick 223061da546Spatrick print("") 224061da546Spatrick print("=============================================================================================") 225061da546Spatrick print("") 226061da546Spatrick print("Modules seen in stack walks:") 227061da546Spatrick print("") 228061da546Spatrick modules_already_seen = set() 229061da546Spatrick for module in modules_seen: 230061da546Spatrick if module is not None and module.GetFileSpec().GetFilename() is not None: 231061da546Spatrick if not module.GetFileSpec().GetFilename() in modules_already_seen: 232061da546Spatrick debugger.HandleCommand( 233061da546Spatrick 'image list %s' % 234061da546Spatrick module.GetFileSpec().GetFilename()) 235061da546Spatrick modules_already_seen.add( 236061da546Spatrick module.GetFileSpec().GetFilename()) 237061da546Spatrick 238061da546Spatrick print("") 239061da546Spatrick print("=============================================================================================") 240061da546Spatrick print("") 241061da546Spatrick print("Disassembly ofaddresses seen in stack walks:") 242061da546Spatrick print("") 243061da546Spatrick additional_addresses_to_disassemble = addresses_seen 244061da546Spatrick for frame in thread.frames: 245061da546Spatrick if not frame.IsInlined(): 246061da546Spatrick print("--------------------------------------------------------------------------------------") 247061da546Spatrick print("") 248061da546Spatrick print("Disassembly of %s, frame %d, address 0x%x" % (frame.GetFunctionName(), frame.GetFrameID(), frame.GetPC())) 249061da546Spatrick print("") 250061da546Spatrick if target.triple[ 251061da546Spatrick 0:6] == "x86_64" or target.triple[ 252061da546Spatrick 0:4] == "i386": 253061da546Spatrick debugger.HandleCommand( 254061da546Spatrick 'disassemble -F att -a 0x%x' % frame.GetPC()) 255061da546Spatrick else: 256061da546Spatrick debugger.HandleCommand( 257061da546Spatrick 'disassemble -a 0x%x' % 258061da546Spatrick frame.GetPC()) 259061da546Spatrick if frame.GetPC() in additional_addresses_to_disassemble: 260061da546Spatrick additional_addresses_to_disassemble.remove( 261061da546Spatrick frame.GetPC()) 262061da546Spatrick 263061da546Spatrick for address in list(additional_addresses_to_disassemble): 264061da546Spatrick print("--------------------------------------------------------------------------------------") 265061da546Spatrick print("") 266061da546Spatrick print("Disassembly of 0x%x" % address) 267061da546Spatrick print("") 268061da546Spatrick if target.triple[ 269061da546Spatrick 0:6] == "x86_64" or target.triple[ 270061da546Spatrick 0:4] == "i386": 271061da546Spatrick debugger.HandleCommand( 272061da546Spatrick 'disassemble -F att -a 0x%x' % address) 273061da546Spatrick else: 274061da546Spatrick debugger.HandleCommand('disassemble -a 0x%x' % address) 275061da546Spatrick 276061da546Spatrick print("") 277061da546Spatrick print("=============================================================================================") 278061da546Spatrick print("") 279061da546Spatrick additional_addresses_to_show_unwind = addresses_seen 280061da546Spatrick for frame in thread.frames: 281061da546Spatrick if not frame.IsInlined(): 282061da546Spatrick print("--------------------------------------------------------------------------------------") 283061da546Spatrick print("") 284061da546Spatrick print("Unwind instructions for %s, frame %d" % (frame.GetFunctionName(), frame.GetFrameID())) 285061da546Spatrick print("") 286061da546Spatrick debugger.HandleCommand( 287061da546Spatrick 'image show-unwind -a "0x%x"' % frame.GetPC()) 288061da546Spatrick if frame.GetPC() in additional_addresses_to_show_unwind: 289061da546Spatrick additional_addresses_to_show_unwind.remove( 290061da546Spatrick frame.GetPC()) 291061da546Spatrick 292061da546Spatrick for address in list(additional_addresses_to_show_unwind): 293061da546Spatrick print("--------------------------------------------------------------------------------------") 294061da546Spatrick print("") 295061da546Spatrick print("Unwind instructions for 0x%x" % address) 296061da546Spatrick print("") 297061da546Spatrick debugger.HandleCommand( 298061da546Spatrick 'image show-unwind -a "0x%x"' % address) 299061da546Spatrick 300061da546Spatrick 301061da546Spatrickdef create_diagnose_unwind_options(): 302061da546Spatrick usage = "usage: %prog" 303061da546Spatrick description = '''Print diagnostic information about a thread backtrace which will help to debug unwind problems''' 304061da546Spatrick parser = optparse.OptionParser( 305061da546Spatrick description=description, 306061da546Spatrick prog='diagnose_unwind', 307061da546Spatrick usage=usage) 308061da546Spatrick return parser 309061da546Spatrick 310*f6aab3d8Srobertdef __lldb_init_module(debugger, internal_dict): 311*f6aab3d8Srobert debugger.HandleCommand( 312*f6aab3d8Srobert 'command script add -o -f %s.diagnose_unwind diagnose-unwind' % 313061da546Spatrick __name__) 314061da546Spatrick print('The "diagnose-unwind" command has been installed, type "help diagnose-unwind" for detailed help.') 315