]> git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/xnu.py
ce5997b5efed37be4e52d43dfa59383821eebf6d
[apple/xnu.git] / tools / lldbmacros / xnu.py
1 import sys, subprocess, os, re, time, getopt, shlex, xnudefines
2 import lldb
3 from functools import wraps
4 from ctypes import c_ulonglong as uint64_t
5 from ctypes import c_void_p as voidptr_t
6 import code
7 import core
8 from core import caching
9 from core.standard import *
10 from core.configuration import *
11 from core.kernelcore import *
12 from utils import *
13 from core.lazytarget import *
14
15 MODULE_NAME=__name__
16
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.
20 """
21
22 COMMON_HELP_STRING = """
23 -h Show the help string for the command.
24 -c [always|auto|never|0|1]
25 Control the colorized output of certain commands
26 -o <path/to/filename> The output of this command execution will be saved to file. Parser information or errors will
27 not be sent to file though. eg /tmp/output.txt
28 -s <filter_string> The "filter_string" param is parsed to python regex expression and each line of output
29 will be printed/saved only if it matches the expression.
30 -v [-v...] Each additional -v will increase the verbosity of the command.
31 -p <plugin_name> Send the output of the command to plugin. Please see README for usage of plugins.
32 """
33 # End Utility functions
34 # Debugging specific utility functions
35
36 #decorators. Not to be called directly.
37
38 def static_var(var_name, initial_value):
39 def _set_var(obj):
40 setattr(obj, var_name, initial_value)
41 return obj
42 return _set_var
43
44 def header(initial_value):
45 def _set_header(obj):
46 setattr(obj, 'header', initial_value)
47 return obj
48 return _set_header
49
50 # holds type declarations done by xnu.
51 #DONOTTOUCHME: Exclusive use of lldb_type_summary only.
52 lldb_summary_definitions = {}
53 def lldb_type_summary(types_list):
54 """ A function decorator to register a summary for a type in lldb.
55 params: types_list - [] an array of types that you wish to register a summary callback function. (ex. ['task *', 'task_t'])
56 returns: Nothing. This is a decorator.
57 """
58 def _get_summary(obj):
59 def _internal_summary_function(lldbval, internal_dict):
60 out_string= ""
61 if internal_dict != None and len(obj.header) > 0 :
62 out_string += "\n" + obj.header +"\n"
63 out_string += obj( core.value(lldbval) )
64 return out_string
65
66 myglobals = globals()
67 summary_function_name = "LLDBSummary" + obj.__name__
68 myglobals[summary_function_name] = _internal_summary_function
69 summary_function = myglobals[summary_function_name]
70 summary_function.__doc__ = obj.__doc__
71
72 global lldb_summary_definitions
73 for single_type in types_list:
74 if config['showTypeSummary']:
75 if single_type in lldb_summary_definitions.keys():
76 lldb.debugger.HandleCommand("type summary delete --category kernel \""+ single_type + "\"")
77 lldb.debugger.HandleCommand("type summary add \""+ single_type +"\" --category kernel --python-function " + MODULE_NAME + "." + summary_function_name)
78 lldb_summary_definitions[single_type] = obj
79
80 return obj
81 return _get_summary
82
83 #global cache of documentation for lldb commands exported by this module
84 #DONOTTOUCHME: Exclusive use of lldb_command only.
85 lldb_command_documentation = {}
86
87 def lldb_command(cmd_name, option_string = '', fancy=False):
88 """ A function decorator to define a command with namd 'cmd_name' in the lldb scope to call python function.
89 params: cmd_name - str : name of command to be set in lldb prompt.
90 option_string - str: getopt like option string. Only CAPITAL LETTER options allowed.
91 see README on Customizing command options.
92 fancy - bool : whether the command will receive an 'O' object to do fancy output (tables, indent, color)
93 """
94 if option_string != option_string.upper():
95 raise RuntimeError("Cannot setup command with lowercase option args. %s" % option_string)
96
97 def _cmd(obj):
98 def _internal_command_function(debugger, command, result, internal_dict):
99 global config, lldb_run_command_state
100 stream = CommandOutput(cmd_name, result)
101 # need to avoid printing on stdout if called from lldb_run_command.
102 if 'active' in lldb_run_command_state and lldb_run_command_state['active']:
103 debuglog('Running %s from lldb_run_command' % command)
104 else:
105 result.SetImmediateOutputFile(sys.__stdout__)
106
107 command_args = shlex.split(command)
108 lldb.debugger.HandleCommand('type category disable kernel' )
109 def_verbose_level = config['verbosity']
110
111 try:
112 stream.setOptions(command_args, option_string)
113 if stream.verbose_level != 0:
114 config['verbosity'] += stream.verbose_level
115 with RedirectStdStreams(stdout=stream) :
116 args = { 'cmd_args': stream.target_cmd_args }
117 if option_string:
118 args['cmd_options'] = stream.target_cmd_options
119 if fancy:
120 args['O'] = stream
121 obj(**args)
122 except KeyboardInterrupt:
123 print "Execution interrupted by user"
124 except ArgumentError as arg_error:
125 if str(arg_error) != "HELP":
126 print "Argument Error: " + str(arg_error)
127 print "{0:s}:\n {1:s}".format(cmd_name, obj.__doc__.strip())
128 return False
129 except Exception as exc:
130 if not config['debug']:
131 print """
132 ************ LLDB found an exception ************
133 There has been an uncaught exception. A possible cause could be that remote connection has been disconnected.
134 However, it is recommended that you report the exception to lldb/kernel debugging team about it.
135 ************ Please run 'xnudebug debug enable' to start collecting logs. ************
136 """
137 raise
138
139 if config['showTypeSummary']:
140 lldb.debugger.HandleCommand('type category enable kernel' )
141
142 if stream.pluginRequired :
143 plugin = LoadXNUPlugin(stream.pluginName)
144 if plugin == None :
145 print "Could not load plugins."+stream.pluginName
146 return
147 plugin.plugin_init(kern, config, lldb, kern.IsDebuggerConnected())
148 return_data = plugin.plugin_execute(cmd_name, result.GetOutput())
149 ProcessXNUPluginResult(return_data)
150 plugin.plugin_cleanup()
151
152 #restore the verbose level after command is complete
153 config['verbosity'] = def_verbose_level
154
155 return
156
157 myglobals = globals()
158 command_function_name = obj.__name__+"Command"
159 myglobals[command_function_name] = _internal_command_function
160 command_function = myglobals[command_function_name]
161 if not obj.__doc__ :
162 print "ERROR: Cannot register command({:s}) without documentation".format(cmd_name)
163 return obj
164 obj.__doc__ += "\n" + COMMON_HELP_STRING
165 command_function.__doc__ = obj.__doc__
166 global lldb_command_documentation
167 if cmd_name in lldb_command_documentation:
168 lldb.debugger.HandleCommand("command script delete "+cmd_name)
169 lldb_command_documentation[cmd_name] = (obj.__name__, obj.__doc__.lstrip(), option_string)
170 lldb.debugger.HandleCommand("command script add -f " + MODULE_NAME + "." + command_function_name + " " + cmd_name)
171
172 if fancy:
173 def wrapped_fun(cmd_args=None, cmd_options={}, O=None):
174 if O is None:
175 stream = CommandOutput(cmd_name, fhandle=sys.stdout)
176 with RedirectStdStreams(stdout=stream):
177 return obj(cmd_args, cmd_options, stream)
178 else:
179 return obj(cmd_args, cmd_options, O)
180 return wrapped_fun
181 return obj
182 return _cmd
183
184 def lldb_alias(alias_name, cmd_line):
185 """ define an alias in the lldb command line.
186 A programatic way of registering an alias. This basically does
187 (lldb)command alias alias_name "cmd_line"
188 ex.
189 lldb_alias('readphys16', 'readphys 16')
190 """
191 alias_name = alias_name.strip()
192 cmd_line = cmd_line.strip()
193 lldb.debugger.HandleCommand("command alias " + alias_name + " "+ cmd_line)
194
195 def SetupLLDBTypeSummaries(reset=False):
196 global lldb_summary_definitions, MODULE_NAME
197 if reset == True:
198 lldb.debugger.HandleCommand("type category delete kernel ")
199 for single_type in lldb_summary_definitions.keys():
200 summary_function = lldb_summary_definitions[single_type]
201 lldb_cmd = "type summary add \""+ single_type +"\" --category kernel --python-function " + MODULE_NAME + ".LLDBSummary" + summary_function.__name__
202 debuglog(lldb_cmd)
203 lldb.debugger.HandleCommand(lldb_cmd)
204 if config['showTypeSummary']:
205 lldb.debugger.HandleCommand("type category enable kernel")
206 else:
207 lldb.debugger.HandleCommand("type category disable kernel")
208
209 return
210
211 def LoadXNUPlugin(name):
212 """ Try to load a plugin from the plugins directory.
213 """
214 retval = None
215 name=name.strip()
216 try:
217 module_obj = __import__('plugins.'+name, globals(), locals(), [], -1)
218 module_obj = module_obj.__dict__[name]
219 defs = dir(module_obj)
220 if 'plugin_init' in defs and 'plugin_execute' in defs and 'plugin_cleanup' in defs:
221 retval = module_obj
222 else:
223 print "Plugin is not correctly implemented. Please read documentation on implementing plugins"
224 except:
225 print "plugin not found :"+name
226
227 return retval
228
229 def ProcessXNUPluginResult(result_data):
230 """ Look at the returned data from plugin and see if anymore actions are required or not
231 params: result_data - list of format (status, out_string, more_commands)
232 """
233 ret_status = result_data[0]
234 ret_string = result_data[1]
235 ret_commands = result_data[2]
236
237 if ret_status == False:
238 print "Plugin failed: " + ret_string
239 return
240 print ret_string
241 if len(ret_commands) >= 0:
242 for cmd in ret_commands:
243 print "Running command on behalf of plugin:" + cmd
244 lldb.debugger.HandleCommand(cmd)
245 return
246
247 # holds tests registered with xnu.
248 #DONOTTOUCHME: Exclusive use of xnudebug_test only
249 lldb_command_tests = {}
250 def xnudebug_test(test_name):
251 """ A function decoratore to register a test with the framework. Each test is supposed to be of format
252 def Test<name>(kernel_target, config, lldb_obj, isConnected )
253
254 NOTE: The testname should start with "Test" else exception will be raised.
255 """
256 def _test(obj):
257 global lldb_command_tests
258 if obj.__name__.find("Test") != 0 :
259 print "Test name ", obj.__name__ , " should start with Test"
260 raise ValueError
261 lldb_command_tests[test_name] = (test_name, obj.__name__, obj, obj.__doc__)
262 return obj
263 return _test
264
265
266 # End Debugging specific utility functions
267 # Kernel Debugging specific classes and accessor methods
268
269 # global access object for target kernel
270
271 def GetObjectAtIndexFromArray(array_base, index):
272 """ Subscript indexing for arrays that are represented in C as pointers.
273 for ex. int *arr = malloc(20*sizeof(int));
274 now to get 3rd int from 'arr' you'd do
275 arr[2] in C
276 GetObjectAtIndexFromArray(arr_val,2)
277 params:
278 array_base : core.value - representing a pointer type (ex. base of type 'ipc_entry *')
279 index : int - 0 based index into the array
280 returns:
281 core.value : core.value of the same type as array_base_val but pointing to index'th element
282 """
283 array_base_val = array_base.GetSBValue()
284 base_address = array_base_val.GetValueAsUnsigned()
285 size = array_base_val.GetType().GetPointeeType().GetByteSize()
286 obj_address = base_address + (index * size)
287 obj = kern.GetValueFromAddress(obj_address, array_base_val.GetType())
288 return Cast(obj, array_base_val.GetType())
289
290
291 kern = None
292
293 def GetLLDBThreadForKernelThread(thread_obj):
294 """ Get a reference to lldb.SBThread representation for kernel thread.
295 params:
296 thread_obj : core.cvalue - thread object of type thread_t
297 returns
298 lldb.SBThread - lldb thread object for getting backtrace/registers etc.
299 """
300 tid = unsigned(thread_obj.thread_id)
301 lldb_process = LazyTarget.GetProcess()
302 sbthread = lldb_process.GetThreadByID(tid)
303 if not sbthread.IsValid():
304 # in case lldb doesnt know about this thread, create one
305 if hasattr(lldb_process, "CreateOSPluginThread"):
306 debuglog("creating os plugin thread on the fly for {0:d} 0x{1:x}".format(tid, thread_obj))
307 lldb_process.CreateOSPluginThread(tid, unsigned(thread_obj))
308 else:
309 raise RuntimeError("LLDB process does not support CreateOSPluginThread.")
310 sbthread = lldb_process.GetThreadByID(tid)
311
312 if not sbthread.IsValid():
313 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))
314
315 return sbthread
316
317 def GetKextSymbolInfo(load_addr):
318 """ Get a string descriptiong load_addr <kextname> + offset
319 params:
320 load_addr - int address value of pc in backtrace.
321 returns: str - kext name + offset string. If no cached data available, warning message is returned.
322 """
323 symbol_name = "None"
324 symbol_offset = load_addr
325 kmod_val = kern.globals.kmod
326 if not kern.arch.startswith('arm64'):
327 for kval in IterateLinkedList(kmod_val, 'next'):
328 if load_addr >= unsigned(kval.address) and \
329 load_addr <= (unsigned(kval.address) + unsigned(kval.size)):
330 symbol_name = kval.name
331 symbol_offset = load_addr - unsigned(kval.address)
332 break
333 return "{:#018x} {:s} + {:#x} \n".format(load_addr, symbol_name, symbol_offset)
334
335 # only for arm64 we do lookup for split kexts.
336 cached_kext_info = caching.GetDynamicCacheData("kern.kexts.loadinformation", [])
337 if not cached_kext_info and str(GetConnectionProtocol()) == "core":
338 cached_kext_info = GetKextLoadInformation()
339
340 if not cached_kext_info:
341 return "{:#018x} ~ kext info not available. please run 'showallkexts' once ~ \n".format(load_addr)
342
343 for kval in cached_kext_info:
344 text_seg = kval[5]
345 if load_addr >= text_seg.vmaddr and \
346 load_addr <= (text_seg.vmaddr + text_seg.vmsize):
347 symbol_name = kval[2]
348 symbol_offset = load_addr - text_seg.vmaddr
349 break
350 return "{:#018x} {:s} + {:#x} \n".format(load_addr, symbol_name, symbol_offset)
351
352 def GetThreadBackTrace(thread_obj, verbosity = vHUMAN, prefix = ""):
353 """ Get a string to display back trace for a thread.
354 params:
355 thread_obj - core.cvalue : a thread object of type thread_t.
356 verbosity - int : either of vHUMAN, vSCRIPT or vDETAIL to describe the verbosity of output
357 prefix - str : a string prefix added before the line for each frame.
358 isContinuation - bool : is thread a continuation?
359 returns:
360 str - a multi line string showing each frame in backtrace.
361 """
362 is_continuation = not bool(unsigned(thread_obj.kernel_stack))
363 thread_val = GetLLDBThreadForKernelThread(thread_obj)
364 out_string = ""
365 kernel_stack = unsigned(thread_obj.kernel_stack)
366 reserved_stack = unsigned(thread_obj.reserved_stack)
367 if not is_continuation:
368 if kernel_stack and reserved_stack:
369 out_string += prefix + "reserved_stack = {:#018x}\n".format(reserved_stack)
370 out_string += prefix + "kernel_stack = {:#018x}\n".format(kernel_stack)
371 else:
372 out_string += prefix + "continuation ="
373 iteration = 0
374 last_frame_p = 0
375 for frame in thread_val.frames:
376 addr = frame.GetPCAddress()
377 load_addr = addr.GetLoadAddress(LazyTarget.GetTarget())
378 function = frame.GetFunction()
379 frame_p = frame.GetFP()
380 mod_name = frame.GetModule().GetFileSpec().GetFilename()
381
382 if iteration == 0 and not is_continuation:
383 out_string += prefix +"stacktop = {:#018x}\n".format(frame_p)
384
385 if not function:
386 # No debug info for 'function'.
387 out_string += prefix
388 if not is_continuation:
389 out_string += "{fp:#018x} ".format(fp = frame_p)
390
391 symbol = frame.GetSymbol()
392 if not symbol:
393 out_string += GetKextSymbolInfo(load_addr)
394 else:
395 file_addr = addr.GetFileAddress()
396 start_addr = symbol.GetStartAddress().GetFileAddress()
397 symbol_name = symbol.GetName()
398 symbol_offset = file_addr - start_addr
399 out_string += "{addr:#018x} {mod}`{symbol} + {offset:#x} \n".format(addr=load_addr,
400 mod=mod_name, symbol=symbol_name, offset=symbol_offset)
401 else:
402 # Debug info is available for 'function'.
403 func_name = frame.GetFunctionName()
404 file_name = frame.GetLineEntry().GetFileSpec().GetFilename()
405 line_num = frame.GetLineEntry().GetLine()
406 func_name = '%s [inlined]' % func_name if frame.IsInlined() else func_name
407 if is_continuation and frame.IsInlined():
408 debuglog("Skipping frame for thread {:#018x} since its inlined".format(thread_obj))
409 continue
410 out_string += prefix
411 if not is_continuation:
412 out_string += "{fp:#018x} ".format(fp=frame_p)
413 out_string += "{addr:#018x} {func}{args} \n".format(addr=load_addr,
414 func=func_name,
415 file=file_name, line=line_num,
416 args="(" + (str(frame.arguments).replace("\n", ", ") if len(frame.arguments) > 0 else "void") + ")")
417 iteration += 1
418 if frame_p:
419 last_frame_p = frame_p
420
421 if not is_continuation and last_frame_p:
422 out_string += prefix + "stackbottom = {:#018x}".format(last_frame_p)
423 out_string = out_string.replace("variable not available","")
424 return out_string
425
426 def GetSourceInformationForAddress(addr):
427 """ convert and address to function +offset information.
428 params: addr - int address in the binary to be symbolicated
429 returns: string of format "0xaddress: function + offset"
430 """
431 symbols = kern.SymbolicateFromAddress(addr)
432 format_string = "{0:#018x} <{1:s} + {2:#0x}>"
433 offset = 0
434 function_name = ""
435 if len(symbols) > 0:
436 s = symbols[0]
437 function_name = str(s.name)
438 offset = addr - s.GetStartAddress().GetLoadAddress(LazyTarget.GetTarget())
439 if function_name == "":
440 function_name = "???"
441 return format_string.format(addr, function_name, offset)
442
443 def GetFrameLocalVariable(variable_name, frame_no=0):
444 """ Find a local variable by name
445 params:
446 variable_name: str - name of variable to search for
447 returns:
448 core.value - if the variable is found.
449 None - if not found or not Valid
450 """
451 retval = None
452 sbval = None
453 lldb_SBThread = LazyTarget.GetProcess().GetSelectedThread()
454 frame = lldb_SBThread.GetSelectedFrame()
455 if frame_no :
456 frame = lldb_SBThread.GetFrameAtIndex(frame_no)
457 if frame :
458 sbval = frame.FindVariable(variable_name)
459 if sbval and sbval.IsValid():
460 retval = core.cvalue.value(sbval)
461 return retval
462
463 # Begin Macros for kernel debugging
464
465 @lldb_command('kgmhelp')
466 def KernelDebugCommandsHelp(cmd_args=None):
467 """ Show a list of registered commands for kenel debugging.
468 """
469 global lldb_command_documentation
470 print "List of commands provided by " + MODULE_NAME + " for kernel debugging."
471 cmds = lldb_command_documentation.keys()
472 cmds.sort()
473 for cmd in cmds:
474 if type(lldb_command_documentation[cmd][-1]) == type(""):
475 print " {0: <20s} - {1}".format(cmd , lldb_command_documentation[cmd][1].split("\n")[0].strip())
476 else:
477 print " {0: <20s} - {1}".format(cmd , "No help string found.")
478 print 'Each of the functions listed here accept the following common options. '
479 print COMMON_HELP_STRING
480 print 'Additionally, each command implementation may have more options. "(lldb) help <command> " will show these options.'
481 return None
482
483
484 @lldb_command('showraw')
485 def ShowRawCommand(cmd_args=None):
486 """ A command to disable the kernel summaries and show data as seen by the system.
487 This is useful when trying to read every field of a struct as compared to brief summary
488 """
489 command = " ".join(cmd_args)
490 lldb.debugger.HandleCommand('type category disable kernel' )
491 lldb.debugger.HandleCommand( command )
492 lldb.debugger.HandleCommand('type category enable kernel' )
493
494
495 @lldb_command('xnudebug')
496 def XnuDebugCommand(cmd_args=None):
497 """ command interface for operating on the xnu macros. Allowed commands are as follows
498 reload:
499 Reload a submodule from the xnu/tools/lldb directory. Do not include the ".py" suffix in modulename.
500 usage: xnudebug reload <modulename> (eg. memory, process, stats etc)
501 flushcache:
502 remove any cached data held in static or dynamic data cache.
503 usage: xnudebug flushcache
504 test:
505 Start running registered test with <name> from various modules.
506 usage: xnudebug test <name> (eg. test_memstats)
507 testall:
508 Go through all registered tests and run them
509 debug:
510 Toggle state of debug configuration flag.
511 """
512 global config
513 command_args = cmd_args
514 if len(command_args) == 0:
515 raise ArgumentError("No command specified.")
516 supported_subcommands = ['debug', 'reload', 'test', 'testall', 'flushcache']
517 subcommand = GetLongestMatchOption(command_args[0], supported_subcommands, True)
518
519 if len(subcommand) == 0:
520 raise ArgumentError("Subcommand (%s) is not a valid command. " % str(command_args[0]))
521
522 subcommand = subcommand[0].lower()
523 if subcommand == 'debug':
524 if command_args[-1].lower().find('dis') >=0 and config['debug']:
525 config['debug'] = False
526 print "Disabled debug logging."
527 elif command_args[-1].lower().find('dis') < 0 and not config['debug']:
528 config['debug'] = True
529 EnableLLDBAPILogging() # provided by utils.py
530 print "Enabled debug logging. \nPlease run 'xnudebug debug disable' to disable it again. "
531 if subcommand == 'flushcache':
532 print "Current size of cache: {}".format(caching.GetSizeOfCache())
533 caching.ClearAllCache()
534
535 if subcommand == 'reload':
536 module_name = command_args[-1]
537 if module_name in sys.modules:
538 reload(sys.modules[module_name])
539 print module_name + " is reloaded from " + sys.modules[module_name].__file__
540 else:
541 print "Unable to locate module named ", module_name
542 if subcommand == 'testall':
543 for test_name in lldb_command_tests.keys():
544 print "[BEGIN]", test_name
545 res = lldb_command_tests[test_name][2](kern, config, lldb, True)
546 if res:
547 print "[PASSED] {:s}".format(test_name)
548 else:
549 print "[FAILED] {:s}".format(test_name)
550 if subcommand == 'test':
551 test_name = command_args[-1]
552 if test_name in lldb_command_tests:
553 test = lldb_command_tests[test_name]
554 print "Running test {:s}".format(test[0])
555 if test[2](kern, config, lldb, True) :
556 print "[PASSED] {:s}".format(test[0])
557 else:
558 print "[FAILED] {:s}".format(test[0])
559 return ""
560 else:
561 print "No such test registered with name: {:s}".format(test_name)
562 print "XNUDEBUG Available tests are:"
563 for i in lldb_command_tests.keys():
564 print i
565 return None
566
567 return False
568
569 @lldb_command('showversion')
570 def ShowVersion(cmd_args=None):
571 """ Read the kernel version string from a fixed address in low
572 memory. Useful if you don't know which kernel is on the other end,
573 and need to find the appropriate symbols. Beware that if you've
574 loaded a symbol file, but aren't connected to a remote target,
575 the version string from the symbol file will be displayed instead.
576 This macro expects to be connected to the remote kernel to function
577 correctly.
578
579 """
580 print kern.version
581
582 def ProcessPanicStackshot(panic_stackshot_addr, panic_stackshot_len):
583 """ Process the panic stackshot from the panic header, saving it to a file if it is valid
584 params: panic_stackshot_addr : start address of the panic stackshot binary data
585 panic_stackshot_len : length of the stackshot binary data
586 returns: nothing
587 """
588 if not panic_stackshot_addr:
589 print "No panic stackshot available (invalid addr)"
590 return
591
592 if not panic_stackshot_len:
593 print "No panic stackshot available (zero length)"
594 return;
595
596 ts = int(time.time())
597 ss_binfile = "/tmp/panic_%d.bin" % ts
598 ss_ipsfile = "/tmp/stacks_%d.ips" % ts
599
600 if not SaveDataToFile(panic_stackshot_addr, panic_stackshot_len, ss_binfile, None):
601 print "Failed to save stackshot binary data to file"
602 return
603
604 self_path = str(__file__)
605 base_dir_name = self_path[:self_path.rfind("/")]
606 print "python %s/kcdata.py %s -s %s" % (base_dir_name, ss_binfile, ss_ipsfile)
607 (c,so,se) = RunShellCommand("python %s/kcdata.py %s -s %s" % (base_dir_name, ss_binfile, ss_ipsfile))
608 if c == 0:
609 print "Saved ips stackshot file as %s" % ss_ipsfile
610 return
611 else:
612 print "Failed to run command: exit code: %d, SO: %s SE: %s" % (c, so, se)
613 return
614
615 def ParseEmbeddedPanicLog(panic_header, cmd_options={}):
616 panic_buf = Cast(panic_header, 'char *')
617 panic_log_magic = unsigned(panic_header.eph_magic)
618 panic_log_begin_offset = unsigned(panic_header.eph_panic_log_offset)
619 panic_log_len = unsigned(panic_header.eph_panic_log_len)
620 other_log_begin_offset = unsigned(panic_header.eph_other_log_offset)
621 other_log_len = unsigned(panic_header.eph_other_log_len)
622 expected_panic_magic = xnudefines.EMBEDDED_PANIC_MAGIC
623 panic_stackshot_addr = unsigned(panic_header) + unsigned(panic_header.eph_stackshot_offset)
624 panic_stackshot_len = unsigned(panic_header.eph_stackshot_len)
625 panic_header_flags = unsigned(panic_header.eph_panic_flags)
626
627 warn_str = ""
628 out_str = ""
629
630 if panic_log_magic != 0 and panic_log_magic != expected_panic_magic:
631 warn_str += "BAD MAGIC! Found 0x%x expected 0x%x" % (panic_log_magic,
632 expected_panic_magic)
633
634 if warn_str:
635 print "\n %s" % warn_str
636 if panic_log_begin_offset == 0:
637 return
638
639 if "-S" in cmd_options:
640 if panic_header_flags & xnudefines.EMBEDDED_PANIC_STACKSHOT_SUCCEEDED_FLAG:
641 ProcessPanicStackshot(panic_stackshot_addr, panic_stackshot_len)
642 else:
643 print "No panic stackshot available"
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
650
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)])
655 out_str += p_char
656 other_log_curindex += 1
657
658 print out_str
659 return
660
661 def ParseMacOSPanicLog(panic_header, cmd_options={}):
662 panic_buf = Cast(panic_header, 'char *')
663 panic_log_magic = unsigned(panic_header.mph_magic)
664 panic_log_begin_offset = unsigned(panic_header.mph_panic_log_offset)
665 panic_log_len = unsigned(panic_header.mph_panic_log_len)
666 other_log_begin_offset = unsigned(panic_header.mph_other_log_offset)
667 other_log_len = unsigned(panic_header.mph_other_log_len)
668 cur_debug_buf_ptr_offset = (unsigned(kern.globals.debug_buf_ptr) - unsigned(panic_header))
669 if other_log_begin_offset != 0 and (other_log_len == 0 or other_log_len < (cur_debug_buf_ptr_offset - other_log_begin_offset)):
670 other_log_len = cur_debug_buf_ptr_offset - other_log_begin_offset
671 expected_panic_magic = xnudefines.MACOS_PANIC_MAGIC
672
673 # use the global if it's available (on an x86 corefile), otherwise refer to the header
674 if hasattr(kern.globals, "panic_stackshot_buf"):
675 panic_stackshot_addr = unsigned(kern.globals.panic_stackshot_buf)
676 panic_stackshot_len = unsigned(kern.globals.panic_stackshot_len)
677 else:
678 panic_stackshot_addr = unsigned(panic_header) + unsigned(panic_header.mph_stackshot_offset)
679 panic_stackshot_len = unsigned(panic_header.mph_stackshot_len)
680
681 panic_header_flags = unsigned(panic_header.mph_panic_flags)
682
683 warn_str = ""
684 out_str = ""
685
686 if panic_log_magic != 0 and panic_log_magic != expected_panic_magic:
687 warn_str += "BAD MAGIC! Found 0x%x expected 0x%x" % (panic_log_magic,
688 expected_panic_magic)
689
690 if warn_str:
691 print "\n %s" % warn_str
692 if panic_log_begin_offset == 0:
693 return
694
695 if "-S" in cmd_options:
696 if panic_header_flags & xnudefines.MACOS_PANIC_STACKSHOT_SUCCEEDED_FLAG:
697 ProcessPanicStackshot(panic_stackshot_addr, panic_stackshot_len)
698 else:
699 print "No panic stackshot available"
700
701 panic_log_curindex = 0
702 while panic_log_curindex < panic_log_len:
703 p_char = str(panic_buf[(panic_log_begin_offset + panic_log_curindex)])
704 out_str += p_char
705 panic_log_curindex += 1
706
707 if other_log_begin_offset != 0:
708 other_log_curindex = 0
709 while other_log_curindex < other_log_len:
710 p_char = str(panic_buf[(other_log_begin_offset + other_log_curindex)])
711 out_str += p_char
712 other_log_curindex += 1
713
714 print out_str
715 return
716
717 def ParseAURRPanicLog(panic_header, cmd_options={}):
718 reset_cause = {
719 0x0: "OTHER",
720 0x1: "CATERR",
721 0x2: "SWD_TIMEOUT",
722 0x3: "GLOBAL RESET",
723 0x4: "STRAIGHT TO S5",
724 }
725
726 expected_panic_magic = xnudefines.AURR_PANIC_MAGIC
727
728 panic_buf = Cast(panic_header, 'char *')
729
730 try:
731 # This line will blow up if there's not type info for this struct (older kernel)
732 # We fall back to manual parsing below
733 aurr_panic_header = Cast(panic_header, 'struct efi_aurr_panic_header *')
734 panic_log_magic = unsigned(aurr_panic_header.efi_aurr_magic)
735 panic_log_version = unsigned(aurr_panic_header.efi_aurr_version)
736 panic_log_reset_cause = unsigned(aurr_panic_header.efi_aurr_reset_cause)
737 panic_log_reset_log_offset = unsigned(aurr_panic_header.efi_aurr_reset_log_offset)
738 panic_log_reset_log_len = unsigned(aurr_panic_header.efi_aurr_reset_log_len)
739 except Exception as e:
740 print "*** Warning: kernel symbol file has no type information for 'struct efi_aurr_panic_header'..."
741 print "*** Warning: trying to manually parse..."
742 aurr_panic_header = Cast(panic_header, "uint32_t *")
743 panic_log_magic = unsigned(aurr_panic_header[0])
744 # panic_log_crc = unsigned(aurr_panic_header[1])
745 panic_log_version = unsigned(aurr_panic_header[2])
746 panic_log_reset_cause = unsigned(aurr_panic_header[3])
747 panic_log_reset_log_offset = unsigned(aurr_panic_header[4])
748 panic_log_reset_log_len = unsigned(aurr_panic_header[5])
749
750 if panic_log_magic != 0 and panic_log_magic != expected_panic_magic:
751 print "BAD MAGIC! Found 0x%x expected 0x%x" % (panic_log_magic,
752 expected_panic_magic)
753 return
754
755 print "AURR Panic Version: %d" % (panic_log_version)
756
757 # When it comes time to extend this in the future, please follow the
758 # construct used below in ShowPanicLog()
759 if panic_log_version in (xnudefines.AURR_PANIC_VERSION, xnudefines.AURR_CRASHLOG_PANIC_VERSION):
760 # AURR Report Version 1 (AURR/MacEFI) or 2 (Crashlog)
761 # see macefifirmware/Vendor/Apple/EfiPkg/AppleDebugSupport/Library/Debugger.h
762 print "Reset Cause: 0x%x (%s)" % (panic_log_reset_cause, reset_cause.get(panic_log_reset_cause, "UNKNOWN"))
763
764 # Adjust panic log string length (cap to maximum supported values)
765 if panic_log_version == xnudefines.AURR_PANIC_VERSION:
766 max_string_len = panic_log_reset_log_len
767 elif panic_log_version == xnudefines.AURR_CRASHLOG_PANIC_VERSION:
768 max_string_len = xnudefines.CRASHLOG_PANIC_STRING_LEN
769
770 panic_str_offset = 0
771 out_str = ""
772
773 while panic_str_offset < max_string_len:
774 p_char = str(panic_buf[panic_log_reset_log_offset + panic_str_offset])
775 out_str += p_char
776 panic_str_offset += 1
777
778 print out_str
779
780 # Save Crashlog Binary Data (if available)
781 if "-S" in cmd_options and panic_log_version == xnudefines.AURR_CRASHLOG_PANIC_VERSION:
782 crashlog_binary_offset = panic_log_reset_log_offset + xnudefines.CRASHLOG_PANIC_STRING_LEN
783 crashlog_binary_size = (panic_log_reset_log_len > xnudefines.CRASHLOG_PANIC_STRING_LEN) and (panic_log_reset_log_len - xnudefines.CRASHLOG_PANIC_STRING_LEN) or 0
784
785 if 0 == crashlog_binary_size:
786 print "No crashlog data found..."
787 return
788
789 # Save to file
790 ts = int(time.time())
791 ss_binfile = "/tmp/crashlog_%d.bin" % ts
792
793 if not SaveDataToFile(panic_buf + crashlog_binary_offset, crashlog_binary_size, ss_binfile, None):
794 print "Failed to save crashlog binary data to file"
795 return
796 else:
797 return ParseUnknownPanicLog(panic_header, cmd_options)
798
799 return
800
801 def ParseUnknownPanicLog(panic_header, cmd_options={}):
802 magic_ptr = Cast(panic_header, 'uint32_t *')
803 panic_log_magic = dereference(magic_ptr)
804 print "Unrecognized panic header format. Magic: 0x%x..." % unsigned(panic_log_magic)
805 print "Panic region starts at 0x%08x" % int(panic_header)
806 print "Hint: To dump this panic header in order to try manually parsing it, use this command:"
807 print " (lldb) memory read -fx -s4 -c64 0x%08x" % int(panic_header)
808 print " ^ that will dump the first 256 bytes of the panic region"
809 ## TBD: Hexdump some bits here to allow folks to poke at the region manually?
810 return
811
812
813 @lldb_command('paniclog', 'SM')
814 def ShowPanicLog(cmd_args=None, cmd_options={}):
815 """ Display the paniclog information
816 usage: (lldb) paniclog
817 options:
818 -v : increase verbosity
819 -S : parse stackshot data (if panic stackshot available)
820 -M : parse macOS panic area (print panic string (if available), and/or capture crashlog info)
821 """
822
823 if "-M" in cmd_options:
824 if not hasattr(kern.globals, "mac_panic_header"):
825 print "macOS panic data requested but unavailable on this device"
826 return
827 panic_header = kern.globals.mac_panic_header
828 # DEBUG HACK FOR TESTING
829 #panic_header = kern.GetValueFromAddress(0xfffffff054098000, "uint32_t *")
830 else:
831 panic_header = kern.globals.panic_info
832
833 if hasattr(panic_header, "eph_magic"):
834 panic_log_magic = unsigned(panic_header.eph_magic)
835 elif hasattr(panic_header, "mph_magic"):
836 panic_log_magic = unsigned(panic_header.mph_magic)
837 else:
838 print "*** Warning: unsure of panic header format, trying anyway"
839 magic_ptr = Cast(panic_header, 'uint32_t *')
840 panic_log_magic = int(dereference(magic_ptr))
841
842 if panic_log_magic == 0:
843 # No panic here..
844 return
845
846 panic_parsers = {
847 int(xnudefines.AURR_PANIC_MAGIC) : ParseAURRPanicLog,
848 int(xnudefines.MACOS_PANIC_MAGIC) : ParseMacOSPanicLog,
849 int(xnudefines.EMBEDDED_PANIC_MAGIC) : ParseEmbeddedPanicLog,
850 }
851
852 # Find the right parser (fall back to unknown parser above)
853 parser = panic_parsers.get(panic_log_magic, ParseUnknownPanicLog)
854
855 # execute it
856 return parser(panic_header, cmd_options)
857
858 @lldb_command('showbootargs')
859 def ShowBootArgs(cmd_args=None):
860 """ Display boot arguments passed to the target kernel
861 """
862 bootargs = Cast(kern.GetGlobalVariable('PE_state').bootArgs, 'boot_args *')
863 bootargs_cmd = bootargs.CommandLine
864 print str(bootargs_cmd)
865
866 @static_var("last_process_uniq_id", 1)
867 def GetDebuggerStopIDValue():
868 """ Create a unique session identifier.
869 returns:
870 int - a unique number identified by processid and stopid.
871 """
872 stop_id = 0
873 process_obj = LazyTarget.GetProcess()
874 if hasattr(process_obj, "GetStopID"):
875 stop_id = process_obj.GetStopID()
876 proc_uniq_id = 0
877 if hasattr(process_obj, 'GetUniqueID'):
878 proc_uniq_id = process_obj.GetUniqueID()
879 #FIXME <rdar://problem/13034329> forces us to do this twice
880 proc_uniq_id = process_obj.GetUniqueID()
881 else:
882 GetDebuggerStopIDValue.last_process_uniq_id +=1
883 proc_uniq_id = GetDebuggerStopIDValue.last_process_uniq_id + 1
884
885 stop_id_str = "{:d}:{:d}".format(proc_uniq_id, stop_id)
886 return hash(stop_id_str)
887
888 # The initialization code to add your commands
889 _xnu_framework_init = False
890 def __lldb_init_module(debugger, internal_dict):
891 global kern, lldb_command_documentation, config, _xnu_framework_init
892 if _xnu_framework_init:
893 return
894 _xnu_framework_init = True
895 caching._GetDebuggerSessionID = GetDebuggerStopIDValue
896 debugger.HandleCommand('type summary add --regex --summary-string "${var%s}" -C yes -p -v "char \[[0-9]*\]"')
897 debugger.HandleCommand('type format add --format hex -C yes uintptr_t')
898 kern = KernelTarget(debugger)
899 if not hasattr(lldb.SBValue, 'GetValueAsAddress'):
900 warn_str = "WARNING: lldb version is too old. Some commands may break. Please update to latest lldb."
901 if os.isatty(sys.__stdout__.fileno()):
902 warn_str = VT.DarkRed + warn_str + VT.Default
903 print warn_str
904 print "xnu debug macros loaded successfully. Run showlldbtypesummaries to enable type summaries."
905
906 __lldb_init_module(lldb.debugger, None)
907
908 @lldb_command("showlldbtypesummaries")
909 def ShowLLDBTypeSummaries(cmd_args=[]):
910 """ Enable/Disable kernel type summaries. Default is disabled.
911 Usage: showlldbtypesummaries [enable|disable]
912 default is enable
913 """
914 global config
915 action = "enable"
916 trailer_msg = ''
917 if len(cmd_args) > 0 and cmd_args[0].lower().find('disable') >=0:
918 action = "disable"
919 config['showTypeSummary'] = False
920 trailer_msg = "Please run 'showlldbtypesummaries enable' to enable the summary feature."
921 else:
922 config['showTypeSummary'] = True
923 SetupLLDBTypeSummaries(True)
924 trailer_msg = "Please run 'showlldbtypesummaries disable' to disable the summary feature."
925 lldb_run_command("type category "+ action +" kernel")
926 print "Successfully "+action+"d the kernel type summaries. %s" % trailer_msg
927
928 @lldb_command('walkqueue_head', 'S')
929 def WalkQueueHead(cmd_args=[], cmd_options={}):
930 """ walk a queue_head_t and list all members in it. Note this is for queue_head_t. refer to osfmk/kern/queue.h
931 Option: -S - suppress summary output.
932 Usage: (lldb) walkqueue_head <queue_entry *> <struct type> <fieldname>
933 ex: (lldb) walkqueue_head 0x7fffff80 "thread *" "task_threads"
934
935 """
936 global lldb_summary_definitions
937 if not cmd_args:
938 raise ArgumentError("invalid arguments")
939 if len(cmd_args) != 3:
940 raise ArgumentError("insufficient arguments")
941 queue_head = kern.GetValueFromAddress(cmd_args[0], 'struct queue_entry *')
942 el_type = cmd_args[1]
943 field_name = cmd_args[2]
944 showsummary = False
945 if el_type in lldb_summary_definitions:
946 showsummary = True
947 if '-S' in cmd_options:
948 showsummary = False
949
950 for i in IterateQueue(queue_head, el_type, field_name):
951 if showsummary:
952 print lldb_summary_definitions[el_type](i)
953 else:
954 print "{0: <#020x}".format(i)
955
956
957
958 @lldb_command('walklist_entry', 'SE')
959 def WalkList(cmd_args=[], cmd_options={}):
960 """ iterate over a list as defined with LIST_ENTRY in bsd/sys/queue.h
961 params:
962 object addr - value : address of object
963 element_type - str : Type of the next element
964 field_name - str : Name of the field in next element's structure
965
966 Options: -S - suppress summary output.
967 -E - Iterate using SLIST_ENTRYs
968
969 Usage: (lldb) walklist_entry <obj with list_entry *> <struct type> <fieldname>
970 ex: (lldb) walklist_entry 0x7fffff80 "struct proc *" "p_sibling"
971
972 """
973 global lldb_summary_definitions
974 if not cmd_args:
975 raise ArgumentError("invalid arguments")
976 if len(cmd_args) != 3:
977 raise ArgumentError("insufficient arguments")
978 el_type = cmd_args[1]
979 queue_head = kern.GetValueFromAddress(cmd_args[0], el_type)
980 field_name = cmd_args[2]
981 showsummary = False
982 if el_type in lldb_summary_definitions:
983 showsummary = True
984 if '-S' in cmd_options:
985 showsummary = False
986 if '-E' in cmd_options:
987 prefix = 's'
988 else:
989 prefix = ''
990 elt = queue_head
991 while unsigned(elt) != 0:
992 i = elt
993 elt = elt.__getattr__(field_name).__getattr__(prefix + 'le_next')
994 if showsummary:
995 print lldb_summary_definitions[el_type](i)
996 else:
997 print "{0: <#020x}".format(i)
998
999 def trace_parse_Copt(Copt):
1000 """Parses the -C option argument and returns a list of CPUs
1001 """
1002 cpusOpt = Copt
1003 cpuList = cpusOpt.split(",")
1004 chosen_cpus = []
1005 for cpu_num_string in cpuList:
1006 try:
1007 if '-' in cpu_num_string:
1008 parts = cpu_num_string.split('-')
1009 if len(parts) != 2 or not (parts[0].isdigit() and parts[1].isdigit()):
1010 raise ArgumentError("Invalid cpu specification: %s" % cpu_num_string)
1011 firstRange = int(parts[0])
1012 lastRange = int(parts[1])
1013 if firstRange >= kern.globals.real_ncpus or lastRange >= kern.globals.real_ncpus:
1014 raise ValueError()
1015 if lastRange < firstRange:
1016 raise ArgumentError("Invalid CPU range specified: `%s'" % cpu_num_string)
1017 for cpu_num in range(firstRange, lastRange + 1):
1018 if cpu_num not in chosen_cpus:
1019 chosen_cpus.append(cpu_num)
1020 else:
1021 chosen_cpu = int(cpu_num_string)
1022 if chosen_cpu < 0 or chosen_cpu >= kern.globals.real_ncpus:
1023 raise ValueError()
1024 if chosen_cpu not in chosen_cpus:
1025 chosen_cpus.append(chosen_cpu)
1026 except ValueError:
1027 raise ArgumentError("Invalid CPU number specified. Valid range is 0..%d" % (kern.globals.real_ncpus - 1))
1028
1029 return chosen_cpus
1030
1031
1032 IDX_CPU = 0
1033 IDX_RINGPOS = 1
1034 IDX_RINGENTRY = 2
1035 def Trace_cmd(cmd_args=[], cmd_options={}, headerString=lambda:"", entryString=lambda x:"", ring=[], entries_per_cpu=0, max_backtraces=0):
1036 """Generic trace dumper helper function
1037 """
1038
1039 if '-S' in cmd_options:
1040 field_arg = cmd_options['-S']
1041 try:
1042 getattr(ring[0][0], field_arg)
1043 sort_key_field_name = field_arg
1044 except AttributeError:
1045 raise ArgumentError("Invalid sort key field name `%s'" % field_arg)
1046 else:
1047 sort_key_field_name = 'start_time_abs'
1048
1049 if '-C' in cmd_options:
1050 chosen_cpus = trace_parse_Copt(cmd_options['-C'])
1051 else:
1052 chosen_cpus = [x for x in range(kern.globals.real_ncpus)]
1053
1054 try:
1055 limit_output_count = int(cmd_options['-N'])
1056 except ValueError:
1057 raise ArgumentError("Invalid output count `%s'" % cmd_options['-N']);
1058 except KeyError:
1059 limit_output_count = None
1060
1061 reverse_sort = '-R' in cmd_options
1062 backtraces = '-B' in cmd_options
1063
1064 # entries will be a list of 3-tuples, each holding the CPU on which the iotrace entry was collected,
1065 # the original ring index, and the iotrace entry.
1066 entries = []
1067 for x in chosen_cpus:
1068 ring_slice = [(x, y, ring[x][y]) for y in range(entries_per_cpu)]
1069 entries.extend(ring_slice)
1070
1071 total_entries = len(entries)
1072
1073 entries.sort(key=lambda x: getattr(x[IDX_RINGENTRY], sort_key_field_name), reverse=reverse_sort)
1074
1075 if limit_output_count is not None and limit_output_count > total_entries:
1076 print ("NOTE: Output count `%d' is too large; showing all %d entries" % (limit_output_count, total_entries));
1077 limit_output_count = total_entries
1078
1079 if len(chosen_cpus) < kern.globals.real_ncpus:
1080 print "NOTE: Limiting to entries from cpu%s %s" % ("s" if len(chosen_cpus) > 1 else "", str(chosen_cpus))
1081
1082 if limit_output_count is not None and limit_output_count < total_entries:
1083 entries_to_display = limit_output_count
1084 print "NOTE: Limiting to the %s" % ("first entry" if entries_to_display == 1 else ("first %d entries" % entries_to_display))
1085 else:
1086 entries_to_display = total_entries
1087
1088 print headerString()
1089
1090 for x in xrange(entries_to_display):
1091 print entryString(entries[x])
1092
1093 if backtraces:
1094 for btidx in range(max_backtraces):
1095 nextbt = entries[x][IDX_RINGENTRY].backtrace[btidx]
1096 if nextbt == 0:
1097 break
1098 print "\t" + GetSourceInformationForAddress(nextbt)
1099
1100
1101 @lldb_command('iotrace', 'C:N:S:RB')
1102 def IOTrace_cmd(cmd_args=[], cmd_options={}):
1103 """ Prints the iotrace ring buffers for all CPUs by default.
1104 Arguments:
1105 -B : Print backtraces for each ring entry
1106 -C <cpuSpec#>[,...,<cpuSpec#N>] : Limit trace entries to those generated by the specified CPUs (each cpuSpec can be a
1107 single CPU number or a range separated by a dash (e.g. "0-3"))
1108 -N <count> : Limit output to the first <count> entries (across all chosen CPUs)
1109 -R : Display results in reverse-sorted order (oldest first; default is newest-first)
1110 -S <sort_key_field_name> : Sort output by specified iotrace_entry_t field name (instead of by timestamp)
1111 """
1112 MAX_IOTRACE_BACKTRACES = 16
1113
1114 if kern.arch != "x86_64":
1115 print "Sorry, iotrace is an x86-only command."
1116 return
1117
1118 hdrString = lambda : "%-19s %-8s %-10s %-20s SZ %-18s %-17s DATA" % (
1119 "START TIME",
1120 "DURATION",
1121 "CPU#[RIDX]",
1122 " TYPE",
1123 " VIRT ADDR",
1124 " PHYS ADDR")
1125
1126 entryString = lambda x : "%-20u(%6u) %6s[%02d] %-20s %-2d 0x%016x 0x%016x 0x%x" % (
1127 x[IDX_RINGENTRY].start_time_abs,
1128 x[IDX_RINGENTRY].duration,
1129 "CPU%d" % x[IDX_CPU],
1130 x[IDX_RINGPOS],
1131 str(x[IDX_RINGENTRY].iotype).split("=")[1].strip(),
1132 x[IDX_RINGENTRY].size,
1133 x[IDX_RINGENTRY].vaddr,
1134 x[IDX_RINGENTRY].paddr,
1135 x[IDX_RINGENTRY].val)
1136
1137 Trace_cmd(cmd_args, cmd_options, hdrString, entryString, kern.globals.iotrace_ring, kern.globals.iotrace_entries_per_cpu, MAX_IOTRACE_BACKTRACES)
1138
1139
1140 @lldb_command('ttrace', 'C:N:S:RB')
1141 def TrapTrace_cmd(cmd_args=[], cmd_options={}):
1142 """ Prints the iotrace ring buffers for all CPUs by default.
1143 Arguments:
1144 -B : Print backtraces for each ring entry
1145 -C <cpuSpec#>[,...,<cpuSpec#N>] : Limit trace entries to those generated by the specified CPUs (each cpuSpec can be a
1146 single CPU number or a range separated by a dash (e.g. "0-3"))
1147 -N <count> : Limit output to the first <count> entries (across all chosen CPUs)
1148 -R : Display results in reverse-sorted order (oldest first; default is newest-first)
1149 -S <sort_key_field_name> : Sort output by specified traptrace_entry_t field name (instead of by timestamp)
1150 """
1151 MAX_TRAPTRACE_BACKTRACES = 8
1152
1153 if kern.arch != "x86_64":
1154 print "Sorry, ttrace is an x86-only command."
1155 return
1156
1157 hdrString = lambda : "%-30s CPU#[RIDX] VECT INTERRUPTED_THREAD PREMLV INTRLV INTERRUPTED_PC" % (
1158 "START TIME (DURATION [ns])")
1159 entryString = lambda x : "%-20u(%6s) %8s[%02d] 0x%02x 0x%016x %6d %6d %s" % (
1160 x[IDX_RINGENTRY].start_time_abs,
1161 str(x[IDX_RINGENTRY].duration) if hex(x[IDX_RINGENTRY].duration) != "0xffffffffffffffff" else 'inprog',
1162 "CPU%d" % x[IDX_CPU],
1163 x[IDX_RINGPOS],
1164 int(x[IDX_RINGENTRY].vector),
1165 x[IDX_RINGENTRY].curthread,
1166 x[IDX_RINGENTRY].curpl,
1167 x[IDX_RINGENTRY].curil,
1168 GetSourceInformationForAddress(x[IDX_RINGENTRY].interrupted_pc))
1169
1170 Trace_cmd(cmd_args, cmd_options, hdrString, entryString, kern.globals.traptrace_ring,
1171 kern.globals.traptrace_entries_per_cpu, MAX_TRAPTRACE_BACKTRACES)
1172
1173 # Yields an iterator over all the sysctls from the provided root.
1174 # Can optionally filter by the given prefix
1175 def IterateSysctls(root_oid=kern.globals.sysctl__children, prefix="", depth = 0, parent = ""):
1176 headp = root_oid
1177 for pp in IterateListEntry(headp, 'struct sysctl_oid *', 'oid_link', 's'):
1178 node_str = ""
1179 if prefix != "":
1180 node_str = str(pp.oid_name)
1181 if parent != "":
1182 node_str = parent + "." + node_str
1183 if node_str.startswith(prefix):
1184 yield pp, depth, parent
1185 else:
1186 yield pp, depth, parent
1187 type = pp.oid_kind & 0xf
1188 if type == 1 and pp.oid_arg1 != 0:
1189 if node_str == "":
1190 next_parent = str(pp.oid_name)
1191 if parent != "":
1192 next_parent = parent + "." + next_parent
1193 else:
1194 next_parent = node_str
1195 # Only recurse if the next parent starts with our allowed prefix.
1196 # Note that it's OK if the parent string is too short (because the prefix might be for a deeper node).
1197 prefix_len = min(len(prefix), len(next_parent))
1198 if next_parent[:prefix_len] == prefix[:prefix_len]:
1199 for x in IterateSysctls(Cast(pp.oid_arg1, "struct sysctl_oid_list *"), prefix, depth + 1, next_parent):
1200 yield x
1201
1202 @lldb_command('showsysctls', 'P:')
1203 def ShowSysctls(cmd_args=[], cmd_options={}):
1204 """ Walks the list of sysctl data structures, printing out each during traversal.
1205 Arguments:
1206 -P <string> : Limit output to sysctls starting with the specified prefix.
1207 """
1208 if '-P' in cmd_options:
1209 _ShowSysctl_prefix = cmd_options['-P']
1210 allowed_prefixes = _ShowSysctl_prefix.split('.')
1211 if allowed_prefixes:
1212 for x in xrange(1, len(allowed_prefixes)):
1213 allowed_prefixes[x] = allowed_prefixes[x - 1] + "." + allowed_prefixes[x]
1214 else:
1215 _ShowSysctl_prefix = ''
1216 allowed_prefixes = []
1217
1218 for sysctl, depth, parentstr in IterateSysctls(kern.globals.sysctl__children, _ShowSysctl_prefix):
1219 if parentstr == "":
1220 parentstr = "<none>"
1221 headp = sysctl
1222 st = (" " * depth * 2) + str(sysctl.GetSBValue().Dereference()).replace("\n", "\n" + (" " * depth * 2))
1223 print 'parent = "%s"' % parentstr, st[st.find("{"):]
1224
1225 @lldb_command('showexperiments', 'F')
1226 def ShowExperiments(cmd_args=[], cmd_options={}):
1227 """ Shows any active kernel experiments being run on the device via trial.
1228 Arguments:
1229 -F: Scan for changed experiment values even if no trial identifiers have been set.
1230 """
1231
1232 treatment_id = str(kern.globals.trial_treatment_id)
1233 experiment_id = str(kern.globals.trial_experiment_id)
1234 deployment_id = kern.globals.trial_deployment_id._GetValueAsSigned()
1235 if treatment_id == "" and experiment_id == "" and deployment_id == -1:
1236 print("Device is not enrolled in any kernel experiments.")
1237 if not '-F' in cmd_options:
1238 return
1239 else:
1240 print("""Device is enrolled in a kernel experiment:
1241 treatment_id: %s
1242 experiment_id: %s
1243 deployment_id: %d""" % (treatment_id, experiment_id, deployment_id))
1244
1245 print("Scanning sysctl tree for modified factors...")
1246
1247 kExperimentFactorFlag = 0x00100000
1248
1249 formats = {
1250 "IU": gettype("unsigned int *"),
1251 "I": gettype("int *"),
1252 "LU": gettype("unsigned long *"),
1253 "L": gettype("long *"),
1254 "QU": gettype("uint64_t *"),
1255 "Q": gettype("int64_t *")
1256 }
1257
1258 for sysctl, depth, parentstr in IterateSysctls(kern.globals.sysctl__children):
1259 if sysctl.oid_kind & kExperimentFactorFlag:
1260 spec = cast(sysctl.oid_arg1, "struct experiment_spec *")
1261 # Skip if arg2 isn't set to 1 (indicates an experiment factor created without an experiment_spec).
1262 if sysctl.oid_arg2 == 1:
1263 if spec.modified == 1:
1264 fmt = str(sysctl.oid_fmt)
1265 ptr = spec.ptr
1266 t = formats.get(fmt, None)
1267 if t:
1268 value = cast(ptr, t)
1269 else:
1270 # Unknown type
1271 continue
1272 name = str(parentstr) + "." + str(sysctl.oid_name)
1273 print("%s = %d (Default value is %d)" % (name, dereference(value), spec.original_value))
1274
1275 from memory import *
1276 from process import *
1277 from ipc import *
1278 from pmap import *
1279 from ioreg import *
1280 from mbufs import *
1281 from net import *
1282 from skywalk import *
1283 from kdp import *
1284 from userspace import *
1285 from pci import *
1286 from misc import *
1287 from apic import *
1288 from scheduler import *
1289 from structanalyze import *
1290 from ipcimportancedetail import *
1291 from bank import *
1292 from turnstile import *
1293 from kasan import *
1294 from kauth import *
1295 from waitq import *
1296 from usertaskgdbserver import *
1297 from ktrace import *
1298 from pgtrace import *
1299 from xnutriage import *
1300 from kevent import *
1301 from workqueue import *
1302 from ulock import *
1303 from ntstat import *
1304 from zonetriage import *
1305 from sysreg import *
1306 from counter import *