]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kperf/pet.c
xnu-3248.60.10.tar.gz
[apple/xnu.git] / osfmk / kperf / pet.c
CommitLineData
316670eb
A
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>
316670eb
A
41#include <kperf/pet.h>
42#include <kperf/timetrigger.h>
43
3e170ce0 44#include <kern/task.h>
fe8ab488 45
316670eb
A
46/* timer id to call back on */
47static unsigned pet_timerid = 0;
48
49/* aciton ID to call
50 * We also use this as the sync point for waiting, for no good reason
51 */
52static unsigned pet_actionid = 0;
53
54/* the actual thread pointer */
55static thread_t pet_thread = NULL;
56
57/* Lock on which to synchronise */
58static IOLock *pet_lock = NULL;
59
60/* where to sample data to */
61static struct kperf_sample pet_sample_buf;
62
39236c6e
A
63static int pet_idle_rate = 15;
64
316670eb
A
65/* sample an actual, honest to god thread! */
66static void
67pet_sample_thread( thread_t thread )
68{
69 struct kperf_context ctx;
70 task_t task;
39236c6e 71 unsigned skip_callstack;
316670eb
A
72
73 /* work out the context */
74 ctx.cur_thread = thread;
39236c6e 75 ctx.cur_pid = 0;
316670eb
A
76
77 task = chudxnu_task_for_thread(thread);
78 if(task)
79 ctx.cur_pid = chudxnu_pid_for_task(task);
80
39236c6e
A
81 skip_callstack = (chudxnu_thread_get_dirty(thread) == TRUE) || ((thread->kperf_pet_cnt % (uint64_t)pet_idle_rate) == 0) ? 0 : SAMPLE_FLAG_EMPTY_CALLSTACK;
82
316670eb 83 /* do the actual sample */
39236c6e
A
84 kperf_sample( &pet_sample_buf, &ctx, pet_actionid,
85 SAMPLE_FLAG_IDLE_THREADS | skip_callstack );
86
87 if (!skip_callstack)
88 chudxnu_thread_set_dirty(thread, FALSE);
89
90 thread->kperf_pet_cnt++;
316670eb
A
91}
92
93/* given a list of threads, preferably stopped, sample 'em! */
94static void
95pet_sample_thread_list( mach_msg_type_number_t threadc, thread_array_t threadv )
96{
97 unsigned int i;
39236c6e 98 int ncpu;
316670eb
A
99
100 for( i = 0; i < threadc; i++ )
101 {
102 thread_t thread = threadv[i];
103
104 if( !thread )
105 /* XXX? */
106 continue;
107
39236c6e
A
108 for (ncpu = 0; ncpu < machine_info.logical_cpu_max; ++ncpu)
109 {
110 thread_t candidate = kperf_thread_on_cpus[ncpu];
111 if (candidate && candidate->thread_id == thread->thread_id)
112 break;
113 }
114
115 /* the thread was not on a CPU */
116 if (ncpu == machine_info.logical_cpu_max)
117 pet_sample_thread( thread );
316670eb
A
118 }
119}
120
121/* given a task (preferably stopped), sample all the threads in it */
122static void
123pet_sample_task( task_t task )
124{
125 mach_msg_type_number_t threadc;
126 thread_array_t threadv;
127 kern_return_t kr;
128
129 kr = chudxnu_task_threads(task, &threadv, &threadc);
130 if( kr != KERN_SUCCESS )
131 {
132 BUF_INFO2(PERF_PET_ERROR, ERR_THREAD, kr);
133 return;
134 }
135
136 pet_sample_thread_list( threadc, threadv );
137
138 chudxnu_free_thread_list(&threadv, &threadc);
139}
140
141/* given a list of tasks, sample all the threads in 'em */
142static void
143pet_sample_task_list( int taskc, task_array_t taskv )
144{
145 int i;
39236c6e 146
316670eb
A
147 for( i = 0; i < taskc; i++ )
148 {
149 kern_return_t kr;
150 task_t task = taskv[i];
151
152 /* FIXME: necessary? old code did this, our hacky
153 * filtering code does, too
154 */
155 if(!task) {
156 continue;
157 }
39236c6e 158
316670eb
A
159 /* try and stop any task other than the kernel task */
160 if( task != kernel_task )
161 {
fe8ab488 162 kr = task_suspend_internal( task );
316670eb
A
163
164 /* try the next task */
165 if( kr != KERN_SUCCESS )
166 continue;
167 }
39236c6e 168
316670eb
A
169 /* sample it */
170 pet_sample_task( task );
171
172 /* if it wasn't the kernel, resume it */
173 if( task != kernel_task )
fe8ab488 174 (void) task_resume_internal(task);
316670eb
A
175 }
176}
177
178static void
179pet_sample_all_tasks(void)
180{
181 task_array_t taskv = NULL;
182 mach_msg_type_number_t taskc = 0;
183 kern_return_t kr;
184
185 kr = chudxnu_all_tasks(&taskv, &taskc);
186
187 if( kr != KERN_SUCCESS )
188 {
189 BUF_INFO2(PERF_PET_ERROR, ERR_TASK, kr);
190 return;
191 }
192
193 pet_sample_task_list( taskc, taskv );
194 chudxnu_free_task_list(&taskv, &taskc);
195}
196
39236c6e 197#if 0
316670eb
A
198static void
199pet_sample_pid_filter(void)
200{
201 task_t *taskv = NULL;
202 int *pidv, pidc, i;
203 vm_size_t asize;
204
205 kperf_filter_pid_list( &pidc, &pidv );
206 if( pidc == 0 )
207 {
208 BUF_INFO2(PERF_PET_ERROR, ERR_PID, 0);
209 return;
210 }
211
212 asize = pidc * sizeof(task_t);
213 taskv = kalloc( asize );
214
215 if( taskv == NULL )
216 goto out;
217
218 /* convert the pid list into a task list */
219 for( i = 0; i < pidc; i++ )
220 {
221 int pid = pidv[i];
222 if( pid == -1 )
223 taskv[i] = NULL;
224 else
225 taskv[i] = chudxnu_task_for_pid(pid);
226 }
227
228 /* now sample the task list */
229 pet_sample_task_list( pidc, taskv );
230
231 kfree(taskv, asize);
232
233out:
234 kperf_filter_free_pid_list( &pidc, &pidv );
235}
39236c6e 236#endif
316670eb
A
237
238/* do the pet sample */
239static void
240pet_work_unit(void)
241{
242 int pid_filter;
243
244 /* check if we're filtering on pid */
39236c6e
A
245 // pid_filter = kperf_filter_on_pid();
246 pid_filter = 0; // FIXME
316670eb 247
39236c6e 248#if 0
316670eb
A
249 if( pid_filter )
250 {
251 BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_START, 1);
252 pet_sample_pid_filter();
253 }
254 else
39236c6e 255#endif
316670eb
A
256 {
257 /* otherwise filter everything */
258 BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_START, 0);
259 pet_sample_all_tasks();
260 }
261
262 BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_END, 0);
263
264}
265
266/* sleep indefinitely */
267static void
268pet_idle(void)
269{
316670eb 270 IOLockSleep(pet_lock, &pet_actionid, THREAD_UNINT);
316670eb
A
271}
272
273/* loop between sampling and waiting */
274static void
275pet_thread_loop( __unused void *param, __unused wait_result_t wr )
276{
39236c6e
A
277 uint64_t work_unit_ticks;
278
316670eb
A
279 BUF_INFO1(PERF_PET_THREAD, 1);
280
39236c6e 281 IOLockLock(pet_lock);
316670eb
A
282 while(1)
283 {
284 BUF_INFO1(PERF_PET_IDLE, 0);
285 pet_idle();
286
287 BUF_INFO1(PERF_PET_RUN, 0);
39236c6e
A
288
289 /* measure how long the work unit takes */
290 work_unit_ticks = mach_absolute_time();
316670eb 291 pet_work_unit();
39236c6e 292 work_unit_ticks = mach_absolute_time() - work_unit_ticks;
316670eb
A
293
294 /* re-program the timer */
39236c6e 295 kperf_timer_pet_set( pet_timerid, work_unit_ticks );
316670eb
A
296
297 /* FIXME: break here on a condition? */
298 }
299}
300
301/* make sure the thread takes a new period value */
302void
303kperf_pet_timer_config( unsigned timerid, unsigned actionid )
304{
39236c6e
A
305 if( !pet_lock )
306 return;
307
316670eb
A
308 /* hold the lock so pet thread doesn't run while we do this */
309 IOLockLock(pet_lock);
310
311 BUF_INFO1(PERF_PET_THREAD, 3);
312
313 /* set values */
314 pet_timerid = timerid;
315 pet_actionid = actionid;
316
317 /* done */
318 IOLockUnlock(pet_lock);
319}
320
321/* make the thread run! */
322void
323kperf_pet_thread_go(void)
324{
39236c6e
A
325 if( !pet_lock )
326 return;
327
316670eb
A
328 /* Make the thread go */
329 IOLockWakeup(pet_lock, &pet_actionid, FALSE);
330}
331
332
333/* wait for the pet thread to finish a run */
334void
335kperf_pet_thread_wait(void)
336{
39236c6e
A
337 if( !pet_lock )
338 return;
339
316670eb
A
340 /* acquire the lock to ensure the thread is parked. */
341 IOLockLock(pet_lock);
342 IOLockUnlock(pet_lock);
343}
344
345/* keep the pet thread around while we run */
346int
347kperf_pet_init(void)
348{
349 kern_return_t rc;
350 thread_t t;
351
352 if( pet_thread != NULL )
353 return 0;
354
355 /* make the sync poing */
356 pet_lock = IOLockAlloc();
357 if( pet_lock == NULL )
358 return ENOMEM;
359
360 /* create the thread */
361 BUF_INFO1(PERF_PET_THREAD, 0);
362 rc = kernel_thread_start( pet_thread_loop, NULL, &t );
363 if( rc != KERN_SUCCESS )
364 {
365 IOLockFree( pet_lock );
366 pet_lock = NULL;
367 return ENOMEM;
368 }
369
370 /* OK! */
371 return 0;
372}
39236c6e
A
373
374int
375kperf_get_pet_idle_rate( void )
376{
377 return pet_idle_rate;
378}
379
380void
381kperf_set_pet_idle_rate( int val )
382{
383 pet_idle_rate = val;
384}