]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/lldbmacros/ipc.py
xnu-4903.270.47.tar.gz
[apple/xnu.git] / tools / lldbmacros / ipc.py
old mode 100644 (file)
new mode 100755 (executable)
index ec78395..81090bb
@@ -7,10 +7,12 @@ from utils import *
 from process import *
 from atm import *
 from bank import *
+from waitq import *
+from ioreg import *
 import xnudefines
 
-@header("{0: <20s} {1: <6s} {2: <6s} {3: <10s} {4: <15s}".format("task", "pid", '#acts', "tablesize", "command"))
-def GetTaskIPCSummary(task):
+@header("{0: <20s} {1: <6s} {2: <6s} {3: <10s} {4: <20s}".format("task", "pid", '#acts', "tablesize", "command"))
+def GetTaskIPCSummary(task, show_busy = False):
     """ Display a task's ipc summary. 
         params:
             task : core.value represeting a Task in kernel
@@ -18,16 +20,49 @@ def GetTaskIPCSummary(task):
             str - string of ipc info for the task
     """
     out_string = ''
-    format_string = "{0: <#020x} {1: <6d} {2: <6d} {3: <10d} {4: <15s}"
+    format_string = "{0: <#020x} {1: <6d} {2: <6d} {3: <10d} {4: <20s}"
+    busy_format = " {0: <10d} {1: <6d}"
+    proc_name = ''
+    if not task.active:
+        proc_name = 'terminated: '
+    if task.halting:
+        proc_name += 'halting: '
     pval = Cast(task.bsd_info, 'proc *')
+    if int(pval) != 0:
+        proc_name += str(pval.p_comm)
+    elif int(task.task_imp_base) != 0 and hasattr(task.task_imp_base, 'iit_procname'):
+        proc_name += str(task.task_imp_base.iit_procname)
     table_size = int(task.itk_space.is_table_size)
-    proc_name = str(pval.p_comm)
     out_string += format_string.format(task, pval.p_pid, task.thread_count, table_size, proc_name)
-    return out_string
+    if show_busy:
+        nbusy, nmsgs = GetTaskBusyPortsSummary(task)
+        out_string += busy_format.format(nbusy, nmsgs)
+        return (out_string, table_size, nbusy, nmsgs)
+    return (out_string, table_size)
+
+@header("{0: <20s} {1: <6s} {2: <6s} {3: <10s} {4: <20s} {5: <10s} {6: <6s}".format("task", "pid", '#acts', "tablesize", "command", "#busyports", "#kmsgs"))
+def GetTaskBusyIPCSummary(task):
+    return GetTaskIPCSummary(task, True)
+
+def GetTaskBusyPortsSummary(task):
+    isp = task.itk_space
+    i = 0
+    nbusy = 0
+    nmsgs = 0
+    while i < isp.is_table_size:
+        iep = addressof(isp.is_table[i])
+        if iep.ie_bits & 0x00020000:
+            port = Cast(iep.ie_object, 'ipc_port_t')
+            if port.ip_messages.data.port.msgcount > 0:
+                nbusy += 1
+                nmsgs += port.ip_messages.data.port.msgcount
+        i = i + 1
+    return (nbusy, nmsgs)
+
 
 @header("{0: <20s} {1: <28s} {2: <12s} {3: <6s} {4: <4s}  {5: <20s} {6: <4s}\n".format(
             "port", "mqueue", "recvname", "flags", "refs", "recvname", "dest"))
-def GetPortSummary(port, show_kmsg_summary=True, prefix=""):
+def PrintPortSummary(port, show_kmsg_summary=True, prefix=""):
     """ Display a port's summary
         params:
             port : core.value representing a port in the kernel
@@ -52,17 +87,17 @@ def GetPortSummary(port, show_kmsg_summary=True, prefix=""):
                                 unsigned(portp.ip_messages.data.port.receiver_name),
                                 "DPort", portp.ip_object.io_references, unsigned(portp),
                                 "inactive-port")
-    
+    print out_string
     if show_kmsg_summary:
         kmsgp = Cast(portp.ip_messages.data.port.messages.ikmq_base, 'ipc_kmsg_t')
-        out_string += prefix + GetKMsgSummary.header + prefix + GetKMsgSummary(kmsgp)
-        
-        kmsgheadp = kmsgp
-        kmsgp = kmsgp.ikm_next
-        while (kmsgp) != (kmsgheadp):
-            out_string += prefix + GetKMsgSummary(kmsgp)
+        if unsigned(kmsgp):
+            print prefix + GetKMsgSummary.header + prefix + GetKMsgSummary(kmsgp, prefix)
+            kmsgheadp = kmsgp
             kmsgp = kmsgp.ikm_next
-    return out_string
+            while (kmsgp) != (kmsgheadp):
+                print prefix + GetKMsgSummary(kmsgp, prefix)
+                kmsgp = kmsgp.ikm_next
+    return
 
 def GetPortDestProc(portp):
     """ Display the name and pid of a given port's receiver
@@ -85,9 +120,59 @@ def GetPortDestProc(portp):
     
     return out_str
 
