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