]>
Commit | Line | Data |
---|---|---|
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 */ |
47 | static 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 | */ | |
52 | static unsigned pet_actionid = 0; | |
53 | ||
54 | /* the actual thread pointer */ | |
55 | static thread_t pet_thread = NULL; | |
56 | ||
57 | /* Lock on which to synchronise */ | |
58 | static IOLock *pet_lock = NULL; | |
59 | ||
60 | /* where to sample data to */ | |
61 | static struct kperf_sample pet_sample_buf; | |
62 | ||
39236c6e A |
63 | static int pet_idle_rate = 15; |
64 | ||
316670eb A |
65 | /* sample an actual, honest to god thread! */ |
66 | static void | |
67 | pet_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! */ | |
94 | static void | |
95 | pet_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 */ | |
122 | static void | |
123 | pet_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 */ | |
142 | static void | |
143 | pet_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 | ||
178 | static void | |
179 | pet_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 |
198 | static void |
199 | pet_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 | ||
233 | out: | |
234 | kperf_filter_free_pid_list( &pidc, &pidv ); | |
235 | } | |
39236c6e | 236 | #endif |
316670eb A |
237 | |
238 | /* do the pet sample */ | |
239 | static void | |
240 | pet_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 */ | |
267 | static void | |
268 | pet_idle(void) | |
269 | { | |
316670eb | 270 | IOLockSleep(pet_lock, &pet_actionid, THREAD_UNINT); |
316670eb A |
271 | } |
272 | ||
273 | /* loop between sampling and waiting */ | |
274 | static void | |
275 | pet_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 */ | |
302 | void | |
303 | kperf_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! */ | |
322 | void | |
323 | kperf_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 */ | |
334 | void | |
335 | kperf_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 */ | |
346 | int | |
347 | kperf_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 | |
374 | int | |
375 | kperf_get_pet_idle_rate( void ) | |
376 | { | |
377 | return pet_idle_rate; | |
378 | } | |
379 | ||
380 | void | |
381 | kperf_set_pet_idle_rate( int val ) | |
382 | { | |
383 | pet_idle_rate = val; | |
384 | } |