-@header("{0: <20s} {1: <28s} {2: <12s} {3: <6s} {4: <6s} {5: <19s} {6: <26s} {7: <26s}\n".format(
-            "dest-port", "kmsg", "msgid", "disp", "size", "reply-port", "source", "destination"))
-def GetKMsgSummary(kmsgp):
+
+def GetPortDispositionString(disp):
+    if (disp < 0): ## use negative numbers for request ports
+        portname = 'notify'
+        if disp == -1:
+            disp_str = 'reqNS'
+        elif disp == -2:
+            disp_str = 'reqPD'
+        elif disp == -3:
+            disp_str = 'reqSPa'
+        elif disp == -4:
+            disp_str = 'reqSPr'
+        elif disp == -5:
+            disp_str = 'reqSPra'
+        else:
+            disp_str = '-X'
+    ## These dispositions should match those found in osfmk/mach/message.h
+    elif disp == 16:
+        disp_str = 'R'  ## receive
+    elif disp == 24:
+        disp_str = 'dR' ## dispose receive
+    elif disp == 17:
+        disp_str = 'S'  ## (move) send
+    elif disp == 19:
+        disp_str = 'cS' ## copy send
+    elif disp == 20:
+        disp_str = 'mS' ## make send
+    elif disp == 25:
+        disp_str = 'dS' ## dispose send
+    elif disp == 18:
+        disp_str = 'O'  ## send-once
+    elif disp == 21:
+        disp_str = 'mO' ## make send-once
+    elif disp == 26:
+        disp_str = 'dO' ## dispose send-once
+    ## faux dispositions used to string-ify IPC entry types
+    elif disp == 100:
+        disp_str = 'PS' ## port set
+    elif disp == 101:
+        disp_str = 'dead' ## dead name
+    elif disp == 102:
+        disp_str = 'L' ## LABELH
+    elif disp == 103:
+        disp_str = 'V' ## Thread voucher (thread->ith_voucher->iv_port)
+    ## Catch-all
+    else:
+        disp_str = 'X'  ## invalid
+    return disp_str
+
+
+@header("{:<20s} {:<28s} {:<12s} {:<8s} {:<6s} {:<19s} {:<26s} {:<26s}\n".format(
+            "", "kmsg", "msgid", "disp", "size", "reply-port", "source", "destination"))
+def GetKMsgSummary(kmsgp, prefix_str=""):
     """ Display a summary for type ipc_kmsg_t
         params:
             kmsgp : core.value representing the given ipc_kmsg_t struct
@@ -97,25 +182,61 @@ def GetKMsgSummary(kmsgp):
     kmsghp = kmsgp.ikm_header
     kmsgh = dereference(kmsghp)
     out_string = ""
-    out_string += "{0: <19s} {1: <#019x} {2: <8s} {3: <#011x} ".format(
-                    ' '*19, unsigned(kmsgp), ' '*8, kmsgh.msgh_id)
+    out_string += "{0: <20s} {1: <#019x} {2: <8s} {3: <#011x} ".format(
+                    ' ', unsigned(kmsgp), ' '*8, kmsgh.msgh_id)
+    prefix_str = "{0: <20s} ".format(' ') + prefix_str
+    disposition = ""
+    bits = kmsgh.msgh_bits & 0xff
     
-    if (kmsgh.msgh_bits & 0xff) == 17:
-        out_string += "{0: <2s}".format("rS")
+    # remote port
+    if bits == 17:
+        disposition = "rS"
+    elif bits == 18:
+        disposition = "rO"
+    else :
+        disposition = "rX" # invalid
+    
+    out_string += "{0: <2s}".format(disposition)
+    
+    # local port
+    disposition = ""
+    bits = (kmsgh.msgh_bits & 0xff00) >> 8
+    
+    if bits == 17:
+        disposition = "lS"
+    elif bits == 18:
+        disposition = "lO"
+    elif bits == 0:
+        disposition = "l-"
     else:
-        out_string += "{0: <2s}".format("rO")
+        disposition = "lX"  # invalid
+        
+    out_string += "{0: <2s}".format(disposition)
+    
+    # voucher
+    disposition = ""
+    bits = (kmsgh.msgh_bits & 0xff0000) >> 16
     
-    if (kmsgh.msgh_bits & 0xff00) == (17 << 8):
-        out_string += "{0: <2s}".format("lS")
+    if bits == 17:
+        disposition = "vS"
+    elif bits == 0:
+        disposition = "v-"
     else:
-        if (kmsgh.msgh_bits & 0xff00) == (18 << 8):
-            out_string += "{0: <2s}".format("lO")
-        else:
-            out_string += "{0: <2s}".format("l-")
-    if kmsgh.msgh_bits & 0xf0000000:
-        out_string += "{0: <2s}".format("c")
+        disposition = "vX"
+
+    out_string += "{0: <2s}".format(disposition) 
+        
+    # complex message
+    if kmsgh.msgh_bits & 0x80000000:
+        out_string += "{0: <1s}".format("c")
     else:
-        out_string += "{0: <2s}".format("s")
+        out_string += "{0: <1s}".format("s")
+    
+    # importance boost
+    if kmsgh.msgh_bits & 0x20000000:
+        out_string += "{0: <1s}".format("I")
+    else:
+        out_string += "{0: <1s}".format("-")
     
     dest_proc_name = ""
     if kmsgp.ikm_header.msgh_remote_port:
@@ -124,8 +245,73 @@ def GetKMsgSummary(kmsgp):
     out_string += "{0: ^6d}   {1: <#019x} {2: <26s} {3: <26s}\n".format(
                     unsigned(kmsgh.msgh_size), unsigned(kmsgh.msgh_local_port),
                     GetKMsgSrc(kmsgp), dest_proc_name)
+    
+    if kmsgh.msgh_bits & 0x80000000:
+        out_string += prefix_str + "\t" + GetKMsgComplexBodyDesc.header + "\n"
+        out_string += prefix_str + "\t" + GetKMsgComplexBodyDesc(kmsgp, prefix_str + "\t") + "\n"
+    
     return out_string
 
+@header("{: <20s} {: <20s} {: <10s}".format("descriptor", "address", "size"))
+def GetMachMsgOOLDescriptorSummary(desc):
+    """ Returns description for mach_msg_ool_descriptor_t * object
+    """
+    format_string = "{: <#020x} {: <#020x} {: <#010x}"
+    out_string = format_string.format(desc, desc.address, desc.size)
+    return out_string
+
+
+def GetKmsgDescriptors(kmsgp):
+    """ Get a list of descriptors in a complex message
+    """
+    kmsghp = kmsgp.ikm_header
+    kmsgh = dereference(kmsghp)
+    if not (kmsgh.msgh_bits & 0x80000000):
+        return []
+    ## Something in the python/lldb types is not getting alignment correct here.
+    ## I'm grabbing a pointer to the body manually, and using tribal knowledge
+    ## of the location of the descriptor count to get this correct
+    body = Cast(addressof(Cast(addressof(kmsgh), 'char *')[sizeof(kmsgh)]), 'mach_msg_body_t *')
+    #dsc_count = body.msgh_descriptor_count
+    dsc_count = dereference(Cast(body, 'uint32_t *'))
+    #dschead = Cast(addressof(body[1]), 'mach_msg_descriptor_t *')
+    dschead = Cast(addressof(Cast(addressof(body[0]), 'char *')[sizeof('uint32_t')]), 'mach_msg_descriptor_t *')
+    dsc_list = []
+    for i in range(dsc_count):
+        dsc_list.append(dschead[i])
+    return (body, dschead, dsc_list)
+
+
+@header("{: <20s} {: <8s} {: <20s} {: <10s} {: <20s}".format("kmsgheader", "size", "body", "ds_count", "dsc_head"))
+def GetKMsgComplexBodyDesc(kmsgp, prefix_str=""):
+    """ Routine that prints a complex kmsg's body
+    """
+    kmsghp = kmsgp.ikm_header
+    kmsgh = dereference(kmsghp)
+    if not (kmsgh.msgh_bits & 0x80000000):
+        return ""
+    format_string = "{: <#020x} {: <#08x} {: <#020x} {: <#010x} {: <#020x}"
+    out_string = ""
+
+    (body, dschead, dsc_list) = GetKmsgDescriptors(kmsgp)
+    out_string += format_string.format(kmsghp, sizeof(dereference(kmsghp)), body, len(dsc_list), dschead)
+    for dsc in dsc_list:
+        try:
+            dsc_type = unsigned(dsc.type.type)
+            out_string += "\n" + prefix_str + "Descriptor: " + xnudefines.mach_msg_type_descriptor_strings[dsc_type]
+            if dsc_type == 0:
+                # its a port.
+                p = dsc.port.name
+                dstr = GetPortDispositionString(dsc.port.disposition)
+                out_string += " disp:{:s}, name:{: <#20x}".format(dstr, p)
+            elif unsigned(dsc.type.type) in (1,3):
+                # its OOL DESCRIPTOR or OOL VOLATILE DESCRIPTOR
+                ool = dsc.out_of_line
+                out_string += " " + GetMachMsgOOLDescriptorSummary(addressof(ool))
+        except:
+            out_string += "\n" + prefix_str + "Invalid Descriptor: {}".format(dsc)
+    return out_string 
+
 def GetKMsgSrc(kmsgp):
     """ Routine that prints a kmsg's source process and pid details
         params:
@@ -138,9 +324,66 @@ def GetKMsgSrc(kmsgp):
     
     return "{0:s} ({1:d})".format(GetProcNameForPid(kmsgpid), kmsgpid)
 
+
+def PrintPortSetMembers(space, setid, show_kmsg_summary):
+    """ Print out the members of a given IPC PSet
+    """
+    num_entries = int(space.is_table_size)
+    is_tableval = space.is_table
+    setid_str = GetWaitqSetidString(setid)
+
+    prefix_str = "{0:<21s}".format(' '*21)
+    once = True
+    verbose = False
+    if config['verbosity'] > vHUMAN:
+        verbose = True
+
+    idx = 0
+    while idx < num_entries:
+        entryval = GetObjectAtIndexFromArray(is_tableval, idx)
+        ie_bits = unsigned(entryval.ie_bits)
+        if not (ie_bits & 0x00180000):
+            # It's a port entry that's _not_ dead
+            portval = Cast(entryval.ie_object, 'ipc_port_t')
+            waitq = addressof(portval.ip_messages.data.port.waitq)
+            psets = GetWaitqSets(addressof(portval.ip_messages.data.port.waitq))
+            for ps in psets:
+                if ps == setid_str:
+                    if once:
+                        once = False
+                        print "{:s}\n{:s}{:s}".format(GetPortDestProc(portval), prefix_str, PrintPortSummary.header)
+                    PrintPortSummary(portval, show_kmsg_summary, prefix_str)
+            if verbose:
+                sys.stderr.write('{:d}/{:d}...          \r'.format(idx, num_entries))
+        idx += 1
+    return
+
+def FindEntryName(obj, space):
+    """ Routine to locate a port/ipc_object in an ipc_space
+        and return the name within that space.
+    """
+    if space == 0:
+        return 0
+
+    num_entries = int(space.is_table_size)
+    is_tableval = space.is_table
+    idx = 0
+    while idx < num_entries:
+        entry_val = GetObjectAtIndexFromArray(is_tableval, idx)
+        entry_bits= unsigned(entry_val.ie_bits)
+        entry_obj = 0
+        if (int(entry_bits) & 0x001f0000) != 0: ## it's a valid entry
+            entry_obj = unsigned(entry_val.ie_object)
+        if entry_obj == unsigned(obj):
+            nm = (idx << 8) | (entry_bits >> 24)
+            return nm
+        idx += 1
+    return 0
+
+
 @header("{0: <20s} {1: <28s} {2: <12s} {3: <6s} {4: <6s} {5: <20s} {6: <7s}\n".format(
             "portset", "waitqueue", "recvname", "flags", "refs", "recvname", "process"))
-def GetPortSetSummary(pset):
+def PrintPortSetSummary(pset, space = 0):
     """ Display summary for a given struct ipc_pset *
         params:
             pset : core.value representing a pset in the kernel
@@ -148,33 +391,32 @@ def GetPortSetSummary(pset):
             str  : string of summary information for the given pset
     """
     out_str = ""
+    show_kmsg_summary = False
+    if config['verbosity'] > vHUMAN :
+        show_kmsg_summary = True
+
+    local_name = FindEntryName(pset, space)
+    setid = 0
     if pset.ips_object.io_bits & 0x80000000:
