Only linearizeblock on #M qremoves
[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 kprof_alarm(struct alarm_waiter *waiter, struct hw_trapframe *hw_tf)
138         {
139                 struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
140                 kproftimer(get_hwtf_pc(hw_tf));
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_irq(waiter, kprof_alarm);
147         set_awaiter_rel(waiter, 1000);
148         set_alarm(tchain, waiter);
149 }
150
151 static void
152 kprofinit(void)
153 {
154         if(CELLSIZE != sizeof kprof.buf[0])
155                 panic("kprof size");
156 }
157
158 static struct walkqid*
159 kprofwalk(struct chan *c, struct chan *nc, char **name, int nname)
160 {
161         return devwalk(c, nc, name, nname, kproftab, ARRAY_SIZE(kproftab), devgen);
162 }
163
164 static int
165 kprofstat(struct chan *c, uint8_t *db, int n)
166 {
167         /* barf. */
168         kproftab[3].length = oproflen();
169
170         return devstat(c, db, n, kproftab, ARRAY_SIZE(kproftab), devgen);
171 }
172
173 static struct chan*
174 kprofopen(struct chan *c, int omode)
175 {
176         if(c->qid.type & QTDIR){
177                 if(omode != OREAD)
178                         error(Eperm);
179         }
180         c->mode = openmode(omode);
181         c->flag |= COPEN;
182         c->offset = 0;
183         return c;
184 }
185
186 static void
187 kprofclose(struct chan*unused)
188 {
189 }
190
191 static long
192 kprofread(struct chan *c, void *va, long n, int64_t off)
193 {
194         uint64_t w, *bp;
195         char *a, *ea;
196         uintptr_t offset = off;
197         uint64_t pc;
198         int snp_ret, ret = 0;
199
200         switch((int)c->qid.path){
201         case Kprofdirqid:
202                 return devdirread(c, va, n, kproftab, ARRAY_SIZE(kproftab), devgen);
203
204         case Kprofdataqid:
205
206                 if (n < FORMATSIZE){
207                         n = 0;
208                         break;
209                 }
210                 a = va;
211                 ea = a + n;
212
213                 /* we check offset later before deref bp.  offset / FORMATSIZE is how
214                  * many entries we're skipping/offsetting. */
215                 bp = kprof.buf + offset/FORMATSIZE;
216                 pc = kprof.minpc + ((offset/FORMATSIZE)<<LRES);
217                 while((a < ea) && (n >= FORMATSIZE)){
218                         /* what a pain. We need to manage the
219                          * fact that the *prints all make room for
220                          * \0
221                          */
222                         char print[FORMATSIZE+1];
223                         char *name;
224                         int amt_read;
225
226                         if (pc >= kprof.maxpc)
227                                 break;
228                         /* pc is also our exit for bp.  should be in lockstep */
229                         // XXX this assert fails, fix it!
230                         //assert(bp < kprof.buf + kprof.nbuf);
231                         /* do not attempt to filter these results based on w < threshold.
232                          * earlier, we computed bp/pc based on assuming a full-sized file,
233                          * and skipping entries will result in read() calls thinking they
234                          * received earlier entries when they really received later ones.
235                          * imagine a case where there are 1000 skipped items, and read()
236                          * asks for chunks of 32.  it'll get chunks of the next 32 valid
237                          * items, over and over (1000/32 times). */
238                         w = *bp++;
239
240                         if (pc == kprof.minpc)
241                                 name = "Total";
242                         else if (pc == kprof.minpc + 8)
243                                 name = "User";
244                         else
245                                 name = get_fn_name(pc);
246
247                         snp_ret = snprintf(print, sizeof(print), outformat, pc, name, w);
248                         assert(snp_ret == FORMATSIZE);
249                         if ((pc != kprof.minpc) && (pc != kprof.minpc + 8))
250                                 kfree(name);
251
252                         amt_read = readmem(offset % FORMATSIZE, a, n, print, FORMATSIZE);
253                         offset = 0;     /* future loops have no offset */
254
255                         a += amt_read;
256                         n -= amt_read;
257                         ret += amt_read;
258
259                         pc += (1 << LRES);
260                 }
261                 n = ret;
262                 break;
263         case Kprofoprofileqid:
264                 n = oprofread(va,n);
265                 break;
266         default:
267                 n = 0;
268                 break;
269         }
270         return n;
271 }
272
273 static void kprof_clear(struct kprof *kp)
274 {
275         spin_lock(&kp->lock);
276         memset(kp->buf, 0, kp->buf_sz);
277         spin_unlock(&kp->lock);
278 }
279
280 static long
281 kprofwrite(struct chan *c, void *a, long n, int64_t unused)
282 {
283         uintptr_t pc;
284         switch((int)(c->qid.path)){
285         case Kprofctlqid:
286                 if(strncmp(a, "startclr", 8) == 0){
287                         kprof_clear(&kprof);
288                         kprof.time = 1;
289                 }else if(strncmp(a, "start", 5) == 0) {
290                         kprof.time = 1;
291                         /* this sets up the timer on the *calling* core! */
292                         setup_timers();
293                 } else if(strncmp(a, "stop", 4) == 0) {
294                         /* TODO: stop the timers! */
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 };