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