]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/kperf/kperfbsd.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / osfmk / kperf / kperfbsd.c
index 2a6554ab62af22aef6eec65a51ec96e5497c9afd..1b3ab5f5da527e3985afa4fe3102d223985fbfda 100644 (file)
@@ -2,7 +2,7 @@
  * 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
  * 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,
  * 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@
  */
 
 /*  sysctl interface for paramters from user-land */
 
+#include <kern/debug.h>
+#include <libkern/libkern.h>
+#include <pexpert/pexpert.h>
 #include <sys/param.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/sysctl.h>
 #include <sys/kauth.h>
-#include <libkern/libkern.h>
-#include <kern/debug.h>
-#include <pexpert/pexpert.h>
 
-#include <kperf/context.h>
 #include <kperf/action.h>
-#include <kperf/timetrigger.h>
-#include <kperf/pet.h>
-#include <kperf/kperfbsd.h>
+#include <kperf/context.h>
+#include <kperf/kdebug_trigger.h>
 #include <kperf/kperf.h>
+#include <kperf/kperfbsd.h>
+#include <kperf/kperf_timer.h>
+#include <kperf/pet.h>
 
-
-/* a pid which is allowed to control kperf without requiring root access */
-static pid_t blessed_pid = -1;
-static boolean_t blessed_preempt = FALSE;
+#include <sys/ktrace.h>
 
 /* IDs for dispatch from SYSCTL macros */
-#define REQ_SAMPLING        (1)
-#define REQ_ACTION_COUNT    (2)
-#define REQ_ACTION_SAMPLERS (3)
-#define REQ_TIMER_COUNT     (4)
-#define REQ_TIMER_PERIOD    (5)
-#define REQ_TIMER_PET       (6)
-#define REQ_TIMER_ACTION    (7)
-#define REQ_BLESS           (8)
-#define REQ_ACTION_USERDATA (9)
-#define REQ_ACTION_FILTER_BY_TASK (10)
-#define REQ_ACTION_FILTER_BY_PID  (11)
-#define REQ_KDBG_CALLSTACKS (12)
-#define REQ_PET_IDLE_RATE   (13)
-#define REQ_BLESS_PREEMPT   (14)
-#define REQ_KDBG_CSWITCH    (15)
-#define REQ_CSWITCH_ACTION  (16)
-#define REQ_SIGNPOST_ACTION (17)
-
-/* simple state variables */
-int kperf_debug_level = 0;
+#define REQ_SAMPLING                (1)
+#define REQ_ACTION_COUNT            (2)
+#define REQ_ACTION_SAMPLERS         (3)
+#define REQ_TIMER_COUNT             (4)
+#define REQ_TIMER_PERIOD            (5)
+#define REQ_TIMER_PET               (6)
+#define REQ_TIMER_ACTION            (7)
+#define REQ_BLESS                   (8)
+#define REQ_ACTION_USERDATA         (9)
+#define REQ_ACTION_FILTER_BY_TASK   (10)
+#define REQ_ACTION_FILTER_BY_PID    (11)
+/* 12 unused */
+#define REQ_PET_IDLE_RATE           (13)
+#define REQ_BLESS_PREEMPT           (14)
+#define REQ_KDBG_CSWITCH            (15)
+#define REQ_RESET                   (16)
+/* 17 unused */
+#define REQ_ACTION_UCALLSTACK_DEPTH (18)
+#define REQ_ACTION_KCALLSTACK_DEPTH (19)
+#define REQ_LIGHTWEIGHT_PET         (20)
+#define REQ_KDEBUG_ACTION           (21)
+#define REQ_KDEBUG_FILTER           (22)
 
-static lck_grp_attr_t *kperf_cfg_lckgrp_attr = NULL;
-static lck_grp_t      *kperf_cfg_lckgrp = NULL;
-static lck_mtx_t       kperf_cfg_lock;
-static boolean_t       kperf_cfg_initted = FALSE;
+int kperf_debug_level = 0;
 
-void kdbg_swap_global_state_pid(pid_t old_pid, pid_t new_pid); /* bsd/kern/kdebug.c */
+#if DEVELOPMENT || DEBUG
+_Atomic long long kperf_pending_ipis = 0;
+#endif /* DEVELOPMENT || DEBUG */
 
-/***************************
+/*
+ * kperf has a different sysctl model than others.
+ *
+ * For simple queries like the number of actions, the normal sysctl style
+ * of get/set works well.
  *
- * lock init
+ * However, when requesting information about something specific, like an
+ * action, user space needs to provide some contextual information.  This
+ * information is stored in a uint64_t array that includes the context, like
+ * the action ID it is interested in.  If user space is getting the value from
+ * the kernel, then the get side of the sysctl is valid.  If it is setting the
+ * value, then the get pointers are left NULL.
  *
- ***************************/
+ * These functions handle marshalling and unmarshalling data from sysctls.
+ */
 
-void
-kperf_bootstrap(void)
+static int
+kperf_sysctl_get_set_uint32(struct sysctl_req *req,
+       uint32_t (*get)(void), int (*set)(uint32_t))
 {
-       kperf_cfg_lckgrp_attr = lck_grp_attr_alloc_init();
-       kperf_cfg_lckgrp = lck_grp_alloc_init("kperf cfg", 
-                                          kperf_cfg_lckgrp_attr);
-       lck_mtx_init(&kperf_cfg_lock, kperf_cfg_lckgrp, LCK_ATTR_NULL);
+       assert(req != NULL);
+       assert(get != NULL);
+       assert(set != NULL);
 
-       kperf_cfg_initted = TRUE;
-}
+       uint32_t value = 0;
+       if (req->oldptr) {
+               value = get();
+       }
 
