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