00f640128f8538501119ae2b343389df4a54a223
[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
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                 return qread(opq, va, n);
263         default:
264                 n = 0;
265                 break;
266         }
267         return n;
268 }
269
270 static void kprof_clear(struct kprof *kp)
271 {
272         spin_lock(&kp->lock);
273         memset(kp->buf, 0, kp->buf_sz);
274         spin_unlock(&kp->lock);
275 }
276
277 static long
278 kprofwrite(struct chan *c, void *a, long n, int64_t unused)
279 {
280         switch((int)(c->qid.path)){
281         case Kprofctlqid:
282                 if(strncmp(a, "startclr", 8) == 0){
283                         kprof_clear(&kprof);
284                         kprof.time = 1;
285                 }else if(strncmp(a, "start", 5) == 0) {
286                         kprof.time = 1;
287                         setup_timers();
288                 } else if(strncmp(a, "stop", 4) == 0) {
289                         kprof.time = 0;
290                 } else if(strncmp(a, "clear", 5) == 0) {
291                         kprof_clear(&kprof);
292                 }
293                 break;
294         default:
295                 error(Ebadusefd);
296         }
297         return n;
298 }
299
300 struct dev kprofdevtab __devtab = {
301         'K',
302         "kprof",
303
304         devreset,
305         kprofinit,
306         devshutdown,
307         kprofattach,
308         kprofwalk,
309         kprofstat,
310         kprofopen,
311         devcreate,
312         kprofclose,
313         kprofread,
314         devbread,
315         kprofwrite,
316         devbwrite,
317         devremove,
318         devwstat,
319 };