9ns: Fix devtab function pointer signatures
[akaros.git] / kern / drivers / dev / kprof.c
index 453e195..cbc7b1c 100644 (file)
@@ -27,6 +27,7 @@
 #include <profiler.h>
 #include <kprof.h>
 #include <ros/procinfo.h>
+#include <init.h>
 
 #define KTRACE_BUFFER_SIZE (128 * 1024)
 #define TRACE_PRINTK_BUFFER_SIZE (8 * 1024)
@@ -35,6 +36,7 @@ enum {
        Kprofdirqid = 0,
        Kprofdataqid,
        Kprofctlqid,
+       Kptracectlqid,
        Kptraceqid,
        Kprintxqid,
        Kmpstatqid,
@@ -48,11 +50,9 @@ struct trace_printk_buffer {
 
 struct kprof {
        qlock_t lock;
-       struct alarm_waiter *alarms;
        bool mpstat_ipi;
        bool profiling;
-       char *pdata;
-       size_t psize;
+       bool opened;
 };
 
 struct dev kprofdevtab;
@@ -60,6 +60,7 @@ struct dirtab kproftab[] = {
        {".",                   {Kprofdirqid,           0, QTDIR}, 0,   DMDIR|0550},
        {"kpdata",              {Kprofdataqid},         0,      0600},
        {"kpctl",               {Kprofctlqid},          0,      0600},
+       {"kptrace_ctl", {Kptracectlqid},        0,      0660},
        {"kptrace",             {Kptraceqid},           0,      0600},
        {"kprintx",             {Kprintxqid},           0,      0600},
        {"mpstat",              {Kmpstatqid},           0,      0600},
@@ -71,7 +72,6 @@ static bool ktrace_init_done = FALSE;
 static spinlock_t ktrace_lock = SPINLOCK_INITIALIZER_IRQSAVE;
 static struct circular_buffer ktrace_data;
 static char ktrace_buffer[KTRACE_BUFFER_SIZE];
-static int kprof_timer_period = 1000;
 static char kprof_control_usage[128];
 
 static size_t mpstat_len(void)
@@ -94,53 +94,18 @@ static char *devname(void)
        return kprofdevtab.name;
 }
 
-static void kprof_alarm_handler(struct alarm_waiter *waiter,
-                                struct hw_trapframe *hw_tf)
-{
-       int coreid = core_id();
-       struct timer_chain *tchain = &per_cpu_info[coreid].tchain;
-
-       profiler_add_hw_sample(hw_tf, PROF_MKINFO(PROF_DOM_TIMER,
-                                                                                         kprof_timer_period));
-       reset_alarm_rel(tchain, waiter, kprof_timer_period);
-}
-
 static struct chan *kprof_attach(char *spec)
 {
-       if (!kprof.alarms)
-               error(ENOMEM, ERROR_FIXME);
-
        return devattach(devname(), spec);
 }
 
-static void kprof_enable_timer(int coreid, int on_off)
-{
-       struct timer_chain *tchain = &per_cpu_info[coreid].tchain;
-       struct alarm_waiter *waiter = &kprof.alarms[coreid];
-
-       if (on_off) {
-               /* Per CPU waiters already inited.  Will set/reset each time (1 ms
-                * default). */
-               reset_alarm_rel(tchain, waiter, kprof_timer_period);
-       } else {
-               /* Since the alarm handler runs and gets reset within IRQ context, then
-                * we should never fail to cancel the alarm if it was already running
-                * (tchain locks synchronize us).  But it might not be set at all, which
-                * is fine. */
-               unset_alarm(tchain, waiter);
-       }
-}
-
-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()) {
@@ -148,43 +113,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);
@@ -195,16 +134,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);
@@ -214,95 +153,54 @@ 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);
 }
 
 static void kprof_init(void)
 {
-       int i;
-       ERRSTACK(1);
-
        profiler_init();
 
        qlock_init(&kprof.lock);
        kprof.profiling = FALSE;
-       kprof.pdata = NULL;
-       kprof.psize = 0;
+       kprof.opened = FALSE;
 
-       kprof.alarms = kzmalloc(sizeof(struct alarm_waiter) * num_cores,
-                               MEM_WAIT);
-       if (!kprof.alarms)
-               error(ENOMEM, ERROR_FIXME);
-       if (waserror()) {
-               kfree(kprof.alarms);
-               kprof.alarms = NULL;
-               nexterror();
-       }
-       for (i = 0; i < num_cores; i++)
-               init_awaiter_irq(&kprof.alarms[i], kprof_alarm_handler);
-
-       for (i = 0; i < ARRAY_SIZE(kproftab); i++)
+       for (int i = 0; i < ARRAY_SIZE(kproftab); i++)
                kproftab[i].length = 0;
 
        kprof.mpstat_ipi = TRUE;
        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",
                sizeof(kprof_control_usage));
        profiler_append_configure_usage(kprof_control_usage,
                                        sizeof(kprof_control_usage));
