]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kperf/pet.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / osfmk / kperf / pet.c
1 /*
2 * Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /* all thread states code */
30 #include <mach/mach_types.h>
31 #include <IOKit/IOTypes.h>
32 #include <IOKit/IOLocks.h>
33 #include <sys/errno.h>
34
35 #include <chud/chud_xnu.h>
36
37 #include <kperf/buffer.h>
38 #include <kperf/sample.h>
39 #include <kperf/context.h>
40 #include <kperf/action.h>
41 #include <kperf/pet.h>
42 #include <kperf/timetrigger.h>
43
44 /* timer id to call back on */
45 static unsigned pet_timerid = 0;
46
47 /* aciton ID to call
48 * We also use this as the sync point for waiting, for no good reason
49 */
50 static unsigned pet_actionid = 0;
51
52 /* the actual thread pointer */
53 static thread_t pet_thread = NULL;
54
55 /* Lock on which to synchronise */
56 static IOLock *pet_lock = NULL;
57
58 /* where to sample data to */
59 static struct kperf_sample pet_sample_buf;
60
61 static int pet_idle_rate = 15;
62
63 /* sample an actual, honest to god thread! */
64 static void
65 pet_sample_thread( thread_t thread )
66 {
67 struct kperf_context ctx;
68 task_t task;
69 unsigned skip_callstack;
70
71 /* work out the context */
72 ctx.cur_thread = thread;
73 ctx.cur_pid = 0;
74
75 task = chudxnu_task_for_thread(thread);
76 if(task)
77 ctx.cur_pid = chudxnu_pid_for_task(task);
78
79 skip_callstack = (chudxnu_thread_get_dirty(thread) == TRUE) || ((thread->kperf_pet_cnt % (uint64_t)pet_idle_rate) == 0) ? 0 : SAMPLE_FLAG_EMPTY_CALLSTACK;
80
81 /* do the actual sample */
82 kperf_sample( &pet_sample_buf, &ctx, pet_actionid,
83 SAMPLE_FLAG_IDLE_THREADS | skip_callstack );
84
85 if (!skip_callstack)
86 chudxnu_thread_set_dirty(thread, FALSE);
87
88 thread->kperf_pet_cnt++;
89 }
90
91 /* given a list of threads, preferably stopped, sample 'em! */
92 static void
93 pet_sample_thread_list( mach_msg_type_number_t threadc, thread_array_t threadv )
94 {
95 unsigned int i;
96 int ncpu;
97
98 for( i = 0; i < threadc; i++ )
99 {
100 thread_t thread = threadv[i];
101
102 if( !thread )
103 /* XXX? */
104 continue;
105
106 for (ncpu = 0; ncpu < machine_info.logical_cpu_max; ++ncpu)
107 {
108 thread_t candidate = kperf_thread_on_cpus[ncpu];
109 if (candidate && candidate->thread_id == thread->thread_id)
110 break;
111 }
112
113 /* the thread was not on a CPU */
114 if (ncpu == machine_info.logical_cpu_max)
115 pet_sample_thread( thread );
116 }
117 }
118
119 /* given a task (preferably stopped), sample all the threads in it */
120 static void
121 pet_sample_task( task_t task )
122 {
123 mach_msg_type_number_t threadc;
124 thread_array_t threadv;
125 kern_return_t kr;
126
127 kr = chudxnu_task_threads(task, &threadv, &threadc);
128 if( kr != KERN_SUCCESS )
129 {
130 BUF_INFO2(PERF_PET_ERROR, ERR_THREAD, kr);
131 return;
132 }
133
134 pet_sample_thread_list( threadc, threadv );
135
136 chudxnu_free_thread_list(&threadv, &threadc);
137 }
138
139 /* given a list of tasks, sample all the threads in 'em */
140 static void
141 pet_sample_task_list( int taskc, task_array_t taskv )
142 {
143 int i;
144
145 for( i = 0; i < taskc; i++ )
146 {
147 kern_return_t kr;
148 task_t task = taskv[i];
149
150 /* FIXME: necessary? old code did this, our hacky
151 * filtering code does, too
152 */
153 if(!task) {
154 continue;
155 }
156
157 /* try and stop any task other than the kernel task */
158 if( task != kernel_task )
159 {
160 kr = task_suspend( task );
161
162 /* try the next task */
163 if( kr != KERN_SUCCESS )
164 continue;
165 }
166
167 /* sample it */
168 pet_sample_task( task );
169
170 /* if it wasn't the kernel, resume it */
171 if( task != kernel_task )
172 task_resume(task);
173 }
174 }
175
176 static void
177 pet_sample_all_tasks(void)
178 {
179 task_array_t taskv = NULL;
180 mach_msg_type_number_t taskc = 0;
181 kern_return_t kr;
182
183 kr = chudxnu_all_tasks(&taskv, &taskc);
184
185 if( kr != KERN_SUCCESS )
186 {
187 BUF_INFO2(PERF_PET_ERROR, ERR_TASK, kr);
188 return;
189 }
190
191 pet_sample_task_list( taskc, taskv );
192 chudxnu_free_task_list(&taskv, &taskc);
193 }
194
195 #if 0
196 static void
197 pet_sample_pid_filter(void)
198 {
199 task_t *taskv = NULL;
200 int *pidv, pidc, i;
201 vm_size_t asize;
202
203 kperf_filter_pid_list( &pidc, &pidv );
204 if( pidc == 0 )
205 {
206 BUF_INFO2(PERF_PET_ERROR, ERR_PID, 0);
207 return;
208 }
209
210 asize = pidc * sizeof(task_t);
211 taskv = kalloc( asize );
212
213 if( taskv == NULL )
214 goto out;
215
216 /* convert the pid list into a task list */
217 for( i = 0; i < pidc; i++ )
218 {
219 int pid = pidv[i];
220 if( pid == -1 )
221 taskv[i] = NULL;
222 else
223 taskv[i] = chudxnu_task_for_pid(pid);
224 }
225
226 /* now sample the task list */
227 pet_sample_task_list( pidc, taskv );
228
229 kfree(taskv, asize);
230
231 out:
232 kperf_filter_free_pid_list( &pidc, &pidv );
233 }
234 #endif
235
236 /* do the pet sample */
237 static void
238 pet_work_unit(void)
239 {
240 int pid_filter;
241
242 /* check if we're filtering on pid */
243 // pid_filter = kperf_filter_on_pid();
244 pid_filter = 0; // FIXME
245
246 #if 0
247 if( pid_filter )
248 {
249 BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_START, 1);
250 pet_sample_pid_filter();
251 }
252 else
253 #endif
254 {
255 /* otherwise filter everything */
256 BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_START, 0);
257 pet_sample_all_tasks();
258 }
259
260 BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_END, 0);
261
262 }
263
264 /* sleep indefinitely */
265 static void
266 pet_idle(void)
267 {
268 IOLockSleep(pet_lock, &pet_actionid, THREAD_UNINT);
269 }
270
271 /* loop between sampling and waiting */
272 static void
273 pet_thread_loop( __unused void *param, __unused wait_result_t wr )
274 {
275 uint64_t work_unit_ticks;
276
277 BUF_INFO1(PERF_PET_THREAD, 1);
278
279 IOLockLock(pet_lock);
280 while(1)
281 {
282 BUF_INFO1(PERF_PET_IDLE, 0);
283 pet_idle();
284
285 BUF_INFO1(PERF_PET_RUN, 0);
286
287 /* measure how long the work unit takes */
288 work_unit_ticks = mach_absolute_time();
289 pet_work_unit();
290 work_unit_ticks = mach_absolute_time() - work_unit_ticks;
291
292 /* re-program the timer */
293 kperf_timer_pet_set( pet_timerid, work_unit_ticks );
294
295 /* FIXME: break here on a condition? */
296 }
297 }
298
299 /* make sure the thread takes a new period value */
300 void
301 kperf_pet_timer_config( unsigned timerid, unsigned actionid )
302 {
303 if( !pet_lock )
304 return;
305
306 /* hold the lock so pet thread doesn't run while we do this */
307 IOLockLock(pet_lock);
308
309 BUF_INFO1(PERF_PET_THREAD, 3);
310
311 /* set values */
312 pet_timerid = timerid;
313 pet_actionid = actionid;
314
315 /* done */
316 IOLockUnlock(pet_lock);
317 }
318
319 /* make the thread run! */
320 void
321 kperf_pet_thread_go(void)
322 {
323 if( !pet_lock )
324 return;
325
326 /* Make the thread go */
327 IOLockWakeup(pet_lock, &pet_actionid, FALSE);
328 }
329
330
331 /* wait for the pet thread to finish a run */
332 void
333 kperf_pet_thread_wait(void)
334 {
335 if( !pet_lock )
336 return;
337
338 /* acquire the lock to ensure the thread is parked. */
339 IOLockLock(pet_lock);
340 IOLockUnlock(pet_lock);
341 }
342
343 /* keep the pet thread around while we run */
344 int
345 kperf_pet_init(void)
346 {
347 kern_return_t rc;
348 thread_t t;
349
350 if( pet_thread != NULL )
351 return 0;
352
353 /* make the sync poing */
354 pet_lock = IOLockAlloc();
355 if( pet_lock == NULL )
356 return ENOMEM;
357
358 /* create the thread */
359 BUF_INFO1(PERF_PET_THREAD, 0);
360 rc = kernel_thread_start( pet_thread_loop, NULL, &t );
361 if( rc != KERN_SUCCESS )
362 {
363 IOLockFree( pet_lock );
364 pet_lock = NULL;
365 return ENOMEM;
366 }
367
368 /* OK! */
369 return 0;
370 }
371
372 int
373 kperf_get_pet_idle_rate( void )
374 {
375 return pet_idle_rate;
376 }
377
378 void
379 kperf_set_pet_idle_rate( int val )
380 {
381 pet_idle_rate = val;
382 }