]> git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/xnu.py
xnu-4570.71.2.tar.gz
[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 -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 """
31 # End Utility functions
32 # Debugging specific utility functions
33
34 #decorators. Not to be called directly.
35
36 def 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
42 def 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.
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.
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.
83 lldb_command_documentation = {}
84
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.
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
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)
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:
111 config['verbosity'] += stream.verbose_level
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 ************
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. ************
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
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)
166 return obj
167 return _cmd
168
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"
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
180 def 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
196 def 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
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)
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
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 )
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
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
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
276 kern = None
277
278 def 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():
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
300 return sbthread
301
302 def 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
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)
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
337 def 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)
369
370 if not function:
371 # No debug info for 'function'.
372 out_string += prefix
373 if not is_continuation:
374 out_string += "{fp:#018x} ".format(fp = frame_p)
375
376 symbol = frame.GetSymbol()
377 if not symbol:
378 out_string += GetKextSymbolInfo(load_addr)
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)
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
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"
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
428 def 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')
451 def 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.")
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.'
466 return None
467
468
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
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')
481 def 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)
486 flushcache:
487 remove any cached data held in static or dynamic data cache.
488 usage: xnudebug flushcache
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.")
501 supported_subcommands = ['debug', 'reload', 'test', 'testall', 'flushcache']
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]))
506
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()
519
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')
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
562 correctly.
563
564 """
565 print kern.version
566
567
568 @lldb_command('paniclog', 'S')
569 def ShowPanicLog(cmd_args=None, cmd_options={}):
570 """ Display the paniclog information
571 usage: (lldb) paniclog
572 options:
573 -v : increase verbosity
574 -S : parse stackshot data (if panic stackshot available)
575 """
576
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
585 else:
586 print "No panic stackshot available"
587 else:
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)
607 else:
608 print "kc_panic_data is unavailable for this kernel config."
609
610 out_str = ""
611 warn_str = ""
612
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
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)
631 expected_panic_magic = xnudefines.EMBEDDED_PANIC_MAGIC
632
633 if panic_log_begin_offset == 0:
634 return
635
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)
639
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
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
660 if warn_str:
661 print "\n %s" % warn_str
662
663 return
664
665 @lldb_command('showbootargs')
666 def 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)
674 def 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
697 def __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")
711 def 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
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"
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')
761 def 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
798 from memory import *
799 from process import *
800 from ipc import *
801 from pmap import *
802 from ioreg import *
803 from mbufs import *
804 from net import *
805 from kdp import *
806 from userspace import *
807 from pci import *
808 from misc import *
809 from apic import *
810 from scheduler import *
811 from atm import *
812 from structanalyze import *
813 from ipcimportancedetail import *
814 from bank import *
815 from kasan import *
816 from kauth import *
817 from waitq import *
818 from usertaskgdbserver import *
819 from ktrace import *
820 from pgtrace import *
821 from xnutriage import *
822 from kevent import *
823 from ntstat import *
824 from zonetriage import *
825