]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/kperf/kperf.c
xnu-4903.241.1.tar.gz
[apple/xnu.git] / osfmk / kperf / kperf.c
index 45d441e5f813b66dd0d2c86c49ddd4932d09827d..7d33d9f1b96a788c255d55b3b26126b7c5944633 100644 (file)
 #include <kperf/kdebug_trigger.h>
 #include <kperf/kperf.h>
 #include <kperf/kperf_timer.h>
+#include <kperf/lazy.h>
 #include <kperf/pet.h>
 #include <kperf/sample.h>
 
+/* from libkern/libkern.h */
+extern uint64_t strtouq(const char *, char **, int);
+
 lck_grp_t kperf_lck_grp;
 
-/* thread on CPUs before starting the PET thread */
-thread_t *kperf_thread_on_cpus = NULL;
+/* IDs of threads on CPUs before starting the PET thread */
+uint64_t *kperf_tid_on_cpus = NULL;
 
 /* one wired sample buffer per CPU */
 static struct kperf_sample *intr_samplev;
@@ -59,6 +63,9 @@ static boolean_t kperf_initted = FALSE;
 /* whether or not to callback to kperf on context switch */
 boolean_t kperf_on_cpu_active = FALSE;
 
+unsigned int kperf_thread_blocked_action;
+unsigned int kperf_cpu_sample_action;
+
 struct kperf_sample *
 kperf_intr_sample_buffer(void)
 {
@@ -76,8 +83,6 @@ kperf_init(void)
 {
        static lck_grp_attr_t lck_grp_attr;
 
-       lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
-
        unsigned ncpus = 0;
        int err;
 
@@ -91,13 +96,13 @@ kperf_init(void)
        ncpus = machine_info.logical_cpu_max;
 
        /* create buffers to remember which threads don't need to be sampled by PET */
-       kperf_thread_on_cpus = kalloc_tag(ncpus * sizeof(*kperf_thread_on_cpus),
+       kperf_tid_on_cpus = kalloc_tag(ncpus * sizeof(*kperf_tid_on_cpus),
                                          VM_KERN_MEMORY_DIAG);
-       if (kperf_thread_on_cpus == NULL) {
+       if (kperf_tid_on_cpus == NULL) {
                err = ENOMEM;
                goto error;
        }
-       bzero(kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus));
+       bzero(kperf_tid_on_cpus, ncpus * sizeof(*kperf_tid_on_cpus));
 
        /* create the interrupt buffers */
        intr_samplec = ncpus;
@@ -124,9 +129,9 @@ error:
                intr_samplec = 0;
        }
 
-       if (kperf_thread_on_cpus) {
-               kfree(kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus));
-               kperf_thread_on_cpus = NULL;
+       if (kperf_tid_on_cpus) {
+               kfree(kperf_tid_on_cpus, ncpus * sizeof(*kperf_tid_on_cpus));
+               kperf_tid_on_cpus = NULL;
        }
 
        return err;
@@ -135,12 +140,11 @@ error:
 void
 kperf_reset(void)
 {
-       lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
-
        /* 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();
@@ -150,6 +154,77 @@ kperf_reset(void)
        kperf_action_reset();
 }
 
+void
+kperf_kernel_configure(const char *config)
+{
+       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();
+}
+
+void kperf_on_cpu_internal(thread_t thread, thread_continue_t continuation,
+               uintptr_t *starting_fp);
 void
 kperf_on_cpu_internal(thread_t thread, thread_continue_t continuation,
                       uintptr_t *starting_fp)
@@ -157,19 +232,22 @@ kperf_on_cpu_internal(thread_t thread, thread_continue_t continuation,
        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_lightweight_pet_active ||
+                             kperf_lazy_wait_action != 0;
 }
 
 /* random misc-ish functions */
@@ -256,22 +334,17 @@ kperf_thread_set_dirty(thread_t thread, boolean_t dirty)
 int
 kperf_port_to_pid(mach_port_name_t portname)
 {
-       task_t task;
-       int pid;
-
        if (!MACH_PORT_VALID(portname)) {
                return -1;
        }
 
-       task = port_name_to_task(portname);
-
+       task_t task = port_name_to_task(portname);
        if (task == TASK_NULL) {
                return -1;
        }
-
-       pid = task_pid(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;
 }