-/***************************
- *
- * sysctl handlers
- *
- ***************************/
+       int error = sysctl_io_number(req, value, sizeof(value), &value, NULL);
+
+       if (error || !req->newptr) {
+               return error;
+       }
+
+       return set(value);
+}
 
 static int
-sysctl_timer_period( __unused struct sysctl_oid *oidp, struct sysctl_req *req )
+kperf_sysctl_get_set_int(struct sysctl_req *req,
+       int (*get)(void), int (*set)(int))
 {
-    int error = 0;
-    uint64_t inputs[2], retval;
-    unsigned timer, set = 0;
-    
-    /* get 2x 64-bit words */
-    error = SYSCTL_IN( req, inputs, 2*sizeof(inputs[0]) );
-    if(error)
-           return (error);
-
-    /* setup inputs */
-    timer = (unsigned) inputs[0];
-    if( inputs[1] != ~0ULL )
-           set = 1;
-
-    if( set )
-    {
-           error = kperf_timer_set_period( timer, inputs[1] );
-           if( error )
-                   return error;
-    }
+       assert(req != NULL);
+       assert(get != NULL);
+       assert(set != NULL);
 
-    error = kperf_timer_get_period(timer, &retval);
-    if(error)
-           return (error);
+       int value = 0;
+       if (req->oldptr) {
+               value = get();
+       }
 
-    inputs[1] = retval;
-    
-    if( error == 0 )
-           error = SYSCTL_OUT( req, inputs, 2*sizeof(inputs[0]) );
+       int error = sysctl_io_number(req, value, sizeof(value), &value, NULL);
 
-    return error;
+       if (error || !req->newptr) {
+               return error;
+       }
+
+       return set(value);
 }
 
 static int
-sysctl_timer_action( __unused struct sysctl_oid *oidp, struct sysctl_req *req )
+kperf_sysctl_get_set_unsigned_uint32(struct sysctl_req *req,
+       int (*get)(unsigned int, uint32_t *), int (*set)(unsigned int, uint32_t))
 {
-    int error = 0;
-    uint64_t inputs[2];
-    uint32_t retval;
-    unsigned timer, set = 0;
-    
-    /* get 2x 64-bit words */
-    error = SYSCTL_IN( req, inputs, 2*sizeof(inputs[0]) );
-    if(error)
-           return (error);
-
-    /* setup inputs */
-    timer = (unsigned) inputs[0];
-    if( inputs[1] != ~0ULL )
-           set = 1;
-
-    if( set )
-    {
-           error = kperf_timer_set_action( timer, inputs[1] );
-           if( error )
-                   return error;
-    }
+       assert(req != NULL);
+       assert(get != NULL);
+       assert(set != NULL);
+
+       int error;
+       uint64_t inputs[2];
+       if ((error = SYSCTL_IN(req, inputs, sizeof(inputs)))) {
+               return error;
+       }
+
+       unsigned int action_id = (unsigned int)inputs[0];
+       uint32_t new_value = (uint32_t)inputs[1];
 
-    error = kperf_timer_get_action(timer, &retval);
-    if(error)
-           return (error);
+       if (req->oldptr != USER_ADDR_NULL) {
+               uint32_t value_out = 0;
+               if ((error = get(action_id, &value_out))) {
+                       return error;
+               }
 
-    inputs[1] = retval;
-    
-    if( error == 0 )
-           error = SYSCTL_OUT( req, inputs, 2*sizeof(inputs[0]) );
+               inputs[1] = value_out;
+       } else {
+               if ((error = set(action_id, new_value))) {
+                       return error;
+               }
+       }
 
-    return error;
+       if (req->oldptr != USER_ADDR_NULL) {
+               error =  SYSCTL_OUT(req, inputs, sizeof(inputs));
+               return error;
+       } else {
+               return 0;
+       }
 }
 
+/*
+ * These functions are essentially the same as the generic
+ * kperf_sysctl_get_set_unsigned_uint32, except they have unique input sizes.
+ */
+
 static int
-sysctl_action_samplers( __unused struct sysctl_oid *oidp, 
-                        struct sysctl_req *req )
+sysctl_timer_period(struct sysctl_req *req)
 {
-    int error = 0;
-    uint64_t inputs[3];
-    uint32_t retval;
-    unsigned actionid, set = 0;
-    
-    /* get 3x 64-bit words */
-    error = SYSCTL_IN( req, inputs, 3*sizeof(inputs[0]) );
-    if(error)
-           return (error);
-
-    /* setup inputs */
-    set = (unsigned) inputs[0];
-    actionid = (unsigned) inputs[1];
-
-    if( set )
-    {
-           error = kperf_action_set_samplers( actionid, inputs[2] );
-           if( error )
-                   return error;
-    }
+       assert(req != NULL);
 
-    error = kperf_action_get_samplers(actionid, &retval);
-    if(error)
-           return (error);
+       int error;
+       uint64_t inputs[2];
+       if ((error = SYSCTL_IN(req, inputs, sizeof(inputs)))) {
+               return error;
+       }
+
+       unsigned int timer = (unsigned int)inputs[0];
+       uint64_t new_period = inputs[1];
 
-    inputs[2] = retval;
-    
-    if( error == 0 )
-           error = SYSCTL_OUT( req, inputs, 3*sizeof(inputs[0]) );
+       if (req->oldptr != USER_ADDR_NULL) {
+               uint64_t period_out = 0;
+               if ((error = kperf_timer_get_period(timer, &period_out))) {
+                       return error;
+               }
+
+               inputs[1] = period_out;
+       } else {
+               if ((error = kperf_timer_set_period(timer, new_period))) {
+                       return error;
+               }
+       }
 
-    return error;
+       return SYSCTL_OUT(req, inputs, sizeof(inputs));
 }
 
 static int
