1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 #include "probe.h"
9
10 enum {
11 Qdir,
12 Qctl,
13 Qdata,
14 };
15
16 enum {
17 ProbeEntry = 1,
18 ProbeExit
19 };
20
21 /* fix me make this programmable */
22 enum {
23 defaultlogsize = 1024,
24 printsize = 64,
25 };
26
27 typedef struct Probelog Probelog;
28 struct Probelog {
29 uvlong ticks;
30 /* yeah, waste a whole int on something stupid but ... */
31 int info;
32 ulong pc;
33 /* these are different depending on type */
34 long dat[4];
35 };
36
37 static Rendez probesleep;
38 static QLock probeslk;
39 static Probe *probes;
40 static Lock loglk;
41 static Probelog *probelog = nil;
42 /* probe indices. These are just unsigned longs. You mask them
43 * to get an index. This makes fifo empty/full etc. trivial.
44 */
45 static ulong pw = 0, pr = 0;
46 static int probesactive = 0;
47 static unsigned long logsize = defaultlogsize, logmask = defaultlogsize - 1;
48
49 static char eventname[] = {
50 [ProbeEntry] = 'E',
51 [ProbeExit] = 'X'
52 };
53
54 static Dirtab probedir[]={
55 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
56 "probectl", {Qctl}, 0, 0664,
57 "probe", {Qdata}, 0, 0440,
58 };
59
60 char hex[] = {
61 '0',
62 '1',
63 '2',
64 '3',
65 '4',
66 '5',
67 '6',
68 '7',
69 '8',
70 '9',
71 'A',
72 'B',
73 'C',
74 'D',
75 'E',
76 'F',
77 };
78
79 /* big-endian ... */
80 void
hex32(ulong l,char * c)81 hex32(ulong l, char *c)
82 {
83 int i;
84 for(i = 8; i; i--){
85 c[i-1] = hex[l&0xf];
86 l >>= 4;
87 }
88 }
89
90 void
hex64(uvlong l,char * c)91 hex64(uvlong l, char *c)
92 {
93 hex32(l>>32, c);
94 hex32(l, &c[8]);
95 }
96 static int
lognonempty(void *)97 lognonempty(void *)
98 {
99 return pw - pr;
100 }
101
102 static int
logfull(void)103 logfull(void)
104 {
105 return (pw - pr) >= logsize;
106 }
107
108 static ulong
idx(ulong f)109 idx(ulong f)
110 {
111 return f & logmask;
112 }
113
114 /* can return NULL, meaning, no record for you */
115 static struct Probelog *
newpl(void)116 newpl(void)
117 {
118 ulong index;
119
120 if (logfull()){
121 wakeup(&probesleep);
122 return nil;
123 }
124
125 ilock(&loglk);
126 index = pw++;
127 iunlock(&loglk);
128
129 return &probelog[idx(index)];
130
131 }
132
133 static void
probeentry(Probe * p)134 probeentry(Probe *p)
135 {
136 struct Probelog *pl;
137 //print("probeentry %p p %p func %p argp %p\n", &p, p, p->func, p->argp);
138 pl = newpl();
139 if (! pl)
140 return;
141 cycles(&pl->ticks);
142 pl->pc = (ulong)p->func;
143 pl->dat[0] = p->argp[0];
144 pl->dat[1] = p->argp[1];
145 pl->dat[2] = p->argp[2];
146 pl->dat[3] = p->argp[3];
147 pl->info = ProbeEntry;
148 }
149
150 static void
probeexit(Probe * p)151 probeexit(Probe *p)
152 {
153 //print("probeexit %p p %p func %p argp %p\n", &p, p, p->func, p->argp);
154 struct Probelog *pl;
155 pl = newpl();
156 if (! pl)
157 return;
158 cycles(&pl->ticks);
159 pl->pc = (ulong)p->func;
160 pl->dat[0] = p->rval;
161 pl->info = ProbeExit;
162 }
163
164 static Chan*
probeattach(char * spec)165 probeattach(char *spec)
166 {
167 return devattach('+', spec);
168 }
169
170 static Walkqid*
probewalk(Chan * c,Chan * nc,char ** name,int nname)171 probewalk(Chan *c, Chan *nc, char **name, int nname)
172 {
173 return devwalk(c, nc, name, nname, probedir, nelem(probedir), devgen);
174 }
175
176 static long
probestat(Chan * c,uchar * db,long n)177 probestat(Chan *c, uchar *db, long n)
178 {
179 return devstat(c, db, n, probedir, nelem(probedir), devgen);
180 }
181
182 static Chan*
probeopen(Chan * c,int omode)183 probeopen(Chan *c, int omode)
184 {
185 /* if there is no probelog, allocate one. Open always fails
186 * if the basic alloc fails. You can resize it later.
187 */
188 if (! probelog)
189 probelog = malloc(sizeof(*probelog)*logsize);
190 /* I guess malloc doesn't toss an error */
191 if (! probelog)
192 error("probelog malloc failed");
193
194 c = devopen(c, omode, probedir, nelem(probedir), devgen);
195 return c;
196 }
197
198 static void
probeclose(Chan *)199 probeclose(Chan *)
200 {
201 }
202
203 static long
proberead(Chan * c,void * a,long n,vlong offset)204 proberead(Chan *c, void *a, long n, vlong offset)
205 {
206 char *buf;
207 char *cp = a;
208 struct Probelog *pl;
209 Probe *p;
210 int i;
211 static QLock gate;
212 if(c->qid.type == QTDIR)
213 return devdirread(c, a, n, probedir, nelem(probedir), devgen);
214 switch((ulong)c->qid.path){
215 default:
216 error("proberead: bad qid");
217 case Qctl:
218 buf = malloc(READSTR);
219 i = 0;
220 qlock(&probeslk);
221 i += snprint(buf + i, READSTR - i, "logsize %lud\n", logsize);
222 for(p = probes; p != nil; p = p->next)
223 i += snprint(buf + i, READSTR - i, "probe %p new %s\n",
224 p->func, p->name);
225
226 for(p = probes; p != nil; p = p->next)
227 if (p->enabled)
228 i += snprint(buf + i, READSTR - i, "probe %s on\n",
229 p->name);
230 i += snprint(buf + i, READSTR - i, "#probehits %lud, in queue %lud\n",
231 pw, pw-pr);
232 snprint(buf + i, READSTR - i, "#probelog %p\n", probelog);
233 qunlock(&probeslk);
234 n = readstr(offset, a, n, buf);
235 free(buf);
236 break;
237 case Qdata:
238 qlock(&gate);
239 if(waserror()){
240 qunlock(&gate);
241 nexterror();
242 }
243 while(!lognonempty(nil))
244 tsleep(&probesleep, lognonempty, nil, 5000);
245 i = 0;
246 while(lognonempty((void *)0)){
247 int j;
248 pl = probelog + idx(pr);
249
250 if ((i + printsize) >= n)
251 break;
252 /* simple format */
253 cp[0] = eventname[pl->info];
254 cp ++;
255 *cp++ = ' ';
256 hex32(pl->pc, cp);
257 cp[8] = ' ';
258 cp += 9;
259 hex64(pl->ticks, cp);
260 cp[16] = ' ';
261 cp += 17;
262 for(j = 0; j < 4; j++){
263 hex32(pl->dat[j], cp);
264 cp[8] = ' ';
265 cp += 9;
266 }
267 /* adjust for extra skip above */
268 cp--;
269 *cp++ = '\n';
270 pr++;
271 i += printsize;
272 }
273 poperror();
274 qunlock(&gate);
275 n = i;
276 break;
277 }
278 return n;
279 }
280
281 static long
probewrite(Chan * c,void * a,long n,vlong)282 probewrite(Chan *c, void *a, long n, vlong)
283 {
284 char *tok[5];
285 char *ep, *s = nil;
286 Probe *p, **pp;
287 int ntok;
288
289 qlock(&probeslk);
290 if(waserror()){
291 qunlock(&probeslk);
292 if(s != nil) free(s);
293 nexterror();
294 }
295 switch((ulong)c->qid.path){
296 default:
297 error("proberead: bad qid");
298 case Qctl:
299 s = malloc(n + 1);
300 memmove(s, a, n);
301 s[n] = 0;
302 ntok = tokenize(s, tok, nelem(tok));
303 if(!strcmp(tok[0], "probe")){ /* 'probe' ktextaddr 'on'|'off'|'mk'|'del' [name] */
304 if(ntok < 3)
305 error("devprobe: usage: 'probe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name]");
306 for(pp = &probes; *pp != nil; pp = &(*pp)->next)
307 if(!strcmp(tok[1], (*pp)->name))
308 break;
309 p = *pp;
310 if(!strcmp(tok[2], "new")){
311 ulong addr;
312 void *func;
313 addr = strtoul(tok[1], &ep, 0);
314 func = (void*)addr;
315 if(*ep)
316 error("devprobe: address not in recognized format");
317 // if(addr < ((ulong) start) || addr > ((ulong) end))
318 // error("devprobe: address out of bounds");
319 if(p != nil)
320 error("devprobe: %#p already has probe");
321 p = mkprobe(func, probeentry, probeexit);
322 p->next = probes;
323 if(ntok < 4)
324 snprint(p->name, sizeof p->name, "%p", func);
325 else
326 strncpy(p->name, tok[3], sizeof p->name);
327 probes = p;
328 } else if(!strcmp(tok[2], "on")){
329 if(p == nil)
330 error("devprobe: probe not found");
331 if(!p->enabled)
332 probeinstall(p);
333 print("probeinstall in devprobe\n");
334 probesactive++;
335 } else if(!strcmp(tok[2], "off")){
336 if(p == nil)
337 error("devprobe: probe not found");
338 if(p->enabled)
339 probeuninstall(p);
340 probesactive--;
341 } else if(!strcmp(tok[2], "del")){
342 if(p == nil)
343 error("devprobe: probe not found");
344 if(p->enabled)
345 probeuninstall(p);
346 probesactive--;
347 *pp = p->next;
348 freeprobe(p);
349 } else if(!strcmp(tok[2], "mv")){
350 if(p == nil)
351 error("devprobe: probe not found");
352 if(ntok < 4)
353 error("devprobe: rename without new name?");
354 strncpy(p->name, tok[3], sizeof p->name);
355 }
356 } else if(!strcmp(tok[0], "size")){
357 int l, size;
358 struct Probelog *newprobelog;
359 l = strtoul(tok[1], &ep, 0);
360 if(*ep)
361 error("devprobe: size not in recognized format");
362 size = 1 << l;
363 /* sort of foolish. Alloc new probe first, then free old. */
364 /* and too bad if there are unread probes */
365 newprobelog = malloc(sizeof(*newprobelog)*size);
366 /* does malloc throw waserror? I don't know */
367 free(probelog);
368 probelog = newprobelog;
369 logsize = size;
370 pr = pw = 0;
371 } else {
372 error("devprobe: usage: 'probe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name] or: 'size' buffersize (power of 2)");
373 }
374 free(s);
375 break;
376 }
377 poperror();
378 qunlock(&probeslk);
379 return n;
380 }
381
382 Dev probedevtab = {
383 '+',
384 "probe",
385 devreset,
386 devinit,
387 devshutdown,
388 probeattach,
389 probewalk,
390 probestat,
391 probeopen,
392 devcreate,
393 probeclose,
394 proberead,
395 devbread,
396 probewrite,
397 devbwrite,
398 devremove,
399 devwstat,
400 };
401