xref: /xnu-8796.121.2/tools/lldbmacros/ipc.py (revision c54f35ca767986246321eb901baf8f5ff7923f6a)
1""" Please make sure you read the README file COMPLETELY BEFORE reading anything below.
2    It is very critical that you read coding guidelines in Section E in README file.
3"""
4from __future__ import absolute_import, division, print_function
5
6from builtins import hex
7from builtins import range
8
9from xnu import *
10import sys
11from utils import *
12from process import *
13from bank import *
14from waitq import *
15from ioreg import *
16from memory import *
17import xnudefines
18import kmemory
19
20@lldb_type_summary(['struct ipc_entry_table *', 'ipc_entry_table_t'])
21def PrintIpcEntryTable(array):
22    t, s = kalloc_array_decode(array, 'struct ipc_entry')
23    return "ptr = {:#x}, size = {:d}, elem_type = struct ipc_entry".format(unsigned(t), s)
24
25@lldb_type_summary(['struct ipc_port_requests_table *', 'ipc_port_requests_table_t'])
26def PrintIpcPortRequestTable(array):
27    t, s = kalloc_array_decode(array, 'struct ipc_port_requests')
28    return "ptr = {:#x}, size = {:d}, elem_type = struct ipc_port_requests".format(unsigned(t), s)
29
30def GetSpaceTable(space):
31    """ Return the tuple of (entries, size) of the table for a space
32    """
33    table = space.is_table.__smr_ptr
34    if table:
35        return kalloc_array_decode(table, 'struct ipc_entry')
36    return (None, 0)
37
38def GetSpaceEntriesWithBits(is_tableval, num_entries, mask):
39    base = is_tableval.GetSBValue().Dereference()
40    return (
41        (index, iep)
42        for index, iep in enumerate(base.xIterSiblings(1, num_entries), 1)
43        if  iep.xGetIntegerByName('ie_bits') & mask
44    )
45
46def GetSpaceObjectsWithBits(is_tableval, num_entries, mask, ty):
47    base = is_tableval.GetSBValue().Dereference()
48    return (
49        iep.xCreateValueFromAddress(
50            None,
51            iep.xGetIntegerByName('ie_object'),
52            ty,
53        )
54        for iep in base.xIterSiblings(1, num_entries)
55        if  iep.xGetIntegerByName('ie_bits') & mask
56    )
57
58
59@header("{0: <20s} {1: <6s} {2: <6s} {3: <10s} {4: <32s}".format("task", "pid", '#acts', "tablesize", "command"))
60def GetTaskIPCSummary(task, show_busy = False):
61    """ Display a task's ipc summary.
62        params:
63            task : core.value represeting a Task in kernel
64        returns
65            str - string of ipc info for the task
66    """
67    out_string = ''
68    format_string = "{0: <#20x} {1: <6d} {2: <6d} {3: <10d} {4: <32s}"
69    busy_format = " {0: <10d} {1: <6d}"
70    proc_name = ''
71    if not task.active:
72        proc_name = 'terminated: '
73    if task.halting:
74        proc_name += 'halting: '
75    proc_name += GetProcNameForTask(task)
76    _, table_size = GetSpaceTable(task.itk_space)
77    out_string += format_string.format(task, GetProcPIDForTask(task), task.thread_count, table_size, proc_name)
78    if show_busy:
79        nbusy, nmsgs = GetTaskBusyPortsSummary(task)
80        out_string += busy_format.format(nbusy, nmsgs)
81        return (out_string, table_size, nbusy, nmsgs)
82    return (out_string, table_size)
83
84@header("{0: <20s} {1: <6s} {2: <6s} {3: <10s} {4: <32s} {5: <10s} {6: <6s}".format("task", "pid", '#acts', "tablesize", "command", "#busyports", "#kmsgs"))
85def GetTaskBusyIPCSummary(task):
86    return GetTaskIPCSummary(task, True)
87
88def GetTaskBusyPortsSummary(task):
89    is_tableval, num_entries = GetSpaceTable(task.itk_space)
90    port_ty = gettype('struct ipc_port')
91    nbusy = 0
92    nmsgs = 0
93
94    if is_tableval:
95        ports = GetSpaceObjectsWithBits(is_tableval, num_entries, 0x00020000,
96            gettype('struct ipc_port'))
97
98    for port in ports:
99        count = port.xGetIntegerByPath('.ip_messages.imq_msgcount')
100        if count:
101            nbusy += 1
102            nmsgs += count
103
104    return (nbusy, nmsgs)
105
106
107@header("{:<20s} {:<20s} {:<10s} {:>6s}  {:<20s}  {:>8s}  {:<20s} {:s}".format(
108            "port", "waitqueue", "recvname", "refs", "receiver", "nmsgs", "service", "dest/kobject"))
109def PrintPortSummary(port, show_kmsg_summary=True, show_sets=False, prefix="", O=None):
110    """ Display a port's summary
111        params:
112            port : core.value representing a port in the kernel
113        returns
114            str  : string of ipc info for the given port
115    """
116
117    format_string = "{:<#20x} {:<#20x} {:#010x} {:>6d}  {:<#20x}  {:>8d}  {:<20s} {:<s}"
118    receiver_name = port.ip_messages.imq_receiver_name
119    splabel_name = 'N/A'
120    space = 0
121    refs = 0
122
123    if port.ip_object.io_bits & 0x80000000:
124        if receiver_name:
125            space = unsigned(port.ip_receiver)
126
127        try:
128            if port.ip_service_port:
129                splabel = Cast(port.ip_splabel, 'struct ipc_service_port_label *')
130                splabel_name = str(splabel.ispl_service_name) # Not on RELEASE kernel
131        except:
132            splabel_name = 'unknown'
133
134        dest_str = GetPortDestProc(port)[1]
135    else:
136        dest_str = "inactive-port"
137
138    print(prefix + format_string.format(unsigned(port), addressof(port.ip_waitq),
139        unsigned(receiver_name), port.ip_object.io_references, space,
140        port.ip_messages.imq_msgcount, splabel_name, dest_str))
141
142    if show_kmsg_summary:
143        with O.table(prefix + GetKMsgSummary.header):
144            for kmsgp in IterateCircleQueue(port.ip_messages.imq_messages, 'ipc_kmsg', 'ikm_link'):
145                print(prefix + GetKMsgSummary(kmsgp, prefix))
146
147    wq = Waitq(addressof(port.ip_waitq))
148    if show_sets and wq.hasSets():
149        def doit(wq):
150            for wqs in Waitq(addressof(port.ip_waitq)).iterateSets():
151                PrintPortSetSummary(wqs.asPset(), space=port.ip_receiver, verbose=False, O=O)
152
153        if O is None:
154            print(PrintPortSetSummary.header)
155            doit(wq)
156        else:
157            with O.table(PrintPortSetSummary.header, indent=True):
158                doit(wq)
159                print("")
160
161def GetPortDispositionString(disp):
162    if (disp < 0): ## use negative numbers for request ports
163        portname = 'notify'
164        if disp == -1:
165            disp_str = 'reqNS'
166        elif disp == -2:
167            disp_str = 'reqPD'
168        elif disp == -3:
169            disp_str = 'reqSPa'
170        elif disp == -4:
171            disp_str = 'reqSPr'
172        elif disp == -5:
173            disp_str = 'reqSPra'
174        else:
175            disp_str = '-X'
176    ## These dispositions should match those found in osfmk/mach/message.h
177    elif disp == 16:
178        disp_str = 'R'  ## receive
179    elif disp == 24:
180        disp_str = 'dR' ## dispose receive
181    elif disp == 17:
182        disp_str = 'S'  ## (move) send
183    elif disp == 19:
184        disp_str = 'cS' ## copy send
185    elif disp == 20:
186        disp_str = 'mS' ## make send
187    elif disp == 25:
188        disp_str = 'dS' ## dispose send
189    elif disp == 18:
190        disp_str = 'O'  ## send-once
191    elif disp == 21:
192        disp_str = 'mO' ## make send-once
193    elif disp == 26:
194        disp_str = 'dO' ## dispose send-once
195    ## faux dispositions used to string-ify IPC entry types
196    elif disp == 100:
197        disp_str = 'PS' ## port set
198    elif disp == 101:
199        disp_str = 'dead' ## dead name
200    elif disp == 102:
201        disp_str = 'L' ## LABELH
202    elif disp == 103:
203        disp_str = 'V' ## Thread voucher (thread->ith_voucher->iv_port)
204    ## Catch-all
205    else:
206        disp_str = 'X'  ## invalid
207    return disp_str
208
209def GetPortPDRequest(port):
210    """ Returns the port-destroyed notification port if any
211    """
212    if port.ip_has_watchport:
213        return port.ip_twe.twe_pdrequest
214    if not port.ip_specialreply:
215        return port.ip_pdrequest
216    return 0
217
218def GetKmsgHeader(kmsgp):
219    """ Helper to get mach message header of a kmsg.
220        Assumes the kmsg has not been put to user.
221    params:
222        kmsgp : core.value representing the given ipc_kmsg_t struct
223    returns:
224        Mach message header for kmsgp
225    """
226    inline_buffer = kmsgp + sizeof(dereference(kmsgp))
227
228    if (kmsgp.ikm_type <= int(GetEnumValue('ipc_kmsg_type_t', 'IKM_TYPE_UDATA_OOL'))):
229        return kern.GetValueFromAddress(inline_buffer, 'mach_msg_header_t *')
230    else:
231        vec = kern.GetValueFromAddress(inline_buffer, 'ipc_kmsg_vector_t *')
232        return kern.GetValueFromAddress(unsigned(vec.kmsgv_data), 'mach_msg_header_t *')
233
234@header("{:<20s} {:<20s} {:<20s} {:<10s} {:>6s}  {:<20s}  {:<8s}  {:<26s} {:<26s}".format(
235            "", "kmsg", "header", "msgid", "size", "reply-port", "disp", "source", "destination"))
236def GetKMsgSummary(kmsgp, prefix_str=""):
237    """ Display a summary for type ipc_kmsg_t
238        params:
239            kmsgp : core.value representing the given ipc_kmsg_t struct
240        returns:
241            str   : string of summary info for the given ipc_kmsg_t instance
242    """
243    kmsghp = GetKmsgHeader(kmsgp)
244    kmsgh = dereference(kmsghp)
245    out_string = ""
246    out_string += "{:<20s} {:<#20x} {:<#20x} {kmsgh.msgh_id:#010x} {kmsgh.msgh_size:>6d}  {kmsgh.msgh_local_port:<#20x}  ".format(
247            '', unsigned(kmsgp), unsigned(kmsghp), kmsgh=kmsghp)
248    prefix_str = "{:<20s} ".format(' ') + prefix_str
249    disposition = ""
250    bits = kmsgh.msgh_bits & 0xff
251
252    # remote port
253    if bits == 17:
254        disposition = "rS"
255    elif bits == 18:
256        disposition = "rO"
257    else :
258        disposition = "rX" # invalid
259
260    out_string += "{:<2s}".format(disposition)
261
262    # local port
263    disposition = ""
264    bits = (kmsgh.msgh_bits & 0xff00) >> 8
265
266    if bits == 17:
267        disposition = "lS"
268    elif bits == 18:
269        disposition = "lO"
270    elif bits == 0:
271        disposition = "l-"
272    else:
273        disposition = "lX"  # invalid
274
275    out_string += "{:<2s}".format(disposition)
276
277    # voucher
278    disposition = ""
279    bits = (kmsgh.msgh_bits & 0xff0000) >> 16
280
281    if bits == 17:
282        disposition = "vS"
283    elif bits == 0:
284        disposition = "v-"
285    else:
286        disposition = "vX"
287
288    out_string += "{:<2s}".format(disposition)
289
290    # complex message
291    if kmsgh.msgh_bits & 0x80000000:
292        out_string += "{0: <1s}".format("c")
293    else:
294        out_string += "{0: <1s}".format("s")
295
296    # importance boost
297    if kmsgh.msgh_bits & 0x20000000:
298        out_string += "{0: <1s}".format("I")
299    else:
300        out_string += "{0: <1s}".format("-")
301
302    dest_proc_name = ""
303    if GetKmsgHeader(kmsgp).msgh_remote_port:
304        dest_proc_name = GetPortDestinationSummary(GetKmsgHeader(kmsgp).msgh_remote_port)
305
306    out_string += "  {:<26s} {:<26s}\n".format(GetKMsgSrc(kmsgp), dest_proc_name)
307
308    if kmsgh.msgh_bits & 0x80000000:
309        out_string += prefix_str + "\t" + GetKMsgComplexBodyDesc.header + "\n"
310        out_string += prefix_str + "\t" + GetKMsgComplexBodyDesc(kmsgp, prefix_str + "\t") + "\n"
311
312    return out_string
313
314@header("{: <20s} {: <20s} {: <10s}".format("descriptor", "address", "size"))
315def GetMachMsgOOLDescriptorSummary(desc):
316    """ Returns description for mach_msg_ool_descriptor_t * object
317    """
318    format_string = "{: <#20x} {: <#20x} {:#010x}"
319    out_string = format_string.format(desc, desc.address, desc.size)
320    return out_string
321
322
323def GetKmsgDescriptors(kmsgp):
324    """ Get a list of descriptors in a complex message
325    """
326    kmsghp = GetKmsgHeader(kmsgp)
327    kmsgh = dereference(kmsghp)
328    if not (kmsgh.msgh_bits & 0x80000000):
329        return []
330    ## Something in the python/lldb types is not getting alignment correct here.
331    ## I'm grabbing a pointer to the body manually, and using tribal knowledge
332    ## of the location of the descriptor count to get this correct
333    body = Cast(addressof(Cast(addressof(kmsgh), 'char *')[sizeof(kmsgh)]), 'mach_msg_body_t *')
334    #dsc_count = body.msgh_descriptor_count
335    dsc_count = dereference(Cast(body, 'uint32_t *'))
336    #dschead = Cast(addressof(body[1]), 'mach_msg_descriptor_t *')
337    dschead = Cast(addressof(Cast(addressof(body[0]), 'char *')[sizeof('uint32_t')]), 'mach_msg_descriptor_t *')
338    dsc_list = []
339    for i in range(dsc_count):
340        dsc_list.append(dschead[i])
341    return (body, dschead, dsc_list)
342
343def GetKmsgTotalDescSize(kmsgp):
344    """ Helper to get total descriptor size of a kmsg.
345        Assumes the kmsg has full kernel representation (header and descriptors)
346    params:
347        kmsgp : core.value representing the given ipc_kmsg_t struct
348    returns:
349        Total descriptor size
350    """
351    kmsghp = GetKmsgHeader(kmsgp)
352    kmsgh = dereference(kmsghp)
353    dsc_count = 0
354
355    if kmsgh.msgh_bits & 0x80000000: # MACH_MSGH_BITS_COMPLEX
356        (body, _, _) = GetKmsgDescriptors(kmsgp)
357        dsc_count = dereference(Cast(body, 'uint32_t *'))
358
359    return dsc_count * sizeof('mach_msg_descriptor_t')
360
361@header("{: <20s} {: <8s} {: <20s} {: <10s} {: <20s}".format("kmsgheader", "size", "body", "ds_count", "dsc_head"))
362def GetKMsgComplexBodyDesc(kmsgp, prefix_str=""):
363    """ Routine that prints a complex kmsg's body
364    """
365    kmsghp = GetKmsgHeader(kmsgp)
366    kmsgh = dereference(kmsghp)
367    if not (kmsgh.msgh_bits & 0x80000000):
368        return ""
369    format_string = "{: <#20x} {: <#8x} {: <#20x} {:#010x} {: <#20x}"
370    out_string = ""
371
372    (body, dschead, dsc_list) = GetKmsgDescriptors(kmsgp)
373    out_string += format_string.format(kmsghp, sizeof(dereference(kmsghp)), body, len(dsc_list), dschead)
374    for dsc in dsc_list:
375        try:
376            dsc_type = unsigned(dsc.type.type)
377            out_string += "\n" + prefix_str + "Descriptor: " + xnudefines.mach_msg_type_descriptor_strings[dsc_type]
378            if dsc_type == 0:
379                # its a port.
380                p = dsc.port.name
381                dstr = GetPortDispositionString(dsc.port.disposition)
382                out_string += " disp:{:s}, name:{: <#20x}".format(dstr, p)
383            elif unsigned(dsc.type.type) in (1,3):
384                # its OOL DESCRIPTOR or OOL VOLATILE DESCRIPTOR
385                ool = dsc.out_of_line
386                out_string += " " + GetMachMsgOOLDescriptorSummary(addressof(ool))
387        except:
388            out_string += "\n" + prefix_str + "Invalid Descriptor: {}".format(dsc)
389    return out_string
390
391def GetKmsgTrailer(kmsgp):
392    """ Helper to get trailer address of a kmsg
393    params:
394        kmsgp : core.value representing the given ipc_kmsg_t struct
395    returns:
396        Trailer address
397    """
398    kmsghp = GetKmsgHeader(kmsgp)
399    kmsgh = dereference(kmsghp)
400
401    if (kmsgp.ikm_type == int(GetEnumValue('ipc_kmsg_type_t', 'IKM_TYPE_ALL_INLINED')) or
402        kmsgp.ikm_type == int(GetEnumValue('ipc_kmsg_type_t', 'IKM_TYPE_KDATA_OOL'))):
403        return kern.GetValueFromAddress(unsigned(kmsghp) + kmsgh.msgh_size, 'mach_msg_max_trailer_t *')
404    else:
405        if kmsgh.msgh_bits & 0x80000000: # MACH_MSGH_BITS_COMPLEX
406            content_size = kmsgh.msgh_size - sizeof('mach_msg_base_t') - GetKmsgTotalDescSize(kmsgp)
407        else:
408            content_size = kmsgh.msgh_size - sizeof('mach_msg_header_t')
409        return kern.GetValueFromAddress(unsigned(kmsgp.ikm_udata) + content_size, 'mach_msg_max_trailer_t *')
410
411def GetKMsgSrc(kmsgp):
412    """ Routine that prints a kmsg's source process and pid details
413        params:
414            kmsgp : core.value representing the given ipc_kmsg_t struct
415        returns:
416            str  : string containing the name and pid of the kmsg's source proc
417    """
418    trailer = GetKmsgTrailer(kmsgp)
419    kmsgpid = Cast(trailer, 'uint *')[10] # audit_token.val[5]
420    return "{0:s} ({1:d})".format(GetProcNameForPid(kmsgpid), kmsgpid)
421
422@header("{:<20s} {:<20s} {:<10s} {:>6s}  {:<6s}".format(
423            "portset", "waitqueue", "name", "refs", "flags"))
424def PrintPortSetSummary(pset, space=0, verbose=True, O=None):
425    """ Display summary for a given struct ipc_pset *
426        params:
427            pset : core.value representing a pset in the kernel
428        returns:
429            str  : string of summary information for the given pset
430    """
431    show_kmsg_summary = False
432    if config['verbosity'] > vHUMAN :
433        show_kmsg_summary = True
434
435    wqs = Waitq(addressof(pset.ips_wqset))
436
437    local_name = unsigned(pset.ips_wqset.wqset_index) << 8
438    dest = "-"
439    if space:
440        is_tableval, _ = GetSpaceTable(space)
441        if is_tableval:
442            entry_val = GetObjectAtIndexFromArray(is_tableval, local_name >> 8)
443            local_name |= unsigned(entry_val.ie_bits) >> 24
444        dest = GetSpaceProcDesc(space)
445    else:
446        for wq in wqs.iterateMembers():
447            dest = GetSpaceProcDesc(wq.asPort().ip_receiver)
448
449    if pset.ips_object.io_bits & 0x80000000:
450        state = "ASet"
451    else:
452        state = "DSet"
453
454    print("{:<#20x} {:<#20x} {:#010x} {:>6d}  {:<6s}  {:<20s}".format(
455        unsigned(pset), addressof(pset.ips_wqset), local_name,
456        pset.ips_object.io_references, "ASet", dest))
457
458    if verbose and wqs.hasThreads():
459        with O.table("{:<20s} {:<20s}".format('waiter', 'event'), indent=True):
460            for thread in wqs.iterateThreads():
461                print("{:<#20x} {:<#20x}".format(unsigned(thread), thread.wait_event))
462            print("")
463
464    if verbose and wqs.hasMembers():
465       with O.table(PrintPortSummary.header, indent=True):
466           for wq in wqs.iterateMembers():
467               portval = wq.asPort()
468               PrintPortSummary(wq.asPort(), show_kmsg_summary=show_kmsg_summary, O=O)
469           print("")
470
471
472
473# Macro: showipc
474
475@lldb_command('showipc')
476def ShowIPC(cmd_args=None):
477    """  Routine to print data for the given IPC space
478         Usage: showipc <address of ipc space>
479    """
480    if not cmd_args:
481        print("No arguments passed")
482        print(ShowIPC.__doc__)
483        return False
484    ipc = kern.GetValueFromAddress(cmd_args[0], 'ipc_space *')
485    if not ipc:
486        print("unknown arguments:", str(cmd_args))
487        return False
488    print(PrintIPCInformation.header)
489    PrintIPCInformation(ipc, False, False)
490
491# EndMacro: showipc
492
493# Macro: showtaskipc
494
495@lldb_command('showtaskipc')
496def ShowTaskIPC(cmd_args=None):
497    """  Routine to print IPC summary of given task
498         Usage: showtaskipc <address of task>
499    """
500    if not cmd_args:
501        print("No arguments passed")
502        print(ShowTaskIPC.__doc__)
503        return False
504    tval = kern.GetValueFromAddress(cmd_args[0], 'task *')
505    if not tval:
506        print("unknown arguments:", str(cmd_args))
507        return False
508    print(GetTaskSummary.header + " " + GetProcSummary.header)
509    pval = GetProcFromTask(tval)
510    print(GetTaskSummary(tval) + " " + GetProcSummary(pval))
511    print(GetTaskBusyIPCSummary.header)
512    summary, _, _, _ = GetTaskBusyIPCSummary(tval)
513    print(summary)
514
515# EndMacro: showtaskipc
516
517# Macro: showallipc
518
519@lldb_command('showallipc')
520def ShowAllIPC(cmd_args=None):
521    """  Routine to print IPC summary of all tasks
522         Usage: showallipc
523    """
524    for t in kern.tasks:
525        print(GetTaskSummary.header + " " + GetProcSummary.header)
526        pval = GetProcFromTask(t)
527        print(GetTaskSummary(t) + " " + GetProcSummary(pval))
528        print(PrintIPCInformation.header)
529        PrintIPCInformation(t.itk_space, False, False) + "\n\n"
530
531# EndMacro: showallipc
532
533@lldb_command('showipcsummary', fancy=True)
534def ShowIPCSummary(cmd_args=None, cmd_options={}, O=None):
535    """ Summarizes the IPC state of all tasks.
536        This is a convenient way to dump some basic clues about IPC messaging. You can use the output to determine
537        tasks that are candidates for further investigation.
538    """
539    with O.table(GetTaskIPCSummary.header):
540        ipc_table_size = 0
541
542        l = [ GetTaskIPCSummary(t) for t in kern.tasks ]
543        l.sort(key = lambda e: e[1], reverse=True)
544
545        for e in l:
546            print(e[0])
547            ipc_table_size += e[1]
548
549        for t in kern.terminated_tasks:
550            ipc_table_size += GetTaskIPCSummary(t)[1]
551
552        print("Total Table size: {:d}".format(ipc_table_size))
553
554def GetKObjectFromPort(portval):
555    """ Get Kobject description from the port.
556        params: portval - core.value representation of 'ipc_port *' object
557        returns: str - string of kobject information
558    """
559    io_bits       = unsigned(portval.ip_object.io_bits)
560    objtype_index = io_bits & 0x3ff
561
562    if not objtype_index:
563        return "not a kobject"
564
565    kobject_addr  = kern.StripKernelPAC(unsigned(portval.ip_kobject))
566    objtype_str   = GetEnumName('ipc_kotype_t', objtype_index, "IKOT_")
567
568    desc_str = "{:<#20x} {:<16s}".format(kobject_addr, objtype_str)
569
570    if not kobject_addr:
571        pass
572
573    elif objtype_str == 'IOKIT_OBJECT':
574        iokit_classnm = GetObjectTypeStr(portval.ip_kobject)
575        if not iokit_classnm:
576            desc_str += " <unknown class>"
577        else:
578            desc_str += re.sub(r'vtable for ', r' ', iokit_classnm)
579
580    elif objtype_str[:5] == 'TASK_':
581        task = value(portval.GetSBValue().xCreateValueFromAddress(
582            None, kobject_addr, gettype('struct task')).AddressOf())
583        if GetProcFromTask(task):
584            desc_str += " {:s}({:d})".format(GetProcNameForTask(task), GetProcPIDForTask(task))
585
586    return desc_str
587
588def GetSpaceProcDesc(space):
589    """ Display the name and pid of a space's task
590        params:
591            space: core.value representing a pointer to a space
592        returns:
593            str  : string containing receiver's name and pid
594    """
595    task = space.is_task
596    if not GetProcFromTask(task):
597        return "task {:<#20x}".format(unsigned(task))
598    return "{:s}({:d})".format(GetProcNameForTask(task), GetProcPIDForTask(task))
599
600def GetPortDestProc(port):
601    """ Display the name and pid of a given port's receiver
602        params:
603            port : core.value representing a pointer to a port in the kernel
604        returns:
605            str  : string containing receiver's name and pid
606    """
607
608    bits = unsigned(port.ip_object.io_bits)
609    name = unsigned(port.ip_messages.imq_receiver_name)
610
611    if bits & 0x3ff:
612        return ('', GetKObjectFromPort(port))
613
614    if bits & 0x80000000 == 0:
615        return ('', 'inactive-port')
616
617    if name == 0:
618        return ('{:<#20x}'.format(port.ip_destination), 'in-transit')
619
620    return ('{:<#20x}'.format(name), GetSpaceProcDesc(port.ip_receiver))
621
622@header("{:<20s} {:<20s}".format("destname", "destination") )
623def GetPortDestinationSummary(port):
624    """ Get destination information for a port.
625        params: port - core.value representation of 'ipc_port *' object
626        returns: str - string of info about ports destination
627    """
628    a, b = GetPortDestProc(port)
629    return "{:<20s} {:<20s}".format(a, b)
630
631@lldb_type_summary(['ipc_entry_t'])
632@header("{: <20s} {: <12s} {: <8s} {: <8s} {: <8s} {: <8s} {: <20s} {: <20s}".format("object", "name", "rite", "urefs", "nsets", "nmsgs", "destname", "destination"))
633def GetIPCEntrySummary(entry, ipc_name='', rights_filter=0):
634    """ Get summary of a ipc entry.
635        params:
636            entry - core.value representing ipc_entry_t in the kernel
637            ipc_name - str of format '0x0123' for display in summary.
638        returns:
639            str - string of ipc entry related information
640
641        types of rights:
642            'Dead'  : Dead name
643            'Set'   : Port set
644            'S'     : Send right
645            'R'     : Receive right
646            'O'     : Send-once right
647            'm'     : Immovable send port
648            'i'     : Immovable receive port
649            'g'     : No grant port
650        types of notifications:
651            'd'     : Dead-Name notification requested
652            's'     : Send-Possible notification armed
653            'r'     : Send-Possible notification requested
654            'n'     : No-Senders notification requested
655            'x'     : Port-destroy notification requested
656    """
657    out_str = ''
658    entry_ptr = int(hex(entry), 16)
659    format_string = "{: <#20x} {: <12s} {: <8s} {: <8d} {: <8d} {: <8d} {: <20s} {: <20s}"
660    right_str = ''
661    destname_str = ''
662    destination_str = ''
663
664    ie_object = entry.ie_object
665    ie_bits = int(entry.ie_bits)
666    io_bits = int(ie_object.io_bits) if ie_object else 0
667    urefs = int(ie_bits & 0xffff)
668    nsets = 0
669    nmsgs = 0
670    if ie_bits & 0x00100000 :
671        right_str = 'Dead'
672    elif ie_bits & 0x00080000:
673        right_str = 'Set'
674        psetval = kern.CreateTypedPointerFromAddress(unsigned(ie_object), 'struct ipc_pset')
675        wqs = Waitq(addressof(psetval.ips_wqset))
676        members = 0
677        for m in wqs.iterateMembers(): members += 1
678        destname_str = "{:d} Members".format(members)
679    else:
680        if ie_bits & 0x00010000 :
681            if ie_bits & 0x00020000 :
682                # SEND + RECV
683                right_str = 'SR'
684            else:
685                # SEND only
686                right_str = 'S'
687        elif ie_bits & 0x00020000:
688            # RECV only
689            right_str = 'R'
690        elif ie_bits & 0x00040000 :
691            # SEND_ONCE
692            right_str = 'O'
693        portval = kern.CreateTypedPointerFromAddress(unsigned(ie_object), 'struct ipc_port')
694        if int(entry.ie_request) != 0:
695            requestsval, _ = kalloc_array_decode(portval.ip_requests, 'struct ipc_port_request')
696            sorightval = requestsval[int(entry.ie_request)].ipr_soright
697            soright_ptr = unsigned(sorightval)
698            if soright_ptr != 0:
699                # dead-name notification requested
700                right_str += 'd'
701                # send-possible armed
702                if soright_ptr & 0x1 : right_str +='s'
703                # send-possible requested
704                if soright_ptr & 0x2 : right_str +='r'
705        # No-senders notification requested
706        if portval.ip_nsrequest != 0: right_str += 'n'
707        # port-destroy notification requested
708        if GetPortPDRequest(portval): right_str += 'x'
709        # Immovable receive rights
710        if portval.ip_immovable_receive != 0: right_str += 'i'
711        # Immovable send rights
712        if portval.ip_immovable_send != 0: right_str += 'm'
713        # No-grant Port
714        if portval.ip_no_grant != 0: right_str += 'g'
715        # Port with SB filtering on
716        if io_bits & 0x00001000 != 0: right_str += 'f'
717
718        # early-out if the rights-filter doesn't match
719        if rights_filter != 0 and rights_filter != right_str:
720            return ''
721
722        # now show the port destination part
723        destname_str = GetPortDestinationSummary(portval)
724        # Get the number of sets to which this port belongs
725        nsets = len([s for s in Waitq(addressof(portval.ip_waitq)).iterateSets()])
726        nmsgs = portval.ip_messages.imq_msgcount
727
728    # append the generation to the name value
729    # (from osfmk/ipc/ipc_entry.h)
730    # bits    rollover period
731    # 0 0     64
732    # 0 1     48
733    # 1 0     32
734    # 1 1     16
735    ie_gen_roll = { 0:'.64', 1:'.48', 2:'.32', 3:'.16' }
736    ipc_name = '{:s}{:s}'.format(ipc_name.strip(), ie_gen_roll[(ie_bits & 0x00c00000) >> 22])
737
738    if rights_filter == 0 or rights_filter == right_str:
739        out_str = format_string.format(ie_object, ipc_name, right_str, urefs, nsets, nmsgs, destname_str, destination_str)
740    return out_str
741
742@header("{0: >20s}".format("user bt") )
743def GetPortUserStack(port, task):
744    """ Get UserStack information for the given port & task.
745        params: port - core.value representation of 'ipc_port *' object
746                task - value representing 'task *' object
747        returns: str - string information on port's userstack
748    """
749    out_str = ''
750    pid = port.ip_made_pid;
751    proc_val = GetProcFromTask(task)
752    if port.ip_made_bt:
753        btlib = kmemory.BTLibrary.get_shared()
754        out_str += "\n".join(btlib.get_stack(port.ip_made_bt).symbolicated_frames()) + "\n"
755        if pid != GetProcPID(proc_val):
756            out_str += " ({:<10d})\n".format(pid)
757    return out_str
758
759@lldb_type_summary(['ipc_space *'])
760@header("{0: <20s} {1: <20s} {2: <20s} {3: <8s} {4: <10s} {5: >8s} {6: <8s}".format('ipc_space', 'is_task', 'is_table', 'flags', 'ports', 'low_mod', 'high_mod'))
761def PrintIPCInformation(space, show_entries=False, show_userstack=False, rights_filter=0):
762    """ Provide a summary of the ipc space
763    """
764    out_str = ''
765    format_string = "{0: <#20x} {1: <#20x} {2: <#20x} {3: <8s} {4: <10d} {5: >8d} {6: <8d}"
766    is_tableval, num_entries = GetSpaceTable(space)
767    flags =''
768    if is_tableval:
769        flags += 'A'
770    else:
771        flags += ' '
772    if (space.is_grower) != 0:
773        flags += 'G'
774    print(format_string.format(space, space.is_task, is_tableval if is_tableval else 0, flags,
775            num_entries, space.is_low_mod, space.is_high_mod))
776
777    #should show the each individual entries if asked.
778    if show_entries == True and is_tableval:
779        print("\t" + GetIPCEntrySummary.header)
780
781        entries = (
782            (index, value(iep.AddressOf()))
783            for index, iep
784            in  GetSpaceEntriesWithBits(is_tableval, num_entries, 0x001f0000)
785        )
786
787        for index, entryval in entries:
788            entry_ie_bits = unsigned(entryval.ie_bits)
789            entry_name = "{0: <#20x}".format( (index <<8 | entry_ie_bits >> 24) )
790            entry_str = GetIPCEntrySummary(entryval, entry_name, rights_filter)
791            if not len(entry_str):
792                continue
793
794            print("\t" + entry_str)
795            if show_userstack == True:
796                entryport = Cast(entryval.ie_object, 'ipc_port *')
797                if entryval.ie_object and (int(entry_ie_bits) & 0x00070000) and entryport.ip_callstack[0]:
798                    print(GetPortUserStack.header + GetPortUserStack(entryport, space.is_task))
799
800    #done with showing entries
801    return out_str
802
803# Macro: showrights
804
805@lldb_command('showrights', 'R:')
806def ShowRights(cmd_args=None, cmd_options={}):
807    """  Routine to print rights information for the given IPC space
808         Usage: showrights [-R rights_type] <address of ipc space>
809                -R rights_type  : only display rights matching the string 'rights_type'
810
811                types of rights:
812                    'Dead'  : Dead name
813                    'Set'   : Port set
814                    'S'     : Send right
815                    'R'     : Receive right
816                    'O'     : Send-once right
817                types of notifications:
818                    'd'     : Dead-Name notification requested
819                    's'     : Send-Possible notification armed
820                    'r'     : Send-Possible notification requested
821                    'n'     : No-Senders notification requested
822                    'x'     : Port-destroy notification requested
823    """
824    if not cmd_args:
825        print("No arguments passed")
826        print(ShowRights.__doc__)
827        return False
828    ipc = kern.GetValueFromAddress(cmd_args[0], 'ipc_space *')
829    if not ipc:
830        print("unknown arguments:", str(cmd_args))
831        return False
832    rights_type = 0
833    if "-R" in cmd_options:
834        rights_type = cmd_options["-R"]
835    print(PrintIPCInformation.header)
836    PrintIPCInformation(ipc, True, False, rights_type)
837
838# EndMacro: showrights
839
840@lldb_command('showtaskrights','R:')
841def ShowTaskRights(cmd_args=None, cmd_options={}):
842    """ Routine to ipc rights information for a task
843        Usage: showtaskrights [-R rights_type] <task address>
844               -R rights_type  : only display rights matching the string 'rights_type'
845
846               types of rights:
847                   'Dead'  : Dead name
848                   'Set'   : Port set
849                   'S'     : Send right
850                   'R'     : Receive right
851                   'O'     : Send-once right
852                   'm'     : Immovable send port
853                   'i'     : Immovable receive port
854                   'g'     : No grant port
855                   'f'     : Port with SB filtering on
856               types of notifications:
857                   'd'     : Dead-Name notification requested
858                   's'     : Send-Possible notification armed
859                   'r'     : Send-Possible notification requested
860                   'n'     : No-Senders notification requested
861                   'x'     : Port-destroy notification requested
862    """
863    if cmd_args == None:
864        print("No arguments passed")
865        print(ShowTaskStacksCmdHelper.__doc__)
866        return False
867    tval = kern.GetValueFromAddress(cmd_args[0], 'task *')
868    if not tval:
869        print("unknown arguments:", str(cmd_args))
870        return False
871    rights_type = 0
872    if "-R" in cmd_options:
873        rights_type = cmd_options["-R"]
874    print(GetTaskSummary.header + " " + GetProcSummary.header)
875    pval = GetProcFromTask(tval)
876    print(GetTaskSummary(tval) + " " + GetProcSummary(pval))
877    print(PrintIPCInformation.header)
878    PrintIPCInformation(tval.itk_space, True, False, rights_type)
879
880# Count the vouchers in a given task's ipc space
881@header("{: <20s} {: <6s} {: <20s} {: <8s}".format("task", "pid", "name", "#vouchers"))
882def GetTaskVoucherCount(t):
883    is_tableval, num_entries = GetSpaceTable(t.itk_space)
884    count = 0
885    voucher_kotype = int(GetEnumValue('ipc_kotype_t', 'IKOT_VOUCHER'))
886
887    if is_tableval:
888        ports = GetSpaceObjectsWithBits(is_tableval, num_entries, 0x00070000,
889            gettype('struct ipc_port'))
890
891    for port in ports:
892        io_bits = port.xGetIntegerByPath('.ip_object.io_bits')
893        if io_bits & 0x3ff == voucher_kotype:
894            count += 1
895
896    format_str = "{: <#20x} {: <6d} {: <20s} {: <8d}"
897    pval = GetProcFromTask(t)
898    return format_str.format(t, GetProcPID(pval), GetProcNameForTask(t), count)
899
900# Macro: countallvouchers
901@lldb_command('countallvouchers', fancy=True)
902def CountAllVouchers(cmd_args=None, cmd_options={}, O=None):
903    """ Routine to count the number of vouchers by task. Useful for finding leaks.
904        Usage: countallvouchers
905    """
906
907    with O.table(GetTaskVoucherCount.header):
908        for t in kern.tasks:
909            print(GetTaskVoucherCount(t))
910
911# Macro: showataskrightsbt
912
913@lldb_command('showtaskrightsbt', 'R:')
914def ShowTaskRightsBt(cmd_args=None, cmd_options={}):
915    """ Routine to ipc rights information with userstacks for a task
916        Usage: showtaskrightsbt [-R rights_type] <task address>
917               -R rights_type  : only display rights matching the string 'rights_type'
918
919               types of rights:
920                   'Dead'  : Dead name
921                   'Set'   : Port set
922                   'S'     : Send right
923                   'R'     : Receive right
924                   'O'     : Send-once right
925                   'm'     : Immovable send port
926                   'i'     : Immovable receive port
927                   'g'     : No grant port
928               types of notifications:
929                   'd'     : Dead-Name notification requested
930                   's'     : Send-Possible notification armed
931                   'r'     : Send-Possible notification requested
932                   'n'     : No-Senders notification requested
933                   'x'     : Port-destroy notification requested
934    """
935    if cmd_args == None:
936        print("No arguments passed")
937        print(ShowTaskRightsBt.__doc__)
938        return False
939    tval = kern.GetValueFromAddress(cmd_args[0], 'task *')
940    if not tval:
941        print("unknown arguments:", str(cmd_args))
942        return False
943    rights_type = 0
944    if "-R" in cmd_options:
945        rights_type = cmd_options["-R"]
946    print(GetTaskSummary.header + " " + GetProcSummary.header)
947    pval = GetProcFromTask(tval)
948    print(GetTaskSummary(tval) + " " + GetProcSummary(pval))
949    print(PrintIPCInformation.header)
950    PrintIPCInformation(tval.itk_space, True, True, rights_type)
951
952# EndMacro: showtaskrightsbt
953
954# Macro: showallrights
955
956@lldb_command('showallrights', 'R:')
957def ShowAllRights(cmd_args=None, cmd_options={}):
958    """  Routine to print rights information for IPC space of all tasks
959         Usage: showallrights [-R rights_type]
960                -R rights_type  : only display rights matching the string 'rights_type'
961
962                types of rights:
963                    'Dead'  : Dead name
964                    'Set'   : Port set
965                    'S'     : Send right
966                    'R'     : Receive right
967                    'O'     : Send-once right
968                    'm'     : Immovable send port
969                    'i'     : Immovable receive port
970                    'g'     : No grant port
971                types of notifications:
972                    'd'     : Dead-Name notification requested
973                    's'     : Send-Possible notification armed
974                    'r'     : Send-Possible notification requested
975                    'n'     : No-Senders notification requested
976                    'x'     : Port-destroy notification requested
977    """
978    rights_type = 0
979    if "-R" in cmd_options:
980        rights_type = cmd_options["-R"]
981    for t in kern.tasks:
982        print(GetTaskSummary.header + " " + GetProcSummary.header)
983        pval = GetProcFromTask(t)
984        print(GetTaskSummary(t) + " " + GetProcSummary(pval))
985        try:
986            print(PrintIPCInformation.header)
987            PrintIPCInformation(t.itk_space, True, False, rights_type) + "\n\n"
988        except (KeyboardInterrupt, SystemExit):
989            raise
990        except:
991            print("Failed to get IPC information. Do individual showtaskrights <task> to find the error. \n\n")
992
993# EndMacro: showallrights
994
995
996def GetInTransitPortSummary(port, disp, holding_port, holding_kmsg):
997    """ String-ify the in-transit dispostion of a port.
998    """
999    ## This should match the summary generated by GetIPCEntrySummary
1000    ##              "object"   "name"   "rite"  "urefs" "nsets" "nmsgs" "destname" "destination"
1001    format_str = "\t{: <#20x} {: <12} {: <8s} {: <8d} {: <8d} {: <8d} p:{: <#19x} k:{: <#19x}"
1002    portname = 'intransit'
1003
1004    disp_str = GetPortDispositionString(disp)
1005
1006    out_str = format_str.format(unsigned(port), 'in-transit', disp_str, 0, 0, port.ip_messages.imq_msgcount, unsigned(holding_port), unsigned(holding_kmsg))
1007    return out_str
1008
1009
1010def GetDispositionFromEntryType(entry_bits):
1011    """ Translate an IPC entry type into an in-transit disposition. This allows
1012        the GetInTransitPortSummary function to be re-used to string-ify IPC
1013        entry types.
1014    """
1015    ebits = int(entry_bits)
1016    if (ebits & 0x003f0000) == 0:
1017        return 0
1018
1019    if (ebits & 0x00010000) != 0:
1020        return 17 ## MACH_PORT_RIGHT_SEND
1021    elif (ebits & 0x00020000) != 0:
1022        return 16 ## MACH_PORT_RIGHT_RECEIVE
1023    elif (ebits & 0x00040000) != 0:
1024        return 18 ## MACH_PORT_RIGHT_SEND_ONCE
1025    elif (ebits & 0x00080000) != 0:
1026        return 100 ## MACH_PORT_RIGHT_PORT_SET
1027    elif (ebits & 0x00100000) != 0:
1028        return 101 ## MACH_PORT_RIGHT_DEAD_NAME
1029    elif (ebits & 0x00200000) != 0:
1030        return 102 ## MACH_PORT_RIGHT_LABELH
1031    else:
1032        return 0
1033
1034def GetDispositionFromVoucherPort(th_vport):
1035    """ Translate a thread's voucher port into a 'disposition'
1036    """
1037    if unsigned(th_vport) > 0:
1038        return 103  ## Voucher type
1039    return 0
1040
1041
1042g_kmsg_prog = 0
1043g_progmeter = {
1044    0 : '*',
1045    1 : '-',
1046    2 : '\\',
1047    3 : '|',
1048    4 : '/',
1049    5 : '-',
1050    6 : '\\',
1051    7 : '|',
1052    8 : '/',
1053}
1054
1055def PrintProgressForKmsg():
1056    global g_kmsg_prog
1057    global g_progmeter
1058    sys.stderr.write(" {:<1s}\r".format(g_progmeter[g_kmsg_prog % 9]))
1059    g_kmsg_prog += 1
1060
1061
1062def CollectPortsForAnalysis(port, disposition):
1063    """
1064    """
1065    p = Cast(port, 'struct ipc_port *')
1066    yield (p, disposition)
1067
1068    # no-senders notification port
1069    if unsigned(p.ip_nsrequest) not in (0, 1): # 1 is IP_KOBJECT_NSREQUEST_ARMED
1070        PrintProgressForKmsg()
1071        yield (p.ip_nsrequest, -1)
1072
1073    # port-death notification port
1074    pdrequest = GetPortPDRequest(p)
1075    if pdrequest:
1076        PrintProgressForKmsg()
1077        yield (pdrequest, -2)
1078
1079    ## ports can have many send-possible notifications armed: go through the table!
1080    if unsigned(p.ip_requests) != 0:
1081        table, table_sz = kalloc_array_decode(p.ip_requests, 'struct ipc_port_request')
1082        for i in range(table_sz):
1083            if i == 0:
1084                continue
1085            ipr = table[i]
1086            if unsigned(ipr.ipr_name) in (0, 0xfffffffe):
1087                # 0xfffffffe is a host notify request
1088                continue
1089            ipr_bits = unsigned(ipr.ipr_soright) & 3
1090            ipr_port = kern.GetValueFromAddress(int(ipr.ipr_soright) & ~3, 'struct ipc_port *')
1091            ipr_disp = 0
1092            if ipr_bits & 3: ## send-possible armed and requested
1093                ipr_disp = -5
1094            elif ipr_bits & 2: ## send-possible requested
1095                ipr_disp = -4
1096            elif ipr_bits & 1: ## send-possible armed
1097                ipr_disp = -3
1098            PrintProgressForKmsg()
1099            yield (ipr_port, ipr_disp)
1100    return
1101
1102def CollectKmsgPorts(task, task_port, kmsgp):
1103    """ Look through a message, 'kmsgp' destined for 'task'
1104        (enqueued on task_port). Collect any port descriptors,
1105        remote, local, voucher, or other port references
1106        into a (ipc_port_t, disposition) list.
1107    """
1108    kmsgh = dereference(GetKmsgHeader(kmsgp))
1109
1110    p_list = []
1111
1112    PrintProgressForKmsg()
1113    if kmsgh.msgh_remote_port and unsigned(kmsgh.msgh_remote_port) != unsigned(task_port):
1114        disp = kmsgh.msgh_bits & 0x1f
1115        p_list += list(CollectPortsForAnalysis(kmsgh.msgh_remote_port, disp))
1116
1117    if kmsgh.msgh_local_port and unsigned(kmsgh.msgh_local_port) != unsigned(task_port) \
1118       and unsigned(kmsgh.msgh_local_port) != unsigned(kmsgh.msgh_remote_port):
1119        disp = (kmsgh.msgh_bits & 0x1f00) >> 8
1120        p_list += list(CollectPortsForAnalysis(kmsgh.msgh_local_port, disp))
1121
1122    if kmsgp.ikm_voucher_port:
1123        p_list += list(CollectPortsForAnalysis(kmsgp.ikm_voucher_port, 0))
1124
1125    if kmsgh.msgh_bits & 0x80000000:
1126        ## Complex message - look for descriptors
1127        PrintProgressForKmsg()
1128        (body, dschead, dsc_list) = GetKmsgDescriptors(kmsgp)
1129        for dsc in dsc_list:
1130            PrintProgressForKmsg()
1131            dsc_type = unsigned(dsc.type.type)
1132            if dsc_type == 0 or dsc_type == 2: ## 0 == port, 2 == ool port
1133                if dsc_type == 0:
1134                    ## its a port descriptor
1135                    dsc_disp = dsc.port.disposition
1136                    p_list += list(CollectPortsForAnalysis(dsc.port.name, dsc_disp))
1137                else:
1138                    ## it's an ool_ports descriptor which is an array of ports
1139                    dsc_disp = dsc.ool_ports.disposition
1140                    dispdata = Cast(dsc.ool_ports.address, 'struct ipc_port *')
1141                    for pidx in range(dsc.ool_ports.count):
1142                        PrintProgressForKmsg()
1143                        p_list += list(CollectPortsForAnalysis(dispdata[pidx], dsc_disp))
1144    return p_list
1145
1146def CollectKmsgPortRefs(task, task_port, kmsgp, p_refs):
1147    """ Recursively collect all references to ports inside the kmsg 'kmsgp'
1148        into the set 'p_refs'
1149    """
1150    p_list = CollectKmsgPorts(task, task_port, kmsgp)
1151
1152    ## Iterate over each ports we've collected, to see if they
1153    ## have messages on them, and then recurse!
1154    for p, pdisp in p_list:
1155        ptype = (p.ip_object.io_bits & 0x7fff0000) >> 16
1156        p_refs.add((p, pdisp, ptype))
1157        if ptype != 0: ## don't bother with port sets
1158            continue
1159        ## If the port that's in-transit has messages already enqueued,
1160        ## go through each of those messages and look for more ports!
1161        for p_kmsgp in IterateCircleQueue(p.ip_messages.imq_messages, 'ipc_kmsg', 'ikm_link'):
1162            CollectKmsgPortRefs(task, p, p_kmsgp, p_refs)
1163
1164
1165def FindKmsgPortRefs(instr, task, task_port, kmsgp, qport):
1166    """ Look through a message, 'kmsgp' destined for 'task'. If we find
1167        any port descriptors, remote, local, voucher, or other port that
1168        matches 'qport', return a short description
1169        which should match the format of GetIPCEntrySummary.
1170    """
1171
1172    out_str = instr
1173    p_list = CollectKmsgPorts(task, task_port, kmsgp)
1174
1175    ## Run through all ports we've collected looking for 'qport'
1176    for p, pdisp in p_list:
1177        PrintProgressForKmsg()
1178        if unsigned(p) == unsigned(qport):
1179            ## the port we're looking for was found in this message!
1180            if len(out_str) > 0:
1181                out_str += '\n'
1182            out_str += GetInTransitPortSummary(p, pdisp, task_port, kmsgp)
1183
1184        ptype = (p.ip_object.io_bits & 0x7fff0000) >> 16
1185        if ptype != 0: ## don't bother with port sets
1186            continue
1187
1188        ## If the port that's in-transit has messages already enqueued,
1189        ## go through each of those messages and look for more ports!
1190        for p_kmsgp in IterateCircleQueue(p.ip_messages.imq_messages, 'ipc_kmsg', 'ikm_link'):
1191            out_str = FindKmsgPortRefs(out_str, task, p, p_kmsgp, qport)
1192
1193    return out_str
1194
1195
1196port_iteration_do_print_taskname = False
1197registeredport_idx = -10
1198excports_idx = -20
1199intransit_idx = -1000
1200taskports_idx = -2000
1201thports_idx = -3000
1202
1203def IterateAllPorts(tasklist, func, ctx, include_psets, follow_busyports, should_log):
1204    """ Iterate over all ports in the system, calling 'func'
1205        for each entry in
1206    """
1207    global port_iteration_do_print_taskname
1208    global intransit_idx, taskports_idx, thports_idx, registeredport_idx, excports_idx
1209
1210    ## XXX: also host special ports
1211
1212    entry_port_type_mask = 0x00070000
1213    if include_psets:
1214        entry_port_type_mask = 0x000f0000
1215
1216    if tasklist is None:
1217        tasklist = list(kern.tasks)
1218        tasklist += list(kern.terminated_tasks)
1219
1220    tidx = 1
1221
1222    for t in tasklist:
1223        # Write a progress line.  Using stderr avoids automatic newline when
1224        # writing to stdout from lldb.  Blank spaces at the end clear out long
1225        # lines.
1226        if should_log:
1227            procname = ""
1228            if not t.active:
1229                procname = 'terminated: '
1230            if t.halting:
1231                procname += 'halting: '
1232            procname += GetProcNameForTask(t)
1233            sys.stderr.write("  checking {:s} ({}/{})...{:50s}\r".format(procname, tidx, len(tasklist), ''))
1234        tidx += 1
1235
1236        port_iteration_do_print_taskname = True
1237        space = t.itk_space
1238        is_tableval, num_entries = GetSpaceTable(space)
1239
1240        if is_tableval:
1241            base  = is_tableval.GetSBValue().Dereference()
1242            entries = (
1243                value(iep.AddressOf())
1244                for iep in base.xIterSiblings(1, num_entries)
1245            )
1246
1247        for idx, entry_val in enumerate(entries, 1):
1248            entry_bits= unsigned(entry_val.ie_bits)
1249            entry_obj = 0
1250            entry_str = ''
1251            entry_name = "{:x}".format( (idx << 8 | entry_bits >> 24) )
1252
1253            entry_disp = GetDispositionFromEntryType(entry_bits)
1254
1255            ## If the entry in the table represents a port of some sort,
1256            ## then make the callback provided
1257            if int(entry_bits) & entry_port_type_mask:
1258                eport = kern.CreateTypedPointerFromAddress(unsigned(entry_val.ie_object), 'struct ipc_port')
1259                ## Make the callback
1260                func(t, space, ctx, idx, entry_val, eport, entry_disp)
1261
1262                ## if the port has pending messages, look through
1263                ## each message for ports (and recurse)
1264                if follow_busyports and unsigned(eport) > 0 and eport.ip_messages.imq_msgcount > 0:
1265                    ## collect all port references from all messages
1266                    for kmsgp in IterateCircleQueue(eport.ip_messages.imq_messages, 'ipc_kmsg', 'ikm_link'):
1267                        p_refs = set()
1268                        CollectKmsgPortRefs(t, eport, kmsgp, p_refs)
1269                        for (port, pdisp, ptype) in p_refs:
1270                            func(t, space, ctx, intransit_idx, None, port, pdisp)
1271        ## for idx in xrange(1, num_entries)
1272
1273        ## Task ports (send rights)
1274        if unsigned(t.itk_settable_self) > 0:
1275            func(t, space, ctx, taskports_idx, 0, t.itk_settable_self, 17)
1276        if unsigned(t.itk_host) > 0:
1277            func(t, space, ctx, taskports_idx, 0, t.itk_host, 17)
1278        if unsigned(t.itk_bootstrap) > 0:
1279            func(t, space, ctx, taskports_idx, 0, t.itk_bootstrap, 17)
1280        if unsigned(t.itk_debug_control) > 0:
1281            func(t, space, ctx, taskports_idx, 0, t.itk_debug_control, 17)
1282        if unsigned(t.itk_task_access) > 0:
1283            func(t, space, ctx, taskports_idx, 0, t.itk_task_access, 17)
1284        if unsigned(t.itk_task_ports[1]) > 0: ## task read port
1285            func(t, space, ctx, taskports_idx, 0, t.itk_task_ports[1], 17)
1286        if unsigned(t.itk_task_ports[2]) > 0: ## task inspect port
1287            func(t, space, ctx, taskports_idx, 0, t.itk_task_ports[2], 17)
1288
1289        ## Task name port (not a send right, just a naked ref); TASK_FLAVOR_NAME = 3
1290        if unsigned(t.itk_task_ports[3]) > 0:
1291            func(t, space, ctx, taskports_idx, 0, t.itk_task_ports[3], 0)
1292
1293        ## task resume port is a receive right to resume the task
1294        if unsigned(t.itk_resume) > 0:
1295            func(t, space, ctx, taskports_idx, 0, t.itk_resume, 16)
1296
1297        ## registered task ports (all send rights)
1298        tr_idx = 0
1299        tr_max = sizeof(t.itk_registered) // sizeof(t.itk_registered[0])
1300        while tr_idx < tr_max:
1301            tport = t.itk_registered[tr_idx]
1302            if unsigned(tport) > 0:
1303                try:
1304                    func(t, space, ctx, registeredport_idx, 0, tport, 17)
1305                except Exception as e:
1306                    print("\texception looking through registered port {:d}/{:d} in {:s}".format(tr_idx,tr_max,t))
1307                    pass
1308            tr_idx += 1
1309
1310        ## Task exception ports
1311        exidx = 0
1312        exmax = sizeof(t.exc_actions) // sizeof(t.exc_actions[0])
1313        while exidx < exmax: ## see: osfmk/mach/[arm|i386]/exception.h
1314            export = t.exc_actions[exidx].port ## send right
1315            if unsigned(export) > 0:
1316                try:
1317                    func(t, space, ctx, excports_idx, 0, export, 17)
1318                except Exception as e:
1319                    print("\texception looking through exception port {:d}/{:d} in {:s}".format(exidx,exmax,t))
1320                    pass
1321            exidx += 1
1322
1323        ## XXX: any  ports still valid after clearing IPC space?!
1324
1325        for thval in IterateQueue(t.threads, 'thread *', 'task_threads'):
1326            ## XXX: look at block reason to see if it's in mach_msg_receive - then look at saved state / message
1327
1328            ## Thread port (send right)
1329            if unsigned(thval.t_tro.tro_settable_self_port) > 0:
1330                thport = thval.t_tro.tro_settable_self_port
1331                func(t, space, ctx, thports_idx, 0, thport, 17) ## see: osfmk/mach/message.h
1332            ## Thread special reply port (send-once right)
1333            if unsigned(thval.ith_special_reply_port) > 0:
1334                thport = thval.ith_special_reply_port
1335                func(t, space, ctx, thports_idx, 0, thport, 18) ## see: osfmk/mach/message.h
1336            ## Thread voucher port
1337            if unsigned(thval.ith_voucher) > 0:
1338                vport = thval.ith_voucher.iv_port
1339                if unsigned(vport) > 0:
1340                    vdisp = GetDispositionFromVoucherPort(vport)
1341                    func(t, space, ctx, thports_idx, 0, vport, vdisp)
1342            ## Thread exception ports
1343            if unsigned(thval.t_tro.tro_exc_actions) > 0:
1344                exidx = 0
1345                while exidx < exmax: ## see: osfmk/mach/[arm|i386]/exception.h
1346                    export = thval.t_tro.tro_exc_actions[exidx].port ## send right
1347                    if unsigned(export) > 0:
1348                        try:
1349                            func(t, space, ctx, excports_idx, 0, export, 17)
1350                        except Exception as e:
1351                            print("\texception looking through exception port {:d}/{:d} in {:s}".format(exidx,exmax,t))
1352                            pass
1353                    exidx += 1
1354            ## XXX: the message on a thread (that's currently being received)
1355        ## for (thval in t.threads)
1356    ## for (t in tasklist)
1357
1358
1359# Macro: findportrights
1360def FindPortRightsCallback(task, space, ctx, entry_idx, ipc_entry, ipc_port, port_disp):
1361    """ Callback which uses 'ctx' as the (port,rights_types) tuple for which
1362        a caller is seeking references. This should *not* be used from a
1363        recursive call to IterateAllPorts.
1364    """
1365    global port_iteration_do_print_taskname
1366
1367    (qport, rights_type) = ctx
1368    entry_name = ''
1369    entry_str = ''
1370    if unsigned(ipc_entry) != 0:
1371        entry_bits = unsigned(ipc_entry.ie_bits)
1372        entry_name = "{:x}".format( (entry_idx << 8 | entry_bits >> 24) )
1373        if (int(entry_bits) & 0x001f0000) != 0 and unsigned(ipc_entry.ie_object) == unsigned(qport):
1374            ## it's a valid entry, and it points to the port
1375            entry_str = '\t' + GetIPCEntrySummary(ipc_entry, entry_name, rights_type)
1376
1377    procname = GetProcNameForTask(task)
1378    if unsigned(ipc_port) != 0 and ipc_port.ip_messages.imq_msgcount > 0:
1379        sys.stderr.write("  checking {:s} busy-port {}:{:#x}...{:30s}\r".format(procname, entry_name, unsigned(ipc_port), ''))
1380        ## Search through busy ports to find descriptors which could
1381        ## contain the only reference to this port!
1382        for kmsgp in IterateCircleQueue(ipc_port.ip_messages.imq_messages, 'ipc_kmsg', 'ikm_link'):
1383            entry_str = FindKmsgPortRefs(entry_str, task, ipc_port, kmsgp, qport)
1384
1385    if len(entry_str) > 0:
1386        sys.stderr.write("{:80s}\r".format(''))
1387        if port_iteration_do_print_taskname:
1388            print("Task: {0: <#x} {1: <s}".format(task, procname))
1389            print('\t' + GetIPCEntrySummary.header)
1390            port_iteration_do_print_taskname = False
1391        print(entry_str)
1392
1393@lldb_command('findportrights', 'R:S:')
1394def FindPortRights(cmd_args=None, cmd_options={}):
1395    """  Routine to locate and print all extant rights to a given port
1396         Usage: findportrights [-R rights_type] [-S <ipc_space_t>] <ipc_port_t>
1397                -S ipc_space    : only search the specified ipc space
1398                -R rights_type  : only display rights matching the string 'rights_type'
1399
1400                types of rights:
1401                    'Dead'  : Dead name
1402                    'Set'   : Port set
1403                    'S'     : Send right
1404                    'R'     : Receive right
1405                    'O'     : Send-once right
1406                types of notifications:
1407                    'd'     : Dead-Name notification requested
1408                    's'     : Send-Possible notification armed
1409                    'r'     : Send-Possible notification requested
1410                    'n'     : No-Senders notification requested
1411                    'x'     : Port-destroy notification requested
1412    """
1413    if not cmd_args:
1414        raise ArgumentError("no port address provided")
1415    port = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_port *')
1416
1417    rights_type = 0
1418    if "-R" in cmd_options:
1419        rights_type = cmd_options["-R"]
1420
1421    tasklist = None
1422    if "-S" in cmd_options:
1423        space = kern.GetValueFromAddress(cmd_options["-S"], 'struct ipc_space *')
1424        tasklist = [ space.is_task ]
1425
1426    ## Don't include port sets
1427    ## Don't recurse on busy ports (we do that manually)
1428    ## DO log progress
1429    IterateAllPorts(tasklist, FindPortRightsCallback, (port, rights_type), False, False, True)
1430    sys.stderr.write("{:120s}\r".format(' '))
1431
1432    print("Done.")
1433    return
1434# EndMacro: findportrights
1435
1436# Macro: countallports
1437
1438def CountPortsCallback(task, space, ctx, entry_idx, ipc_entry, ipc_port, port_disp):
1439    """ Callback which uses 'ctx' as the set of all ports found in the
1440        iteration. This should *not* be used from a recursive
1441        call to IterateAllPorts.
1442    """
1443    global intransit_idx
1444
1445    (p_set, p_intransit, p_bytask) = ctx
1446
1447    ## Add the port address to the set of all port addresses
1448    p_set.add(unsigned(ipc_port))
1449
1450    if entry_idx == intransit_idx:
1451        p_intransit.add(unsigned(ipc_port))
1452
1453    if task.active or (task.halting and not task.active):
1454        pname = GetProcNameForTask(task)
1455        if not pname in p_bytask:
1456            p_bytask[pname] = { 'transit':0, 'table':0, 'other':0 }
1457        if entry_idx == intransit_idx:
1458            p_bytask[pname]['transit'] += 1
1459        elif entry_idx >= 0:
1460            p_bytask[pname]['table'] += 1
1461        else:
1462            p_bytask[pname]['other'] += 1
1463
1464
1465@lldb_command('countallports', 'P')
1466def CountAllPorts(cmd_args=None, cmd_options={}):
1467    """ Routine to search for all as many references to ipc_port structures in the kernel
1468        that we can find.
1469        Usage: countallports [-P]
1470                -P : include port sets in the count (default: NO)
1471    """
1472    p_set = set()
1473    p_intransit = set()
1474    p_bytask = {}
1475
1476    find_psets = False
1477    if "-P" in cmd_options:
1478        find_psets = True
1479
1480    ## optionally include port sets
1481    ## DO recurse on busy ports
1482    ## DO log progress
1483    IterateAllPorts(None, CountPortsCallback, (p_set, p_intransit, p_bytask), find_psets, True, True)
1484    sys.stderr.write("{:120s}\r".format(' '))
1485
1486    print("Total ports found: {:d}".format(len(p_set)))
1487    print("In Transit: {:d}".format(len(p_intransit)))
1488    print("By Task:")
1489    for pname in sorted(p_bytask.keys()):
1490        count = p_bytask[pname]
1491        print("\t{: <20s}: table={: <5d}, transit={: <5d}, other={: <5d}".format(pname, count['table'], count['transit'], count['other']))
1492    return
1493# EndMacro: countallports
1494# Macro: showpipestats
1495
1496@lldb_command('showpipestats')
1497def ShowPipeStats(cmd_args=None):
1498    """ Display pipes usage information in the kernel
1499    """
1500    print("Number of pipes: {: d}".format(kern.globals.amountpipes))
1501    print("Memory used by pipes: {:s}".format(sizeof_fmt(int(kern.globals.amountpipekva))))
1502    print("Max memory allowed for pipes: {:s}".format(sizeof_fmt(int(kern.globals.maxpipekva))))
1503
1504# EndMacro: showpipestats
1505# Macro: showtaskbusyports
1506
1507@lldb_command('showtaskbusyports', fancy=True)
1508def ShowTaskBusyPorts(cmd_args=None, cmd_options={}, O=None):
1509    """ Routine to print information about receive rights belonging to this task that
1510        have enqueued messages. This is often a sign of a blocked or hung process
1511        Usage: showtaskbusyports <task address>
1512    """
1513    if not cmd_args:
1514        print("No arguments passed. Please pass in the address of a task")
1515        print(ShowTaskBusyPorts.__doc__)
1516        return
1517    task = kern.GetValueFromAddress(cmd_args[0], 'task_t')
1518    is_tableval, num_entries = GetSpaceTable(task.itk_space)
1519
1520    if is_tableval:
1521        ports = GetSpaceObjectsWithBits(is_tableval, num_entries, 0x00020000,
1522            gettype('struct ipc_port'))
1523
1524    with O.table(PrintPortSummary.header):
1525        for port in ports:
1526            if port.xGetIntegerByPath('.ip_messages.imq_msgcount'):
1527                PrintPortSummary(value(port.AddressOf()), O=O)
1528
1529# EndMacro: showtaskbusyports
1530# Macro: showallbusyports
1531
1532@lldb_command('showallbusyports', fancy=True)
1533def ShowAllBusyPorts(cmd_args=None, cmd_options={}, O=None):
1534    """ Routine to print information about all receive rights on the system that
1535        have enqueued messages.
1536    """
1537    with O.table(PrintPortSummary.header):
1538        port_ty = gettype("struct ipc_port")
1539        for port in kmemory.Zone("ipc ports").iter_allocated(port_ty):
1540            if port.xGetIntegerByPath('.ip_messages.imq_msgcount') > 0:
1541                PrintPortSummary(value(port.AddressOf()), O=O)
1542
1543# EndMacro: showallbusyports
1544# Macro: showallports
1545
1546@lldb_command('showallports', fancy=True)
1547def ShowAllPorts(cmd_args=None, cmd_options={}, O=None):
1548    """ Routine to print information about all allocated ports in the system
1549
1550        usage: showallports
1551    """
1552    with O.table(PrintPortSummary.header):
1553        port_ty = gettype("struct ipc_port")
1554        for port in kmemory.Zone("ipc ports").iter_allocated(port_ty):
1555            PrintPortSummary(value(port.AddressOf()), show_kmsg_summary=False, O=O)
1556
1557# EndMacro: showallports
1558# Macro: findkobjectport
1559
1560@lldb_command('findkobjectport', fancy=True)
1561def FindKobjectPort(cmd_args=None, cmd_options={}, O=None):
1562    """ Locate all ports pointing to a given kobject
1563
1564        usage: findkobjectport <kobject-addr>
1565    """
1566
1567    kobj_addr = unsigned(kern.GetValueFromAddress(cmd_args[0]))
1568    kmem = kmemory.KMem.get_shared()
1569    port_ty = gettype("struct ipc_port")
1570
1571    with O.table(PrintPortSummary.header):
1572        for port in kmemory.Zone("ipc ports").iter_allocated(port_ty):
1573            if port.xGetIntegerByPath('.ip_object.io_bits') & 0x3ff == 0:
1574                continue
1575
1576            ip_kobject = kmem.make_address(port.xGetScalarByName('ip_kobject'))
1577            if ip_kobject == kobj_addr:
1578                PrintPortSummary(value(port.AddressOf()), show_kmsg_summary=False, O=O)
1579
1580# EndMacro: findkobjectport
1581# Macro: showtaskbusypsets
1582
1583@lldb_command('showtaskbusypsets', fancy=True)
1584def ShowTaskBusyPortSets(cmd_args=None, cmd_options={}, O=None):
1585    """ Routine to print information about port sets belonging to this task that
1586        have enqueued messages. This is often a sign of a blocked or hung process
1587        Usage: showtaskbusypsets <task address>
1588    """
1589    if not cmd_args:
1590        print("No arguments passed. Please pass in the address of a task")
1591        print(ShowTaskBusyPortsSets.__doc__)
1592        return
1593    task = kern.GetValueFromAddress(cmd_args[0], 'task_t')
1594    is_tableval, num_entries = GetSpaceTable(task.itk_space)
1595
1596    if is_tableval:
1597        psets = GetSpaceObjectsWithBits(is_tableval, num_entries, 0x00080000,
1598            gettype('struct ipc_pset'))
1599
1600    with O.table(PrintPortSetSummary.header):
1601        for pset in (value(v.AddressOf()) for v in psets):
1602            for wq in Waitq(addressof(pset.ips_wqset)).iterateMembers():
1603                if wq.asPort().ip_messages.imq_msgcount > 0:
1604                    PrintPortSetSummary(pset, space=task.itk_space, O=O)
1605
1606# EndMacro: showtaskbusyports
1607# Macro: showallbusypsets
1608
1609@lldb_command('showallbusypsets', fancy=True)
1610def ShowAllBusyPortSets(cmd_args=None, cmd_options={}, O=None):
1611    """ Routine to print information about all port sets on the system that
1612        have enqueued messages.
1613    """
1614    with O.table(PrintPortSetSummary.header):
1615        pset_ty = gettype("struct ipc_pset")
1616        for pset in kmemory.Zone("ipc port sets").iter_allocated(pset_ty):
1617            pset = value(pset.AddressOf())
1618            for wq in Waitq(addressof(pset.ips_wqset)).iterateMembers():
1619                port = wq.asPort()
1620                if port.ip_messages.imq_msgcount > 0:
1621                    PrintPortSetSummary(pset, space=port.ip_receiver, O=O)
1622
1623# EndMacro: showallbusyports
1624# Macro: showallpsets
1625
1626@lldb_command('showallpsets', fancy=True)
1627def ShowAllPortSets(cmd_args=None, cmd_options={}, O=None):
1628    """ Routine to print information about all allocated psets in the system
1629
1630        usage: showallpsets
1631    """
1632    with O.table(PrintPortSetSummary.header):
1633        pset_ty = gettype("struct ipc_pset")
1634        for pset in kmemory.Zone("ipc port sets").iter_allocated(pset_ty):
1635            PrintPortSetSummary(value(pset.AddressOf()), O=O)
1636
1637# EndMacro: showallports
1638# Macro: showbusyportsummary
1639
1640@lldb_command('showbusyportsummary')
1641def ShowBusyPortSummary(cmd_args=None):
1642    """ Routine to print a summary of information about all receive rights
1643        on the system that have enqueued messages.
1644    """
1645    task_queue_head = kern.globals.tasks
1646
1647    ipc_table_size = 0
1648    ipc_busy_ports = 0
1649    ipc_msgs = 0
1650
1651    print(GetTaskBusyIPCSummary.header)
1652    for tsk in kern.tasks:
1653        (summary, table_size, nbusy, nmsgs) = GetTaskBusyIPCSummary(tsk)
1654        ipc_table_size += table_size
1655        ipc_busy_ports += nbusy
1656        ipc_msgs += nmsgs
1657        print(summary)
1658    for t in kern.terminated_tasks:
1659        (summary, table_size, nbusy, nmsgs) = GetTaskBusyIPCSummary(tsk)
1660        ipc_table_size += table_size
1661        ipc_busy_ports += nbusy
1662        ipc_msgs += nmsgs
1663        print(summary)
1664    print("Total Table Size: {:d}, Busy Ports: {:d}, Messages in-flight: {:d}".format(ipc_table_size, ipc_busy_ports, ipc_msgs))
1665    return
1666
1667# EndMacro: showbusyportsummary
1668# Macro: showport / showpset
1669
1670def ShowPortOrPset(obj, space=0, O=None):
1671    """ Routine that lists details about a given IPC port or pset
1672        Syntax: (lldb) showport 0xaddr
1673    """
1674    otype = (obj.io_bits & 0x7fff0000) >> 16
1675    if otype == 0: # IOT_PORT
1676        with O.table(PrintPortSummary.header):
1677            PrintPortSummary(cast(obj, 'ipc_port_t'), show_sets=True, O=O)
1678    elif otype == 1: # IOT_PSET
1679        with O.table(PrintPortSetSummary.header):
1680            PrintPortSetSummary(cast(obj, 'ipc_pset_t'), space, O=O)
1681
1682@lldb_command('showport', 'K', fancy=True)
1683def ShowPort(cmd_args=None, cmd_options={}, O=None):
1684    """ Routine that lists details about a given IPC port
1685
1686        usage: showport <address>
1687    """
1688    # -K is default and kept for backward compat, it used to mean "show kmsg queue"
1689    if not cmd_args:
1690        return O.error("Missing port argument")
1691    obj = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_object *')
1692    ShowPortOrPset(obj, O=O)
1693
1694@lldb_command('showpset', "S:", fancy=True)
1695def ShowPSet(cmd_args=None, cmd_options={}, O=None):
1696    """ Routine that prints details for a given ipc_pset *
1697
1698        usage: showpset [-S <space>] <address>
1699    """
1700    if not cmd_args:
1701        return O.error("Missing port argument")
1702    space = 0
1703    if "-S" in cmd_options:
1704        space = kern.GetValueFromAddress(cmd_options["-S"], 'struct ipc_space *')
1705    obj = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_object *')
1706    ShowPortOrPset(obj, space=space, O=O)
1707
1708# EndMacro: showport / showpset
1709# Macro: showkmsg:
1710
1711@lldb_command('showkmsg')
1712def ShowKMSG(cmd_args=[]):
1713    """ Show detail information about a <ipc_kmsg_t> structure
1714        Usage: (lldb) showkmsg <ipc_kmsg_t>
1715    """
1716    if not cmd_args:
1717        raise ArgumentError('Invalid arguments')
1718    kmsg = kern.GetValueFromAddress(cmd_args[0], 'ipc_kmsg_t')
1719    print(GetKMsgSummary.header)
1720    print(GetKMsgSummary(kmsg))
1721
1722# EndMacro: showkmsg
1723# IPC importance inheritance related macros.
1724
1725@lldb_command('showalliits')
1726def ShowAllIITs(cmd_args=[], cmd_options={}):
1727    """ Development only macro. Show list of all iits allocated in the system. """
1728    try:
1729        iit_queue = kern.globals.global_iit_alloc_queue
1730    except ValueError:
1731        print("This debug macro is only available in development or debug kernels")
1732        return
1733
1734    print(GetIPCImportantTaskSummary.header)
1735    for iit in IterateQueue(iit_queue, 'struct ipc_importance_task *', 'iit_allocation'):
1736        print(GetIPCImportantTaskSummary(iit))
1737    return
1738
1739@header("{: <18s} {: <3s} {: <18s} {: <32s} {: <18s} {: <8s}".format("ipc_imp_inherit", "don", "to_task", "proc_name", "from_elem", "depth"))
1740@lldb_type_summary(['ipc_importance_inherit *', 'ipc_importance_inherit_t'])
1741def GetIPCImportanceInheritSummary(iii):
1742    """ describes iii object of type ipc_importance_inherit_t * """
1743    out_str = ""
1744    fmt = "{o: <#18x} {don: <3s} {o.iii_to_task.iit_task: <#18x} {task_name: <20s} {o.iii_from_elem: <#18x} {o.iii_depth: <#8x}"
1745    donating_str = ""
1746    if unsigned(iii.iii_donating):
1747        donating_str = "DON"
1748    taskname = GetProcNameForTask(iii.iii_to_task.iit_task)
1749    if hasattr(iii.iii_to_task, 'iit_bsd_pid'):
1750        taskname =  "({:d}) {:s}".format(iii.iii_to_task.iit_bsd_pid, iii.iii_to_task.iit_procname)
1751    out_str += fmt.format(o=iii, task_name = taskname, don=donating_str)
1752    return out_str
1753
1754@static_var('recursion_count', 0)
1755@header("{: <18s} {: <4s} {: <8s} {: <8s} {: <18s} {: <18s}".format("iie", "type", "refs", "made", "#kmsgs", "#inherits"))
1756@lldb_type_summary(['ipc_importance_elem *'])
1757def GetIPCImportanceElemSummary(iie):
1758    """ describes an ipc_importance_elem * object """
1759
1760    if GetIPCImportanceElemSummary.recursion_count > 500:
1761        GetIPCImportanceElemSummary.recursion_count = 0
1762        return "Recursion of 500 reached"
1763
1764    out_str = ''
1765    fmt = "{: <#18x} {: <4s} {: <8d} {: <8d} {: <#18x} {: <#18x}"
1766    if unsigned(iie.iie_bits) & 0x80000000:
1767        type_str = "INH"
1768        inherit_count = 0
1769    else:
1770        type_str = 'TASK'
1771        iit = Cast(iie, 'struct ipc_importance_task *')
1772        inherit_count = sum(1 for i in IterateQueue(iit.iit_inherits, 'struct ipc_importance_inherit *',  'iii_inheritance'))
1773
1774    refs = unsigned(iie.iie_bits) & 0x7fffffff
1775    made_refs = unsigned(iie.iie_made)
1776    kmsg_count = sum(1 for i in IterateQueue(iie.iie_kmsgs, 'struct ipc_kmsg *',  'ikm_inheritance'))
1777    out_str += fmt.format(iie, type_str, refs, made_refs, kmsg_count, inherit_count)
1778    if config['verbosity'] > vHUMAN:
1779        if kmsg_count > 0:
1780            out_str += "\n\t"+ GetKMsgSummary.header
1781            for k in IterateQueue(iie.iie_kmsgs, 'struct ipc_kmsg *',  'ikm_inheritance'):
1782                out_str += "\t" + "{: <#18x}".format(GetKmsgHeader(k).msgh_remote_port) + '   ' + GetKMsgSummary(k, "\t").lstrip()
1783            out_str += "\n"
1784        if inherit_count > 0:
1785            out_str += "\n\t" + GetIPCImportanceInheritSummary.header + "\n"
1786            for i in IterateQueue(iit.iit_inherits, 'struct ipc_importance_inherit *',  'iii_inheritance'):
1787                out_str += "\t" + GetIPCImportanceInheritSummary(i) + "\n"
1788            out_str += "\n"
1789        if type_str == "INH":
1790            iii = Cast(iie, 'struct ipc_importance_inherit *')
1791            out_str += "Inherit from: " + GetIPCImportanceElemSummary(iii.iii_from_elem)
1792
1793    return out_str
1794
1795@header("{: <18s} {: <18s} {: <32}".format("iit", "task", "name"))
1796@lldb_type_summary(['ipc_importance_task *'])
1797def GetIPCImportantTaskSummary(iit):
1798    """ iit is a ipc_importance_task value object.
1799    """
1800    fmt = "{: <#18x} {: <#18x} {: <32}"
1801    out_str=''
1802    pname = GetProcNameForTask(iit.iit_task)
1803    if hasattr(iit, 'iit_bsd_pid'):
1804        pname = "({:d}) {:s}".format(iit.iit_bsd_pid, iit.iit_procname)
1805    out_str += fmt.format(iit, iit.iit_task, pname)
1806    return out_str
1807
1808@lldb_command('showallimportancetasks')
1809def ShowIPCImportanceTasks(cmd_args=[], cmd_options={}):
1810    """ display a list of all tasks with ipc importance information.
1811        Usage: (lldb) showallimportancetasks
1812        Tip: add "-v" to see detailed information on each kmsg or inherit elems
1813    """
1814    print(' ' + GetIPCImportantTaskSummary.header + ' ' + GetIPCImportanceElemSummary.header)
1815    for t in kern.tasks:
1816        s = ""
1817        if unsigned(t.task_imp_base):
1818            s += ' ' + GetIPCImportantTaskSummary(t.task_imp_base)
1819            s += ' ' + GetIPCImportanceElemSummary(addressof(t.task_imp_base.iit_elem))
1820            print(s)
1821
1822@lldb_command('showipcimportance', '')
1823def ShowIPCImportance(cmd_args=[], cmd_options={}):
1824    """ Describe an importance from <ipc_importance_elem_t> argument.
1825        Usage: (lldb) showimportance <ipc_importance_elem_t>
1826    """
1827    if not cmd_args:
1828        raise ArgumentError("Please provide valid argument")
1829
1830    elem = kern.GetValueFromAddress(cmd_args[0], 'ipc_importance_elem_t')
1831    print(GetIPCImportanceElemSummary.header)
1832    print(GetIPCImportanceElemSummary(elem))
1833
1834@header("{: <18s} {: <10s} {: <18s} {: <8s} {: <5s} {: <5s} {: <5s}".format("ivac", "refs", "tbl", "tblsize", "index", "Grow", "freelist"))
1835@lldb_type_summary(['ipc_voucher_attr_control *', 'ipc_voucher_attr_control_t'])
1836def GetIPCVoucherAttrControlSummary(ivac):
1837    """ describes a voucher attribute control settings """
1838    out_str = ""
1839    fmt = "{c: <#18x} {c.ivac_refs.ref_count: <10d} {c.ivac_table: <#18x} {c.ivac_table_size: <8d} {c.ivac_key_index: <5d} {growing: <5s} {c.ivac_freelist: <5d}"
1840    growing_str = ""
1841
1842    if unsigned(ivac) == 0:
1843        return "{: <#18x}".format(ivac)
1844
1845    if unsigned(ivac.ivac_is_growing):
1846        growing_str = "Y"
1847    out_str += fmt.format(c=ivac, growing = growing_str)
1848    return out_str
1849
1850@lldb_command('showivac','')
1851def ShowIPCVoucherAttributeControl(cmd_args=[], cmd_options={}):
1852    """ Show summary of voucher attribute contols.
1853        Usage: (lldb) showivac <ipc_voucher_attr_control_t>
1854    """
1855    if not cmd_args:
1856        raise ArgumentError("Please provide correct arguments.")
1857    ivac = kern.GetValueFromAddress(cmd_args[0], 'ipc_voucher_attr_control_t')
1858    print(GetIPCVoucherAttrControlSummary.header)
1859    print(GetIPCVoucherAttrControlSummary(ivac))
1860    if config['verbosity'] > vHUMAN:
1861        cur_entry_index = 0
1862        last_entry_index = unsigned(ivac.ivac_table_size)
1863        print("index " + GetIPCVoucherAttributeEntrySummary.header)
1864        while cur_entry_index < last_entry_index:
1865            print("{: <5d} ".format(cur_entry_index) + GetIPCVoucherAttributeEntrySummary(addressof(ivac.ivac_table[cur_entry_index])))
1866            cur_entry_index += 1
1867
1868
1869
1870
1871@header("{: <18s} {: <30s} {: <30s} {: <30s} {: <30s}".format("ivam", "get_value_fn", "extract_fn", "release_value_fn", "command_fn"))
1872@lldb_type_summary(['ipc_voucher_attr_manager *', 'ipc_voucher_attr_manager_t'])
1873def GetIPCVoucherAttrManagerSummary(ivam):
1874    """ describes a voucher attribute manager settings """
1875    out_str = ""
1876    fmt = "{: <#18x} {: <30s} {: <30s} {: <30s} {: <30s}"
1877
1878    if unsigned(ivam) == 0 :
1879        return "{: <#18x}".format(ivam)
1880
1881    get_value_fn = kern.Symbolicate(unsigned(ivam.ivam_get_value))
1882    extract_fn = kern.Symbolicate(unsigned(ivam.ivam_extract_content))
1883    release_value_fn = kern.Symbolicate(unsigned(ivam.ivam_release_value))
1884    command_fn = kern.Symbolicate(unsigned(ivam.ivam_command))
1885    out_str += fmt.format(ivam, get_value_fn, extract_fn, release_value_fn, command_fn)
1886    return out_str
1887
1888
1889
1890@header("{: <18s} {: <10s} {:s} {:s}".format("ivgte", "key", GetIPCVoucherAttrControlSummary.header.strip(), GetIPCVoucherAttrManagerSummary.header.strip()))
1891@lldb_type_summary(['ipc_voucher_global_table_element *', 'ipc_voucher_global_table_element_t'])
1892def GetIPCVoucherGlobalTableElementSummary(ivgte):
1893    """ describes a ipc_voucher_global_table_element object """
1894    out_str = ""
1895    fmt = "{g: <#18x} {g.ivgte_key: <10d} {ctrl_s:s} {mgr_s:s}"
1896    out_str += fmt.format(g=ivgte, ctrl_s=GetIPCVoucherAttrControlSummary(ivgte.ivgte_control), mgr_s=GetIPCVoucherAttrManagerSummary(ivgte.ivgte_manager))
1897    return out_str
1898
1899@lldb_command('showglobalvouchertable', '')
1900def ShowGlobalVoucherTable(cmd_args=[], cmd_options={}):
1901    """ show detailed information of all voucher attribute managers registered with vouchers system
1902        Usage: (lldb) showglobalvouchertable
1903    """
1904    entry_size = sizeof(kern.globals.iv_global_table[0])
1905    elems = sizeof(kern.globals.iv_global_table) // entry_size
1906    print(GetIPCVoucherGlobalTableElementSummary.header)
1907    for i in range(elems):
1908        elt = addressof(kern.globals.iv_global_table[i])
1909        print(GetIPCVoucherGlobalTableElementSummary(elt))
1910
1911# Type summaries for Bag of Bits.
1912
1913@lldb_type_summary(['user_data_value_element', 'user_data_element_t'])
1914@header("{0: <20s} {1: <16s} {2: <20s} {3: <20s} {4: <16s} {5: <20s}".format("user_data_ve", "maderefs", "checksum", "hash value", "size", "data"))
1915def GetBagofBitsElementSummary(data_element):
1916    """ Summarizes the Bag of Bits element
1917        params: data_element = value of the object of type user_data_value_element_t
1918        returns: String with summary of the type.
1919    """
1920    format_str = "{0: <#20x} {1: <16d} {2: <#20x} {3: <#20x} {4: <16d}"
1921    out_string = format_str.format(data_element, unsigned(data_element.e_made), data_element.e_sum, data_element.e_hash, unsigned(data_element.e_size))
1922    out_string += " 0x"
1923
1924    for i in range(0, (unsigned(data_element.e_size) - 1)):
1925      out_string += "{:02x}".format(int(data_element.e_data[i]))
1926    return out_string
1927
1928def GetIPCHandleSummary(handle_ptr):
1929    """ converts a handle value inside a voucher attribute table to ipc element and returns appropriate summary.
1930        params: handle_ptr - uint64 number stored in handle of voucher.
1931        returns: str - string summary of the element held in internal structure
1932    """
1933    elem = kern.GetValueFromAddress(handle_ptr, 'ipc_importance_elem_t')
1934    if elem.iie_bits & 0x80000000 :
1935        iie = Cast(elem, 'struct ipc_importance_inherit *')
1936        return GetIPCImportanceInheritSummary(iie)
1937    else:
1938        iit = Cast(elem, 'struct ipc_importance_task *')
1939        return GetIPCImportantTaskSummary(iit)
1940
1941def GetATMHandleSummary(handle_ptr):
1942    """ Convert a handle value to atm value and returns corresponding summary of its fields.
1943        params: handle_ptr - uint64 number stored in handle of voucher
1944        returns: str - summary of atm value
1945    """
1946    return "???"
1947
1948def GetBankHandleSummary(handle_ptr):
1949    """ converts a handle value inside a voucher attribute table to bank element and returns appropriate summary.
1950        params: handle_ptr - uint64 number stored in handle of voucher.
1951        returns: str - summary of bank element
1952    """
1953    if handle_ptr == 1 :
1954        return "Bank task of Current task"
1955    elem = kern.GetValueFromAddress(handle_ptr, 'bank_element_t')
1956    if elem.be_type & 1 :
1957        ba = Cast(elem, 'struct bank_account *')
1958        return GetBankAccountSummary(ba)
1959    else:
1960        bt = Cast(elem, 'struct bank_task *')
1961        return GetBankTaskSummary(bt)
1962
1963def GetBagofBitsHandleSummary(handle_ptr):
1964    """ Convert a handle value to bag of bits value and returns corresponding summary of its fields.
1965        params: handle_ptr - uint64 number stored in handle of voucher
1966        returns: str - summary of bag of bits element
1967    """
1968    elem = kern.GetValueFromAddress(handle_ptr, 'user_data_element_t')
1969    return GetBagofBitsElementSummary(elem)
1970
1971@static_var('attr_managers',{1: GetATMHandleSummary, 2: GetIPCHandleSummary, 3: GetBankHandleSummary, 7: GetBagofBitsHandleSummary})
1972def GetHandleSummaryForKey(handle_ptr, key_num):
1973    """ Get a summary of handle pointer from the voucher attribute manager.
1974        For example key 2 -> ipc and it puts either ipc_importance_inherit_t or ipc_important_task_t.
1975                    key 3 -> Bank and it puts either bank_task_t or bank_account_t.
1976                    key 7 -> Bag of Bits and it puts user_data_element_t in handle. So summary of it would be Bag of Bits content and refs etc.
1977    """
1978    key_num = int(key_num)
1979    if key_num not in GetHandleSummaryForKey.attr_managers:
1980        return "Unknown key %d" % key_num
1981    return GetHandleSummaryForKey.attr_managers[key_num](handle_ptr)
1982
1983
1984@header("{: <18s} {: <18s} {: <10s} {: <4s} {: <18s} {: <18s}".format("ivace", "value_handle", "#refs", "rel?", "maderefs", "next_layer"))
1985@lldb_type_summary(['ivac_entry *', 'ivac_entry_t'])
1986def GetIPCVoucherAttributeEntrySummary(ivace, manager_key_num = 0):
1987    """ Get summary for voucher attribute entry.
1988    """
1989    out_str = ""
1990    fmt = "{e: <#18x} {e.ivace_value: <#18x} {e.ivace_refs: <10d} {release: <4s} {made_refs: <18s} {next_layer: <18s}"
1991    release_str = ""
1992    free_str = ""
1993    made_refs = ""
1994    next_layer = ""
1995
1996    if unsigned(ivace.ivace_releasing):
1997        release_str = "Y"
1998    if unsigned(ivace.ivace_free):
1999        free_str = 'F'
2000    if unsigned(ivace.ivace_layered):
2001        next_layer = "{: <#18x}".format(ivace.ivace_u.ivaceu_layer)
2002    else:
2003        made_refs = "{: <18d}".format(ivace.ivace_u.ivaceu_made)
2004
2005    out_str += fmt.format(e=ivace, release=release_str, made_refs=made_refs, next_layer=next_layer)
2006    if config['verbosity'] > vHUMAN and manager_key_num > 0:
2007        out_str += " " + GetHandleSummaryForKey(unsigned(ivace.ivace_value), manager_key_num)
2008    if config['verbosity'] > vHUMAN :
2009        out_str += ' {: <2s} {: <4d} {: <4d}'.format(free_str, ivace.ivace_next, ivace.ivace_index)
2010    return out_str
2011
2012@lldb_command('showivacfreelist','')
2013def ShowIVACFreeList(cmd_args=[], cmd_options={}):
2014    """ Walk the free list and print every entry in the list.
2015        usage: (lldb) showivacfreelist <ipc_voucher_attr_control_t>
2016    """
2017    if not cmd_args:
2018        raise ArgumentError('Please provide <ipc_voucher_attr_control_t>')
2019    ivac = kern.GetValueFromAddress(cmd_args[0], 'ipc_voucher_attr_control_t')
2020    print(GetIPCVoucherAttrControlSummary.header)
2021    print(GetIPCVoucherAttrControlSummary(ivac))
2022    if unsigned(ivac.ivac_freelist) == 0:
2023        print("ivac table is full")
2024        return
2025    print("index " + GetIPCVoucherAttributeEntrySummary.header)
2026    next_free = unsigned(ivac.ivac_freelist)
2027    while next_free != 0:
2028        print("{: <5d} ".format(next_free) + GetIPCVoucherAttributeEntrySummary(addressof(ivac.ivac_table[next_free])))
2029        next_free = unsigned(ivac.ivac_table[next_free].ivace_next)
2030
2031
2032
2033@header('{: <18s} {: <8s} {: <18s} {: <18s}'.format("ipc_voucher", "refs", "table", "voucher_port"))
2034@lldb_type_summary(['ipc_voucher *', 'ipc_voucher_t'])
2035def GetIPCVoucherSummary(voucher, show_entries=False):
2036    """ describe a voucher from its ipc_voucher * object """
2037    out_str = ""
2038    fmt = "{v: <#18x} {v.iv_refs.ref_count: <8d} {table_addr: <#18x} {v.iv_port: <#18x}"
2039    out_str += fmt.format(v = voucher, table_addr = addressof(voucher.iv_table))
2040    entries_str = ''
2041    if show_entries or config['verbosity'] > vHUMAN:
2042        elems = sizeof(voucher.iv_table) // sizeof(voucher.iv_table[0])
2043        entries_header_str = "\n\t" + "{: <5s} {: <3s} {: <16s} {: <30s}".format("index", "key", "value_index", "manager") + " " + GetIPCVoucherAttributeEntrySummary.header
2044        fmt =  "{: <5d} {: <3d} {: <16d} {: <30s}"
2045        for i in range(elems):
2046            voucher_entry_index = unsigned(voucher.iv_table[i])
2047            if voucher_entry_index:
2048                s = fmt.format(i, GetVoucherManagerKeyForIndex(i), voucher_entry_index, GetVoucherAttributeManagerNameForIndex(i))
2049                e = GetVoucherValueHandleFromVoucherForIndex(voucher, i)
2050                if e is not None:
2051                    s += " " + GetIPCVoucherAttributeEntrySummary(addressof(e), GetVoucherManagerKeyForIndex(i) )
2052                if entries_header_str :
2053                    entries_str = entries_header_str
2054                    entries_header_str = ''
2055                entries_str += "\n\t" + s
2056        if not entries_header_str:
2057            entries_str += "\n\t"
2058    out_str += entries_str
2059    return out_str
2060
2061def GetVoucherManagerKeyForIndex(idx):
2062    """ Returns key number for index based on global table. Will raise index error if value is incorrect
2063    """
2064    return unsigned(kern.globals.iv_global_table[idx].ivgte_key)
2065
2066def GetVoucherAttributeManagerForKey(k):
2067    """ Walks through the iv_global_table and finds the attribute manager name
2068        params: k - int key number of the manager
2069        return: cvalue - the attribute manager object.
2070                None - if not found
2071    """
2072    retval = None
2073    entry_size = sizeof(kern.globals.iv_global_table[0])
2074    elems = sizeof(kern.globals.iv_global_table) // entry_size
2075    for i in range(elems):
2076        elt = addressof(kern.globals.iv_global_table[i])
2077        if k == unsigned(elt.ivgte_key):
2078            retval = elt.ivgte_manager
2079            break
2080    return retval
2081
2082def GetVoucherAttributeControllerForKey(k):
2083    """ Walks through the iv_global_table and finds the attribute controller
2084        params: k - int key number of the manager
2085        return: cvalue - the attribute controller object.
2086                None - if not found
2087    """
2088    retval = None
2089    entry_size = sizeof(kern.globals.iv_global_table[0])
2090    elems = sizeof(kern.globals.iv_global_table) // entry_size
2091    for i in range(elems):
2092        elt = addressof(kern.globals.iv_global_table[i])
2093        if k == unsigned(elt.ivgte_key):
2094            retval = elt.ivgte_control
2095            break
2096    return retval
2097
2098
2099def GetVoucherAttributeManagerName(ivam):
2100    """ find the name of the ivam object
2101        param: ivam - cvalue object of type ipc_voucher_attr_manager_t
2102        returns: str - name of the manager
2103    """
2104    return kern.Symbolicate(unsigned(ivam))
2105
2106def GetVoucherAttributeManagerNameForIndex(idx):
2107    """ get voucher attribute manager name for index
2108        return: str - name of the attribute manager object
2109    """
2110    return GetVoucherAttributeManagerName(GetVoucherAttributeManagerForKey(GetVoucherManagerKeyForIndex(idx)))
2111
2112def GetVoucherValueHandleFromVoucherForIndex(voucher, idx):
2113    """ traverse the voucher attrs and get value_handle in the voucher attr controls table
2114        params:
2115            voucher - cvalue object of type ipc_voucher_t
2116            idx - int index in the entries for which you wish to get actual handle for
2117        returns: cvalue object of type ivac_entry_t
2118                 None if no handle found.
2119    """
2120    manager_key = GetVoucherManagerKeyForIndex(idx)
2121    voucher_num_elems = sizeof(voucher.iv_table) // sizeof(voucher.iv_table[0])
2122    if idx >= voucher_num_elems:
2123        debuglog("idx %d is out of range max: %d" % (idx, voucher_num_elems))
2124        return None
2125    voucher_entry_value = unsigned(voucher.iv_table[idx])
2126    debuglog("manager_key %d" % manager_key)
2127    ivac = GetVoucherAttributeControllerForKey(manager_key)
2128    if ivac is None or unsigned(ivac) == 0:
2129        debuglog("No voucher attribute controller for idx %d" % idx)
2130        return None
2131
2132    ivac = kern.GetValueFromAddress(unsigned(ivac), 'ipc_voucher_attr_control_t')  # ??? No idea why lldb does not addressof directly
2133    ivace_table = ivac.ivac_table
2134    if voucher_entry_value >= unsigned(ivac.ivac_table_size):
2135        print("Failed to get ivace for value %d in table of size %d" % (voucher_entry_value, unsigned(ivac.ivac_table_size)))
2136        return None
2137    return ivace_table[voucher_entry_value]
2138
2139
2140
2141@lldb_command('showallvouchers')
2142def ShowAllVouchers(cmd_args=[], cmd_options={}):
2143    """ Display a list of all vouchers in the global voucher hash table
2144        Usage: (lldb) showallvouchers
2145    """
2146    print(GetIPCVoucherSummary.header)
2147    voucher_ty = gettype("struct ipc_voucher")
2148    for v in kmemory.Zone("ipc vouchers").iter_allocated(voucher_ty):
2149        print(GetIPCVoucherSummary(value(v.AddressOf())))
2150
2151@lldb_command('showvoucher', '')
2152def ShowVoucher(cmd_args=[], cmd_options={}):
2153    """ Describe a voucher from <ipc_voucher_t> argument.
2154        Usage: (lldb) showvoucher <ipc_voucher_t>
2155    """
2156    if not cmd_args:
2157        raise ArgumentError("Please provide valid argument")
2158
2159    voucher = kern.GetValueFromAddress(cmd_args[0], 'ipc_voucher_t')
2160    print(GetIPCVoucherSummary.header)
2161    print(GetIPCVoucherSummary(voucher, show_entries=True))
2162
2163@lldb_command('showportsendrights')
2164def ShowPortSendRights(cmd_args=[], cmd_options={}):
2165    """ Display a list of send rights across all tasks for a given port.
2166        Usage: (lldb) showportsendrights <ipc_port_t>
2167    """
2168    if not cmd_args:
2169        raise ArgumentError("no port address provided")
2170    port = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_port *')
2171    i = 1
2172
2173    return FindPortRights(cmd_args=[unsigned(port)], cmd_options={'-R':'S'})
2174
2175
2176@lldb_command('showtasksuspenders')
2177def ShowTaskSuspenders(cmd_args=[], cmd_options={}):
2178    """ Display the tasks and send rights that are holding a target task suspended.
2179        Usage: (lldb) showtasksuspenders <task_t>
2180    """
2181    if not cmd_args:
2182        raise ArgumentError("no task address provided")
2183    task = kern.GetValueFromAddress(cmd_args[0], 'task_t')
2184
2185    if task.suspend_count == 0:
2186        print("task {:#x} ({:s}) is not suspended".format(unsigned(task), GetProcNameForTask(task)))
2187        return
2188
2189    # If the task has been suspended by the kernel (potentially by
2190    # kperf, using task_suspend_internal) or a client of task_suspend2
2191    # that does not convert its task suspension token to a port using
2192    # convert_task_suspension_token_to_port, then it's impossible to determine
2193    # which task did the suspension.
2194    port = task.itk_resume
2195    if not port:
2196        print("task {:#x} ({:s}) is suspended but no resume port exists".format(unsigned(task), GetProcNameForTask(task)))
2197        return
2198
2199    return FindPortRights(cmd_args=[unsigned(port)], cmd_options={'-R':'S'})
2200