+        setid = pset.ips_messages.data.pset.setq.wqset_id
         out_str += "{0: #019x}  {1: #019x} {2: <7s} {3: #011x}   {4: <4s} {5: >6d}  {6: #019x}   ".format(
                     unsigned(pset), addressof(pset.ips_messages), ' '*7,
-                    pset.ips_messages.data.pset.local_name, "ASet",
+                    local_name, "ASet",
                     pset.ips_object.io_references,
-                    pset.ips_messages.data.pset.local_name)
+                    local_name)
 
     else:
         out_str += "{0: #019x}  {1: #019x} {2: <7s} {3: #011x}   {4: <4s} {5: >6d}  {6: #019x}   ".format(
                     unsigned(pset), addressof(pset.ips_messages), ' '*7,
-                    pset.ips_messages.data.pset.local_name, "DSet",
+                    local_name, "DSet",
                     pset.ips_object.io_references,
-                    pset.ips_messages.data.pset.local_name)
-    
-    once = True
-    setlinksp = addressof(pset.ips_messages.data.pset.set_queue.wqs_setlinks)
-    wql = Cast(pset.ips_messages.data.pset.set_queue.wqs_setlinks.next, 'WaitQueueLink *')
-    portoff = getfieldoffset('struct ipc_port', 'ip_messages')
-    prefix_str = "{0:<21s}".format(' '*21)
-    while unsigned(wql) != unsigned(Cast(setlinksp, 'void *')):
-        portp = kern.GetValueFromAddress(unsigned(wql.wql_element.wqe_queue) - portoff, 'ipc_port *')
-        if once:
-            once = False
-            out_str += "{0:s}\n{1:s}{2:s}".format(GetPortDestProc(portp), prefix_str, GetPortSummary.header)
-        out_str += GetPortSummary(portp, False, prefix_str)
-        wql = Cast(wql.wql_setlinks.next, 'WaitQueueLink *')
-    return out_str
+                    local_name)
+    print out_str
+
+    if setid != 0 and space != 0:
+        PrintPortSetMembers(space, setid, show_kmsg_summary)
+
+    return
 
 # Macro: showipc
 
@@ -191,8 +433,8 @@ def ShowIPC(cmd_args=None):
     if not ipc:
         print "unknown arguments:", str(cmd_args)
         return False
-    print GetIPCInformation.header
-    print GetIPCInformation(ipc, False, False)
+    print PrintIPCInformation.header
+    PrintIPCInformation(ipc, False, False)
 
 # EndMacro: showipc
 
@@ -214,8 +456,9 @@ def ShowTaskIPC(cmd_args=None):
     print GetTaskSummary.header + " " + GetProcSummary.header
     pval = Cast(tval.bsd_info, 'proc *')
     print GetTaskSummary(tval) + " " + GetProcSummary(pval)
-    print GetTaskIPCSummary.header
-    print GetTaskIPCSummary(tval)
+    print GetTaskBusyIPCSummary.header
+    (summary, table_size, nbusy, nmsgs) = GetTaskBusyIPCSummary(tval)
+    print summary
 
 # EndMacro: showtaskipc
 
@@ -230,8 +473,8 @@ def ShowAllIPC(cmd_args=None):
         print GetTaskSummary.header + " " + GetProcSummary.header
         pval = Cast(t.bsd_info, 'proc *')
         print GetTaskSummary(t) + " " + GetProcSummary(pval)
-        print GetIPCInformation.header
-        print GetIPCInformation(t.itk_space, False, False) + "\n\n"
+        print PrintIPCInformation.header
+        PrintIPCInformation(t.itk_space, False, False) + "\n\n"
 
 # EndMacro: showallipc
 
@@ -242,8 +485,15 @@ def ShowIPCSummary(cmd_args=None):
         tasks that are candidates for further investigation.
     """
     print GetTaskIPCSummary.header
+    ipc_table_size = 0
     for t in kern.tasks:
-        print GetTaskIPCSummary(t)
+        (summary, table_size) = GetTaskIPCSummary(t)
+        ipc_table_size += table_size
+        print summary
+    for t in kern.terminated_tasks:
+        (summary, table_size) = GetTaskIPCSummary(t)
+        ipc_table_size += table_size
+    print "Total Table size: {:d}".format(ipc_table_size)
     return
 
 def GetKObjectFromPort(portval):
@@ -255,9 +505,18 @@ def GetKObjectFromPort(portval):
     io_bits = unsigned(portval.ip_object.io_bits)
     objtype_index = io_bits & 0xfff
     if objtype_index < len(xnudefines.kobject_types) :
-        desc_str = "kobject({0:s})".format(xnudefines.kobject_types[objtype_index])
-        if xnudefines.kobject_types[objtype_index] in ('TASK_RESUME', 'TASK'):
-            desc_str += " " + GetProcNameForTask(Cast(portval.kdata.kobject, 'task *'))
+        objtype_str = xnudefines.kobject_types[objtype_index]
+        if objtype_str == 'IOKIT_OBJ':
+            iokit_classnm = GetObjectTypeStr(portval.kdata.kobject)
+            if not iokit_classnm:
+                iokit_classnm = "<unknown class>"
+            else:
+                iokit_classnm = re.sub(r'vtable for ', r'', iokit_classnm)
+            desc_str = "kobject({:s}:{:s})".format(objtype_str, iokit_classnm)
+        else:
+            desc_str = "kobject({0:s})".format(objtype_str)
+            if xnudefines.kobject_types[objtype_index] in ('TASK_RESUME', 'TASK'):
+                desc_str += " " + GetProcNameForTask(Cast(portval.kdata.kobject, 'task *'))
     else:
         desc_str = "kobject(UNKNOWN) {:d}".format(objtype_index)
     return kobject_str + " " + desc_str
@@ -318,38 +577,61 @@ def GetPortDestinationSummary(port):
     return out_str
     
 @lldb_type_summary(['ipc_entry_t'])
-@header("{0: <20s} {1: <20s} {2: <8s} {3: <8s} {4: <20s} {5: <20s}".format("object", "name","rite", "urefs", "destname", "destination"))
-def GetIPCEntrySummary(entry, ipc_name=''):
+@header("{: <20s} {: <12s} {: <8s} {: <8s} {: <8s} {: <8s} {: <20s} {: <20s}".format("object", "name", "rite", "urefs", "nsets", "nmsgs", "destname", "destination"))
+def GetIPCEntrySummary(entry, ipc_name='', rights_filter=0):
     """ Get summary of a ipc entry.
         params:
             entry - core.value representing ipc_entry_t in the kernel
             ipc_name - str of format '0x0123' for display in summary.  
         returns:
             str - string of ipc entry related information
