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