xref: /xnu-8019.80.24/tools/lldbmacros/waitq.py (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1from xnu import *
2from utils import *
3from core.configuration import *
4
5import sys
6
7def GetWaitqStateStr(waitq):
8    wq_types = {
9            0: 'INV',
10            1: ' TS',
11            2: '  Q',
12            3: 'SET'
13    }
14    return wq_types[int(waitq.waitq_type)]
15
16def GetWaitqBitsStr(waitq):
17    out_str = ""
18    if int(waitq.waitq_interlock.nticket) != int(waitq.waitq_interlock.cticket):
19        if waitq.waitq_irq:
20            out_str += '!'
21        else:
22            out_str += '*'
23    if waitq.waitq_fifo:
24        out_str += 'F'
25    if waitq.waitq_prepost:
26        out_str += 'P'
27    if waitq.waitq_irq:
28        out_str += 'I'
29    return out_str
30
31def WaitqTableElemType(e):
32    type = (e.wqte.lt_bits >> 29) & 0x3
33    wqe_type = {
34            0: 'FREE',
35            1: 'ELEM',
36            2: 'LINK',
37            3: 'RSVD'
38    }
39    return wqe_type[type]
40
41def WaitqTableElemId(e):
42    return e.wqte.lt_id.id
43
44def WaitqTableElemValid(e):
45    if unsigned(e) == 0:
46        return 0
47    return (e.wqte.lt_bits & 0x80000000) == 0x80000000
48
49def WaitqTableElemRefcnt(e):
50    return (e.wqte.lt_bits & 0x1fffffff)
51
52LTABLE_ID_GEN_SHIFT = 0
53LTABLE_ID_GEN_BITS  = 46
54LTABLE_ID_GEN_MASK  = 0x00003fffffffffff
55LTABLE_ID_IDX_SHIFT = LTABLE_ID_GEN_BITS
56LTABLE_ID_IDX_BITS  = 18
57LTABLE_ID_IDX_MASK  = 0xffffc00000000000
58
59def WaitqTableIdxFromId(id):
60    return int((id & LTABLE_ID_IDX_MASK) >> LTABLE_ID_IDX_SHIFT)
61
62def WaitqTableGenFromId(id):
63    return (unsigned(id) & LTABLE_ID_GEN_MASK) >> LTABLE_ID_GEN_SHIFT
64
65def GetWaitqLink(id):
66    if int(id) == 0:
67        return 0, "NULL link id"
68    idx = WaitqTableIdxFromId(id)
69    if idx >= kern.globals.g_wqlinktable.nelem:
70        return 0, "Invalid waitq link table id: {:d}".format(id)
71    slab_slot = idx / kern.globals.g_wqlinktable.slab_elem;
72    slab = kern.globals.g_wqlinktable.table[int(slab_slot)]
73    if slab == 0:
74        print "Invalid waitq link table id:", str(id), " (invalid slab)"
75    first_elem = Cast(slab, 'lt_elem *')
76    addr = int(slab) + ((idx - first_elem.lt_id.idx) * int(kern.globals.g_wqlinktable.elem_sz))
77    link = kern.GetValueFromAddress(addr, 'waitq_link *')
78    gen = WaitqTableGenFromId(id)
79    warn_str = ''
80    if gen > 0 and link.wqte.lt_id.generation != gen:
81        warn_str = "WARNING: found idx:{:d}/gen:{:d}, but requested idx:{:d}/gen:{:d}".format(link.wqte.lt_id.idx, link.wqte.lt_id.generation, idx, gen)
82        link = 0
83    return link, warn_str
84
85def GetWaitqPrepost(id):
86    idx = WaitqTableIdxFromId(id)
87    if idx > int(kern.globals.g_prepost_table.nelem):
88        warn_str = "Invalid waitq prepost table id {:s}".format(str(id))
89        return 0, warn_str
90    slab_slot = idx / kern.globals.g_prepost_table.slab_elem;
91    slab = kern.globals.g_prepost_table.table[int(slab_slot)]
92    if slab == 0:
93        warn_str = "Invalid waitq prepost table id:", str(id), " (invalid slab)"
94        return 0, warn_str
95    first_elem = Cast(slab, 'lt_elem *')
96    addr = int(slab) + ((idx - first_elem.lt_id.idx) * int(kern.globals.g_prepost_table.elem_sz))
97    wqp = kern.GetValueFromAddress(addr, 'wq_prepost *')
98    gen = WaitqTableGenFromId(id)
99    warn_str = ''
100    if gen > 0 and wqp.wqte.lt_id.generation != gen:
101        warn_str = "WARNING: found idx:{:d}/gen:{:d}, but requested idx:{:d}/gen:{:d}".format(wqp.wqte.lt_id.idx, wqp.wqte.lt_id.generation, idx, gen)
102        wqp = 0
103    return wqp, warn_str
104
105
106def GetWaitqSetidString(setid):
107    idx = WaitqTableIdxFromId(setid)
108    gen = WaitqTableGenFromId(setid)
109    str = "{:>7d}/{:<#14x}".format(unsigned(idx), unsigned(gen))
110    return str
111
112
113def GetWaitqSets(waitq):
114    sets = []
115
116    if int(waitq) == 0:
117        return sets
118
119    ref = waitq.waitq_set_id
120    while int(ref.wqr_value) != 0:
121        if int(ref.wqr_value) & 1:
122            sets.append(GetWaitqSetidString(ref.wqr_value))
123            break
124
125        link = Cast(ref.wqr_value, 'struct waitq_link *')
126        sets.append(GetWaitqSetidString(link.wql_node))
127        ref  = link.wql_next
128
129    return sets
130
131def GetFrameString(pc, compact=True):
132    str = GetSourceInformationForAddress(unsigned(pc))
133    if compact:
134        return re.sub(r'.*0x[0-9a-f]+\s+<(\w+)( \+ 0x[0-9a-f]+)*>.*', r'\1', str, re.UNICODE)
135    else:
136        return re.sub(r'.*(0x[0-9a-f]+)\s+<(\w+)( \+ 0x[0-9a-f]+)*>.*', r'\2(\1)', str, re.UNICODE)
137
138@lldb_type_summary(['waitq_link', 'waitq_link *'])
139@header("{:<18s} {:<18s} {:<19s} {:<10s} {:<1s} {:<4s} {:<10s} {:<20s}".format('addr','id','idx','gen','V','type','refcnt','info'))
140def GetWaitqSetidLinkSummary(link, verbose=False):
141    has_stats = 0
142    if not link:
143        return ""
144    fmt_str = "{l: <#18x} {l.wqte.lt_id.id: <#18x} {l.wqte.lt_id.idx: <7d} (->{l.wqte.lt_next_idx: <7d}) {l.wqte.lt_id.generation: <#10x} {v: <1s} {t: <4s} {rcnt: <10d} "
145    if hasattr(link, 'sl_alloc_task'):
146        has_stats = 1
147        fmt_str += "owner:{l.sl_alloc_task: <#x}/th:{l.sl_alloc_th: <#x}\n"
148        fmt_str += ' '*87
149        try:
150            pid = GetProcPIDForTask(link.sl_alloc_task)
151        except:
152            proc_ro = Cast(link.sl_alloc_task.bsd_info_ro, 'proc_ro *')
153            pid = unsigned(proc_ro.task_tokens.audit_token.val[5])
154        pidnm = ""
155        if pid < 0:
156            pidnm = "DEAD:{:s}".format(GetProcNameForTask(link.sl_alloc_task))
157        else:
158            pidnm += GetProcNameForPid(pid)
159        fmt_str += "      ({:d}/{:s}), ".format(pid, pidnm)
160    type = WaitqTableElemType(link)
161    if type == "ELEM":
162        type = "WQS"
163    v = "F"
164    if WaitqTableElemValid(link):
165        v = "T"
166    refcnt = WaitqTableElemRefcnt(link)
167    out_str = fmt_str.format(l=link, v=v, t=type, rcnt=refcnt)
168    if type == "WQS":
169        out_str += "wqs:{0: <#18x}".format(unsigned(link.wql_set))
170    elif type == "LINK":
171        sID = link.wql_node
172        stype = "<invalid>"
173        if WaitqTableElemValid(GetWaitqLink(sID)[0]):
174            stype = "WQS"
175        if int(link.wql_next.wqr_value) & 1:
176            nID = link.wql_next.wqr_value
177            ntype = "<invalid>"
178            if WaitqTableElemValid(GetWaitqLink(nID)[0]):
179                ntype = "WQS"
180        else:
181            nID = WaitqTableElemId(Cast(link.wql_next.wqr_value, 'struct waitq_link *'))
182            ntype = "LINK"
183        out_str += "set:{:<#x}({:s}), next:{:<#x}({:s})".format(sID, stype, nID, ntype)
184    if hasattr(link, 'sl_alloc_bt') and unsigned(link.sl_alloc_bt[0]) > 0:
185        fmt_str = "\n{:s}alloc_bt({:d}):[".format(' '*87, link.sl_alloc_ts)
186        f = 0
187        while f < kern.globals.g_nwaitq_btframes:
188            fstr = GetFrameString(link.sl_alloc_bt[f], not verbose)
189            f += 1
190            if f == kern.globals.g_nwaitq_btframes:
191                fmt_str += "{:<s}".format(fstr)
192            else:
193                fmt_str += "{:<s} <- ".format(fstr)
194        fmt_str += "]"
195        out_str += fmt_str
196    if hasattr(link, 'sl_mkvalid_bt') and unsigned(link.sl_mkvalid_bt[0]) > 0:
197        fmt_str = "\n{:s}mkvalid_bt({:d}):[".format(' '*87, link.sl_mkvalid_ts)
198        f = 0
199        while f < kern.globals.g_nwaitq_btframes:
200            fstr = GetFrameString(link.sl_mkvalid_bt[f], not verbose)
201            f += 1
202            if f == kern.globals.g_nwaitq_btframes:
203                fmt_str += "{:<s}".format(fstr)
204            else:
205                fmt_str += "{:<s} <- ".format(fstr)
206        fmt_str += "]"
207        out_str += fmt_str
208    if hasattr(link, 'sl_invalidate_bt') and unsigned(link.sl_invalidate_bt[0]) > 0:
209        fmt_str = "\n{:s}invalidate_bt({:d}):[".format(' '*87, link.sl_invalidate_ts)
210        f = 0
211        while f < kern.globals.g_nwaitq_btframes:
212            fstr = GetFrameString(link.sl_invalidate_bt[f], not verbose)
213            f += 1
214            if f == kern.globals.g_nwaitq_btframes:
215                fmt_str += "{:<s}".format(fstr)
216            else:
217                fmt_str += "{:<s} <- ".format(fstr)
218        fmt_str += "]"
219        out_str += fmt_str
220    return out_str
221
222def PrintWaitqSetidLinkTree(link, verbose, sets, indent=87):
223    if not WaitqTableElemType(link) == "LINK":
224        return
225
226    # set
227    sID = link.wql_node
228    sset = GetWaitqLink(nID)[0]
229    stype = "<invalid>"
230    if WaitqTableElemValid(sset):
231        sets.append(addressof(sset.wql_set.wqset_q))
232        stype = "WQS"
233    lstr = "S:{:<#x}({:s})".format(sID, stype)
234
235    # next
236    if int(link.wql_next.wqr_value) & 1:
237        nID = link.wql_next.wqr_value
238        nset = GetWaitqLink(nID)[0]
239        ntype = "<invalid>"
240        if WaitqTableElemValid():
241            sets.append(addressof(sset.wql_set.wqset_q))
242            ntype = "WQS"
243    else:
244        nID = WaitqTableElemId(Cast(link.wql_next.wqr_value, 'struct waitq_link *'))
245        ntype = "LINK"
246    rstr = "P:{:<#x}({:s})".format(sID, stype)
247
248    print "{:s}`->{:s}, {:s}".format(' '*indent, lstr, rstr)
249    if ltype == "WQS":
250        PrintWaitqSetidLinkTree(right, verbose, sets, indent + len(lstr) + 6);
251    else:
252        print "{:s}`->{:s}, {:s}".format(' '*indent, lstr, rstr)
253        PrintWaitqSetidLinkTree(left, verbose, sets, indent + 4);
254        PrintWaitqSetidLinkTree(right, verbose, sets, indent + len(lstr) + 6)
255    return
256
257# Macro: showsetidlink
258@lldb_command('showsetidlink', "S:FT")
259def ShowSetidLink(cmd_args=None, cmd_options={}):
260    """ Print waitq_link structure summary
261
262        Note: you can pass either a complete ID (generation + index), or
263              just the index to the -S argument.
264
265        usage: showsetidlink [-F] [-S ID] [0xaddr]
266            -S {ID} : show the setid link whose ID is {ID}
267            -F      : follow the chain of setid structures
268                      and print a summary of each one
269            -T      : print the tree of setidlinks in table format
270    """
271    link = 0
272    followchain = 0
273    showtree = 0
274    verbose = False
275    if config['verbosity'] > vHUMAN:
276        verbose = True
277    if "-T" in cmd_options:
278        showtree = 1
279    if "-S" in cmd_options:
280        id = value(kern.GetValueFromAddress(0).GetSBValue().CreateValueFromExpression(None, '(uint64_t)'+cmd_options["-S"]))
281        link, warn_str = GetWaitqLink(id)
282        if not link:
283            if warn_str != '':
284                raise LookupError(warn_str)
285            else:
286                raise ArgumentError("Invalid link ID {:d}({:<#x}".format(id, id))
287    if "-F" in cmd_options:
288        followchain = 1
289    if link == 0:
290        if not cmd_args:
291            raise ArgumentError("Please pass the address of a waitq_link object")
292        link = kern.GetValueFromAddress(cmd_args[0], 'waitq_link *')
293    if not link:
294        raise ArgumentError("Invalid waitq_link {:s}".format(cmd_args[0]))
295
296    print GetWaitqSetidLinkSummary.header
297    print GetWaitqSetidLinkSummary(link, verbose)
298    if followchain == 1:
299        next_id = link.wqte.lt_next_idx
300        max_elem = int(kern.globals.g_wqlinktable.nelem)
301        if hasattr(kern.globals, 'g_lt_idx_max'):
302            max_elem = unsigned(kern.globals.g_lt_idx_max)
303        while link != 0 and next_id < max_elem:
304            link, warn_str = GetWaitqLink(unsigned(next_id))
305            if link != 0:
306                print GetWaitqSetidLinkSummary(link, verbose)
307                next_id = link.wqte.lt_next_idx
308    if showtree == 1:
309        sets = []
310        print "\nLinkTree:{:<#x}({:s})".format(link.wqte.lt_id.id, WaitqTableElemType(link))
311        PrintWaitqSetidLinkTree(link, verbose, sets, 9)
312        if len(sets) > 0:
313            print "{:d} Sets:".format(len(sets))
314            for wq in sets:
315                pp_str = GetWaitqPreposts(wq)
316                npreposts = len(pp_str)
317                nps = ""
318                if npreposts > 0:
319                    if npreposts > 1:
320                        nps = "s: "
321                    else:
322                        nps = ": "
323                    nps += ';'.join(pp_str)
324                else:
325                    nps = "s"
326                print "\tWQS:{:<#x} ({:d} prepost{:s})".format(unsigned(wq),npreposts,nps)
327# EndMacro: showsetidlink
328@lldb_command('showwaitqlink', "S:FT")
329def ShowWaitqLink(cmd_args=None, cmd_options={}):
330    """ Print waitq_link structure summary
331    """
332    ShowSetidLink(cmd_args, cmd_options)
333
334
335# Macro: showallpreposts
336@lldb_command('showallpreposts', 'VQT:F:Y:')
337def ShowAllPreposts(cmd_args=None, cmd_options={}):
338    """ Dump / summarize all waitq prepost linkage elements
339
340        usage: showallpreposts [-V] [-T {type}] [-Y n] [-F n] [-Q]
341            -V        : only show valid / live links
342            -T {type} : only display objects of type {type}
343            -Y {0|1}  : only only show POST objects that are
344                        valid (-Y 1) or invalid (-Y 0)
345            -F n      : summarize the backtraces at frame level 'n'
346            -Q        : be quiet, only summarize
347    """
348    opt_summary = 0
349    opt_type_filt = ""
350    opt_valid_only = 0
351    opt_post_type = -1
352    opt_bt_idx = 0
353    verbose = False
354    if config['verbosity'] > vHUMAN:
355        verbose = True
356    if "-Q" in cmd_options:
357        opt_summary = 1
358    if "-V" in cmd_options:
359        opt_valid_only = 1
360    if "-Y" in cmd_options:
361        opt_post_type = unsigned(cmd_options["-Y"])
362        if opt_post_type != 0 and opt_post_type != 1:
363            raise ArgumentError("Invalid POST obj specifier [-Y %d] (expected 0 or 1)" % cmd_options["-Y"])
364    if "-F" in cmd_options:
365        opt_bt_idx = unsigned(cmd_options["-F"])
366        if hasattr(kern.globals, "g_nwaitq_btframes"):
367            if opt_bt_idx >= unsigned(kern.globals.g_nwaitq_btframes):
368                raise ArgumentError("Invalid BT index '{:s}' max:{:d}".format(cmd_options["-F"], unsigned(kern.globals.g_nwaitq_btframes) - 1))
369    if "-T" in cmd_options:
370        opt_type_filt = cmd_options["-T"]
371        if opt_type_filt == "FREE" or opt_type_filt == "RSVD":
372            pass
373        elif opt_type_filt == "POST":
374            opt_type_filt = "LINK"
375        elif opt_type_filt == "WQ":
376            opt_type_filt = "ELEM"
377        else:
378            raise ArgumentError("Invalid type filter'{:s}'".format(cmd_options["-T"]))
379    table = kern.globals.g_prepost_table
380    nelem = int(table.nelem)
381    bt_summary = {}
382    nfree = 0
383    ninv = 0
384    nwq = 0
385    npost = 0
386    nrsvd = 0
387    hdr_str = "Looking through {:d} objects from g_prepost_table@{:<#x}".format(nelem, addressof(kern.globals.g_prepost_table))
388    if opt_type_filt != "" or opt_valid_only != 0:
389        hdr_str += "\n\t`-> for "
390        if opt_valid_only:
391            hdr_str += "valid "
392        else:
393            hdr_str += "all "
394        if opt_type_filt == "":
395            hdr_str += "objects"
396        else:
397            hdr_str += "{:s} objects".format(cmd_options["-T"])
398    print hdr_str
399    if not opt_summary:
400        print GetWaitqPrepostSummary.header
401    id = 0
402    while id < nelem:
403        wqp = GetWaitqPrepost(id)[0]
404        if wqp == 0:
405            print "<<<invalid prepost:{:d}>>>".format(id)
406            ninv += 1
407        else:
408            lt = WaitqTableElemType(wqp)
409            isvalid = WaitqTableElemValid(wqp)
410            should_count = 1
411            if isvalid and opt_post_type > -1 and lt == "LINK":
412                post_wqp = GetWaitqPrepost(wqp.wqp_post.wqp_wq_id)[0]
413                post_valid = WaitqTableElemValid(post_wqp)
414                if opt_post_type == 0 and post_valid: # only count _invalid_ POST objects
415                    should_count = 0
416                elif opt_post_type == 1 and not post_valid: # only count _valid_ POST objects
417                    should_count = 0
418            if should_count and (opt_type_filt == "" or opt_type_filt == lt) and ((opt_valid_only == 0 or isvalid)):
419                if lt == "ELEM":
420                    nwq += 1
421                elif lt == "LINK":
422                    npost += 1
423                elif lt == "RSVD":
424                    nrsvd += 1
425                elif lt == "FREE":
426                    nfree += 1
427                else:
428                    ninv += 1
429                if hasattr(wqp, 'wqp_alloc_bt'):
430                    pc = unsigned(wqp.wqp_alloc_bt[opt_bt_idx])
431                    pc_str = str(pc)
432                    if pc > 0:
433                        if pc_str in bt_summary:
434                            bt_summary[pc_str] += 1
435                        else:
436                            bt_summary[pc_str] = 1
437                if not opt_summary:
438                    print GetWaitqPrepostSummary(wqp)
439        if verbose:
440            sys.stderr.write('id: {:d}/{:d}...          \r'.format(id, nelem))
441        id += 1
442    nused = nwq + npost + nrsvd
443    nfound = nused + nfree + ninv
444    print "\nFound {:d} objects: {:d} WQ, {:d} POST, {:d} RSVD, {:d} FREE".format(nfound, nwq, npost, nrsvd, nfree)
445    if (opt_type_filt == "" and opt_valid_only == 0) and (nused != table.used_elem):
446        print"\tWARNING: inconsistent state! Table reports {:d}/{:d} used elem, found {:d}/{:d}".format(table.used_elem, nelem, nused, nfound)
447    if len(bt_summary) > 0:
448        print "Link allocation BT (frame={:d})".format(opt_bt_idx)
449    for k,v in bt_summary.iteritems():
450        print "\t[{:d}] from: {:s}".format(v, GetSourceInformationForAddress(unsigned(k)))
451# EndMacro: showallpreposts
452
453
454@lldb_type_summary(['wq_prepost', 'wq_prepost *'])
455@header("{:<18s} {:<18s} {:<19s} {:<10s} {:<1s} {:<4s} {:<10s} {:<20s}".format('addr','id','idx','gen','V','type','refcnt','info'))
456def GetWaitqPrepostSummary(wqp):
457    if not wqp:
458        return
459    fmt_str = "{w: <#18x} {w.wqte.lt_id.id: <#18x} {w.wqte.lt_id.idx: <7d} (->{w.wqte.lt_next_idx: <7d}) {w.wqte.lt_id.generation: <#10x} {v: <1s} {t: <4s} {rcnt: <10d} "
460    type = WaitqTableElemType(wqp)
461    if type == "ELEM":
462        type = "WQ"
463    elif type == "LINK":
464        type = "POST"
465    v = "F"
466    if WaitqTableElemValid(wqp):
467        v = "T"
468    refcnt = WaitqTableElemRefcnt(wqp)
469    out_str = fmt_str.format(w=wqp, v=v, t=type, rcnt=refcnt)
470    if type == "WQ":
471        out_str += "wq:{0: <#18x}".format(unsigned(wqp.wqp_wq.wqp_wq_ptr))
472    elif type == "POST":
473        out_str += "next:{0: <#18x}, wqid:{1: <#18x}".format(wqp.wqp_post.wqp_next_id, wqp.wqp_post.wqp_wq_id)
474        post_wqp = GetWaitqPrepost(wqp.wqp_post.wqp_wq_id)[0]
475        if not WaitqTableElemValid(post_wqp):
476            out_str += "(<invalid>)"
477        else:
478            if WaitqTableElemType(post_wqp) != "ELEM":
479                out_str += "(!WQP_WQ?)"
480            else:
481                out_str += "({0: <#18x})".format(unsigned(post_wqp.wqp_wq.wqp_wq_ptr))
482    return out_str
483
484
485# Macro: showprepost
486@lldb_command('showprepost', "P:")
487def ShowPrepost(cmd_args=None, cmd_options={}):
488    """ Print prepost structure summary
489
490        Note: you can pass either a complete ID (generation + index), or
491              just the index to the -P argument.
492
493        usage: showprepost [-P ID] [0xaddr]
494            -P {ID} : show prepost structure whose ID is {ID}
495    """
496    wqp = 0
497    if "-P" in cmd_options:
498        wqp, warn_str = GetWaitqPrepost(unsigned(kern.GetValueFromAddress(cmd_options["-P"], 'uint64_t *')))
499        if wqp == 0:
500            if warn_str != '':
501                raise LookupError(warn_str)
502            else:
503                raise ArgumentError("Invalid prepost ID {:s}".format(cmd_options["-P"]))
504    if wqp == 0:
505        if not cmd_args:
506            raise ArgumentError("Please pass the address of a prepost object")
507        wqp = kern.GetValueFromAddress(cmd_args[0], 'wq_prepost *')
508    if not wqp:
509        raise ArgumentError("Invalid prepost {:s}".format(cmd_args[0]))
510
511    print GetWaitqPrepostSummary.header
512    print GetWaitqPrepostSummary(wqp)
513# EndMacro: showprepost
514
515
516def WaitqPrepostFromObj(wqp, head_id, inv_ok, prepost_str, pp_arr = 0, depth = 0):
517    if pp_arr != 0:
518        pp_arr.append(wqp)
519    etype = WaitqTableElemType(wqp)
520    if not WaitqTableElemValid(wqp) and not inv_ok:
521        id = 0
522        if wqp:
523            id = wqp.wqte.lt_id.id
524        prepost_str.append("{0: <#18x}:{1: <18s}".format(id, "<invalid>"))
525        return
526    if etype == "ELEM": # WQP_WQ
527        prepost_str.append("{0: <#18x}:{1: <#18x}".format(wqp.wqte.lt_id.id, unsigned(wqp.wqp_wq.wqp_wq_ptr)))
528        return
529
530    post_wq = 0
531
532    if etype == "LINK": # WQP_POST
533        next_id = wqp.wqp_post.wqp_next_id
534        post_wq = GetWaitqPrepost(wqp.wqp_post.wqp_wq_id)[0]
535        if WaitqTableElemValid(post_wq):
536            if WaitqTableElemType(post_wq) != "ELEM":
537                prepost_str.append("{0: <#18x}:{1: <18s}".format(post_wq.wqte.lt_id.id, "<invalid post>"))
538            else:
539                prepost_str.append("{0: <#18x}:{1: <#18x}".format(wqp.wqte.lt_id.id, unsigned(post_wq.wqp_wq.wqp_wq_ptr)))
540        if next_id > 0 and next_id != head_id:
541            if depth >= 950:
542                prepost_str.append("{: <37s}".format("!recursion limit!"))
543                return
544            WaitqPrepostFromObj(GetWaitqPrepost(next_id)[0], head_id, inv_ok, prepost_str, pp_arr, depth + 1)
545    else: #  "RSVD" or "FREE":
546        prepost_str.append("{0: <#18x} -> {1: <15d}".format(wqp.wqte.lt_id.id, wqp.wqte.lt_next_idx))
547        next_id = wqp.wqte.lt_next_idx
548        max_elem = int(kern.globals.g_prepost_table.nelem)
549        if hasattr(kern.globals, 'g_lt_idx_max'):
550            max_elem = unsigned(kern.globals.g_lt_idx_max)
551        if next_id < max_elem:
552            if depth >= 950:
553                prepost_str.append("{: <37s}".format("!recursion limit!"))
554                return
555            WaitqPrepostFromObj(GetWaitqPrepost(next_id)[0], head_id, inv_ok, prepost_str, pp_arr, depth + 1)
556    return
557
558def GetPrepostChain(head_id, inv_ok = False, pp_arr = 0):
559    pp = []
560    if unsigned(head_id) == 0:
561        return [ "{0: <#18x}:{1: <18s}".format(head_id, "<invalid>") ]
562    if unsigned(head_id) == 0xffffffffffffffff:
563        return [ "{0: <#18x}:{1: <18s}".format(head_id, "<anonymous>") ]
564    wqp = GetWaitqPrepost(head_id)[0]
565    if wqp != 0:
566        WaitqPrepostFromObj(wqp, head_id, inv_ok, pp, pp_arr)
567    else:
568        return [ "{0: <#18x}:{1: <18s}".format(head_id, "<invalid>") ]
569    return pp
570
571def GetWaitqPreposts(waitq):
572    if GetWaitqStateStr(waitq) != "SET":
573        return []
574    wqset = Cast(waitq, 'waitq_set *')
575    if wqset.wqset_prepost_id == 0:
576        return []
577    return GetPrepostChain(wqset.wqset_prepost_id)
578
579
580# Macro: showprepostchain
581@lldb_command('showprepostchain', "P:")
582def ShowPrepostChain(cmd_args=None, cmd_options={}):
583    """ Follow a chain of preposts, printing each one.
584        Note that prepost chains are circular, so this will print
585        the entire chain given a single element.
586
587        Note: you can pass either a complete ID (generation + index), or
588              just the index to the -P argument.
589
590        usage: showprepostchain [-P ID] [0xaddr]
591            -P {ID} : start printing with the prepost whose ID is {ID}
592    """
593    wqp = 0
594    if "-P" in cmd_options:
595        wqp, warn_str = GetWaitqPrepost(unsigned(kern.GetValueFromAddress(cmd_options["-P"], 'uint64_t *')))
596        if wqp == 0:
597            if warn_str != '':
598                raise LookupError(warn_str)
599            else:
600                raise ArgumentError("Invalid prepost ID {:s}".format(cmd_options["-P"]))
601    if wqp == 0:
602        if not cmd_args:
603            raise ArgumentError("Please pass the address of a prepost object")
604        wqp = kern.GetValueFromAddress(cmd_args[0], 'wq_prepost *')
605    if not wqp:
606        raise ArgumentError("Invalid prepost {:s}".format(cmd_args[0]))
607
608    pp_arr = []
609    GetPrepostChain(wqp.wqte.lt_id.id, True, pp_arr)
610    pp_cnt = len(pp_arr)
611    idx = 0
612    nvalid = 0
613    ninvalid = 0
614    print GetWaitqPrepostSummary.header
615    while idx < pp_cnt:
616        print GetWaitqPrepostSummary(pp_arr[idx])
617        if pp_arr[idx] != 0:
618            type = WaitqTableElemType(pp_arr[idx])
619            if type == "LINK":
620                post_wqp = GetWaitqPrepost(pp_arr[idx].wqp_post.wqp_wq_id)[0]
621                if not WaitqTableElemValid(post_wqp):
622                    ninvalid += 1
623                else:
624                    nvalid += 1
625            else:
626                nvalid += 1
627        idx += 1
628    print "%s" % '-'*86
629    print "Total: {:d} ({:d} valid, {:d} invalid)".format(len(pp_arr), nvalid, ninvalid)
630# EndMacro: showprepostchain
631
632
633@lldb_type_summary(['waitq', 'waitq *'])
634@header("{: <16s} {: <3s} {: <4s} {: <17s} {: <18s} {: <18s} {: <37s} {: <22s} {: <10s}".format('waitq', 'typ', 'bits', 'evtmask', 'setid', 'wq_wqp', 'preposts', 'member_of', 'threads'))
635def GetWaitqSummary(waitq):
636    fmt_str = "{q: <16x} {state: <3s} {bits: <4s} {q.waitq_eventmask: <#17x} {setid: <#18x} {q.waitq_prepost_id: <#18x}"
637    th_str = []
638    if waitq.waitq_queue.next and waitq.waitq_queue.prev:
639        for thread in IterateLinkageChain(addressof(waitq.waitq_queue), 'thread *', 'wait_links'):
640            th_str.append("{: <18s} e:{: <#18x}".format(hex(thread), thread.wait_event))
641    else:
642        th_str.append("{: <39s}".format('<invalid (NULL) queue>'))
643    th_cnt = len(th_str)
644    set_str = GetWaitqSets(waitq)
645    set_cnt = len(set_str)
646    pp_str = GetWaitqPreposts(waitq)
647    pp_cnt = len(pp_str)
648    last_str = ''
649    idx = 0;
650    while idx < pp_cnt or idx < set_cnt or idx < th_cnt:
651        p = ""
652        s = ""
653        t = ""
654        if idx < pp_cnt:
655            p = pp_str[idx]
656        if idx < set_cnt:
657            s = set_str[idx]
658        if idx < th_cnt:
659            t = th_str[idx]
660        if idx == 0:
661            last_str += "{0: <37s} {1: <22s} {2: <39s}".format(p, s, t)
662        else:
663            last_str += "\n{0: <80s} {1: <37s} {2: <22s} {3: <39s}".format('', p, s, t)
664        idx += 1
665    if pp_cnt > 0 or set_cnt > 0 or th_cnt > 0:
666        last_str += "\n{:<80s} {: <37s} {: <22s} {: <39s}".format('', '-'*37, '-'*20, '-'*39)
667        last_str += "\n{0: <80s} {1: <37d} {2: <22d} {3: <39d}".format('', pp_cnt, set_cnt, th_cnt)
668
669    state = GetWaitqStateStr(waitq)
670    setid = 0
671    if state == "SET":
672        setid = Cast(waitq, 'waitq_set *').wqset_id
673    out_str = fmt_str.format(q=waitq, state=state, bits=GetWaitqBitsStr(waitq), setid=setid)
674    out_str += last_str
675    return out_str
676
677# Macro: showwaitq
678@lldb_command('showwaitq', "P:S:")
679def ShowWaitq(cmd_args=None, cmd_options={}):
680    """ Print waitq structure summary.
681        Lookup the waitq either by address, by Set ID, or indirectly
682        through a prepost object that points to the waitq.
683
684        Note: you can pass either a complete ID (generation + index), or
685              just the index to the -P and -S arguments.
686
687        usage: showwaitq [-P PrePostID] [-S SetID] [0xaddr]
688            -P {ID}  : prepost ID that points to a waitq
689            -S {ID}  : waitq_set ID
690    """
691    waitq = 0
692    if "-P" in cmd_options:
693        wqp, warn_str = GetWaitqPrepost(unsigned(kern.GetValueFromAddress(cmd_options["-P"], 'uint64_t *')))
694        if wqp == 0:
695            if warn_str:
696                raise LookupError(warn_str)
697            else:
698                raise ArgumentError("Invalid prepost ID {:s}".format(cmd_options["-P"]))
699        if WaitqTableElemType(wqp) != "ELEM":
700            raise ArgumentError("Prepost ID {:s} points to a WQP_POST object, not a WQP_WQ!".format(cmd_options["-P"]))
701        waitq = wqp.wqp_wq.wqp_wq_ptr
702    if "-S" in cmd_options:
703        if waitq:
704            raise ArgumentError("Please pass only one of '-S' or '-P'!")
705        link, warn_str = GetWaitqLink(unsigned(kern.GetValueFromAddress(cmd_options["-S"],'uint64_t *')))
706        if not link:
707            if warn_str != '':
708                raise LookupError(warn_str)
709            else:
710                raise ArgumentError("Invalid link ID {:s}".format(cmd_options["-S"]))
711        if WaitqTableElemType(link) != "ELEM":
712            raise ArgumentError("Link ID {:s} points to a SLT_LINK object, not an SLT_WQS!".format(cmd_options["-S"]))
713        waitq = addressof(link.wql_set.wqset_q)
714
715    if not waitq and not cmd_args:
716        raise ArgumentError("Please pass the address of a waitq!")
717    if not waitq:
718        waitq = kern.GetValueFromAddress(cmd_args[0], 'waitq *')
719    if not waitq:
720        raise ("Unknown arguments: %r %r" % (cmd_args, cmd_options))
721    print GetWaitqSummary.header
722    print GetWaitqSummary(waitq)
723# EndMacro: showwaitq
724
725
726# Macro: showglobalwaitqs
727@lldb_command('showglobalwaitqs')
728def ShowGlobalWaitqs(cmd_args=None):
729    """ Summarize global waitq usage
730    """
731    global kern
732    q = 0
733
734    print "Global waitq objects"
735    print GetWaitqSummary.header
736
737    while q < kern.globals.g_num_waitqs:
738        print GetWaitqSummary(addressof(kern.globals.global_waitqs[q]))
739        q = q + 1
740# EndMacro: showglobalwaitqs
741
742
743# Macro: showglobalqstats
744@lldb_command('showglobalqstats', "OF")
745def ShowGlobalQStats(cmd_args=None, cmd_options={}):
746    """ Summarize global waitq statistics
747
748        usage: showglobalqstats [-O] [-F]
749            -O  : only output waitqs with outstanding waits
750            -F  : output as much backtrace as was recorded
751    """
752    global kern
753    q = 0
754
755    if not hasattr(kern.globals, 'g_waitq_stats'):
756        print "No waitq stats support (use DEVELOPMENT kernel)!"
757        return
758
759    print "Global waitq stats"
760    print "{0: <18s} {1: <8s} {2: <8s} {3: <8s} {4: <8s} {5: <8s} {6: <32s}".format('waitq', '#waits', '#wakes', '#diff', '#fails', '#clears', 'backtraces')
761
762    waiters_only = False
763    full_bt = False
764    if "-O" in cmd_options:
765        waiters_only = True
766    if "-F" in cmd_options:
767        full_bt = True
768
769    fmt_str = "{q: <#18x} {stats.waits: <8d} {stats.wakeups: <8d} {diff: <8d} {stats.failed_wakeups: <8d} {stats.clears: <8d} {bt_str: <s}"
770    while q < kern.globals.g_num_waitqs:
771        waitq = kern.globals.global_waitqs[q]
772        stats = kern.globals.g_waitq_stats[q]
773        diff = stats.waits - stats.wakeups
774        if diff == 0 and waiters_only:
775            q = q + 1
776            continue
777        last_waitstr = ''
778        last_wakestr = ''
779        fw_str = ''
780        if (stats.last_wait[0]):
781            last_waitstr = GetSourceInformationForAddress(unsigned(stats.last_wait[0]))
782        if (stats.last_wakeup[0]):
783            last_wakestr = GetSourceInformationForAddress(unsigned(stats.last_wakeup[0]))
784        if (stats.last_failed_wakeup[0]):
785            fw_str = GetSourceInformationForAddress(unsigned(stats.last_failed_wakeup[0]))
786
787        if full_bt:
788            f = 1
789            while f < kern.globals.g_nwaitq_btframes:
790                if stats.last_wait[f]:
791                    last_waitstr = "{0}->{1}".format(GetSourceInformationForAddress(unsigned(stats.last_wait[f])), last_waitstr)
792                if stats.last_wakeup[f]:
793                    last_wakestr = "{0}->{1}".format(GetSourceInformationForAddress(unsigned(stats.last_wakeup[f])), last_wakestr)
794                if stats.last_failed_wakeup[f]:
795                    fw_str = "{0}->{1}".format(GetSourceInformationForAddress(unsigned(stats.last_failed_wakeup[f])), fw_str)
796                f = f + 1
797        bt_str = ''
798        if last_waitstr:
799            bt_str += "wait : " + last_waitstr
800        if last_wakestr:
801            if bt_str:
802                bt_str += "\n{0: <70s} ".format('')
803            bt_str += "wake : " + last_wakestr
804        if fw_str:
805            if bt_str:
806                bt_str += "\n{0: <70s} ".format('')
807            bt_str += "fails: " + fw_str
808
809        print fmt_str.format(q=addressof(waitq), stats=stats, diff=diff, bt_str=bt_str)
810        q = q + 1
811# EndMacro: showglobalqstats
812