+
+        types of rights:
+            'Dead'  : Dead name
+            'Set'   : Port set
+            'S'     : Send right
+            'R'     : Receive right
+            'O'     : Send-once right
+        types of notifications:
+            'd'     : Dead-Name notification requested
+            's'     : Send-Possible notification armed
+            'r'     : Send-Possible notification requested
+            'n'     : No-Senders notification requested
+            'x'     : Port-destroy notification requested
     """
-    out_str = ''    
+    out_str = ''
     entry_ptr = int(hex(entry), 16)
-    format_string = "{0: <#020x} {1: <12s} {2: <8s} {3: <8d} {4: <20s} {5: <20s}"
+    format_string = "{: <#020x} {: <12s} {: <8s} {: <8d} {: <8d} {: <8d} {: <20s} {: <20s}"
     right_str = ''
     destname_str = ''
     destination_str = ''
-    
+
     ie_object = entry.ie_object
     ie_bits = int(entry.ie_bits)
     urefs = int(ie_bits & 0xffff)
+    nsets = 0
+    nmsgs = 0
     if ie_bits & 0x00100000 :
         right_str = 'Dead'
     elif ie_bits & 0x00080000:
         right_str = 'Set'
+        psetval = Cast(ie_object, 'ipc_pset *')
+        set_str = GetWaitqSets(addressof(psetval.ips_messages.data.pset.setq.wqset_q))
+        nsets = len(set_str)
+        nmsgs = 0
     else:
         if ie_bits & 0x00010000 :
             if ie_bits & 0x00020000 :
+                # SEND + RECV
                 right_str = 'SR'
             else:
+                # SEND only
                 right_str = 'S'
         elif ie_bits & 0x00020000:
+            # RECV only
             right_str = 'R'
         elif ie_bits & 0x00040000 :
+            # SEND_ONCE
             right_str = 'O'
         portval = Cast(ie_object, 'ipc_port_t')
         if int(entry.index.request) != 0:
@@ -357,15 +639,39 @@ def GetIPCEntrySummary(entry, ipc_name=''):
             sorightval = requestsval[int(entry.index.request)].notify.port
             soright_ptr = unsigned(sorightval)
             if soright_ptr != 0:
-                 if soright_ptr & 0x1 : right_str +='s'
-                 elif soright_ptr & 0x2 : right_str +='d'
-                 else : right_str +='n'
-        if ie_bits & 0x00800000 : right_str +='c'
-        if portval.ip_nsrequest != 0: right_str +='x'
+                # dead-name notification requested
+                right_str += 'd'
+                # send-possible armed
+                if soright_ptr & 0x1 : right_str +='s'
+                # send-possible requested
+                if soright_ptr & 0x2 : right_str +='r'
+        # No-senders notification requested
+        if portval.ip_nsrequest != 0: right_str += 'n'
+        # port-destroy notification requested
+        if portval.ip_pdrequest != 0: right_str += 'x'
+
+        # early-out if the rights-filter doesn't match
+        if rights_filter != 0 and rights_filter != right_str:
+            return ''
+
+        # append the generation to the name value
+        # (from osfmk/ipc/ipc_entry.h)
+        # bits    rollover period
+        # 0 0     64
+        # 0 1     48
+        # 1 0     32
+        # 1 1     16
+        ie_gen_roll = { 0:'.64', 1:'.48', 2:'.32', 3:'.16' }
+        ipc_name = '{:s}{:s}'.format(strip(ipc_name), ie_gen_roll[(ie_bits & 0x00c00000) >> 22])
+
         # now show the port destination part
         destname_str = GetPortDestinationSummary(Cast(ie_object, 'ipc_port_t'))
-        
-    out_str = format_string.format(ie_object, ipc_name, right_str, urefs, destname_str, destination_str)
+        # Get the number of sets to which this port belongs
+        set_str = GetWaitqSets(addressof(portval.ip_messages.data.port.waitq))
+        nsets = len(set_str)
+        nmsgs = portval.ip_messages.data.port.msgcount
+    if rights_filter == 0 or rights_filter == right_str:
+        out_str = format_string.format(ie_object, ipc_name, right_str, urefs, nsets, nmsgs, destname_str, destination_str)
     return out_str
 
 @header("{0: >20s}".format("user bt") )
@@ -391,12 +697,12 @@ def GetPortUserStack(port, task):
     return out_str
 
 @lldb_type_summary(['ipc_space *'])
-@header("{0: <20s} {1: <20s} {2: <20s} {3: <8s} {4: <10s} {5: <16s} {6: <10s} {7: <7s}".format('ipc_space', 'is_task', 'is_table', 'flags', 'ports', 'table_next', 'low_mod', 'high_mod'))
-def GetIPCInformation(space, show_entries=False, show_userstack=False):
+@header("{0: <20s} {1: <20s} {2: <20s} {3: <8s} {4: <10s} {5: <18s} {6: >8s} {7: <8s}".format('ipc_space', 'is_task', 'is_table', 'flags', 'ports', 'table_next', 'low_mod', 'high_mod'))
+def PrintIPCInformation(space, show_entries=False, show_userstack=False, rights_filter=0):
     """ Provide a summary of the ipc space
     """
     out_str = ''
-    format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: <8s} {4: <10d} {5: <#01x} {6: >10d} {7: >10d}"
+    format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: <8s} {4: <10d} {5: <#18x} {6: >8d} {7: <8d}"
     is_tableval = space.is_table
     ports = int(space.is_table_size)
     flags =''
@@ -404,11 +710,11 @@ def GetIPCInformation(space, show_entries=False, show_userstack=False):
     if (is_bits & 0x40000000) == 0: flags +='A'
     else: flags += ' '
     if (is_bits & 0x20000000) != 0: flags +='G'
-    out_str += format_string.format(space, space.is_task, space.is_table, flags, space.is_table_size, space.is_table_next, space.is_low_mod, space.is_high_mod)
+    print format_string.format(space, space.is_task, space.is_table, flags, space.is_table_size, space.is_table_next, space.is_low_mod, space.is_high_mod)
     
     #should show the each individual entries if asked.
     if show_entries == True:
-        out_str += "\n\t" + GetIPCEntrySummary.header + "\n"
+        print "\t" + GetIPCEntrySummary.header
         num_entries = ports
         index = 0
         while index < num_entries:
@@ -416,22 +722,41 @@ def GetIPCInformation(space, show_entries=False, show_userstack=False):
             entry_ie_bits = unsigned(entryval.ie_bits)
             if (int(entry_ie_bits) & 0x001f0000 ) != 0:
                 entry_name = "{0: <#020x}".format( (index <<8 | entry_ie_bits >> 24) )
-                out_str += "\t" + GetIPCEntrySummary(entryval, entry_name) + "\n"
-                if show_userstack == True:
-                    entryport = Cast(entryval.ie_object, 'ipc_port *')
-                    if entryval.ie_object and (int(entry_ie_bits) & 0x00070000) and entryport.ip_callstack[0]:
-                        out_str += GetPortUserStack.header
-                        out_str += GetPortUserStack(entryport, space.is_task)
-            index +=1    
+                entry_str = GetIPCEntrySummary(entryval, entry_name, rights_filter)
+                if len(entry_str) > 0:
+                    print "                  \r\t" + entry_str
+                    if show_userstack == True:
+                        entryport = Cast(entryval.ie_object, 'ipc_port *')
+                        if entryval.ie_object and (int(entry_ie_bits) & 0x00070000) and entryport.ip_callstack[0]:
+                            print GetPortUserStack.header + GetPortUserStack(entryport, space.is_task)
+                else:
+                    # give some progress indication (this is especially
+                    # helpful for tasks with large sets of rights)
+                    sys.stderr.write(' {:d}/{:d}...\r'.format(index, num_entries))
+            index += 1
     #done with showing entries
     return out_str
 
 # Macro: showrights
 
-@lldb_command('showrights'
-def ShowRights(cmd_args=None):
+@lldb_command('showrights', 'R:')
+def ShowRights(cmd_args=None, cmd_options={}):
     """  Routine to print rights information for the given IPC space 
-         Usage: showrights <address of ipc space>
+         Usage: showrights [-R rights_type] <address of ipc space>
+                -R rights_type  : only display rights matching the string 'rights_type'
+
+                types of rights:
+                    'Dead'  : Dead name
+                    'Set'   : Port set
+                    'S'     : Send right
+                    'R'     : Receive right
+                    'O'     : Send-once right
+                types of notifications:
+                    'd'     : Dead-Name notification requested
+                    's'     : Send-Possible notification armed
+                    'r'     : Send-Possible notification requested
+                    'n'     : No-Senders notification requested
+                    'x'     : Port-destroy notification requested
     """
     if not cmd_args:
         print "No arguments passed"
@@ -441,15 +766,32 @@ def ShowRights(cmd_args=None):
     if not ipc:
         print "unknown arguments:", str(cmd_args)
         return False
-    print GetIPCInformation.header
-    print GetIPCInformation(ipc, True, False)
+    rights_type = 0
+    if "-R" in cmd_options:
+        rights_type = cmd_options["-R"]
+    print PrintIPCInformation.header
+    PrintIPCInformation(ipc, True, False, rights_type)
 
 # EndMacro: showrights
 
-@lldb_command('showtaskrights')
-def ShowTaskRights(cmd_args=None):
+@lldb_command('showtaskrights','R:')
+def ShowTaskRights(cmd_args=None, cmd_options={}):
     """ Routine to ipc rights information for a task
-        Usage: showtaskrights <task address>
+        Usage: showtaskrights [-R rights_type] <task address>
+               -R rights_type  : only display rights matching the string 'rights_type'
+
+               types of rights:
+                   'Dead'  : Dead name
+                   'Set'   : Port set
+                   'S'     : Send right
+                   'R'     : Receive right
+                   'O'     : Send-once right
+               types of notifications:
+                   'd'     : Dead-Name notification requested
+                   's'     : Send-Possible notification armed
+                   'r'     : Send-Possible notification requested
+                   'n'     : No-Senders notification requested
+                   'x'     : Port-destroy notification requested
     """
     if cmd_args == None:
         print "No arguments passed"
@@ -459,18 +801,35 @@ def ShowTaskRights(cmd_args=None):
     if not tval:
         print "unknown arguments:", str(cmd_args)
         return False
+    rights_type = 0
+    if "-R" in cmd_options:
+        rights_type = cmd_options["-R"]
     print GetTaskSummary.header + " " + GetProcSummary.header
     pval = Cast(tval.bsd_info, 'proc *')
     print GetTaskSummary(tval) + " " + GetProcSummary(pval)
-    print GetIPCInformation.header
-    print GetIPCInformation(tval.itk_space, True, False)
+    print PrintIPCInformation.header
+    PrintIPCInformation(tval.itk_space, True, False, rights_type)
 
 # Macro: showataskrightsbt
 
-@lldb_command('showtaskrightsbt')
-def ShowTaskRightsBt(cmd_args=None):
+@lldb_command('showtaskrightsbt', 'R:')
+def ShowTaskRightsBt(cmd_args=None, cmd_options={}):
     """ Routine to ipc rights information with userstacks for a task
-        Usage: showtaskrightsbt <task address>
+        Usage: showtaskrightsbt [-R rights_type] <task address>
+               -R rights_type  : only display rights matching the string 'rights_type'
+
+               types of rights:
+                   'Dead'  : Dead name
+                   'Set'   : Port set
+                   'S'     : Send right
+                   'R'     : Receive right
+                   'O'     : Send-once right
+               types of notifications:
+                   'd'     : Dead-Name notification requested
+                   's'     : Send-Possible notification armed
+                   'r'     : Send-Possible notification requested
+                   'n'     : No-Senders notification requested
+                   'x'     : Port-destroy notification requested
     """
     if cmd_args == None:
         print "No arguments passed"
@@ -480,28 +839,48 @@ def ShowTaskRightsBt(cmd_args=None):
     if not tval:
         print "unknown arguments:", str(cmd_args)
         return False
+    rights_type = 0
+    if "-R" in cmd_options:
+        rights_type = cmd_options["-R"]
     print GetTaskSummary.header + " " + GetProcSummary.header
     pval = Cast(tval.bsd_info, 'proc *')
     print GetTaskSummary(tval) + " " + GetProcSummary(pval)
-    print GetIPCInformation.header
-    print GetIPCInformation(tval.itk_space, True, True)
+    print PrintIPCInformation.header
+    PrintIPCInformation(tval.itk_space, True, True, rights_type)
 
 # EndMacro: showtaskrightsbt
 
 # Macro: showallrights
 
-@lldb_command('showallrights'
-def ShowAllRights(cmd_args=None):
+@lldb_command('showallrights', 'R:')
+def ShowAllRights(cmd_args=None, cmd_options={}):
     """  Routine to print rights information for IPC space of all tasks
