Add gross hack so we can see how big the oprofile queue is
[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 handler(struct alarm_waiter *waiter)
141         {
142                 struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
143                 kproftimer(per_cpu_info[core_id()].rip);
144                 set_awaiter_rel(waiter, 1000);
145                 __set_alarm(tchain, waiter);
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, handler);
150         set_awaiter_rel(waiter, 1000);
151         set_alarm(tchain, waiter);
152 }
153 static void
154 kprofinit(void)
155 {
156         if(CELLSIZE != sizeof kprof.buf[0])
157                 panic("kprof size");
158 }
159
160 static struct walkqid*
161 kprofwalk(struct chan *c, struct chan *nc, char **name, int nname)
162 {
163         return devwalk(c, nc, name, nname, kproftab, ARRAY_SIZE(kproftab), devgen);
164 }
165
166 static int
167 kprofstat(struct chan *c, uint8_t *db, int n)
168 {
169         /* barf. */
170         kproftab[3].length = qlen(opq);
171
172         return devstat(c, db, n, kproftab, ARRAY_SIZE(kproftab), devgen);
173 }
174
175 static struct chan*
176 kprofopen(struct chan *c, int omode)
177 {
178         if(c->qid.type & QTDIR){
179                 if(omode != OREAD)
180                         error(Eperm);
181         }
182         c->mode = openmode(omode);
183         c->flag |= COPEN;
184         c->offset = 0;
185         return c;
186 }
187
188 static void
189 kprofclose(struct chan*unused)
190 {
191 }
192
193 static long
194 kprofread(struct chan *c, void *va, long n, int64_t off)
195 {
196         uint64_t w, *bp;
197         char *a, *ea;
198         uintptr_t offset = off;
199         uint64_t pc;
200         int snp_ret, ret = 0;
201
202         switch((int)c->qid.path){
203         case Kprofdirqid:
204                 return devdirread(c, va, n, kproftab, ARRAY_SIZE(kproftab), devgen);
205
206         case Kprofdataqid:
207
208                 if (n < FORMATSIZE){
209                         n = 0;
210                         break;
211                 }
212                 a = va;
213                 ea = a + n;
214
215                 /* we check offset later before deref bp.  offset / FORMATSIZE is how
216                  * many entries we're skipping/offsetting. */
217                 bp = kprof.buf + offset/FORMATSIZE;
218                 pc = kprof.minpc + ((offset/FORMATSIZE)<<LRES);
219                 while((a < ea) && (n >= FORMATSIZE)){
220                         /* what a pain. We need to manage the
221                          * fact that the *prints all make room for
222                          * \0
223                          */
224                         char print[FORMATSIZE+1];
225                         char *name;
226                         int amt_read;
227
228                         if (pc >= kprof.maxpc)
229                                 break;
230                         /* pc is also our exit for bp.  should be in lockstep */
231                         // XXX this assert fails, fix it!
232                         //assert(bp < kprof.buf + kprof.nbuf);
233                         /* do not attempt to filter these results based on w < threshold.
234                          * earlier, we computed bp/pc based on assuming a full-sized file,
235                          * and skipping entries will result in read() calls thinking they
236                          * received earlier entries when they really received later ones.
237                          * imagine a case where there are 1000 skipped items, and read()
238                          * asks for chunks of 32.  it'll get chunks of the next 32 valid
239                          * items, over and over (1000/32 times). */
240                         w = *bp++;
241
242                         if (pc == kprof.minpc)
243                                 name = "Total";
244                         else if (pc == kprof.minpc + 8)
245                                 name = "User";
246                         else
247                                 name = get_fn_name(pc);
248
249                         snp_ret = snprintf(print, sizeof(print), outformat, pc, name, w);
250                         assert(snp_ret == FORMATSIZE);
251                         if ((pc != kprof.minpc) && (pc != kprof.minpc + 8))
252                                 kfree(name);
253
254                         amt_read = readmem(offset % FORMATSIZE, a, n, print, FORMATSIZE);
255                         offset = 0;     /* future loops have no offset */
256
257                         a += amt_read;
258                         n -= amt_read;
259                         ret += amt_read;
260
261                         pc += (1 << LRES);
262                 }
263                 n = ret;
264                 break;
265         case Kprofoprofileqid:
266                 n = qread(opq, va, n);
267                 break;
268         default:
269                 n = 0;
270                 break;
271         }
272         return n;
273 }
274
275 static void kprof_clear(struct kprof *kp)
276 {
277         spin_lock(&kp->lock);
278         memset(kp->buf, 0, kp->buf_sz);
279         spin_unlock(&kp->lock);
280 }
281
282 static long
283 kprofwrite(struct chan *c, void *a, long n, int64_t unused)
284 {
285         uintptr_t pc;
286         switch((int)(c->qid.path)){
287         case Kprofctlqid:
288                 if(strncmp(a, "startclr", 8) == 0){
289                         kprof_clear(&kprof);
290                         kprof.time = 1;
291                 }else if(strncmp(a, "start", 5) == 0) {
292                         kprof.time = 1;
293                         setup_timers();
294                 } else if(strncmp(a, "stop", 4) == 0) {
295                         kprof.time = 0;
296                 } else if(strncmp(a, "clear", 5) == 0) {
297                         kprof_clear(&kprof);
298                 }else if(strncmp(a, "opstart", 7) == 0) {
299                         oprofile_control_trace(1);
300                 }else if(strncmp(a, "opstop", 6) == 0) {
301                         oprofile_control_trace(0);
302                 } else  {
303                         printk("startclr|start|stop|clear|opstart|opstop");
304                         error("startclr|start|stop|clear|opstart|opstop");
305                 }
306                 break;
307
308                 /* The format is a long as text. We strtoul, and jam it into the
309                  * trace buffer.
310                  */
311         case Kprofoprofileqid:
312                 pc = strtoul(a, 0, 0);
313                 oprofile_add_trace(pc);
314                 break;
315         default:
316                 error(Ebadusefd);
317         }
318         return n;
319 }
320
321 struct dev kprofdevtab __devtab = {
322         'K',
323         "kprof",
324
325         devreset,
326         kprofinit,
327         devshutdown,
328         kprofattach,
329         kprofwalk,
330         kprofstat,
331         kprofopen,
332         devcreate,
333         kprofclose,
334         kprofread,
335         devbread,
336         kprofwrite,
337         devbwrite,
338         devremove,
339         devwstat,
340 };