2 * Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
35 #include <chud/chud_xnu.h>
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>
44 extern kern_return_t
task_resume_internal(task_t
);
45 extern kern_return_t
task_suspend_internal(task_t
);
47 /* timer id to call back on */
48 static unsigned pet_timerid
= 0;
51 * We also use this as the sync point for waiting, for no good reason
53 static unsigned pet_actionid
= 0;
55 /* the actual thread pointer */
56 static thread_t pet_thread
= NULL
;
58 /* Lock on which to synchronise */
59 static IOLock
*pet_lock
= NULL
;
61 /* where to sample data to */
62 static struct kperf_sample pet_sample_buf
;
64 static int pet_idle_rate
= 15;
66 /* sample an actual, honest to god thread! */
68 pet_sample_thread( thread_t thread
)
70 struct kperf_context ctx
;
72 unsigned skip_callstack
;
74 /* work out the context */
75 ctx
.cur_thread
= thread
;
78 task
= chudxnu_task_for_thread(thread
);
80 ctx
.cur_pid
= chudxnu_pid_for_task(task
);
82 skip_callstack
= (chudxnu_thread_get_dirty(thread
) == TRUE
) || ((thread
->kperf_pet_cnt
% (uint64_t)pet_idle_rate
) == 0) ? 0 : SAMPLE_FLAG_EMPTY_CALLSTACK
;
84 /* do the actual sample */
85 kperf_sample( &pet_sample_buf
, &ctx
, pet_actionid
,
86 SAMPLE_FLAG_IDLE_THREADS
| skip_callstack
);
89 chudxnu_thread_set_dirty(thread
, FALSE
);
91 thread
->kperf_pet_cnt
++;
94 /* given a list of threads, preferably stopped, sample 'em! */
96 pet_sample_thread_list( mach_msg_type_number_t threadc
, thread_array_t threadv
)
101 for( i
= 0; i
< threadc
; i
++ )
103 thread_t thread
= threadv
[i
];
109 for (ncpu
= 0; ncpu
< machine_info
.logical_cpu_max
; ++ncpu
)
111 thread_t candidate
= kperf_thread_on_cpus
[ncpu
];
112 if (candidate
&& candidate
->thread_id
== thread
->thread_id
)
116 /* the thread was not on a CPU */
117 if (ncpu
== machine_info
.logical_cpu_max
)
118 pet_sample_thread( thread
);
122 /* given a task (preferably stopped), sample all the threads in it */
124 pet_sample_task( task_t task
)
126 mach_msg_type_number_t threadc
;
127 thread_array_t threadv
;
130 kr
= chudxnu_task_threads(task
, &threadv
, &threadc
);
131 if( kr
!= KERN_SUCCESS
)
133 BUF_INFO2(PERF_PET_ERROR
, ERR_THREAD
, kr
);
137 pet_sample_thread_list( threadc
, threadv
);
139 chudxnu_free_thread_list(&threadv
, &threadc
);
142 /* given a list of tasks, sample all the threads in 'em */
144 pet_sample_task_list( int taskc
, task_array_t taskv
)
148 for( i
= 0; i
< taskc
; i
++ )
151 task_t task
= taskv
[i
];
153 /* FIXME: necessary? old code did this, our hacky
154 * filtering code does, too
160 /* try and stop any task other than the kernel task */
161 if( task
!= kernel_task
)
163 kr
= task_suspend_internal( task
);
165 /* try the next task */
166 if( kr
!= KERN_SUCCESS
)
171 pet_sample_task( task
);
173 /* if it wasn't the kernel, resume it */
174 if( task
!= kernel_task
)
175 (void) task_resume_internal(task
);
180 pet_sample_all_tasks(void)
182 task_array_t taskv
= NULL
;
183 mach_msg_type_number_t taskc
= 0;
186 kr
= chudxnu_all_tasks(&taskv
, &taskc
);
188 if( kr
!= KERN_SUCCESS
)
190 BUF_INFO2(PERF_PET_ERROR
, ERR_TASK
, kr
);
194 pet_sample_task_list( taskc
, taskv
);
195 chudxnu_free_task_list(&taskv
, &taskc
);
200 pet_sample_pid_filter(void)
202 task_t
*taskv
= NULL
;
206 kperf_filter_pid_list( &pidc
, &pidv
);
209 BUF_INFO2(PERF_PET_ERROR
, ERR_PID
, 0);
213 asize
= pidc
* sizeof(task_t
);
214 taskv
= kalloc( asize
);
219 /* convert the pid list into a task list */
220 for( i
= 0; i
< pidc
; i
++ )
226 taskv
[i
] = chudxnu_task_for_pid(pid
);
229 /* now sample the task list */
230 pet_sample_task_list( pidc
, taskv
);
235 kperf_filter_free_pid_list( &pidc
, &pidv
);
239 /* do the pet sample */
245 /* check if we're filtering on pid */
246 // pid_filter = kperf_filter_on_pid();
247 pid_filter
= 0; // FIXME
252 BUF_INFO1(PERF_PET_SAMPLE
| DBG_FUNC_START
, 1);
253 pet_sample_pid_filter();
258 /* otherwise filter everything */
259 BUF_INFO1(PERF_PET_SAMPLE
| DBG_FUNC_START
, 0);
260 pet_sample_all_tasks();
263 BUF_INFO1(PERF_PET_SAMPLE
| DBG_FUNC_END
, 0);
267 /* sleep indefinitely */
271 IOLockSleep(pet_lock
, &pet_actionid
, THREAD_UNINT
);
274 /* loop between sampling and waiting */
276 pet_thread_loop( __unused
void *param
, __unused wait_result_t wr
)
278 uint64_t work_unit_ticks
;
280 BUF_INFO1(PERF_PET_THREAD
, 1);
282 IOLockLock(pet_lock
);
285 BUF_INFO1(PERF_PET_IDLE
, 0);
288 BUF_INFO1(PERF_PET_RUN
, 0);
290 /* measure how long the work unit takes */
291 work_unit_ticks
= mach_absolute_time();
293 work_unit_ticks
= mach_absolute_time() - work_unit_ticks
;
295 /* re-program the timer */
296 kperf_timer_pet_set( pet_timerid
, work_unit_ticks
);
298 /* FIXME: break here on a condition? */
302 /* make sure the thread takes a new period value */
304 kperf_pet_timer_config( unsigned timerid
, unsigned actionid
)
309 /* hold the lock so pet thread doesn't run while we do this */
310 IOLockLock(pet_lock
);
312 BUF_INFO1(PERF_PET_THREAD
, 3);
315 pet_timerid
= timerid
;
316 pet_actionid
= actionid
;
319 IOLockUnlock(pet_lock
);
322 /* make the thread run! */
324 kperf_pet_thread_go(void)
329 /* Make the thread go */
330 IOLockWakeup(pet_lock
, &pet_actionid
, FALSE
);
334 /* wait for the pet thread to finish a run */
336 kperf_pet_thread_wait(void)
341 /* acquire the lock to ensure the thread is parked. */
342 IOLockLock(pet_lock
);
343 IOLockUnlock(pet_lock
);
346 /* keep the pet thread around while we run */
353 if( pet_thread
!= NULL
)
356 /* make the sync poing */
357 pet_lock
= IOLockAlloc();
358 if( pet_lock
== NULL
)
361 /* create the thread */
362 BUF_INFO1(PERF_PET_THREAD
, 0);
363 rc
= kernel_thread_start( pet_thread_loop
, NULL
, &t
);
364 if( rc
!= KERN_SUCCESS
)
366 IOLockFree( pet_lock
);
376 kperf_get_pet_idle_rate( void )
378 return pet_idle_rate
;
382 kperf_set_pet_idle_rate( int val
)