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