X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/fe8ab488e9161c46dd9885d58fc52996dc0249ff..0a7de7458d150b5d4dffc935ba399be265ef0a1a:/tools/lldbmacros/ipc.py diff --git a/tools/lldbmacros/ipc.py b/tools/lldbmacros/ipc.py old mode 100644 new mode 100755 index ec783959f..81090bbd8 --- a/tools/lldbmacros/ipc.py +++ b/tools/lldbmacros/ipc.py @@ -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 = "" + 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
+ Usage: showrights [-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 """ 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 + Usage: showtaskrights [-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 """ 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 + Usage: showtaskrightsbt [-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 """ 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 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 + """ + 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 + """ + 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'})