/* * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* all thread states code */ #include #include #include #include #include #include #include #include #include #include #include #include /* timer id to call back on */ static unsigned pet_timerid = 0; /* aciton ID to call * We also use this as the sync point for waiting, for no good reason */ static unsigned pet_actionid = 0; /* the actual thread pointer */ static thread_t pet_thread = NULL; /* Lock on which to synchronise */ static IOLock *pet_lock = NULL; /* where to sample data to */ static struct kperf_sample pet_sample_buf; /* sample an actual, honest to god thread! */ static void pet_sample_thread( thread_t thread ) { struct kperf_context ctx; task_t task; /* work out the context */ ctx.cur_thread = thread; ctx.cur_pid = -1; task = chudxnu_task_for_thread(thread); if(task) ctx.cur_pid = chudxnu_pid_for_task(task); /* do the actual sample */ kperf_sample( &pet_sample_buf, &ctx, pet_actionid, false ); } /* given a list of threads, preferably stopped, sample 'em! */ static void pet_sample_thread_list( mach_msg_type_number_t threadc, thread_array_t threadv ) { unsigned int i; for( i = 0; i < threadc; i++ ) { thread_t thread = threadv[i]; if( !thread ) /* XXX? */ continue; pet_sample_thread( thread ); } } /* given a task (preferably stopped), sample all the threads in it */ static void pet_sample_task( task_t task ) { mach_msg_type_number_t threadc; thread_array_t threadv; kern_return_t kr; kr = chudxnu_task_threads(task, &threadv, &threadc); if( kr != KERN_SUCCESS ) { BUF_INFO2(PERF_PET_ERROR, ERR_THREAD, kr); return; } pet_sample_thread_list( threadc, threadv ); chudxnu_free_thread_list(&threadv, &threadc); } /* given a list of tasks, sample all the threads in 'em */ static void pet_sample_task_list( int taskc, task_array_t taskv ) { int i; for( i = 0; i < taskc; i++ ) { kern_return_t kr; task_t task = taskv[i]; /* FIXME: necessary? old code did this, our hacky * filtering code does, too */ if(!task) { continue; } /* try and stop any task other than the kernel task */ if( task != kernel_task ) { kr = task_suspend( task ); /* try the next task */ if( kr != KERN_SUCCESS ) continue; } /* sample it */ pet_sample_task( task ); /* if it wasn't the kernel, resume it */ if( task != kernel_task ) task_resume(task); } } static void pet_sample_all_tasks(void) { task_array_t taskv = NULL; mach_msg_type_number_t taskc = 0; kern_return_t kr; kr = chudxnu_all_tasks(&taskv, &taskc); if( kr != KERN_SUCCESS ) { BUF_INFO2(PERF_PET_ERROR, ERR_TASK, kr); return; } pet_sample_task_list( taskc, taskv ); chudxnu_free_task_list(&taskv, &taskc); } static void pet_sample_pid_filter(void) { task_t *taskv = NULL; int *pidv, pidc, i; vm_size_t asize; kperf_filter_pid_list( &pidc, &pidv ); if( pidc == 0 ) { BUF_INFO2(PERF_PET_ERROR, ERR_PID, 0); return; } asize = pidc * sizeof(task_t); taskv = kalloc( asize ); if( taskv == NULL ) goto out; /* convert the pid list into a task list */ for( i = 0; i < pidc; i++ ) { int pid = pidv[i]; if( pid == -1 ) taskv[i] = NULL; else taskv[i] = chudxnu_task_for_pid(pid); } /* now sample the task list */ pet_sample_task_list( pidc, taskv ); kfree(taskv, asize); out: kperf_filter_free_pid_list( &pidc, &pidv ); } /* do the pet sample */ static void pet_work_unit(void) { int pid_filter; /* check if we're filtering on pid */ pid_filter = kperf_filter_on_pid(); if( pid_filter ) { BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_START, 1); pet_sample_pid_filter(); } else { /* otherwise filter everything */ BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_START, 0); pet_sample_all_tasks(); } BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_END, 0); } /* sleep indefinitely */ static void pet_idle(void) { IOLockLock(pet_lock); IOLockSleep(pet_lock, &pet_actionid, THREAD_UNINT); IOLockUnlock(pet_lock); } /* loop between sampling and waiting */ static void pet_thread_loop( __unused void *param, __unused wait_result_t wr ) { BUF_INFO1(PERF_PET_THREAD, 1); while(1) { BUF_INFO1(PERF_PET_IDLE, 0); pet_idle(); BUF_INFO1(PERF_PET_RUN, 0); pet_work_unit(); /* re-program the timer */ kperf_timer_pet_set( pet_timerid ); /* FIXME: break here on a condition? */ } } /* make sure the thread takes a new period value */ void kperf_pet_timer_config( unsigned timerid, unsigned actionid ) { /* hold the lock so pet thread doesn't run while we do this */ IOLockLock(pet_lock); BUF_INFO1(PERF_PET_THREAD, 3); /* set values */ pet_timerid = timerid; pet_actionid = actionid; /* done */ IOLockUnlock(pet_lock); } /* make the thread run! */ void kperf_pet_thread_go(void) { /* Make the thread go */ IOLockWakeup(pet_lock, &pet_actionid, FALSE); } /* wait for the pet thread to finish a run */ void kperf_pet_thread_wait(void) { /* acquire the lock to ensure the thread is parked. */ IOLockLock(pet_lock); IOLockUnlock(pet_lock); } /* keep the pet thread around while we run */ int kperf_pet_init(void) { kern_return_t rc; thread_t t; if( pet_thread != NULL ) return 0; /* make the sync poing */ pet_lock = IOLockAlloc(); if( pet_lock == NULL ) return ENOMEM; /* create the thread */ BUF_INFO1(PERF_PET_THREAD, 0); rc = kernel_thread_start( pet_thread_loop, NULL, &t ); if( rc != KERN_SUCCESS ) { IOLockFree( pet_lock ); pet_lock = NULL; return ENOMEM; } /* OK! */ return 0; }