-         Usage: showallrights
+         Usage: showallrights [-R rights_type]
+                -R rights_type  : only display rights matching the string 'rights_type'
+
+                types of rights:
+                    'Dead'  : Dead name
+                    'Set'   : Port set
+                    'S'     : Send right
+                    'R'     : Receive right
+                    'O'     : Send-once right
+                types of notifications:
+                    'd'     : Dead-Name notification requested
+                    's'     : Send-Possible notification armed
+                    'r'     : Send-Possible notification requested
+                    'n'     : No-Senders notification requested
+                    'x'     : Port-destroy notification requested
     """
+    rights_type = 0
+    if "-R" in cmd_options:
+        rights_type = cmd_options["-R"]
     for t in kern.tasks:
         print GetTaskSummary.header + " " + GetProcSummary.header
         pval = Cast(t.bsd_info, 'proc *')
         print GetTaskSummary(t) + " " + GetProcSummary(pval)
         try:
-            print GetIPCInformation.header
-            print GetIPCInformation(t.itk_space, True, False) + "\n\n"
+            print PrintIPCInformation.header
+            PrintIPCInformation(t.itk_space, True, False, rights_type) + "\n\n"
         except (KeyboardInterrupt, SystemExit):
             raise
         except:
@@ -509,6 +888,525 @@ def ShowAllRights(cmd_args=None):
 
 # EndMacro: showallrights
 
+
+def GetInTransitPortSummary(port, disp, holding_port, holding_kmsg):
+    """ String-ify the in-transit dispostion of a port.
+    """
+    ## This should match the summary generated by GetIPCEntrySummary
+    ##              "object"   "name"   "rite"  "urefs" "nsets" "nmsgs" "destname" "destination"
+    format_str = "\t{: <#20x} {: <12} {: <8s} {: <8d} {: <8d} {: <8d} p:{: <#19x} k:{: <#19x}"
+    portname = 'intransit'
+
+    disp_str = GetPortDispositionString(disp)
+
+    out_str = format_str.format(unsigned(port), 'in-transit', disp_str, 0, 0, port.ip_messages.data.port.msgcount, unsigned(holding_port), unsigned(holding_kmsg))
+    return out_str
+
+
+def GetDispositionFromEntryType(entry_bits):
+    """ Translate an IPC entry type into an in-transit disposition. This allows
+        the GetInTransitPortSummary function to be re-used to string-ify IPC
+        entry types.
+    """
+    ebits = int(entry_bits)
+    if (ebits & 0x003f0000) == 0:
+        return 0
+
+    if (ebits & 0x00010000) != 0:
+        return 17 ## MACH_PORT_RIGHT_SEND
+    elif (ebits & 0x00020000) != 0:
+        return 16 ## MACH_PORT_RIGHT_RECEIVE
+    elif (ebits & 0x00040000) != 0:
+        return 18 ## MACH_PORT_RIGHT_SEND_ONCE
+    elif (ebits & 0x00080000) != 0:
+        return 100 ## MACH_PORT_RIGHT_PORT_SET
+    elif (ebits & 0x00100000) != 0:
+        return 101 ## MACH_PORT_RIGHT_DEAD_NAME
+    elif (ebits & 0x00200000) != 0:
+        return 102 ## MACH_PORT_RIGHT_LABELH
+    else:
+        return 0
+
+def GetDispositionFromVoucherPort(th_vport):
+    """ Translate a thread's voucher port into a 'disposition'
+    """
+    if unsigned(th_vport) > 0:
+        return 103  ## Voucher type
+    return 0
+
+
+g_kmsg_prog = 0
+g_progmeter = {
+    0 : '*',
+    1 : '-',
+    2 : '\\',
+    3 : '|',
+    4 : '/',
+    5 : '-',
+    6 : '\\',
+    7 : '|',
+    8 : '/',
+}
+
+def PrintProgressForKmsg():
+    global g_kmsg_prog
+    global g_progmeter
+    sys.stderr.write(" {:<1s}\r".format(g_progmeter[g_kmsg_prog % 9]))
+    g_kmsg_prog += 1
+
+
+def CollectPortsForAnalysis(port, disposition):
+    """
+    """
+    p = Cast(port, 'struct ipc_port *')
+    yield (p, disposition)
+
+    # no-senders notification port
+    if unsigned(p.ip_nsrequest) != 0:
+        PrintProgressForKmsg()
+        yield (Cast(p.ip_nsrequest, 'struct ipc_port *'), -1)
+
+    # port-death notification port
+    if unsigned(p.ip_pdrequest) != 0:
+        PrintProgressForKmsg()
+        yield (Cast(p.ip_pdrequest, 'struct ipc_port *'), -2)
+
+    ## ports can have many send-possible notifications armed: go through the table!
+    if unsigned(p.ip_requests) != 0:
+        table = Cast(p.ip_requests, 'struct ipc_port_request *')
+        table_sz = int(table.name.size.its_size)
+        for i in range(table_sz):
+            if i == 0:
+                continue
+            ipr = table[i]
+            if unsigned(ipr.name.name) != 0:
+                ipr_bits = unsigned(ipr.notify.port) & 3
+                ipr_port = kern.GetValueFromAddress(int(ipr.notify.port) & ~3, 'struct ipc_port *')
+                ipr_disp = 0
+                if ipr_bits & 3: ## send-possible armed and requested
+                    ipr_disp = -5
+                elif ipr_bits & 2: ## send-possible requested
+                    ipr_disp = -4
+                elif ipr_bits & 1: ## send-possible armed
+                    ipr_disp = -3
+                PrintProgressForKmsg()
+                yield (ipr_port, ipr_disp)
+    return
+
+def CollectKmsgPorts(task, task_port, kmsgp):
+    """ Look through a message, 'kmsgp' destined for 'task'
+        (enqueued on task_port). Collect any port descriptors,
+        remote, local, voucher, or other port references
+        into a (ipc_port_t, disposition) list.
+    """
+    kmsgh = dereference(kmsgp.ikm_header)
+
+    p_list = []
+
+    PrintProgressForKmsg()
+    if kmsgh.msgh_remote_port and unsigned(kmsgh.msgh_remote_port) != unsigned(task_port):
+        disp = kmsgh.msgh_bits & 0x1f
+        p_list += list(CollectPortsForAnalysis(kmsgh.msgh_remote_port, disp))
+
+    if kmsgh.msgh_local_port and unsigned(kmsgh.msgh_local_port) != unsigned(task_port) \
+       and unsigned(kmsgh.msgh_local_port) != unsigned(kmsgh.msgh_remote_port):
+        disp = (kmsgh.msgh_bits & 0x1f00) >> 8
+        p_list += list(CollectPortsForAnalysis(kmsgh.msgh_local_port, disp))
+
+    if kmsgp.ikm_voucher:
+        p_list += list(CollectPortsForAnalysis(kmsgp.ikm_voucher, 0))
+
+    if kmsgh.msgh_bits & 0x80000000:
+        ## Complex message - look for descriptors
+        PrintProgressForKmsg()
+        (body, dschead, dsc_list) = GetKmsgDescriptors(kmsgp)
+        for dsc in dsc_list:
+            PrintProgressForKmsg()
+            dsc_type = unsigned(dsc.type.type)
+            if dsc_type == 0 or dsc_type == 2: ## 0 == port, 2 == ool port
+                if dsc_type == 0:
+                    ## its a port descriptor
+                    dsc_disp = dsc.port.disposition
+                    p_list += list(CollectPortsForAnalysis(dsc.port.name, dsc_disp))
+                else:
+                    ## it's an ool_ports descriptor which is an array of ports
+                    dsc_disp = dsc.ool_ports.disposition
+                    dispdata = Cast(dsc.ool_ports.address, 'struct ipc_port *')
+                    for pidx in range(dsc.ool_ports.count):
+                        PrintProgressForKmsg()
+                        p_list += list(CollectPortsForAnalysis(dispdata[pidx], dsc_disp))
+    return p_list
+
+def CollectKmsgPortRefs(task, task_port, kmsgp, p_refs):
+    """ Recursively collect all references to ports inside the kmsg 'kmsgp'
+        into the set 'p_refs'
+    """
+    p_list = CollectKmsgPorts(task, task_port, kmsgp)
+
+    ## Iterate over each ports we've collected, to see if they
+    ## have messages on them, and then recurse!
+    for p, pdisp in p_list:
+        ptype = (p.ip_object.io_bits & 0x7fff0000) >> 16
+        p_refs.add((p, pdisp, ptype))
+        if ptype != 0: ## don't bother with port sets
+            continue
+        ## If the port that's in-transit has messages already enqueued,
+        ## go through each of those messages and look for more ports!
+        if p.ip_messages.data.port.msgcount > 0:
+            p_kmsgp = Cast(p.ip_messages.data.port.messages.ikmq_base, 'ipc_kmsg_t')
+            kmsgheadp = p_kmsgp
+            while unsigned(p_kmsgp) > 0:
+                CollectKmsgPortRefs(task, p, p_kmsgp, p_refs)
+                p_kmsgp = p_kmsgp.ikm_next
+                if p_kmsgp == kmsgheadp:
+                    break;
+
+
+def FindKmsgPortRefs(instr, task, task_port, kmsgp, qport):
+    """ Look through a message, 'kmsgp' destined for 'task'. If we find
+        any port descriptors, remote, local, voucher, or other port that
+        matches 'qport', return a short description
+        which should match the format of GetIPCEntrySummary.
+    """
+
+    out_str = instr
+    p_list = CollectKmsgPorts(task, task_port, kmsgp)
+
+    ## Run through all ports we've collected looking for 'qport'
+    for p, pdisp in p_list:
+        PrintProgressForKmsg()
+        if unsigned(p) == unsigned(qport):
+            ## the port we're looking for was found in this message!
+            if len(out_str) > 0:
+                out_str += '\n'
+            out_str += GetInTransitPortSummary(p, pdisp, task_port, kmsgp)
+
+        ptype = (p.ip_object.io_bits & 0x7fff0000) >> 16
+        if ptype != 0: ## don't bother with port sets
+            continue
+
+        ## If the port that's in-transit has messages already enqueued,
+        ## go through each of those messages and look for more ports!
+        if p.ip_messages.data.port.msgcount > 0:
+            p_kmsgp = Cast(p.ip_messages.data.port.messages.ikmq_base, 'ipc_kmsg_t')
+            kmsgheadp = p_kmsgp
+            while unsigned(p_kmsgp) > 0:
+                out_str = FindKmsgPortRefs(out_str, task, p, p_kmsgp, qport)
+                p_kmsgp = p_kmsgp.ikm_next
+                if p_kmsgp == kmsgheadp:
+                    break
+    return out_str
+
+
+port_iteration_do_print_taskname = False
+registeredport_idx = -10
+excports_idx = -20
+intransit_idx = -1000
+taskports_idx = -2000
+thports_idx = -3000
+
+def IterateAllPorts(tasklist, func, ctx, include_psets, follow_busyports, should_log):
+    """ Iterate over all ports in the system, calling 'func'
+        for each entry in 
+    """
+    global port_iteration_do_print_taskname
+    global intransit_idx, taskports_idx, thports_idx, registeredport_idx, excports_idx
+
+    ## XXX: also host special ports
+
+    entry_port_type_mask = 0x00070000
+    if include_psets:
+        entry_port_type_mask = 0x000f0000
+
+    if tasklist is None:
+        tasklist = kern.tasks
+        tasklist += kern.terminated_tasks
+
+    tidx = 1
+
+    for t in tasklist:
+        # Write a progress line.  Using stderr avoids automatic newline when
+        # writing to stdout from lldb.  Blank spaces at the end clear out long
+        # lines.
+        if should_log:
+            procname = ""
+            if not t.active:
+                procname = 'terminated: '
+            if t.halting:
+                procname += 'halting: '
+            t_p = Cast(t.bsd_info, 'proc *')
+            if unsigned(t_p) != 0:
+                procname += str(t_p.p_name)
+            elif unsigned(t.task_imp_base) != 0 and hasattr(t.task_imp_base, 'iit_procname'):
+                procname += str(t.task_imp_base.iit_procname)
+            sys.stderr.write("  checking {:s} ({}/{})...{:50s}\r".format(procname, tidx, len(tasklist), ''))
+        tidx += 1
+
+        port_iteration_do_print_taskname = True
+        space = t.itk_space
+        num_entries = int(space.is_table_size)
+        is_tableval = space.is_table
+        idx = 0
+        while idx < num_entries:
+            entry_val = GetObjectAtIndexFromArray(is_tableval, idx)
+            entry_bits= unsigned(entry_val.ie_bits)
+            entry_obj = 0
+            entry_str = ''
+            entry_name = "{:x}".format( (idx << 8 | entry_bits >> 24) )
+
+            entry_disp = GetDispositionFromEntryType(entry_bits)
+
+            ## If the entry in the table represents a port of some sort,
+            ## then make the callback provided
+            if int(entry_bits) & entry_port_type_mask:
+                eport = Cast(entry_val.ie_object, 'ipc_port_t')
+                ## Make the callback
+                func(t, space, ctx, idx, entry_val, eport, entry_disp)
+
+                ## if the port has pending messages, look through
+                ## each message for ports (and recurse)
+                if follow_busyports and unsigned(eport) > 0 and eport.ip_messages.data.port.msgcount > 0:
+                    ## collect all port references from all messages
+                    kmsgp = Cast(eport.ip_messages.data.port.messages.ikmq_base, 'ipc_kmsg_t')
+                    kmsgheadp = kmsgp
+                    while unsigned(kmsgp) > 0:
+                        p_refs = set()
+                        CollectKmsgPortRefs(t, eport, kmsgp, p_refs)
+                        for (port, pdisp, ptype) in p_refs:
+                            func(t, space, ctx, intransit_idx, None, port, pdisp)
+                        kmsgp = kmsgp.ikm_next
+                        if kmsgp == kmsgheadp:
+                            break
+
+            idx = idx + 1
+        ## while (idx < num_entries)
+
+        ## Task ports (send rights)
+        if unsigned(t.itk_sself) > 0:
+            func(t, space, ctx, taskports_idx, 0, t.itk_sself, 17)
+        if unsigned(t.itk_host) > 0:
+            func(t, space, ctx, taskports_idx, 0, t.itk_host, 17)
+        if unsigned(t.itk_bootstrap) > 0:
+            func(t, space, ctx, taskports_idx, 0, t.itk_bootstrap, 17)
+        if unsigned(t.itk_seatbelt) > 0:
+            func(t, space, ctx, taskports_idx, 0, t.itk_seatbelt, 17)
+        if unsigned(t.itk_gssd) > 0:
+            func(t, space, ctx, taskports_idx, 0, t.itk_gssd, 17)
+        if unsigned(t.itk_debug_control) > 0:
+            func(t, space, ctx, taskports_idx, 0, t.itk_debug_control, 17)
+        if unsigned(t.itk_task_access) > 0:
+            func(t, space, ctx, taskports_idx, 0, t.itk_task_access, 17)
+
+        ## Task name port (not a send right, just a naked ref)
+        if unsigned(t.itk_nself) > 0:
+            func(t, space, ctx, taskports_idx, 0,t.itk_nself, 0)
+
+        ## task resume port is a receive right to resume the task
+        if unsigned(t.itk_resume) > 0:
+            func(t, space, ctx, taskports_idx, 0, t.itk_resume, 16)
+
+        ## registered task ports (all send rights)
+        tr_idx = 0
+        tr_max = sizeof(t.itk_registered) / sizeof(t.itk_registered[0])
+        while tr_idx < tr_max:
+            tport = t.itk_registered[tr_idx]
+            if unsigned(tport) > 0:
+                try:
+                    func(t, space, ctx, registeredport_idx, 0, tport, 17)
+                except Exception, e:
+                    print("\texception looking through registered port {:d}/{:d} in {:s}".format(tr_idx,tr_max,t))
+                    pass
+            tr_idx += 1
+
+        ## Task exception ports
+        exidx = 0
+        exmax = sizeof(t.exc_actions) / sizeof(t.exc_actions[0])
+        while exidx < exmax: ## see: osfmk/mach/[arm|i386]/exception.h
+            export = t.exc_actions[exidx].port ## send right
+            if unsigned(export) > 0:
+                try:
+                    func(t, space, ctx, excports_idx, 0, export, 17)
+                except Exception, e:
+                    print("\texception looking through exception port {:d}/{:d} in {:s}".format(exidx,exmax,t))
+                    pass
+            exidx += 1
+
+        ## XXX: any  ports still valid after clearing IPC space?!
+
+        for thval in IterateQueue(t.threads, 'thread *', 'task_threads'):
+            ## XXX: look at block reason to see if it's in mach_msg_receive - then look at saved state / message
+
+            ## Thread port (send right)
+            if unsigned(thval.ith_sself) > 0:
+                thport = thval.ith_sself
+                func(t, space, ctx, thports_idx, 0, thport, 17) ## see: osfmk/mach/message.h
+            ## Thread special reply port (send-once right)
+            if unsigned(thval.ith_special_reply_port) > 0:
+                thport = thval.ith_special_reply_port
+                func(t, space, ctx, thports_idx, 0, thport, 18) ## see: osfmk/mach/message.h
+            ## Thread voucher port
+            if unsigned(thval.ith_voucher) > 0:
+                vport = thval.ith_voucher.iv_port
+                if unsigned(vport) > 0:
+                    vdisp = GetDispositionFromVoucherPort(vport)
+                    func(t, space, ctx, thports_idx, 0, vport, vdisp)
+            ## Thread exception ports
+            if unsigned(thval.exc_actions) > 0:
+                exidx = 0
+                while exidx < exmax: ## see: osfmk/mach/[arm|i386]/exception.h
+                    export = thval.exc_actions[exidx].port ## send right
+                    if unsigned(export) > 0:
+                        try:
+                            func(t, space, ctx, excports_idx, 0, export, 17)
+                        except Exception, e:
+                            print("\texception looking through exception port {:d}/{:d} in {:s}".format(exidx,exmax,t))
+                            pass
+                    exidx += 1
+            ## XXX: the message on a thread (that's currently being received)
+        ## for (thval in t.threads)
+    ## for (t in tasklist)
+
+
+# Macro: findportrights
+def FindPortRightsCallback(task, space, ctx, entry_idx, ipc_entry, ipc_port, port_disp):
+    """ Callback which uses 'ctx' as the (port,rights_types) tuple for which
+        a caller is seeking references. This should *not* be used from a
+        recursive call to IterateAllPorts.
+    """
+    global port_iteration_do_print_taskname
+
+    (qport, rights_type) = ctx
+    entry_name = ''
+    entry_str = ''
+    if unsigned(ipc_entry) != 0:
+        entry_bits = unsigned(ipc_entry.ie_bits)
+        entry_name = "{:x}".format( (entry_idx << 8 | entry_bits >> 24) )
+        if (int(entry_bits) & 0x001f0000) != 0 and unsigned(ipc_entry.ie_object) == unsigned(qport):
+            ## it's a valid entry, and it points to the port
+            entry_str = '\t' + GetIPCEntrySummary(ipc_entry, entry_name, rights_type)
+
+    procname = GetProcNameForTask(task)
+    if unsigned(ipc_port) != 0 and ipc_port.ip_messages.data.port.msgcount > 0:
+        sys.stderr.write("  checking {:s} busy-port {}:{:#x}...{:30s}\r".format(procname, entry_name, unsigned(ipc_port), ''))
+        ## Search through busy ports to find descriptors which could
+        ## contain the only reference to this port!
+        kmsgp = Cast(ipc_port.ip_messages.data.port.messages.ikmq_base, 'ipc_kmsg_t')
+        kmsgheadp = kmsgp
+        while unsigned(kmsgp):
+            entry_str = FindKmsgPortRefs(entry_str, task, ipc_port, kmsgp, qport)
+            kmsgp = kmsgp.ikm_next
+            if kmsgp == kmsgheadp:
+                break;
+    if len(entry_str) > 0:
+        sys.stderr.write("{:80s}\r".format(''))
+        if port_iteration_do_print_taskname:
+            print "Task: {0: <#x} {1: <s}".format(task, procname)
+            print '\t' + GetIPCEntrySummary.header
+            port_iteration_do_print_taskname = False
+        print entry_str
+
+@lldb_command('findportrights', 'R:S:')
+def FindPortRights(cmd_args=None, cmd_options={}):
+    """  Routine to locate and print all extant rights to a given port
+         Usage: findportrights [-R rights_type] [-S <ipc_space_t>] <ipc_port_t>
+                -S ipc_space    : only search the specified ipc space
+                -R rights_type  : only display rights matching the string 'rights_type'
+
+                types of rights:
+                    'Dead'  : Dead name
+                    'Set'   : Port set
+                    'S'     : Send right
+                    'R'     : Receive right
+                    'O'     : Send-once right
+                types of notifications:
+                    'd'     : Dead-Name notification requested
+                    's'     : Send-Possible notification armed
+                    'r'     : Send-Possible notification requested
+                    'n'     : No-Senders notification requested
+                    'x'     : Port-destroy notification requested
+    """
+    if not cmd_args:
+        raise ArgumentError("no port address provided")
+    port = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_port *')
+
+    rights_type = 0
+    if "-R" in cmd_options:
+        rights_type = cmd_options["-R"]
+
+    tasklist = None
+    if "-S" in cmd_options:
+        space = kern.GetValueFromAddress(cmd_options["-S"], 'struct ipc_space *')
+        tasklist = [ space.is_task ]
+
+    ## Don't include port sets
+    ## Don't recurse on busy ports (we do that manually)
+    ## DO log progress
+    IterateAllPorts(tasklist, FindPortRightsCallback, (port, rights_type), False, False, True)
+    sys.stderr.write("{:120s}\r".format(' '))
+
+    print "Done."
+    return
+# EndMacro: findportrights
+
+# Macro: countallports
+
+def CountPortsCallback(task, space, ctx, entry_idx, ipc_entry, ipc_port, port_disp):
+    """ Callback which uses 'ctx' as the set of all ports found in the
+        iteration. This should *not* be used from a recursive
+        call to IterateAllPorts.
+    """
+    global intransit_idx
+
+    (p_set, p_intransit, p_bytask) = ctx
+
+    ## Add the port address to the set of all port addresses
+    p_set.add(unsigned(ipc_port))
+
+    if entry_idx == intransit_idx:
+        p_intransit.add(unsigned(ipc_port))
+
+    if task.active or (task.halting and not task.active):
+        pname = str(Cast(task.bsd_info, 'proc *').p_name)
+        if not pname in p_bytask.keys():
+            p_bytask[pname] = { 'transit':0, 'table':0, 'other':0 }
+        if entry_idx == intransit_idx:
+            p_bytask[pname]['transit'] += 1
+        elif entry_idx >= 0:
+            p_bytask[pname]['table'] += 1
+        else:
+            p_bytask[pname]['other'] += 1
+
+
+@lldb_command('countallports', 'P')
+def CountAllPorts(cmd_args=None, cmd_options={}):
+    """ Routine to search for all as many references to ipc_port structures in the kernel
+        that we can find.
+        Usage: countallports [-P]
+                -P : include port sets in the count (default: NO)
+    """
+    p_set = set()
+    p_intransit = set()
+    p_bytask = {}
+
+    find_psets = False
+    if "-P" in cmd_options:
+        find_psets = True
+
+    ## optionally include port sets
+    ## DO recurse on busy ports
+    ## DO log progress
+    IterateAllPorts(None, CountPortsCallback, (p_set, p_intransit, p_bytask), find_psets, True, True)
+    sys.stderr.write("{:120s}\r".format(' '))
+
+    print "Total ports found: {:d}".format(len(p_set))
+    print "In Transit: {:d}".format(len(p_intransit))
+    print "By Task:"
+    for pname in sorted(p_bytask.keys()):
+        count = p_bytask[pname]
+        print "\t{: <20s}: table={: <5d}, transit={: <5d}, other={: <5d}".format(pname, count['table'], count['transit'], count['other'])
+    return
+# EndMacro: countallports
+
 # Macro: showpipestats
 @lldb_command('showpipestats')
 def ShowPipeStats(cmd_args=None):
@@ -531,10 +1429,10 @@ def ShowTaskBusyPorts(cmd_args=None):
         print ShowTaskBusyPorts.__doc__
         return
     task = kern.GetValueFromAddress(cmd_args[0], 'task_t')
-    print GetTaskBusyPorts(task)
+    PrintTaskBusyPorts(task)
     return
 
-def GetTaskBusyPorts(task):
+def PrintTaskBusyPorts(task):
     """ Prints all busy ports for a given task. ie. all receive rights belonging
         to this task that have enqueued messages.
         params:
