1from xnu import * 2from process import GetBSDThread, GetMachThread 3from scheduler import GetRecentTimestamp 4import xnudefines 5 6def GetProcWorkqueue(proc): 7 wq = proc.p_wqptr; 8 if unsigned(wq): 9 return Cast(wq, "struct workqueue *"); 10 return None 11 12@header("{:<20s} {:<20s} {:<20s} {:<10s} {:<10s} {:<10s} {:<10s} {:<10s} {:<10s} {:<30s}".format( 13 'task', 'proc', 'wq', 'sched', 'pending', 'idle', 'dying', 'creations', 'fulfilled', 'wq_flags')) 14def GetWorkqueueSummary(proc, wq): 15 wq_flags = [] 16 if wq.wq_flags & GetEnumValue("workq_state_flags_t::WQ_EXITING"): 17 wq_flags.append("EXITING") 18 if wq.wq_flags & GetEnumValue("workq_state_flags_t::WQ_PROC_SUSPENDED"): 19 wq_flags.append("PROC_SUSPENDED") 20 if wq.wq_flags & GetEnumValue("workq_state_flags_t::WQ_DEATH_CALL_SCHEDULED"): 21 wq_flags.append("DEATH_CALL") 22 23 scheduled = GetEnumValue("workq_state_flags_t::WQ_DELAYED_CALL_SCHEDULED") 24 pended = GetEnumValue("workq_state_flags_t::WQ_DELAYED_CALL_PENDED") 25 if wq.wq_flags & (scheduled | pended): 26 s = "DELAYED_CALL[" 27 if wq.wq_flags & scheduled: s += 'S' 28 if wq.wq_flags & pended: s += 'P' 29 s += ']' 30 wq_flags.append(s) 31 32 scheduled = GetEnumValue("workq_state_flags_t::WQ_IMMEDIATE_CALL_SCHEDULED") 33 pended = GetEnumValue("workq_state_flags_t::WQ_IMMEDIATE_CALL_PENDED") 34 if wq.wq_flags & (scheduled | pended): 35 s = "IMMEDIATE_CALL[" 36 if wq.wq_flags & scheduled: s += 'S' 37 if wq.wq_flags & pended: s += 'P' 38 s += ']' 39 wq_flags.append(s) 40 task = GetTaskFromProc(proc) 41 42 return "{task: <#020x} {p: <#020x} {wq: <#020x} {wq.wq_threads_scheduled: <10d} {wq.wq_reqcount: <10d} {wq.wq_thidlecount: <10d} {wq.wq_thdying_count: <10d} {wq.wq_creations: <10d} {wq.wq_fulfilled: <10d} {wq_flags: <30s}".format(task=task, p=proc, wq=wq, wq_flags=" ".join(wq_flags)); 43 44@header("{:<20s} {:<20s} {:>10s} {:9s} {:<20s} {:<10s} {:<30s}".format( 45 'thread', 'uthread', 'thport', 'kind', 'kqueue', 'idle (ms)', 'uu_workq_flags')) 46def GetWQThreadSummary(th, uth): 47 p = th.t_tro.tro_proc 48 wq = p.p_wqptr 49 50 uu_workq_flags = [] 51 if uth.uu_workq_flags & 0x01: uu_workq_flags.append("NEW") 52 if uth.uu_workq_flags & 0x02: 53 uu_workq_flags.append("RUNNING") 54 if wq.wq_creator == uth: 55 kind = "creator" 56 else: 57 kind = "workq" 58 idle = "" 59 else: 60 ts = kern.GetNanotimeFromAbstime(GetRecentTimestamp() - uth.uu_save.uus_workq_park_data.idle_stamp) / 1e9 61 kind = "idle" 62 idle = "%#.03f" % (ts) 63 if uth.uu_workq_flags & 0x04: uu_workq_flags.append("DYING") 64 if uth.uu_workq_flags & 0x08: uu_workq_flags.append("OVERCOMMIT") 65 if uth.uu_workq_flags & 0x100: uu_workq_flags.append("COOPERATIVE") 66 if uth.uu_workq_flags & 0x200: uu_workq_flags.append("PERMANENT_BIND") 67 if uth.uu_workq_flags & 0x400: uu_workq_flags.append("WORK_INTERVAL_JOINED") 68 if uth.uu_workq_flags & 0x800: uu_workq_flags.append("WORK_INTERVAL_FAILED") 69 if uth.uu_workq_flags & 0x10: uu_workq_flags.append("OUTSIDE_QOS") 70 if uth.uu_workq_flags & 0x20: uu_workq_flags.append("IDLE_CLEANUP") 71 if uth.uu_workq_flags & 0x40: uu_workq_flags.append("EARLY_BOUND") 72 if uth.uu_workq_flags & 0x80: uu_workq_flags.append("CPU%") 73 74 kqr = uth.uu_kqr_bound 75 if not kqr: 76 kq = 0 77 elif kqr.tr_flags & 0x1: # kevent 78 kq = p.p_fd.fd_wqkqueue 79 kind = "kqwq[%s]" % (xnudefines.thread_qos_short_strings[int(kqr.tr_kq_qos_index)]) 80 elif kqr.tr_flags & 0x2: # workloop 81 kq = ContainerOf(kqr, 'struct kqworkloop', 'kqwl_request') 82 kind = "workloop" 83 else: 84 kq = 0 85 kind = "???" 86 87 return "{th: <#020x} {uth: <#020x} {thport: >#010x} {kind: <9s} {kq: <#020x} {idle: <10s} {uu_workq_flags: <30s}".format(th=th, uth=uth, thport=uth.uu_workq_thport, kind=kind, kq=kq, idle=idle, uu_workq_flags=" ".join(uu_workq_flags)) 88 89@header("{:<20s} {:<20s} {:<20s} {:<10s} {:<4s} {:<6s} {:<6s} {:<6s} {:<30s}".format( 90 'request', 'kqueue', 'thread', 'state', '#', 'qos', 'kq_qos', 'kq_ovr', 'tr_flags')) 91def GetWorkqueueThreadRequestSummary(proc, req): # req is the actual structure, not pointer 92 kq = 0 93 tr_flags = [] 94 req_addr = addressof(req) 95 96 if req.tr_flags & 0x01: 97 tr_flags.append("KEVENT") 98 kq = proc.p_fd.fd_wqkqueue 99 if req.tr_flags & 0x02: 100 tr_flags.append("WORKLOOP") 101 kq = ContainerOf(req_addr, 'struct kqworkloop', 'kqwl_request') 102 if req.tr_flags & 0x04: tr_flags.append("OVERCOMMIT") 103 if req.tr_flags & 0x08: tr_flags.append("PARAMS") 104 if req.tr_flags & 0x10: tr_flags.append("OUTSIDE_QOS") 105 if req.tr_flags & 0x20: tr_flags.append("COOPERATIVE") 106 if req.tr_flags & 0x40: tr_flags.append("PERMANENT_BIND") 107 108 state = {0: "IDLE", 1: "NEW", 2: "QUEUED", 3: "CANCELED", 4: "BINDING", 5: "BOUND" }[int(req.tr_state)] 109 110 thread = 0 111 if int(req.tr_state) in [4, 5]: # BINDING or BOUND 112 thread = req.tr_thread 113 114 qos = int(req.tr_qos) 115 if qos == 8: 116 qos = "MG" 117 elif qos == 7: 118 qos = "SP" 119 else: 120 qos = xnudefines.thread_qos_short_strings[qos] 121 122 kq_qos = xnudefines.thread_qos_short_strings[int(req.tr_kq_qos_index)] 123 kq_ovr = xnudefines.thread_qos_short_strings[int(req.tr_kq_override_index)] 124 125 return "{req_addr: <#020x} {kq: <#020x} {thread: <#020x} {state: <10s} {tr_count: <4d} {qos: <6s} {kq_qos: <6s} {kq_ovr: <6s} {tr_flags: <30s}".format( 126 req_addr=unsigned(req_addr), kq=kq, thread=thread, state=state, qos=qos, kq_qos=kq_qos, kq_ovr=kq_ovr, tr_count=req.tr_count, tr_flags=" ".join(tr_flags)) 127 128@lldb_command('showwqthread', fancy=True) 129def ShowWQThread(cmd_args=None, cmd_options={}, O=None): 130 """ Shows info about a workqueue thread 131 132 usage: showworkqthread <thread_t> 133 """ 134 135 if not cmd_args: 136 return O.error('missing struct proc * argument') 137 138 th = kern.GetValueFromAddress(cmd_args[0], "struct thread *") 139 if not (th.thread_tag & 0x20): 140 raise ArgumentError('not a workqueue thread') 141 142 with O.table(GetWQThreadSummary.header): 143 print(GetWQThreadSummary(th, GetBSDThread(th))) 144 145 146@lldb_command('showprocworkqueue', fancy=True) 147def ShowProcWorkqueue(cmd_args=None, cmd_options={}, O=None): 148 """ Shows the process workqueue 149 150 usage: showprocworkqueue <proc_t> 151 """ 152 153 if not cmd_args: 154 return O.error('missing struct proc * argument') 155 156 proc = kern.GetValueFromAddress(cmd_args[0], "proc_t") 157 wq = Cast(proc.p_wqptr, "struct workqueue *") 158 if not wq: 159 return O.error("{:#x} doesn't have a workqueue", proc) 160 161 with O.table(GetWorkqueueSummary.header): 162 print(GetWorkqueueSummary(proc, wq)) 163 164 with O.table(GetWorkqueueThreadRequestSummary.header, indent=True): 165 if wq.wq_reqcount: 166 print("") 167 if wq.wq_event_manager_threadreq: 168 print(GetWorkqueueThreadRequestSummary(proc, dereference(wq.wq_event_manager_threadreq))) 169 for req in IterateSchedPriorityQueue(wq.wq_overcommit_queue, 'struct workq_threadreq_s', 'tr_entry'): 170 print(GetWorkqueueThreadRequestSummary(proc, dereference(req))) 171 for req in IterateSchedPriorityQueue(wq.wq_constrained_queue, 'struct workq_threadreq_s', 'tr_entry'): 172 print(GetWorkqueueThreadRequestSummary(proc, dereference(req))) 173 for req in IterateSchedPriorityQueue(wq.wq_special_queue, 'struct workq_threadreq_s', 'tr_entry'): 174 print(GetWorkqueueThreadRequestSummary(proc, dereference(req))) 175 for qos in xnudefines.thread_qos_short_strings: 176 bucket = 0; 177 if qos > 2: #Greater than BG 178 bucket = qos - 2; 179 for req in IterateSTAILQ_HEAD(wq.wq_cooperative_queue[bucket], "tr_link"): 180 print(GetWorkqueueThreadRequestSummary(proc, dereference(req))) 181 182 183 with O.table(GetWQThreadSummary.header, indent=True): 184 print("") 185 for uth in IterateTAILQ_HEAD(wq.wq_thrunlist, "uu_workq_entry"): 186 print(GetWQThreadSummary(GetMachThread(uth), uth)) 187 for uth in IterateTAILQ_HEAD(wq.wq_thidlelist, "uu_workq_entry"): 188 print(GetWQThreadSummary(GetMachThread(uth), uth)) 189 for uth in IterateTAILQ_HEAD(wq.wq_thnewlist, "uu_workq_entry"): 190 print(GetWQThreadSummary(GetMachThread(uth), uth)) 191 192@lldb_command('showallworkqueues', fancy=True) 193def ShowAllWorkqueues(cmd_args=None, cmd_options={}, O=None): 194 """ Display a summary of all the workqueues in the system 195 196 usage: showallworkqueues 197 """ 198 199 with O.table(GetWorkqueueSummary.header): 200 for t in kern.tasks: 201 proc = GetProcFromTask(t) 202 if proc is None: 203 continue 204 wq = Cast(proc.p_wqptr, "struct workqueue *"); 205 if wq: 206 print(GetWorkqueueSummary(proc, wq)) 207