-sysctl_action_userdata( __unused struct sysctl_oid *oidp, 
-                        struct sysctl_req *req )
+sysctl_action_filter(struct sysctl_req *req, boolean_t is_task_t)
 {
-    int error = 0;
-    uint64_t inputs[3];
-    uint32_t retval;
-    unsigned actionid, set = 0;
-    
-    /* get 3x 64-bit words */
-    error = SYSCTL_IN( req, inputs, 3*sizeof(inputs[0]) );
-    if(error)
-           return (error);
-
-    /* setup inputs */
-    set = (unsigned) inputs[0];
-    actionid = (unsigned) inputs[1];
-
-    if( set )
-    {
-           error = kperf_action_set_userdata( actionid, inputs[2] );
-           if( error )
-                   return error;
-    }
+       assert(req != NULL);
+
+       int error;
+       uint64_t inputs[2];
+       if ((error = SYSCTL_IN(req, inputs, sizeof(inputs)))) {
+               return error;
+       }
+
+       unsigned int actionid = (unsigned int)inputs[0];
+       int new_filter = (int)inputs[1];
 
-    error = kperf_action_get_userdata(actionid, &retval);
-    if(error)
-           return (error);
+       if (req->oldptr != USER_ADDR_NULL) {
+               int filter_out;
+               if ((error = kperf_action_get_filter(actionid, &filter_out))) {
+                       return error;
+               }
+
+               inputs[1] = filter_out;
+       } else {
+               int pid = is_task_t ? kperf_port_to_pid((mach_port_name_t)new_filter)
+                                   : new_filter;
 
-    inputs[2] = retval;
-    
-    if( error == 0 )
-           error = SYSCTL_OUT( req, inputs, 3*sizeof(inputs[0]) );
+               if ((error = kperf_action_set_filter(actionid, pid))) {
+                   return error;
+               }
+       }
 
-    return error;
+       return SYSCTL_OUT(req, inputs, sizeof(inputs));
 }
 
 static int
-sysctl_action_filter( __unused struct sysctl_oid *oidp,
-                     struct sysctl_req *req, int is_task_t )
+sysctl_bless(struct sysctl_req *req)
 {
-    int error = 0;
-    uint64_t inputs[3];
-    int retval;
-    unsigned actionid, set = 0;
-    mach_port_name_t portname;
-    int pid;
-
-    /* get 3x 64-bit words */
-    error = SYSCTL_IN( req, inputs, 3*sizeof(inputs[0]) );
-    if(error)
-           return (error);
-
-    /* setup inputs */
-    set = (unsigned) inputs[0];
-    actionid = (unsigned) inputs[1];
-
-    if( set )
-    {
-           if( is_task_t )
-           {
-                   portname = (mach_port_name_t) inputs[2];
-                   pid = kperf_port_to_pid(portname);
-           }
-           else
-                   pid = (int) inputs[2];
-
-           error = kperf_action_set_filter( actionid, pid );
-           if( error )
-                   return error;
-    }
-
-    error = kperf_action_get_filter(actionid, &retval);
-    if(error)
-           return (error);
+       int value = ktrace_get_owning_pid();
+       int error = sysctl_io_number(req, value, sizeof(value), &value, NULL);
 
-    inputs[2] = retval;
-    
-    if( error == 0 )
-           error = SYSCTL_OUT( req, inputs, 3*sizeof(inputs[0]) );
+       if (error || !req->newptr) {
+               return error;
+       }
 
-    return error;
+       return ktrace_set_owning_pid(value);
 }
 
+/* sysctl handlers that use the generic functions */
+
 static int
-sysctl_sampling( struct sysctl_oid *oidp, struct sysctl_req *req )
+sysctl_action_samplers(struct sysctl_req *req)
 {
-    int error = 0;
-    uint32_t value = 0;
-    
-    /* get the old value and process it */
-    value = kperf_sampling_status();
-
-    /* copy out the old value, get the new value */
-    error = sysctl_handle_int(oidp, &value, 0, req);
-    if (error || !req->newptr)
-           return (error);
-
-    /* if that worked, and we're writing... */
-    if( value )
-           error = kperf_sampling_enable();
-    else
-           error = kperf_sampling_disable();
-
-    return error;
+       return kperf_sysctl_get_set_unsigned_uint32(req,
+               kperf_action_get_samplers, kperf_action_set_samplers);
 }
 
 static int