@@ -544,15 +1442,15 @@ def GetTaskBusyPorts(task):
     """
     isp = task.itk_space
     i = 0
-    out_string = ""
     while i < isp.is_table_size:
         iep = addressof(isp.is_table[i])
         if iep.ie_bits & 0x00020000:
             port = Cast(iep.ie_object, 'ipc_port_t')
             if port.ip_messages.data.port.msgcount > 0:
-                out_string += GetPortSummary.header + GetPortSummary(port)
+                print PrintPortSummary.header
+                PrintPortSummary(port)
         i = i + 1
-    return out_string
+    return
 # EndMacro: showtaskbusyports
 
 # Macro: showallbusyports
@@ -562,32 +1460,86 @@ def ShowAllBusyPorts(cmd_args=None):
         have enqueued messages.
     """
     task_queue_head = kern.globals.tasks
-    
+
     for tsk in kern.tasks:
-        print GetTaskBusyPorts(tsk)
+        PrintTaskBusyPorts(tsk)
     return
 # EndMacro: showallbusyports
 
+# Macro: showbusyportsummary
+@lldb_command('showbusyportsummary')
+def ShowBusyPortSummary(cmd_args=None):
+    """ Routine to print a summary of information about all receive rights
+        on the system that have enqueued messages.
+    """
+    task_queue_head = kern.globals.tasks
+
+    ipc_table_size = 0
+    ipc_busy_ports = 0
+    ipc_msgs = 0
+
+    print GetTaskBusyIPCSummary.header
+    for tsk in kern.tasks:
+        (summary, table_size, nbusy, nmsgs) = GetTaskBusyIPCSummary(tsk)
+        ipc_table_size += table_size
+        ipc_busy_ports += nbusy
+        ipc_msgs += nmsgs
+        print summary
+    for t in kern.terminated_tasks:
+        (summary, table_size, nbusy, nmsgs) = GetTaskBusyIPCSummary(tsk)
+        ipc_table_size += table_size
+        ipc_busy_ports += nbusy
+        ipc_msgs += nmsgs
+        print summary
+    print "Total Table Size: {:d}, Busy Ports: {:d}, Messages in-flight: {:d}".format(ipc_table_size, ipc_busy_ports, ipc_msgs)
+    return
+# EndMacro: showbusyportsummary
+
+# Macro: showport:
+@lldb_command('showport','K')
+def ShowPort(cmd_args=None, cmd_options={}):
+    """ Routine that lists details about a given IPC port 
+        Syntax: (lldb) showport 0xaddr
+    """
+    show_kmsgs = True
+    if "-K" in cmd_options:
+        show_kmsgs = False
+    if not cmd_args:
+        print "Please specify the address of the port whose details you want to print"
+        print ShowPort.__doc__
+        return
+    port = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_port *')
+    print PrintPortSummary.header
+    PrintPortSummary(port, show_kmsgs)
+# EndMacro: showport
+
 # Macro: showmqueue:
