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