-sysctl_action_count( struct sysctl_oid *oidp, struct sysctl_req *req )
+sysctl_action_userdata(struct sysctl_req *req)
 {
-    int error = 0;
-    uint32_t value = 0;
-    
-    /* get the old value and process it */
-    value = kperf_action_get_count();
-
-    /* copy out the old value, get the new value */
-    error = sysctl_handle_int(oidp, &value, 0, req);
-    if (error || !req->newptr)
-           return (error);
-
-    /* if that worked, and we're writing... */
-    return kperf_action_set_count(value);
+       return kperf_sysctl_get_set_unsigned_uint32(req,
+               kperf_action_get_userdata, kperf_action_set_userdata);
 }
 
 static int
-sysctl_timer_count( struct sysctl_oid *oidp, struct sysctl_req *req )
+sysctl_action_ucallstack_depth(struct sysctl_req *req)
 {
-    int error = 0;
-    uint32_t value = 0;
-    
-    /* get the old value and process it */
-    value = kperf_timer_get_count();
-
-    /* copy out the old value, get the new value */
-    error = sysctl_handle_int(oidp, &value, 0, req);
-    if (error || !req->newptr)
-           return (error);
-
-    /* if that worked, and we're writing... */
-    return kperf_timer_set_count(value);
+       return kperf_sysctl_get_set_unsigned_uint32(req,
+               kperf_action_get_ucallstack_depth, kperf_action_set_ucallstack_depth);
 }
 
 static int
-sysctl_timer_pet( struct sysctl_oid *oidp, struct sysctl_req *req )
+sysctl_action_kcallstack_depth(struct sysctl_req *req)
 {
-    int error = 0;
-    uint32_t value = 0;
-    
-    /* get the old value and process it */
-    value = kperf_timer_get_petid();
-
-    /* copy out the old value, get the new value */
-    error = sysctl_handle_int(oidp, &value, 0, req);
-    if (error || !req->newptr)
-           return (error);
-
-    /* if that worked, and we're writing... */
-    return kperf_timer_set_petid(value);
+       return kperf_sysctl_get_set_unsigned_uint32(req,
+               kperf_action_get_kcallstack_depth, kperf_action_set_kcallstack_depth);
 }
 
 static int
-sysctl_bless( struct sysctl_oid *oidp, struct sysctl_req *req )
+sysctl_kdebug_action(struct sysctl_req *req)
 {
-    int error = 0;
-    int value = 0;
-
-    /* get the old value and process it */
-    value = blessed_pid;
-
-    /* copy out the old value, get the new value */
-    error = sysctl_handle_int(oidp, &value, 0, req);
-    if (error || !req->newptr)
-           return (error);
-
-    /* if that worked, and we're writing... */
-    error = kperf_bless_pid(value);
-
-    return error;
+       return kperf_sysctl_get_set_int(req, kperf_kdebug_get_action,
+               kperf_kdebug_set_action);
 }
 
 static int
-sysctl_bless_preempt( struct sysctl_oid *oidp, struct sysctl_req *req )
+sysctl_kdebug_filter(struct sysctl_req *req)
 {
-    int error = 0;
-    int value = 0;
+       assert(req != NULL);
 
-    /* get the old value and process it */
-    value = blessed_preempt;
+       if (req->oldptr != USER_ADDR_NULL) {
+               struct kperf_kdebug_filter *filter = NULL;
+               uint32_t n_debugids = kperf_kdebug_get_filter(&filter);
+               size_t filter_size = KPERF_KDEBUG_FILTER_SIZE(n_debugids);
 
-    /* copy out the old value, get the new value */
-    error = sysctl_handle_int(oidp, &value, 0, req);
-    if (error || !req->newptr)
-           return (error);
+               if (n_debugids == 0) {
+                       return EINVAL;
+               }
 
-    /* if that worked, and we're writing... */
-    blessed_preempt = value ? TRUE : FALSE;
+               return SYSCTL_OUT(req, filter, filter_size);
+       }
 
-    return 0;
+       return kperf_kdebug_set_filter(req->newptr, (uint32_t)req->newlen);
 }
 
-
 static int
-sysctl_kdbg_callstacks( struct sysctl_oid *oidp, struct sysctl_req *req )
+kperf_sampling_set(uint32_t sample_start)
 {
-    int error = 0;
-    int value = 0;
-    
-    /* get the old value and process it */
-    value = kperf_kdbg_get_stacks();
-
-    /* copy out the old value, get the new value */
-    error = sysctl_handle_int(oidp, &value, 0, req);
-    if (error || !req->newptr)
-           return (error);
-
-    /* if that worked, and we're writing... */
-    error = kperf_kdbg_set_stacks(value);
-
-    return error;
+       if (sample_start) {
+               return kperf_sampling_enable();
+       } else {
+               return kperf_sampling_disable();
+       }
 }
 
 static int
-sysctl_pet_idle_rate( struct sysctl_oid *oidp, struct sysctl_req *req )
+sysctl_sampling(struct sysctl_req *req)
 {
-    int error = 0;
-    int value = 0;
-    
-    /* get the old value and process it */
-    value = kperf_get_pet_idle_rate();
-
-    /* copy out the old value, get the new value */
-    error = sysctl_handle_int(oidp, &value, 0, req);
-    if (error || !req->newptr)
-           return (error);
+       return kperf_sysctl_get_set_uint32(req, kperf_sampling_status,
+               kperf_sampling_set);
+}
 
