1from xnu import * 2from workqueue import GetWorkqueueThreadRequestSummary 3 4import kmemory 5 6def IterateProcKqueues(proc): 7 """ Iterate through all kqueues in the given process 8 9 params: 10 proc - the proc object 11 returns: nothing, this is meant to be used as a generator function 12 kq - yields each kqueue in the process 13 """ 14 for kqf in IterateProcKqfiles(proc): 15 yield cast(kqf, 'struct kqueue *') 16 if int((fd_wqkqueue := proc.p_fd.fd_wqkqueue)) != 0: 17 yield cast(fd_wqkqueue, 'struct kqueue *') 18 for kqwl in IterateProcKqworkloops(proc): 19 yield cast(kqwl, 'struct kqueue *') 20 21def IterateProcKqfiles(proc): 22 """ Iterate through all kqfiles in the given process 23 24 params: 25 proc - the proc object 26 returns: nothing, this is meant to be used as a generator function 27 kqf - yields each kqfile in the process 28 """ 29 filetype_KQUEUE = 5 30 31 proc_filedesc = addressof(proc.p_fd) 32 proc_ofiles = proc_filedesc.fd_ofiles 33 queues = list() 34 35 if unsigned(proc_ofiles) == 0: 36 return 37 38 for fd in range(0, unsigned(proc_filedesc.fd_afterlast)): 39 fd_fileproc = proc_ofiles[fd] 40 if unsigned(fd_fileproc) != 0: 41 # proc_fd_flags = fd_fileproc.fp_flags 42 proc_fd_fglob = fd_fileproc.fp_glob 43 proc_fd_ftype = unsigned(proc_fd_fglob.fg_ops.fo_type) 44 if proc_fd_ftype == xnudefines.DTYPE_KQUEUE: 45 proc_fd_fglob_fg_data = Cast(proc_fd_fglob.fg_data, 'void *') 46 yield Cast(proc_fd_fglob_fg_data, 'struct kqfile *') 47 48def IterateProcKqworkloops(proc): 49 """ Iterate through all kqworkloops in the given process 50 51 params: 52 proc - the proc object 53 returns: nothing, this is meant to be used as a generator function 54 kqwl - yields each kqworkloop in the process 55 """ 56 proc_filedesc = addressof(proc.p_fd) 57 proc_fd_kqhash = proc_filedesc.fd_kqhash 58 if int(proc_fd_kqhash) == 0: 59 return 60 61 hash_mask = proc_filedesc.fd_kqhashmask 62 for i in range(hash_mask + 1): 63 for kqwl in IterateListEntry(proc_fd_kqhash[i], 'kqwl_hashlink'): 64 yield kqwl 65 66def IterateAllKqueues(): 67 """ Iterate through all kqueues in the system 68 69 returns: nothing, this is meant to be used as a generator function 70 kq - yields each kqueue in the system 71 """ 72 for t in kern.tasks: 73 proc = GetProcFromTask(t) 74 if proc is None: 75 continue 76 for kq in IterateProcKqueues(proc): 77 yield kq 78 79def IterateProcKnotes(proc): 80 """ Iterate through all knotes in the given process 81 82 params: 83 proc - the proc object 84 returns: nothing, this is meant to be used as a generator function 85 kn - yields each knote in the process 86 """ 87 proc_filedesc = addressof(proc.p_fd) 88 89 if int(proc.p_fd.fd_knlist) != 0: 90 for i in range(proc.p_fd.fd_knlistsize): 91 for kn in IterateListEntry(proc.p_fd.fd_knlist[i], 'kn_link', list_prefix='s'): 92 yield kn 93 if int(proc.p_fd.fd_knhash) != 0: 94 for i in range(proc.p_fd.fd_knhashmask + 1): 95 for kn in IterateListEntry(proc.p_fd.fd_knhash[i], 'kn_link', list_prefix='s'): 96 yield kn 97 98def GetKnoteKqueue(kn): 99 """ Get the kqueue corresponding to a given knote 100 101 params: 102 kn - the knote object 103 returns: kq - the kqueue corresponding to the knote 104 """ 105 106 kmem = kmemory.KMem.get_shared() 107 addr = kmem.kn_kq_packing.unpack(unsigned(kn.kn_kq_packed)) 108 return kern.CreateTypedPointerFromAddress(addr, 'struct kqueue') 109 110 111@lldb_type_summary(['knote *']) 112@header('{:<20s} {:<20s} {:<10s} {:<20s} {:<20s} {:<30s} {:<10} {:<10} {:<10} {:<20s}'.format('knote', 'ident', 'kev_flags', 'kqueue', 'udata', 'filtops', 'qos_req', 'qos_use', 'qos_ovr', 'status')) 113def GetKnoteSummary(kn): 114 """ Summarizes a knote and related information 115 116 returns: str - summary of knote 117 """ 118 format_string = '{o: <#020x} {o.kn_kevent.kei_ident: <#020x} {o.kn_kevent.kei_flags: <#010x} {kq_ptr: <#020x} {o.kn_kevent.kei_udata: <#020x} {ops_str: <30s} {qos_req: <10s} {qos_use: <10s} {qos_ovr: <10s} {st_str: <20s}' 119 state = unsigned(kn.kn_status) 120 fops_str = kern.Symbolicate(kern.globals.sysfilt_ops[unsigned(kn.kn_kevent.kei_filtid)]) 121 qos_index = int(kn.kn_qos_index) 122 if qos_index > 6: 123 qos_req = qos_index 124 else: 125 qos_req = int((kn.kn_kevent.kei_qos & 0x003fff00) >> 8).bit_length() 126 return format_string.format( 127 o=kn, 128 qos_req=xnudefines.thread_qos_short_strings[qos_req], 129 qos_use=xnudefines.thread_qos_short_strings[qos_index], 130 qos_ovr=xnudefines.thread_qos_short_strings[int(kn.kn_qos_override)], 131 st_str=GetOptionString('kn_status_t', state, 'KN_'), 132 kq_ptr=int(GetKnoteKqueue(kn)), 133 ops_str=fops_str) 134 135@lldb_command('showknote', fancy=True) 136def ShowKnote(cmd_args=None, cmd_options={}, O=None): 137 """ Show information about a knote 138 139 usage: showknote <struct knote *> 140 """ 141 if cmd_args is None or len(cmd_args) == 0: 142 return O.error('missing struct knote * argument') 143 144 kn = kern.GetValueFromAddress(cmd_args[0], 'struct knote *') 145 with O.table(GetKnoteSummary.header): 146 print(GetKnoteSummary(kn)) 147 148def IterateKqueueKnotes(kq): 149 """ Iterate through all knotes of a given kqueue 150 151 params: 152 kq - the kqueue to iterate the knotes of 153 returns: nothing, this is meant to be used as a generator function 154 kn - yields each knote in the kqueue 155 """ 156 proc = kq.kq_p 157 for kn in IterateProcKnotes(proc): 158 if unsigned(GetKnoteKqueue(kn)) != unsigned(addressof(kq)): 159 continue 160 yield kn 161 162kqueue_summary_fmt = '{ptr: <#020x} {o.kq_p: <#020x} {dyn_id: <#020x} {servicer: <#20x} {owner: <#20x} {o.kq_count: <6d} {st_str: <10s}' 163 164def GetServicer(req): 165 if req.tr_state in [4, 5]: # [ BINDING , BOUND ] 166 return int(req.tr_thread) 167 return 0 168 169@lldb_type_summary(['struct kqueue *']) 170@header('{: <20s} {: <20s} {: <20s} {: <20s} {: <20s} {: <6s} {: <10s}'.format('kqueue', 'process', 'dynamic_id', 'servicer', 'owner', '#evts', 'state')) 171def GetKqueueSummary(kq): 172 """ Summarize kqueue information 173 174 params: 175 kq - the kqueue object 176 returns: str - summary of kqueue 177 """ 178 if int(kq.kq_state) & GetEnumValue('kq_state_t', 'KQ_WORKQ'): 179 return GetKqworkqSummary(cast(kq, 'struct kqworkq *')) 180 elif int(kq.kq_state) & GetEnumValue('kq_state_t', 'KQ_WORKLOOP'): 181 return GetKqworkloopSummary(cast(kq, 'struct kqworkloop *')) 182 else: 183 return GetKqfileSummary(cast(kq, 'struct kqfile *')) 184 185@lldb_type_summary(['struct kqfile *']) 186@header(GetKqueueSummary.header) 187def GetKqfileSummary(kqf): 188 kq = cast(kqf, 'struct kqueue *') 189 state = int(kq.kq_state) 190 return kqueue_summary_fmt.format( 191 o=kq, 192 ptr=int(kq), 193 dyn_id=0, 194 st_str=GetOptionString('kq_state_t', state, 'KQ_'), 195 servicer=0, 196 owner=0) 197 198@lldb_command('showkqfile', fancy=True) 199def ShowKqfile(cmd_args=None, cmd_options={}, O=None): 200 """ Display information about a kqfile object. 201 202 usage: showkqfile <struct kqfile *> 203 """ 204 if len(cmd_args) < 1: 205 return O.error('missing struct kqfile * argument') 206 207 kqf = kern.GetValueFromAddress(cmd_args[0], 'kqfile *') 208 209 with O.table(GetKqfileSummary.header): 210 print(GetKqfileSummary(kqf)) 211 with O.table(GetKnoteSummary.header): 212 for kn in IterateKqueueKnotes(kqf.kqf_kqueue): 213 print(GetKnoteSummary(kn)) 214 for kn in IterateTAILQ_HEAD(kqf.kqf_suppressed, 'kn_tqe'): 215 print(GetKnoteSummary(kn)) 216 217@lldb_type_summary(['struct kqworkq *']) 218@header(GetKqueueSummary.header) 219def GetKqworkqSummary(kqwq): 220 """ Summarize workqueue kqueue information 221 222 params: 223 kqwq - the kqworkq object (type 'struct kqworkq') 224 returns: str - summary of workqueue kqueue 225 """ 226 return GetKqfileSummary(kqwq) 227 228@lldb_command('showkqworkq', fancy=True) 229def ShowKqworkq(cmd_args=None, cmd_options={}, O=None): 230 """ Display summary and knote information about a kqworkq. 231 232 usage: showkqworkq <struct kqworkq *> 233 """ 234 if len(cmd_args) < 1: 235 return O.error('missing struct kqworkq * argument') 236 237 kqwq = kern.GetValueFromAddress(cmd_args[0], 'struct kqworkq *') 238 kq = kqwq.kqwq_kqueue 239 with O.table(GetKqueueSummary.header): 240 print(GetKqworkqSummary(kqwq)) 241 242 with O.table(GetWorkqueueThreadRequestSummary.header): 243 for i in range(0, 7): 244 print(GetWorkqueueThreadRequestSummary(kq.kq_p, kqwq.kqwq_request[i])) 245 246 with O.table(GetKnoteSummary.header): 247 for kn in IterateKqueueKnotes(kq): 248 print(GetKnoteSummary(kn)) 249 250@lldb_type_summary(['struct kqworkloop *']) 251@header(GetKqueueSummary.header) 252def GetKqworkloopSummary(kqwl): 253 """ Summarize workloop kqueue information 254 255 params: 256 kqwl - the kqworkloop object 257 returns: str - summary of workloop kqueue 258 """ 259 kqwl_kqueue = kqwl.kqwl_kqueue 260 state = int(kqwl_kqueue.kq_state) 261 return kqueue_summary_fmt.format( 262 ptr=int(kqwl), 263 o=kqwl_kqueue, 264 dyn_id=kqwl.kqwl_dynamicid, 265 st_str=GetOptionString('kq_state_t', state, 'KQ_'), 266 servicer=GetServicer(kqwl.kqwl_request), 267 owner=int(kqwl.kqwl_owner) 268 ) 269 270@lldb_command('showkqworkloop', fancy=True) 271def ShowKqworkloop(cmd_args=None, cmd_options={}, O=None): 272 """ Display information about a kqworkloop. 273 274 usage: showkqworkloop <struct kqworkloop *> 275 """ 276 if len(cmd_args) < 1: 277 return O.error('missing struct kqworkloop * argument') 278 279 kqwl = kern.GetValueFromAddress(cmd_args[0], 'struct kqworkloop *') 280 281 with O.table(GetKqworkloopSummary.header): 282 print(GetKqworkloopSummary(kqwl)) 283 284 with O.table(GetWorkqueueThreadRequestSummary.header): 285 print(GetWorkqueueThreadRequestSummary(kqwl.kqwl_kqueue.kq_p, kqwl.kqwl_request)) 286 287 with O.table(GetKnoteSummary.header): 288 for kn in IterateKqueueKnotes(kqwl.kqwl_kqueue): 289 print(GetKnoteSummary(kn)) 290 291@lldb_command('showkqueue', fancy=True) 292def ShowKqueue(cmd_args=None, cmd_options={}, O=None): 293 """ Given a struct kqueue pointer, display the summary of the kqueue 294 295 usage: showkqueue <struct kqueue *> 296 """ 297 if cmd_args is None or len(cmd_args) == 0: 298 return O.error('missing struct kqueue * argument') 299 300 kq = kern.GetValueFromAddress(cmd_args[0], 'struct kqueue *') 301 if int(kq.kq_state) & GetEnumValue('kq_state_t', 'KQ_WORKQ'): 302 ShowKqworkq(cmd_args, cmd_options, O) 303 elif int(kq.kq_state) & GetEnumValue('kq_state_t', 'KQ_WORKLOOP'): 304 ShowKqworkloop(cmd_args, cmd_options, O) 305 else: 306 ShowKqfile(cmd_args, cmd_options, O) 307 308@lldb_command('showprocworkqkqueue', fancy=True) 309def ShowProcWorkqKqueue(cmd_args=None, cmd_options={}, O=None): 310 """ Show the workqueue kqueue for a given process. 311 312 usage: showprocworkqkqueue <proc_t> 313 """ 314 if cmd_args is None or len(cmd_args) == 0: 315 return O.error('missing struct proc * argument') 316 317 proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t') 318 ShowKqworkq(cmd_args=[str(int(proc.p_fd.fd_wqkqueue))]) 319 320@lldb_command('showprockqueues', fancy=True) 321def ShowProcKqueues(cmd_args=None, cmd_options={}, O=None): 322 """ Show the kqueues for a given process. 323 324 usage: showprockqueues <proc_t> 325 """ 326 if cmd_args is None or len(cmd_args) == 0: 327 return O.error('missing struct proc * argument') 328 329 proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t') 330 331 with O.table(GetKqueueSummary.header): 332 for kq in IterateProcKqueues(proc): 333 print(GetKqueueSummary(kq)) 334 335@lldb_command('showprocknotes', fancy=True) 336def ShowProcKnotes(cmd_args=None, cmd_options={}, O=None): 337 """ Show the knotes for a given process. 338 339 usage: showprocknotes <proc_t> 340 """ 341 342 if cmd_args is None or len(cmd_args) == 0: 343 return O.error('missing struct proc * argument') 344 345 proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t') 346 347 with O.table(GetKnoteSummary.header): 348 for kn in IterateProcKnotes(proc): 349 print(GetKnoteSummary(kn)) 350 351@lldb_command('showallkqueues', fancy=True) 352def ShowAllKqueues(cmd_args=None, cmd_options={}, O=None): 353 """ Display a summary of all the kqueues in the system 354 355 usage: showallkqueues 356 """ 357 with O.table(GetKqueueSummary.header): 358 for kq in IterateAllKqueues(): 359 print(GetKqueueSummary(kq)) 360 361@lldb_command('showkqueuecounts', fancy=True) 362def ShowKqCounts(cmd_args=None, cmd_options={}, O=None): 363 """ Display a count of all the kqueues in the system - lskq summary 364 365 usage: showkqueuecounts 366 """ 367 print ('{: <20s} {: <35s} {: <10s} {: <6s}'.format('process', 'proc_name', '#kqfiles', '#kqworkloop')) 368 for t in kern.tasks: 369 proc = GetProcFromTask(t) 370 if proc is None: 371 continue 372 proc = kern.GetValueFromAddress(unsigned(proc), 'proc_t') 373 kqfcount = 0 374 kqwlcount = 0 375 for kqf in IterateProcKqfiles(proc): 376 kqfcount += 1 377 for kqwl in IterateProcKqworkloops(proc): 378 kqwlcount += 1 379 print("{proc: <#20x} {name: <35s} {kqfile: <10d} {kqwl: <6d}".format(proc=proc, 380 name=GetProcName(proc), kqfile=kqfcount, kqwl=kqwlcount)) 381