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