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