X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/39236c6e673c41db228275375ab7fdb0f837b292..b226f5e54a60dc81db17b1260381d7dbfea3cdf1:/osfmk/kperf/kperf.c diff --git a/osfmk/kperf/kperf.c b/osfmk/kperf/kperf.c index b1d9d4cb4..7d33d9f1b 100644 --- a/osfmk/kperf/kperf.c +++ b/osfmk/kperf/kperf.c @@ -25,183 +25,245 @@ * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -#include +#include /* port_name_to_task */ #include #include #include +#include #include +#include -#include -#include #include +#include +#include #include -#include - -#include /* port_name_to_task */ +#include +#include +#include +#include -/** misc functions **/ -#include /* XXX: should bust this out */ +/* from libkern/libkern.h */ +extern uint64_t strtouq(const char *, char **, int); -/* thread on CPUs before starting the PET thread */ -thread_t *kperf_thread_on_cpus = NULL; +lck_grp_t kperf_lck_grp; -/* interupt sample buffers -- one wired per CPU */ -static struct kperf_sample *intr_samplev = NULL; -static unsigned intr_samplec = 0; +/* IDs of threads on CPUs before starting the PET thread */ +uint64_t *kperf_tid_on_cpus = NULL; -/* track recursion in the trace code */ -static struct -{ - int active; - int pad[64 / sizeof(int)]; -} *kpdbg_recursev; -static unsigned kpdbg_recursec = 0; +/* one wired sample buffer per CPU */ +static struct kperf_sample *intr_samplev; +static unsigned int intr_samplec = 0; -/* Curren sampling status */ +/* current sampling status */ static unsigned sampling_status = KPERF_SAMPLING_OFF; -/* Make sure we only init once */ -static unsigned kperf_initted = 0; +/* only init once */ +static boolean_t kperf_initted = FALSE; -extern void (*chudxnu_thread_ast_handler)(thread_t); +/* whether or not to callback to kperf on context switch */ +boolean_t kperf_on_cpu_active = FALSE; -struct kperf_sample* -kperf_intr_sample_buffer(void) -{ - unsigned ncpu = chudxnu_cpu_number(); - - // XXX: assert? - if( ncpu >= intr_samplec ) - return NULL; +unsigned int kperf_thread_blocked_action; +unsigned int kperf_cpu_sample_action; - return &intr_samplev[ncpu]; -} - -int -kperf_kdbg_recurse(int step) +struct kperf_sample * +kperf_intr_sample_buffer(void) { - unsigned ncpu = chudxnu_cpu_number(); - - // XXX: assert? - if( ncpu >= kpdbg_recursec ) - return 1; - - /* recursing in, available */ - if( (step > 0) - && (kpdbg_recursev[ncpu].active == 0) ) - { - kpdbg_recursev[ncpu].active = 1; - return 0; - } + unsigned ncpu = cpu_number(); - /* recursing in, unavailable */ - if( (step > 0) - && (kpdbg_recursev[ncpu].active != 0) ) - { - return 1; - } - - /* recursing out, unavailable */ - if( (step < 0) - && (kpdbg_recursev[ncpu].active != 0) ) - { - kpdbg_recursev[ncpu].active = 0; - return 0; - } - - /* recursing out, available */ - if( (step < 0) - && (kpdbg_recursev[ncpu].active == 0) ) - panic( "return from non-recursed kperf kdebug call" ); + assert(ml_get_interrupts_enabled() == FALSE); + assert(ncpu < intr_samplec); - panic( "unknown kperf kdebug call" ); - return 1; + return &(intr_samplev[ncpu]); } /* setup interrupt sample buffers */ int kperf_init(void) { + static lck_grp_attr_t lck_grp_attr; + unsigned ncpus = 0; int err; - if( kperf_initted ) + if (kperf_initted) { return 0; + } + + lck_grp_attr_setdefault(&lck_grp_attr); + lck_grp_init(&kperf_lck_grp, "kperf", &lck_grp_attr); - /* get number of cpus */ ncpus = machine_info.logical_cpu_max; - kperf_thread_on_cpus = kalloc( ncpus * sizeof(*kperf_thread_on_cpus) ); - if( kperf_thread_on_cpus == NULL ) - { + /* create buffers to remember which threads don't need to be sampled by PET */ + kperf_tid_on_cpus = kalloc_tag(ncpus * sizeof(*kperf_tid_on_cpus), + VM_KERN_MEMORY_DIAG); + if (kperf_tid_on_cpus == NULL) { err = ENOMEM; goto error; } + bzero(kperf_tid_on_cpus, ncpus * sizeof(*kperf_tid_on_cpus)); - /* clear it */ - bzero( kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus) ); - - /* make the CPU array - * FIXME: cache alignment - */ - intr_samplev = kalloc( ncpus * sizeof(*intr_samplev)); + /* create the interrupt buffers */ intr_samplec = ncpus; - - if( intr_samplev == NULL ) - { + intr_samplev = kalloc_tag(ncpus * sizeof(*intr_samplev), + VM_KERN_MEMORY_DIAG); + if (intr_samplev == NULL) { err = ENOMEM; goto error; } + bzero(intr_samplev, ncpus * sizeof(*intr_samplev)); - /* clear it */ - bzero( intr_samplev, ncpus * sizeof(*intr_samplev) ); + /* create kdebug trigger filter buffers */ + if ((err = kperf_kdebug_init())) { + goto error; + } - /* make the recursion array */ - kpdbg_recursev = kalloc( ncpus * sizeof(*kpdbg_recursev)); - kpdbg_recursec = ncpus; + kperf_initted = TRUE; + return 0; - /* clear it */ - bzero( kpdbg_recursev, ncpus * sizeof(*kpdbg_recursev) ); +error: + if (intr_samplev) { + kfree(intr_samplev, ncpus * sizeof(*intr_samplev)); + intr_samplev = NULL; + intr_samplec = 0; + } - /* we're done */ - kperf_initted = 1; + if (kperf_tid_on_cpus) { + kfree(kperf_tid_on_cpus, ncpus * sizeof(*kperf_tid_on_cpus)); + kperf_tid_on_cpus = NULL; + } - return 0; -error: - if( intr_samplev ) - kfree( intr_samplev, ncpus * sizeof(*intr_samplev) ); - if( kperf_thread_on_cpus ) - kfree( kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus) ); return err; } -/* random misc-ish functions */ -uint32_t -kperf_get_thread_bits( thread_t thread ) +void +kperf_reset(void) { - return thread->t_chud; + /* turn off sampling first */ + (void)kperf_sampling_disable(); + + /* cleanup miscellaneous configuration first */ + kperf_lazy_reset(); + (void)kperf_kdbg_cswitch_set(0); + (void)kperf_set_lightweight_pet(0); + kperf_kdebug_reset(); + + /* timers, which require actions, first */ + kperf_timer_reset(); + kperf_action_reset(); } void -kperf_set_thread_bits( thread_t thread, uint32_t bits ) +kperf_kernel_configure(const char *config) { - thread->t_chud = bits; + int pairs = 0; + char *end; + bool pet = false; + + assert(config != NULL); + + ktrace_start_single_threaded(); + + ktrace_kernel_configure(KTRACE_KPERF); + + if (config[0] == 'p') { + pet = true; + config++; + } + + do { + uint32_t action_samplers; + uint64_t timer_period_ns; + uint64_t timer_period; + + pairs += 1; + kperf_action_set_count(pairs); + kperf_timer_set_count(pairs); + + action_samplers = (uint32_t)strtouq(config, &end, 0); + if (config == end) { + kprintf("kperf: unable to parse '%s' as action sampler\n", config); + goto out; + } + config = end; + + kperf_action_set_samplers(pairs, action_samplers); + + if (config[0] == '\0') { + kprintf("kperf: missing timer period in config\n"); + goto out; + } + config++; + + timer_period_ns = strtouq(config, &end, 0); + if (config == end) { + kprintf("kperf: unable to parse '%s' as timer period\n", config); + goto out; + } + nanoseconds_to_absolutetime(timer_period_ns, &timer_period); + config = end; + + kperf_timer_set_period(pairs - 1, timer_period); + kperf_timer_set_action(pairs - 1, pairs); + + if (pet) { + kperf_timer_set_petid(pairs - 1); + kperf_set_lightweight_pet(1); + pet = false; + } + } while (*(config++) == ','); + + int error = kperf_sampling_enable(); + if (error) { + kprintf("kperf: cannot enable sampling at boot: %d", error); + } + +out: + ktrace_end_single_threaded(); } -/* mark an AST to fire on a thread */ +void kperf_on_cpu_internal(thread_t thread, thread_continue_t continuation, + uintptr_t *starting_fp); void -kperf_set_thread_ast( thread_t thread ) +kperf_on_cpu_internal(thread_t thread, thread_continue_t continuation, + uintptr_t *starting_fp) { - /* FIXME: only call this on current thread from an interrupt - * handler for now... - */ - if( thread != current_thread() ) - panic( "unsafe AST set" ); + if (kperf_kdebug_cswitch) { + /* trace the new thread's PID for Instruments */ + int pid = task_pid(get_threadtask(thread)); + BUF_DATA(PERF_TI_CSWITCH, thread_tid(thread), pid); + } + if (kperf_lightweight_pet_active) { + kperf_pet_on_cpu(thread, continuation, starting_fp); + } + if (kperf_lazy_wait_action != 0) { + kperf_lazy_wait_sample(thread, continuation, starting_fp); + } +} + +void +kperf_on_cpu_update(void) +{ + kperf_on_cpu_active = kperf_kdebug_cswitch || + kperf_lightweight_pet_active || + kperf_lazy_wait_action != 0; +} - act_set_kperf(thread); +/* random misc-ish functions */ +uint32_t +kperf_get_thread_flags(thread_t thread) +{ + return thread->kperf_flags; } -unsigned +void +kperf_set_thread_flags(thread_t thread, uint32_t flags) +{ + thread->kperf_flags = flags; +} + +unsigned int kperf_sampling_status(void) { return sampling_status; @@ -210,20 +272,22 @@ kperf_sampling_status(void) int kperf_sampling_enable(void) { - /* already running! */ - if( sampling_status == KPERF_SAMPLING_ON ) + if (sampling_status == KPERF_SAMPLING_ON) { return 0; + } - if ( sampling_status != KPERF_SAMPLING_OFF ) - panic( "kperf: sampling wasn't off" ); + if (sampling_status != KPERF_SAMPLING_OFF) { + panic("kperf: sampling was %d when asked to enable", sampling_status); + } /* make sure interrupt tables and actions are initted */ - if( !kperf_initted - || (kperf_action_get_count() == 0) ) + if (!kperf_initted || (kperf_action_get_count() == 0)) { return ECANCELED; + } /* mark as running */ sampling_status = KPERF_SAMPLING_ON; + kperf_lightweight_pet_active_update(); /* tell timers to enable */ kperf_timer_go(); @@ -234,8 +298,9 @@ kperf_sampling_enable(void) int kperf_sampling_disable(void) { - if( sampling_status != KPERF_SAMPLING_ON ) + if (sampling_status != KPERF_SAMPLING_ON) { return 0; + } /* mark a shutting down */ sampling_status = KPERF_SAMPLING_SHUTDOWN; @@ -245,28 +310,41 @@ kperf_sampling_disable(void) /* mark as off */ sampling_status = KPERF_SAMPLING_OFF; + kperf_lightweight_pet_active_update(); return 0; } +boolean_t +kperf_thread_get_dirty(thread_t thread) +{ + return (thread->c_switch != thread->kperf_c_switch); +} + +void +kperf_thread_set_dirty(thread_t thread, boolean_t dirty) +{ + if (dirty) { + thread->kperf_c_switch = thread->c_switch - 1; + } else { + thread->kperf_c_switch = thread->c_switch; + } +} + int kperf_port_to_pid(mach_port_name_t portname) { - task_t task; - int pid; - - if( !MACH_PORT_VALID(portname) ) + if (!MACH_PORT_VALID(portname)) { return -1; + } - task = port_name_to_task(portname); - - if( task == TASK_NULL ) + task_t task = port_name_to_task(portname); + if (task == TASK_NULL) { return -1; - - - pid = chudxnu_pid_for_task(task); - - task_deallocate_internal(task); + } + pid_t pid = task_pid(task); + /* drop the ref taken by port_name_to_task */ + (void)task_deallocate_internal(task); return pid; }