perf: Remove the kprof.pdata staging ground
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 15 Jun 2016 18:25:19 +0000 (14:25 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 17 Jun 2016 16:17:54 +0000 (12:17 -0400)
Previously, kpdata would be a snapshotted view of the profiler queue.  This
was a pain, and it didn't help with anything.  Instead, now we just drain
the profiler queue directly.

This is also a nice step towards having multiple profilers, instead of the
existing global profiler.  When you open kpctl, the chan has a reference on
a profiler.  That happens to be the global one now.  When you close the
chan, the profiler is released.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/drivers/dev/kprof.c
kern/include/profiler.h
kern/src/profiler.c

index 387ea01..1ead21e 100644 (file)
@@ -52,8 +52,6 @@ struct kprof {
        bool mpstat_ipi;
        bool profiling;
        bool opened;
-       char *pdata;
-       size_t psize;
 };
 
 struct dev kprofdevtab;
@@ -132,16 +130,13 @@ static void kprof_enable_timer(int coreid, int on_off)
        }
 }
 
-static void kprof_profdata_clear(void)
-{
-       kfree(kprof.pdata);
-       kprof.pdata = NULL;
-       kprof.psize = 0;
-}
-
+/* Start collecting samples from perf events into the profiler.
+ *
+ * This command only runs if the user successfully opened kpctl, which gives
+ * them a profiler (the global profiler, for now). */
 static void kprof_start_profiler(void)
 {
-       ERRSTACK(2);
+       ERRSTACK(1);
 
        qlock(&kprof.lock);
        if (waserror()) {
@@ -149,43 +144,17 @@ static void kprof_start_profiler(void)
                nexterror();
        }
        if (!kprof.profiling) {
-               profiler_setup();
-               if (waserror()) {
-                       kprof.profiling = FALSE;
-                       profiler_cleanup();
-                       nexterror();
-               }
-               profiler_control_trace(1);
+               profiler_start();
                kprof.profiling = TRUE;
-               kprof_profdata_clear();
-               poperror();
        }
        poperror();
        qunlock(&kprof.lock);
 }
 
-static void kprof_fetch_profiler_data(void)
-{
-       size_t psize = kprof.psize + profiler_size();
-       size_t read_amt;
-       char *ndata = krealloc(kprof.pdata, psize, MEM_WAIT);
-
-       if (!ndata)
-               error(ENOMEM, ERROR_FIXME);
-       kprof.pdata = ndata;
-       /* psize includes a snapshot of the profiler's size.  It might grow in the
-        * meantime, but we'll only ever grab as much as we saw originally.  This is
-        * fine.  The important thing is that we only grab contiguous records. */
-       while (kprof.psize < psize) {
-               read_amt = profiler_read(kprof.pdata + kprof.psize,
-                                        psize - kprof.psize);
-               /* We are the only reader - we must always get whatever was in the
-                * queue. */
-               assert(read_amt);
-               kprof.psize += read_amt;
-       }
-}
-
+/* Stops collecting samples from perf events.
+ *
+ * This command only runs if the user successfully opened kpctl, which gives
+ * them a profiler (the global profiler, for now). */
 static void kprof_stop_profiler(void)
 {
        ERRSTACK(1);
@@ -196,16 +165,16 @@ static void kprof_stop_profiler(void)
                nexterror();
        }
        if (kprof.profiling) {
-               profiler_control_trace(0);
-               kprof_fetch_profiler_data();
-               profiler_cleanup();
-
+               profiler_stop();
                kprof.profiling = FALSE;
        }
        poperror();
        qunlock(&kprof.lock);
 }
 
+/* Makes each core flush its results into the profiler queue.  You can do this
+ * while the profiler is still running.  However, this does not hang up the
+ * queue, so reads on kpdata will block. */
 static void kprof_flush_profiler(void)
 {
        ERRSTACK(1);
@@ -215,10 +184,8 @@ static void kprof_flush_profiler(void)
                qunlock(&kprof.lock);
                nexterror();
        }
-       if (kprof.profiling) {
+       if (kprof.profiling)
                profiler_trace_data_flush();
-               kprof_fetch_profiler_data();
-       }
        poperror();
        qunlock(&kprof.lock);
 }
@@ -233,8 +200,6 @@ static void kprof_init(void)
        qlock_init(&kprof.lock);
        kprof.profiling = FALSE;
        kprof.opened = FALSE;
-       kprof.pdata = NULL;
-       kprof.psize = 0;
 
        kprof.alarms = kzmalloc(sizeof(struct alarm_waiter) * num_cores,
                                MEM_WAIT);
@@ -255,7 +220,7 @@ static void kprof_init(void)
        kproftab[Kmpstatqid].length = mpstat_len();
        kproftab[Kmpstatrawqid].length = mpstatraw_len();
 
