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