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