-       strlcpy(kprof_control_usage, "clear|start|stop|flush|timer",
+       strlcpy(kprof_control_usage, "start|stop|flush|timer",
                sizeof(kprof_control_usage));
        profiler_append_configure_usage(kprof_control_usage,
                                        sizeof(kprof_control_usage));
@@ -265,20 +230,10 @@ static void kprof_init(void)
 
 static void kprof_shutdown(void)
 {
-       kprof_stop_profiler();
-       kprof_profdata_clear();
-
        kfree(kprof.alarms);
        kprof.alarms = NULL;
 }
 
-static void kprofclear(void)
-{
-       qlock(&kprof.lock);
-       kprof_profdata_clear();
-       qunlock(&kprof.lock);
-}
-
 static struct walkqid *kprof_walk(struct chan *c, struct chan *nc, char **name,
                                   int nname)
 {
@@ -287,21 +242,12 @@ static struct walkqid *kprof_walk(struct chan *c, struct chan *nc, char **name,
 
 static size_t kprof_profdata_size(void)
 {
-       return kprof.pdata != NULL ? kprof.psize : profiler_size();
+       return profiler_size();
 }
 
 static long kprof_profdata_read(void *dest, long size, int64_t off)
 {
-       qlock(&kprof.lock);
-       if (kprof.pdata && off < kprof.psize) {
-               size = MIN(kprof.psize - off, size);
-               memcpy(dest, kprof.pdata + off, size);
-       } else {
-               size = 0;
-       }
-       qunlock(&kprof.lock);
-
-       return size;
+       return profiler_read(dest, size);
 }
 
 static int kprof_stat(struct chan *c, uint8_t *db, int n)
@@ -329,6 +275,8 @@ static struct chan *kprof_open(struct chan *c, int omode)
                        error(EBUSY, "Global profiler is already open");
                }
                kprof.opened = TRUE;
+               /* TODO: have a real creation function for a non-global profiler */
+               profiler_setup();
                qunlock(&kprof.lock);
                break;
        }
@@ -344,7 +292,10 @@ static void kprof_close(struct chan *c)
                switch ((int) c->qid.path) {
                case Kprofctlqid:
                        kprof_stop_profiler();
+                       qlock(&kprof.lock);
+                       profiler_cleanup();
                        kprof.opened = FALSE;
+                       qunlock(&kprof.lock);
                        break;
                }
        }
@@ -477,9 +428,7 @@ static long kprof_write(struct chan *c, void *a, long n, int64_t unused)
                        error(EFAIL, kprof_control_usage);
                if (profiler_configure(cb))
                        break;
-               if (!strcmp(cb->f[0], "clear")) {
-                       kprofclear();
-               } else if (!strcmp(cb->f[0], "timer")) {
+               if (!strcmp(cb->f[0], "timer")) {
                        if (cb->nf < 3)
                                error(EFAIL, "timer {{all, N} {on, off}, period USEC}");
                        if (!strcmp(cb->f[1], "period")) {
index 802a830..7fa9504 100644 (file)
@@ -18,10 +18,11 @@ void profiler_append_configure_usage(char *msgbuf, size_t buflen);
 void profiler_init(void);
 void profiler_setup(void);
 void profiler_cleanup(void);
+void profiler_start(void);
+void profiler_stop(void);
 void profiler_add_kernel_backtrace(uintptr_t pc, uintptr_t fp, uint64_t info);
 void profiler_add_user_backtrace(uintptr_t pc, uintptr_t fp, uint64_t info);
 void profiler_add_trace(uintptr_t pc, uint64_t info);
-void profiler_control_trace(int onoff);
 void profiler_trace_data_flush(void);
 void profiler_add_hw_sample(struct hw_trapframe *hw_tf, uint64_t info);
 int profiler_size(void);
index f51e7a3..6e68b53 100644 (file)
@@ -1,7 +1,26 @@
 /* Copyright (c) 2015 Google Inc
  * Davide Libenzi <dlibenzi@google.com>
  * See LICENSE for details.
- */
+ *
+ * This controls the emitting, collecting, and exporting of samples for perf
+ * events.  Examples of events are PMU counter overflows, mmaps, and process
+ * creation.
+ *
+ * Events are collected in a central qio queue.  High-frequency events (e.g.
+ * profiler_add_hw_sample()) are collected in per-core buffers, which are
+ * flushed to the central queue when they fill up or on command.
+ * Lower-frequency events (e.g. profiler_notify_mmap()) just go straight to the
+ * central queue.
+ *
+ * Currently there is one global profiler.  Kprof is careful to only have one
+ * open profiler at a time.  We assert that this is true.  TODO: stop using the
+ * global profiler!
+ *
+ * A few other notes:
+ * - profiler_control_trace() controls the per-core trace collection.  When it
+ *   is disabled, it also flushes the per-core blocks to the central queue.
+ * - The collection of mmap and comm samples is independent of trace collection.
+ *   Those will occur whenever the profiler is open (refcnt check, for now). */
 
 #include <ros/common.h>
 #include <ros/mman.h>
@@ -390,8 +409,8 @@ void profiler_setup(void)
                qunlock(&profiler_mtx);
                nexterror();
        }
-       if (!profiler_queue)
-               alloc_cpu_buffers();
+       assert(!profiler_queue);
+       alloc_cpu_buffers();
 
        /* Do this only when everything is initialized (as last init operation).
         */
@@ -430,7 +449,7 @@ static void profiler_core_trace_enable(void *opaque)
                profiler_cpu_flush(cpu_buf);
 }
 
-void profiler_control_trace(int onoff)
+static void profiler_control_trace(int onoff)
 {
        struct core_set cset;
 
@@ -442,6 +461,20 @@ void profiler_control_trace(int onoff)
                        (void *) (uintptr_t) onoff);
 }
 
+void profiler_start(void)
+{
+       assert(profiler_queue);
+       profiler_control_trace(1);
+       qreopen(profiler_queue);
+}
+
+void profiler_stop(void)
+{
+       assert(profiler_queue);
+       profiler_control_trace(0);
+       qhangup(profiler_queue, 0);
+}
+
 static void profiler_core_flush(void *opaque)
 {
        struct profiler_cpu_context *cpu_buf = profiler_get_cpu_ctx(core_id());