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 /* timer id to call back on */
45 static unsigned pet_timerid
= 0;
48 * We also use this as the sync point for waiting, for no good reason
50 static unsigned pet_actionid
= 0;
52 /* the actual thread pointer */
53 static thread_t pet_thread
= NULL
;
55 /* Lock on which to synchronise */
56 static IOLock
*pet_lock
= NULL
;
58 /* where to sample data to */
59 static struct kperf_sample pet_sample_buf
;
61 static int pet_idle_rate
= 15;
63 /* sample an actual, honest to god thread! */
65 pet_sample_thread( thread_t thread
)
67 struct kperf_context ctx
;
69 unsigned skip_callstack
;
71 /* work out the context */
72 ctx
.cur_thread
= thread
;
75 task
= chudxnu_task_for_thread(thread
);
77 ctx
.cur_pid
= chudxnu_pid_for_task(task
);
79 skip_callstack
= (chudxnu_thread_get_dirty(thread
) == TRUE
) || ((thread
->kperf_pet_cnt
% (uint64_t)pet_idle_rate
) == 0) ? 0 : SAMPLE_FLAG_EMPTY_CALLSTACK
;
81 /* do the actual sample */
82 kperf_sample( &pet_sample_buf
, &ctx
, pet_actionid
,
83 SAMPLE_FLAG_IDLE_THREADS
| skip_callstack
);
86 chudxnu_thread_set_dirty(thread
, FALSE
);
88 thread
->kperf_pet_cnt
++;
91 /* given a list of threads, preferably stopped, sample 'em! */
93 pet_sample_thread_list( mach_msg_type_number_t threadc
, thread_array_t threadv
)
98 for( i
= 0; i
< threadc
; i
++ )
100 thread_t thread
= threadv
[i
];
106 for (ncpu
= 0; ncpu
< machine_info
.logical_cpu_max
; ++ncpu
)
108 thread_t candidate
= kperf_thread_on_cpus
[ncpu
];
109 if (candidate
&& candidate
->thread_id
== thread
->thread_id
)
113 /* the thread was not on a CPU */
114 if (ncpu
== machine_info
.logical_cpu_max
)
115 pet_sample_thread( thread
);
119 /* given a task (preferably stopped), sample all the threads in it */
121 pet_sample_task( task_t task
)
123 mach_msg_type_number_t threadc
;
124 thread_array_t threadv
;
127 kr
= chudxnu_task_threads(task
, &threadv
, &threadc
);
128 if( kr
!= KERN_SUCCESS
)
130 BUF_INFO2(PERF_PET_ERROR
, ERR_THREAD
, kr
);
134 pet_sample_thread_list( threadc
, threadv
);
136 chudxnu_free_thread_list(&threadv
, &threadc
);
139 /* given a list of tasks, sample all the threads in 'em */
141 pet_sample_task_list( int taskc
, task_array_t taskv
)
145 for( i
= 0; i
< taskc
; i
++ )
148 task_t task
= taskv
[i
];
150 /* FIXME: necessary? old code did this, our hacky
151 * filtering code does, too
157 /* try and stop any task other than the kernel task */
158 if( task
!= kernel_task
)
160 kr
= task_suspend( task
);
162 /* try the next task */
163 if( kr
!= KERN_SUCCESS
)
168 pet_sample_task( task
);
170 /* if it wasn't the kernel, resume it */
171 if( task
!= kernel_task
)
177 pet_sample_all_tasks(void)
179 task_array_t taskv
= NULL
;
180 mach_msg_type_number_t taskc
= 0;
183 kr
= chudxnu_all_tasks(&taskv
, &taskc
);
185 if( kr
!= KERN_SUCCESS
)
187 BUF_INFO2(PERF_PET_ERROR
, ERR_TASK
, kr
);
191 pet_sample_task_list( taskc
, taskv
);
192 chudxnu_free_task_list(&taskv
, &taskc
);
197 pet_sample_pid_filter(void)
199 task_t
*taskv
= NULL
;
203 kperf_filter_pid_list( &pidc
, &pidv
);
206 BUF_INFO2(PERF_PET_ERROR
, ERR_PID
, 0);
210 asize
= pidc
* sizeof(task_t
);
211 taskv
= kalloc( asize
);
216 /* convert the pid list into a task list */
217 for( i
= 0; i
< pidc
; i
++ )
223 taskv
[i
] = chudxnu_task_for_pid(pid
);
226 /* now sample the task list */
227 pet_sample_task_list( pidc
, taskv
);
232 kperf_filter_free_pid_list( &pidc
, &pidv
);
236 /* do the pet sample */
242 /* check if we're filtering on pid */
243 // pid_filter = kperf_filter_on_pid();
244 pid_filter
= 0; // FIXME
249 BUF_INFO1(PERF_PET_SAMPLE
| DBG_FUNC_START
, 1);
250 pet_sample_pid_filter();
255 /* otherwise filter everything */
256 BUF_INFO1(PERF_PET_SAMPLE
| DBG_FUNC_START
, 0);
257 pet_sample_all_tasks();
260 BUF_INFO1(PERF_PET_SAMPLE
| DBG_FUNC_END
, 0);
264 /* sleep indefinitely */
268 IOLockSleep(pet_lock
, &pet_actionid
, THREAD_UNINT
);
271 /* loop between sampling and waiting */
273 pet_thread_loop( __unused
void *param
, __unused wait_result_t wr
)
275 uint64_t work_unit_ticks
;
277 BUF_INFO1(PERF_PET_THREAD
, 1);
279 IOLockLock(pet_lock
);
282 BUF_INFO1(PERF_PET_IDLE
, 0);
285 BUF_INFO1(PERF_PET_RUN
, 0);
287 /* measure how long the work unit takes */
288 work_unit_ticks
= mach_absolute_time();
290 work_unit_ticks
= mach_absolute_time() - work_unit_ticks
;
292 /* re-program the timer */
293 kperf_timer_pet_set( pet_timerid
, work_unit_ticks
);
295 /* FIXME: break here on a condition? */
299 /* make sure the thread takes a new period value */
301 kperf_pet_timer_config( unsigned timerid
, unsigned actionid
)
306 /* hold the lock so pet thread doesn't run while we do this */
307 IOLockLock(pet_lock
);
309 BUF_INFO1(PERF_PET_THREAD
, 3);
312 pet_timerid
= timerid
;
313 pet_actionid
= actionid
;
316 IOLockUnlock(pet_lock
);
319 /* make the thread run! */
321 kperf_pet_thread_go(void)
326 /* Make the thread go */
327 IOLockWakeup(pet_lock
, &pet_actionid
, FALSE
);
331 /* wait for the pet thread to finish a run */
333 kperf_pet_thread_wait(void)
338 /* acquire the lock to ensure the thread is parked. */
339 IOLockLock(pet_lock
);
340 IOLockUnlock(pet_lock
);
343 /* keep the pet thread around while we run */
350 if( pet_thread
!= NULL
)
353 /* make the sync poing */
354 pet_lock
= IOLockAlloc();
355 if( pet_lock
== NULL
)
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
)
363 IOLockFree( pet_lock
);
373 kperf_get_pet_idle_rate( void )
375 return pet_idle_rate
;
379 kperf_set_pet_idle_rate( int val
)