]> git.saurik.com Git - apple/xnu.git/blame - tools/lldbmacros/xnu.py
xnu-4570.71.2.tar.gz
[apple/xnu.git] / tools / lldbmacros / xnu.py
CommitLineData
5ba3f43e 1import sys, subprocess, os, re, time, getopt, shlex, xnudefines
39236c6e
A
2import lldb
3from functools import wraps
4from ctypes import c_ulonglong as uint64_t
5from ctypes import c_void_p as voidptr_t
6import code
7import core
8from core import caching
9from core.standard import *
10from core.configuration import *
11from core.kernelcore import *
12from utils import *
13from core.lazytarget import *
14
5ba3f43e 15MODULE_NAME=__name__
39236c6e
A
16
17""" Kernel Debugging macros for lldb.
18 Please make sure you read the README COMPLETELY BEFORE reading anything below.
5ba3f43e 19 It is very critical that you read coding guidelines in Section E in README file.
39236c6e
A
20"""
21
5ba3f43e
A
22COMMON_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.
30"""
39236c6e 31# End Utility functions
5ba3f43e 32# Debugging specific utility functions
39236c6e
A
33
34#decorators. Not to be called directly.
35
36def static_var(var_name, initial_value):
37 def _set_var(obj):
38 setattr(obj, var_name, initial_value)
39 return obj
40 return _set_var
41
42def header(initial_value):
43 def _set_header(obj):
44 setattr(obj, 'header', initial_value)
45 return obj
46 return _set_header
47
48# holds type declarations done by xnu.
49#DONOTTOUCHME: Exclusive use of lldb_type_summary only.
50lldb_summary_definitions = {}
51def 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.
55 """
56 def _get_summary(obj):
57 def _internal_summary_function(lldbval, internal_dict):
58 out_string= ""
59 if internal_dict != None and len(obj.header) > 0 :
60 out_string += "\n" + obj.header +"\n"
61 out_string += obj( core.value(lldbval) )
62 return out_string
63
64 myglobals = globals()
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__
69
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
77
78 return obj
79 return _get_summary
80
81#global cache of documentation for lldb commands exported by this module
82#DONOTTOUCHME: Exclusive use of lldb_command only.
83lldb_command_documentation = {}
84
85def 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.
90 """
91 if option_string != option_string.upper():
92 raise RuntimeError("Cannot setup command with lowercase option args. %s" % option_string)
93
94 def _cmd(obj):
95 def _internal_command_function(debugger, command, result, internal_dict):
96 global config, lldb_run_command_state
813fb2f6 97 stream = CommandOutput(cmd_name, result)
39236c6e
A
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)
101 else:
102 result.SetImmediateOutputFile(sys.__stdout__)
103
104 command_args = shlex.split(command)
105 lldb.debugger.HandleCommand('type category disable kernel' )
106 def_verbose_level = config['verbosity']
107
108 try:
109 stream.setOptions(command_args, option_string)
110 if stream.verbose_level != 0:
fe8ab488 111 config['verbosity'] += stream.verbose_level
39236c6e
A
112 with RedirectStdStreams(stdout=stream) :
113 if option_string:
114 obj(cmd_args=stream.target_cmd_args, cmd_options=stream.target_cmd_options)
115 else:
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())
123 return False
124 except Exception as exc:
125 if not config['debug']:
126 print """
127************ LLDB found an exception ************
128There has been an uncaught exception. A possible cause could be that remote connection has been disconnected.
129However, 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. ************
131 """
132 raise
133
134 if config['showTypeSummary']:
135 lldb.debugger.HandleCommand('type category enable kernel' )
136
137 if stream.pluginRequired :
138 plugin = LoadXNUPlugin(stream.pluginName)
139 if plugin == None :
140 print "Could not load plugins."+stream.pluginName
141 return
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()
146
147 #restore the verbose level after command is complete
148 config['verbosity'] = def_verbose_level
149
150 return
151
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]
156 if not obj.__doc__ :
157 print "ERROR: Cannot register command({:s}) without documentation".format(cmd_name)
158 return obj
5ba3f43e 159 obj.__doc__ += "\n" + COMMON_HELP_STRING
39236c6e
A
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)
166 return obj
167 return _cmd
168
169def 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"
173 ex.
174 lldb_alias('readphys16', 'readphys 16')
175 """
176 alias_name = alias_name.strip()
177 cmd_line = cmd_line.strip()
178 lldb.debugger.HandleCommand("command alias " + alias_name + " "+ cmd_line)
179
180def SetupLLDBTypeSummaries(reset=False):
181 global lldb_summary_definitions, MODULE_NAME
182 if reset == True:
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__
187 debuglog(lldb_cmd)
188 lldb.debugger.HandleCommand(lldb_cmd)
189 if config['showTypeSummary']:
190 lldb.debugger.HandleCommand("type category enable kernel")
191 else:
192 lldb.debugger.HandleCommand("type category disable kernel")
193
194 return
195
196def LoadXNUPlugin(name):
197 """ Try to load a plugin from the plugins directory.
198 """
199 retval = None
200 name=name.strip()
201 try:
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:
206 retval = module_obj
207 else:
208 print "Plugin is not correctly implemented. Please read documentation on implementing plugins"
209 except:
210 print "plugin not found :"+name
211
212 return retval
213
214def 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)
217 """
218 ret_status = result_data[0]
219 ret_string = result_data[1]
220 ret_commands = result_data[2]
221
222 if ret_status == False:
223 print "Plugin failed: " + ret_string
224 return
225 print 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)
230 return
231
232# holds tests registered with xnu.
233#DONOTTOUCHME: Exclusive use of xnudebug_test only
234lldb_command_tests = {}
235def 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 )
238
239 NOTE: The testname should start with "Test" else exception will be raised.
240 """
241 def _test(obj):
242 global lldb_command_tests
243 if obj.__name__.find("Test") != 0 :
244 print "Test name ", obj.__name__ , " should start with Test"
245 raise ValueError
246 lldb_command_tests[test_name] = (test_name, obj.__name__, obj, obj.__doc__)
247 return obj
248 return _test
249
250
251# End Debugging specific utility functions
252# Kernel Debugging specific classes and accessor methods
253
254# global access object for target kernel
255
256def 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
260 arr[2] in C
261 GetObjectAtIndexFromArray(arr_val,2)
262 params:
263 array_base : core.value - representing a pointer type (ex. base of type 'ipc_entry *')
264 index : int - 0 based index into the array
265 returns:
266 core.value : core.value of the same type as array_base_val but pointing to index'th element
267 """
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())
274
275
276kern = None
277
278def GetLLDBThreadForKernelThread(thread_obj):
279 """ Get a reference to lldb.SBThread representation for kernel thread.
280 params:
281 thread_obj : core.cvalue - thread object of type thread_t
282 returns
283 lldb.SBThread - lldb thread object for getting backtrace/registers etc.
284 """
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))
293 else:
294 raise RuntimeError("LLDB process does not support CreateOSPluginThread.")
295 sbthread = lldb_process.GetThreadByID(tid)
296
297 if not sbthread.IsValid():
fe8ab488
A
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))
299
39236c6e
A
300 return sbthread
301
39037602
A
302def GetKextSymbolInfo(load_addr):
303 """ Get a string descriptiong load_addr <kextname> + offset
304 params:
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.
307 """
308 symbol_name = "None"
309 symbol_offset = load_addr
310 kmod_val = kern.globals.kmod
5ba3f43e 311 if not kern.arch.startswith('arm64'):
39037602
A
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)
317 break
318 return "{:#018x} {:s} + {:#x} \n".format(load_addr, symbol_name, symbol_offset)
319
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()
324
325 if not cached_kext_info:
326 return "{:#018x} ~ kext info not available. please run 'showallkexts' once ~ \n".format(load_addr)
327
328 for kval in cached_kext_info:
329 text_seg = kval[5]
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
334 break
335 return "{:#018x} {:s} + {:#x} \n".format(load_addr, symbol_name, symbol_offset)
336
39236c6e
A
337def GetThreadBackTrace(thread_obj, verbosity = vHUMAN, prefix = ""):
338 """ Get a string to display back trace for a thread.
339 params:
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?
344 returns:
345 str - a multi line string showing each frame in backtrace.
346 """
347 is_continuation = not bool(unsigned(thread_obj.kernel_stack))
348 thread_val = GetLLDBThreadForKernelThread(thread_obj)
349 out_string = ""
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)
356 else:
357 out_string += prefix + "continuation ="
358 iteration = 0
359 last_frame_p = 0
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()
366
367 if iteration == 0 and not is_continuation:
368 out_string += prefix +"stacktop = {:#018x}\n".format(frame_p)
fe8ab488 369
39236c6e
A
370 if not function:
371 # No debug info for 'function'.
39236c6e
A
372 out_string += prefix
373 if not is_continuation:
374 out_string += "{fp:#018x} ".format(fp = frame_p)
fe8ab488
A
375
376 symbol = frame.GetSymbol()
377 if not symbol:
39037602 378 out_string += GetKextSymbolInfo(load_addr)
fe8ab488
A
379 else:
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)
39236c6e
A
386 else:
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))
394 continue
395 out_string += prefix
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,
399 func=func_name,
400 file=file_name, line=line_num,
401 args="(" + (str(frame.arguments).replace("\n", ", ") if len(frame.arguments) > 0 else "void") + ")")
402 iteration += 1
403 if frame_p:
404 last_frame_p = frame_p
405
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","")
409 return out_string
410
411def 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"
415 """
416 symbols = kern.SymbolicateFromAddress(addr)
417 format_string = "{0:#018x} <{1:s} + {2:#0x}>"
418 offset = 0
419 function_name = ""
420 if len(symbols) > 0:
421 s = symbols[0]
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)
427
428def GetFrameLocalVariable(variable_name, frame_no=0):
429 """ Find a local variable by name
430 params:
431 variable_name: str - name of variable to search for
432 returns:
433 core.value - if the variable is found.
434 None - if not found or not Valid
435 """
436 retval = None
437 sbval = None
438 lldb_SBThread = LazyTarget.GetProcess().GetSelectedThread()
439 frame = lldb_SBThread.GetSelectedFrame()
440 if frame_no :
441 frame = lldb_SBThread.GetFrameAtIndex(frame_no)
442 if frame :
443 sbval = frame.FindVariable(variable_name)
444 if sbval and sbval.IsValid():
445 retval = core.cvalue.value(sbval)
446 return retval
447
448# Begin Macros for kernel debugging
449
450@lldb_command('kgmhelp')
451def KernelDebugCommandsHelp(cmd_args=None):
452 """ Show a list of registered commands for kenel debugging.
453 """
454 global lldb_command_documentation
455 print "List of commands provided by " + MODULE_NAME + " for kernel debugging."
456 cmds = lldb_command_documentation.keys()
457 cmds.sort()
458 for cmd in cmds:
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())
461 else:
462 print " {0: <20s} - {1}".format(cmd , "No help string found.")
5ba3f43e
A
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.'
39236c6e
A
466 return None
467
468
469@lldb_command('showraw')
470def 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
473 """
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' )
478
479
480@lldb_command('xnudebug')
481def XnuDebugCommand(cmd_args=None):
482 """ command interface for operating on the xnu macros. Allowed commands are as follows
483 reload:
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)
39037602
A
486 flushcache:
487 remove any cached data held in static or dynamic data cache.
488 usage: xnudebug flushcache
39236c6e
A
489 test:
490 Start running registered test with <name> from various modules.
491 usage: xnudebug test <name> (eg. test_memstats)
492 testall:
493 Go through all registered tests and run them
494 debug:
495 Toggle state of debug configuration flag.
496 """
497 global config
498 command_args = cmd_args
499 if len(command_args) == 0:
500 raise ArgumentError("No command specified.")
39037602 501 supported_subcommands = ['debug', 'reload', 'test', 'testall', 'flushcache']
39236c6e
A
502 subcommand = GetLongestMatchOption(command_args[0], supported_subcommands, True)
503
504 if len(subcommand) == 0:
505 raise ArgumentError("Subcommand (%s) is not a valid command. " % str(command_args[0]))
39037602 506
39236c6e
A
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. "
39037602
A
516 if subcommand == 'flushcache':
517 print "Current size of cache: {}".format(caching.GetSizeOfCache())
518 caching.ClearAllCache()
519
39236c6e
A
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__
525 else:
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)
531 if res:
532 print "[PASSED] {:s}".format(test_name)
533 else:
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])
542 else:
543 print "[FAILED] {:s}".format(test[0])
544 return ""
545 else:
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():
549 print i
550 return None
551
552 return False
553
554@lldb_command('showversion')
555def 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
562 correctly.
563
564 """
565 print kern.version
566
567
3e170ce0
A
568@lldb_command('paniclog', 'S')
569def ShowPanicLog(cmd_args=None, cmd_options={}):
39236c6e 570 """ Display the paniclog information
fe8ab488
A
571 usage: (lldb) paniclog
572 options:
573 -v : increase verbosity
3e170ce0 574 -S : parse stackshot data (if panic stackshot available)
39236c6e 575 """
3e170ce0
A
576
577 if "-S" in cmd_options:
578 if hasattr(kern.globals, "kc_panic_data"):
5ba3f43e 579 stackshot_saved = False
cc8bc92a
A
580 # TODO: Update logic to handle "in-memory" panic stackshot on Gibraltar platforms
581 # once we drop support for the on disk one there.
5ba3f43e
A
582 if kern.arch == 'x86_64':
583 if kern.globals.panic_stackshot_len != 0:
584 stackshot_saved = True
585 else:
586 print "No panic stackshot available"
3e170ce0 587 else:
5ba3f43e
A
588 if unsigned(kern.globals.panic_info.eph_panic_flags) & xnudefines.EMBEDDED_PANIC_STACKSHOT_SUCCEEDED_FLAG:
589 stackshot_saved = True
590 else:
591 print "No panic stackshot available"
592 if stackshot_saved:
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))
603 if c == 0:
604 print "Saved ips stackshot file as %s" % ss_ipsfile
605 else:
606 print "Failed to run command: exit code: %d, SO: %s SE: %s" % (c, so, se)
3e170ce0
A
607 else:
608 print "kc_panic_data is unavailable for this kernel config."
609
fe8ab488 610 out_str = ""
3e170ce0 611 warn_str = ""
5ba3f43e
A
612
613 if kern.arch == 'x86_64':
cc8bc92a
A
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
5ba3f43e
A
624 else:
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)
cc8bc92a 631 expected_panic_magic = xnudefines.EMBEDDED_PANIC_MAGIC
5ba3f43e 632
cc8bc92a
A
633 if panic_log_begin_offset == 0:
634 return
5ba3f43e 635
cc8bc92a
A
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)
5ba3f43e 639
cc8bc92a
A
640 if panic_log_begin_offset == 0:
641 if warn_str:
642 print "\n %s" % warn_str
643 return
644
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)])
648 out_str += p_char
649 panic_log_curindex += 1
5ba3f43e 650
cc8bc92a
A
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)])
5ba3f43e 655 out_str += p_char
cc8bc92a 656 other_log_curindex += 1
5ba3f43e
A
657
658 print out_str
3e170ce0 659
fe8ab488 660 if warn_str:
5ba3f43e 661 print "\n %s" % warn_str
3e170ce0 662
39236c6e
A
663 return
664
665@lldb_command('showbootargs')
666def ShowBootArgs(cmd_args=None):
667 """ Display boot arguments passed to the target kernel
668 """
669 bootargs = Cast(kern.GetGlobalVariable('PE_state').bootArgs, 'boot_args *')
670 bootargs_cmd = bootargs.CommandLine
671 print str(bootargs_cmd)
672
673@static_var("last_process_uniq_id", 1)
674def GetDebuggerStopIDValue():
675 """ Create a unique session identifier.
676 returns:
677 int - a unique number identified by processid and stopid.
678 """
679 stop_id = 0
680 process_obj = LazyTarget.GetProcess()
681 if hasattr(process_obj, "GetStopID"):
682 stop_id = process_obj.GetStopID()
683 proc_uniq_id = 0
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()
688 else:
689 GetDebuggerStopIDValue.last_process_uniq_id +=1
690 proc_uniq_id = GetDebuggerStopIDValue.last_process_uniq_id + 1
691
692 stop_id_str = "{:d}:{:d}".format(proc_uniq_id, stop_id)
693 return hash(stop_id_str)
694
695# The initialization code to add your commands
696_xnu_framework_init = False
697def __lldb_init_module(debugger, internal_dict):
698 global kern, lldb_command_documentation, config, _xnu_framework_init
699 if _xnu_framework_init:
700 return
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."
707
708__lldb_init_module(lldb.debugger, None)
709
710@lldb_command("showlldbtypesummaries")
711def ShowLLDBTypeSummaries(cmd_args=[]):
712 """ Enable/Disable kernel type summaries. Default is disabled.
713 Usage: showlldbtypesummaries [enable|disable]
714 default is enable
715 """
716 global config
717 action = "enable"
718 trailer_msg = ''
719 if len(cmd_args) > 0 and cmd_args[0].lower().find('disable') >=0:
720 action = "disable"
721 config['showTypeSummary'] = False
722 trailer_msg = "Please run 'showlldbtypesummaries enable' to enable the summary feature."
723 else:
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
729
fe8ab488
A
730@lldb_command('walkqueue_head', 'S')
731def 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"
736
737 """
738 global lldb_summary_definitions
739 if not cmd_args:
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]
746 showsummary = False
747 if el_type in lldb_summary_definitions:
748 showsummary = True
749 if '-S' in cmd_options:
750 showsummary = False
751
752 for i in IterateQueue(queue_head, el_type, field_name):
753 if showsummary:
754 print lldb_summary_definitions[el_type](i)
755 else:
756 print "{0: <#020x}".format(i)
757
758
759
760@lldb_command('walklist_entry', 'S')
761def WalkList(cmd_args=[], cmd_options={}):
762 """ iterate over a list as defined with LIST_ENTRY in bsd/sys/queue.h
763 params:
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
767
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"
771
772 """
773 global lldb_summary_definitions
774 if not cmd_args:
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]
781
782 showsummary = False
783 if el_type in lldb_summary_definitions:
784 showsummary = True
785 if '-S' in cmd_options:
786 showsummary = False
787 elt = queue_head
788 while unsigned(elt) != 0:
789 i = elt
790 elt = elt.__getattr__(field_name).le_next
791 if showsummary:
792 print lldb_summary_definitions[el_type](i)
793 else:
794 print "{0: <#020x}".format(i)
795
796
797
39236c6e
A
798from memory import *
799from process import *
800from ipc import *
801from pmap import *
802from ioreg import *
803from mbufs import *
804from net import *
805from kdp import *
806from userspace import *
807from pci import *
808from misc import *
809from apic import *
810from scheduler import *
fe8ab488
A
811from atm import *
812from structanalyze import *
813from ipcimportancedetail import *
814from bank import *
5ba3f43e 815from kasan import *
3e170ce0
A
816from kauth import *
817from waitq import *
818from usertaskgdbserver import *
39037602
A
819from ktrace import *
820from pgtrace import *
821from xnutriage import *
5ba3f43e
A
822from kevent import *
823from ntstat import *
a39ff7e2 824from zonetriage import *
5ba3f43e 825