]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/lldbmacros/xnu.py
xnu-4570.31.3.tar.gz
[apple/xnu.git] / tools / lldbmacros / xnu.py
old mode 100644 (file)
new mode 100755 (executable)
index d72c3ae..8b5d5bb
@@ -1,4 +1,4 @@
-import sys, subprocess, os, re, time, getopt, shlex
+import sys, subprocess, os, re, time, getopt, shlex, xnudefines
 import lldb
 from functools import wraps
 from ctypes import c_ulonglong as uint64_t
@@ -12,15 +12,24 @@ from core.kernelcore import *
 from utils import *
 from core.lazytarget import *
 
-MODULE_NAME=__name__ 
+MODULE_NAME=__name__
 
 """ Kernel Debugging macros for lldb.
     Please make sure you read the README COMPLETELY BEFORE reading anything below.
-    It is very critical that you read coding guidelines in Section E in README file. 
+    It is very critical that you read coding guidelines in Section E in README file.
 """
 
+COMMON_HELP_STRING = """
+    -h  Show the help string for the command.
+    -o <path/to/filename>   The output of this command execution will be saved to file. Parser information or errors will
+                            not be sent to file though. eg /tmp/output.txt
+    -s <filter_string>      The "filter_string" param is parsed to python regex expression and each line of output
+                            will be printed/saved only if it matches the expression.
+    -v [-v...]  Each additional -v will increase the verbosity of the command.
+    -p <plugin_name>        Send the output of the command to plugin. Please see README for usage of plugins.
+"""
 # End Utility functions
-# Debugging specific utility functions 
+# Debugging specific utility functions
 
 #decorators. Not to be called directly.
 
@@ -85,7 +94,7 @@ def lldb_command(cmd_name, option_string = ''):
     def _cmd(obj):
         def _internal_command_function(debugger, command, result, internal_dict):
             global config, lldb_run_command_state
-            stream = CommandOutput(result)
+            stream = CommandOutput(cmd_name, result)
             # need to avoid printing on stdout if called from lldb_run_command.
             if 'active' in lldb_run_command_state and lldb_run_command_state['active']:
                 debuglog('Running %s from lldb_run_command' % command)
@@ -99,7 +108,7 @@ def lldb_command(cmd_name, option_string = ''):
             try:
                 stream.setOptions(command_args, option_string)
                 if stream.verbose_level != 0:
-                    config['verbosity'] = stream.verbose_level 
+                    config['verbosity'] +=  stream.verbose_level 
                 with RedirectStdStreams(stdout=stream) :
                     if option_string:
                         obj(cmd_args=stream.target_cmd_args, cmd_options=stream.target_cmd_options)
@@ -147,6 +156,7 @@ However, it is recommended that you report the exception to lldb/kernel debuggin
         if not obj.__doc__ :
             print "ERROR: Cannot register command({:s}) without documentation".format(cmd_name)
             return obj
+        obj.__doc__ += "\n" + COMMON_HELP_STRING
         command_function.__doc__ = obj.__doc__
         global lldb_command_documentation
         if cmd_name in lldb_command_documentation:
@@ -285,10 +295,45 @@ def GetLLDBThreadForKernelThread(thread_obj):
         sbthread = lldb_process.GetThreadByID(tid)
 
     if not sbthread.IsValid():
-        raise RuntimeError("Unable to find lldb thread for tid={0:d} thread = {1:#018x}".format(tid, thread_obj))
-    
+        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))
+
     return sbthread
 
