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