#include <mach/mach_types.h>
#include <IOKit/IOTypes.h>
#include <IOKit/IOLocks.h>
#include <sys/errno.h>
#include <chud/chud_xnu.h>
#include <kperf/buffer.h>
#include <kperf/sample.h>
#include <kperf/context.h>
#include <kperf/action.h>
#include <kperf/pet.h>
#include <kperf/timetrigger.h>
static unsigned pet_timerid = 0;
static unsigned pet_actionid = 0;
static thread_t pet_thread = NULL;
static IOLock *pet_lock = NULL;
static struct kperf_sample pet_sample_buf;
static int pet_idle_rate = 15;
static void
pet_sample_thread( thread_t thread )
{
struct kperf_context ctx;
task_t task;
unsigned skip_callstack;
ctx.cur_thread = thread;
ctx.cur_pid = 0;
task = chudxnu_task_for_thread(thread);
if(task)
ctx.cur_pid = chudxnu_pid_for_task(task);
skip_callstack = (chudxnu_thread_get_dirty(thread) == TRUE) || ((thread->kperf_pet_cnt % (uint64_t)pet_idle_rate) == 0) ? 0 : SAMPLE_FLAG_EMPTY_CALLSTACK;
kperf_sample( &pet_sample_buf, &ctx, pet_actionid,
SAMPLE_FLAG_IDLE_THREADS | skip_callstack );
if (!skip_callstack)
chudxnu_thread_set_dirty(thread, FALSE);
thread->kperf_pet_cnt++;
}
static void
pet_sample_thread_list( mach_msg_type_number_t threadc, thread_array_t threadv )
{
unsigned int i;
int ncpu;
for( i = 0; i < threadc; i++ )
{
thread_t thread = threadv[i];
if( !thread )
continue;
for (ncpu = 0; ncpu < machine_info.logical_cpu_max; ++ncpu)
{
thread_t candidate = kperf_thread_on_cpus[ncpu];
if (candidate && candidate->thread_id == thread->thread_id)
break;
}
if (ncpu == machine_info.logical_cpu_max)
pet_sample_thread( thread );
}
}
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);
}
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];
if(!task) {
continue;
}
if( task != kernel_task )
{
kr = task_suspend( task );
if( kr != KERN_SUCCESS )
continue;
}
pet_sample_task( task );
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);
}
#if 0
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;
for( i = 0; i < pidc; i++ )
{
int pid = pidv[i];
if( pid == -1 )
taskv[i] = NULL;
else
taskv[i] = chudxnu_task_for_pid(pid);
}
pet_sample_task_list( pidc, taskv );
kfree(taskv, asize);
out:
kperf_filter_free_pid_list( &pidc, &pidv );
}
#endif
static void
pet_work_unit(void)
{
int pid_filter;
pid_filter = 0;
#if 0
if( pid_filter )
{
BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_START, 1);
pet_sample_pid_filter();
}
else
#endif
{
BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_START, 0);
pet_sample_all_tasks();
}
BUF_INFO1(PERF_PET_SAMPLE | DBG_FUNC_END, 0);
}
static void
pet_idle(void)
{
IOLockSleep(pet_lock, &pet_actionid, THREAD_UNINT);
}
static void
pet_thread_loop( __unused void *param, __unused wait_result_t wr )
{
uint64_t work_unit_ticks;
BUF_INFO1(PERF_PET_THREAD, 1);
IOLockLock(pet_lock);
while(1)
{
BUF_INFO1(PERF_PET_IDLE, 0);
pet_idle();
BUF_INFO1(PERF_PET_RUN, 0);
work_unit_ticks = mach_absolute_time();
pet_work_unit();
work_unit_ticks = mach_absolute_time() - work_unit_ticks;
kperf_timer_pet_set( pet_timerid, work_unit_ticks );
}
}
void
kperf_pet_timer_config( unsigned timerid, unsigned actionid )
{
if( !pet_lock )
return;
IOLockLock(pet_lock);
BUF_INFO1(PERF_PET_THREAD, 3);
pet_timerid = timerid;
pet_actionid = actionid;
IOLockUnlock(pet_lock);
}
void
kperf_pet_thread_go(void)
{
if( !pet_lock )
return;
IOLockWakeup(pet_lock, &pet_actionid, FALSE);
}
void
kperf_pet_thread_wait(void)
{
if( !pet_lock )
return;
IOLockLock(pet_lock);
IOLockUnlock(pet_lock);
}
int
kperf_pet_init(void)
{
kern_return_t rc;
thread_t t;
if( pet_thread != NULL )
return 0;
pet_lock = IOLockAlloc();
if( pet_lock == NULL )
return ENOMEM;
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;
}
return 0;
}
int
kperf_get_pet_idle_rate( void )
{
return pet_idle_rate;
}
void
kperf_set_pet_idle_rate( int val )
{
pet_idle_rate = val;
}