oprofile: sampling works
[akaros.git] / kern / drivers / dev / kprof.c
index e15a471..042d86e 100644 (file)
@@ -36,7 +36,7 @@
 #include <pmap.h>
 #include <smp.h>
 #include <ip.h>
-
+#include <oprofile.h>
 
 #define LRES   3               /* log of PC resolution */
 #define CELLSIZE       8       /* sizeof of count cell */
@@ -63,11 +63,13 @@ enum{
        Kprofdirqid,
        Kprofdataqid,
        Kprofctlqid,
+       Kprofoprofileqid,
 };
 struct dirtab kproftab[]={
        {".",           {Kprofdirqid, 0, QTDIR},0,      DMDIR|0550},
        {"kpdata",      {Kprofdataqid},         0,      0600},
        {"kpctl",       {Kprofctlqid},          0,      0600},
+       {"kpoprofile",  {Kprofoprofileqid},     0,      0600},
 };
 
 static struct chan*
@@ -88,6 +90,9 @@ kprofattach(char *spec)
        }
        kproftab[1].length = kprof.nbuf * FORMATSIZE;
        kprof.buf_sz = n;
+       /* NO, I'm not sure how we should do this yet. */
+       int alloc_cpu_buffers(void);
+       alloc_cpu_buffers();
        return devattach('K', spec);
 }
 
@@ -129,19 +134,27 @@ kproftimer(uintptr_t pc)
 
 static void setup_timers(void)
 {
-       void handler(struct alarm_waiter *waiter)
+       void dummy_alarm(struct alarm_waiter *waiter)
        {
                struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
-               kproftimer(per_cpu_info[core_id()].rip);
                set_awaiter_rel(waiter, 1000);
                __set_alarm(tchain, waiter);
        }
+       void kprof_irq_handler(struct hw_trapframe *hw_tf, void *data)
+       {
+               kproftimer(x86_get_hwtf_pc(hw_tf));     /* TODO arch */
+       }
        struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
        struct alarm_waiter *waiter = kmalloc(sizeof(struct alarm_waiter), 0);
-       init_awaiter(waiter, handler);
+       init_awaiter(waiter, dummy_alarm);
        set_awaiter_rel(waiter, 1000);
+       /* with this style, the timer IRQ goes off and runs us, but doesn't get
+        * reprogrammed til the next alarm. TODO: irq/rkm alarms. */
+       register_irq(IdtLAPIC_TIMER, kprof_irq_handler, NULL,
+                    MKBUS(BusLAPIC, 0, 0, 0));
        set_alarm(tchain, waiter);
 }
+
 static void
 kprofinit(void)
 {
@@ -158,6 +171,9 @@ kprofwalk(struct chan *c, struct chan *nc, char **name, int nname)
 static int
 kprofstat(struct chan *c, uint8_t *db, int n)
 {
+       /* barf. */
+       kproftab[3].length = oproflen();
+
        return devstat(c, db, n, kproftab, ARRAY_SIZE(kproftab), devgen);
 }
 
@@ -227,10 +243,18 @@ kprofread(struct chan *c, void *va, long n, int64_t off)
                         * asks for chunks of 32.  it'll get chunks of the next 32 valid
                         * items, over and over (1000/32 times). */
                        w = *bp++;
-                       name = get_fn_name(pc);
+
+                       if (pc == kprof.minpc)
+                               name = "Total";
+                       else if (pc == kprof.minpc + 8)
+                               name = "User";
+                       else
+                               name = get_fn_name(pc);
+
                        snp_ret = snprintf(print, sizeof(print), outformat, pc, name, w);
                        assert(snp_ret == FORMATSIZE);
-                       kfree(name);
+                       if ((pc != kprof.minpc) && (pc != kprof.minpc + 8))
+                               kfree(name);
 
                        amt_read = readmem(offset % FORMATSIZE, a, n, print, FORMATSIZE);
                        offset = 0;     /* future loops have no offset */
@@ -243,7 +267,9 @@ kprofread(struct chan *c, void *va, long n, int64_t off)
                }
                n = ret;
                break;
-
+       case Kprofoprofileqid:
+               n = oprofread(va,n);
+               break;
        default:
                n = 0;
                break;
@@ -261,6 +287,7 @@ static void kprof_clear(struct kprof *kp)
 static long
 kprofwrite(struct chan *c, void *a, long n, int64_t unused)
 {
+       uintptr_t pc;
        switch((int)(c->qid.path)){
        case Kprofctlqid:
                if(strncmp(a, "startclr", 8) == 0){
@@ -273,8 +300,23 @@ kprofwrite(struct chan *c, void *a, long n, int64_t unused)
                        kprof.time = 0;
                } else if(strncmp(a, "clear", 5) == 0) {
                        kprof_clear(&kprof);
+               }else if(strncmp(a, "opstart", 7) == 0) {
+                       oprofile_control_trace(1);
+               }else if(strncmp(a, "opstop", 6) == 0) {
+                       oprofile_control_trace(0);
+               } else  {
+                       printk("startclr|start|stop|clear|opstart|opstop");
+                       error("startclr|start|stop|clear|opstart|opstop");
                }
                break;
+
+               /* The format is a long as text. We strtoul, and jam it into the
+                * trace buffer.
+                */
+       case Kprofoprofileqid:
+               pc = strtoul(a, 0, 0);
+               oprofile_add_trace(pc);
+               break;
        default:
                error(Ebadusefd);
        }