]>
Commit | Line | Data |
---|---|---|
3e170ce0 A |
1 | from xnu import * |
2 | from utils import * | |
3 | from core.configuration import * | |
4 | ||
5 | import sys | |
6 | ||
7 | def 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 | ||
16 | def 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 | ||
31 | def 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 | ||
41 | def WaitqTableElemId(e): | |
42 | return e.wqte.wqt_id.id | |
43 | ||
44 | def WaitqTableElemValid(e): | |
45 | if unsigned(e) == 0: | |
46 | return 0 | |
47 | return (e.wqte.wqt_bits & 0x80000000) == 0x80000000 | |
48 | ||
49 | def WaitqTableElemRefcnt(e): | |
50 | return (e.wqte.wqt_bits & 0x1fffffff) | |
51 | ||
52 | def 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 | ||
60 | def 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 | ||
72 | def 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 | ||
92 | def 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 | ||
113 | def 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 | ||
121 | def 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 | ||
139 | def 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 | ||
149 | def 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')) | |
158 | def 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 | ||
241 | def 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") | |
279 | def 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') | |
352 | def 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:') | |
592 | def 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')) | |
711 | def 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:") | |
742 | def 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 | ||
771 | def 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 | ||
813 | def 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 | ||
824 | def 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:") | |
835 | def 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')) | |
888 | def 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:") | |
932 | def 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') | |
981 | def 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") | |
998 | def 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 |