]> git.saurik.com Git - apple/xnu.git/blame - tools/lldbmacros/waitq.py
xnu-3247.1.106.tar.gz
[apple/xnu.git] / tools / lldbmacros / waitq.py
CommitLineData
3e170ce0
A
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: '???',
11 2: ' Q',
12 3: 'SET'
13 }
14 return wq_types[int(waitq.waitq_type)]
15
16def GetWaitqBitsStr(waitq):
17 out_str = ""
18 if (Cast(waitq.waitq_interlock, 'int') != 0):
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.wqt_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.wqt_id.id
43
44def WaitqTableElemValid(e):
45 if unsigned(e) == 0:
46 return 0
47 return (e.wqte.wqt_bits & 0x80000000) == 0x80000000
48
49def WaitqTableElemRefcnt(e):
50 return (e.wqte.wqt_bits & 0x1fffffff)
51
52def WaitqTableIdxFromId(id):
53 if hasattr(kern.globals, 'g_wqt_idx_max'):
54 idx = id & unsigned(kern.globals.g_wqt_idx_max)
55 else:
56 # best guess
57 idx = id & 0x000000000003ffff
58 return int(idx)
59
60def WaitqTableGenFromId(id):
61 if hasattr(kern.globals, 'g_wqt_idx_max'):
62 msk = ~unsigned(kern.globals.g_wqt_idx_max)
63 else:
64 # best guess
65 msk = ~0x000000000003ffff
66 shift = 0
67 while (msk & 0x1) == 0:
68 msk >>= 1
69 shift += 1
70 return (unsigned(id) >> shift) & msk
71
72def GetWaitqLink(id):
73 if int(id) == 0:
74 return 0, "NULL link id"
75 idx = WaitqTableIdxFromId(id)
76 if idx >= kern.globals.g_linktable.nelem:
77 return 0, "Invalid waitq link table id: {:d}".format(id)
78 slab_slot = idx / kern.globals.g_linktable.slab_elem;
79 slab = kern.globals.g_linktable.table[int(slab_slot)]
80 if slab == 0:
81 print "Invalid waitq link table id:", str(id), " (invalid slab)"
82 first_elem = Cast(slab, 'wqt_elem *')
83 addr = int(slab) + ((idx - first_elem.wqt_id.idx) * int(kern.globals.g_linktable.elem_sz))
84 link = kern.GetValueFromAddress(addr, 'setid_link *')
85 gen = WaitqTableGenFromId(id)
86 warn_str = ''
87 if gen > 0 and link.wqte.wqt_id.generation != gen:
88 warn_str = "WARNING: found idx:{:d}/gen:{:d}, but requested idx:{:d}/gen:{:d}".format(link.wqte.wqt_id.idx, link.wqte.wqt_id.generation, idx, gen)
89 link = 0
90 return link, warn_str
91
92def GetWaitqPrepost(id):
93 idx = WaitqTableIdxFromId(id)
94 if idx > int(kern.globals.g_prepost_table.nelem):
95 warn_str = "Invalid waitq prepost table id {:s}".format(str(id))
96 return 0, warn_str
97 slab_slot = idx / kern.globals.g_prepost_table.slab_elem;
98 slab = kern.globals.g_prepost_table.table[int(slab_slot)]
99 if slab == 0:
100 warn_str = "Invalid waitq prepost table id:", str(id), " (invalid slab)"
101 return 0, warn_str
102 first_elem = Cast(slab, 'wqt_elem *')
103 addr = int(slab) + ((idx - first_elem.wqt_id.idx) * int(kern.globals.g_prepost_table.elem_sz))
104 wqp = kern.GetValueFromAddress(addr, 'wq_prepost *')
105 gen = WaitqTableGenFromId(id)
106 warn_str = ''
107 if gen > 0 and wqp.wqte.wqt_id.generation != gen:
108 warn_str = "WARNING: found idx:{:d}/gen:{:d}, but requested idx:{:d}/gen:{:d}".format(wqp.wqte.wqt_id.idx, wqp.wqte.wqt_id.generation, idx, gen)
109 wqp = 0
110 return wqp, warn_str
111
112
113def GetWaitqSetidString(setid):
114 idx = WaitqTableIdxFromId(setid)
115 gen = WaitqTableGenFromId(setid)
116 # This must match the format used in WaitqSetsFromLink
117 str = "{:>7d}/{:<#14x}".format(unsigned(idx), unsigned(gen))
118 return str
119
120
121def WaitqSetsFromLink(link, sets, depth):
122 if int(link) == 0:
123 sets.append("{: <22s}".format("<link:NULL>"))
124 return
125 if WaitqTableElemType(link) == "ELEM":
126 #sets.append("{: <#18x}".format(unsigned(link.sl_wqs.sl_set)))
127 #sets.append("{:>7d}/{:<#14x}".format(unsigned(id.idx),unsigned(id.generation)))
128 sets.append(GetWaitqSetidString(link.wqte.wqt_id.id))
129 return
130 if depth >= 950:
131 sets.append("{: <22s}".format("!recursion limit!"))
132 return
133 left_link = GetWaitqLink(link.sl_link.sl_left_setid)[0]
134 right_link = GetWaitqLink(link.sl_link.sl_right_setid)[0]
135 WaitqSetsFromLink(left_link, sets, depth + 1)
136 WaitqSetsFromLink(right_link, sets, depth + 1)
137 return
138
139def GetWaitqSets(waitq):
140 sets = []
141 if int(waitq) == 0:
142 return sets
143 if waitq.waitq_set_id == 0:
144 return sets
145 link = GetWaitqLink(waitq.waitq_set_id)[0]
146 WaitqSetsFromLink(link, sets, 0)
147 return sets
148
149def GetFrameString(pc, compact=True):
150 str = GetSourceInformationForAddress(unsigned(pc))
151 if compact:
152 return re.sub(r'.*0x[0-9a-f]+\s+<(\w+)( \+ 0x[0-9a-f]+)*>.*', r'\1', str, re.UNICODE)
153 else:
154 return re.sub(r'.*(0x[0-9a-f]+)\s+<(\w+)( \+ 0x[0-9a-f]+)*>.*', r'\2(\1)', str, re.UNICODE)
155
156@lldb_type_summary(['setid_link', 'setid_link *'])
157@header("{:<18s} {:<18s} {:<19s} {:<10s} {:<1s} {:<4s} {:<10s} {:<20s}".format('addr','id','idx','gen','V','type','refcnt','info'))
158def GetWaitqSetidLinkSummary(link, verbose=False):
159 has_stats = 0
160 if not link:
161 return ""
162 fmt_str = "{l: <#18x} {l.wqte.wqt_id.id: <#18x} {l.wqte.wqt_id.idx: <7d} (->{l.wqte.wqt_next_idx: <7d}) {l.wqte.wqt_id.generation: <#10x} {v: <1s} {t: <4s} {rcnt: <10d} "
163 if hasattr(link, 'sl_alloc_task'):
164 has_stats = 1
165 fmt_str += "owner:{l.sl_alloc_task: <#x}/th:{l.sl_alloc_th: <#x}\n"
166 fmt_str += ' '*87
167 try:
168 pid = GetProcPIDForTask(link.sl_alloc_task)
169 except:
170 pid = unsigned(link.sl_alloc_task.audit_token.val[5])
171 pidnm = ""
172 if pid < 0:
173 pidnm = "DEAD:{:s}".format(GetProcNameForTask(link.sl_alloc_task))
174 else:
175 pidnm += GetProcNameForPid(pid)
176 fmt_str += " ({:d}/{:s}), ".format(pid, pidnm)
177 type = WaitqTableElemType(link)
178 if type == "ELEM":
179 type = "WQS"
180 v = "F"
181 if WaitqTableElemValid(link):
182 v = "T"
183 refcnt = WaitqTableElemRefcnt(link)
184 out_str = fmt_str.format(l=link, v=v, t=type, rcnt=refcnt)
185 if type == "WQS":
186 out_str += "wqs:{0: <#18x}".format(unsigned(link.sl_wqs.sl_set))
187 elif type == "LINK":
188 lID = link.sl_link.sl_left_setid
189 rID = link.sl_link.sl_right_setid
190 left = GetWaitqLink(lID)[0]
191 right = GetWaitqLink(rID)[0]
192 ltype = "<invalid>"
193 if WaitqTableElemValid(left):
194 ltype = WaitqTableElemType(left)
195 if ltype == "ELEM":
196 ltype = "WQS"
197 rtype = "<invalid>"
198 if WaitqTableElemValid(right):
199 rtype = WaitqTableElemType(right)
200 if rtype == "ELEM":
201 rtype = "WQS"
202 out_str += "left:{:<#x}({:s}), right:{:<#x}({:s})".format(lID, ltype, rID, rtype)
203 if hasattr(link, 'sl_alloc_bt') and unsigned(link.sl_alloc_bt[0]) > 0:
204 fmt_str = "\n{:s}alloc_bt({:d}):[".format(' '*87, link.sl_alloc_ts)
205 f = 0
206 while f < kern.globals.g_nwaitq_btframes:
207 fstr = GetFrameString(link.sl_alloc_bt[f], not verbose)
208 f += 1
209 if f == kern.globals.g_nwaitq_btframes:
210 fmt_str += "{:<s}".format(fstr)
211 else:
212 fmt_str += "{:<s} <- ".format(fstr)
213 fmt_str += "]"
214 out_str += fmt_str
215 if hasattr(link, 'sl_mkvalid_bt') and unsigned(link.sl_mkvalid_bt[0]) > 0:
216 fmt_str = "\n{:s}mkvalid_bt({:d}):[".format(' '*87, link.sl_mkvalid_ts)
217 f = 0
218 while f < kern.globals.g_nwaitq_btframes:
219 fstr = GetFrameString(link.sl_mkvalid_bt[f], not verbose)
220 f += 1
221 if f == kern.globals.g_nwaitq_btframes:
222 fmt_str += "{:<s}".format(fstr)
223 else:
224 fmt_str += "{:<s} <- ".format(fstr)
225 fmt_str += "]"
226 out_str += fmt_str
227 if hasattr(link, 'sl_invalidate_bt') and unsigned(link.sl_invalidate_bt[0]) > 0:
228 fmt_str = "\n{:s}invalidate_bt({:d}):[".format(' '*87, link.sl_invalidate_ts)
229 f = 0
230 while f < kern.globals.g_nwaitq_btframes:
231 fstr = GetFrameString(link.sl_invalidate_bt[f], not verbose)
232 f += 1
233 if f == kern.globals.g_nwaitq_btframes:
234 fmt_str += "{:<s}".format(fstr)
235 else:
236 fmt_str += "{:<s} <- ".format(fstr)
237 fmt_str += "]"
238 out_str += fmt_str
239 return out_str
240
241def PrintWaitqSetidLinkTree(link, verbose, sets, indent=87):
242 if not WaitqTableElemType(link) == "LINK":
243 return
244 lID = link.sl_link.sl_left_setid
245 rID = link.sl_link.sl_right_setid
246 left = GetWaitqLink(lID)[0]
247 right = GetWaitqLink(rID)[0]
248
249 ltype = "<invalid>"
250 if WaitqTableElemValid(left):
251 ltype = WaitqTableElemType(left)
252 if ltype == "ELEM":
253 ltype = "WQS"
254 lstr = "L:{:<#x}({:s})".format(lID, ltype)
255
256 rtype = "<invalid>"
257 if WaitqTableElemValid(right):
258 rtype = WaitqTableElemType(right)
259 if rtype == "ELEM":
260 rtype = "WQS"
261 rstr = "R:{:<#x}({:s})".format(rID, rtype)
262
263 if ltype == "WQS":
264 sets.append(addressof(left.sl_wqs.sl_set.wqset_q))
265 if rtype == "WQS":
266 sets.append(addressof(right.sl_wqs.sl_set.wqset_q))
267
268 print "{:s}`->{:s}, {:s}".format(' '*indent, lstr, rstr)
269 if ltype == "WQS":
270 PrintWaitqSetidLinkTree(right, verbose, sets, indent + len(lstr) + 6);
271 else:
272 print "{:s}`->{:s}, {:s}".format(' '*indent, lstr, rstr)
273 PrintWaitqSetidLinkTree(left, verbose, sets, indent + 4);
274 PrintWaitqSetidLinkTree(right, verbose, sets, indent + len(lstr) + 6)
275 return
276
277# Macro: showsetidlink
278@lldb_command('showsetidlink', "S:FT")
279def ShowSetidLink(cmd_args=None, cmd_options={}):
280 """ Print setid_link structure summary
281
282 Note: you can pass either a complete ID (generation + index), or
283 just the index to the -S argument.
284
285 usage: showsetidlink [-F] [-S ID] [0xaddr]
286 -S {ID} : show the setid link whose ID is {ID}
287 -F : follow the chain of setid structures
288 and print a summary of each one
289 -T : print the tree of setidlinks in table format
290 """
291 link = 0
292 followchain = 0
293 showtree = 0
294 verbose = False
295 if config['verbosity'] > vHUMAN:
296 verbose = True
297 if "-T" in cmd_options:
298 showtree = 1
299 if "-S" in cmd_options:
300 id = unsigned(kern.GetValueFromAddress(cmd_options["-S"], 'uint64_t *'))
301 link, warn_str = GetWaitqLink(id)
302 if not link:
303 if warn_str != '':
304 raise LookupError(warn_str)
305 else:
306 raise ArgumentError("Invalid link ID {:d}({:<#x}".format(id, id))
307 if "-F" in cmd_options:
308 followchain = 1
309 if link == 0:
310 if not cmd_args:
311 raise ArgumentError("Please pass the address of a setid_link object")
312 link = kern.GetValueFromAddress(cmd_args[0], 'setid_link *')
313 if not link:
314 raise ArgumentError("Invalid setid_link {:s}".format(cmd_args[0]))
315
316 print GetWaitqSetidLinkSummary.header
317 print GetWaitqSetidLinkSummary(link, verbose)
318 if followchain == 1:
319 next_id = link.wqte.wqt_next_idx
320 max_elem = int(kern.globals.g_linktable.nelem)
321 if hasattr(kern.globals, 'g_wqt_idx_max'):
322 max_elem = unsigned(kern.globals.g_wqt_idx_max)
323 while link != 0 and next_id < max_elem:
324 link, warn_str = GetWaitqLink(unsigned(next_id))
325 if link != 0:
326 print GetWaitqSetidLinkSummary(link, verbose)
327 next_id = link.wqte.wqt_next_idx
328 if showtree == 1:
329 sets = []
330 print "\nLinkTree:{:<#x}({:s})".format(link.wqte.wqt_id.id, WaitqTableElemType(link))
331 PrintWaitqSetidLinkTree(link, verbose, sets, 9)
332 if len(sets) > 0:
333 print "{:d} Sets:".format(len(sets))
334 for wq in sets:
335 pp_str = GetWaitqPreposts(wq)
336 npreposts = len(pp_str)
337 nps = ""
338 if npreposts > 0:
339 if npreposts > 1:
340 nps = "s: "
341 else:
342 nps = ": "
343 nps += ';'.join(pp_str)
344 else:
345 nps = "s"
346 print "\tWQS:{:<#x} ({:d} prepost{:s})".format(unsigned(wq),npreposts,nps)
347# EndMacro: showsetidlink
348
349
350# Macro: showallsetidlinks
351@lldb_command('showallsetidlinks', 'V:T:S:F:XQ')
352def ShowAllSetidLinks(cmd_args=None, cmd_options={}):
353 """ Dump / summarize all waitq set linktable elements
354
355 usage: showallsetidlinks [options]
356 -V {0,1} : only show [1 == valid/live links, 0 == invalid links]
357 -T {type} : only display objects of type {type}
358 -S {desc} : only display objects of type {type} which fit {desc}
359 -T LINK -S {desc} can be:
360 iL : Invalid left-link pointer (only)
361 iR : Invalid right-link pointer (only)
362 iLR : Invalid left+right link pointers
363 iLRI : Invalid left+right link pointers AND dead allocating process
364 w/o "-T" -S {desc} can be:
365 iP : Invalid / Dead allocating process
366 -F n : summarize the backtraces at frame level 'n'
367 -X : cross-check waitq pointers in link table
368 -Q : be quiet, only summarize
369 """
370 opt_summary = 0
371 opt_type_filt = ""
372 opt_valid_only = 0
373 opt_invalid_only = 0
374 opt_bt_idx = 0
375 opt_cross_check = 0
376 opt_validate_links = 0
377 opt_subtype_filter = 0
378 verbose = False
379 if config['verbosity'] > vHUMAN:
380 verbose = True
381 if "-Q" in cmd_options:
382 opt_summary = 1
383 if "-V" in cmd_options:
384 if int(cmd_options["-V"]) == 1:
385 opt_valid_only = 1
386 elif int(cmd_options["-V"]) == 0:
387 opt_invalid_only = 1
388 else:
389 raise ArgumentError("Invalid parameter to -V '{:s}': expecting 0 or 1".format(cmd_options["-V"]))
390 if "-X" in cmd_options:
391 opt_cross_check = 1
392 nunique_wqs = 0
393 nduplicated_wqs = 0
394 max_wqs_dupes = 0
395 if "-F" in cmd_options:
396 opt_bt_idx = unsigned(cmd_options["-F"])
397 if hasattr(kern.globals, "g_nwaitq_btframes"):
398 if opt_bt_idx >= unsigned(kern.globals.g_nwaitq_btframes):
399 raise ArgumentError("Invalid BT index '{:s}' max:{:d}".format(cmd_options["-F"], unsigned(kern.globals.g_nwaitq_btframes) - 1))
400 if "-T" in cmd_options:
401 opt_type_filt = cmd_options["-T"]
402 if opt_type_filt == "FREE" or opt_type_filt == "RSVD" or opt_type_filt == "LINK":
403 pass
404 elif opt_type_filt == "WQS":
405 opt_type_filt = "ELEM"
406 else:
407 raise ArgumentError("Invalid type filter'{:s}'".format(cmd_options["-T"]))
408 if "-S" in cmd_options:
409 opt_subtype_filter = cmd_options["-S"]
410 if opt_type_filt == "LINK":
411 if not (opt_subtype_filter == "iL" or \
412 opt_subtype_filter == "iR" or \
413 opt_subtype_filter == "iLR" or \
414 opt_subtype_filter == "iLRI"):
415 raise ArgumentError("Invalid LINK sub-type filter \{desc\}: {:s}".format(opt_subtype_filter))
416 elif opt_type_filt == "":
417 if not opt_subtype_filter == "iP":
418 raise ArgumentError("Invalid sub-type filter \{desc\}: {:s}".format(opt_subtype_filter))
419 table = kern.globals.g_linktable
420 nelem = int(table.nelem)
421 wq_ptr = {}
422 bt_summary = {}
423 nfree = 0
424 ninv = 0
425 nwqs = 0
426 nlink = 0
427 nrsvd = 0
428 hdr_str = "Looking through {:d} setid_link objects from g_linktable@{:<#x}".format(nelem, addressof(kern.globals.g_linktable))
429 if opt_type_filt != "" or opt_valid_only != 0:
430 hdr_str += "\n\t`-> for "
431 if opt_valid_only:
432 hdr_str += "valid "
433 else:
434 hdr_str += "all "
435 if opt_type_filt == "":
436 hdr_str += "objects"
437 else:
438 hdr_str += "{:s} objects".format(opt_type_filt)
439 else:
440 if opt_valid_only:
441 hdr_str += "\n\t`-> showing only VALID links"
442 elif opt_invalid_only:
443 hdr_str += "\n\t`-> showing only INVALID links"
444 if opt_subtype_filter != 0:
445 if opt_type_filt != "LINK" and opt_type_filt != "":
446 raise ArgumentError("Subtype (-S {desc}) can only be used with (-T LINK) or no type filter at all")
447 hdr_str += "\n\t`-> filtering {:s} objects through '{:s}'".format(opt_type_filt, opt_subtype_filter)
448 if opt_cross_check:
449 hdr_str += "\n\t`-> cross-checking WQS elements for duplicates"
450 hdr_str += "\n\n"
451 print hdr_str
452 if not opt_summary:
453 print GetWaitqSetidLinkSummary.header
454 id = 0
455 while id < nelem:
456 if id == 0:
457 # Set a generation count to differentiate from an invalid ID
458 first_entry = Cast(kern.globals.g_linktable.table[0], 'wqt_elem *')
459 link = GetWaitqLink(first_entry.wqt_id.id)[0]
460 else:
461 link = GetWaitqLink(id)[0]
462 if not link:
463 print "<<<invalid link:{:d}>>>".format(id)
464 ninv += 1
465 else:
466 lt = WaitqTableElemType(link)
467 isvalid = WaitqTableElemValid(link)
468 inconsistent = 0
469 do_print = not ( (isvalid and opt_invalid_only) or (not isvalid and opt_valid_only) )
470 if do_print and opt_subtype_filter != 0 and lt == "LINK":
471 lID = link.sl_link.sl_left_setid
472 rID = link.sl_link.sl_right_setid
473 left = GetWaitqLink(lID)[0]
474 right = GetWaitqLink(rID)[0]
475 lValid = WaitqTableElemValid(left)
476 rValid = WaitqTableElemValid(right)
477 if opt_subtype_filter == "iL":
478 if lValid or (not lValid and not rValid):
479 do_print = False
480 elif opt_subtype_filter == "iR":
481 if rValid or (not rValid and not lValid):
482 do_print = False
483 elif opt_subtype_filter == "iLR":
484 if rValid or lValid:
485 do_print = False
486 elif opt_subtype_filter == "iLRI" and hasattr(link, 'sl_alloc_task'):
487 # only print this if both left and right are invalid
488 # and the allocating task is unknown/dead
489 do_print = False
490 is_dead = 0
491 pid = -1
492 try:
493 pid = GetProcPIDForTask(link.sl_alloc_task)
494 except:
495 if link.sl_alloc_task:
496 pid = unsigned(link.sl_alloc_task.audit_token.val[5])
497 if pid < 0:
498 is_dead = 1
499 else:
500 pidnm = GetProcNameForPid(pid)
501 if pidnm == "Unknown":
502 is_dead = 1
503 if (not rValid) and (not lValid) and is_dead:
504 do_print = True
505
506 if do_print and opt_type_filt == "" and opt_subtype_filter == "iP" and hasattr(link, 'sl_alloc_task'):
507 # Only print non-free table objects that were allocated by
508 # dead processes
509 do_print = False
510 is_dead = 0
511 pid = -1
512 try:
513 pid = GetProcPIDForTask(link.sl_alloc_task)
514 except:
515 if link.sl_alloc_task:
516 pid = unsigned(link.sl_alloc_task.audit_token.val[5])
517 if pid < 0:
518 is_dead = 1
519 else:
520 pidnm = GetProcNameForPid(pid)
521 if pidnm == "Unknown":
522 is_dead = 1
523 if is_dead:
524 do_print = True
525
526 if (opt_type_filt == "" or opt_type_filt == lt) and do_print:
527 if lt == "ELEM":
528 nwqs += 1
529 elif lt == "LINK":
530 nlink += 1
531 elif lt == "RSVD":
532 nrsvd += 1
533 elif lt == "FREE":
534 nfree += 1
535 else:
536 ninv += 1
537 inconsistent = 1
538 if hasattr(link, 'sl_alloc_bt'):
539 pc = unsigned(link.sl_alloc_bt[opt_bt_idx])
540 pc_str = str(pc)
541 if pc > 0:
542 if pc_str in bt_summary:
543 bt_summary[pc_str] += 1
544 else:
545 bt_summary[pc_str] = 1
546 if not opt_summary:
547 print GetWaitqSetidLinkSummary(link, verbose)
548 if inconsistent:
549 ninconsistent += 1
550 # print out warnings about inconsistent state as we parse
551 # the list - even if the caller wants a summary
552 print "[WARNING] inconsistent state in idx: {:d} ({:s} element)".format(link.wqte.wqt_id.idx, lt)
553 if opt_cross_check == 1 and lt == "ELEM":
554 wq = unsigned(addressof(link.sl_wqs.sl_set.wqset_q))
555 if wq in wq_ptr:
556 wq_ptr[wq].append(id)
557 l = len(wq_ptr[wq])
558 if l == 2:
559 nduplicated_wqs += 1
560 if l > max_wqs_dupes:
561 max_wqs_dupes = l
562 else:
563 wq_ptr[wq] = [ id ]
564 nunique_wqs += 1
565 id += 1
566 if opt_summary or verbose:
567 if verbose and opt_cross_check:
568 sys.stderr.write('[{:d}|{:d}|{:d}] id: {:d}/{:d}... \r'.format(nunique_wqs, nduplicated_wqs, max_wqs_dupes, id, nelem))
569 else:
570 sys.stderr.write('id: {:d}/{:d}... \r'.format(id, nelem))
571
572 nused = nwqs + nlink + nrsvd
573 nfound = nused + nfree + ninv
574 print "\n\nFound {:d} objects: {:d} WQS, {:d} LINK, {:d} RSVD, {:d} FREE".format(nfound, nwqs, nlink, nrsvd, nfree)
575 if (opt_type_filt == "" and opt_valid_only == 0) and (nused != table.used_elem):
576 print"\tWARNING: inconsistent state! Table reports {:d}/{:d} used elem, found {:d}/{:d}".format(table.used_elem, nelem, nused, nfound)
577 if len(bt_summary) > 0:
578 print "Link allocation BT (frame={:d})".format(opt_bt_idx)
579 for k,v in bt_summary.iteritems():
580 print "\t[{:d}] from: {:s}".format(v, GetSourceInformationForAddress(unsigned(k)))
581 if opt_cross_check:
582 print "\n{:d} Duplicated WQS objects:".format(nduplicated_wqs)
583 for wq in wq_ptr:
584 l = len(wq_ptr[wq])
585 if l > 1:
586 print "\tWQS:{:#x} ({:d} {:s}".format(wq, l, str(wq_ptr[wq]))
587# EndMacro: showallsetidlinks
588
589
590# Macro: showallpreposts
591@lldb_command('showallpreposts', 'VQT:F:Y:')
592def ShowAllPreposts(cmd_args=None, cmd_options={}):
593 """ Dump / summarize all waitq prepost linkage elements
594
595 usage: showallpreposts [-V] [-T {type}] [-Y n] [-F n] [-Q]
596 -V : only show valid / live links
597 -T {type} : only display objects of type {type}
598 -Y {0|1} : only only show POST objects that are
599 valid (-Y 1) or invalid (-Y 0)
600 -F n : summarize the backtraces at frame level 'n'
601 -Q : be quiet, only summarize
602 """
603 opt_summary = 0
604 opt_type_filt = ""
605 opt_valid_only = 0
606 opt_post_type = -1
607 opt_bt_idx = 0
608 verbose = False
609 if config['verbosity'] > vHUMAN:
610 verbose = True
611 if "-Q" in cmd_options:
612 opt_summary = 1
613 if "-V" in cmd_options:
614 opt_valid_only = 1
615 if "-Y" in cmd_options:
616 opt_post_type = unsigned(cmd_options["-Y"])
617 if opt_post_type != 0 and opt_post_type != 1:
618 raise ArgumentError("Invalid POST obj specifier [-Y %d] (expected 0 or 1)" % cmd_options["-Y"])
619 if "-F" in cmd_options:
620 opt_bt_idx = unsigned(cmd_options["-F"])
621 if hasattr(kern.globals, "g_nwaitq_btframes"):
622 if opt_bt_idx >= unsigned(kern.globals.g_nwaitq_btframes):
623 raise ArgumentError("Invalid BT index '{:s}' max:{:d}".format(cmd_options["-F"], unsigned(kern.globals.g_nwaitq_btframes) - 1))
624 if "-T" in cmd_options:
625 opt_type_filt = cmd_options["-T"]
626 if opt_type_filt == "FREE" or opt_type_filt == "RSVD":
627 pass
628 elif opt_type_filt == "POST":
629 opt_type_filt = "LINK"
630 elif opt_type_filt == "WQ":
631 opt_type_filt = "ELEM"
632 else:
633 raise ArgumentError("Invalid type filter'{:s}'".format(cmd_options["-T"]))
634 table = kern.globals.g_prepost_table
635 nelem = int(table.nelem)
636 bt_summary = {}
637 nfree = 0
638 ninv = 0
639 nwq = 0
640 npost = 0
641 nrsvd = 0
642 hdr_str = "Looking through {:d} objects from g_prepost_table@{:<#x}".format(nelem, addressof(kern.globals.g_prepost_table))
643 if opt_type_filt != "" or opt_valid_only != 0:
644 hdr_str += "\n\t`-> for "
645 if opt_valid_only:
646 hdr_str += "valid "
647 else:
648 hdr_str += "all "
649 if opt_type_filt == "":
650 hdr_str += "objects"
651 else:
652 hdr_str += "{:s} objects".format(cmd_options["-T"])
653 print hdr_str
654 if not opt_summary:
655 print GetWaitqPrepostSummary.header
656 id = 0
657 while id < nelem:
658 wqp = GetWaitqPrepost(id)[0]
659 if wqp == 0:
660 print "<<<invalid prepost:{:d}>>>".format(id)
661 ninv += 1
662 else:
663 lt = WaitqTableElemType(wqp)
664 isvalid = WaitqTableElemValid(wqp)
665 should_count = 1
666 if isvalid and opt_post_type > -1 and lt == "LINK":
667 post_wqp = GetWaitqPrepost(wqp.wqp_post.wqp_wq_id)[0]
668 post_valid = WaitqTableElemValid(post_wqp)
669 if opt_post_type == 0 and post_valid: # only count _invalid_ POST objects
670 should_count = 0
671 elif opt_post_type == 1 and not post_valid: # only count _valid_ POST objects
672 should_count = 0
673 if should_count and (opt_type_filt == "" or opt_type_filt == lt) and ((opt_valid_only == 0 or isvalid)):
674 if lt == "ELEM":
675 nwq += 1
676 elif lt == "LINK":
677 npost += 1
678 elif lt == "RSVD":
679 nrsvd += 1
680 elif lt == "FREE":
681 nfree += 1
682 else:
683 ninv += 1
684 if hasattr(wqp, 'wqp_alloc_bt'):
685 pc = unsigned(wqp.wqp_alloc_bt[opt_bt_idx])
686 pc_str = str(pc)
687 if pc > 0:
688 if pc_str in bt_summary:
689 bt_summary[pc_str] += 1
690 else:
691 bt_summary[pc_str] = 1
692 if not opt_summary:
693 print GetWaitqPrepostSummary(wqp)
694 if verbose:
695 sys.stderr.write('id: {:d}/{:d}... \r'.format(id, nelem))
696 id += 1
697 nused = nwq + npost + nrsvd
698 nfound = nused + nfree + ninv
699 print "\nFound {:d} objects: {:d} WQ, {:d} POST, {:d} RSVD, {:d} FREE".format(nfound, nwq, npost, nrsvd, nfree)
700 if (opt_type_filt == "" and opt_valid_only == 0) and (nused != table.used_elem):
701 print"\tWARNING: inconsistent state! Table reports {:d}/{:d} used elem, found {:d}/{:d}".format(table.used_elem, nelem, nused, nfound)
702 if len(bt_summary) > 0:
703 print "Link allocation BT (frame={:d})".format(opt_bt_idx)
704 for k,v in bt_summary.iteritems():
705 print "\t[{:d}] from: {:s}".format(v, GetSourceInformationForAddress(unsigned(k)))
706# EndMacro: showallpreposts
707
708
709@lldb_type_summary(['wq_prepost', 'wq_prepost *'])
710@header("{:<18s} {:<18s} {:<19s} {:<10s} {:<1s} {:<4s} {:<10s} {:<20s}".format('addr','id','idx','gen','V','type','refcnt','info'))
711def GetWaitqPrepostSummary(wqp):
712 if not wqp:
713 return
714 fmt_str = "{w: <#18x} {w.wqte.wqt_id.id: <#18x} {w.wqte.wqt_id.idx: <7d} (->{w.wqte.wqt_next_idx: <7d}) {w.wqte.wqt_id.generation: <#10x} {v: <1s} {t: <4s} {rcnt: <10d} "
715 type = WaitqTableElemType(wqp)
716 if type == "ELEM":
717 type = "WQ"
718 elif type == "LINK":
719 type = "POST"
720 v = "F"
721 if WaitqTableElemValid(wqp):
722 v = "T"
723 refcnt = WaitqTableElemRefcnt(wqp)
724 out_str = fmt_str.format(w=wqp, v=v, t=type, rcnt=refcnt)
725 if type == "WQ":
726 out_str += "wq:{0: <#18x}".format(unsigned(wqp.wqp_wq.wqp_wq_ptr))
727 elif type == "POST":
728 out_str += "next:{0: <#18x}, wqid:{1: <#18x}".format(wqp.wqp_post.wqp_next_id, wqp.wqp_post.wqp_wq_id)
729 post_wqp = GetWaitqPrepost(wqp.wqp_post.wqp_wq_id)[0]
730 if not WaitqTableElemValid(post_wqp):
731 out_str += "(<invalid>)"
732 else:
733 if WaitqTableElemType(post_wqp) != "ELEM":
734 out_str += "(!WQP_WQ?)"
735 else:
736 out_str += "({0: <#18x})".format(unsigned(post_wqp.wqp_wq.wqp_wq_ptr))
737 return out_str
738
739
740# Macro: showprepost
741@lldb_command('showprepost', "P:")
742def ShowPrepost(cmd_args=None, cmd_options={}):
743 """ Print prepost structure summary
744
745 Note: you can pass either a complete ID (generation + index), or
746 just the index to the -P argument.
747
748 usage: showprepost [-P ID] [0xaddr]
749 -P {ID} : show prepost structure whose ID is {ID}
750 """
751 wqp = 0
752 if "-P" in cmd_options:
753 wqp, warn_str = GetWaitqPrepost(unsigned(kern.GetValueFromAddress(cmd_options["-P"], 'uint64_t *')))
754 if wqp == 0:
755 if warn_str != '':
756 raise LookupError(warn_str)
757 else:
758 raise ArgumentError("Invalid prepost ID {:s}".format(cmd_options["-P"]))
759 if wqp == 0:
760 if not cmd_args:
761 raise ArgumentError("Please pass the address of a prepost object")
762 wqp = kern.GetValueFromAddress(cmd_args[0], 'wq_prepost *')
763 if not wqp:
764 raise ArgumentError("Invalid prepost {:s}".format(cmd_args[0]))
765
766 print GetWaitqPrepostSummary.header
767 print GetWaitqPrepostSummary(wqp)
768# EndMacro: showprepost
769
770
771def WaitqPrepostFromObj(wqp, head_id, inv_ok, prepost_str, pp_arr = 0, depth = 0):
772 if pp_arr != 0:
773 pp_arr.append(wqp)
774 etype = WaitqTableElemType(wqp)
775 if not WaitqTableElemValid(wqp) and not inv_ok:
776 id = 0
777 if wqp:
778 id = wqp.wqte.wqt_id.id
779 prepost_str.append("{0: <#18x}:{1: <18s}".format(id, "<invalid>"))
780 return
781 if etype == "ELEM": # WQP_WQ
782 prepost_str.append("{0: <#18x}:{1: <#18x}".format(wqp.wqte.wqt_id.id, unsigned(wqp.wqp_wq.wqp_wq_ptr)))
783 return
784
785 post_wq = 0
786
787 if etype == "LINK": # WQP_POST
788 next_id = wqp.wqp_post.wqp_next_id
789 post_wq = GetWaitqPrepost(wqp.wqp_post.wqp_wq_id)[0]
790 if WaitqTableElemValid(post_wq):
791 if WaitqTableElemType(post_wq) != "ELEM":
792 prepost_str.append("{0: <#18x}:{1: <18s}".format(post_wq.wqte.wqt_id.id, "<invalid post>"))
793 else:
794 prepost_str.append("{0: <#18x}:{1: <#18x}".format(wqp.wqte.wqt_id.id, unsigned(post_wq.wqp_wq.wqp_wq_ptr)))
795 if next_id > 0 and next_id != head_id:
796 if depth >= 950:
797 prepost_str.append("{: <37s}".format("!recursion limit!"))
798 return
799 WaitqPrepostFromObj(GetWaitqPrepost(next_id)[0], head_id, inv_ok, prepost_str, pp_arr, depth + 1)
800 else: # "RSVD" or "FREE":
801 prepost_str.append("{0: <#18x} -> {1: <15d}".format(wqp.wqte.wqt_id.id, wqp.wqte.wqt_next_idx))
802 next_id = wqp.wqte.wqt_next_idx
803 max_elem = int(kern.globals.g_prepost_table.nelem)
804 if hasattr(kern.globals, 'g_wqt_idx_max'):
805 max_elem = unsigned(kern.globals.g_wqt_idx_max)
806 if next_id < max_elem:
807 if depth >= 950:
808 prepost_str.append("{: <37s}".format("!recursion limit!"))
809 return
810 WaitqPrepostFromObj(GetWaitqPrepost(next_id)[0], head_id, inv_ok, prepost_str, pp_arr, depth + 1)
811 return
812
813def GetPrepostChain(head_id, inv_ok = False, pp_arr = 0):
814 pp = []
815 if unsigned(head_id) == 0:
816 return [ "{0: <#18x}:{1: <18s}".format(head_id, "<invalid>") ]
817 wqp = GetWaitqPrepost(head_id)[0]
818 if wqp != 0:
819 WaitqPrepostFromObj(wqp, head_id, inv_ok, pp, pp_arr)
820 else:
821 return [ "{0: <#18x}:{1: <18s}".format(head_id, "<invalid>") ]
822 return pp
823
824def GetWaitqPreposts(waitq):
825 if GetWaitqStateStr(waitq) != "SET":
826 return []
827 wqset = Cast(waitq, 'waitq_set *')
828 if wqset.wqset_prepost_id == 0:
829 return []
830 return GetPrepostChain(wqset.wqset_prepost_id)
831
832
833# Macro: showprepostchain
834@lldb_command('showprepostchain', "P:")
835def ShowPrepostChain(cmd_args=None, cmd_options={}):
836 """ Follow a chain of preposts, printing each one.
837 Note that prepost chains are circular, so this will print
838 the entire chain given a single element.
839
840 Note: you can pass either a complete ID (generation + index), or
841 just the index to the -P argument.
842
843 usage: showprepostchain [-P ID] [0xaddr]
844 -P {ID} : start printing with the prepost whose ID is {ID}
845 """
846 wqp = 0
847 if "-P" in cmd_options:
848 wqp, warn_str = GetWaitqPrepost(unsigned(kern.GetValueFromAddress(cmd_options["-P"], 'uint64_t *')))
849 if wqp == 0:
850 if warn_str != '':
851 raise LookupError(warn_str)
852 else:
853 raise ArgumentError("Invalid prepost ID {:s}".format(cmd_options["-P"]))
854 if wqp == 0:
855 if not cmd_args:
856 raise ArgumentError("Please pass the address of a prepost object")
857 wqp = kern.GetValueFromAddress(cmd_args[0], 'wq_prepost *')
858 if not wqp:
859 raise ArgumentError("Invalid prepost {:s}".format(cmd_args[0]))
860
861 pp_arr = []
862 GetPrepostChain(wqp.wqte.wqt_id.id, True, pp_arr)
863 pp_cnt = len(pp_arr)
864 idx = 0
865 nvalid = 0
866 ninvalid = 0
867 print GetWaitqPrepostSummary.header
868 while idx < pp_cnt:
869 print GetWaitqPrepostSummary(pp_arr[idx])
870 if pp_arr[idx] != 0:
871 type = WaitqTableElemType(pp_arr[idx])
872 if type == "LINK":
873 post_wqp = GetWaitqPrepost(pp_arr[idx].wqp_post.wqp_wq_id)[0]
874 if not WaitqTableElemValid(post_wqp):
875 ninvalid += 1
876 else:
877 nvalid += 1
878 else:
879 nvalid += 1
880 idx += 1
881 print "%s" % '-'*86
882 print "Total: {:d} ({:d} valid, {:d} invalid)".format(len(pp_arr), nvalid, ninvalid)
883# EndMacro: showprepostchain
884
885
886@lldb_type_summary(['waitq', 'waitq *'])
887@header("{: <16s} {: <3s} {: <4s} {: <17s} {: <18s} {: <18s} {: <37s} {: <22s} {: <10s}".format('waitq', 'typ', 'bits', 'evtmask', 'setid', 'wq_wqp', 'preposts', 'member_of', 'threads'))
888def GetWaitqSummary(waitq):
889 fmt_str = "{q: <16x} {state: <3s} {bits: <4s} {q.waitq_eventmask: <#17x} {setid: <#18x} {q.waitq_prepost_id: <#18x}"
890 th_str = []
891 if waitq.waitq_queue.next and waitq.waitq_queue.prev:
892 for thread in IterateLinkageChain(addressof(waitq.waitq_queue), 'thread *', 'links'):
893 th_str.append("{: <18s} e:{: <#18x}".format(hex(thread), thread.wait_event))
894 else:
895 th_str.append("{: <39s}".format('<invalid (NULL) queue>'))
896 th_cnt = len(th_str)
897 set_str = GetWaitqSets(waitq)
898 set_cnt = len(set_str)
899 pp_str = GetWaitqPreposts(waitq)
900 pp_cnt = len(pp_str)
901 last_str = ''
902 idx = 0;
903 while idx < pp_cnt or idx < set_cnt or idx < th_cnt:
904 p = ""
905 s = ""
906 t = ""
907 if idx < pp_cnt:
908 p = pp_str[idx]
909 if idx < set_cnt:
910 s = set_str[idx]
911 if idx < th_cnt:
912 t = th_str[idx]
913 if idx == 0:
914 last_str += "{0: <37s} {1: <22s} {2: <39s}".format(p, s, t)
915 else:
916 last_str += "\n{0: <80s} {1: <37s} {2: <22s} {3: <39s}".format('', p, s, t)
917 idx += 1
918 if pp_cnt > 0 or set_cnt > 0 or th_cnt > 0:
919 last_str += "\n{:<80s} {: <37s} {: <22s} {: <39s}".format('', '-'*37, '-'*20, '-'*39)
920 last_str += "\n{0: <80s} {1: <37d} {2: <22d} {3: <39d}".format('', pp_cnt, set_cnt, th_cnt)
921
922 state = GetWaitqStateStr(waitq)
923 setid = 0
924 if state == "SET":
925 setid = Cast(waitq, 'waitq_set *').wqset_id
926 out_str = fmt_str.format(q=waitq, state=state, bits=GetWaitqBitsStr(waitq), setid=setid)
927 out_str += last_str
928 return out_str
929
930# Macro: showwaitq
931@lldb_command('showwaitq', "P:S:")
932def ShowWaitq(cmd_args=None, cmd_options={}):
933 """ Print waitq structure summary.
934 Lookup the waitq either by address, by Set ID, or indirectly
935 through a prepost object that points to the waitq.
936
937 Note: you can pass either a complete ID (generation + index), or
938 just the index to the -P and -S arguments.
939
940 usage: showwaitq [-P PrePostID] [-S SetID] [0xaddr]
941 -P {ID} : prepost ID that points to a waitq
942 -S {ID} : waitq_set ID
943 """
944 waitq = 0
945 if "-P" in cmd_options:
946 wqp, warn_str = GetWaitqPrepost(unsigned(kern.GetValueFromAddress(cmd_options["-P"], 'uint64_t *')))
947 if wqp == 0:
948 if warn_str:
949 raise LookupError(warn_str)
950 else:
951 raise ArgumentError("Invalid prepost ID {:s}".format(cmd_options["-P"]))
952 if WaitqTableElemType(wqp) != "ELEM":
953 raise ArgumentError("Prepost ID {:s} points to a WQP_POST object, not a WQP_WQ!".format(cmd_options["-P"]))
954 waitq = wqp.wqp_wq.wqp_wq_ptr
955 if "-S" in cmd_options:
956 if waitq:
957 raise ArgumentError("Please pass only one of '-S' or '-P'!")
958 link, warn_str = GetWaitqLink(unsigned(kern.GetValueFromAddress(cmd_options["-S"],'uint64_t *')))
959 if not link:
960 if warn_str != '':
961 raise LookupError(warn_str)
962 else:
963 raise ArgumentError("Invalid link ID {:s}".format(cmd_options["-S"]))
964 if WaitqTableElemType(link) != "ELEM":
965 raise ArgumentError("Link ID {:s} points to a SLT_LINK object, not an SLT_WQS!".format(cmd_options["-S"]))
966 waitq = addressof(link.sl_wqs.sl_set.wqset_q)
967
968 if not waitq and not cmd_args:
969 raise ArgumentError("Please pass the address of a waitq!")
970 if not waitq:
971 waitq = kern.GetValueFromAddress(cmd_args[0], 'waitq *')
972 if not waitq:
973 raise ("Unknown arguments: %r %r" % (cmd_args, cmd_options))
974 print GetWaitqSummary.header
975 print GetWaitqSummary(waitq)
976# EndMacro: showwaitq
977
978
979# Macro: showglobalwaitqs
980@lldb_command('showglobalwaitqs')
981def ShowGlobalWaitqs(cmd_args=None):
982 """ Summarize global waitq usage
983 """
984 global kern
985 q = 0
986
987 print "Global waitq objects"
988 print GetWaitqSummary.header
989
990 while q < kern.globals.g_num_waitqs:
991 print GetWaitqSummary(addressof(kern.globals.global_waitqs[q]))
992 q = q + 1
993# EndMacro: showglobalwaitqs
994
995
996# Macro: showglobalqstats
997@lldb_command('showglobalqstats', "OF")
998def ShowGlobalQStats(cmd_args=None, cmd_options={}):
999 """ Summarize global waitq statistics
1000
1001 usage: showglobalqstats [-O] [-F]
1002 -O : only output waitqs with outstanding waits
1003 -F : output as much backtrace as was recorded
1004 """
1005 global kern
1006 q = 0
1007
1008 if not hasattr(kern.globals, 'g_waitq_stats'):
1009 print "No waitq stats support (use DEVELOPMENT kernel)!"
1010 return
1011
1012 print "Global waitq stats"
1013 print "{0: <18s} {1: <8s} {2: <8s} {3: <8s} {4: <8s} {5: <8s} {6: <32s}".format('waitq', '#waits', '#wakes', '#diff', '#fails', '#clears', 'backtraces')
1014
1015 waiters_only = False
1016 full_bt = False
1017 if "-O" in cmd_options:
1018 waiters_only = True
1019 if "-F" in cmd_options:
1020 full_bt = True
1021
1022 fmt_str = "{q: <#18x} {stats.waits: <8d} {stats.wakeups: <8d} {diff: <8d} {stats.failed_wakeups: <8d} {stats.clears: <8d} {bt_str: <s}"
1023 while q < kern.globals.g_num_waitqs:
1024 waitq = kern.globals.global_waitqs[q]
1025 stats = kern.globals.g_waitq_stats[q]
1026 diff = stats.waits - stats.wakeups
1027 if diff == 0 and waiters_only:
1028 q = q + 1
1029 continue
1030 last_waitstr = ''
1031 last_wakestr = ''
1032 fw_str = ''
1033 if (stats.last_wait[0]):
1034 last_waitstr = GetSourceInformationForAddress(unsigned(stats.last_wait[0]))
1035 if (stats.last_wakeup[0]):
1036 last_wakestr = GetSourceInformationForAddress(unsigned(stats.last_wakeup[0]))
1037 if (stats.last_failed_wakeup[0]):
1038 fw_str = GetSourceInformationForAddress(unsigned(stats.last_failed_wakeup[0]))
1039
1040 if full_bt:
1041 f = 1
1042 while f < kern.globals.g_nwaitq_btframes:
1043 if stats.last_wait[f]:
1044 last_waitstr = "{0}->{1}".format(GetSourceInformationForAddress(unsigned(stats.last_wait[f])), last_waitstr)
1045 if stats.last_wakeup[f]:
1046 last_wakestr = "{0}->{1}".format(GetSourceInformationForAddress(unsigned(stats.last_wakeup[f])), last_wakestr)
1047 if stats.last_failed_wakeup[f]:
1048 fw_str = "{0}->{1}".format(GetSourceInformationForAddress(unsigned(stats.last_failed_wakeup[f])), fw_str)
1049 f = f + 1
1050 bt_str = ''
1051 if last_waitstr:
1052 bt_str += "wait : " + last_waitstr
1053 if last_wakestr:
1054 if bt_str:
1055 bt_str += "\n{0: <70s} ".format('')
1056 bt_str += "wake : " + last_wakestr
1057 if fw_str:
1058 if bt_str:
1059 bt_str += "\n{0: <70s} ".format('')
1060 bt_str += "fails: " + fw_str
1061
1062 print fmt_str.format(q=addressof(waitq), stats=stats, diff=diff, bt_str=bt_str)
1063 q = q + 1
1064# EndMacro: showglobalqstats