1 # This implements the "diagnose-unwind" command, usually installed
2 # in the debug session like
3 # command script import lldb.diagnose
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
14 # Print the frame number, pc, frame pointer, module UUID and function name
15 # Returns the SBModule that contains the PC, if it could be found
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
25 sbaddr
= lldb
.SBAddress()
27 sbaddr
.SetLoadAddress(addr
, target
)
28 module_description
= ""
29 if sbaddr
.GetModule():
31 module_uuid_str
= sbaddr
.GetModule().GetUUIDString()
32 if module_uuid_str
is None:
34 if sbaddr
.GetModule().GetFileSpec():
35 module_filename
= sbaddr
.GetModule().GetFileSpec().GetFilename()
36 if module_filename
is None:
38 if module_uuid_str
!= "" or module_filename
!= "":
39 module_description
= '%s %s' % (
40 module_filename
, module_uuid_str
)
42 print('%2d: pc==0x%-*x fp==0x%-*x' % (frame_num
, addr_width
, addr_for_printing
, addr_width
, fp
))
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
))
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()
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.
60 def simple_backtrace(debugger
):
61 target
= debugger
.GetSelectedTarget()
62 process
= target
.GetProcess()
63 cur_thread
= process
.GetSelectedThread()
65 initial_fp
= cur_thread
.GetFrameAtIndex(0).GetFP()
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":
73 if reg
.GetName() == "r7":
74 initial_fp
= int(reg
.GetValue(), 16)
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
)
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
]
87 cur_fp
= process
.ReadPointerFromMemory(initial_fp
, lldb
.SBError())
88 cur_pc
= process
.ReadPointerFromMemory(
89 initial_fp
+ process
.GetAddressByteSize(), lldb
.SBError())
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
)
98 if this_module
is not None:
99 module_list
.append(this_module
)
100 frame_num
= frame_num
+ 1
104 0:6] == "x86_64" or target
.triple
[
105 0:4] == "i386" or target
.triple
[
107 error
= lldb
.SBError()
108 next_pc
= process
.ReadPointerFromMemory(
109 cur_fp
+ process
.GetAddressByteSize(), error
)
110 if not error
.Success():
112 next_fp
= process
.ReadPointerFromMemory(cur_fp
, error
)
113 if not error
.Success():
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
120 this_module
= backtrace_print_frame(target
, frame_num
, cur_pc
, cur_fp
)
121 print_stack_frame(process
, cur_fp
)
123 if this_module
is not None:
124 module_list
.append(this_module
)
125 return [module_list
, address_list
]
128 def print_stack_frame(process
, fp
):
129 if fp
== 0 or fp
== lldb
.LLDB_INVALID_ADDRESS
or fp
== 1:
131 addr_size
= process
.GetAddressByteSize()
132 addr
= fp
- (2 * addr_size
)
134 outline
= "Stack frame from $fp-%d: " % (2 * addr_size
)
135 error
= lldb
.SBError()
137 while i
< 5 and error
.Success():
138 address
= process
.ReadPointerFromMemory(
139 addr
+ (i
* addr_size
), error
)
140 outline
+= " 0x%x" % address
147 def diagnose_unwind(debugger
, command
, result
, dict):
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.
156 command_args
= shlex
.split(command
)
157 parser
= create_diagnose_unwind_options()
159 (options
, args
) = parser
.parse_args(command_args
)
162 target
= debugger
.GetSelectedTarget()
164 process
= target
.GetProcess()
166 thread
= process
.GetSelectedThread()
168 lldb_versions_match
= re
.search(
169 r
'[lL][lL][dD][bB]-(\d+)([.](\d+))?([.](\d+))?',
170 debugger
.GetVersionString())
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])
183 print('LLDB version %s' % debugger
.GetVersionString())
184 print('Unwind diagnostics for thread %d' % thread
.GetIndexID())
186 print("=============================================================================================")
188 print("OS plugin setting:")
189 debugger
.HandleCommand(
190 "settings show target.process.python-os-plugin-path")
192 print("Live register context:")
193 thread
.SetSelectedFrame(0)
194 debugger
.HandleCommand("register read")
196 print("=============================================================================================")
198 print("lldb's unwind algorithm:")
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())
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
212 print("=============================================================================================")
214 print("Simple stack walk algorithm:")
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
))
224 print("=============================================================================================")
226 print("Modules seen in stack walks:")
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(
234 module
.GetFileSpec().GetFilename())
235 modules_already_seen
.add(
236 module
.GetFileSpec().GetFilename())
239 print("=============================================================================================")
241 print("Disassembly ofaddresses seen in stack walks:")
243 additional_addresses_to_disassemble
= addresses_seen
244 for frame
in thread
.frames
:
245 if not frame
.IsInlined():
246 print("--------------------------------------------------------------------------------------")
248 print("Disassembly of %s, frame %d, address 0x%x" % (frame
.GetFunctionName(), frame
.GetFrameID(), frame
.GetPC()))
251 0:6] == "x86_64" or target
.triple
[
253 debugger
.HandleCommand(
254 'disassemble -F att -a 0x%x' % frame
.GetPC())
256 debugger
.HandleCommand(
257 'disassemble -a 0x%x' %
259 if frame
.GetPC() in additional_addresses_to_disassemble
:
260 additional_addresses_to_disassemble
.remove(
263 for address
in list(additional_addresses_to_disassemble
):
264 print("--------------------------------------------------------------------------------------")
266 print("Disassembly of 0x%x" % address
)
269 0:6] == "x86_64" or target
.triple
[
271 debugger
.HandleCommand(
272 'disassemble -F att -a 0x%x' % address
)
274 debugger
.HandleCommand('disassemble -a 0x%x' % address
)
277 print("=============================================================================================")
279 additional_addresses_to_show_unwind
= addresses_seen
280 for frame
in thread
.frames
:
281 if not frame
.IsInlined():
282 print("--------------------------------------------------------------------------------------")
284 print("Unwind instructions for %s, frame %d" % (frame
.GetFunctionName(), frame
.GetFrameID()))
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(
292 for address
in list(additional_addresses_to_show_unwind
):
293 print("--------------------------------------------------------------------------------------")
295 print("Unwind instructions for 0x%x" % address
)
297 debugger
.HandleCommand(
298 'image show-unwind -a "0x%x"' % address
)
301 def create_diagnose_unwind_options():
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',
310 def __lldb_init_module(debugger
, internal_dict
):
311 debugger
.HandleCommand(
312 'command script add -o -f %s.diagnose_unwind diagnose-unwind' %
314 print('The "diagnose-unwind" command has been installed, type "help diagnose-unwind" for detailed help.')