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