xref: /xnu-10002.81.5/tools/lldbmacros/kevent.py (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
1from __future__ import absolute_import, print_function
2
3from builtins import range
4
5from xnu import *
6from workqueue import GetWorkqueueThreadRequestSummary
7
8import kmemory
9
10def IterateProcKqueues(proc):
11    """ Iterate through all kqueues in the given process
12
13        params:
14            proc - the proc object
15        returns: nothing, this is meant to be used as a generator function
16            kq - yields each kqueue in the process
17    """
18    for kqf in IterateProcKqfiles(proc):
19        yield kern.GetValueFromAddress(int(kqf), 'struct kqueue *')
20    if int(proc.p_fd.fd_wqkqueue) != 0:
21        yield kern.GetValueFromAddress(int(proc.p_fd.fd_wqkqueue), 'struct kqueue *')
22    for kqwl in IterateProcKqworkloops(proc):
23        yield kern.GetValueFromAddress(int(kqwl), 'struct kqueue *')
24
25def IterateProcKqfiles(proc):
26    """ Iterate through all kqfiles in the given process
27
28        params:
29            proc - the proc object
30        returns: nothing, this is meant to be used as a generator function
31            kqf - yields each kqfile in the process
32    """
33    filetype_KQUEUE = 5
34
35    proc_filedesc = addressof(proc.p_fd)
36    proc_ofiles = proc_filedesc.fd_ofiles
37    queues = list()
38
39    if unsigned(proc_ofiles) == 0:
40        return
41
42    for fd in range(0, unsigned(proc_filedesc.fd_afterlast)):
43        if unsigned(proc_ofiles[fd]) != 0:
44            proc_fd_flags = proc_ofiles[fd].fp_flags
45            proc_fd_fglob = proc_ofiles[fd].fp_glob
46            proc_fd_ftype = unsigned(proc_fd_fglob.fg_ops.fo_type)
47            if proc_fd_ftype == xnudefines.DTYPE_KQUEUE:
48                proc_fd_fglob_fg_data = Cast(proc_fd_fglob.fg_data, 'void *')
49                yield Cast(proc_fd_fglob_fg_data, 'struct kqfile *')
50
51def IterateProcKqworkloops(proc):
52    """ Iterate through all kqworkloops in the given process
53
54        params:
55            proc - the proc object
56        returns: nothing, this is meant to be used as a generator function
57            kqwl - yields each kqworkloop in the process
58    """
59    proc_filedesc = addressof(proc.p_fd)
60    if int(proc_filedesc.fd_kqhash) == 0:
61        return
62
63    hash_mask = proc_filedesc.fd_kqhashmask
64    for i in range(hash_mask + 1):
65        for kqwl in IterateListEntry(proc_filedesc.fd_kqhash[i], 'kqwl_hashlink'):
66            yield kqwl
67
68def IterateAllKqueues():
69    """ Iterate through all kqueues in the system
70
71        returns: nothing, this is meant to be used as a generator function
72            kq - yields each kqueue in the system
73    """
74    for t in kern.tasks:
75        proc = GetProcFromTask(t)
76        if not proc:
77            continue
78        proc = kern.GetValueFromAddress(unsigned(proc), 'proc_t')
79        for kq in IterateProcKqueues(proc):
80            yield kq
81
82def IterateProcKnotes(proc):
83    """ Iterate through all knotes in the given process
84
85        params:
86            proc - the proc object
87        returns: nothing, this is meant to be used as a generator function
88            kn - yields each knote in the process
89    """
90    proc_filedesc = addressof(proc.p_fd)
91
92    if int(proc.p_fd.fd_knlist) != 0:
93        for i in range(proc.p_fd.fd_knlistsize):
94            for kn in IterateListEntry(proc.p_fd.fd_knlist[i], 'kn_link', list_prefix='s'):
95                yield kn
96    if int(proc.p_fd.fd_knhash) != 0:
97        for i in range(proc.p_fd.fd_knhashmask + 1):
98            for kn in IterateListEntry(proc.p_fd.fd_knhash[i], 'kn_link', list_prefix='s'):
99                yield kn
100
101def GetKnoteKqueue(kn):
102    """ Get the kqueue corresponding to a given knote
103
104        params:
105            kn - the knote object
106        returns: kq - the kqueue corresponding to the knote
107    """
108
109    kmem = kmemory.KMem.get_shared()
110    addr = kmem.kn_kq_packing.unpack(unsigned(kn.kn_kq_packed))
111    return kern.CreateTypedPointerFromAddress(addr, 'struct kqueue')
112
113
114@lldb_type_summary(['knote *'])
115@header('{:<20s} {:<20s} {:<10s} {:<20s} {:<20s} {:<30s} {:<10} {:<10} {:<10} {:<20s}'.format('knote', 'ident', 'kev_flags', 'kqueue', 'udata', 'filtops', 'qos_req', 'qos_use', 'qos_ovr', 'status'))
116def GetKnoteSummary(kn):
117    """ Summarizes a knote and related information
118
119        returns: str - summary of knote
120    """
121    format_string = '{o: <#020x} {o.kn_kevent.kei_ident: <#020x} {o.kn_kevent.kei_flags: <#010x} {kq_ptr: <#020x} {o.kn_kevent.kei_udata: <#020x} {ops_str: <30s} {qos_req: <10s} {qos_use: <10s} {qos_ovr: <10s} {st_str: <20s}'
122    state = unsigned(kn.kn_status)
123    fops_str = kern.Symbolicate(kern.globals.sysfilt_ops[unsigned(kn.kn_kevent.kei_filtid)])
124    qos_index = int(kn.kn_qos_index)
125    if qos_index > 6:
126        qos_req = qos_index
127    else:
128        qos_req = int((kn.kn_kevent.kei_qos & 0x003fff00) >> 8).bit_length()
129    return format_string.format(
130            o=kn,
131            qos_req=xnudefines.thread_qos_short_strings[qos_req],
132            qos_use=xnudefines.thread_qos_short_strings[qos_index],
133            qos_ovr=xnudefines.thread_qos_short_strings[int(kn.kn_qos_override)],
134            st_str=GetOptionString('kn_status_t', state, 'KN_'),
135            kq_ptr=int(GetKnoteKqueue(kn)),
136            ops_str=fops_str)
137
138@lldb_command('showknote', fancy=True)
139def ShowKnote(cmd_args=None, cmd_options={}, O=None):
140    """ Show information about a knote
141
142        usage: showknote <struct knote *>
143    """
144    if not cmd_args:
145        return O.error('missing struct knote * argument')
146
147    kn = kern.GetValueFromAddress(cmd_args[0], 'struct knote *')
148    with O.table(GetKnoteSummary.header):
149        print(GetKnoteSummary(kn))
150
151def IterateKqueueKnotes(kq):
152    """ Iterate through all knotes of a given kqueue
153
154        params:
155            kq - the kqueue to iterate the knotes of
156        returns: nothing, this is meant to be used as a generator function
157            kn - yields each knote in the kqueue
158    """
159    proc = kq.kq_p
160    for kn in IterateProcKnotes(proc):
161        if unsigned(GetKnoteKqueue(kn)) != unsigned(addressof(kq)):
162            continue
163        yield kn
164
165kqueue_summary_fmt = '{ptr: <#020x} {o.kq_p: <#020x} {dyn_id: <#020x} {servicer: <#20x} {owner: <#20x} {o.kq_count: <6d} {st_str: <10s}'
166
167def GetServicer(req):
168    if req.tr_state in [3, 4]: # [ BINDING , BOUND ]
169        return int(req.tr_thread)
170    return 0
171
172@lldb_type_summary(['struct kqueue *'])
173@header('{: <20s} {: <20s} {: <20s} {: <20s} {: <20s} {: <6s} {: <10s}'.format('kqueue', 'process', 'dynamic_id', 'servicer', 'owner', '#evts', 'state'))
174def GetKqueueSummary(kq):
175    """ Summarize kqueue information
176
177        params:
178            kq - the kqueue object
179        returns: str - summary of kqueue
180    """
181    if int(kq.kq_state) & GetEnumValue('kq_state_t', 'KQ_WORKQ'):
182        return GetKqworkqSummary(kern.GetValueFromAddress(int(kq), 'struct kqworkq *'))
183    elif int(kq.kq_state) & GetEnumValue('kq_state_t', 'KQ_WORKLOOP'):
184        return GetKqworkloopSummary(kern.GetValueFromAddress(int(kq), 'struct kqworkloop *'))
185    else:
186        return GetKqfileSummary(kern.GetValueFromAddress(int(kq), 'struct kqfile *'))
187
188@lldb_type_summary(['struct kqfile *'])
189@header(GetKqueueSummary.header)
190def GetKqfileSummary(kqf):
191    kq = kern.GetValueFromAddress(int(kqf), 'struct kqueue *')
192    state = int(kq.kq_state)
193    return kqueue_summary_fmt.format(
194            o=kq,
195            ptr=int(kq),
196            dyn_id=0,
197            st_str=GetOptionString('kq_state_t', state, 'KQ_'),
198            servicer=0,
199            owner=0)
200
201@lldb_command('showkqfile', fancy=True)
202def ShowKqfile(cmd_args=None, cmd_options={}, O=None):
203    """ Display information about a kqfile object.
204
205        usage: showkqfile <struct kqfile *>
206    """
207    if len(cmd_args) < 1:
208        return O.error('missing struct kqfile * argument')
209
210    kqf = kern.GetValueFromAddress(cmd_args[0], 'kqfile *')
211
212    with O.table(GetKqfileSummary.header):
213        print(GetKqfileSummary(kqf))
214    with O.table(GetKnoteSummary.header):
215        for kn in IterateKqueueKnotes(kqf.kqf_kqueue):
216            print(GetKnoteSummary(kn))
217        for kn in IterateTAILQ_HEAD(kqf.kqf_suppressed, 'kn_tqe'):
218            print(GetKnoteSummary(kn))
219
220@lldb_type_summary(['struct kqworkq *'])
221@header(GetKqueueSummary.header)
222def GetKqworkqSummary(kqwq):
223    """ Summarize workqueue kqueue information
224
225        params:
226            kqwq - the kqworkq object
227        returns: str - summary of workqueue kqueue
228    """
229    return GetKqfileSummary(kern.GetValueFromAddress(int(kqwq), 'struct kqfile *'))
230
231@lldb_command('showkqworkq', fancy=True)
232def ShowKqworkq(cmd_args=None, cmd_options={}, O=None):
233    """ Display summary and knote information about a kqworkq.
234
235        usage: showkqworkq <struct kqworkq *>
236    """
237    if len(cmd_args) < 1:
238        return O.error('missing struct kqworkq * argument')
239
240    kqwq = kern.GetValueFromAddress(cmd_args[0], 'struct kqworkq *')
241    kq = kqwq.kqwq_kqueue
242    with O.table(GetKqueueSummary.header):
243        print(GetKqworkqSummary(kqwq))
244
245    with O.table(GetWorkqueueThreadRequestSummary.header):
246        for i in range(0, 7):
247            print(GetWorkqueueThreadRequestSummary(kq.kq_p, kqwq.kqwq_request[i]))
248
249    with O.table(GetKnoteSummary.header):
250        for kn in IterateKqueueKnotes(kq):
251            print(GetKnoteSummary(kn))
252
253@lldb_type_summary(['struct kqworkloop *'])
254@header(GetKqueueSummary.header)
255def GetKqworkloopSummary(kqwl):
256    """ Summarize workloop kqueue information
257
258        params:
259            kqwl - the kqworkloop object
260        returns: str - summary of workloop kqueue
261    """
262    state = int(kqwl.kqwl_kqueue.kq_state)
263    return kqueue_summary_fmt.format(
264            ptr=int(kqwl),
265            o=kqwl.kqwl_kqueue,
266            dyn_id=kqwl.kqwl_dynamicid,
267            st_str=GetOptionString('kq_state_t', state, 'KQ_'),
268            servicer=GetServicer(kqwl.kqwl_request),
269            owner=int(kqwl.kqwl_owner)
270            )
271
272@lldb_command('showkqworkloop', fancy=True)
273def ShowKqworkloop(cmd_args=None, cmd_options={}, O=None):
274    """ Display information about a kqworkloop.
275
276        usage: showkqworkloop <struct kqworkloop *>
277    """
278    if len(cmd_args) < 1:
279        return O.error('missing struct kqworkloop * argument')
280
281    kqwl = kern.GetValueFromAddress(cmd_args[0], 'struct kqworkloop *')
282
283    with O.table(GetKqworkloopSummary.header):
284        print(GetKqworkloopSummary(kqwl))
285
286    with O.table(GetWorkqueueThreadRequestSummary.header):
287        print(GetWorkqueueThreadRequestSummary(kqwl.kqwl_kqueue.kq_p, kqwl.kqwl_request))
288
289    with O.table(GetKnoteSummary.header):
290        for kn in IterateKqueueKnotes(kqwl.kqwl_kqueue):
291            print(GetKnoteSummary(kn))
292
293@lldb_command('showkqueue', fancy=True)
294def ShowKqueue(cmd_args=None, cmd_options={}, O=None):
295    """ Given a struct kqueue pointer, display the summary of the kqueue
296
297        usage: showkqueue <struct kqueue *>
298    """
299    if not cmd_args:
300        return O.error('missing struct kqueue * argument')
301
302    kq = kern.GetValueFromAddress(cmd_args[0], 'struct kqueue *')
303    if int(kq.kq_state) & GetEnumValue('kq_state_t', 'KQ_WORKQ'):
304        ShowKqworkq(cmd_args, cmd_options, O)
305    elif int(kq.kq_state) & GetEnumValue('kq_state_t', 'KQ_WORKLOOP'):
306        ShowKqworkloop(cmd_args, cmd_options, O)
307    else:
308        ShowKqfile(cmd_args, cmd_options, O)
309
310@lldb_command('showprocworkqkqueue', fancy=True)
311def ShowProcWorkqKqueue(cmd_args=None, cmd_options={}, O=None):
312    """ Show the workqueue kqueue for a given process.
313
314        usage: showprocworkqkqueue <proc_t>
315    """
316    if not cmd_args:
317        return O.error('missing struct proc * argument')
318
319    proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t')
320    ShowKqworkq(cmd_args=[str(int(proc.p_fd.fd_wqkqueue))])
321
322@lldb_command('showprockqueues', fancy=True)
323def ShowProcKqueues(cmd_args=None, cmd_options={}, O=None):
324    """ Show the kqueues for a given process.
325
326        usage: showprockqueues <proc_t>
327    """
328    if not cmd_args:
329        return O.error('missing struct proc * argument')
330
331    proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t')
332
333    with O.table(GetKqueueSummary.header):
334        for kq in IterateProcKqueues(proc):
335            print(GetKqueueSummary(kq))
336
337@lldb_command('showprocknotes', fancy=True)
338def ShowProcKnotes(cmd_args=None, cmd_options={}, O=None):
339    """ Show the knotes for a given process.
340
341        usage: showprocknotes <proc_t>
342    """
343
344    if not cmd_args:
345        return O.error('missing struct proc * argument')
346
347    proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t')
348
349    with O.table(GetKnoteSummary.header):
350        for kn in IterateProcKnotes(proc):
351            print(GetKnoteSummary(kn))
352
353@lldb_command('showallkqueues', fancy=True)
354def ShowAllKqueues(cmd_args=None, cmd_options={}, O=None):
355    """ Display a summary of all the kqueues in the system
356
357        usage: showallkqueues
358    """
359    with O.table(GetKqueueSummary.header):
360        for kq in IterateAllKqueues():
361            print(GetKqueueSummary(kq))
362
363@lldb_command('showkqueuecounts', fancy=True)
364def ShowKqCounts(cmd_args=None, cmd_options={}, O=None):
365    """ Display a count of all the kqueues in the system - lskq summary
366
367        usage: showkqueuecounts
368    """
369    print ('{: <20s} {: <35s} {: <10s} {: <6s}'.format('process', 'proc_name', '#kqfiles', '#kqworkloop'))
370    for t in kern.tasks:
371        proc = GetProcFromTask(t)
372        if not proc:
373            continue
374        proc = kern.GetValueFromAddress(unsigned(proc), 'proc_t')
375        kqfcount = 0
376        kqwlcount = 0
377        for kqf in IterateProcKqfiles(proc):
378            kqfcount += 1
379        for kqwl in IterateProcKqworkloops(proc):
380            kqwlcount += 1
381        print("{proc: <#20x} {name: <35s} {kqfile: <10d} {kqwl: <6d}".format(proc=proc,
382            name=GetProcName(proc), kqfile=kqfcount, kqwl=kqwlcount))
383