-    /* if that worked, and we're writing... */
-    kperf_set_pet_idle_rate(value);
+static int
+sysctl_action_count(struct sysctl_req *req)
+{
+       return kperf_sysctl_get_set_uint32(req, kperf_action_get_count,
+               kperf_action_set_count);
+}
 
-    return error;
+static int
+sysctl_timer_count(struct sysctl_req *req)
+{
+       return kperf_sysctl_get_set_uint32(req, kperf_timer_get_count,
+               kperf_timer_set_count);
 }
 
 static int
-sysctl_kdbg_cswitch( struct sysctl_oid *oidp, struct sysctl_req *req )
+sysctl_timer_action(struct sysctl_req *req)
 {
-    int value = kperf_kdbg_cswitch_get();
-    int error = sysctl_handle_int(oidp, &value, 0, req);
+       return kperf_sysctl_get_set_unsigned_uint32(req, kperf_timer_get_action,
+               kperf_timer_set_action);
+}
 
-    if (error || !req->newptr) {
-        return error;
-    }
+static int
+sysctl_timer_pet(struct sysctl_req *req)
+{
+       return kperf_sysctl_get_set_uint32(req, kperf_timer_get_petid,
+               kperf_timer_set_petid);
+}
 
-    return kperf_kdbg_cswitch_set(value);
+static int
+sysctl_bless_preempt(struct sysctl_req *req)
+{
+       return sysctl_io_number(req, ktrace_root_set_owner_allowed,
+               sizeof(ktrace_root_set_owner_allowed),
+               &ktrace_root_set_owner_allowed, NULL);
 }
 
 static int
-sysctl_cswitch_action( struct sysctl_oid *oidp, struct sysctl_req *req )
+sysctl_kperf_reset(struct sysctl_req *req)
 {
-    int value = kperf_cswitch_action_get();
-    int error = sysctl_handle_int(oidp, &value, 0, req);
+       int should_reset = 0;
 
-    if (error || !req->newptr) {
-        return error;
-    }
+       int error = sysctl_io_number(req, should_reset, sizeof(should_reset),
+               &should_reset, NULL);
+       if (error) {
+               return error;
+       }
 
-    return kperf_cswitch_action_set(value);
+       if (should_reset) {
+               ktrace_reset(KTRACE_KPERF);
+       }
+       return 0;
 }
 
 static int
-sysctl_signpost_action( struct sysctl_oid *oidp, struct sysctl_req *req )
+sysctl_pet_idle_rate(struct sysctl_req *req)
 {
-    int value = kperf_signpost_action_get();
-    int error = sysctl_handle_int(oidp, &value, 0, req);
+       return kperf_sysctl_get_set_int(req, kperf_get_pet_idle_rate,
+               kperf_set_pet_idle_rate);
+}
 
-    if (error || !req->newptr) {
-        return error;
-    }
+static int
+sysctl_lightweight_pet(struct sysctl_req *req)
+{
+       return kperf_sysctl_get_set_int(req, kperf_get_lightweight_pet,
+               kperf_set_lightweight_pet);
+}
 
-    return kperf_signpost_action_set(value);
+static int
+sysctl_kdbg_cswitch(struct sysctl_req *req)
+{
+       return kperf_sysctl_get_set_int(req, kperf_kdbg_cswitch_get,
+               kperf_kdbg_cswitch_set);
 }
 
