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 */ 46static 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 */ 51static unsigned pet_actionid = 0; 52 53/* the actual thread pointer */ 54static thread_t pet_thread = NULL; 55 56/* Lock on which to synchronise */ 57static IOLock *pet_lock = NULL; 58 59/* where to sample data to */ 60static struct kperf_sample pet_sample_buf; 61 62/* sample an actual, honest to god thread! */ 63static void 64pet_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! */ 82static void 83pet_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 */ 100static void 101pet_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 */ 120static void 121pet_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 156static void 157pet_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 175static void 176pet_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 210out: 211 kperf_filter_free_pid_list( &pidc, &pidv ); 212} 213 214/* do the pet sample */ 215static void 216pet_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 */ 240static void 241pet_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 */ 249static void 250pet_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 */ 270void 271kperf_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! */ 287void 288kperf_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 */ 296void 297kperf_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 */ 305int 306kperf_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} 332