#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;
/* 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)
{
{
static lck_grp_attr_t lck_grp_attr;
- lck_mtx_assert(ktrace_lock, LCK_MTX_ASSERT_OWNED);
-
unsigned ncpus = 0;
int err;
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;
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;
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();
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)
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 */
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;
}