]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/lldbmacros/xnu.py
xnu-4903.270.47.tar.gz
[apple/xnu.git] / tools / lldbmacros / xnu.py
old mode 100644 (file)
new mode 100755 (executable)
index f93fd47..6880016
@@ -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)
@@ -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:
@@ -298,7 +308,7 @@ def GetKextSymbolInfo(load_addr):
     symbol_name = "None"
     symbol_offset = load_addr
     kmod_val = kern.globals.kmod
-    if kern.arch not in ('arm64',):
+    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)):
@@ -450,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
 
 
@@ -572,65 +573,92 @@ def ShowPanicLog(cmd_args=None, cmd_options={}):
             -v : increase verbosity
             -S : parse stackshot data (if panic stackshot available)
     """
-    binary_data_bytes_to_skip = 0
-    if hasattr(kern.globals, "kc_panic_data"):
-        binary_data_bytes_to_skip = unsigned(kern.globals.kc_panic_data.kcd_addr_end) - unsigned(kern.globals.kc_panic_data.kcd_addr_begin)
-        if binary_data_bytes_to_skip > 0:
-            binary_data_bytes_to_skip += sizeof("struct kcdata_item")
-        else:
-            binary_data_bytes_to_skip = 0
 
     if "-S" in cmd_options:
         if hasattr(kern.globals, "kc_panic_data"):
-            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
+            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:
-                print "Failed to run command: exit code: %d, SO: %s SE: %s" % (c, so, se)
+                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:
             print "kc_panic_data is unavailable for this kernel config."
 
-    panic_buf = kern.globals.debug_buf_addr
-    panic_buf_start = unsigned(panic_buf)
-    panic_buf_end = unsigned(kern.globals.debug_buf_ptr)
-    num_bytes = panic_buf_end - panic_buf_start
-    if num_bytes == 0 :
-        return
     out_str = ""
     warn_str = ""
-    num_print_bytes = 0
-    in_binary_data_region = False
-    pos = 0
-    while pos < num_bytes:
-        p_char = str(panic_buf[pos])
+
+    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
-        if p_char == '\n':
-            if not in_binary_data_region:
-                num_print_bytes += 1
-                print out_str
-            if (out_str.find("Data: BEGIN>>") >= 0):
-                in_binary_data_region = True
-                pos += binary_data_bytes_to_skip - 1
-            if (out_str.find("<<END") >= 0):
-                in_binary_data_region = False
-            out_str = ""
-        if num_print_bytes > 4096 and config['verbosity'] == vHUMAN:
-            warn_str = "LLDBMacro Warning: The paniclog is too large. Trimming to 4096 bytes."
-            warn_str += " If you wish to see entire log please use '-v' argument."
-            break
-        pos += 1
+        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 warn_str
+        print "\n %s" % warn_str
 
     return
 
@@ -765,6 +793,135 @@ def WalkList(cmd_args=[], cmd_options={}):
         else:
             print "{0: <#020x}".format(i)
 
+def iotrace_parse_Copt(Copt):
+    """Parses the -C option argument and returns a list of CPUs
+    """
+    cpusOpt = Copt
+    cpuList = cpusOpt.split(",")
+    chosen_cpus = []
+    for cpu_num_string in cpuList:
+        try:
+            if '-' in cpu_num_string:
+                parts = cpu_num_string.split('-')
+                if len(parts) != 2 or not (parts[0].isdigit() and parts[1].isdigit()):
+                    raise ArgumentError("Invalid cpu specification: %s" % cpu_num_string)
+                firstRange = int(parts[0])
+                lastRange = int(parts[1])
+                if firstRange >= kern.globals.real_ncpus or lastRange >= kern.globals.real_ncpus:
+                    raise ValueError()
+                if lastRange < firstRange:
+                    raise ArgumentError("Invalid CPU range specified: `%s'" % cpu_num_string)
+                for cpu_num in range(firstRange, lastRange + 1):
+                    if cpu_num not in chosen_cpus:
+                        chosen_cpus.append(cpu_num)
+            else:
+                chosen_cpu = int(cpu_num_string)
+                if chosen_cpu < 0 or chosen_cpu >= kern.globals.real_ncpus:
+                    raise ValueError()
+                if chosen_cpu not in chosen_cpus:
+                    chosen_cpus.append(chosen_cpu)
+        except ValueError:
+            raise ArgumentError("Invalid CPU number specified.  Valid range is 0..%d" % (kern.globals.real_ncpus - 1))
+
+    return chosen_cpus
+
+
+@lldb_command('iotrace', 'C:N:S:RB')
+def IOTrace_cmd(cmd_args=[], cmd_options={}):
+    """ Prints the iotrace ring buffers for all CPUs by default.
+        Arguments:
+          -B                              : Print backtraces for each ring entry
+          -C <cpuSpec#>[,...,<cpuSpec#N>] : Limit trace entries to those generated by the specified CPUs (each cpuSpec can be a
+                                            single CPU number or a range separated by a dash (e.g. "0-3"))
+          -N <count>                      : Limit output to the first <count> entries (across all chosen CPUs)
+          -R                              : Display results in reverse-sorted order (oldest first; default is newest-first)
+          -S <sort_key_field_name>        : Sort output by specified iotrace_entry_t field name (instead of by timestamp)
+    """
+    IDX_CPU = 0
+    IDX_RINGPOS = 1
+    IDX_RINGENTRY = 2
+    MAX_IOTRACE_BACKTRACES = 16
+
+    if kern.arch != "x86_64":
+        print "Sorry, iotrace is an x86-only command."
+        return
+
+    if '-S' in cmd_options:
+        field_arg = cmd_options['-S']
+        try:
+            getattr(kern.globals.iotrace_ring[0][0], field_arg)
+            sort_key_field_name = field_arg
+        except AttributeError:
+            raise ArgumentError("Invalid sort key field name `%s'" % field_arg)
+    else:
+            sort_key_field_name = 'start_time_abs'
+
+    if '-C' in cmd_options:
+        chosen_cpus = iotrace_parse_Copt(cmd_options['-C'])
+    else:
+        chosen_cpus = [x for x in range(kern.globals.real_ncpus)]
+
+    try:
+        limit_output_count = int(cmd_options['-N'])
+    except ValueError:
+        raise ArgumentError("Invalid output count `%s'" % cmd_options['-N']);
+    except KeyError:
+        limit_output_count = None
+        
+    reverse_sort = '-R' in cmd_options
+    backtraces = '-B' in cmd_options
+
+    # entries will be a list of 3-tuples, each holding the CPU on which the iotrace entry was collected,
+    # the original ring index, and the iotrace entry. 
+    entries = []
+    for x in chosen_cpus:
+        ring_slice = [(x, y, kern.globals.iotrace_ring[x][y]) for y in range(kern.globals.iotrace_entries_per_cpu)]
+        entries.extend(ring_slice)
+
+    total_entries = len(entries)
+
+    entries.sort(key=lambda x: getattr(x[IDX_RINGENTRY], sort_key_field_name), reverse=reverse_sort)
+
+    if limit_output_count is not None and limit_output_count > total_entries:
+        print ("NOTE: Output count `%d' is too large; showing all %d entries" % (limit_output_count, total_entries));
+        limit_output_count = total_entries
+
+    if len(chosen_cpus) < kern.globals.real_ncpus:
+        print "NOTE: Limiting to entries from cpu%s %s" % ("s" if len(chosen_cpus) > 1 else "", str(chosen_cpus))
+
+    if limit_output_count is not None and limit_output_count < total_entries:
+        entries_to_display = limit_output_count
+        print "NOTE: Limiting to the %s" % ("first entry" if entries_to_display == 1 else ("first %d entries" % entries_to_display))
+    else:
+        entries_to_display = total_entries
+
+    print "%-19s %-8s %-10s %-20s SZ %-18s %-17s DATA" % (
+        "START TIME",
+        "DURATION",
+        "CPU#[RIDX]",
+        "      TYPE",
+        "   VIRT ADDR",
+        "   PHYS ADDR")
+
+    for x in xrange(entries_to_display):
+        print "%-20u(%6u) %6s[%02d] %-20s %d 0x%016x 0x%016x 0x%x" % (
+            entries[x][IDX_RINGENTRY].start_time_abs,
+            entries[x][IDX_RINGENTRY].duration,
+            "CPU%d" % entries[x][IDX_CPU],
+            entries[x][IDX_RINGPOS],
+            str(entries[x][IDX_RINGENTRY].iotype).split("=")[1].strip(),
+            entries[x][IDX_RINGENTRY].size,
+            entries[x][IDX_RINGENTRY].vaddr,
+            entries[x][IDX_RINGENTRY].paddr,
+            entries[x][IDX_RINGENTRY].val)
+        if backtraces:
+            for btidx in range(MAX_IOTRACE_BACKTRACES):
+                nextbt = entries[x][IDX_RINGENTRY].backtrace[btidx]
+                if nextbt == 0:
+                    break
+                print "\t" + GetSourceInformationForAddress(nextbt)
+                
+
 
 
 from memory import *
@@ -774,6 +931,7 @@ from pmap import *
 from ioreg import *
 from mbufs import *
 from net import *
+from skywalk import *
 from kdp import *
 from userspace import *
 from pci import *
@@ -784,9 +942,15 @@ from atm import *
 from structanalyze import *
 from ipcimportancedetail import *
 from bank import *
+from turnstile 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 workqueue import *
+from ntstat import *
+from zonetriage import *