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 | |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 17 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 18 | def 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() |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 54 | |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 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. |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 58 | |
| 59 | |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 60 | def simple_backtrace(debugger): |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 61 | target = debugger.GetSelectedTarget() |
| 62 | process = target.GetProcess() |
| 63 | cur_thread = process.GetSelectedThread() |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 64 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 65 | initial_fp = cur_thread.GetFrameAtIndex(0).GetFP() |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 66 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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) |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 75 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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) |
Jason Molenda | 5bd4a46 | 2013-07-09 05:08:14 +0000 | [diff] [blame] | 125 | return [module_list, address_list] |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 126 | |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 127 | |
Jason Molenda | 5bd4a46 | 2013-07-09 05:08:14 +0000 | [diff] [blame] | 128 | def print_stack_frame(process, fp): |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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 | |
Jason Molenda | 5bd4a46 | 2013-07-09 05:08:14 +0000 | [diff] [blame] | 146 | |
Jason Molenda | f7305a61 | 2013-04-30 03:03:06 +0000 | [diff] [blame] | 147 | def diagnose_unwind(debugger, command, result, dict): |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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 | """ |
Jason Molenda | 811d259 | 2013-06-05 02:56:58 +0000 | [diff] [blame] | 155 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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]) |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 179 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 180 | modules_seen = [] |
| 181 | addresses_seen = [] |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 182 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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)) |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 222 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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()) |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 237 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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()) |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 262 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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) |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 275 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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()) |
Jason Molenda | 8359214 | 2013-06-19 04:52:55 +0000 | [diff] [blame] | 291 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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 | |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 300 | |
Jason Molenda | f7305a61 | 2013-04-30 03:03:06 +0000 | [diff] [blame] | 301 | def create_diagnose_unwind_options(): |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 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 |
Jason Molenda | caae381 | 2013-04-23 03:40:32 +0000 | [diff] [blame] | 309 | |
Kate Stone | b9c1b51 | 2016-09-06 20:57:50 +0000 | [diff] [blame] | 310 | lldb.debugger.HandleCommand( |
| 311 | 'command script add -f %s.diagnose_unwind diagnose-unwind' % |
| 312 | __name__) |
Jason Molenda | f7305a61 | 2013-04-30 03:03:06 +0000 | [diff] [blame] | 313 | print 'The "diagnose-unwind" command has been installed, type "help diagnose-unwind" for detailed help.' |