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