+def GetKextSymbolInfo(load_addr):
+    """ Get a string descriptiong load_addr <kextname> + offset
+        params:
+            load_addr - int address value of pc in backtrace.
+        returns: str - kext name + offset string. If no cached data available, warning message is returned.
+    """
+    symbol_name = "None"
+    symbol_offset = load_addr
+    kmod_val = kern.globals.kmod
+    if not kern.arch.startswith('arm64'):
+        for kval in IterateLinkedList(kmod_val, 'next'):
+            if load_addr >= unsigned(kval.address) and \
+                load_addr <= (unsigned(kval.address) + unsigned(kval.size)):
+                symbol_name = kval.name
+                symbol_offset = load_addr - unsigned(kval.address)
+                break
+        return "{:#018x} {:s} + {:#x} \n".format(load_addr, symbol_name, symbol_offset)
+
+    # only for arm64 we do lookup for split kexts.
+    cached_kext_info = caching.GetDynamicCacheData("kern.kexts.loadinformation", [])
+    if not cached_kext_info and str(GetConnectionProtocol()) == "core":
+        cached_kext_info = GetKextLoadInformation()
+
+    if not cached_kext_info:
+        return "{:#018x} ~ kext info not available. please run 'showallkexts' once ~ \n".format(load_addr)
+
+    for kval in cached_kext_info:
+        text_seg = kval[5]
+        if load_addr >= text_seg.vmaddr and \
+            load_addr <= (text_seg.vmaddr + text_seg.vmsize):
+            symbol_name = kval[2]
+            symbol_offset = load_addr - text_seg.vmaddr
+            break
+    return "{:#018x} {:s} + {:#x} \n".format(load_addr, symbol_name, symbol_offset)
+
 def GetThreadBackTrace(thread_obj, verbosity = vHUMAN, prefix = ""):
     """ Get a string to display back trace for a thread.
         params:
@@ -321,18 +366,23 @@ def GetThreadBackTrace(thread_obj, verbosity = vHUMAN, prefix = ""):
 
         if iteration == 0 and not is_continuation:
             out_string += prefix +"stacktop = {:#018x}\n".format(frame_p)
-        
+
         if not function:
             # No debug info for 'function'.
-            symbol = frame.GetSymbol()
-            file_addr = addr.GetFileAddress()
-            start_addr = symbol.GetStartAddress().GetFileAddress()
-            symbol_name = symbol.GetName()
-            symbol_offset = file_addr - start_addr
             out_string += prefix 
             if not is_continuation:
                 out_string += "{fp:#018x} ".format(fp = frame_p) 
-            out_string += "{addr:#018x} {mod}`{symbol} + {offset} \n".format(addr=load_addr, mod=mod_name, symbol=symbol_name, offset=symbol_offset)
+            
+            symbol = frame.GetSymbol()
+            if not symbol:
+                out_string += GetKextSymbolInfo(load_addr)
+            else:
+                file_addr = addr.GetFileAddress()
+                start_addr = symbol.GetStartAddress().GetFileAddress()
+                symbol_name = symbol.GetName()
+                symbol_offset = file_addr - start_addr
+                out_string += "{addr:#018x} {mod}`{symbol} + {offset:#x} \n".format(addr=load_addr, 
+                    mod=mod_name, symbol=symbol_name, offset=symbol_offset)
         else:
             # Debug info is available for 'function'.
             func_name = frame.GetFunctionName()
@@ -410,18 +460,9 @@ def KernelDebugCommandsHelp(cmd_args=None):
             print " {0: <20s} - {1}".format(cmd , lldb_command_documentation[cmd][1].split("\n")[0].strip())
         else:
             print " {0: <20s} - {1}".format(cmd , "No help string found.")
-    print """
-    Each of the functions listed here accept the following common options. 
-        -h  Show the help string for the command.
-        -o <path/to/filename>   The output of this command execution will be saved to file. Parser information or errors will 
-                                not be sent to file though. eg /tmp/output.txt
-        -s <filter_string>      The "filter_string" param is parsed to python regex expression and each line of output 
-                                will be printed/saved only if it matches the expression. 
-        -v [-v...]  Each additional -v will increase the verbosity of the command.
-        -p <plugin_name>        Send the output of the command to plugin. Please see README for usage of plugins.
-
-    Additionally, each command implementation may have more options. "(lldb) help <command> " will show these options.
-    """
+    print 'Each of the functions listed here accept the following common options. '
+    print COMMON_HELP_STRING
+    print 'Additionally, each command implementation may have more options. "(lldb) help <command> " will show these options.'
     return None
 
 
@@ -442,6 +483,9 @@ def XnuDebugCommand(cmd_args=None):
         reload:
             Reload a submodule from the xnu/tools/lldb directory. Do not include the ".py" suffix in modulename.
             usage: xnudebug reload <modulename> (eg. memory, process, stats etc)
+        flushcache:
+            remove any cached data held in static or dynamic data cache.
+            usage: xnudebug flushcache
         test:
             Start running registered test with <name> from various modules.
             usage: xnudebug test <name> (eg. test_memstats)
@@ -454,12 +498,12 @@ def XnuDebugCommand(cmd_args=None):
     command_args = cmd_args
     if len(command_args) == 0:
         raise ArgumentError("No command specified.")
-    supported_subcommands = ['debug', 'reload', 'test', 'testall']
+    supported_subcommands = ['debug', 'reload', 'test', 'testall', 'flushcache']
     subcommand = GetLongestMatchOption(command_args[0], supported_subcommands, True)
 
     if len(subcommand) == 0:
         raise ArgumentError("Subcommand (%s) is not a valid command. " % str(command_args[0]))
-    
+
     subcommand = subcommand[0].lower()
     if subcommand == 'debug':
         if command_args[-1].lower().find('dis') >=0 and config['debug']:
@@ -469,7 +513,10 @@ def XnuDebugCommand(cmd_args=None):
             config['debug'] = True
             EnableLLDBAPILogging()  # provided by utils.py
             print "Enabled debug logging. \nPlease run 'xnudebug debug disable' to disable it again. "
+    if subcommand == 'flushcache':
+        print "Current size of cache: {}".format(caching.GetSizeOfCache())
+        caching.ClearAllCache()
+
     if subcommand == 'reload':
         module_name = command_args[-1]
         if module_name in sys.modules:
@@ -518,32 +565,101 @@ def ShowVersion(cmd_args=None):
     print kern.version
 
 
-@lldb_command('paniclog')
-def ShowPanicLog(cmd_args=None):
+@lldb_command('paniclog', 'S')
+def ShowPanicLog(cmd_args=None, cmd_options={}):
     """ Display the paniclog information