-/*
- * #define SYSCTL_HANDLER_ARGS (struct sysctl_oid *oidp,         \
- *                                void *arg1, int arg2,                 \
- *                              struct sysctl_req *req )
- */
 static int
 kperf_sysctl SYSCTL_HANDLER_ARGS
 {
+#pragma unused(oidp, arg2)
        int ret;
+       uintptr_t type = (uintptr_t)arg1;
 
-       // __unused struct sysctl_oid *unused_oidp = oidp;
-       (void)arg2;
+       lck_mtx_lock(ktrace_lock);
 
-       if ( !kperf_cfg_initted )
-               panic("kperf_bootstrap not called");
-
-       ret = kperf_access_check();
-       if (ret) {
-               return ret;
+       if (req->oldptr == USER_ADDR_NULL && req->newptr != USER_ADDR_NULL) {
+               if ((ret = ktrace_configure(KTRACE_KPERF))) {
+                       lck_mtx_unlock(ktrace_lock);
+                       return ret;
+               }
+       } else {
+               if ((ret = ktrace_read_check())) {
+                       lck_mtx_unlock(ktrace_lock);
+                       return ret;
+               }
        }
 
-       lck_mtx_lock(&kperf_cfg_lock);
-
        /* which request */
-       switch( (uintptr_t) arg1 )
-       {
+       switch (type) {
        case REQ_ACTION_COUNT:
-               ret = sysctl_action_count( oidp, req );
+               ret = sysctl_action_count(req);
                break;
        case REQ_ACTION_SAMPLERS:
-               ret = sysctl_action_samplers( oidp, req );
+               ret = sysctl_action_samplers(req);
                break;
        case REQ_ACTION_USERDATA:
-               ret = sysctl_action_userdata( oidp, req );
+               ret = sysctl_action_userdata(req);
                break;
        case REQ_TIMER_COUNT:
-               ret = sysctl_timer_count( oidp, req );
+               ret = sysctl_timer_count(req);
                break;
        case REQ_TIMER_PERIOD:
-               ret = sysctl_timer_period( oidp, req );
+               ret = sysctl_timer_period(req);
                break;
        case REQ_TIMER_PET:
-               ret = sysctl_timer_pet( oidp, req );
+               ret = sysctl_timer_pet(req);
                break;
        case REQ_TIMER_ACTION:
-               ret = sysctl_timer_action( oidp, req );
+               ret = sysctl_timer_action(req);
                break;
        case REQ_SAMPLING:
-               ret = sysctl_sampling( oidp, req );
-               break;
-       case REQ_KDBG_CALLSTACKS:
-               ret = sysctl_kdbg_callstacks( oidp, req );
+               ret = sysctl_sampling(req);
                break;
        case REQ_KDBG_CSWITCH:
-               ret = sysctl_kdbg_cswitch( oidp, req );
+               ret = sysctl_kdbg_cswitch(req);
                break;
        case REQ_ACTION_FILTER_BY_TASK:
-               ret = sysctl_action_filter( oidp, req, 1 );
+               ret = sysctl_action_filter(req, TRUE);
                break;
        case REQ_ACTION_FILTER_BY_PID:
-               ret = sysctl_action_filter( oidp, req, 0 );
+               ret = sysctl_action_filter(req, FALSE);
+               break;
+       case REQ_KDEBUG_ACTION:
+               ret = sysctl_kdebug_action(req);
+               break;
+       case REQ_KDEBUG_FILTER:
+               ret = sysctl_kdebug_filter(req);
                break;
        case REQ_PET_IDLE_RATE:
-               ret = sysctl_pet_idle_rate( oidp, req );
+               ret = sysctl_pet_idle_rate(req);
                break;
        case REQ_BLESS_PREEMPT:
-               ret = sysctl_bless_preempt( oidp, req );
+               ret = sysctl_bless_preempt(req);
+               break;
+       case REQ_RESET:
+               ret = sysctl_kperf_reset(req);
                break;
-       case REQ_CSWITCH_ACTION:
-               ret = sysctl_cswitch_action( oidp, req );
+       case REQ_ACTION_UCALLSTACK_DEPTH:
+               ret = sysctl_action_ucallstack_depth(req);
                break;
-       case REQ_SIGNPOST_ACTION:
-               ret = sysctl_signpost_action( oidp, req );
+       case REQ_ACTION_KCALLSTACK_DEPTH:
+               ret = sysctl_action_kcallstack_depth(req);
                break;
+       case REQ_LIGHTWEIGHT_PET:
+               ret = sysctl_lightweight_pet(req);
+        break;
        default:
                ret = ENOENT;
                break;
        }
 
-       lck_mtx_unlock(&kperf_cfg_lock);
+       lck_mtx_unlock(ktrace_lock);
 
        return ret;
 }
@@ -579,218 +497,186 @@ kperf_sysctl SYSCTL_HANDLER_ARGS
 static int
 kperf_sysctl_bless_handler SYSCTL_HANDLER_ARGS
 {
+#pragma unused(oidp, arg2)
        int ret;
-       // __unused struct sysctl_oid *unused_oidp = oidp;
-       (void)arg2;
-  
-       if ( !kperf_cfg_initted )
-               panic("kperf_bootstrap not called");
-
-       lck_mtx_lock(&kperf_cfg_lock);
-
-       /* which request */
-       if ( (uintptr_t) arg1 == REQ_BLESS )
-               ret = sysctl_bless( oidp, req );
-       else
-               ret = ENOENT;
-
-       lck_mtx_unlock(&kperf_cfg_lock);
-
-       return ret;
-}
-
-/***************************
- *
- * Access control
- *
- ***************************/
 
-/* Validate whether the current process has priviledges to access
- * kperf (and by extension, trace). Returns 0 if access is granted.
- */
-int
-kperf_access_check(void)
-{
-       proc_t p = current_proc();
-       proc_t blessed_p;
-       int ret = 0;
-       boolean_t pid_gone = FALSE;
-
-       /* check if the pid that held the lock is gone */
-       blessed_p = proc_find(blessed_pid);
-
-       if ( blessed_p != NULL )
-               proc_rele(blessed_p);
-       else
-               pid_gone = TRUE;
-
-       if ( blessed_pid == -1 || pid_gone ) {
-               /* check for root */
-               ret = suser(kauth_cred_get(), &p->p_acflag);
-               if( !ret )
+       lck_mtx_lock(ktrace_lock);
+
+       /* if setting a new "blessed pid" (ktrace owning pid) */
+       if (req->newptr != USER_ADDR_NULL) {
+               /*
+                * root can bypass the ktrace check when a flag is set (for
+                * backwards compatibility) or when ownership is maintained over
+                * subsystems resets (to allow the user space process that set
+                * ownership to unset it).
+                */
+               if (!((ktrace_root_set_owner_allowed ||
+                      ktrace_keep_ownership_on_reset) &&
+                     kauth_cred_issuser(kauth_cred_get())))
+               {
+                       if ((ret = ktrace_configure(KTRACE_KPERF))) {
+                               lck_mtx_unlock(ktrace_lock);
+                               return ret;
+                       }
+               }
+       } else {
+               if ((ret = ktrace_read_check())) {
+                       lck_mtx_unlock(ktrace_lock);
                        return ret;
-       }
-
-       /* check against blessed pid */
-       if( p->p_pid != blessed_pid )
-               return EACCES;
-
-       /* access granted. */
-       return 0;
-}
-
-/* specify a pid as being able to access kperf/trace, depiste not
- * being root
- */
-int
-kperf_bless_pid(pid_t newpid)
-{
-       proc_t p = NULL;
-       pid_t current_pid;
-
-       p = current_proc();
-       current_pid = p->p_pid;
-
-       /* are we allowed to preempt? */
-       if ( (newpid != -1) && (blessed_pid != -1) &&
-            (blessed_pid != current_pid) && !blessed_preempt ) {
-               /* check if the pid that held the lock is gone */
-               p = proc_find(blessed_pid);
-
-               if ( p != NULL ) {
-                       proc_rele(p);
-                       return EACCES;
                }
        }
 
-       /* validate new pid */
-       if ( newpid != -1 ) {
-               p = proc_find(newpid);
-
-               if ( p == NULL )
-                       return EINVAL;
-
-               proc_rele(p);
+       /* which request */
+       if ((uintptr_t)arg1 == REQ_BLESS) {
+               ret = sysctl_bless(req);
+       } else {
+               ret = ENOENT;
        }
 
-       /* take trace facility as well */
-       kdbg_swap_global_state_pid(blessed_pid, newpid);
+       lck_mtx_unlock(ktrace_lock);
 
-       blessed_pid = newpid;
-       blessed_preempt = FALSE;
-
-       return 0;
+       return ret;
 }
 
-/***************************
- *
- * sysctl hooks
- *
- ***************************/
-
 /* root kperf node */
-SYSCTL_NODE(, OID_AUTO, kperf, CTLFLAG_RW|CTLFLAG_LOCKED, 0,
+
+SYSCTL_NODE(, OID_AUTO, kperf, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
             "kperf");
 
-/* action sub-section */
-SYSCTL_NODE(_kperf, OID_AUTO, action, CTLFLAG_RW|CTLFLAG_LOCKED, 0,
+/* actions */
+
+SYSCTL_NODE(_kperf, OID_AUTO, action, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
             "action");
 
 SYSCTL_PROC(_kperf_action, OID_AUTO, count,
-            CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_ACTION_COUNT, 
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
+            (void *)REQ_ACTION_COUNT,
             sizeof(int), kperf_sysctl, "I", "Number of actions");
 
 SYSCTL_PROC(_kperf_action, OID_AUTO, samplers,
-            CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_ACTION_SAMPLERS, 
-            3*sizeof(uint64_t), kperf_sysctl, "UQ", 
-            "What to sample what a trigger fires an action");
+            CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+            (void *)REQ_ACTION_SAMPLERS,
+            3 * sizeof(uint64_t), kperf_sysctl, "UQ",
+            "What to sample when a trigger fires an action");
 
 SYSCTL_PROC(_kperf_action, OID_AUTO, userdata,
-            CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_ACTION_USERDATA, 
-            3*sizeof(uint64_t), kperf_sysctl, "UQ", 
+            CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+            (void *)REQ_ACTION_USERDATA,
+            3 * sizeof(uint64_t), kperf_sysctl, "UQ",
             "User data to attribute to action");
 
 SYSCTL_PROC(_kperf_action, OID_AUTO, filter_by_task,
-            CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_ACTION_FILTER_BY_TASK, 
-            3*sizeof(uint64_t), kperf_sysctl, "UQ", 
+            CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+            (void *)REQ_ACTION_FILTER_BY_TASK,
+            3 * sizeof(uint64_t), kperf_sysctl, "UQ",
             "Apply a task filter to the action");
 
 SYSCTL_PROC(_kperf_action, OID_AUTO, filter_by_pid,
-            CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_ACTION_FILTER_BY_PID, 
-            3*sizeof(uint64_t), kperf_sysctl, "UQ", 
+            CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+            (void *)REQ_ACTION_FILTER_BY_PID,
+            3 * sizeof(uint64_t), kperf_sysctl, "UQ",
             "Apply a pid filter to the action");
 
-/* timer sub-section */
-SYSCTL_NODE(_kperf, OID_AUTO, timer, CTLFLAG_RW|CTLFLAG_LOCKED, 0,
+SYSCTL_PROC(_kperf_action, OID_AUTO, ucallstack_depth,
+            CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+            (void *)REQ_ACTION_UCALLSTACK_DEPTH,
+            sizeof(int), kperf_sysctl, "I",
+            "Maximum number of frames to include in user callstacks");
+
+SYSCTL_PROC(_kperf_action, OID_AUTO, kcallstack_depth,
+            CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+            (void *)REQ_ACTION_KCALLSTACK_DEPTH,
+            sizeof(int), kperf_sysctl, "I",
+            "Maximum number of frames to include in kernel callstacks");
+
+/* timers */
+
+SYSCTL_NODE(_kperf, OID_AUTO, timer, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
             "timer");
 
 SYSCTL_PROC(_kperf_timer, OID_AUTO, count,
-            CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_TIMER_COUNT, 
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
+            (void *)REQ_TIMER_COUNT,
             sizeof(int), kperf_sysctl, "I", "Number of time triggers");
 
 SYSCTL_PROC(_kperf_timer, OID_AUTO, period,
-            CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_TIMER_PERIOD, 
-            2*sizeof(uint64_t), kperf_sysctl, "UQ", "Timer number and period");
+            CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+            (void *)REQ_TIMER_PERIOD,
+            2 * sizeof(uint64_t), kperf_sysctl, "UQ",
+            "Timer number and period");
 
 SYSCTL_PROC(_kperf_timer, OID_AUTO, action,
-            CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_TIMER_ACTION, 
-            2*sizeof(uint64_t), kperf_sysctl, "UQ", "Timer number and actionid");
+            CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+            (void *)REQ_TIMER_ACTION,
+            2 * sizeof(uint64_t), kperf_sysctl, "UQ",
+            "Timer number and actionid");
 
 SYSCTL_PROC(_kperf_timer, OID_AUTO, pet_timer,
-            CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_TIMER_PET, 
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
+            (void *)REQ_TIMER_PET,
             sizeof(int), kperf_sysctl, "I", "Which timer ID does PET");
 
+/* kdebug trigger */
+
+SYSCTL_NODE(_kperf, OID_AUTO, kdebug, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
+            "kdebug");
+
+SYSCTL_PROC(_kperf_kdebug, OID_AUTO, action,
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
+            (void*)REQ_KDEBUG_ACTION,
+            sizeof(int), kperf_sysctl, "I", "ID of action to trigger on kdebug events");
+
+SYSCTL_PROC(_kperf_kdebug, OID_AUTO, filter,
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+            (void*)REQ_KDEBUG_FILTER,
+            sizeof(int), kperf_sysctl, "P", "The filter that determines which kdebug events trigger a sample");
+
 /* misc */
+
 SYSCTL_PROC(_kperf, OID_AUTO, sampling,
-            CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_SAMPLING, 
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
+            (void *)REQ_SAMPLING,
             sizeof(int), kperf_sysctl, "I", "Sampling running");
 
+SYSCTL_PROC(_kperf, OID_AUTO, reset,
+            CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MASKED | CTLFLAG_LOCKED,
+            (void *)REQ_RESET,
+            0, kperf_sysctl, "-", "Reset kperf");
+
 SYSCTL_PROC(_kperf, OID_AUTO, blessed_pid,
-            CTLTYPE_INT|CTLFLAG_RW, /* must be root */
-            (void*)REQ_BLESS, 
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, /* must be root */
+            (void *)REQ_BLESS,
             sizeof(int), kperf_sysctl_bless_handler, "I", "Blessed pid");
 
 SYSCTL_PROC(_kperf, OID_AUTO, blessed_preempt,
-            CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_BLESS_PREEMPT, 
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
+            (void *)REQ_BLESS_PREEMPT,
             sizeof(int), kperf_sysctl, "I", "Blessed preemption");
 
-SYSCTL_PROC(_kperf, OID_AUTO, kdbg_callstacks,
-            CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_KDBG_CALLSTACKS, 
-            sizeof(int), kperf_sysctl, "I", "Generate kdbg callstacks");
-
 SYSCTL_PROC(_kperf, OID_AUTO, kdbg_cswitch,
-            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
             (void *)REQ_KDBG_CSWITCH,
             sizeof(int), kperf_sysctl, "I", "Generate context switch info");
 
 SYSCTL_PROC(_kperf, OID_AUTO, pet_idle_rate,
-            CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
-            (void*)REQ_PET_IDLE_RATE,
-            sizeof(int), kperf_sysctl, "I", "Rate at which unscheduled threads are forced to be sampled in PET mode");
-
-SYSCTL_PROC(_kperf, OID_AUTO, cswitch_action,
-            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
-            (void*)REQ_CSWITCH_ACTION,
-            sizeof(int), kperf_sysctl, "I", "ID of action to trigger on context-switch");
-
-SYSCTL_PROC(_kperf, OID_AUTO, signpost_action,
-            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
-            (void*)REQ_SIGNPOST_ACTION,
-            sizeof(int), kperf_sysctl, "I", "ID of action to trigger on signposts");
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
+            (void *)REQ_PET_IDLE_RATE,
+            sizeof(int), kperf_sysctl, "I",
+            "Rate at which unscheduled threads are forced to be sampled in "
+            "PET mode");
+
+SYSCTL_PROC(_kperf, OID_AUTO, lightweight_pet,
+            CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_LOCKED,
+            (void *)REQ_LIGHTWEIGHT_PET,
+            sizeof(int), kperf_sysctl, "I",
+            "Status of lightweight PET mode");
 
 /* debug */
-SYSCTL_INT(_kperf, OID_AUTO, debug_level, CTLFLAG_RW
+SYSCTL_INT(_kperf, OID_AUTO, debug_level, CTLFLAG_RW | CTLFLAG_LOCKED,
            &kperf_debug_level, 0, "debug level");
 
+#if DEVELOPMENT || DEBUG
+SYSCTL_QUAD(_kperf, OID_AUTO, already_pending_ipis,
+            CTLFLAG_RD | CTLFLAG_LOCKED,
+            &kperf_pending_ipis, "");
+#endif /* DEVELOPMENT || DEBUG */