1 import sys
, subprocess
, os
, re
, time
, getopt
, shlex
, xnudefines
3 from functools
import wraps
4 from ctypes
import c_ulonglong
as uint64_t
5 from ctypes
import c_void_p
as voidptr_t
8 from core
import caching
9 from core
.standard
import *
10 from core
.configuration
import *
11 from core
.kernelcore
import *
13 from core
.lazytarget
import *
17 """ Kernel Debugging macros for lldb.
18 Please make sure you read the README COMPLETELY BEFORE reading anything below.
19 It is very critical that you read coding guidelines in Section E in README file.
22 COMMON_HELP_STRING
= """
23 -h Show the help string for the command.
24 -o <path/to/filename> The output of this command execution will be saved to file. Parser information or errors will
25 not be sent to file though. eg /tmp/output.txt
26 -s <filter_string> The "filter_string" param is parsed to python regex expression and each line of output
27 will be printed/saved only if it matches the expression.
28 -v [-v...] Each additional -v will increase the verbosity of the command.
29 -p <plugin_name> Send the output of the command to plugin. Please see README for usage of plugins.
31 # End Utility functions
32 # Debugging specific utility functions
34 #decorators. Not to be called directly.
36 def static_var(var_name
, initial_value
):
38 setattr(obj
, var_name
, initial_value
)
42 def header(initial_value
):
44 setattr(obj
, 'header', initial_value
)
48 # holds type declarations done by xnu.
49 #DONOTTOUCHME: Exclusive use of lldb_type_summary only.
50 lldb_summary_definitions
= {}
51 def lldb_type_summary(types_list
):
52 """ A function decorator to register a summary for a type in lldb.
53 params: types_list - [] an array of types that you wish to register a summary callback function. (ex. ['task *', 'task_t'])
54 returns: Nothing. This is a decorator.
56 def _get_summary(obj
):
57 def _internal_summary_function(lldbval
, internal_dict
):
59 if internal_dict
!= None and len(obj
.header
) > 0 :
60 out_string
+= "\n" + obj
.header
+"\n"
61 out_string
+= obj( core
.value(lldbval
) )
65 summary_function_name
= "LLDBSummary" + obj
.__name
__
66 myglobals
[summary_function_name
] = _internal_summary_function
67 summary_function
= myglobals
[summary_function_name
]
68 summary_function
.__doc
__ = obj
.__doc
__
70 global lldb_summary_definitions
71 for single_type
in types_list
:
72 if config
['showTypeSummary']:
73 if single_type
in lldb_summary_definitions
.keys():
74 lldb
.debugger
.HandleCommand("type summary delete --category kernel \""+ single_type
+ "\"")
75 lldb
.debugger
.HandleCommand("type summary add \""+ single_type
+"\" --category kernel --python-function " + MODULE_NAME
+ "." + summary_function_name
)
76 lldb_summary_definitions
[single_type
] = obj
81 #global cache of documentation for lldb commands exported by this module
82 #DONOTTOUCHME: Exclusive use of lldb_command only.
83 lldb_command_documentation
= {}
85 def lldb_command(cmd_name
, option_string
= ''):
86 """ A function decorator to define a command with namd 'cmd_name' in the lldb scope to call python function.
87 params: cmd_name - str : name of command to be set in lldb prompt.
88 option_string - str: getopt like option string. Only CAPITAL LETTER options allowed.
89 see README on Customizing command options.
91 if option_string
!= option_string
.upper():
92 raise RuntimeError("Cannot setup command with lowercase option args. %s" % option_string
)
95 def _internal_command_function(debugger
, command
, result
, internal_dict
):
96 global config
, lldb_run_command_state
97 stream
= CommandOutput(cmd_name
, result
)
98 # need to avoid printing on stdout if called from lldb_run_command.
99 if 'active' in lldb_run_command_state
and lldb_run_command_state
['active']:
100 debuglog('Running %s from lldb_run_command' % command
)
102 result
.SetImmediateOutputFile(sys
.__stdout
__)
104 command_args
= shlex
.split(command
)
105 lldb
.debugger
.HandleCommand('type category disable kernel' )
106 def_verbose_level
= config
['verbosity']
109 stream
.setOptions(command_args
, option_string
)
110 if stream
.verbose_level
!= 0:
111 config
['verbosity'] += stream
.verbose_level
112 with RedirectStdStreams(stdout
=stream
) :
114 obj(cmd_args
=stream
.target_cmd_args
, cmd_options
=stream
.target_cmd_options
)
116 obj(cmd_args
=stream
.target_cmd_args
)
117 except KeyboardInterrupt:
118 print "Execution interrupted by user"
119 except ArgumentError
as arg_error
:
120 if str(arg_error
) != "HELP":
121 print "Argument Error: " + str(arg_error
)
122 print "{0:s}:\n {1:s}".format(cmd_name
, obj
.__doc
__.strip())
124 except Exception as exc
:
125 if not config
['debug']:
127 ************ LLDB found an exception ************
128 There has been an uncaught exception. A possible cause could be that remote connection has been disconnected.
129 However, it is recommended that you report the exception to lldb/kernel debugging team about it.
130 ************ Please run 'xnudebug debug enable' to start collecting logs. ************
134 if config
['showTypeSummary']:
135 lldb
.debugger
.HandleCommand('type category enable kernel' )
137 if stream
.pluginRequired
:
138 plugin
= LoadXNUPlugin(stream
.pluginName
)
140 print "Could not load plugins."+stream
.pluginName
142 plugin
.plugin_init(kern
, config
, lldb
, kern
.IsDebuggerConnected())
143 return_data
= plugin
.plugin_execute(cmd_name
, result
.GetOutput())
144 ProcessXNUPluginResult(return_data
)
145 plugin
.plugin_cleanup()
147 #restore the verbose level after command is complete
148 config
['verbosity'] = def_verbose_level
152 myglobals
= globals()
153 command_function_name
= obj
.__name
__+"Command"
154 myglobals
[command_function_name
] = _internal_command_function
155 command_function
= myglobals
[command_function_name
]
157 print "ERROR: Cannot register command({:s}) without documentation".format(cmd_name
)
159 obj
.__doc
__ += "\n" + COMMON_HELP_STRING
160 command_function
.__doc
__ = obj
.__doc
__
161 global lldb_command_documentation
162 if cmd_name
in lldb_command_documentation
:
163 lldb
.debugger
.HandleCommand("command script delete "+cmd_name
)
164 lldb_command_documentation
[cmd_name
] = (obj
.__name
__, obj
.__doc
__.lstrip(), option_string
)
165 lldb
.debugger
.HandleCommand("command script add -f " + MODULE_NAME
+ "." + command_function_name
+ " " + cmd_name
)
169 def lldb_alias(alias_name
, cmd_line
):
170 """ define an alias in the lldb command line.
171 A programatic way of registering an alias. This basically does
172 (lldb)command alias alias_name "cmd_line"
174 lldb_alias('readphys16', 'readphys 16')
176 alias_name
= alias_name
.strip()
177 cmd_line
= cmd_line
.strip()
178 lldb
.debugger
.HandleCommand("command alias " + alias_name
+ " "+ cmd_line
)
180 def SetupLLDBTypeSummaries(reset
=False):
181 global lldb_summary_definitions
, MODULE_NAME
183 lldb
.debugger
.HandleCommand("type category delete kernel ")
184 for single_type
in lldb_summary_definitions
.keys():
185 summary_function
= lldb_summary_definitions
[single_type
]
186 lldb_cmd
= "type summary add \""+ single_type
+"\" --category kernel --python-function " + MODULE_NAME
+ ".LLDBSummary" + summary_function
.__name
__
188 lldb
.debugger
.HandleCommand(lldb_cmd
)
189 if config
['showTypeSummary']:
190 lldb
.debugger
.HandleCommand("type category enable kernel")
192 lldb
.debugger
.HandleCommand("type category disable kernel")
196 def LoadXNUPlugin(name
):
197 """ Try to load a plugin from the plugins directory.
202 module_obj
= __import__('plugins.'+name
, globals(), locals(), [], -1)
203 module_obj
= module_obj
.__dict
__[name
]
204 defs
= dir(module_obj
)
205 if 'plugin_init' in defs
and 'plugin_execute' in defs
and 'plugin_cleanup' in defs
:
208 print "Plugin is not correctly implemented. Please read documentation on implementing plugins"
210 print "plugin not found :"+name
214 def ProcessXNUPluginResult(result_data
):
215 """ Look at the returned data from plugin and see if anymore actions are required or not
216 params: result_data - list of format (status, out_string, more_commands)
218 ret_status
= result_data
[0]
219 ret_string
= result_data
[1]
220 ret_commands
= result_data
[2]
222 if ret_status
== False:
223 print "Plugin failed: " + ret_string
226 if len(ret_commands
) >= 0:
227 for cmd
in ret_commands
:
228 print "Running command on behalf of plugin:" + cmd
229 lldb
.debugger
.HandleCommand(cmd
)
232 # holds tests registered with xnu.
233 #DONOTTOUCHME: Exclusive use of xnudebug_test only
234 lldb_command_tests
= {}
235 def xnudebug_test(test_name
):
236 """ A function decoratore to register a test with the framework. Each test is supposed to be of format
237 def Test<name>(kernel_target, config, lldb_obj, isConnected )
239 NOTE: The testname should start with "Test" else exception will be raised.
242 global lldb_command_tests
243 if obj
.__name
__.find("Test") != 0 :
244 print "Test name ", obj
.__name
__ , " should start with Test"
246 lldb_command_tests
[test_name
] = (test_name
, obj
.__name
__, obj
, obj
.__doc
__)
251 # End Debugging specific utility functions
252 # Kernel Debugging specific classes and accessor methods
254 # global access object for target kernel
256 def GetObjectAtIndexFromArray(array_base
, index
):
257 """ Subscript indexing for arrays that are represented in C as pointers.
258 for ex. int *arr = malloc(20*sizeof(int));
259 now to get 3rd int from 'arr' you'd do
261 GetObjectAtIndexFromArray(arr_val,2)
263 array_base : core.value - representing a pointer type (ex. base of type 'ipc_entry *')
264 index : int - 0 based index into the array
266 core.value : core.value of the same type as array_base_val but pointing to index'th element
268 array_base_val
= array_base
.GetSBValue()
269 base_address
= array_base_val
.GetValueAsUnsigned()
270 size
= array_base_val
.GetType().GetPointeeType().GetByteSize()
271 obj_address
= base_address
+ (index
* size
)
272 obj
= kern
.GetValueFromAddress(obj_address
, array_base_val
.GetType().GetName())
273 return Cast(obj
, array_base_val
.GetType())
278 def GetLLDBThreadForKernelThread(thread_obj
):
279 """ Get a reference to lldb.SBThread representation for kernel thread.
281 thread_obj : core.cvalue - thread object of type thread_t
283 lldb.SBThread - lldb thread object for getting backtrace/registers etc.
285 tid
= unsigned(thread_obj
.thread_id
)
286 lldb_process
= LazyTarget
.GetProcess()
287 sbthread
= lldb_process
.GetThreadByID(tid
)
288 if not sbthread
.IsValid():
289 # in case lldb doesnt know about this thread, create one
290 if hasattr(lldb_process
, "CreateOSPluginThread"):
291 debuglog("creating os plugin thread on the fly for {0:d} 0x{1:x}".format(tid
, thread_obj
))
292 lldb_process
.CreateOSPluginThread(tid
, unsigned(thread_obj
))
294 raise RuntimeError("LLDB process does not support CreateOSPluginThread.")
295 sbthread
= lldb_process
.GetThreadByID(tid
)
297 if not sbthread
.IsValid():
298 raise RuntimeError("Unable to find lldb thread for tid={0:d} thread = {1:#018x} (#16049947: have you put 'settings set target.load-script-from-symbol-file true' in your .lldbinit?)".format(tid
, thread_obj
))
302 def GetKextSymbolInfo(load_addr
):
303 """ Get a string descriptiong load_addr <kextname> + offset
305 load_addr - int address value of pc in backtrace.
306 returns: str - kext name + offset string. If no cached data available, warning message is returned.
309 symbol_offset
= load_addr
310 kmod_val
= kern
.globals.kmod
311 if not kern
.arch
.startswith('arm64'):
312 for kval
in IterateLinkedList(kmod_val
, 'next'):
313 if load_addr
>= unsigned(kval
.address
) and \
314 load_addr
<= (unsigned(kval
.address
) + unsigned(kval
.size
)):
315 symbol_name
= kval
.name
316 symbol_offset
= load_addr
- unsigned(kval
.address
)
318 return "{:#018x} {:s} + {:#x} \n".format(load_addr
, symbol_name
, symbol_offset
)
320 # only for arm64 we do lookup for split kexts.
321 cached_kext_info
= caching
.GetDynamicCacheData("kern.kexts.loadinformation", [])
322 if not cached_kext_info
and str(GetConnectionProtocol()) == "core":
323 cached_kext_info
= GetKextLoadInformation()
325 if not cached_kext_info
:
326 return "{:#018x} ~ kext info not available. please run 'showallkexts' once ~ \n".format(load_addr
)
328 for kval
in cached_kext_info
:
330 if load_addr
>= text_seg
.vmaddr
and \
331 load_addr
<= (text_seg
.vmaddr
+ text_seg
.vmsize
):
332 symbol_name
= kval
[2]
333 symbol_offset
= load_addr
- text_seg
.vmaddr
335 return "{:#018x} {:s} + {:#x} \n".format(load_addr
, symbol_name
, symbol_offset
)
337 def GetThreadBackTrace(thread_obj
, verbosity
= vHUMAN
, prefix
= ""):
338 """ Get a string to display back trace for a thread.
340 thread_obj - core.cvalue : a thread object of type thread_t.
341 verbosity - int : either of vHUMAN, vSCRIPT or vDETAIL to describe the verbosity of output
342 prefix - str : a string prefix added before the line for each frame.
343 isContinuation - bool : is thread a continuation?
345 str - a multi line string showing each frame in backtrace.
347 is_continuation
= not bool(unsigned(thread_obj
.kernel_stack
))
348 thread_val
= GetLLDBThreadForKernelThread(thread_obj
)
350 kernel_stack
= unsigned(thread_obj
.kernel_stack
)
351 reserved_stack
= unsigned(thread_obj
.reserved_stack
)
352 if not is_continuation
:
353 if kernel_stack
and reserved_stack
:
354 out_string
+= prefix
+ "reserved_stack = {:#018x}\n".format(reserved_stack
)
355 out_string
+= prefix
+ "kernel_stack = {:#018x}\n".format(kernel_stack
)
357 out_string
+= prefix
+ "continuation ="
360 for frame
in thread_val
.frames
:
361 addr
= frame
.GetPCAddress()
362 load_addr
= addr
.GetLoadAddress(LazyTarget
.GetTarget())
363 function
= frame
.GetFunction()
364 frame_p
= frame
.GetFP()
365 mod_name
= frame
.GetModule().GetFileSpec().GetFilename()
367 if iteration
== 0 and not is_continuation
:
368 out_string
+= prefix
+"stacktop = {:#018x}\n".format(frame_p
)
371 # No debug info for 'function'.
373 if not is_continuation
:
374 out_string
+= "{fp:#018x} ".format(fp
= frame_p
)
376 symbol
= frame
.GetSymbol()
378 out_string
+= GetKextSymbolInfo(load_addr
)
380 file_addr
= addr
.GetFileAddress()
381 start_addr
= symbol
.GetStartAddress().GetFileAddress()
382 symbol_name
= symbol
.GetName()
383 symbol_offset
= file_addr
- start_addr
384 out_string
+= "{addr:#018x} {mod}`{symbol} + {offset:#x} \n".format(addr
=load_addr
,
385 mod
=mod_name
, symbol
=symbol_name
, offset
=symbol_offset
)
387 # Debug info is available for 'function'.
388 func_name
= frame
.GetFunctionName()
389 file_name
= frame
.GetLineEntry().GetFileSpec().GetFilename()
390 line_num
= frame
.GetLineEntry().GetLine()
391 func_name
= '%s [inlined]' % func_name
if frame
.IsInlined() else func_name
392 if is_continuation
and frame
.IsInlined():
393 debuglog("Skipping frame for thread {:#018x} since its inlined".format(thread_obj
))
396 if not is_continuation
:
397 out_string
+= "{fp:#018x} ".format(fp
=frame_p
)
398 out_string
+= "{addr:#018x} {func}{args} \n".format(addr
=load_addr
,
400 file=file_name
, line
=line_num
,
401 args
="(" + (str(frame
.arguments
).replace("\n", ", ") if len(frame
.arguments
) > 0 else "void") + ")")
404 last_frame_p
= frame_p
406 if not is_continuation
and last_frame_p
:
407 out_string
+= prefix
+ "stackbottom = {:#018x}".format(last_frame_p
)
408 out_string
= out_string
.replace("variable not available","")
411 def GetSourceInformationForAddress(addr
):
412 """ convert and address to function +offset information.
413 params: addr - int address in the binary to be symbolicated
414 returns: string of format "0xaddress: function + offset"
416 symbols
= kern
.SymbolicateFromAddress(addr
)
417 format_string
= "{0:#018x} <{1:s} + {2:#0x}>"
422 function_name
= str(s
.name
)
423 offset
= addr
- s
.GetStartAddress().GetLoadAddress(LazyTarget
.GetTarget())
424 if function_name
== "":
425 function_name
= "???"
426 return format_string
.format(addr
, function_name
, offset
)
428 def GetFrameLocalVariable(variable_name
, frame_no
=0):
429 """ Find a local variable by name
431 variable_name: str - name of variable to search for
433 core.value - if the variable is found.
434 None - if not found or not Valid
438 lldb_SBThread
= LazyTarget
.GetProcess().GetSelectedThread()
439 frame
= lldb_SBThread
.GetSelectedFrame()
441 frame
= lldb_SBThread
.GetFrameAtIndex(frame_no
)
443 sbval
= frame
.FindVariable(variable_name
)
444 if sbval
and sbval
.IsValid():
445 retval
= core
.cvalue
.value(sbval
)
448 # Begin Macros for kernel debugging
450 @lldb_command('kgmhelp')
451 def KernelDebugCommandsHelp(cmd_args
=None):
452 """ Show a list of registered commands for kenel debugging.
454 global lldb_command_documentation
455 print "List of commands provided by " + MODULE_NAME
+ " for kernel debugging."
456 cmds
= lldb_command_documentation
.keys()
459 if type(lldb_command_documentation
[cmd
][-1]) == type(""):
460 print " {0: <20s} - {1}".format(cmd
, lldb_command_documentation
[cmd
][1].split("\n")[0].strip())
462 print " {0: <20s} - {1}".format(cmd
, "No help string found.")
463 print 'Each of the functions listed here accept the following common options. '
464 print COMMON_HELP_STRING
465 print 'Additionally, each command implementation may have more options. "(lldb) help <command> " will show these options.'
469 @lldb_command('showraw')
470 def ShowRawCommand(cmd_args
=None):
471 """ A command to disable the kernel summaries and show data as seen by the system.
472 This is useful when trying to read every field of a struct as compared to brief summary
474 command
= " ".join(cmd_args
)
475 lldb
.debugger
.HandleCommand('type category disable kernel' )
476 lldb
.debugger
.HandleCommand( command
)
477 lldb
.debugger
.HandleCommand('type category enable kernel' )
480 @lldb_command('xnudebug')
481 def XnuDebugCommand(cmd_args
=None):
482 """ command interface for operating on the xnu macros. Allowed commands are as follows
484 Reload a submodule from the xnu/tools/lldb directory. Do not include the ".py" suffix in modulename.
485 usage: xnudebug reload <modulename> (eg. memory, process, stats etc)
487 remove any cached data held in static or dynamic data cache.
488 usage: xnudebug flushcache
490 Start running registered test with <name> from various modules.
491 usage: xnudebug test <name> (eg. test_memstats)
493 Go through all registered tests and run them
495 Toggle state of debug configuration flag.
498 command_args
= cmd_args
499 if len(command_args
) == 0:
500 raise ArgumentError("No command specified.")
501 supported_subcommands
= ['debug', 'reload', 'test', 'testall', 'flushcache']
502 subcommand
= GetLongestMatchOption(command_args
[0], supported_subcommands
, True)
504 if len(subcommand
) == 0:
505 raise ArgumentError("Subcommand (%s) is not a valid command. " % str(command_args
[0]))
507 subcommand
= subcommand
[0].lower()
508 if subcommand
== 'debug':
509 if command_args
[-1].lower().find('dis') >=0 and config
['debug']:
510 config
['debug'] = False
511 print "Disabled debug logging."
512 elif command_args
[-1].lower().find('dis') < 0 and not config
['debug']:
513 config
['debug'] = True
514 EnableLLDBAPILogging() # provided by utils.py
515 print "Enabled debug logging. \nPlease run 'xnudebug debug disable' to disable it again. "
516 if subcommand
== 'flushcache':
517 print "Current size of cache: {}".format(caching
.GetSizeOfCache())
518 caching
.ClearAllCache()
520 if subcommand
== 'reload':
521 module_name
= command_args
[-1]
522 if module_name
in sys
.modules
:
523 reload(sys
.modules
[module_name
])
524 print module_name
+ " is reloaded from " + sys
.modules
[module_name
].__file
__
526 print "Unable to locate module named ", module_name
527 if subcommand
== 'testall':
528 for test_name
in lldb_command_tests
.keys():
529 print "[BEGIN]", test_name
530 res
= lldb_command_tests
[test_name
][2](kern
, config
, lldb
, True)
532 print "[PASSED] {:s}".format(test_name
)
534 print "[FAILED] {:s}".format(test_name
)
535 if subcommand
== 'test':
536 test_name
= command_args
[-1]
537 if test_name
in lldb_command_tests
:
538 test
= lldb_command_tests
[test_name
]
539 print "Running test {:s}".format(test
[0])
540 if test
[2](kern
, config
, lldb
, True) :
541 print "[PASSED] {:s}".format(test
[0])
543 print "[FAILED] {:s}".format(test
[0])
546 print "No such test registered with name: {:s}".format(test_name
)
547 print "XNUDEBUG Available tests are:"
548 for i
in lldb_command_tests
.keys():
554 @lldb_command('showversion')
555 def ShowVersion(cmd_args
=None):
556 """ Read the kernel version string from a fixed address in low
557 memory. Useful if you don't know which kernel is on the other end,
558 and need to find the appropriate symbols. Beware that if you've
559 loaded a symbol file, but aren't connected to a remote target,
560 the version string from the symbol file will be displayed instead.
561 This macro expects to be connected to the remote kernel to function
568 @lldb_command('paniclog', 'S')
569 def ShowPanicLog(cmd_args
=None, cmd_options
={}):
570 """ Display the paniclog information
571 usage: (lldb) paniclog
573 -v : increase verbosity
574 -S : parse stackshot data (if panic stackshot available)
577 if "-S" in cmd_options
:
578 if hasattr(kern
.globals, "kc_panic_data"):
579 stackshot_saved
= False
580 # TODO: Update logic to handle "in-memory" panic stackshot on Gibraltar platforms
581 # once we drop support for the on disk one there.
582 if kern
.arch
== 'x86_64':
583 if kern
.globals.panic_stackshot_len
!= 0:
584 stackshot_saved
= True
586 print "No panic stackshot available"
588 if unsigned(kern
.globals.panic_info
.eph_panic_flags
) & xnudefines
.EMBEDDED_PANIC_STACKSHOT_SUCCEEDED_FLAG
:
589 stackshot_saved
= True
591 print "No panic stackshot available"
593 kc_data
= unsigned(addressof(kern
.globals.kc_panic_data
))
594 ts
= int(time
.time())
595 ss_binfile
= "/tmp/panic_%d.bin" % ts
596 ss_ipsfile
= "/tmp/stacks_%d.ips" % ts
597 print "savekcdata 0x%x -O %s" % (kc_data
, ss_binfile
)
598 SaveKCDataToFile(["0x%x" % kc_data
], {"-O":ss_binfile}
)
599 self_path
= str(__file__
)
600 base_dir_name
= self_path
[:self_path
.rfind("/")]
601 print "python %s/kcdata.py %s -s %s" % (base_dir_name
, ss_binfile
, ss_ipsfile
)
602 (c
,so
,se
) = RunShellCommand("python %s/kcdata.py %s -s %s" % (base_dir_name
, ss_binfile
, ss_ipsfile
))
604 print "Saved ips stackshot file as %s" % ss_ipsfile
606 print "Failed to run command: exit code: %d, SO: %s SE: %s" % (c
, so
, se
)
608 print "kc_panic_data is unavailable for this kernel config."
613 if kern
.arch
== 'x86_64':
614 panic_buf
= Cast(kern
.globals.panic_info
, 'char *')
615 panic_log_magic
= unsigned(kern
.globals.panic_info
.mph_magic
)
616 panic_log_begin_offset
= unsigned(kern
.globals.panic_info
.mph_panic_log_offset
)
617 panic_log_len
= unsigned(kern
.globals.panic_info
.mph_panic_log_len
)
618 other_log_begin_offset
= unsigned(kern
.globals.panic_info
.mph_other_log_offset
)
619 other_log_len
= unsigned(kern
.globals.panic_info
.mph_other_log_len
)
620 cur_debug_buf_ptr_offset
= (unsigned(kern
.globals.debug_buf_ptr
) - unsigned(kern
.globals.panic_info
))
621 if other_log_begin_offset
!= 0 and (other_log_len
== 0 or other_log_len
< (cur_debug_buf_ptr_offset
- other_log_begin_offset
)):
622 other_log_len
= cur_debug_buf_ptr_offset
- other_log_begin_offset
623 expected_panic_magic
= xnudefines
.MACOS_PANIC_MAGIC
625 panic_buf
= Cast(kern
.globals.panic_info
, 'char *')
626 panic_log_magic
= unsigned(kern
.globals.panic_info
.eph_magic
)
627 panic_log_begin_offset
= unsigned(kern
.globals.panic_info
.eph_panic_log_offset
)
628 panic_log_len
= unsigned(kern
.globals.panic_info
.eph_panic_log_len
)
629 other_log_begin_offset
= unsigned(kern
.globals.panic_info
.eph_other_log_offset
)
630 other_log_len
= unsigned(kern
.globals.panic_info
.eph_other_log_len
)
631 expected_panic_magic
= xnudefines
.EMBEDDED_PANIC_MAGIC
633 if panic_log_begin_offset
== 0:
636 if panic_log_magic
!= 0 and panic_log_magic
!= expected_panic_magic
:
637 warn_str
+= "BAD MAGIC! Found 0x%x expected 0x%x".format(panic_log_magic
,
638 expected_panic_magic
)
640 if panic_log_begin_offset
== 0:
642 print "\n %s" % warn_str
645 panic_log_curindex
= 0
646 while panic_log_curindex
< panic_log_len
:
647 p_char
= str(panic_buf
[(panic_log_begin_offset
+ panic_log_curindex
)])
649 panic_log_curindex
+= 1
651 if other_log_begin_offset
!= 0:
652 other_log_curindex
= 0
653 while other_log_curindex
< other_log_len
:
654 p_char
= str(panic_buf
[(other_log_begin_offset
+ other_log_curindex
)])
656 other_log_curindex
+= 1
661 print "\n %s" % warn_str
665 @lldb_command('showbootargs')
666 def ShowBootArgs(cmd_args
=None):
667 """ Display boot arguments passed to the target kernel
669 bootargs
= Cast(kern
.GetGlobalVariable('PE_state').bootArgs
, 'boot_args *')
670 bootargs_cmd
= bootargs
.CommandLine
671 print str(bootargs_cmd
)
673 @static_var("last_process_uniq_id", 1)
674 def GetDebuggerStopIDValue():
675 """ Create a unique session identifier.
677 int - a unique number identified by processid and stopid.
680 process_obj
= LazyTarget
.GetProcess()
681 if hasattr(process_obj
, "GetStopID"):
682 stop_id
= process_obj
.GetStopID()
684 if hasattr(process_obj
, 'GetUniqueID'):
685 proc_uniq_id
= process_obj
.GetUniqueID()
686 #FIXME <rdar://problem/13034329> forces us to do this twice
687 proc_uniq_id
= process_obj
.GetUniqueID()
689 GetDebuggerStopIDValue
.last_process_uniq_id
+=1
690 proc_uniq_id
= GetDebuggerStopIDValue
.last_process_uniq_id
+ 1
692 stop_id_str
= "{:d}:{:d}".format(proc_uniq_id
, stop_id
)
693 return hash(stop_id_str
)
695 # The initialization code to add your commands
696 _xnu_framework_init
= False
697 def __lldb_init_module(debugger
, internal_dict
):
698 global kern
, lldb_command_documentation
, config
, _xnu_framework_init
699 if _xnu_framework_init
:
701 _xnu_framework_init
= True
702 caching
._GetDebuggerSessionID
= GetDebuggerStopIDValue
703 debugger
.HandleCommand('type summary add --regex --summary-string "${var%s}" -C yes -p -v "char \[[0-9]*\]"')
704 debugger
.HandleCommand('type format add --format hex -C yes uintptr_t')
705 kern
= KernelTarget(debugger
)
706 print "xnu debug macros loaded successfully. Run showlldbtypesummaries to enable type summaries."
708 __lldb_init_module(lldb
.debugger
, None)
710 @lldb_command("showlldbtypesummaries")
711 def ShowLLDBTypeSummaries(cmd_args
=[]):
712 """ Enable/Disable kernel type summaries. Default is disabled.
713 Usage: showlldbtypesummaries [enable|disable]
719 if len(cmd_args
) > 0 and cmd_args
[0].lower().find('disable') >=0:
721 config
['showTypeSummary'] = False
722 trailer_msg
= "Please run 'showlldbtypesummaries enable' to enable the summary feature."
724 config
['showTypeSummary'] = True
725 SetupLLDBTypeSummaries(True)
726 trailer_msg
= "Please run 'showlldbtypesummaries disable' to disable the summary feature."
727 lldb_run_command("type category "+ action
+" kernel")
728 print "Successfully "+action
+"d the kernel type summaries. %s" % trailer_msg
730 @lldb_command('walkqueue_head', 'S')
731 def WalkQueueHead(cmd_args
=[], cmd_options
={}):
732 """ walk a queue_head_t and list all members in it. Note this is for queue_head_t. refer to osfmk/kern/queue.h
733 Option: -S - suppress summary output.
734 Usage: (lldb) walkqueue_head <queue_entry *> <struct type> <fieldname>
735 ex: (lldb) walkqueue_head 0x7fffff80 "thread *" "task_threads"
738 global lldb_summary_definitions
740 raise ArgumentError("invalid arguments")
741 if len(cmd_args
) != 3:
742 raise ArgumentError("insufficient arguments")
743 queue_head
= kern
.GetValueFromAddress(cmd_args
[0], 'struct queue_entry *')
744 el_type
= cmd_args
[1]
745 field_name
= cmd_args
[2]
747 if el_type
in lldb_summary_definitions
:
749 if '-S' in cmd_options
:
752 for i
in IterateQueue(queue_head
, el_type
, field_name
):
754 print lldb_summary_definitions
[el_type
](i
)
756 print "{0: <#020x}".format(i
)
760 @lldb_command('walklist_entry', 'S')
761 def WalkList(cmd_args
=[], cmd_options
={}):
762 """ iterate over a list as defined with LIST_ENTRY in bsd/sys/queue.h
764 object addr - value : address of object
765 element_type - str : Type of the next element
766 field_name - str : Name of the field in next element's structure
768 Option: -S - suppress summary output.
769 Usage: (lldb) walklist_entry <obj with list_entry *> <struct type> <fieldname>
770 ex: (lldb) walklist_entry 0x7fffff80 "struct proc *" "p_sibling"
773 global lldb_summary_definitions
775 raise ArgumentError("invalid arguments")
776 if len(cmd_args
) != 3:
777 raise ArgumentError("insufficient arguments")
778 el_type
= cmd_args
[1]
779 queue_head
= kern
.GetValueFromAddress(cmd_args
[0], el_type
)
780 field_name
= cmd_args
[2]
783 if el_type
in lldb_summary_definitions
:
785 if '-S' in cmd_options
:
788 while unsigned(elt
) != 0:
790 elt
= elt
.__getattr
__(field_name
).le_next
792 print lldb_summary_definitions
[el_type
](i
)
794 print "{0: <#020x}".format(i
)
799 from process
import *
806 from userspace
import *
810 from scheduler
import *
812 from structanalyze
import *
813 from ipcimportancedetail
import *
818 from usertaskgdbserver
import *
820 from pgtrace
import *
821 from xnutriage
import *
824 from zonetriage
import *