+        usage: (lldb) paniclog
+        options:
+            -v : increase verbosity
+            -S : parse stackshot data (if panic stackshot available)
     """
-    panic_buf = kern.globals.debug_buf
-    panic_buf_start = addressof(panic_buf)
-    panic_buf_end = unsigned(kern.globals.debug_buf_ptr)
-    num_bytes = panic_buf_end - panic_buf_start
-    if num_bytes == 0 :
-        return
-    panic_data = panic_buf.GetSBValue().GetData()
-    err = lldb.SBError()
-    line = ''
-    for i in range(0, num_bytes):
-        c = panic_data.GetUnsignedInt8(err, i)
-        if chr(c) == '\n':
-            if line =='':
-                line = " "
-            print line 
-            line = ''
+
+    if "-S" in cmd_options:
+        if hasattr(kern.globals, "kc_panic_data"):
+            stackshot_saved = False
+            # TODO: Update logic to handle "in-memory" panic stackshot on Gibraltar platforms
+            #       once we drop support for the on disk one there.
+            if kern.arch == 'x86_64':
+                if kern.globals.panic_stackshot_len != 0:
+                    stackshot_saved = True
+                else:
+                    print "No panic stackshot available"
+            else:
+                if unsigned(kern.globals.panic_info.eph_panic_flags) & xnudefines.EMBEDDED_PANIC_STACKSHOT_SUCCEEDED_FLAG:
+                    stackshot_saved = True
+                else:
+                    print "No panic stackshot available"
+            if stackshot_saved:
+                kc_data = unsigned(addressof(kern.globals.kc_panic_data))
+                ts = int(time.time())
+                ss_binfile = "/tmp/panic_%d.bin" % ts
+                ss_ipsfile = "/tmp/stacks_%d.ips" % ts
+                print "savekcdata  0x%x -O %s" % (kc_data, ss_binfile)
+                SaveKCDataToFile(["0x%x" % kc_data], {"-O":ss_binfile})
+                self_path = str(__file__)
+                base_dir_name = self_path[:self_path.rfind("/")]
+                print "python %s/kcdata.py %s -s %s" % (base_dir_name, ss_binfile, ss_ipsfile)
+                (c,so,se) = RunShellCommand("python %s/kcdata.py %s -s %s" % (base_dir_name, ss_binfile, ss_ipsfile))
+                if c == 0:
+                    print "Saved ips stackshot file as %s" % ss_ipsfile
+                else:
+                    print "Failed to run command: exit code: %d, SO: %s SE: %s" % (c, so, se)
         else:
-            line += chr(c)
-    
-    if len(line) > 0: 
-        print line
-    
+            print "kc_panic_data is unavailable for this kernel config."
+
+    out_str = ""
+    warn_str = ""
+
+    if kern.arch == 'x86_64':
+        panic_buf = Cast(kern.globals.panic_info, 'char *')
+        panic_log_magic = unsigned(kern.globals.panic_info.mph_magic)
+        panic_log_begin_offset = unsigned(kern.globals.panic_info.mph_panic_log_offset)
+        panic_log_len = unsigned(kern.globals.panic_info.mph_panic_log_len)
+        other_log_begin_offset = unsigned(kern.globals.panic_info.mph_other_log_offset)
+        other_log_len = unsigned(kern.globals.panic_info.mph_other_log_len)
+        cur_debug_buf_ptr_offset = (unsigned(kern.globals.debug_buf_ptr) - unsigned(kern.globals.panic_info))
+        if other_log_begin_offset != 0 and (other_log_len == 0 or other_log_len < (cur_debug_buf_ptr_offset - other_log_begin_offset)):
+            other_log_len = cur_debug_buf_ptr_offset - other_log_begin_offset
+        expected_panic_magic = xnudefines.MACOS_PANIC_MAGIC
+    else:
+        panic_buf = Cast(kern.globals.panic_info, 'char *')
+        panic_log_magic = unsigned(kern.globals.panic_info.eph_magic)
+        panic_log_begin_offset = unsigned(kern.globals.panic_info.eph_panic_log_offset)
+        panic_log_len = unsigned(kern.globals.panic_info.eph_panic_log_len)
+        other_log_begin_offset = unsigned(kern.globals.panic_info.eph_other_log_offset)
+        other_log_len = unsigned(kern.globals.panic_info.eph_other_log_len)
+        expected_panic_magic = xnudefines.EMBEDDED_PANIC_MAGIC
+
+    if panic_log_begin_offset == 0:
+        return
+
+    if panic_log_magic != 0 and panic_log_magic != expected_panic_magic:
+        warn_str += "BAD MAGIC! Found 0x%x expected 0x%x".format(panic_log_magic,
+                    expected_panic_magic)
+
+    if panic_log_begin_offset == 0:
+        if warn_str:
+            print "\n %s" % warn_str
+        return
+
+    panic_log_curindex = 0
+    while panic_log_curindex < panic_log_len:
+        p_char = str(panic_buf[(panic_log_begin_offset + panic_log_curindex)])
+        out_str += p_char
+        panic_log_curindex += 1
+
+    if other_log_begin_offset != 0:
+        other_log_curindex = 0
+        while other_log_curindex < other_log_len:
+            p_char = str(panic_buf[(other_log_begin_offset + other_log_curindex)])
+            out_str += p_char
+            other_log_curindex += 1
+
+    print out_str
+
+    if warn_str:
+        print "\n %s" % warn_str
+
     return
 
 @lldb_command('showbootargs')
@@ -611,6 +727,74 @@ def ShowLLDBTypeSummaries(cmd_args=[]):
     lldb_run_command("type category "+ action +" kernel")
     print "Successfully "+action+"d the kernel type summaries. %s" % trailer_msg
 
+@lldb_command('walkqueue_head', 'S')
+def WalkQueueHead(cmd_args=[], cmd_options={}):
+    """ walk a queue_head_t and list all members in it. Note this is for queue_head_t. refer to osfmk/kern/queue.h 
+        Option: -S - suppress summary output.
+        Usage: (lldb) walkqueue_head  <queue_entry *> <struct type> <fieldname>
+        ex:    (lldb) walkqueue_head  0x7fffff80 "thread *" "task_threads"
+        
+    """
+    global lldb_summary_definitions
+    if not cmd_args:
+        raise ArgumentError("invalid arguments")
+    if len(cmd_args) != 3:
+        raise ArgumentError("insufficient arguments")
+    queue_head = kern.GetValueFromAddress(cmd_args[0], 'struct queue_entry *')
+    el_type = cmd_args[1]
+    field_name = cmd_args[2]
+    showsummary = False
+    if el_type in lldb_summary_definitions:
+        showsummary = True
+    if '-S' in cmd_options:
+        showsummary = False
+
+    for i in IterateQueue(queue_head, el_type, field_name):
+        if showsummary:
+            print lldb_summary_definitions[el_type](i)
+        else:
+            print "{0: <#020x}".format(i)
+    
+
+
+@lldb_command('walklist_entry', 'S')
+def WalkList(cmd_args=[], cmd_options={}):
+    """ iterate over a list as defined with LIST_ENTRY in bsd/sys/queue.h
+        params:
+            object addr  - value : address of object
+            element_type - str   : Type of the next element
+            field_name   - str   : Name of the field in next element's structure
+
+        Option: -S - suppress summary output.
+        Usage: (lldb) walklist_entry  <obj with list_entry *> <struct type> <fieldname>
+        ex:    (lldb) walklist_entry  0x7fffff80 "struct proc *" "p_sibling"
+        
+    """
+    global lldb_summary_definitions
+    if not cmd_args:
+        raise ArgumentError("invalid arguments")
+    if len(cmd_args) != 3:
+        raise ArgumentError("insufficient arguments")
+    el_type = cmd_args[1]
+    queue_head = kern.GetValueFromAddress(cmd_args[0], el_type)
+    field_name = cmd_args[2]
+
+    showsummary = False
+    if el_type in lldb_summary_definitions:
+        showsummary = True
+    if '-S' in cmd_options:
+        showsummary = False
+    elt = queue_head
+    while unsigned(elt) != 0:
+        i = elt
+        elt = elt.__getattr__(field_name).le_next
+        if showsummary:
+            print lldb_summary_definitions[el_type](i)
+        else:
+            print "{0: <#020x}".format(i)
+
+
+
 from memory import *
 from process import *
 from ipc import * 
@@ -624,3 +808,17 @@ from pci import *
 from misc import *
 from apic import *
 from scheduler import *
+from atm import *
+from structanalyze import *
+from ipcimportancedetail import *
+from bank import *
+from kasan import *
+from kauth import *
+from waitq import *
+from usertaskgdbserver import *
+from ktrace import *
+from pgtrace import *
+from xnutriage import *
+from kevent import *
+from ntstat import *
+