-@lldb_command('showmqueue')
-def ShowMQueue(cmd_args=None):
+@lldb_command('showmqueue', "S:")
+def ShowMQueue(cmd_args=None, cmd_options={}):
     """ Routine that lists details about a given mqueue
-        Syntax: (lldb) showmqueue 0xaddr
+        Syntax: (lldb) showmqueue 0xaddr [-S ipc_space]
     """
     if not cmd_args:
         print "Please specify the address of the ipc_mqueue whose details you want to print"
         print ShowMQueue.__doc__
         return
+    space = 0
+    if "-S" in cmd_options:
+        space = kern.GetValueFromAddress(cmd_options["-S"], 'struct ipc_space *')
     mqueue = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_mqueue *')
-    wq_type = mqueue.data.pset.set_queue.wqs_wait_queue.wq_type
+    wq_type = mqueue.data.pset.setq.wqset_q.waitq_type
     if int(wq_type) == 3:
         psetoff = getfieldoffset('struct ipc_pset', 'ips_messages')
         pset = unsigned(ArgumentStringToInt(cmd_args[0])) - unsigned(psetoff)
-        print GetPortSetSummary.header + GetPortSetSummary(kern.GetValueFromAddress(pset, 'struct ipc_pset *'))
-    if int(wq_type) == 2:
+        print PrintPortSetSummary.header
+        PrintPortSetSummary(kern.GetValueFromAddress(pset, 'struct ipc_pset *'), space)
+    elif int(wq_type) == 2:
         portoff = getfieldoffset('struct ipc_port', 'ip_messages')
         port = unsigned(ArgumentStringToInt(cmd_args[0])) - unsigned(portoff)
