]>
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 | |
71 | elif kqr.kqr_state & 0x1: # workloop | |
72 | kq = ContainerOf(kqr, 'struct kqworkloop', 'kqwl_request') | |
73 | kind = "workloop" | |
74 | else: | |
75 | kq = p.p_fd.fd_wqkqueue | |
76 | kind = "kqwq[%s]" % (xnudefines.thread_qos_short_strings[int(kqr.kqr_qos_index)]) | |
77 | ||
78 | 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)) | |
79 | ||
80 | @header("{:<20s} {:<20s} {:<10s} {:<3s} {:<4s} {:<30s}".format( | |
81 | 'request', 'kqueue', 'state', '#', 'qos', 'tr_flags')) | |
82 | def GetWorkqueueThreadRequestSummary(proc, req): | |
83 | kq = 0 | |
84 | tr_flags = [] | |
85 | ||
86 | if req.tr_flags & 0x01: | |
87 | tr_flags.append("KEVENT") | |
88 | kq = proc.p_fd.fd_wqkqueue | |
89 | if req.tr_flags & 0x02: | |
90 | tr_flags.append("WORKLOOP") | |
91 | kq = ContainerOf(req, 'struct kqworkloop', 'kqwl_request.kqr_req') | |
92 | if req.tr_flags & 0x04: tr_flags.append("OVERCOMMIT") | |
93 | if req.tr_flags & 0x08: tr_flags.append("PARAMS") | |
94 | if req.tr_flags & 0x10: tr_flags.append("OUTSIDE_QOS") | |
95 | ||
96 | state = {0: "IDLE", 1: "NEW", 2: "QUEUED", 4: "BINDING" }[int(req.tr_state)] | |
97 | ||
98 | qos = int(req.tr_qos) | |
99 | if qos == 8: | |
100 | qos = "MG" | |
101 | elif qos == 7: | |
102 | qos = "SP" | |
103 | else: | |
104 | qos = xnudefines.thread_qos_short_strings[qos] | |
105 | ||
106 | return "{req: <#020x} {kq: <#020x} {state: <10s} {req.tr_count: <3d} {qos: <4s} {tr_flags: <30s}".format(req=req, kq=kq, state=state, qos=qos, tr_flags=" ".join(tr_flags)) | |
107 | ||
108 | @lldb_command('showwqthread') | |
109 | def ShowWQThread(cmd_args=None): | |
110 | """ Shows info about a workqueue thread | |
111 | ||
112 | usage: showworkqthread <thread_t> | |
113 | """ | |
114 | ||
115 | if not cmd_args: | |
116 | raise ArgumentError('missing struct proc * argument') | |
117 | ||
118 | th = kern.GetValueFromAddress(cmd_args[0], "struct thread *") | |
119 | if not (th.thread_tag & 0x20): | |
120 | raise ArgumentError('not a workqueue thread') | |
121 | ||
122 | print GetWQThreadSummary.header | |
123 | print GetWQThreadSummary(th, Cast(th.uthread, 'struct uthread *')) | |
124 | ||
125 | ||
126 | @lldb_command('showprocworkqueue') | |
127 | def ShowProcWorkqueue(cmd_args=None): | |
128 | """ Shows the process workqueue | |
129 | ||
130 | usage: showprocworkqueue <proc_t> | |
131 | """ | |
132 | ||
133 | if not cmd_args: | |
134 | raise ArgumentError('missing struct proc * argument') | |
135 | ||
136 | proc = kern.GetValueFromAddress(cmd_args[0], "proc_t") | |
137 | wq = Cast(proc.p_wqptr, "struct workqueue *"); | |
138 | if wq: | |
139 | print GetWorkqueueSummary.header | |
140 | print GetWorkqueueSummary(proc, wq) | |
141 | ||
142 | if wq.wq_reqcount: | |
143 | print " " | |
144 | print " " + GetWorkqueueThreadRequestSummary.header | |
145 | if wq.wq_event_manager_threadreq: | |
146 | print " " + GetWorkqueueThreadRequestSummary(proc, wq.wq_event_manager_threadreq) | |
147 | for req in IteratePriorityQueueEntry(wq.wq_overcommit_queue, 'struct workq_threadreq_s', 'tr_entry'): | |
148 | print " " + GetWorkqueueThreadRequestSummary(proc, req) | |
149 | for req in IteratePriorityQueueEntry(wq.wq_constrained_queue, 'struct workq_threadreq_s', 'tr_entry'): | |
150 | print " " + GetWorkqueueThreadRequestSummary(proc, req) | |
151 | for req in IteratePriorityQueueEntry(wq.wq_special_queue, 'struct workq_threadreq_s', 'tr_entry'): | |
152 | print " " + GetWorkqueueThreadRequestSummary(proc, req) | |
153 | ||
154 | print " " | |
155 | print " " + GetWQThreadSummary.header | |
156 | for uth in IterateTAILQ_HEAD(wq.wq_thrunlist, "uu_workq_entry"): | |
157 | print " " + GetWQThreadSummary(Cast(uth.uu_thread, 'struct thread *'), uth) | |
158 | for uth in IterateTAILQ_HEAD(wq.wq_thidlelist, "uu_workq_entry"): | |
159 | print " " + GetWQThreadSummary(Cast(uth.uu_thread, 'struct thread *'), uth) | |
160 | for uth in IterateTAILQ_HEAD(wq.wq_thnewlist, "uu_workq_entry"): | |
161 | print " " + GetWQThreadSummary(Cast(uth.uu_thread, 'struct thread *'), uth) | |
162 | ||
163 | @lldb_command('showallworkqueues') | |
164 | def ShowAllWorkqueues(cmd_args=None): | |
165 | """ Display a summary of all the workqueues in the system | |
166 | ||
167 | usage: showallworkqueues | |
168 | """ | |
169 | ||
170 | print GetWorkqueueSummary.header | |
171 | ||
172 | for t in kern.tasks: | |
173 | proc = Cast(t.bsd_info, 'proc *') | |
174 | wq = Cast(proc.p_wqptr, "struct workqueue *"); | |
175 | if wq: | |
176 | print GetWorkqueueSummary(proc, wq) |