]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/kperf/action.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / osfmk / kperf / action.c
index 90d8e341f36f56242dcc28190150d12d5e4707c6..7c1814e51adc79dc671c964bc04689474d63af71 100644 (file)
@@ -50,7 +50,7 @@
 #include <kperf/kdebug_trigger.h>
 #include <kperf/kperf.h>
 #include <kperf/kperf_kpc.h>
-#include <kperf/kperf_timer.h>
+#include <kperf/kptimer.h>
 #include <kperf/pet.h>
 #include <kperf/sample.h>
 #include <kperf/thread_samplers.h>
@@ -120,6 +120,80 @@ kperf_system_memory_log(void)
            (uintptr_t)VM_PAGE_COMPRESSOR_COUNT);
 }
 
+static void
+kperf_sample_user_internal(struct kperf_usample *sbuf,
+    struct kperf_context *context, unsigned int actionid,
+    unsigned int sample_what)
+{
+       if (sample_what & SAMPLER_USTACK) {
+               kperf_ucallstack_sample(&sbuf->ucallstack, context);
+       }
+       if (sample_what & SAMPLER_TH_DISPATCH) {
+               kperf_thread_dispatch_sample(&sbuf->th_dispatch, context);
+       }
+       if (sample_what & SAMPLER_TH_INFO) {
+               kperf_thread_info_sample(&sbuf->th_info, context);
+       }
+
+       boolean_t intren = ml_set_interrupts_enabled(FALSE);
+
+       /*
+        * No userdata or sample_flags for this one.
+        */
+       BUF_DATA(PERF_GEN_EVENT | DBG_FUNC_START, sample_what, actionid);
+
+       if (sample_what & SAMPLER_USTACK) {
+               kperf_ucallstack_log(&sbuf->ucallstack);
+       }
+       if (sample_what & SAMPLER_TH_DISPATCH) {
+               kperf_thread_dispatch_log(&sbuf->th_dispatch);
+       }
+       if (sample_what & SAMPLER_TH_INFO) {
+               kperf_thread_info_log(&sbuf->th_info);
+       }
+
+       BUF_DATA(PERF_GEN_EVENT | DBG_FUNC_END, sample_what);
+
+       ml_set_interrupts_enabled(intren);
+}
+
+void
+kperf_sample_user(struct kperf_usample *sbuf, struct kperf_context *context,
+    unsigned int actionid, unsigned int sample_flags)
+{
+       if (actionid == 0 || actionid > actionc) {
+               return;
+       }
+
+       unsigned int sample_what = actionv[actionid - 1].sample;
+       unsigned int ucallstack_depth = actionv[actionid - 1].ucallstack_depth;
+
+       /* callstacks should be explicitly ignored */
+       if (sample_flags & SAMPLE_FLAG_EMPTY_CALLSTACK) {
+               sample_what &= ~(SAMPLER_KSTACK | SAMPLER_USTACK);
+       }
+       if (sample_flags & SAMPLE_FLAG_ONLY_SYSTEM) {
+               sample_what &= SAMPLER_SYS_MEM;
+       }
+       assert((sample_flags & (SAMPLE_FLAG_THREAD_ONLY | SAMPLE_FLAG_TASK_ONLY))
+           != (SAMPLE_FLAG_THREAD_ONLY | SAMPLE_FLAG_TASK_ONLY));
+       if (sample_flags & SAMPLE_FLAG_THREAD_ONLY) {
+               sample_what &= SAMPLER_THREAD_MASK;
+       }
+       if (sample_flags & SAMPLE_FLAG_TASK_ONLY) {
+               sample_what &= SAMPLER_TASK_MASK;
+       }
+
+       if (sample_what == 0) {
+               return;
+       }
+
+       sbuf->ucallstack.kpuc_nframes = ucallstack_depth ?:
+           MAX_UCALLSTACK_FRAMES;
+
+       kperf_sample_user_internal(sbuf, context, actionid, sample_what);
+}
+
 static kern_return_t
 kperf_sample_internal(struct kperf_sample *sbuf,
     struct kperf_context *context,
@@ -132,9 +206,6 @@ kperf_sample_internal(struct kperf_sample *sbuf,
        uint32_t userdata = actionid;
        bool task_only = false;
 
-       /* not much point continuing here, but what to do ? return
-        * Shutdown? cut a tracepoint and continue?
-        */
        if (sample_what == 0) {
                return SAMPLE_CONTINUE;
        }
@@ -159,7 +230,8 @@ kperf_sample_internal(struct kperf_sample *sbuf,
        }
 
        if (!task_only) {
-               context->cur_thread->kperf_pet_gen = kperf_pet_gen;
+               context->cur_thread->kperf_pet_gen =
+                   os_atomic_load(&kppet_gencount, relaxed);
        }
        bool is_kernel = (context->cur_pid == 0);
 
@@ -170,14 +242,9 @@ kperf_sample_internal(struct kperf_sample *sbuf,
                sbuf->kcallstack.kpkc_nframes = MAX_KCALLSTACK_FRAMES;
        }
 
-       if (ucallstack_depth) {
-               sbuf->ucallstack.kpuc_nframes = ucallstack_depth;
-       } else {
-               sbuf->ucallstack.kpuc_nframes = MAX_UCALLSTACK_FRAMES;
-       }
-
+       ucallstack_depth = ucallstack_depth ?: MAX_UCALLSTACK_FRAMES;
        sbuf->kcallstack.kpkc_flags = 0;
-       sbuf->ucallstack.kpuc_flags = 0;
+       sbuf->usample.ucallstack.kpuc_flags = 0;
 
        if (sample_what & SAMPLER_TH_INFO) {
                kperf_thread_info_sample(&sbuf->th_info, context);
@@ -199,8 +266,8 @@ kperf_sample_internal(struct kperf_sample *sbuf,
        if (sample_what & SAMPLER_KSTACK) {
                if (sample_flags & SAMPLE_FLAG_CONTINUATION) {
                        kperf_continuation_sample(&(sbuf->kcallstack), context);
-                       /* outside of interrupt context, backtrace the current thread */
                } else if (sample_flags & SAMPLE_FLAG_NON_INTERRUPT) {
+                       /* outside of interrupt context, backtrace the current thread */
                        kperf_backtrace_sample(&(sbuf->kcallstack), context);
                } else {
                        kperf_kcallstack_sample(&(sbuf->kcallstack), context);
@@ -210,7 +277,6 @@ kperf_sample_internal(struct kperf_sample *sbuf,
                kperf_task_snapshot_sample(context->cur_task, &(sbuf->tk_snapshot));
        }
 
-       /* sensitive ones */
        if (!is_kernel) {
                if (sample_what & SAMPLER_MEMINFO) {
                        kperf_meminfo_sample(context->cur_task, &(sbuf->meminfo));
@@ -218,19 +284,13 @@ kperf_sample_internal(struct kperf_sample *sbuf,
 
                if (sample_flags & SAMPLE_FLAG_PEND_USER) {
                        if (sample_what & SAMPLER_USTACK) {
-                               pended_ucallstack = kperf_ucallstack_pend(context, sbuf->ucallstack.kpuc_nframes);
+                               pended_ucallstack = kperf_ucallstack_pend(context,
+                                   ucallstack_depth, actionid);
                        }
 
                        if (sample_what & SAMPLER_TH_DISPATCH) {
-                               pended_th_dispatch = kperf_thread_dispatch_pend(context);
-                       }
-               } else {
-                       if (sample_what & SAMPLER_USTACK) {
-                               kperf_ucallstack_sample(&(sbuf->ucallstack), context);
-                       }
-
-                       if (sample_what & SAMPLER_TH_DISPATCH) {
-                               kperf_thread_dispatch_sample(&(sbuf->th_dispatch), context);
+                               pended_th_dispatch =
+                                   kperf_thread_dispatch_pend(context, actionid);
                        }
                }
        }
@@ -307,14 +367,6 @@ log_sample:
                        if (pended_th_dispatch) {
                                BUF_INFO(PERF_TI_DISPPEND);
                        }
-               } else {
-                       if (sample_what & SAMPLER_USTACK) {
-                               kperf_ucallstack_log(&(sbuf->ucallstack));
-                       }
-
-                       if (sample_what & SAMPLER_TH_DISPATCH) {
-                               kperf_thread_dispatch_log(&(sbuf->th_dispatch));
-                       }
                }
        }
 
@@ -357,11 +409,11 @@ kperf_sample(struct kperf_sample *sbuf,
 
        /* the samplers to run */
        unsigned int sample_what = actionv[actionid - 1].sample;
+       unsigned int ucallstack_depth = actionv[actionid - 1].ucallstack_depth;
 
        /* do the actual sample operation */
        return kperf_sample_internal(sbuf, context, sample_what,
-                  sample_flags, actionid,
-                  actionv[actionid - 1].ucallstack_depth);
+                  sample_flags, actionid, ucallstack_depth);
 }
 
 void
@@ -412,11 +464,11 @@ __attribute__((noinline))
 void
 kperf_thread_ast_handler(thread_t thread)
 {
-       BUF_INFO(PERF_AST_HNDLR | DBG_FUNC_START, thread, kperf_get_thread_flags(thread));
+       uint32_t ast = thread->kperf_ast;
+
+       BUF_INFO(PERF_AST_HNDLR | DBG_FUNC_START, thread, ast);
 
-       /* ~2KB of the stack for the sample since this is called from AST */
-       struct kperf_sample sbuf;
-       memset(&sbuf, 0, sizeof(struct kperf_sample));
+       struct kperf_usample sbuf = {};
 
        task_t task = get_threadtask(thread);
 
@@ -425,49 +477,46 @@ kperf_thread_ast_handler(thread_t thread)
                return;
        }
 
-       /* make a context, take a sample */
        struct kperf_context ctx = {
                .cur_thread = thread,
                .cur_task = task,
                .cur_pid = task_pid(task),
        };
 
-       /* decode the flags to determine what to sample */
        unsigned int sample_what = 0;
-       uint32_t flags = kperf_get_thread_flags(thread);
-
-       if (flags & T_KPERF_AST_DISPATCH) {
+       if (ast & T_KPERF_AST_DISPATCH) {
                sample_what |= SAMPLER_TH_DISPATCH;
        }
-       if (flags & T_KPERF_AST_CALLSTACK) {
-               sample_what |= SAMPLER_USTACK;
-               sample_what |= SAMPLER_TH_INFO;
+       if (ast & T_KPERF_AST_CALLSTACK) {
+               /* TH_INFO for backwards compatibility */
+               sample_what |= SAMPLER_USTACK | SAMPLER_TH_INFO;
        }
 
-       uint32_t ucallstack_depth = T_KPERF_GET_CALLSTACK_DEPTH(flags);
-
-       int r = kperf_sample_internal(&sbuf, &ctx, sample_what, 0, 0, ucallstack_depth);
+       sbuf.ucallstack.kpuc_nframes =
+           T_KPERF_GET_CALLSTACK_DEPTH(ast) ?: MAX_UCALLSTACK_FRAMES;
+       unsigned int actionid = T_KPERF_GET_ACTIONID(ast);
+       kperf_sample_user_internal(&sbuf, &ctx, actionid, sample_what);
 
-       BUF_INFO(PERF_AST_HNDLR | DBG_FUNC_END, r);
+       BUF_INFO(PERF_AST_HNDLR | DBG_FUNC_END);
 }
 
-/* register AST bits */
 int
-kperf_ast_pend(thread_t thread, uint32_t set_flags)
+kperf_ast_pend(thread_t thread, uint32_t set_flags, unsigned int set_actionid)
 {
-       /* can only pend on the current thread */
        if (thread != current_thread()) {
-               panic("pending to non-current thread");
+               panic("kperf: pending AST to non-current thread");
        }
 
-       /* get our current bits */
-       uint32_t flags = kperf_get_thread_flags(thread);
+       uint32_t ast = thread->kperf_ast;
+       unsigned int actionid = T_KPERF_GET_ACTIONID(ast);
+       uint32_t flags = ast & T_KPERF_AST_ALL;
+
+       if ((flags | set_flags) != flags || actionid != set_actionid) {
+               ast &= ~T_KPERF_SET_ACTIONID(actionid);
+               ast |= T_KPERF_SET_ACTIONID(set_actionid);
+               ast |= set_flags;
 
-       /* see if it's already been done or pended */
-       if (!(flags & set_flags)) {
-               /* set the bit on the thread */
-               flags |= set_flags;
-               kperf_set_thread_flags(thread, flags);
+               thread->kperf_ast = ast;
 
                /* set the actual AST */
                act_set_kperf(thread);
@@ -480,14 +529,12 @@ kperf_ast_pend(thread_t thread, uint32_t set_flags)
 void
 kperf_ast_set_callstack_depth(thread_t thread, uint32_t depth)
 {
-       uint32_t ast_flags = kperf_get_thread_flags(thread);
-       uint32_t existing_callstack_depth =
-           T_KPERF_GET_CALLSTACK_DEPTH(ast_flags);
-
-       if (existing_callstack_depth < depth) {
-               ast_flags &= ~T_KPERF_SET_CALLSTACK_DEPTH(depth);
-               ast_flags |= T_KPERF_SET_CALLSTACK_DEPTH(depth);
-               kperf_set_thread_flags(thread, ast_flags);
+       uint32_t ast = thread->kperf_ast;
+       uint32_t existing_depth = T_KPERF_GET_CALLSTACK_DEPTH(ast);
+       if (existing_depth < depth) {
+               ast &= ~T_KPERF_SET_CALLSTACK_DEPTH(existing_depth);
+               ast |= T_KPERF_SET_CALLSTACK_DEPTH(depth);
+               thread->kperf_ast = ast;
        }
 }
 
@@ -642,10 +689,7 @@ kperf_action_set_count(unsigned count)
         * more things, too.
         */
        if (actionc == 0) {
-               int r;
-               if ((r = kperf_init())) {
-                       return r;
-               }
+               kperf_setup();
        }
 
        /* create a new array */
@@ -689,6 +733,9 @@ kperf_action_set_ucallstack_depth(unsigned action_id, uint32_t depth)
        if (depth > MAX_UCALLSTACK_FRAMES) {
                return EINVAL;
        }
+       if (depth < 2) {
+               return EINVAL;
+       }
 
        actionv[action_id - 1].ucallstack_depth = depth;
 
@@ -705,6 +752,9 @@ kperf_action_set_kcallstack_depth(unsigned action_id, uint32_t depth)
        if (depth > MAX_KCALLSTACK_FRAMES) {
                return EINVAL;
        }
+       if (depth < 1) {
+               return EINVAL;
+       }
 
        actionv[action_id - 1].kcallstack_depth = depth;