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