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
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
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
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
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:
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:
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
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
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
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
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
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):
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
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:
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") )
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 =''
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:
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"
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"
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"
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:
# 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):
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:
"""
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
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:
# 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.
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":
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 *')
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'})