Add more info to the kprof and fix a big
[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 #include <vfs.h>
11 #include <kfs.h>
12 #include <slab.h>
13 #include <kmalloc.h>
14 #include <kref.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <error.h>
19 #include <cpio.h>
20 #include <pmap.h>
21 #include <smp.h>
22 #include <ip.h>
23
24
25 #define LRES    3               /* log of PC resolution */
26 #define CELLSIZE        8       /* sizeof of count cell; well known as 4 */
27
28 struct
29 {
30         uintptr_t       minpc;
31         uintptr_t       maxpc;
32         int     nbuf;
33         int     time;
34         uint64_t        *buf;
35         spinlock_t lock;
36 }kprof;
37
38 /* output format. Nice fixed size. That makes it seekable.
39  * small subtle bit here. You have to convert offset FROM FORMATSIZE units
40  * to CELLSIZE units in a few places.
41  */
42 char *outformat = "%016llx %29s %016llx\n";
43 #define FORMATSIZE 64
44 enum{
45         Kprofdirqid,
46         Kprofdataqid,
47         Kprofctlqid,
48 };
49 struct dirtab kproftab[]={
50         {".",           {Kprofdirqid, 0, QTDIR},0,      DMDIR|0550},
51         {"kpdata",      {Kprofdataqid},         0,      0600},
52         {"kpctl",       {Kprofctlqid},          0,      0600},
53 };
54
55 static struct chan*
56 kprofattach(char *spec)
57 {
58         uint32_t n;
59
60         /* allocate when first used */
61         kprof.minpc = 0xffffffffc2000000; //KERN_LOAD_ADDR;
62         kprof.maxpc = (uintptr_t)&etext;
63         kprof.nbuf = (kprof.maxpc-kprof.minpc) >> LRES;
64         n = kprof.nbuf*CELLSIZE;
65         if(kprof.buf == 0) {
66                 printk("Allocate %d bytes\n", n);
67                 kprof.buf = kzmalloc(n, KMALLOC_WAIT);
68                 if(kprof.buf == 0)
69                         error(Enomem);
70         }
71         kproftab[1].length = n;
72         return devattach('K', spec);
73 }
74
75 static void
76 kproftimer(uintptr_t pc)
77 {
78         if(kprof.time == 0)
79                 return;
80
81         /*
82          * if the pc corresponds to the idle loop, don't consider it.
83
84         if(m->inidle)
85                 return;
86          */
87         /*
88          *  if the pc is coming out of spllo or splx,
89          *  use the pc saved when we went splhi.
90
91         if(pc>=PTR2UINT(spllo) && pc<=PTR2UINT(spldone))
92                 pc = m->splpc;
93          */
94
95 //      ilock(&kprof);
96         /* this is weird. What we do is assume that all the time since the last
97          * measurement went into this PC. It's the best
98          * we can do I suppose. And we are sampling at 1 ms. for now.
99          * better ideas welcome.
100          */
101         kprof.buf[0] += 1; //Total count of ticks.
102         if(kprof.minpc<=pc && pc<kprof.maxpc){
103                 pc -= kprof.minpc;
104                 pc >>= LRES;
105                 kprof.buf[pc] += 1;
106         }else
107                 kprof.buf[1] += 1; // Why?
108 //      iunlock(&kprof);
109 }
110
111 static void setup_timers(void)
112 {
113         void handler(struct alarm_waiter *waiter)
114         {
115                 struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
116                 kproftimer(per_cpu_info[core_id()].rip);
117                 set_awaiter_rel(waiter, 1000);
118                 __set_alarm(tchain, waiter);
119         }
120         struct timer_chain *tchain = &per_cpu_info[core_id()].tchain;
121         struct alarm_waiter *waiter = kmalloc(sizeof(struct alarm_waiter), 0);
122         init_awaiter(waiter, handler);
123         set_awaiter_rel(waiter, 1000);
124         set_alarm(tchain, waiter);
125 }
126 static void
127 kprofinit(void)
128 {
129         if(CELLSIZE != sizeof kprof.buf[0])
130                 panic("kprof size");
131 }
132
133 static struct walkqid*
134 kprofwalk(struct chan *c, struct chan *nc, char **name, int nname)
135 {
136         return devwalk(c, nc, name, nname, kproftab, ARRAY_SIZE(kproftab), devgen);
137 }
138
139 static int
140 kprofstat(struct chan *c, uint8_t *db, int n)
141 {
142         return devstat(c, db, n, kproftab, ARRAY_SIZE(kproftab), devgen);
143 }
144
145 static struct chan*
146 kprofopen(struct chan *c, int omode)
147 {
148         if(c->qid.type & QTDIR){
149                 if(omode != OREAD)
150                         error(Eperm);
151         }
152         c->mode = openmode(omode);
153         c->flag |= COPEN;
154         c->offset = 0;
155         return c;
156 }
157
158 static void
159 kprofclose(struct chan*unused)
160 {
161 }
162
163 static long
164 kprofread(struct chan *c, void *va, long n, int64_t off)
165 {
166         uintptr_t end;
167         uint64_t w, *bp;
168         char *a, *ea;
169         uintptr_t offset = off;
170         uint64_t pc;
171         int ret = 0;
172
173         switch((int)c->qid.path){
174         case Kprofdirqid:
175                 return devdirread(c, va, n, kproftab, ARRAY_SIZE(kproftab), devgen);
176
177         case Kprofdataqid:
178                 end = kprof.nbuf*CELLSIZE;
179
180                 if (n < FORMATSIZE){
181                         n = 0;
182                         break;
183                 }
184                 a = va;
185                 ea = a + n;
186                 bp = kprof.buf + offset/FORMATSIZE;
187                 while((a < ea) && (n >= FORMATSIZE)){
188                         /* what a pain. We need to manage the
189                          * fact that the *prints all make room for
190                          * \0
191                          */
192                         char print[FORMATSIZE+1];
193                         char *name;
194
195                         if(offset/FORMATSIZE >= kprof.nbuf){
196                                 break;
197                         }
198                         w = *bp++;
199                         pc = kprof.minpc + ((offset/FORMATSIZE)<<LRES);
200                         name = get_fn_name(pc);
201                         snprintf(print, sizeof(print), outformat, pc, name, w);
202                         memmove(a, print, FORMATSIZE);
203                         a += FORMATSIZE;
204                         n -= FORMATSIZE;
205                         ret += FORMATSIZE;
206                 }
207                 n = ret;
208                 break;
209
210         default:
211                 n = 0;
212                 break;
213         }
214         return n;
215 }
216
217 static long
218 kprofwrite(struct chan *c, void *a, long n, int64_t unused)
219 {
220         switch((int)(c->qid.path)){
221         case Kprofctlqid:
222                 if(strncmp(a, "startclr", 8) == 0){
223                         memset((char *)kprof.buf, 0, kprof.nbuf*CELLSIZE);
224                         kprof.time = 1;
225                 }else if(strncmp(a, "start", 5) == 0) {
226                         kprof.time = 1;
227                         setup_timers();
228                 } else if(strncmp(a, "stop", 4) == 0)
229                         kprof.time = 0;
230                 break;
231         default:
232                 error(Ebadusefd);
233         }
234         return n;
235 }
236
237 struct dev kprofdevtab __devtab = {
238         'K',
239         "kprof",
240
241         devreset,
242         kprofinit,
243         devshutdown,
244         kprofattach,
245         kprofwalk,
246         kprofstat,
247         kprofopen,
248         devcreate,
249         kprofclose,
250         kprofread,
251         devbread,
252         kprofwrite,
253         devbwrite,
254         devremove,
255         devwstat,
256 };