1import sys, subprocess, os, re, time, getopt, shlex, xnudefines 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 15MODULE_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 22COMMON_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 38def 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 44def 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. 52lldb_summary_definitions = {} 53def 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. 85lldb_command_documentation = {} 86 87def 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 ************ 133There has been an uncaught exception. A possible cause could be that remote connection has been disconnected. 134However, 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 184def 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 195def 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 211def 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 229def 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 249lldb_command_tests = {} 250def 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 271def 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 291kern = None 292 293def 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 317def 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 352def 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 426def 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 443def 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') 466def 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') 485def 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') 496def 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') 570def 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 582def 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 kcdata_cmd = "%s \"%s/kcdata.py\" \"%s\" -s \"%s\"" % (sys.executable, base_dir_name, ss_binfile, ss_ipsfile) 607 print kcdata_cmd 608 (c, so, se) = RunShellCommand(kcdata_cmd) 609 if c == 0: 610 print "Saved ips stackshot file as %s" % ss_ipsfile 611 return 612 else: 613 print "Failed to run command: exit code: %d, SO: %s SE: %s" % (c, so, se) 614 return 615 616def ParseEmbeddedPanicLog(panic_header, cmd_options={}): 617 panic_buf = Cast(panic_header, 'char *') 618 panic_log_magic = unsigned(panic_header.eph_magic) 619 panic_log_begin_offset = unsigned(panic_header.eph_panic_log_offset) 620 panic_log_len = unsigned(panic_header.eph_panic_log_len) 621 other_log_begin_offset = unsigned(panic_header.eph_other_log_offset) 622 other_log_len = unsigned(panic_header.eph_other_log_len) 623 expected_panic_magic = xnudefines.EMBEDDED_PANIC_MAGIC 624 panic_stackshot_addr = unsigned(panic_header) + unsigned(panic_header.eph_stackshot_offset) 625 panic_stackshot_len = unsigned(panic_header.eph_stackshot_len) 626 panic_header_flags = unsigned(panic_header.eph_panic_flags) 627 628 warn_str = "" 629 out_str = "" 630 631 if panic_log_magic != 0 and panic_log_magic != expected_panic_magic: 632 warn_str += "BAD MAGIC! Found 0x%x expected 0x%x" % (panic_log_magic, 633 expected_panic_magic) 634 635 if warn_str: 636 print "\n %s" % warn_str 637 if panic_log_begin_offset == 0: 638 return 639 640 if "-S" in cmd_options: 641 if panic_header_flags & xnudefines.EMBEDDED_PANIC_STACKSHOT_SUCCEEDED_FLAG: 642 ProcessPanicStackshot(panic_stackshot_addr, panic_stackshot_len) 643 else: 644 print "No panic stackshot available" 645 646 panic_log_curindex = 0 647 while panic_log_curindex < panic_log_len: 648 p_char = str(panic_buf[(panic_log_begin_offset + panic_log_curindex)]) 649 out_str += p_char 650 panic_log_curindex += 1 651 652 if other_log_begin_offset != 0: 653 other_log_curindex = 0 654 while other_log_curindex < other_log_len: 655 p_char = str(panic_buf[(other_log_begin_offset + other_log_curindex)]) 656 out_str += p_char 657 other_log_curindex += 1 658 659 print out_str 660 return 661 662def ParseMacOSPanicLog(panic_header, cmd_options={}): 663 panic_buf = Cast(panic_header, 'char *') 664 panic_log_magic = unsigned(panic_header.mph_magic) 665 panic_log_begin_offset = unsigned(panic_header.mph_panic_log_offset) 666 panic_log_len = unsigned(panic_header.mph_panic_log_len) 667 other_log_begin_offset = unsigned(panic_header.mph_other_log_offset) 668 other_log_len = unsigned(panic_header.mph_other_log_len) 669 cur_debug_buf_ptr_offset = (unsigned(kern.globals.debug_buf_ptr) - unsigned(panic_header)) 670 if other_log_begin_offset != 0 and (other_log_len == 0 or other_log_len < (cur_debug_buf_ptr_offset - other_log_begin_offset)): 671 other_log_len = cur_debug_buf_ptr_offset - other_log_begin_offset 672 expected_panic_magic = xnudefines.MACOS_PANIC_MAGIC 673 674 # use the global if it's available (on an x86 corefile), otherwise refer to the header 675 if hasattr(kern.globals, "panic_stackshot_buf"): 676 panic_stackshot_addr = unsigned(kern.globals.panic_stackshot_buf) 677 panic_stackshot_len = unsigned(kern.globals.panic_stackshot_len) 678 else: 679 panic_stackshot_addr = unsigned(panic_header) + unsigned(panic_header.mph_stackshot_offset) 680 panic_stackshot_len = unsigned(panic_header.mph_stackshot_len) 681 682 panic_header_flags = unsigned(panic_header.mph_panic_flags) 683 684 warn_str = "" 685 out_str = "" 686 687 if panic_log_magic != 0 and panic_log_magic != expected_panic_magic: 688 warn_str += "BAD MAGIC! Found 0x%x expected 0x%x" % (panic_log_magic, 689 expected_panic_magic) 690 691 if warn_str: 692 print "\n %s" % warn_str 693 if panic_log_begin_offset == 0: 694 return 695 696 if "-S" in cmd_options: 697 if panic_header_flags & xnudefines.MACOS_PANIC_STACKSHOT_SUCCEEDED_FLAG: 698 ProcessPanicStackshot(panic_stackshot_addr, panic_stackshot_len) 699 else: 700 print "No panic stackshot available" 701 702 panic_log_curindex = 0 703 while panic_log_curindex < panic_log_len: 704 p_char = str(panic_buf[(panic_log_begin_offset + panic_log_curindex)]) 705 out_str += p_char 706 panic_log_curindex += 1 707 708 if other_log_begin_offset != 0: 709 other_log_curindex = 0 710 while other_log_curindex < other_log_len: 711 p_char = str(panic_buf[(other_log_begin_offset + other_log_curindex)]) 712 out_str += p_char 713 other_log_curindex += 1 714 715 print out_str 716 return 717 718def ParseAURRPanicLog(panic_header, cmd_options={}): 719 reset_cause = { 720 0x0: "OTHER", 721 0x1: "CATERR", 722 0x2: "SWD_TIMEOUT", 723 0x3: "GLOBAL RESET", 724 0x4: "STRAIGHT TO S5", 725 } 726 727 expected_panic_magic = xnudefines.AURR_PANIC_MAGIC 728 729 panic_buf = Cast(panic_header, 'char *') 730 731 try: 732 # This line will blow up if there's not type info for this struct (older kernel) 733 # We fall back to manual parsing below 734 aurr_panic_header = Cast(panic_header, 'struct efi_aurr_panic_header *') 735 panic_log_magic = unsigned(aurr_panic_header.efi_aurr_magic) 736 panic_log_version = unsigned(aurr_panic_header.efi_aurr_version) 737 panic_log_reset_cause = unsigned(aurr_panic_header.efi_aurr_reset_cause) 738 panic_log_reset_log_offset = unsigned(aurr_panic_header.efi_aurr_reset_log_offset) 739 panic_log_reset_log_len = unsigned(aurr_panic_header.efi_aurr_reset_log_len) 740 except Exception as e: 741 print "*** Warning: kernel symbol file has no type information for 'struct efi_aurr_panic_header'..." 742 print "*** Warning: trying to manually parse..." 743 aurr_panic_header = Cast(panic_header, "uint32_t *") 744 panic_log_magic = unsigned(aurr_panic_header[0]) 745 # panic_log_crc = unsigned(aurr_panic_header[1]) 746 panic_log_version = unsigned(aurr_panic_header[2]) 747 panic_log_reset_cause = unsigned(aurr_panic_header[3]) 748 panic_log_reset_log_offset = unsigned(aurr_panic_header[4]) 749 panic_log_reset_log_len = unsigned(aurr_panic_header[5]) 750 751 if panic_log_magic != 0 and panic_log_magic != expected_panic_magic: 752 print "BAD MAGIC! Found 0x%x expected 0x%x" % (panic_log_magic, 753 expected_panic_magic) 754 return 755 756 print "AURR Panic Version: %d" % (panic_log_version) 757 758 # When it comes time to extend this in the future, please follow the 759 # construct used below in ShowPanicLog() 760 if panic_log_version in (xnudefines.AURR_PANIC_VERSION, xnudefines.AURR_CRASHLOG_PANIC_VERSION): 761 # AURR Report Version 1 (AURR/MacEFI) or 2 (Crashlog) 762 # see macefifirmware/Vendor/Apple/EfiPkg/AppleDebugSupport/Library/Debugger.h 763 print "Reset Cause: 0x%x (%s)" % (panic_log_reset_cause, reset_cause.get(panic_log_reset_cause, "UNKNOWN")) 764 765 # Adjust panic log string length (cap to maximum supported values) 766 if panic_log_version == xnudefines.AURR_PANIC_VERSION: 767 max_string_len = panic_log_reset_log_len 768 elif panic_log_version == xnudefines.AURR_CRASHLOG_PANIC_VERSION: 769 max_string_len = xnudefines.CRASHLOG_PANIC_STRING_LEN 770 771 panic_str_offset = 0 772 out_str = "" 773 774 while panic_str_offset < max_string_len: 775 p_char = str(panic_buf[panic_log_reset_log_offset + panic_str_offset]) 776 out_str += p_char 777 panic_str_offset += 1 778 779 print out_str 780 781 # Save Crashlog Binary Data (if available) 782 if "-S" in cmd_options and panic_log_version == xnudefines.AURR_CRASHLOG_PANIC_VERSION: 783 crashlog_binary_offset = panic_log_reset_log_offset + xnudefines.CRASHLOG_PANIC_STRING_LEN 784 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 785 786 if 0 == crashlog_binary_size: 787 print "No crashlog data found..." 788 return 789 790 # Save to file 791 ts = int(time.time()) 792 ss_binfile = "/tmp/crashlog_%d.bin" % ts 793 794 if not SaveDataToFile(panic_buf + crashlog_binary_offset, crashlog_binary_size, ss_binfile, None): 795 print "Failed to save crashlog binary data to file" 796 return 797 else: 798 return ParseUnknownPanicLog(panic_header, cmd_options) 799 800 return 801 802def ParseUnknownPanicLog(panic_header, cmd_options={}): 803 magic_ptr = Cast(panic_header, 'uint32_t *') 804 panic_log_magic = dereference(magic_ptr) 805 print "Unrecognized panic header format. Magic: 0x%x..." % unsigned(panic_log_magic) 806 print "Panic region starts at 0x%08x" % int(panic_header) 807 print "Hint: To dump this panic header in order to try manually parsing it, use this command:" 808 print " (lldb) memory read -fx -s4 -c64 0x%08x" % int(panic_header) 809 print " ^ that will dump the first 256 bytes of the panic region" 810 ## TBD: Hexdump some bits here to allow folks to poke at the region manually? 811 return 812 813 814@lldb_command('paniclog', 'SM') 815def ShowPanicLog(cmd_args=None, cmd_options={}): 816 """ Display the paniclog information 817 usage: (lldb) paniclog 818 options: 819 -v : increase verbosity 820 -S : parse stackshot data (if panic stackshot available) 821 -M : parse macOS panic area (print panic string (if available), and/or capture crashlog info) 822 """ 823 824 if "-M" in cmd_options: 825 if not hasattr(kern.globals, "mac_panic_header"): 826 print "macOS panic data requested but unavailable on this device" 827 return 828 panic_header = kern.globals.mac_panic_header 829 # DEBUG HACK FOR TESTING 830 #panic_header = kern.GetValueFromAddress(0xfffffff054098000, "uint32_t *") 831 else: 832 panic_header = kern.globals.panic_info 833 834 if hasattr(panic_header, "eph_magic"): 835 panic_log_magic = unsigned(panic_header.eph_magic) 836 elif hasattr(panic_header, "mph_magic"): 837 panic_log_magic = unsigned(panic_header.mph_magic) 838 else: 839 print "*** Warning: unsure of panic header format, trying anyway" 840 magic_ptr = Cast(panic_header, 'uint32_t *') 841 panic_log_magic = int(dereference(magic_ptr)) 842 843 if panic_log_magic == 0: 844 # No panic here.. 845 return 846 847 panic_parsers = { 848 int(xnudefines.AURR_PANIC_MAGIC) : ParseAURRPanicLog, 849 int(xnudefines.MACOS_PANIC_MAGIC) : ParseMacOSPanicLog, 850 int(xnudefines.EMBEDDED_PANIC_MAGIC) : ParseEmbeddedPanicLog, 851 } 852 853 # Find the right parser (fall back to unknown parser above) 854 parser = panic_parsers.get(panic_log_magic, ParseUnknownPanicLog) 855 856 # execute it 857 return parser(panic_header, cmd_options) 858 859@lldb_command('showbootargs') 860def ShowBootArgs(cmd_args=None): 861 """ Display boot arguments passed to the target kernel 862 """ 863 bootargs = Cast(kern.GetGlobalVariable('PE_state').bootArgs, 'boot_args *') 864 bootargs_cmd = bootargs.CommandLine 865 print str(bootargs_cmd) 866 867@static_var("last_process_uniq_id", 1) 868def GetDebuggerStopIDValue(): 869 """ Create a unique session identifier. 870 returns: 871 int - a unique number identified by processid and stopid. 872 """ 873 stop_id = 0 874 process_obj = LazyTarget.GetProcess() 875 if hasattr(process_obj, "GetStopID"): 876 stop_id = process_obj.GetStopID() 877 proc_uniq_id = 0 878 if hasattr(process_obj, 'GetUniqueID'): 879 proc_uniq_id = process_obj.GetUniqueID() 880 #FIXME <rdar://problem/13034329> forces us to do this twice 881 proc_uniq_id = process_obj.GetUniqueID() 882 else: 883 GetDebuggerStopIDValue.last_process_uniq_id +=1 884 proc_uniq_id = GetDebuggerStopIDValue.last_process_uniq_id + 1 885 886 stop_id_str = "{:d}:{:d}".format(proc_uniq_id, stop_id) 887 return hash(stop_id_str) 888 889# The initialization code to add your commands 890_xnu_framework_init = False 891def __lldb_init_module(debugger, internal_dict): 892 global kern, lldb_command_documentation, config, _xnu_framework_init 893 if _xnu_framework_init: 894 return 895 _xnu_framework_init = True 896 caching._GetDebuggerSessionID = GetDebuggerStopIDValue 897 debugger.HandleCommand('type summary add --regex --summary-string "${var%s}" -C yes -p -v "char \[[0-9]*\]"') 898 debugger.HandleCommand('type format add --format hex -C yes uintptr_t') 899 kern = KernelTarget(debugger) 900 if not hasattr(lldb.SBValue, 'GetValueAsAddress'): 901 warn_str = "WARNING: lldb version is too old. Some commands may break. Please update to latest lldb." 902 if os.isatty(sys.__stdout__.fileno()): 903 warn_str = VT.DarkRed + warn_str + VT.Default 904 print warn_str 905 print "xnu debug macros loaded successfully. Run showlldbtypesummaries to enable type summaries." 906 907__lldb_init_module(lldb.debugger, None) 908 909@lldb_command("showlldbtypesummaries") 910def ShowLLDBTypeSummaries(cmd_args=[]): 911 """ Enable/Disable kernel type summaries. Default is disabled. 912 Usage: showlldbtypesummaries [enable|disable] 913 default is enable 914 """ 915 global config 916 action = "enable" 917 trailer_msg = '' 918 if len(cmd_args) > 0 and cmd_args[0].lower().find('disable') >=0: 919 action = "disable" 920 config['showTypeSummary'] = False 921 trailer_msg = "Please run 'showlldbtypesummaries enable' to enable the summary feature." 922 else: 923 config['showTypeSummary'] = True 924 SetupLLDBTypeSummaries(True) 925 trailer_msg = "Please run 'showlldbtypesummaries disable' to disable the summary feature." 926 lldb_run_command("type category "+ action +" kernel") 927 print "Successfully "+action+"d the kernel type summaries. %s" % trailer_msg 928 929@lldb_command('walkqueue_head', 'S') 930def WalkQueueHead(cmd_args=[], cmd_options={}): 931 """ walk a queue_head_t and list all members in it. Note this is for queue_head_t. refer to osfmk/kern/queue.h 932 Option: -S - suppress summary output. 933 Usage: (lldb) walkqueue_head <queue_entry *> <struct type> <fieldname> 934 ex: (lldb) walkqueue_head 0x7fffff80 "thread *" "task_threads" 935 936 """ 937 global lldb_summary_definitions 938 if not cmd_args: 939 raise ArgumentError("invalid arguments") 940 if len(cmd_args) != 3: 941 raise ArgumentError("insufficient arguments") 942 queue_head = kern.GetValueFromAddress(cmd_args[0], 'struct queue_entry *') 943 el_type = cmd_args[1] 944 field_name = cmd_args[2] 945 showsummary = False 946 if el_type in lldb_summary_definitions: 947 showsummary = True 948 if '-S' in cmd_options: 949 showsummary = False 950 951 for i in IterateQueue(queue_head, el_type, field_name): 952 if showsummary: 953 print lldb_summary_definitions[el_type](i) 954 else: 955 print "{0: <#020x}".format(i) 956 957 958 959@lldb_command('walklist_entry', 'SE') 960def WalkList(cmd_args=[], cmd_options={}): 961 """ iterate over a list as defined with LIST_ENTRY in bsd/sys/queue.h 962 params: 963 object addr - value : address of object 964 element_type - str : Type of the next element 965 field_name - str : Name of the field in next element's structure 966 967 Options: -S - suppress summary output. 968 -E - Iterate using SLIST_ENTRYs 969 970 Usage: (lldb) walklist_entry <obj with list_entry *> <struct type> <fieldname> 971 ex: (lldb) walklist_entry 0x7fffff80 "struct proc *" "p_sibling" 972 973 """ 974 global lldb_summary_definitions 975 if not cmd_args: 976 raise ArgumentError("invalid arguments") 977 if len(cmd_args) != 3: 978 raise ArgumentError("insufficient arguments") 979 el_type = cmd_args[1] 980 queue_head = kern.GetValueFromAddress(cmd_args[0], el_type) 981 field_name = cmd_args[2] 982 showsummary = False 983 if el_type in lldb_summary_definitions: 984 showsummary = True 985 if '-S' in cmd_options: 986 showsummary = False 987 if '-E' in cmd_options: 988 prefix = 's' 989 else: 990 prefix = '' 991 elt = queue_head 992 while unsigned(elt) != 0: 993 i = elt 994 elt = elt.__getattr__(field_name).__getattr__(prefix + 'le_next') 995 if showsummary: 996 print lldb_summary_definitions[el_type](i) 997 else: 998 print "{0: <#020x}".format(i) 999 1000def trace_parse_Copt(Copt): 1001 """Parses the -C option argument and returns a list of CPUs 1002 """ 1003 cpusOpt = Copt 1004 cpuList = cpusOpt.split(",") 1005 chosen_cpus = [] 1006 for cpu_num_string in cpuList: 1007 try: 1008 if '-' in cpu_num_string: 1009 parts = cpu_num_string.split('-') 1010 if len(parts) != 2 or not (parts[0].isdigit() and parts[1].isdigit()): 1011 raise ArgumentError("Invalid cpu specification: %s" % cpu_num_string) 1012 firstRange = int(parts[0]) 1013 lastRange = int(parts[1]) 1014 if firstRange >= kern.globals.real_ncpus or lastRange >= kern.globals.real_ncpus: 1015 raise ValueError() 1016 if lastRange < firstRange: 1017 raise ArgumentError("Invalid CPU range specified: `%s'" % cpu_num_string) 1018 for cpu_num in range(firstRange, lastRange + 1): 1019 if cpu_num not in chosen_cpus: 1020 chosen_cpus.append(cpu_num) 1021 else: 1022 chosen_cpu = int(cpu_num_string) 1023 if chosen_cpu < 0 or chosen_cpu >= kern.globals.real_ncpus: 1024 raise ValueError() 1025 if chosen_cpu not in chosen_cpus: 1026 chosen_cpus.append(chosen_cpu) 1027 except ValueError: 1028 raise ArgumentError("Invalid CPU number specified. Valid range is 0..%d" % (kern.globals.real_ncpus - 1)) 1029 1030 return chosen_cpus 1031 1032 1033IDX_CPU = 0 1034IDX_RINGPOS = 1 1035IDX_RINGENTRY = 2 1036def Trace_cmd(cmd_args=[], cmd_options={}, headerString=lambda:"", entryString=lambda x:"", ring='', entries_per_cpu=0, max_backtraces=0): 1037 """Generic trace dumper helper function 1038 """ 1039 1040 if '-S' in cmd_options: 1041 field_arg = cmd_options['-S'] 1042 try: 1043 getattr(kern.PERCPU_GET(ring, 0)[0], field_arg) 1044 sort_key_field_name = field_arg 1045 except AttributeError: 1046 raise ArgumentError("Invalid sort key field name `%s'" % field_arg) 1047 else: 1048 sort_key_field_name = 'start_time_abs' 1049 1050 if '-C' in cmd_options: 1051 chosen_cpus = trace_parse_Copt(cmd_options['-C']) 1052 else: 1053 chosen_cpus = [x for x in range(kern.globals.real_ncpus)] 1054 1055 try: 1056 limit_output_count = int(cmd_options['-N']) 1057 except ValueError: 1058 raise ArgumentError("Invalid output count `%s'" % cmd_options['-N']); 1059 except KeyError: 1060 limit_output_count = None 1061 1062 reverse_sort = '-R' in cmd_options 1063 backtraces = '-B' in cmd_options 1064 1065 # entries will be a list of 3-tuples, each holding the CPU on which the iotrace entry was collected, 1066 # the original ring index, and the iotrace entry. 1067 entries = [] 1068 for x in chosen_cpus: 1069 ring_slice = [(x, y, kern.PERCPU_GET(ring, x)[y]) for y in range(entries_per_cpu)] 1070 entries.extend(ring_slice) 1071 1072 total_entries = len(entries) 1073 1074 entries.sort(key=lambda x: getattr(x[IDX_RINGENTRY], sort_key_field_name), reverse=reverse_sort) 1075 1076 if limit_output_count is not None and limit_output_count > total_entries: 1077 print ("NOTE: Output count `%d' is too large; showing all %d entries" % (limit_output_count, total_entries)); 1078 limit_output_count = total_entries 1079 1080 if len(chosen_cpus) < kern.globals.real_ncpus: 1081 print "NOTE: Limiting to entries from cpu%s %s" % ("s" if len(chosen_cpus) > 1 else "", str(chosen_cpus)) 1082 1083 if limit_output_count is not None and limit_output_count < total_entries: 1084 entries_to_display = limit_output_count 1085 print "NOTE: Limiting to the %s" % ("first entry" if entries_to_display == 1 else ("first %d entries" % entries_to_display)) 1086 else: 1087 entries_to_display = total_entries 1088 1089 print headerString() 1090 1091 for x in xrange(entries_to_display): 1092 print entryString(entries[x]) 1093 1094 if backtraces: 1095 for btidx in range(max_backtraces): 1096 nextbt = entries[x][IDX_RINGENTRY].backtrace[btidx] 1097 if nextbt == 0: 1098 break 1099 print "\t" + GetSourceInformationForAddress(nextbt) 1100 1101 1102@lldb_command('iotrace', 'C:N:S:RB') 1103def IOTrace_cmd(cmd_args=[], cmd_options={}): 1104 """ Prints the iotrace ring buffers for all CPUs by default. 1105 Arguments: 1106 -B : Print backtraces for each ring entry 1107 -C <cpuSpec#>[,...,<cpuSpec#N>] : Limit trace entries to those generated by the specified CPUs (each cpuSpec can be a 1108 single CPU number or a range separated by a dash (e.g. "0-3")) 1109 -N <count> : Limit output to the first <count> entries (across all chosen CPUs) 1110 -R : Display results in reverse-sorted order (oldest first; default is newest-first) 1111 -S <sort_key_field_name> : Sort output by specified iotrace_entry_t field name (instead of by timestamp) 1112 """ 1113 MAX_IOTRACE_BACKTRACES = 16 1114 1115 if kern.arch != "x86_64": 1116 print "Sorry, iotrace is an x86-only command." 1117 return 1118 1119 hdrString = lambda : "%-19s %-8s %-10s %-20s SZ %-18s %-17s DATA" % ( 1120 "START TIME", 1121 "DURATION", 1122 "CPU#[RIDX]", 1123 " TYPE", 1124 " VIRT ADDR", 1125 " PHYS ADDR") 1126 1127 entryString = lambda x : "%-20u(%6u) %6s[%02d] %-20s %-2d 0x%016x 0x%016x 0x%x" % ( 1128 x[IDX_RINGENTRY].start_time_abs, 1129 x[IDX_RINGENTRY].duration, 1130 "CPU%d" % x[IDX_CPU], 1131 x[IDX_RINGPOS], 1132 str(x[IDX_RINGENTRY].iotype).split("=")[1].strip(), 1133 x[IDX_RINGENTRY].size, 1134 x[IDX_RINGENTRY].vaddr, 1135 x[IDX_RINGENTRY].paddr, 1136 x[IDX_RINGENTRY].val) 1137 1138 Trace_cmd(cmd_args, cmd_options, hdrString, entryString, 'iotrace_ring', 1139 kern.globals.iotrace_entries_per_cpu, MAX_IOTRACE_BACKTRACES) 1140 1141 1142@lldb_command('ttrace', 'C:N:S:RB') 1143def TrapTrace_cmd(cmd_args=[], cmd_options={}): 1144 """ Prints the iotrace ring buffers for all CPUs by default. 1145 Arguments: 1146 -B : Print backtraces for each ring entry 1147 -C <cpuSpec#>[,...,<cpuSpec#N>] : Limit trace entries to those generated by the specified CPUs (each cpuSpec can be a 1148 single CPU number or a range separated by a dash (e.g. "0-3")) 1149 -N <count> : Limit output to the first <count> entries (across all chosen CPUs) 1150 -R : Display results in reverse-sorted order (oldest first; default is newest-first) 1151 -S <sort_key_field_name> : Sort output by specified traptrace_entry_t field name (instead of by timestamp) 1152 """ 1153 MAX_TRAPTRACE_BACKTRACES = 8 1154 1155 if kern.arch != "x86_64": 1156 print "Sorry, ttrace is an x86-only command." 1157 return 1158 1159 hdrString = lambda : "%-30s CPU#[RIDX] VECT INTERRUPTED_THREAD PREMLV INTRLV INTERRUPTED_PC" % ( 1160 "START TIME (DURATION [ns])") 1161 entryString = lambda x : "%-20u(%6s) %8s[%02d] 0x%02x 0x%016x %6d %6d %s" % ( 1162 x[IDX_RINGENTRY].start_time_abs, 1163 str(x[IDX_RINGENTRY].duration) if hex(x[IDX_RINGENTRY].duration) != "0xffffffffffffffff" else 'inprog', 1164 "CPU%d" % x[IDX_CPU], 1165 x[IDX_RINGPOS], 1166 int(x[IDX_RINGENTRY].vector), 1167 x[IDX_RINGENTRY].curthread, 1168 x[IDX_RINGENTRY].curpl, 1169 x[IDX_RINGENTRY].curil, 1170 GetSourceInformationForAddress(x[IDX_RINGENTRY].interrupted_pc)) 1171 1172 Trace_cmd(cmd_args, cmd_options, hdrString, entryString, 'traptrace_ring', 1173 kern.globals.traptrace_entries_per_cpu, MAX_TRAPTRACE_BACKTRACES) 1174 1175# Yields an iterator over all the sysctls from the provided root. 1176# Can optionally filter by the given prefix 1177def IterateSysctls(root_oid, prefix="", depth = 0, parent = ""): 1178 headp = root_oid 1179 for pp in IterateListEntry(headp, 'struct sysctl_oid *', 'oid_link', 's'): 1180 node_str = "" 1181 if prefix != "": 1182 node_str = str(pp.oid_name) 1183 if parent != "": 1184 node_str = parent + "." + node_str 1185 if node_str.startswith(prefix): 1186 yield pp, depth, parent 1187 else: 1188 yield pp, depth, parent 1189 type = pp.oid_kind & 0xf 1190 if type == 1 and pp.oid_arg1 != 0: 1191 if node_str == "": 1192 next_parent = str(pp.oid_name) 1193 if parent != "": 1194 next_parent = parent + "." + next_parent 1195 else: 1196 next_parent = node_str 1197 # Only recurse if the next parent starts with our allowed prefix. 1198 # Note that it's OK if the parent string is too short (because the prefix might be for a deeper node). 1199 prefix_len = min(len(prefix), len(next_parent)) 1200 if next_parent[:prefix_len] == prefix[:prefix_len]: 1201 for x in IterateSysctls(Cast(pp.oid_arg1, "struct sysctl_oid_list *"), prefix, depth + 1, next_parent): 1202 yield x 1203 1204@lldb_command('showsysctls', 'P:') 1205def ShowSysctls(cmd_args=[], cmd_options={}): 1206 """ Walks the list of sysctl data structures, printing out each during traversal. 1207 Arguments: 1208 -P <string> : Limit output to sysctls starting with the specified prefix. 1209 """ 1210 if '-P' in cmd_options: 1211 _ShowSysctl_prefix = cmd_options['-P'] 1212 allowed_prefixes = _ShowSysctl_prefix.split('.') 1213 if allowed_prefixes: 1214 for x in xrange(1, len(allowed_prefixes)): 1215 allowed_prefixes[x] = allowed_prefixes[x - 1] + "." + allowed_prefixes[x] 1216 else: 1217 _ShowSysctl_prefix = '' 1218 allowed_prefixes = [] 1219 1220 for sysctl, depth, parentstr in IterateSysctls(kern.globals.sysctl__children, _ShowSysctl_prefix): 1221 if parentstr == "": 1222 parentstr = "<none>" 1223 headp = sysctl 1224 st = (" " * depth * 2) + str(sysctl.GetSBValue().Dereference()).replace("\n", "\n" + (" " * depth * 2)) 1225 print 'parent = "%s"' % parentstr, st[st.find("{"):] 1226 1227@lldb_command('showexperiments', 'F') 1228def ShowExperiments(cmd_args=[], cmd_options={}): 1229 """ Shows any active kernel experiments being run on the device via trial. 1230 Arguments: 1231 -F: Scan for changed experiment values even if no trial identifiers have been set. 1232 """ 1233 1234 treatment_id = str(kern.globals.trial_treatment_id) 1235 experiment_id = str(kern.globals.trial_experiment_id) 1236 deployment_id = kern.globals.trial_deployment_id._GetValueAsSigned() 1237 if treatment_id == "" and experiment_id == "" and deployment_id == -1: 1238 print("Device is not enrolled in any kernel experiments.") 1239 if not '-F' in cmd_options: 1240 return 1241 else: 1242 print("""Device is enrolled in a kernel experiment: 1243 treatment_id: %s 1244 experiment_id: %s 1245 deployment_id: %d""" % (treatment_id, experiment_id, deployment_id)) 1246 1247 print("Scanning sysctl tree for modified factors...") 1248 1249 kExperimentFactorFlag = 0x00100000 1250 1251 formats = { 1252 "IU": gettype("unsigned int *"), 1253 "I": gettype("int *"), 1254 "LU": gettype("unsigned long *"), 1255 "L": gettype("long *"), 1256 "QU": gettype("uint64_t *"), 1257 "Q": gettype("int64_t *") 1258 } 1259 1260 for sysctl, depth, parentstr in IterateSysctls(kern.globals.sysctl__children): 1261 if sysctl.oid_kind & kExperimentFactorFlag: 1262 spec = cast(sysctl.oid_arg1, "struct experiment_spec *") 1263 # Skip if arg2 isn't set to 1 (indicates an experiment factor created without an experiment_spec). 1264 if sysctl.oid_arg2 == 1: 1265 if spec.modified == 1: 1266 fmt = str(sysctl.oid_fmt) 1267 ptr = spec.ptr 1268 t = formats.get(fmt, None) 1269 if t: 1270 value = cast(ptr, t) 1271 else: 1272 # Unknown type 1273 continue 1274 name = str(parentstr) + "." + str(sysctl.oid_name) 1275 print("%s = %d (Default value is %d)" % (name, dereference(value), spec.original_value)) 1276 1277from memory import * 1278from process import * 1279from ipc import * 1280from pmap import * 1281from ioreg import * 1282from mbufs import * 1283from net import * 1284from skywalk import * 1285from kdp import * 1286from userspace import * 1287from pci import * 1288from misc import * 1289from apic import * 1290from scheduler import * 1291from structanalyze import * 1292from ipcimportancedetail import * 1293from bank import * 1294from turnstile import * 1295from kasan import * 1296from kauth import * 1297from waitq import * 1298from usertaskgdbserver import * 1299from ktrace import * 1300from xnutriage import * 1301from kevent import * 1302from workqueue import * 1303from ulock import * 1304from ntstat import * 1305from zonetriage import * 1306from sysreg import * 1307from counter import * 1308from btlog import * 1309from refgrp import * 1310