Kprof samples during the timer IRQ
[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 /* the oprofile queue */
76 extern struct queue *opq;
77
78 static struct chan*
79 kprofattach(char *spec)
80 {
81         uint32_t n;
82
83         /* allocate when first used */
84         kprof.minpc = 0xffffffffc2000000; //KERN_LOAD_ADDR;
85         kprof.maxpc = (uintptr_t)&etext;
86         kprof.nbuf = (kprof.maxpc-kprof.minpc) >> LRES;
87         n = kprof.nbuf*CELLSIZE;
88         if(kprof.buf == 0) {
89                 printk("Allocate %d bytes\n", n);
90                 kprof.buf = kzmalloc(n, KMALLOC_WAIT);
91                 if(kprof.buf == 0)
92                         error(Enomem);
93         }
94         kproftab[1].length = kprof.nbuf * FORMATSIZE;
95         kprof.buf_sz = n;
96         /* NO, I'm not sure how we should do this yet. */
97         int alloc_cpu_buffers(void);
98         alloc_cpu_buffers();
99         return devattach('K', spec);
100 }
101
102 static void
103 kproftimer(uintptr_t pc)
104 {
105         if(kprof.time == 0)
106                 return;
107
108         /*
109          * if the pc corresponds to the idle loop, don't consider it.
110
111         if(m->inidle)
112                 return;
113          */
114         /*
115          *  if the pc is coming out of spllo or splx,
116          *  use the pc saved when we went splhi.
117
118         if(pc>=PTR2UINT(spllo) && pc<=PTR2UINT(spldone))
119                 pc = m->splpc;
120          */
121
122 //      ilock(&kprof);
123         /* this is weird. What we do is assume that all the time since the last
124          * measurement went into this PC. It's the best
125          * we can do I suppose. And we are sampling at 1 ms. for now.
126          * better ideas welcome.
127          */
128         kprof.buf[0] += 1; //Total count of ticks.
129         if(kprof.minpc<=pc && pc<kprof.maxpc){
130                 pc -= kprof.minpc;
131                 pc >>= LRES;
132                 kprof.buf[pc] += 1;
133         }else
134                 kprof.buf[1] += 1; // Why?
135 //      iunlock(&kprof);
136 }
137
138 static void setup_timers(void)
139 {
140         void dummy_alarm(struct alarm_waiter *waiter)
141         {
142                 struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
143                 set_awaiter_rel(waiter, 1000);
144                 __set_alarm(tchain, waiter);
145         }
146         void kprof_irq_handler(struct hw_trapframe *hw_tf, void *data)
147         {
148                 kproftimer(x86_get_hwtf_pc(hw_tf));     /* TODO arch */
149         }
150         struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
151         struct alarm_waiter *waiter = kmalloc(sizeof(struct alarm_waiter), 0);
152         init_awaiter(waiter, dummy_alarm);
153         set_awaiter_rel(waiter, 1000);
154         /* with this style, the timer IRQ goes off and runs us, but doesn't get
155          * reprogrammed til the next alarm. TODO: irq/rkm alarms. */
156         register_irq(IdtLAPIC_TIMER, kprof_irq_handler, NULL,
157                      MKBUS(BusLAPIC, 0, 0, 0));
158         set_alarm(tchain, waiter);
159 }
160
161 static void
162 kprofinit(void)
163 {
164         if(CELLSIZE != sizeof kprof.buf[0])
165                 panic("kprof size");
166 }
167
168 static struct walkqid*
169 kprofwalk(struct chan *c, struct chan *nc, char **name, int nname)
170 {
171         return devwalk(c, nc, name, nname, kproftab, ARRAY_SIZE(kproftab), devgen);
172 }
173
174 static int
175 kprofstat(struct chan *c, uint8_t *db, int n)
176 {
177         /* barf. */
178         kproftab[3].length = qlen(opq);
179
180         return devstat(c, db, n, kproftab, ARRAY_SIZE(kproftab), devgen);
181 }
182
183 static struct chan*
184 kprofopen(struct chan *c, int omode)
185 {
186         if(c->qid.type & QTDIR){
187                 if(omode != OREAD)
188                         error(Eperm);
189         }
190         c->mode = openmode(omode);
191         c->flag |= COPEN;
192         c->offset = 0;
193         return c;
194 }
195
196 static void
197 kprofclose(struct chan*unused)
198 {
199 }
200
201 static long
202 kprofread(struct chan *c, void *va, long n, int64_t off)
203 {
204         uint64_t w, *bp;
205         char *a, *ea;
206         uintptr_t offset = off;
207         uint64_t pc;
208         int snp_ret, ret = 0;
209
210         switch((int)c->qid.path){
211         case Kprofdirqid:
212                 return devdirread(c, va, n, kproftab, ARRAY_SIZE(kproftab), devgen);
213
214         case Kprofdataqid:
215
216                 if (n < FORMATSIZE){
217                         n = 0;
218                         break;
219                 }
220                 a = va;
221                 ea = a + n;
222
223                 /* we check offset later before deref bp.  offset / FORMATSIZE is how
224                  * many entries we're skipping/offsetting. */
225                 bp = kprof.buf + offset/FORMATSIZE;
226                 pc = kprof.minpc + ((offset/FORMATSIZE)<<LRES);
227                 while((a < ea) && (n >= FORMATSIZE)){
228                         /* what a pain. We need to manage the
229                          * fact that the *prints all make room for
230                          * \0
231                          */
232                         char print[FORMATSIZE+1];
233                         char *name;
234                         int amt_read;
235
236                         if (pc >= kprof.maxpc)
237                                 break;
238                         /* pc is also our exit for bp.  should be in lockstep */
239                         // XXX this assert fails, fix it!
240                         //assert(bp < kprof.buf + kprof.nbuf);
241                         /* do not attempt to filter these results based on w < threshold.
242                          * earlier, we computed bp/pc based on assuming a full-sized file,
243                          * and skipping entries will result in read() calls thinking they
244                          * received earlier entries when they really received later ones.
245                          * imagine a case where there are 1000 skipped items, and read()
246                          * asks for chunks of 32.  it'll get chunks of the next 32 valid
247                          * items, over and over (1000/32 times). */
248                         w = *bp++;
249
250                         if (pc == kprof.minpc)
251                                 name = "Total";
252                         else if (pc == kprof.minpc + 8)
253                                 name = "User";
254                         else
255                                 name = get_fn_name(pc);
256
257                         snp_ret = snprintf(print, sizeof(print), outformat, pc, name, w);
258                         assert(snp_ret == FORMATSIZE);
259                         if ((pc != kprof.minpc) && (pc != kprof.minpc + 8))
260                                 kfree(name);
261
262                         amt_read = readmem(offset % FORMATSIZE, a, n, print, FORMATSIZE);
263                         offset = 0;     /* future loops have no offset */
264
265                         a += amt_read;
266                         n -= amt_read;
267                         ret += amt_read;
268
269                         pc += (1 << LRES);
270                 }
271                 n = ret;
272                 break;
273         case Kprofoprofileqid:
274                 n = qread(opq, va, n);
275                 break;
276         default:
277                 n = 0;
278                 break;
279         }
280         return n;
281 }
282
283 static void kprof_clear(struct kprof *kp)
284 {
285         spin_lock(&kp->lock);
286         memset(kp->buf, 0, kp->buf_sz);
287         spin_unlock(&kp->lock);
288 }
289
290 static long
291 kprofwrite(struct chan *c, void *a, long n, int64_t unused)
292 {
293         uintptr_t pc;
294         switch((int)(c->qid.path)){
295         case Kprofctlqid:
296                 if(strncmp(a, "startclr", 8) == 0){
297                         kprof_clear(&kprof);
298                         kprof.time = 1;
299                 }else if(strncmp(a, "start", 5) == 0) {
300                         kprof.time = 1;
301                         setup_timers();
302                 } else if(strncmp(a, "stop", 4) == 0) {
303                         kprof.time = 0;
304                 } else if(strncmp(a, "clear", 5) == 0) {
305                         kprof_clear(&kprof);
306                 }else if(strncmp(a, "opstart", 7) == 0) {
307                         oprofile_control_trace(1);
308                 }else if(strncmp(a, "opstop", 6) == 0) {
309                         oprofile_control_trace(0);
310                 } else  {
311                         printk("startclr|start|stop|clear|opstart|opstop");
312                         error("startclr|start|stop|clear|opstart|opstop");
313                 }
314                 break;
315
316                 /* The format is a long as text. We strtoul, and jam it into the
317                  * trace buffer.
318                  */
319         case Kprofoprofileqid:
320                 pc = strtoul(a, 0, 0);
321                 oprofile_add_trace(pc);
322                 break;
323         default:
324                 error(Ebadusefd);
325         }
326         return n;
327 }
328
329 struct dev kprofdevtab __devtab = {
330         'K',
331         "kprof",
332
333         devreset,
334         kprofinit,
335         devshutdown,
336         kprofattach,
337         kprofwalk,
338         kprofstat,
339         kprofopen,
340         devcreate,
341         kprofclose,
342         kprofread,
343         devbread,
344         kprofwrite,
345         devbwrite,
346         devremove,
347         devwstat,
348 };