Fix trivial bug in kprof -- pc not getting set correctly
[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                 pc = kprof.minpc + ((offset/FORMATSIZE)<<LRES);
188                 while((a < ea) && (n >= FORMATSIZE)){
189                         /* what a pain. We need to manage the
190                          * fact that the *prints all make room for
191                          * \0
192                          */
193                         char print[FORMATSIZE+1];
194                         char *name;
195
196                         if(offset/FORMATSIZE >= kprof.nbuf){
197                                 break;
198                         }
199                         w = *bp++;
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                         pc++;
207                 }
208                 n = ret;
209                 break;
210
211         default:
212                 n = 0;
213                 break;
214         }
215         return n;
216 }
217
218 static long
219 kprofwrite(struct chan *c, void *a, long n, int64_t unused)
220 {
221         switch((int)(c->qid.path)){
222         case Kprofctlqid:
223                 if(strncmp(a, "startclr", 8) == 0){
224                         memset((char *)kprof.buf, 0, kprof.nbuf*CELLSIZE);
225                         kprof.time = 1;
226                 }else if(strncmp(a, "start", 5) == 0) {
227                         kprof.time = 1;
228                         setup_timers();
229                 } else if(strncmp(a, "stop", 4) == 0)
230                         kprof.time = 0;
231                 break;
232         default:
233                 error(Ebadusefd);
234         }
235         return n;
236 }
237
238 struct dev kprofdevtab __devtab = {
239         'K',
240         "kprof",
241
242         devreset,
243         kprofinit,
244         devshutdown,
245         kprofattach,
246         kprofwalk,
247         kprofstat,
248         kprofopen,
249         devcreate,
250         kprofclose,
251         kprofread,
252         devbread,
253         kprofwrite,
254         devbwrite,
255         devremove,
256         devwstat,
257 };