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