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: |
Jason Molenda | 5bd4a46 | 2013-07-09 05:08:14 +0000 | [diff] [blame^] | 75 | return [module_list, address_list] |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 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) |
Jason Molenda | 5bd4a46 | 2013-07-09 05:08:14 +0000 | [diff] [blame^] | 85 | print_stack_frame (process, cur_fp) |
| 86 | print "" |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 87 | if this_module != None: |
| 88 | module_list.append (this_module) |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 89 | frame_num = frame_num + 1 |
| 90 | next_pc = 0 |
| 91 | next_fp = 0 |
| 92 | if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386" or target.triple[0:3] == "arm": |
| 93 | error = lldb.SBError() |
| 94 | next_pc = process.ReadPointerFromMemory(cur_fp + process.GetAddressByteSize(), error) |
| 95 | if not error.Success(): |
| 96 | next_pc = 0 |
| 97 | next_fp = process.ReadPointerFromMemory(cur_fp, error) |
| 98 | if not error.Success(): |
| 99 | next_fp = 0 |
| 100 | # Clear the 0th bit for arm frames - this indicates it is a thumb frame |
| 101 | if target.triple[0:3] == "arm" and (next_pc & 1) == 1: |
| 102 | next_pc = next_pc & ~1 |
| 103 | cur_pc = next_pc |
| 104 | cur_fp = next_fp |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 105 | this_module = backtrace_print_frame (target, frame_num, cur_pc, cur_fp) |
Jason Molenda | 5bd4a46 | 2013-07-09 05:08:14 +0000 | [diff] [blame^] | 106 | print_stack_frame (process, cur_fp) |
| 107 | print "" |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 108 | if this_module != None: |
| 109 | module_list.append (this_module) |
| 110 | return [module_list, address_list] |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 111 | |
Jason Molenda | 5bd4a46 | 2013-07-09 05:08:14 +0000 | [diff] [blame^] | 112 | def print_stack_frame(process, fp): |
| 113 | if fp == 0 or fp == lldb.LLDB_INVALID_ADDRESS or fp == 1: |
| 114 | return |
| 115 | addr_size = process.GetAddressByteSize() |
| 116 | addr = fp - (2 * addr_size) |
| 117 | i = 0 |
| 118 | outline = "Stack frame from $fp-%d: " % (2 * addr_size) |
| 119 | error = lldb.SBError() |
| 120 | try: |
| 121 | while i < 5 and error.Success(): |
| 122 | address = process.ReadPointerFromMemory(addr + (i * addr_size), error) |
| 123 | outline += " 0x%x" % address |
| 124 | i += 1 |
| 125 | print outline |
| 126 | except Exception: |
| 127 | return |
| 128 | |
Jason Molenda | f7305a61 | 2013-04-30 03:03:06 +0000 | [diff] [blame] | 129 | def diagnose_unwind(debugger, command, result, dict): |
Jason Molenda | 811d259 | 2013-06-05 02:56:58 +0000 | [diff] [blame] | 130 | """ |
| 131 | Gather diagnostic information to help debug incorrect unwind (backtrace) |
| 132 | behavior in lldb. When there is a backtrace that doesn't look |
| 133 | correct, run this command with the correct thread selected and a |
| 134 | large amount of diagnostic information will be printed, it is likely |
| 135 | to be helpful when reporting the problem. |
| 136 | """ |
| 137 | |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 138 | command_args = shlex.split(command) |
Jason Molenda | f7305a61 | 2013-04-30 03:03:06 +0000 | [diff] [blame] | 139 | parser = create_diagnose_unwind_options() |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 140 | try: |
| 141 | (options, args) = parser.parse_args(command_args) |
| 142 | except: |
| 143 | return |
| 144 | target = debugger.GetSelectedTarget() |
| 145 | if target: |
| 146 | process = target.GetProcess() |
| 147 | if process: |
| 148 | thread = process.GetSelectedThread() |
| 149 | if thread: |
Jason Molenda | 0f224d2 | 2013-04-23 09:38:10 +0000 | [diff] [blame] | 150 | 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] | 151 | lldb_version = 0 |
| 152 | lldb_minor = 0 |
Jason Molenda | 0f224d2 | 2013-04-23 09:38:10 +0000 | [diff] [blame] | 153 | if len(lldb_versions_match.groups()) >= 1 and lldb_versions_match.groups()[0]: |
| 154 | lldb_major = int(lldb_versions_match.groups()[0]) |
| 155 | if len(lldb_versions_match.groups()) >= 5 and lldb_versions_match.groups()[4]: |
| 156 | lldb_minor = int(lldb_versions_match.groups()[4]) |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 157 | |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 158 | modules_seen = [] |
| 159 | addresses_seen = [] |
| 160 | |
Jason Molenda | 811d259 | 2013-06-05 02:56:58 +0000 | [diff] [blame] | 161 | print 'LLDB version %s' % debugger.GetVersionString() |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 162 | print 'Unwind diagnostics for thread %d' % thread.GetIndexID() |
| 163 | print "" |
Jason Molenda | 1332988 | 2013-06-20 21:57:34 +0000 | [diff] [blame] | 164 | print "=============================================================================================" |
| 165 | print "" |
Jason Molenda | e445f8f | 2013-06-20 22:05:35 +0000 | [diff] [blame] | 166 | print "OS plugin setting:" |
| 167 | debugger.HandleCommand("settings show target.process.python-os-plugin-path") |
| 168 | print "" |
Jason Molenda | 1332988 | 2013-06-20 21:57:34 +0000 | [diff] [blame] | 169 | print "Live register context:" |
| 170 | thread.SetSelectedFrame(0) |
| 171 | debugger.HandleCommand("register read") |
| 172 | print "" |
| 173 | print "=============================================================================================" |
| 174 | print "" |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 175 | print "lldb's unwind algorithm:" |
| 176 | print "" |
| 177 | frame_num = 0 |
| 178 | for frame in thread.frames: |
| 179 | if not frame.IsInlined(): |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 180 | this_module = backtrace_print_frame (target, frame_num, frame.GetPC(), frame.GetFP()) |
Jason Molenda | 5bd4a46 | 2013-07-09 05:08:14 +0000 | [diff] [blame^] | 181 | print_stack_frame (process, frame.GetFP()) |
| 182 | print "" |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 183 | if this_module != None: |
| 184 | modules_seen.append (this_module) |
| 185 | addresses_seen.append (frame.GetPC()) |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 186 | frame_num = frame_num + 1 |
| 187 | print "" |
| 188 | print "=============================================================================================" |
| 189 | print "" |
| 190 | print "Simple stack walk algorithm:" |
| 191 | print "" |
Jason Molenda | 5bd4a46 | 2013-07-09 05:08:14 +0000 | [diff] [blame^] | 192 | (module_list, address_list) = simple_backtrace(debugger) |
| 193 | if module_list and module_list != None: |
| 194 | modules_seen += module_list |
| 195 | if address_list and address_list != None: |
| 196 | addresses_seen = set(addresses_seen) |
| 197 | addresses_seen.update(set(address_list)) |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 198 | |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 199 | print "" |
| 200 | print "=============================================================================================" |
| 201 | print "" |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 202 | print "Modules seen in stack walks:" |
| 203 | print "" |
| 204 | modules_already_seen = set() |
| 205 | for module in modules_seen: |
| 206 | if module != None and module.GetFileSpec().GetFilename() != None: |
| 207 | if not module.GetFileSpec().GetFilename() in modules_already_seen: |
| 208 | debugger.HandleCommand('image list %s' % module.GetFileSpec().GetFilename()) |
| 209 | modules_already_seen.add(module.GetFileSpec().GetFilename()) |
| 210 | |
| 211 | print "" |
| 212 | print "=============================================================================================" |
| 213 | print "" |
| 214 | print "Disassembly ofaddresses seen in stack walks:" |
| 215 | print "" |
| 216 | additional_addresses_to_disassemble = addresses_seen |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 217 | for frame in thread.frames: |
| 218 | if not frame.IsInlined(): |
| 219 | print "--------------------------------------------------------------------------------------" |
| 220 | print "" |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 221 | 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] | 222 | print "" |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 223 | if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386": |
| 224 | debugger.HandleCommand('disassemble -F att -a 0x%x' % frame.GetPC()) |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 225 | else: |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 226 | debugger.HandleCommand('disassemble -a 0x%x' % frame.GetPC()) |
| 227 | if frame.GetPC() in additional_addresses_to_disassemble: |
| 228 | additional_addresses_to_disassemble.remove (frame.GetPC()) |
| 229 | |
| 230 | for address in list(additional_addresses_to_disassemble): |
| 231 | print "--------------------------------------------------------------------------------------" |
| 232 | print "" |
| 233 | print "Disassembly of 0x%x" % address |
| 234 | print "" |
| 235 | if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386": |
| 236 | debugger.HandleCommand('disassemble -F att -a 0x%x' % address) |
| 237 | else: |
| 238 | debugger.HandleCommand('disassemble -a 0x%x' % address) |
| 239 | |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 240 | print "" |
| 241 | print "=============================================================================================" |
| 242 | print "" |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 243 | additional_addresses_to_show_unwind = addresses_seen |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 244 | for frame in thread.frames: |
| 245 | if not frame.IsInlined(): |
| 246 | print "--------------------------------------------------------------------------------------" |
| 247 | print "" |
| 248 | print "Unwind instructions for %s, frame %d" % (frame.GetFunctionName(), frame.GetFrameID()) |
| 249 | print "" |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 250 | debugger.HandleCommand('image show-unwind -a "0x%x"' % frame.GetPC()) |
| 251 | if frame.GetPC() in additional_addresses_to_show_unwind: |
| 252 | additional_addresses_to_show_unwind.remove (frame.GetPC()) |
| 253 | |
| 254 | for address in list(additional_addresses_to_show_unwind): |
| 255 | print "--------------------------------------------------------------------------------------" |
| 256 | print "" |
| 257 | print "Unwind instructions for 0x%x" % address |
| 258 | print "" |
| 259 | debugger.HandleCommand('image show-unwind -a "0x%x"' % address) |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 260 | |
Jason Molenda | f7305a61 | 2013-04-30 03:03:06 +0000 | [diff] [blame] | 261 | def create_diagnose_unwind_options(): |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 262 | usage = "usage: %prog" |
| 263 | 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] | 264 | parser = optparse.OptionParser(description=description, prog='diagnose_unwind',usage=usage) |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 265 | return parser |
| 266 | |
Jason Molenda | f7305a61 | 2013-04-30 03:03:06 +0000 | [diff] [blame] | 267 | lldb.debugger.HandleCommand('command script add -f %s.diagnose_unwind diagnose-unwind' % __name__) |
| 268 | print 'The "diagnose-unwind" command has been installed, type "help diagnose-unwind" for detailed help.' |