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