Add a profile device.
[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 enum{
39         Kprofdirqid,
40         Kprofdataqid,
41         Kprofctlqid,
42 };
43 struct dirtab kproftab[]={
44         {".",           {Kprofdirqid, 0, QTDIR},0,      DMDIR|0550},
45         {"kpdata",      {Kprofdataqid},         0,      0600},
46         {"kpctl",       {Kprofctlqid},          0,      0600},
47 };
48
49 static struct chan*
50 kprofattach(char *spec)
51 {
52         uint32_t n;
53
54         /* allocate when first used */
55         kprof.minpc = KERN_LOAD_ADDR;
56         kprof.maxpc = (uintptr_t)&etext;
57         kprof.nbuf = (kprof.maxpc-kprof.minpc) >> LRES;
58         n = kprof.nbuf*CELLSIZE;
59         if(kprof.buf == 0) {
60                 printk("Allocate %d bytes\n", n);
61                 kprof.buf = kzmalloc(n, KMALLOC_WAIT);
62                 if(kprof.buf == 0)
63                         error(Enomem);
64         }
65         kproftab[1].length = n;
66         return devattach('K', spec);
67 }
68
69 static void
70 _kproftimer(uintptr_t pc)
71 {
72         if(kprof.time == 0)
73                 return;
74
75         /*
76          * if the pc corresponds to the idle loop, don't consider it.
77
78         if(m->inidle)
79                 return;
80          */
81         /*
82          *  if the pc is coming out of spllo or splx,
83          *  use the pc saved when we went splhi.
84
85         if(pc>=PTR2UINT(spllo) && pc<=PTR2UINT(spldone))
86                 pc = m->splpc;
87          */
88
89 //      ilock(&kprof);
90 #warning "how do we fill out this timing info"
91 #if 0
92         kprof.buf[0] += TK2MS(1);
93         if(kprof.minpc<=pc && pc<kprof.maxpc){
94                 pc -= kprof.minpc;
95                 pc >>= LRES;
96                 kprof.buf[pc] += TK2MS(1);
97         }else
98                 kprof.buf[1] += TK2MS(1);
99 #endif
100 //      iunlock(&kprof);
101 }
102
103 static void
104 kprofinit(void)
105 {
106         if(CELLSIZE != sizeof kprof.buf[0])
107                 panic("kprof size");
108 #warning "how do we set up this timer"
109         //kproftimer = _kproftimer;
110 }
111
112 static struct walkqid*
113 kprofwalk(struct chan *c, struct chan *nc, char **name, int nname)
114 {
115         return devwalk(c, nc, name, nname, kproftab, ARRAY_SIZE(kproftab), devgen);
116 }
117
118 static int
119 kprofstat(struct chan *c, uint8_t *db, int n)
120 {
121         return devstat(c, db, n, kproftab, ARRAY_SIZE(kproftab), devgen);
122 }
123
124 static struct chan*
125 kprofopen(struct chan *c, int omode)
126 {
127         if(c->qid.type & QTDIR){
128                 if(omode != OREAD)
129                         error(Eperm);
130         }
131         c->mode = openmode(omode);
132         c->flag |= COPEN;
133         c->offset = 0;
134         return c;
135 }
136
137 static void
138 kprofclose(struct chan*unused)
139 {
140 }
141
142 static long
143 kprofread(struct chan *c, void *va, long n, int64_t off)
144 {
145         uintptr_t end;
146         uint64_t w, *bp;
147         uint8_t *a, *ea;
148         uintptr_t offset = off;
149
150         switch((int)c->qid.path){
151         case Kprofdirqid:
152                 return devdirread(c, va, n, kproftab, ARRAY_SIZE(kproftab), devgen);
153
154         case Kprofdataqid:
155                 end = kprof.nbuf*CELLSIZE;
156                 if(offset & (CELLSIZE-1))
157                         error(Ebadarg);
158                 if(offset >= end){
159                         n = 0;
160                         break;
161                 }
162                 if(offset+n > end)
163                         n = end-offset;
164                 n &= ~(CELLSIZE-1);
165                 a = va;
166                 ea = a + n;
167                 bp = kprof.buf + offset/CELLSIZE;
168                 while(a < ea){
169                         w = *bp++;
170                         /* I'd really like not to have to worry
171                          * about 32 bit machines any more!
172                          */
173                         *a++ = w>>56;
174                         *a++ = w>>48;
175                         *a++ = w>>40;
176                         *a++ = w>>32;
177                         *a++ = w>>24;
178                         *a++ = w>>16;
179                         *a++ = w>>8;
180                         *a++ = w>>0;
181                 }
182                 break;
183
184         default:
185                 n = 0;
186                 break;
187         }
188         return n;
189 }
190
191 static long
192 kprofwrite(struct chan *c, void *a, long n, int64_t unused)
193 {
194         switch((int)(c->qid.path)){
195         case Kprofctlqid:
196                 if(strncmp(a, "startclr", 8) == 0){
197                         memset((char *)kprof.buf, 0, kprof.nbuf*CELLSIZE);
198                         kprof.time = 1;
199                 }else if(strncmp(a, "start", 5) == 0)
200                         kprof.time = 1;
201                 else if(strncmp(a, "stop", 4) == 0)
202                         kprof.time = 0;
203                 break;
204         default:
205                 error(Ebadusefd);
206         }
207         return n;
208 }
209
210 struct dev kprofdevtab __devtab = {
211         'K',
212         "kprof",
213
214         devreset,
215         kprofinit,
216         devshutdown,
217         kprofattach,
218         kprofwalk,
219         kprofstat,
220         kprofopen,
221         devcreate,
222         kprofclose,
223         kprofread,
224         devbread,
225         kprofwrite,
226         devbwrite,
227         devremove,
228         devwstat,
229 };