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