-
-       poperror();
 }
 
 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)
+                                  unsigned int nname)
 {
        return devwalk(c, nc, name, nname, kproftab, ARRAY_SIZE(kproftab), devgen);
 }
 
 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)
+static size_t kprof_stat(struct chan *c, uint8_t *db, size_t n)
 {
        kproftab[Kprofdataqid].length = kprof_profdata_size();
        kproftab[Kptraceqid].length = kprof_tracedata_size();
@@ -316,6 +214,22 @@ static struct chan *kprof_open(struct chan *c, int omode)
                if (openmode(omode) != O_READ)
                        error(EPERM, ERROR_FIXME);
        }
+       switch ((int) c->qid.path) {
+       case Kprofctlqid:
+               /* We have one global profiler.  Only one FD may be opened at a time for
+                * it.  If we ever have separate profilers, we can create the profiler
+                * here, and every open would get a separate instance. */
+               qlock(&kprof.lock);
+               if (kprof.opened) {
+                       qunlock(&kprof.lock);
+                       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;
+       }
        c->mode = openmode(omode);
        c->flag |= COPEN;
        c->offset = 0;
@@ -324,6 +238,17 @@ static struct chan *kprof_open(struct chan *c, int omode)
 
 static void kprof_close(struct chan *c)
 {
+       if (c->flag & COPEN) {
+               switch ((int) c->qid.path) {
+               case Kprofctlqid:
+                       kprof_stop_profiler();
+                       qlock(&kprof.lock);
+                       profiler_cleanup();
+                       kprof.opened = FALSE;
+                       qunlock(&kprof.lock);
+                       break;
+               }
+       }
 }
 
 static long mpstat_read(void *va, long n, int64_t off)
@@ -395,7 +320,7 @@ static long mpstatraw_read(void *va, long n, int64_t off)
        return n;
 }
 
-static long kprof_read(struct chan *c, void *va, long n, int64_t off)
+static size_t kprof_read(struct chan *c, void *va, size_t n, off64_t off)
 {
        uint64_t w, *bp;
        char *a, *ea;
@@ -427,18 +352,7 @@ static long kprof_read(struct chan *c, void *va, long n, int64_t off)
        return n;
 }
 
