xref: /xnu-12377.41.6/tools/lldbmacros/waitq.py (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1from xnu import *
2from utils import *
3from core.configuration import *
4from core import OSHashPointer
5
6import sys
7import struct
8
9def _getSafeQ(queue):
10    g_wqs = kern.GetGlobalVariable('global_waitqs')
11    g_cnt = unsigned(kern.GetGlobalVariable('g_num_waitqs'))
12
13    return addressof(g_wqs[OSHashPointer(queue) & (g_cnt - 1)])
14
15class Waitq(object):
16    """
17    Helper class to wrap wait queues
18    """
19
20    def __init__(self, addr):
21        self._wq = kern.CreateTypedPointerFromAddress(unsigned(addr), 'struct waitq')
22        self._ty = unsigned(self._wq.waitq_type) & 0x7
23
24    def kind(self):
25        return GetEnumName('waitq_type_t', self._ty, 'WQT_')
26
27    def fifo(self):
28        return self._wq.waitq_fifo
29
30    def irq_safe(self):
31        return self._ty in [
32                GetEnumValue('waitq_type_t', 'WQT_QUEUE'),
33                GetEnumValue('waitq_type_t', 'WQT_TURNSTILE')]
34
35    def safe_queue(self):
36        if self.irq_safe():
37            return self._wq
38        elif self._ty == GetEnumValue('waitq_type_t', 'WQT_PORT'):
39            ts = self._wq.waitq_ts
40            if ts: return ts.ts_waitq.waitq_ts
41            return 0
42        else:
43            return _getSafeQ(self._wq)
44
45    def bitsStr(self):
46        out_str = ""
47
48        cticket = int(self._wq.waitq_interlock.cticket)
49        try:
50            kern.GetGlobalVariable('has_lock_pv')
51            cticket &= ~1
52        except:
53            pass
54
55        if cticket != int(self._wq.waitq_interlock.nticket):
56            out_str += "L"
57        else:
58            out_str += "-"
59        if self._wq.waitq_fifo:
60            out_str += 'F'
61        else:
62            out_str += "-"
63        if self.irq_safe():
64            out_str += 'I'
65        else:
66            out_str += "-"
67        if self._wq.waitq_preposted:
68            out_str += 'P'
69        else:
70            out_str += "-"
71        return out_str
72
73    def hasThreads(self):
74        for _ in self.iterateThreads():
75            return True
76        return False
77
78    def hasSets(self):
79        for _ in self.iterateSets():
80            return True
81        return False
82
83    def hasMembers(self):
84        for _ in self.iterateMemberLinks():
85            return True
86        return False
87
88    def iterateThreads(self):
89        if self._ty == GetEnumValue('waitq_type_t', 'WQT_PORT'):
90            ts = self._wq.waitq_ts
91            if ts:
92                for t in IterateSchedPriorityQueue(ts.ts_waitq.waitq_prio_queue,
93                        'struct thread', 'wait_prioq_links'):
94                    yield t
95        elif self._ty == GetEnumValue('waitq_type_t', 'WQT_TURNSTILE'):
96            for t in IterateSchedPriorityQueue(self._wq.waitq_prio_queue,
97                    'struct thread', 'wait_prioq_links'):
98                yield t
99        elif self._ty == GetEnumValue('waitq_type_t', 'WQT_QUEUE'):
100            for t in IterateCircleQueue(self._wq.waitq_queue, 'thread', 'wait_links'):
101                yield t
102        else:
103            for t in IterateCircleQueue(_getSafeQ(self._wq).waitq_queue, 'thread', 'wait_links'):
104                if t.waitq.wq_q == self._wq: yield t
105
106    def asTurnstile(self):
107        if self._ty == GetEnumValue('waitq_type_t', 'WQT_TURNSTILE'):
108            return containerof(self._wq, 'turnstile', 'ts_waitq')
109        return None
110
111    def asSelinfo(self):
112        if self._ty == GetEnumValue('waitq_type_t', 'WQT_SELECT'):
113            return containerof(self._wq, 'selinfo', 'si_waitq')
114        return None
115
116    def asPort(self):
117        if self._ty == GetEnumValue('waitq_type_t', 'WQT_PORT'):
118            return containerof(self._wq, 'ipc_port', 'ip_waitq')
119        return None
120
121    def asPset(self):
122        if self._ty == GetEnumValue('waitq_type_t', 'WQT_PORT_SET'):
123            return containerof(self._wq, 'ipc_pset', 'ips_wqset')
124        return None
125
126    def iterateSets(self):
127        if self._ty == GetEnumValue('waitq_type_t', 'WQT_PORT'):
128            for link in IterateCircleQueue(self._wq.waitq_links, 'struct waitq_link', 'wql_qlink'):
129                wqs = link.wql_wqs & ~1
130                if wqs: yield Waitq(kern.GetValueFromAddress(wqs))
131
132        if self._ty == GetEnumValue('waitq_type_t', 'WQT_SELECT'):
133            link = self._wq.waitq_sellinks.next
134            while link:
135                sellink = containerof(link, 'struct waitq_sellink', 'wql_next')
136                wqs = sellink.wql_wqs & ~1
137                if wqs: yield Waitq(kern.GetValueFromAddress(wqs))
138                link = link.next
139
140    def iterateMembers(self):
141        for l in self.iterateMemberLinks():
142            yield Waitq(l.wql_wq)
143
144    def iterateMemberLinks(self):
145        if self._ty == GetEnumValue('waitq_type_t', 'WQT_PORT_SET'):
146            wqs = kern.CreateTypedPointerFromAddress(unsigned(self._wq), 'struct waitq_set')
147            for l in IterateCircleQueue(wqs.wqset_links, 'struct waitq_link', 'wql_slink'):
148                yield l
149            for l in IterateCircleQueue(wqs.wqset_preposts, 'struct waitq_link', 'wql_slink'):
150                yield l
151
152@lldb_type_summary(['waitq_t', 'waitq', 'waitq *', 'waitq_set', 'waitq_set *', 'select_set', 'select_set *'])
153@header("{:<20s} {:<20s} {:<10s} {:<4s} {:>16s} {:>8s}".format(
154    'waitq', 'safequeue', 'kind', 'bits', 'evtmask', 'waiters'))
155def GetWaitqSummary(waitq):
156    if isinstance(waitq, Waitq):
157        wq = waitq
158    else:
159        wq = Waitq(waitq)
160
161    threads = len([t for t in wq.iterateThreads()])
162
163    return "{q:<#20x} {safeq:<#20x} {kind:<10s} {bits:<4s} {q.waitq_eventmask:>#16x} {threads:>8d}".format(
164            q=wq._wq, safeq=wq.safe_queue(), kind=wq.kind(), bits=wq.bitsStr(), threads=threads)
165
166# Macro: showwaitq
167
168def ShowWaitqHelper(waitq, O=None):
169    print(GetWaitqSummary(waitq))
170
171
172    if waitq.hasThreads():
173        print("Waiters:")
174    with O.table("{:<20s} {:<20s} {:s}".format('waiter', 'event', 'hint'), indent=True):
175        for thread in waitq.iterateThreads():
176            hint = thread.block_hint or thread.pending_block_hint
177            hint = GetEnumName('block_hint_t', hint, 'kThreadWait');
178            print(f"{unsigned(thread):<#20x} {thread.wait_event:<#20x} {hint:s}");
179
180    if waitq.hasSets():
181        print("Sets:")
182    with O.table(GetWaitqSummary.header, indent=True):
183        for wqs in waitq.iterateSets():
184            print(GetWaitqSummary(wqs))
185
186    if waitq.hasMembers():
187        print("Members:")
188    with O.table(GetWaitqSummary.header, indent=True):
189        for link in waitq.iterateMemberLinks():
190            print(GetWaitqSummary(link.wql_wq))
191
192@lldb_command('showwaitq', fancy=True)
193def ShowWaitq(cmd_args=None, cmd_options={}, O=None):
194    """ Print waitq structure summary.
195
196        usage: showwaitq <waitq/waitq_set>
197    """
198
199    if cmd_args is None or len(cmd_args) == 0:
200        return O.error("Missing waitq argument")
201
202    with O.table(GetWaitqSummary.header):
203        ShowWaitqHelper(Waitq(kern.GetValueFromAddress(cmd_args[0], 'struct waitq *')), O)
204
205# EndMacro: showwaitq
206# Macro: showglobalwaitqs
207
208@lldb_command('showglobalwaitqs', 'A', fancy=True)
209def ShowGlobalWaitqs(cmd_args=None, cmd_options={}, O=None):
210    """ Summarize global waitq usage
211
212        usage: showglobalwaitqs [-A]
213            -A  : show all queues, not only non empty ones
214    """
215
216    full = '-A' in cmd_options
217
218    with O.table(GetWaitqSummary.header):
219        for q in range(0, int(kern.globals.g_num_waitqs)):
220            wq = Waitq(addressof(kern.globals.global_waitqs[q]))
221            if not full and not wq.hasThreads():
222                continue
223            ShowWaitqHelper(wq, O)
224
225# EndMacro: showglobalwaitqs
226# Macro: showglobalqstats
227
228@lldb_command('showglobalqstats', "OF")
229def ShowGlobalQStats(cmd_args=None, cmd_options={}):
230    """ Summarize global waitq statistics
231
232        usage: showglobalqstats [-O] [-F]
233            -O  : only output waitqs with outstanding waits
234            -F  : output as much backtrace as was recorded
235    """
236    global kern
237    q = 0
238
239    if not hasattr(kern.globals, 'g_waitq_stats'):
240        print("No waitq stats support (use DEVELOPMENT kernel)!")
241        return
242
243    print("Global waitq stats")
244    print("{0: <18s} {1: <8s} {2: <8s} {3: <8s} {4: <8s} {5: <8s} {6: <32s}".format('waitq', '#waits', '#wakes', '#diff', '#fails', '#clears', 'backtraces'))
245
246    waiters_only = False
247    full_bt = False
248    if "-O" in cmd_options:
249        waiters_only = True
250    if "-F" in cmd_options:
251        full_bt = True
252
253    fmt_str = "{q: <#18x} {stats.waits: <8d} {stats.wakeups: <8d} {diff: <8d} {stats.failed_wakeups: <8d} {stats.clears: <8d} {bt_str: <s}"
254    while q < kern.globals.g_num_waitqs:
255        waitq = kern.globals.global_waitqs[q]
256        stats = kern.globals.g_waitq_stats[q]
257        diff = stats.waits - stats.wakeups
258        if diff == 0 and waiters_only:
259            q = q + 1
260            continue
261        last_waitstr = ''
262        last_wakestr = ''
263        fw_str = ''
264        if (stats.last_wait[0]):
265            last_waitstr = GetSourceInformationForAddress(unsigned(stats.last_wait[0]))
266        if (stats.last_wakeup[0]):
267            last_wakestr = GetSourceInformationForAddress(unsigned(stats.last_wakeup[0]))
268        if (stats.last_failed_wakeup[0]):
269            fw_str = GetSourceInformationForAddress(unsigned(stats.last_failed_wakeup[0]))
270
271        if full_bt:
272            f = 1
273            while f < kern.globals.g_nwaitq_btframes:
274                if stats.last_wait[f]:
275                    last_waitstr = "{0}->{1}".format(GetSourceInformationForAddress(unsigned(stats.last_wait[f])), last_waitstr)
276                if stats.last_wakeup[f]:
277                    last_wakestr = "{0}->{1}".format(GetSourceInformationForAddress(unsigned(stats.last_wakeup[f])), last_wakestr)
278                if stats.last_failed_wakeup[f]:
279                    fw_str = "{0}->{1}".format(GetSourceInformationForAddress(unsigned(stats.last_failed_wakeup[f])), fw_str)
280                f = f + 1
281        bt_str = ''
282        if last_waitstr:
283            bt_str += "wait : " + last_waitstr
284        if last_wakestr:
285            if bt_str:
286                bt_str += "\n{0: <70s} ".format('')
287            bt_str += "wake : " + last_wakestr
288        if fw_str:
289            if bt_str:
290                bt_str += "\n{0: <70s} ".format('')
291            bt_str += "fails: " + fw_str
292
293        print(fmt_str.format(q=addressof(waitq), stats=stats, diff=diff, bt_str=bt_str))
294        q = q + 1
295
296# EndMacro: showglobalqstats
297