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