-static void kprof_manage_timer(int coreid, struct cmdbuf *cb)
-{
-       if (!strcmp(cb->f[2], "on")) {
-               kprof_enable_timer(coreid, 1);
-       } else if (!strcmp(cb->f[2], "off")) {
-               kprof_enable_timer(coreid, 0);
-       } else {
-               error(EFAIL, "timer needs on|off");
-       }
-}
-
-static long kprof_write(struct chan *c, void *a, long n, int64_t unused)
+static size_t kprof_write(struct chan *c, void *a, size_t n, off64_t unused)
 {
        ERRSTACK(1);
        struct cmdbuf *cb = parsecmd(a, n);
@@ -453,24 +367,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 (cb->nf < 3)
-                               error(EFAIL, "timer {{all, N} {on, off}, period USEC}");
-                       if (!strcmp(cb->f[1], "period")) {
-                               kprof_timer_period = strtoul(cb->f[2], 0, 10);
-                       } else if (!strcmp(cb->f[1], "all")) {
-                               for (int i = 0; i < num_cores; i++)
-                                       kprof_manage_timer(i, cb);
-                       } else {
-                               int pcoreid = strtoul(cb->f[1], 0, 10);
-
-                               if (pcoreid >= num_cores)
-                                       error(EFAIL, "No such coreid %d", pcoreid);
-                               kprof_manage_timer(pcoreid, cb);
-                       }
-               } else if (!strcmp(cb->f[0], "start")) {
+               if (!strcmp(cb->f[0], "start")) {
                        kprof_start_profiler();
                } else if (!strcmp(cb->f[0], "flush")) {
                        kprof_flush_profiler();
@@ -480,15 +377,21 @@ static long kprof_write(struct chan *c, void *a, long n, int64_t unused)
                        error(EFAIL, kprof_control_usage);
                }
                break;
-       case Kprofdataqid:
-               profiler_add_trace((uintptr_t) strtoul(a, 0, 0), 0);
+       case Kptracectlqid:
+               if (cb->nf < 1)
+                       error(EFAIL, "Bad kptrace_ctl option (reset)");
+               if (!strcmp(cb->f[0], "clear")) {
+                       spin_lock_irqsave(&ktrace_lock);
+                       circular_buffer_clear(&ktrace_data);
+                       spin_unlock_irqsave(&ktrace_lock);
+               }
                break;
        case Kptraceqid:
                if (a && (n > 0)) {
                        char *uptr = user_strdup_errno(current, a, n);
 
                        if (uptr) {
-                               trace_printk(false, "%s", uptr);
+                               trace_printk("%s", uptr);
                                user_memdup_free(current, uptr);
                        } else {
                                n = -1;
@@ -554,6 +457,17 @@ size_t kprof_tracedata_read(void *data, size_t size, size_t offset)
        return size;
 }
 
+void kprof_dump_data(void)
+{
+       void *buf;
+       size_t len = kprof_tracedata_size();
+
+       buf = kmalloc(len, MEM_WAIT);
+       kprof_tracedata_read(buf, len, 0);
+       printk("%s", buf);
+       kfree(buf);
+}
+
 void kprof_tracedata_write(const char *pretty_buf, size_t len)
 {
        spin_lock_irqsave(&ktrace_lock);
@@ -572,7 +486,7 @@ static struct trace_printk_buffer *kprof_get_printk_buffer(void)
        static struct trace_printk_buffer *cpu_tpbs;
        static atomic_t alloc_done;
 
-       if (unlikely(!num_cores))
+       if (unlikely(booting))
                return &boot_tpb;
        if (unlikely(!cpu_tpbs)) {
                /* Poor man per-CPU data structure. I really do no like littering global
@@ -590,7 +504,7 @@ static struct trace_printk_buffer *kprof_get_printk_buffer(void)
        return cpu_tpbs + core_id_early();
 }
 
-void trace_vprintk(bool btrace, const char *fmt, va_list args)
+void trace_vprintk(const char *fmt, va_list args)
 {
        struct print_buf {
                char *ptr;
@@ -608,14 +522,6 @@ void trace_vprintk(bool btrace, const char *fmt, va_list args)
                }
        }
 
-       void bt_print(void *opaque, const char *str)
-       {
-               struct print_buf *pb = (struct print_buf *) opaque;
-
-               emit_print_buf_str(pb, "\t", 1);
-               emit_print_buf_str(pb, str, -1);
-       }
-
        static const size_t bufsz = TRACE_PRINTK_BUFFER_SIZE;
        static const size_t usr_bufsz = (3 * bufsz) / 8;
        static const size_t kp_bufsz = bufsz - usr_bufsz;
@@ -638,10 +544,6 @@ void trace_vprintk(bool btrace, const char *fmt, va_list args)
 
        if (pb.ptr[-1] != '\n')
                emit_print_buf_str(&pb, "\n", 1);
-       if (btrace) {
-               emit_print_buf_str(&pb, "\tBacktrace:\n", -1);
-               gen_backtrace(bt_print, &pb);
-       }
        /* snprintf null terminates the buffer, and does not count that as part of
         * the len.  If we maxed out the buffer, let's make sure it has a \n.
         */
@@ -664,12 +566,12 @@ void trace_vprintk(bool btrace, const char *fmt, va_list args)
        atomic_set(&tpb->in_use, 0);
 }
 
-void trace_printk(bool btrace, const char *fmt, ...)
+void trace_printk(const char *fmt, ...)
 {
        va_list args;
 
        va_start(args, fmt);
-       trace_vprintk(btrace, fmt, args);
+       trace_vprintk(fmt, args);
        va_end(args);
 }