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