oprofile: sampling works
[akaros.git] / kern / drivers / dev / kprof.c
1 /*
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9
10 // get_fn_name is slowing down the kprocread 
11 //      have an array of translated fns
12 //      or a "next" iterator, since we're walking in order
13 //
14 // irqsave locks
15 //
16 // kprof struct should be a ptr, have them per core
17 //              we'll probably need to track the length still, so userspace knows how
18 //              big it is
19 //
20 //              will also want more files in the kprof dir for each cpu or something
21 //              
22 // maybe don't use slot 0 and 1 as total and 'not kernel' ticks
23 //
24 // fix the failed assert XXX
25
26 #include <vfs.h>
27 #include <kfs.h>
28 #include <slab.h>
29 #include <kmalloc.h>
30 #include <kref.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <assert.h>
34 #include <error.h>
35 #include <cpio.h>
36 #include <pmap.h>
37 #include <smp.h>
38 #include <ip.h>
39 #include <oprofile.h>
40
41 #define LRES    3               /* log of PC resolution */
42 #define CELLSIZE        8       /* sizeof of count cell */
43
44 struct kprof
45 {
46         uintptr_t       minpc;
47         uintptr_t       maxpc;
48         int     nbuf;
49         int     time;
50         uint64_t        *buf;   /* keep in sync with cellsize */
51         size_t          buf_sz;
52         spinlock_t lock;
53 };
54 struct kprof kprof;
55
56 /* output format. Nice fixed size. That makes it seekable.
57  * small subtle bit here. You have to convert offset FROM FORMATSIZE units
58  * to CELLSIZE units in a few places.
59  */
60 char *outformat = "%016llx %29.29s %016llx\n";
61 #define FORMATSIZE 64
62 enum{
63         Kprofdirqid,
64         Kprofdataqid,
65         Kprofctlqid,
66         Kprofoprofileqid,
67 };
68 struct dirtab kproftab[]={
69         {".",           {Kprofdirqid, 0, QTDIR},0,      DMDIR|0550},
70         {"kpdata",      {Kprofdataqid},         0,      0600},
71         {"kpctl",       {Kprofctlqid},          0,      0600},
72         {"kpoprofile",  {Kprofoprofileqid},     0,      0600},
73 };
74
75 static struct chan*
76 kprofattach(char *spec)
77 {
78         uint32_t n;
79
80         /* allocate when first used */
81         kprof.minpc = 0xffffffffc2000000; //KERN_LOAD_ADDR;
82         kprof.maxpc = (uintptr_t)&etext;
83         kprof.nbuf = (kprof.maxpc-kprof.minpc) >> LRES;
84         n = kprof.nbuf*CELLSIZE;
85         if(kprof.buf == 0) {
86                 printk("Allocate %d bytes\n", n);
87                 kprof.buf = kzmalloc(n, KMALLOC_WAIT);
88                 if(kprof.buf == 0)
89                         error(Enomem);
90         }
91         kproftab[1].length = kprof.nbuf * FORMATSIZE;
92         kprof.buf_sz = n;
93         /* NO, I'm not sure how we should do this yet. */
94         int alloc_cpu_buffers(void);
95         alloc_cpu_buffers();
96         return devattach('K', spec);
97 }
98
99 static void
100 kproftimer(uintptr_t pc)
101 {
102         if(kprof.time == 0)
103                 return;
104
105         /*
106          * if the pc corresponds to the idle loop, don't consider it.
107
108         if(m->inidle)
109                 return;
110          */
111         /*
112          *  if the pc is coming out of spllo or splx,
113          *  use the pc saved when we went splhi.
114
115         if(pc>=PTR2UINT(spllo) && pc<=PTR2UINT(spldone))
116                 pc = m->splpc;
117          */
118
119 //      ilock(&kprof);
120         /* this is weird. What we do is assume that all the time since the last
121          * measurement went into this PC. It's the best
122          * we can do I suppose. And we are sampling at 1 ms. for now.
123          * better ideas welcome.
124          */
125         kprof.buf[0] += 1; //Total count of ticks.
126         if(kprof.minpc<=pc && pc<kprof.maxpc){
127                 pc -= kprof.minpc;
128                 pc >>= LRES;
129                 kprof.buf[pc] += 1;
130         }else
131                 kprof.buf[1] += 1; // Why?
132 //      iunlock(&kprof);
133 }
134
135 static void setup_timers(void)
136 {
137         void dummy_alarm(struct alarm_waiter *waiter)
138         {
139                 struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
140                 set_awaiter_rel(waiter, 1000);
141                 __set_alarm(tchain, waiter);
142         }
143         void kprof_irq_handler(struct hw_trapframe *hw_tf, void *data)
144         {
145                 kproftimer(x86_get_hwtf_pc(hw_tf));     /* TODO arch */
146         }
147         struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
148         struct alarm_waiter *waiter = kmalloc(sizeof(struct alarm_waiter), 0);
149         init_awaiter(waiter, dummy_alarm);
150         set_awaiter_rel(waiter, 1000);
151         /* with this style, the timer IRQ goes off and runs us, but doesn't get
152          * reprogrammed til the next alarm. TODO: irq/rkm alarms. */
153         register_irq(IdtLAPIC_TIMER, kprof_irq_handler, NULL,
154                      MKBUS(BusLAPIC, 0, 0, 0));
155         set_alarm(tchain, waiter);
156 }
157
158 static void
159 kprofinit(void)
160 {
161         if(CELLSIZE != sizeof kprof.buf[0])
162                 panic("kprof size");
163 }
164
165 static struct walkqid*
166 kprofwalk(struct chan *c, struct chan *nc, char **name, int nname)
167 {
168         return devwalk(c, nc, name, nname, kproftab, ARRAY_SIZE(kproftab), devgen);
169 }
170
171 static int
172 kprofstat(struct chan *c, uint8_t *db, int n)
173 {
174         /* barf. */
175         kproftab[3].length = oproflen();
176
177         return devstat(c, db, n, kproftab, ARRAY_SIZE(kproftab), devgen);
178 }
179
180 static struct chan*
181 kprofopen(struct chan *c, int omode)
182 {
183         if(c->qid.type & QTDIR){
184                 if(omode != OREAD)
185                         error(Eperm);
186         }
187         c->mode = openmode(omode);
188         c->flag |= COPEN;
189         c->offset = 0;
190         return c;
191 }
192
193 static void
194 kprofclose(struct chan*unused)
195 {
196 }
197
198 static long
199 kprofread(struct chan *c, void *va, long n, int64_t off)
200 {
201         uint64_t w, *bp;
202         char *a, *ea;
203         uintptr_t offset = off;
204         uint64_t pc;
205         int snp_ret, ret = 0;
206
207         switch((int)c->qid.path){
208         case Kprofdirqid:
209                 return devdirread(c, va, n, kproftab, ARRAY_SIZE(kproftab), devgen);
210
211         case Kprofdataqid:
212
213                 if (n < FORMATSIZE){
214                         n = 0;
215                         break;
216                 }
217                 a = va;
218                 ea = a + n;
219
220                 /* we check offset later before deref bp.  offset / FORMATSIZE is how
221                  * many entries we're skipping/offsetting. */
222                 bp = kprof.buf + offset/FORMATSIZE;
223                 pc = kprof.minpc + ((offset/FORMATSIZE)<<LRES);
224                 while((a < ea) && (n >= FORMATSIZE)){
225                         /* what a pain. We need to manage the
226                          * fact that the *prints all make room for
227                          * \0
228                          */
229                         char print[FORMATSIZE+1];
230                         char *name;
231                         int amt_read;
232
233                         if (pc >= kprof.maxpc)
234                                 break;
235                         /* pc is also our exit for bp.  should be in lockstep */
236                         // XXX this assert fails, fix it!
237                         //assert(bp < kprof.buf + kprof.nbuf);
238                         /* do not attempt to filter these results based on w < threshold.
239                          * earlier, we computed bp/pc based on assuming a full-sized file,
240                          * and skipping entries will result in read() calls thinking they
241                          * received earlier entries when they really received later ones.
242                          * imagine a case where there are 1000 skipped items, and read()
243                          * asks for chunks of 32.  it'll get chunks of the next 32 valid
244                          * items, over and over (1000/32 times). */
245                         w = *bp++;
246
247                         if (pc == kprof.minpc)
248                                 name = "Total";
249                         else if (pc == kprof.minpc + 8)
250                                 name = "User";
251                         else
252                                 name = get_fn_name(pc);
253
254                         snp_ret = snprintf(print, sizeof(print), outformat, pc, name, w);
255                         assert(snp_ret == FORMATSIZE);
256                         if ((pc != kprof.minpc) && (pc != kprof.minpc + 8))
257                                 kfree(name);
258
259                         amt_read = readmem(offset % FORMATSIZE, a, n, print, FORMATSIZE);
260                         offset = 0;     /* future loops have no offset */
261
262                         a += amt_read;
263                         n -= amt_read;
264                         ret += amt_read;
265
266                         pc += (1 << LRES);
267                 }
268                 n = ret;
269                 break;
270         case Kprofoprofileqid:
271                 n = oprofread(va,n);
272                 break;
273         default:
274                 n = 0;
275                 break;
276         }
277         return n;
278 }
279
280 static void kprof_clear(struct kprof *kp)
281 {
282         spin_lock(&kp->lock);
283         memset(kp->buf, 0, kp->buf_sz);
284         spin_unlock(&kp->lock);
285 }
286
287 static long
288 kprofwrite(struct chan *c, void *a, long n, int64_t unused)
289 {
290         uintptr_t pc;
291         switch((int)(c->qid.path)){
292         case Kprofctlqid:
293                 if(strncmp(a, "startclr", 8) == 0){
294                         kprof_clear(&kprof);
295                         kprof.time = 1;
296                 }else if(strncmp(a, "start", 5) == 0) {
297                         kprof.time = 1;
298                         setup_timers();
299                 } else if(strncmp(a, "stop", 4) == 0) {
300                         kprof.time = 0;
301                 } else if(strncmp(a, "clear", 5) == 0) {
302                         kprof_clear(&kprof);
303                 }else if(strncmp(a, "opstart", 7) == 0) {
304                         oprofile_control_trace(1);
305                 }else if(strncmp(a, "opstop", 6) == 0) {
306                         oprofile_control_trace(0);
307                 } else  {
308                         printk("startclr|start|stop|clear|opstart|opstop");
309                         error("startclr|start|stop|clear|opstart|opstop");
310                 }
311                 break;
312
313                 /* The format is a long as text. We strtoul, and jam it into the
314                  * trace buffer.
315                  */
316         case Kprofoprofileqid:
317                 pc = strtoul(a, 0, 0);
318                 oprofile_add_trace(pc);
319                 break;
320         default:
321                 error(Ebadusefd);
322         }
323         return n;
324 }
325
326 struct dev kprofdevtab __devtab = {
327         'K',
328         "kprof",
329
330         devreset,
331         kprofinit,
332         devshutdown,
333         kprofattach,
334         kprofwalk,
335         kprofstat,
336         kprofopen,
337         devcreate,
338         kprofclose,
339         kprofread,
340         devbread,
341         kprofwrite,
342         devbwrite,
343         devremove,
344         devwstat,
345 };