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" % (module_filename
, module_uuid_str
)
42 "%2d: pc==0x%-*x fp==0x%-*x"
43 % (frame_num
, addr_width
, addr_for_printing
, addr_width
, fp
)
47 sym_ctx
= target
.ResolveSymbolContextForAddress(
48 sbaddr
, lldb
.eSymbolContextEverything
50 if sym_ctx
.IsValid() and sym_ctx
.GetSymbol().IsValid():
51 function_start
= sym_ctx
.GetSymbol().GetStartAddress().GetLoadAddress(target
)
52 offset
= addr
- function_start
54 "%2d: pc==0x%-*x fp==0x%-*x %s %s + %d"
62 sym_ctx
.GetSymbol().GetName(),
68 "%2d: pc==0x%-*x fp==0x%-*x %s"
78 return sbaddr
.GetModule()
81 # A simple stack walk algorithm that follows the frame chain.
82 # Returns a two-element list; the first element is a list of modules
83 # seen and the second element is a list of addresses seen during the backtrace.
86 def simple_backtrace(debugger
):
87 target
= debugger
.GetSelectedTarget()
88 process
= target
.GetProcess()
89 cur_thread
= process
.GetSelectedThread()
91 initial_fp
= cur_thread
.GetFrameAtIndex(0).GetFP()
93 # If the pseudoreg "fp" isn't recognized, on arm hardcode to r7 which is
94 # correct for Darwin programs.
95 if initial_fp
== lldb
.LLDB_INVALID_ADDRESS
and target
.triple
[0:3] == "arm":
96 for reggroup
in cur_thread
.GetFrameAtIndex(1).registers
:
97 if reggroup
.GetName() == "General Purpose Registers":
99 if reg
.GetName() == "r7":
100 initial_fp
= int(reg
.GetValue(), 16)
103 address_list
= [cur_thread
.GetFrameAtIndex(0).GetPC()]
104 this_module
= backtrace_print_frame(
105 target
, 0, cur_thread
.GetFrameAtIndex(0).GetPC(), initial_fp
107 print_stack_frame(process
, initial_fp
)
109 if this_module
is not None:
110 module_list
.append(this_module
)
111 if cur_thread
.GetNumFrames() < 2:
112 return [module_list
, address_list
]
114 cur_fp
= process
.ReadPointerFromMemory(initial_fp
, lldb
.SBError())
115 cur_pc
= process
.ReadPointerFromMemory(
116 initial_fp
+ process
.GetAddressByteSize(), lldb
.SBError()
124 and cur_pc
!= lldb
.LLDB_INVALID_ADDRESS
125 and cur_fp
!= lldb
.LLDB_INVALID_ADDRESS
127 address_list
.append(cur_pc
)
128 this_module
= backtrace_print_frame(target
, frame_num
, cur_pc
, cur_fp
)
129 print_stack_frame(process
, cur_fp
)
131 if this_module
is not None:
132 module_list
.append(this_module
)
133 frame_num
= frame_num
+ 1
137 target
.triple
[0:6] == "x86_64"
138 or target
.triple
[0:4] == "i386"
139 or target
.triple
[0:3] == "arm"
141 error
= lldb
.SBError()
142 next_pc
= process
.ReadPointerFromMemory(
143 cur_fp
+ process
.GetAddressByteSize(), error
145 if not error
.Success():
147 next_fp
= process
.ReadPointerFromMemory(cur_fp
, error
)
148 if not error
.Success():
150 # Clear the 0th bit for arm frames - this indicates it is a thumb frame
151 if target
.triple
[0:3] == "arm" and (next_pc
& 1) == 1:
152 next_pc
= next_pc
& ~
1
155 this_module
= backtrace_print_frame(target
, frame_num
, cur_pc
, cur_fp
)
156 print_stack_frame(process
, cur_fp
)
158 if this_module
is not None:
159 module_list
.append(this_module
)
160 return [module_list
, address_list
]
163 def print_stack_frame(process
, fp
):
164 if fp
== 0 or fp
== lldb
.LLDB_INVALID_ADDRESS
or fp
== 1:
166 addr_size
= process
.GetAddressByteSize()
167 addr
= fp
- (2 * addr_size
)
169 outline
= "Stack frame from $fp-%d: " % (2 * addr_size
)
170 error
= lldb
.SBError()
172 while i
< 5 and error
.Success():
173 address
= process
.ReadPointerFromMemory(addr
+ (i
* addr_size
), error
)
174 outline
+= " 0x%x" % address
181 def diagnose_unwind(debugger
, command
, result
, dict):
183 Gather diagnostic information to help debug incorrect unwind (backtrace)
184 behavior in lldb. When there is a backtrace that doesn't look
185 correct, run this command with the correct thread selected and a
186 large amount of diagnostic information will be printed, it is likely
187 to be helpful when reporting the problem.
190 command_args
= shlex
.split(command
)
191 parser
= create_diagnose_unwind_options()
193 (options
, args
) = parser
.parse_args(command_args
)
196 target
= debugger
.GetSelectedTarget()
198 process
= target
.GetProcess()
200 thread
= process
.GetSelectedThread()
202 lldb_versions_match
= re
.search(
203 r
"[lL][lL][dD][bB]-(\d+)([.](\d+))?([.](\d+))?",
204 debugger
.GetVersionString(),
209 len(lldb_versions_match
.groups()) >= 1
210 and lldb_versions_match
.groups()[0]
212 lldb_major
= int(lldb_versions_match
.groups()[0])
214 len(lldb_versions_match
.groups()) >= 5
215 and lldb_versions_match
.groups()[4]
217 lldb_minor
= int(lldb_versions_match
.groups()[4])
222 print("LLDB version %s" % debugger
.GetVersionString())
223 print("Unwind diagnostics for thread %d" % thread
.GetIndexID())
226 "============================================================================================="
229 print("OS plugin setting:")
230 debugger
.HandleCommand(
231 "settings show target.process.python-os-plugin-path"
234 print("Live register context:")
235 thread
.SetSelectedFrame(0)
236 debugger
.HandleCommand("register read")
239 "============================================================================================="
242 print("lldb's unwind algorithm:")
245 for frame
in thread
.frames
:
246 if not frame
.IsInlined():
247 this_module
= backtrace_print_frame(
248 target
, frame_num
, frame
.GetPC(), frame
.GetFP()
250 print_stack_frame(process
, frame
.GetFP())
252 if this_module
is not None:
253 modules_seen
.append(this_module
)
254 addresses_seen
.append(frame
.GetPC())
255 frame_num
= frame_num
+ 1
258 "============================================================================================="
261 print("Simple stack walk algorithm:")
263 (module_list
, address_list
) = simple_backtrace(debugger
)
264 if module_list
and module_list
is not None:
265 modules_seen
+= module_list
266 if address_list
and address_list
is not None:
267 addresses_seen
= set(addresses_seen
)
268 addresses_seen
.update(set(address_list
))
272 "============================================================================================="
275 print("Modules seen in stack walks:")
277 modules_already_seen
= set()
278 for module
in modules_seen
:
281 and module
.GetFileSpec().GetFilename() is not None
284 not module
.GetFileSpec().GetFilename()
285 in modules_already_seen
287 debugger
.HandleCommand(
288 "image list %s" % module
.GetFileSpec().GetFilename()
290 modules_already_seen
.add(module
.GetFileSpec().GetFilename())
294 "============================================================================================="
297 print("Disassembly ofaddresses seen in stack walks:")
299 additional_addresses_to_disassemble
= addresses_seen
300 for frame
in thread
.frames
:
301 if not frame
.IsInlined():
303 "--------------------------------------------------------------------------------------"
307 "Disassembly of %s, frame %d, address 0x%x"
309 frame
.GetFunctionName(),
316 target
.triple
[0:6] == "x86_64"
317 or target
.triple
[0:4] == "i386"
319 debugger
.HandleCommand(
320 "disassemble -F att -a 0x%x" % frame
.GetPC()
323 debugger
.HandleCommand(
324 "disassemble -a 0x%x" % frame
.GetPC()
326 if frame
.GetPC() in additional_addresses_to_disassemble
:
327 additional_addresses_to_disassemble
.remove(frame
.GetPC())
329 for address
in list(additional_addresses_to_disassemble
):
331 "--------------------------------------------------------------------------------------"
334 print("Disassembly of 0x%x" % address
)
336 if target
.triple
[0:6] == "x86_64" or target
.triple
[0:4] == "i386":
337 debugger
.HandleCommand("disassemble -F att -a 0x%x" % address
)
339 debugger
.HandleCommand("disassemble -a 0x%x" % address
)
343 "============================================================================================="
346 additional_addresses_to_show_unwind
= addresses_seen
347 for frame
in thread
.frames
:
348 if not frame
.IsInlined():
350 "--------------------------------------------------------------------------------------"
354 "Unwind instructions for %s, frame %d"
355 % (frame
.GetFunctionName(), frame
.GetFrameID())
358 debugger
.HandleCommand(
359 'image show-unwind -a "0x%x"' % frame
.GetPC()
361 if frame
.GetPC() in additional_addresses_to_show_unwind
:
362 additional_addresses_to_show_unwind
.remove(frame
.GetPC())
364 for address
in list(additional_addresses_to_show_unwind
):
366 "--------------------------------------------------------------------------------------"
369 print("Unwind instructions for 0x%x" % address
)
371 debugger
.HandleCommand('image show-unwind -a "0x%x"' % address
)
374 def create_diagnose_unwind_options():
375 usage
= "usage: %prog"
376 description
= """Print diagnostic information about a thread backtrace which will help to debug unwind problems"""
377 parser
= optparse
.OptionParser(
378 description
=description
, prog
="diagnose_unwind", usage
=usage
383 def __lldb_init_module(debugger
, internal_dict
):
384 debugger
.HandleCommand(
385 "command script add -o -f %s.diagnose_unwind diagnose-unwind" % __name__
388 'The "diagnose-unwind" command has been installed, type "help diagnose-unwind" for detailed help.'