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 #include <kern/task.h>
46 /* timer id to call back on */
47 static unsigned pet_timerid
= 0;
50 * We also use this as the sync point for waiting, for no good reason
52 static unsigned pet_actionid
= 0;
54 /* the actual thread pointer */
55 static thread_t pet_thread
= NULL
;
57 /* Lock on which to synchronise */
58 static IOLock
*pet_lock
= NULL
;
60 /* where to sample data to */
61 static struct kperf_sample pet_sample_buf
;
63 static int pet_idle_rate
= 15;
65 /* sample an actual, honest to god thread! */
67 pet_sample_thread( thread_t thread
)
69 struct kperf_context ctx
;
71 unsigned skip_callstack
;
73 /* work out the context */
74 ctx
.cur_thread
= thread
;
77 task
= chudxnu_task_for_thread(thread
);
79 ctx
.cur_pid
= chudxnu_pid_for_task(task
);
81 skip_callstack
= (chudxnu_thread_get_dirty(thread
) == TRUE
) || ((thread
->kperf_pet_cnt
% (uint64_t)pet_idle_rate
) == 0) ? 0 : SAMPLE_FLAG_EMPTY_CALLSTACK
;
83 /* do the actual sample */
84 kperf_sample( &pet_sample_buf
, &ctx
, pet_actionid
,
85 SAMPLE_FLAG_IDLE_THREADS
| skip_callstack
);
88 chudxnu_thread_set_dirty(thread
, FALSE
);
90 thread
->kperf_pet_cnt
++;
93 /* given a list of threads, preferably stopped, sample 'em! */
95 pet_sample_thread_list( mach_msg_type_number_t threadc
, thread_array_t threadv
)
100 for( i
= 0; i
< threadc
; i
++ )
102 thread_t thread
= threadv
[i
];
108 for (ncpu
= 0; ncpu
< machine_info
.logical_cpu_max
; ++ncpu
)
110 thread_t candidate
= kperf_thread_on_cpus
[ncpu
];
111 if (candidate
&& candidate
->thread_id
== thread
->thread_id
)
115 /* the thread was not on a CPU */
116 if (ncpu
== machine_info
.logical_cpu_max
)
117 pet_sample_thread( thread
);
121 /* given a task (preferably stopped), sample all the threads in it */
123 pet_sample_task( task_t task
)
125 mach_msg_type_number_t threadc
;
126 thread_array_t threadv
;
129 kr
= chudxnu_task_threads(task
, &threadv
, &threadc
);
130 if( kr
!= KERN_SUCCESS
)
132 BUF_INFO2(PERF_PET_ERROR
, ERR_THREAD
, kr
);
136 pet_sample_thread_list( threadc
, threadv
);
138 chudxnu_free_thread_list(&threadv
, &threadc
);
141 /* given a list of tasks, sample all the threads in 'em */
143 pet_sample_task_list( int taskc
, task_array_t taskv
)
147 for( i
= 0; i
< taskc
; i
++ )
150 task_t task
= taskv
[i
];
152 /* FIXME: necessary? old code did this, our hacky
153 * filtering code does, too
159 /* try and stop any task other than the kernel task */
160 if( task
!= kernel_task
)
162 kr
= task_suspend_internal( task
);
164 /* try the next task */
165 if( kr
!= KERN_SUCCESS
)
170 pet_sample_task( task
);
172 /* if it wasn't the kernel, resume it */
173 if( task
!= kernel_task
)
174 (void) task_resume_internal(task
);
179 pet_sample_all_tasks(void)
181 task_array_t taskv
= NULL
;
182 mach_msg_type_number_t taskc
= 0;
185 kr
= chudxnu_all_tasks(&taskv
, &taskc
);
187 if( kr
!= KERN_SUCCESS
)
189 BUF_INFO2(PERF_PET_ERROR
, ERR_TASK
, kr
);
193 pet_sample_task_list( taskc
, taskv
);
194 chudxnu_free_task_list(&taskv
, &taskc
);
199 pet_sample_pid_filter(void)
201 task_t
*taskv
= NULL
;
205 kperf_filter_pid_list( &pidc
, &pidv
);
208 BUF_INFO2(PERF_PET_ERROR
, ERR_PID
, 0);
212 asize
= pidc
* sizeof(task_t
);
213 taskv
= kalloc( asize
);
218 /* convert the pid list into a task list */
219 for( i
= 0; i
< pidc
; i
++ )
225 taskv
[i
] = chudxnu_task_for_pid(pid
);
228 /* now sample the task list */
229 pet_sample_task_list( pidc
, taskv
);
234 kperf_filter_free_pid_list( &pidc
, &pidv
);
238 /* do the pet sample */
244 /* check if we're filtering on pid */
245 // pid_filter = kperf_filter_on_pid();
246 pid_filter
= 0; // FIXME
251 BUF_INFO1(PERF_PET_SAMPLE
| DBG_FUNC_START
, 1);
252 pet_sample_pid_filter();
257 /* otherwise filter everything */
258 BUF_INFO1(PERF_PET_SAMPLE
| DBG_FUNC_START
, 0);
259 pet_sample_all_tasks();
262 BUF_INFO1(PERF_PET_SAMPLE
| DBG_FUNC_END
, 0);
266 /* sleep indefinitely */
270 IOLockSleep(pet_lock
, &pet_actionid
, THREAD_UNINT
);
273 /* loop between sampling and waiting */
275 pet_thread_loop( __unused
void *param
, __unused wait_result_t wr
)
277 uint64_t work_unit_ticks
;
279 BUF_INFO1(PERF_PET_THREAD
, 1);
281 IOLockLock(pet_lock
);
284 BUF_INFO1(PERF_PET_IDLE
, 0);
287 BUF_INFO1(PERF_PET_RUN
, 0);
289 /* measure how long the work unit takes */
290 work_unit_ticks
= mach_absolute_time();
292 work_unit_ticks
= mach_absolute_time() - work_unit_ticks
;
294 /* re-program the timer */
295 kperf_timer_pet_set( pet_timerid
, work_unit_ticks
);
297 /* FIXME: break here on a condition? */
301 /* make sure the thread takes a new period value */
303 kperf_pet_timer_config( unsigned timerid
, unsigned actionid
)
308 /* hold the lock so pet thread doesn't run while we do this */
309 IOLockLock(pet_lock
);
311 BUF_INFO1(PERF_PET_THREAD
, 3);
314 pet_timerid
= timerid
;
315 pet_actionid
= actionid
;
318 IOLockUnlock(pet_lock
);
321 /* make the thread run! */
323 kperf_pet_thread_go(void)
328 /* Make the thread go */
329 IOLockWakeup(pet_lock
, &pet_actionid
, FALSE
);
333 /* wait for the pet thread to finish a run */
335 kperf_pet_thread_wait(void)
340 /* acquire the lock to ensure the thread is parked. */
341 IOLockLock(pet_lock
);
342 IOLockUnlock(pet_lock
);
345 /* keep the pet thread around while we run */
352 if( pet_thread
!= NULL
)
355 /* make the sync poing */
356 pet_lock
= IOLockAlloc();
357 if( pet_lock
== NULL
)
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
)
365 IOLockFree( pet_lock
);
375 kperf_get_pet_idle_rate( void )
377 return pet_idle_rate
;
381 kperf_set_pet_idle_rate( int val
)