blob: 4dc20a58a5b04e527cc78725a75d8fe57d91883e [file] [log] [blame]
Jason Molendaf7305a612013-04-30 03:03:06 +00001# This implements the "diagnose-unwind" command, usually installed in the debug session like
Jason Molendacaae3812013-04-23 03:40:32 +00002# script import lldb.macosx
3# it is used when lldb's backtrace fails -- it collects and prints information about the stack frames,
4# and tries an alternate unwind algorithm, that will help to understand why lldb's unwind algorithm did
5# not succeed.
6
Jason Molendacaae3812013-04-23 03:40:32 +00007import optparse
Jason Molenda0f224d22013-04-23 09:38:10 +00008import lldb
Jason Molendacaae3812013-04-23 03:40:32 +00009import re
10import shlex
Jason Molendacaae3812013-04-23 03:40:32 +000011
12# Print the frame number, pc, frame pointer, module UUID and function name
13def backtrace_print_frame (target, frame_num, addr, fp):
14 process = target.GetProcess()
15 addr_for_printing = addr
16 if frame_num > 0:
17 addr = addr - 1
18
19 sbaddr = lldb.SBAddress()
Jason Molenda14c84cd2013-04-30 23:03:56 +000020 try:
21 sbaddr.SetLoadAddress(addr, target)
22 module_description = ""
23 if sbaddr.GetModule():
24 module_filename = ""
25 module_uuid_str = sbaddr.GetModule().GetUUIDString()
26 if module_uuid_str == None:
27 module_uuid_str = ""
28 if sbaddr.GetModule().GetFileSpec():
29 module_filename = sbaddr.GetModule().GetFileSpec().GetFilename()
30 if module_filename == None:
31 module_filename = ""
32 if module_uuid_str != "" or module_filename != "":
33 module_description = '%s %s' % (module_filename, module_uuid_str)
34 except Exception:
35 return
Jason Molendacaae3812013-04-23 03:40:32 +000036
37 addr_width = process.GetAddressByteSize() * 2
38 sym_ctx = target.ResolveSymbolContextForAddress(sbaddr, lldb.eSymbolContextEverything)
39 if sym_ctx.IsValid() and sym_ctx.GetSymbol().IsValid():
40 function_start = sym_ctx.GetSymbol().GetStartAddress().GetLoadAddress(target)
41 offset = addr - function_start
42 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)
43 else:
44 print '%2d: pc==0x%-*x fp==0x%-*x %s' % (frame_num, addr_width, addr_for_printing, addr_width, fp, module_description)
45
46# A simple stack walk algorithm that follows the frame chain after the first two frames.
47def simple_backtrace(debugger):
48 target = debugger.GetSelectedTarget()
49 process = target.GetProcess()
50 cur_thread = process.GetSelectedThread()
51
52 backtrace_print_frame (target, 0, cur_thread.GetFrameAtIndex(0).GetPC(), cur_thread.GetFrameAtIndex(0).GetFP())
53 if cur_thread.GetNumFrames() < 2:
54 return
55
56 cur_fp = cur_thread.GetFrameAtIndex(1).GetFP()
57 cur_pc = cur_thread.GetFrameAtIndex(1).GetPC()
58
59 # If the pseudoreg "fp" isn't recognized, on arm hardcode to r7 which is correct for Darwin programs.
60 if cur_fp == lldb.LLDB_INVALID_ADDRESS and target.triple[0:3] == "arm":
61 for reggroup in cur_thread.GetFrameAtIndex(1).registers:
62 if reggroup.GetName() == "General Purpose Registers":
63 for reg in reggroup:
64 if reg.GetName() == "r7":
65 cur_fp = int (reg.GetValue(), 16)
66
67 frame_num = 1
68
69 while cur_pc != 0 and cur_fp != 0 and cur_pc != lldb.LLDB_INVALID_ADDRESS and cur_fp != lldb.LLDB_INVALID_ADDRESS:
70 backtrace_print_frame (target, frame_num, cur_pc, cur_fp)
71 frame_num = frame_num + 1
72 next_pc = 0
73 next_fp = 0
74 if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386" or target.triple[0:3] == "arm":
75 error = lldb.SBError()
76 next_pc = process.ReadPointerFromMemory(cur_fp + process.GetAddressByteSize(), error)
77 if not error.Success():
78 next_pc = 0
79 next_fp = process.ReadPointerFromMemory(cur_fp, error)
80 if not error.Success():
81 next_fp = 0
82 # Clear the 0th bit for arm frames - this indicates it is a thumb frame
83 if target.triple[0:3] == "arm" and (next_pc & 1) == 1:
84 next_pc = next_pc & ~1
85 cur_pc = next_pc
86 cur_fp = next_fp
87 backtrace_print_frame (target, frame_num, cur_pc, cur_fp)
88
Jason Molendaf7305a612013-04-30 03:03:06 +000089def diagnose_unwind(debugger, command, result, dict):
Jason Molendacaae3812013-04-23 03:40:32 +000090 # Use the Shell Lexer to properly parse up command options just like a
91 # shell would
92 command_args = shlex.split(command)
Jason Molendaf7305a612013-04-30 03:03:06 +000093 parser = create_diagnose_unwind_options()
Jason Molendacaae3812013-04-23 03:40:32 +000094 try:
95 (options, args) = parser.parse_args(command_args)
96 except:
97 return
98 target = debugger.GetSelectedTarget()
99 if target:
100 process = target.GetProcess()
101 if process:
102 thread = process.GetSelectedThread()
103 if thread:
Jason Molenda0f224d22013-04-23 09:38:10 +0000104 lldb_versions_match = re.search(r'[lL][lL][dD][bB]-(\d+)([.](\d+))?([.](\d+))?', debugger.GetVersionString())
Jason Molendacaae3812013-04-23 03:40:32 +0000105 lldb_version = 0
106 lldb_minor = 0
Jason Molenda0f224d22013-04-23 09:38:10 +0000107 if len(lldb_versions_match.groups()) >= 1 and lldb_versions_match.groups()[0]:
108 lldb_major = int(lldb_versions_match.groups()[0])
109 if len(lldb_versions_match.groups()) >= 5 and lldb_versions_match.groups()[4]:
110 lldb_minor = int(lldb_versions_match.groups()[4])
Jason Molendacaae3812013-04-23 03:40:32 +0000111
112 print 'Unwind diagnostics for thread %d' % thread.GetIndexID()
113 print ""
114 print "lldb's unwind algorithm:"
115 print ""
116 frame_num = 0
117 for frame in thread.frames:
118 if not frame.IsInlined():
119 backtrace_print_frame (target, frame_num, frame.GetPC(), frame.GetFP())
120 frame_num = frame_num + 1
121 print ""
122 print "============================================================================================="
123 print ""
124 print "Simple stack walk algorithm:"
125 print ""
126 simple_backtrace(debugger)
127 print ""
128 print "============================================================================================="
129 print ""
130 for frame in thread.frames:
131 if not frame.IsInlined():
132 print "--------------------------------------------------------------------------------------"
133 print ""
134 print "Disassembly of %s, frame %d" % (frame.GetFunctionName(), frame.GetFrameID())
135 print ""
136 if lldb_major > 300 or (lldb_major == 300 and lldb_minor >= 18):
137 if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386":
138 debugger.HandleCommand('disassemble -F att -a 0x%x' % frame.GetPC())
139 else:
140 debugger.HandleCommand('disassemble -a 0x%x' % frame.GetPC())
141 else:
142 debugger.HandleCommand('disassemble -n "%s"' % frame.GetFunctionName())
143 print ""
144 print "============================================================================================="
145 print ""
146 for frame in thread.frames:
147 if not frame.IsInlined():
148 print "--------------------------------------------------------------------------------------"
149 print ""
150 print "Unwind instructions for %s, frame %d" % (frame.GetFunctionName(), frame.GetFrameID())
151 print ""
Jason Molenda535ab862013-04-23 04:30:57 +0000152 if lldb_major > 300 or (lldb_major == 300 and lldb_minor >= 20):
153 debugger.HandleCommand('image show-unwind -a "0x%x"' % frame.GetPC())
154 else:
155 debugger.HandleCommand('image show-unwind -n "%s"' % frame.GetFunctionName())
Jason Molendacaae3812013-04-23 03:40:32 +0000156
Jason Molendaf7305a612013-04-30 03:03:06 +0000157def create_diagnose_unwind_options():
Jason Molendacaae3812013-04-23 03:40:32 +0000158 usage = "usage: %prog"
159 description='''Print diagnostic information about a thread backtrace which will help to debug unwind problems'''
Jason Molendaf7305a612013-04-30 03:03:06 +0000160 parser = optparse.OptionParser(description=description, prog='diagnose_unwind',usage=usage)
Jason Molendacaae3812013-04-23 03:40:32 +0000161 return parser
162
Jason Molendaf7305a612013-04-30 03:03:06 +0000163lldb.debugger.HandleCommand('command script add -f %s.diagnose_unwind diagnose-unwind' % __name__)
164print 'The "diagnose-unwind" command has been installed, type "help diagnose-unwind" for detailed help.'