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