-        print GetPortSummary.header + GetPortSummary(kern.GetValueFromAddress(port, 'struct ipc_port *'))
+        print PrintPortSummary.header
+        PrintPortSummary(kern.GetValueFromAddress(port, 'struct ipc_port *'))
+    else:
+        print "Invalid mqueue? (waitq type {:d} is invalid)".format(int(wq_type))
 # EndMacro: showmqueue
 
 # Macro: showkmsg:
@@ -605,17 +1557,21 @@ def ShowKMSG(cmd_args=[]):
 # EndMacro: showkmsg
 
 # Macro: showpset
-@lldb_command('showpset')
-def ShowPSet(cmd_args=None):
+@lldb_command('showpset', "S:")
+def ShowPSet(cmd_args=None, cmd_options={}):
     """ Routine that prints details for a given ipc_pset *
-        Syntax: (lldb) showpset 0xaddr
+        Syntax: (lldb) showpset 0xaddr [-S ipc_space]
     """
     if not cmd_args:
         print "Please specify the address of the pset whose details you want to print"
         print ShowPSet.__doc__
         return
-    
-    print GetPortSetSummary.header + GetPortSetSummary(kern.GetValueFromAddress(cmd_args[0], 'ipc_pset *'))
+    space = 0
+    if "-S" in cmd_options:
+        space = kern.GetValueFromAddress(cmd_options["-S"], 'struct ipc_space *')
+
+    print PrintPortSetSummary.header
+    PrintPortSetSummary(kern.GetValueFromAddress(cmd_args[0], 'ipc_pset *'), space)
 # EndMacro: showpset
 
 # IPC importance inheritance related macros.
@@ -661,23 +1617,27 @@ def GetIPCImportanceElemSummary(iie):
 
     out_str = ''
     fmt = "{: <#018x} {: <4s} {: <8d} {: <8d} {: <#018x} {: <#018x}"
-    type_str = 'TASK'
     if unsigned(iie.iie_bits) & 0x80000000:
         type_str = "INH"
+        inherit_count = 0
+    else:
+        type_str = 'TASK'
+        iit = Cast(iie, 'struct ipc_importance_task *')
+        inherit_count = sum(1 for i in IterateQueue(iit.iit_inherits, 'struct ipc_importance_inherit *',  'iii_inheritance'))
+
     refs = unsigned(iie.iie_bits) & 0x7fffffff
     made_refs = unsigned(iie.iie_made)
     kmsg_count = sum(1 for i in IterateQueue(iie.iie_kmsgs, 'struct ipc_kmsg *',  'ikm_inheritance'))
-    inherit_count = sum(1 for i in IterateQueue(iie.iie_inherits, 'struct ipc_importance_inherit *',  'iii_inheritance'))
     out_str += fmt.format(iie, type_str, refs, made_refs, kmsg_count, inherit_count)
     if config['verbosity'] > vHUMAN:
         if kmsg_count > 0:
             out_str += "\n\t"+ GetKMsgSummary.header 
             for k in IterateQueue(iie.iie_kmsgs, 'struct ipc_kmsg *',  'ikm_inheritance'):
-                out_str += "\t" + "{: <#018x}".format(k.ikm_header.msgh_remote_port) + '   ' + GetKMsgSummary(k).lstrip() 
+                out_str += "\t" + "{: <#018x}".format(k.ikm_header.msgh_remote_port) + '   ' + GetKMsgSummary(k, "\t").lstrip() 
             out_str += "\n"
         if inherit_count > 0:
             out_str += "\n\t" + GetIPCImportanceInheritSummary.header + "\n"
-            for i in IterateQueue(iie.iie_inherits, 'struct ipc_importance_inherit *',  'iii_inheritance'):
+            for i in IterateQueue(iit.iit_inherits, 'struct ipc_importance_inherit *',  'iii_inheritance'):
                 out_str += "\t" + GetIPCImportanceInheritSummary(i) + "\n"
             out_str += "\n"
         if type_str == "INH":
@@ -846,6 +1806,8 @@ def GetBankHandleSummary(handle_ptr):
         params: handle_ptr - uint64 number stored in handle of voucher.
         returns: str - summary of bank element
     """
+    if handle_ptr == 1 :
+        return "Bank task of Current task"
     elem = kern.GetValueFromAddress(handle_ptr, 'bank_element_t')
     if elem.be_type & 1 :
         ba = Cast(elem, 'struct bank_account *')
@@ -1056,5 +2018,63 @@ def ShowVoucher(cmd_args=[], cmd_options={}):
     voucher = kern.GetValueFromAddress(cmd_args[0], 'ipc_voucher_t')
     print GetIPCVoucherSummary.header
     print GetIPCVoucherSummary(voucher, show_entries=True)
-    
 
+def GetSpaceSendRightEntries(space, port):
+    """ Get entry summaries for all send rights to port address in an IPC space.
+        params:
+            space - the IPC space to search for send rights
+            port_addr - the port address to match, or 0 to get all send rights
+        returns: an array of IPC entries
+    """
+    entry_table = space.is_table
+    ports = int(space.is_table_size)
+    i = 0
+    entries = []
+
+    while i < ports:
+        entry = GetObjectAtIndexFromArray(entry_table, i)
+
+        entry_ie_bits = unsigned(entry.ie_bits)
+        if (entry_ie_bits & 0x00010000) != 0 and (not port or entry.ie_object == port):
+            entries.append(entry)
+        i += 1
+
+    return entries
+
+@lldb_command('showportsendrights')
+def ShowPortSendRights(cmd_args=[], cmd_options={}):
+    """ Display a list of send rights across all tasks for a given port.
+        Usage: (lldb) showportsendrights <ipc_port_t>
+    """
+    if not cmd_args:
+        raise ArgumentError("no port address provided")
+    port = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_port *')
+    i = 1
+
+    return FindPortRights(cmd_args=[unsigned(port)], cmd_options={'-R':'S'})
+
+
+@lldb_command('showtasksuspenders')
+def ShowTaskSuspenders(cmd_args=[], cmd_options={}):
+    """ Display the tasks and send rights that are holding a target task suspended.
+        Usage: (lldb) showtasksuspenders <task_t>
+    """
+    if not cmd_args:
+        raise ArgumentError("no task address provided")
+    task = kern.GetValueFromAddress(cmd_args[0], 'task_t')
+
+    if task.suspend_count == 0:
+        print "task {:#x} ({:s}) is not suspended".format(unsigned(task), Cast(task.bsd_info, 'proc_t').p_name)
+        return
+
+    # If the task has been suspended by the kernel (potentially by
+    # kperf, using task_suspend_internal) or a client of task_suspend2
+    # that does not convert its task suspension token to a port using
+    # convert_task_suspension_token_to_port, then it's impossible to determine
+    # which task did the suspension.
+    port = task.itk_resume
+    if not port:
+        print "task {:#x} ({:s}) is suspended but no resume port exists".format(unsigned(task), Cast(task.bsd_info, 'proc_t').p_name)
+        return
+
+    return FindPortRights(cmd_args=[unsigned(port)], cmd_options={'-R':'S'})