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