1 #!/usr/local/bin/luatrace -s
3 trace_codename = function(codename, callback)
4 local debugid = trace.debugid(codename)
6 trace.single(debugid,callback)
8 printf("WARNING: Cannot locate debugid for '%s'\n", codename)
13 workqueue_ptr_map = {};
14 get_prefix = function(buf)
15 if initial_timestamp == 0 then
16 initial_timestamp = buf.timestamp
18 local secs = trace.convert_timestamp_to_nanoseconds(buf.timestamp - initial_timestamp) / 1000000000
21 if trace.debugid_is_start(buf.debugid) then
23 elseif trace.debugid_is_end(buf.debugid) then
30 if buf.command ~= "kernel_task" then
32 workqueue_ptr_map[buf[1]] = buf.command
33 elseif workqueue_ptr_map[buf[1]] ~= nil then
34 proc = workqueue_ptr_map[buf[1]]
39 return string.format("%s %6.9f %-17s [%05d.%06x] %-24s",
40 prefix, secs, proc, buf.pid, buf.threadid, buf.debugname)
43 parse_pthread_priority = function(pri)
44 pri = pri & 0xffffffff
45 if (pri & 0x02000000) == 0x02000000 then
48 local qos = (pri & 0x00ffff00) >> 8
50 return string.format("UInter[%x]", pri);
51 elseif qos == 0x10 then
52 return string.format("UInit[%x]", pri);
53 elseif qos == 0x08 then
54 return string.format("Dflt[%x]", pri);
55 elseif qos == 0x04 then
56 return string.format("Util[%x]", pri);
57 elseif qos == 0x02 then
58 return string.format("BG[%x]", pri);
59 elseif qos == 0x01 then
60 return string.format("Maint[%x]", pri);
61 elseif qos == 0x00 then
62 return string.format("Unsp[%x]", pri);
64 return string.format("Unkn[%x]", pri);
68 -- workqueue lifecycle
70 trace_codename("wq_pthread_exit", function(buf)
71 local prefix = get_prefix(buf)
72 if trace.debugid_is_start(buf.debugid) then
73 printf("%s\tprocess is exiting\n",prefix)
75 printf("%s\tworkqueue marked as exiting and timer is complete\n",prefix)
79 trace_codename("wq_workqueue_exit", function(buf)
80 local prefix = get_prefix(buf)
81 if trace.debugid_is_start(buf.debugid) then
82 printf("%s\tall threads have exited, cleaning up\n",prefix)
84 printf("%s\tclean up complete\n",prefix)
90 trace_codename("wq_kevent_req_threads", function(buf)
91 local prefix = get_prefix(buf)
92 if trace.debugid_is_start(buf.debugid) then
93 printf("%s\tkevent requesting threads (requests[] length = %d)\n", prefix, buf.arg2)
95 printf("%s\tkevent request complete (start_timer: %d)\n", prefix, buf.arg2)
99 trace_codename("wq_req_threads", function(buf)
100 local prefix = get_prefix(buf)
101 printf("%s\trecording %d constrained request(s) at %s, total %d requests\n",
102 prefix, buf.arg4, parse_pthread_priority(buf.arg2), buf.arg3)
105 trace_codename("wq_req_octhreads", function(buf)
106 local prefix = get_prefix(buf)
107 printf("%s\tattempting %d overcommit request(s) at %s, total %d requests\n",
108 prefix, buf.arg4, parse_pthread_priority(buf.arg2), buf.arg3)
110 trace_codename("wq_delay_octhreads", function(buf)
111 local prefix = get_prefix(buf)
112 printf("%s\trecording %d delayed overcommit request(s) at %s, total %d requests\n",
113 prefix, buf.arg4, parse_pthread_priority(buf.arg2), buf.arg3)
116 trace_codename("wq_req_kevent_threads", function(buf)
117 local prefix = get_prefix(buf)
118 printf("%s\trecording kevent constrained request at %s, total %d requests\n",
119 prefix, parse_pthread_priority(buf.arg2), buf.arg3)
121 trace_codename("wq_req_kevent_octhreads", function(buf)
122 local prefix = get_prefix(buf)
123 printf("%s\trecording kevent overcommit request at %s, total %d requests\n",
124 prefix, parse_pthread_priority(buf.arg2), buf.arg3)
126 trace_codename("wq_req_event_manager", function(buf)
127 local prefix = get_prefix(buf)
128 if buf.arg2 == 1 then
129 printf("%s\tstarting event manager thread, existing at %d, %d added\n",
130 prefix, buf.arg3, buf.arg4)
132 printf("%s\trecording event manager request, existing at %d, %d added\n",
133 prefix, buf.arg3, buf.arg4)
137 trace_codename("wq_start_add_timer", function(buf)
138 local prefix = get_prefix(buf)
139 printf("%s\tarming timer to fire in %d us (flags: %x, reqcount: %d)\n",
140 prefix, buf.arg4, buf.arg3, buf.arg2)
143 trace_codename("wq_add_timer", function(buf)
144 local prefix = get_prefix(buf)
145 if trace.debugid_is_start(buf.debugid) then
146 printf("%s\tadd_timer fired (flags: %x, nthreads: %d, thidlecount: %d)\n",
147 prefix, buf.arg2, buf.arg3, buf.arg4)
148 elseif trace.debugid_is_end(buf.debugid) then
149 printf("%s\tadd_timer completed (start_timer: %x, nthreads: %d, thidlecount: %d)\n",
150 prefix, buf.arg2, buf.arg3, buf.arg4)
152 printf("%s\tadd_timer added threads (reqcount: %d, thidlecount: %d, busycount: %d)\n",
153 prefix, buf.arg2, buf.arg3, buf.arg4)
158 trace_codename("wq_overcommitted", function(buf)
159 local prefix = get_prefix(buf)
160 if buf.arg2 & 0x1000000 ~= 0 then
161 printf("%s\tworkqueue overcommitted @ %s, starting timer (thactive_count: %d, busycount; %d)\n",
162 prefix, parse_pthread_priority(buf.arg2), buf.arg3, buf.arg4)
164 printf("%s\tworkqueue overcommitted @ %s (thactive_count: %d, busycount; %d)\n",
165 prefix, parse_pthread_priority(buf.arg2), buf.arg3, buf.arg4)
169 trace_codename("wq_stalled", function(buf)
170 local prefix = get_prefix(buf)
171 printf("%s\tworkqueue stalled (nthreads: %d)\n", prefix, buf.arg3)
176 trace_codename("wq_run_nextitem", function(buf)
177 local prefix = get_prefix(buf)
178 if trace.debugid_is_start(buf.debugid) then
179 if buf.arg2 == 0 then
180 printf("%s\ttrying to run a request on an idle thread (idlecount: %d, reqcount: %d)\n",
181 prefix, buf.arg3, buf.arg4)
183 printf("%s\tthread %x looking for next request (idlecount: %d, reqcount: %d)\n",
184 prefix, buf.threadid, buf.arg3, buf.arg4)
187 if buf.arg4 == 1 then
188 printf("%s\tkicked off work on thread %x (overcommit: %d)\n", prefix, buf.arg2, buf.arg3)
189 elseif buf.arg4 == 3 then
190 printf("%s\tno work %x can currently do (start_timer: %d)\n", prefix, buf.arg2, buf.arg3)
191 elseif buf.arg4 == 4 then
192 printf("%s\treturning to run next item\n", prefix)
194 printf("%s\tWARNING: UNKNOWN END CODE:%d\n", prefix, buf.arg4)
199 trace.enable_thread_cputime()
200 runitem_time_map = {}
201 runitem_cputime_map = {}
202 trace_codename("wq_runitem", function(buf)
203 local prefix = get_prefix(buf)
204 if trace.debugid_is_start(buf.debugid) then
205 runitem_time_map[buf.threadid] = buf.timestamp;
206 runitem_cputime_map[buf.threadid] = trace.cputime_for_thread(buf.threadid);
208 printf("%s\tSTART running item\n", prefix)
209 elseif runitem_time_map[buf.threadid] then
210 local time = buf.timestamp - runitem_time_map[buf.threadid]
211 local cputime = trace.cputime_for_thread(buf.threadid) - runitem_cputime_map[buf.threadid]
213 local time_ms = trace.convert_timestamp_to_nanoseconds(time) / 1000000
214 local cputime_ms = trace.convert_timestamp_to_nanoseconds(cputime) / 1000000
216 printf("%s\tDONE running item: time = %6.6f ms, cputime = %6.6f ms\n", prefix, time_ms, cputime_ms)
218 runitem_time_map[buf.threadid] = 0
219 runitem_cputime_map[buf.threadid] = 0
221 printf("%s\tDONE running item\n", prefix)
225 trace_codename("wq_thread_yielded", function(buf)
226 local prefix = get_prefix(buf)
227 if trace.debugid_is_start(buf.debugid) then
228 printf("%s\tthread_yielded called (yielded_count: %d, reqcount: %d)\n",
229 prefix, buf.arg2, buf.arg3)
231 if buf.arg4 == 1 then
232 printf("%s\tthread_yielded completed kicking thread (yielded_count: %d, reqcount: %d)\n",
233 prefix, buf.arg2, buf.arg3)
234 elseif buf.arg4 == 2 then
235 printf("%s\tthread_yielded completed (yielded_count: %d, reqcount: %d)\n",
236 prefix, buf.arg2, buf.arg3)
238 printf("%s\tthread_yielded completed unusually (yielded_count: %d, reqcount: %d)\n",
239 prefix, buf.arg2, buf.arg3)
244 trace_codename("wq_thread_block", function(buf)
245 local prefix = get_prefix(buf)
246 if trace.debugid_is_start(buf.debugid) then
247 printf("%s\tthread blocked (activecount: %d, prioritiy: %d, start_time: %d)\n",
248 prefix, buf.arg2, buf.arg3, buf.arg3)
250 printf("%s\tthread unblocked (threads_scheduled: %d, priority: %d)\n",
251 prefix, buf.arg2, buf.arg3)
255 trace_codename("wq_thread_suspend", function(buf)
256 local prefix = get_prefix(buf)
257 if trace.debugid_is_start(buf.debugid) then
258 printf("%s\tcreated new suspended thread (nthreads:%d)\n",
261 if buf.arg4 == 0xdead then
262 printf("%s\tthread exited suspension to die (nthreads: %d)\n",
268 trace_codename("wq_thread_park", function(buf)
269 local prefix = get_prefix(buf)
270 if trace.debugid_is_start(buf.debugid) then
271 printf("%s\tthread parked (threads_scheduled: %d, thidlecount: %d, us_to_wait: %d)\n",
272 prefix, buf.arg2, buf.arg3, buf.arg4)
274 if buf.arg4 == 0xdead then
275 printf("%s\tthread exited park to die (nthreads: %d)\n", prefix, buf.arg3)
281 trace_codename("wq_thread_limit_exceeded", function(buf)
282 local prefix = get_prefix(buf)
283 printf("%s\ttotal thread limit exceeded, %d threads, total %d max threads, (kern limit: %d)\n",
284 prefix, buf.arg2, buf.arg3, buf.arg4)
287 trace_codename("wq_thread_constrained_maxed", function(buf)
288 local prefix = get_prefix(buf)
289 printf("%s\tattempted to add thread at max constrained limit, total %d threads (limit: %d)\n",
290 prefix, buf.arg2, buf.arg3)
293 trace_codename("wq_thread_add_during_exit", function(buf)
294 local prefix = get_prefix(buf)
295 printf("%s\tattempted to add thread during WQ_EXITING\n", prefix)
298 trace_codename("wq_thread_create_failed", function(buf)
299 local prefix = get_prefix(buf)
300 if buf.arg3 == 0 then
301 printf("%s\tfailed to create new workqueue thread, kern_return: 0x%x\n",
303 elseif buf.arg3 == 1 then
304 printf("%s\tfailed to vm_map workq thread stack: 0x%x", prefix, buf.arg2)
305 elseif buf.arg3 == 2 then
306 printf("%s\tfailed to vm_protect workq thread guardsize: 0x%x", prefix, buf.arg2)
310 trace_codename("wq_thread_create", function(buf)
311 printf("%s\tcreateed new workqueue thread\n", get_prefix(buf))
314 trace_codename("wq_manager_request", function(buf)
315 local prefix = get_prefix(buf)
316 printf("%s\tthread in bucket %d\n", prefix, buf.arg3)
320 -- The trace codes we need aren't enabled by default
321 darwin.sysctlbyname("kern.pthread_debug_tracing", 1)
322 completion_handler = function()
323 darwin.sysctlbyname("kern.pthread_debug_tracing", 0)
325 trace.set_completion_handler(completion_handler)