]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devdtracy.c
kernel: use 64-bit virtual entry point for expanded header, document behaviour in...
[plan9front.git] / sys / src / 9 / port / devdtracy.c
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        <dtracy.h>
9
10 static Lock *machlocks;
11
12 typedef struct DTKChan DTKChan;
13 typedef struct DTKAux DTKAux;
14 QLock dtracylock;
15
16 struct DTKChan {
17         DTChan *ch;
18         int ref;
19         int idx;
20 };
21 struct DTKAux {
22         char *str;
23 };
24
25 static void
26 prog(DTKChan *p, char *s)
27 {
28         DTClause *c;
29         int rc;
30
31         dtcrun(p->ch, DTCSTOP);
32         dtcreset(p->ch);
33         while(*s != 0){
34                 s = dtclunpack(s, &c);
35                 if(s == nil)
36                         error("invalid program");
37                 rc = dtcaddcl(p->ch, c);
38                 dtclfree(c);
39                 if(rc < 0){
40                         dtcreset(p->ch);
41                         error(up->syserrstr);
42                 }
43         }
44 }
45
46 enum {
47         /* Qdir */
48         Qclone = 1,
49         Qprobes = 2,
50 };
51
52 enum {
53         Qdir,
54         Qctl,
55         Qprog,
56         Qbuf,
57         Qepid,
58         Qaggbuf,
59 };
60
61 static Dirtab dtracydir[] = {
62         "ctl",  { Qctl, 0, 0 }, 0,      0660,
63         "prog", { Qprog, 0, 0 }, 0,     0660,
64         "buf",  { Qbuf, 0, 0, }, 0,     0440,
65         "epid", { Qepid, 0, 0 }, 0,     0440,
66         "aggbuf",       { Qaggbuf, 0, 0 }, 0,   0440,
67 };
68
69 enum {
70         CMstop,
71         CMgo,
72 };
73
74 static Cmdtab dtracyctlmsg[] = {
75         CMstop,         "stop",         1,
76         CMgo,           "go",           1,
77 };
78
79 static DTKChan **dtktab;
80 static int ndtktab;
81
82 static DTKChan *
83 dtklook(vlong n)
84 {
85         if((uvlong)n >= ndtktab) return nil;
86         return dtktab[n];
87 }
88 #define QIDPATH(q,e) ((q) + 1 << 8 | (e))
89 #define SLOT(q) ((vlong)((q).path >> 8) - 1)
90 #define FILE(q) ((int)(q).path & 0xff)
91
92 static DTKChan *
93 dtknew(void)
94 {
95         DTKChan *p;
96         DTKChan **newtab;
97         int i;
98         
99         p = malloc(sizeof(DTKChan));
100         if(p == nil) error(Enomem);
101         for(i = 0; i < ndtktab; i++)
102                 if(dtktab[i] == nil){
103                         dtktab[i] = p;
104                         p->idx = i;
105                         break;
106                 }
107         if(i == ndtktab){
108                 newtab = realloc(dtktab, (ndtktab + 1) * sizeof(DTKChan *));
109                 if(newtab == nil) error(Enomem);
110                 dtktab = newtab;
111                 dtktab[ndtktab] = p;
112                 p->idx = ndtktab++;
113         }
114         p->ch = dtcnew();
115         return p;
116 }
117
118 static void
119 dtkfree(DTKChan *p)
120 {
121         int idx;
122         
123         idx = p->idx;
124         dtcfree(p->ch);
125         free(p);
126         dtktab[idx] = nil;
127 }
128
129 static int dtracyen;
130
131 static void
132 dtracyinit(void)
133 {
134         dtracyen = getconf("*dtracy") != nil;
135         if(!dtracyen) return;
136         machlocks = smalloc(sizeof(Lock) * conf.nmach);
137         dtinit(conf.nmach);
138 }
139
140 static Chan*
141 dtracyattach(char *spec)
142 {
143         if(!dtracyen)
144                 error("*dtracy= not set");
145         return devattach(L'Δ', spec);
146 }
147
148 static int
149 dtracygen(Chan *c, char *, Dirtab *, int, int s, Dir *dp)
150 {
151         Dirtab *tab;
152         uvlong path;
153
154         if(s == DEVDOTDOT){
155                 devdir(c, (Qid){Qdir, 0, QTDIR}, "#Δ", 0, eve, 0555, dp);
156                 return 1;
157         }
158         if(c->qid.path == Qdir){
159                 if(s-- == 0) goto clone;
160                 if(s-- == 0) goto probes;
161                 if(s >= ndtktab) return -1;
162                 if(dtklook(s) == nil) return 0;
163                 sprint(up->genbuf, "%d", s);
164                 devdir(c, (Qid){QIDPATH(s, Qdir), 0, QTDIR}, up->genbuf, 0, eve, DMDIR|0555, dp);
165                 return 1;
166         }
167         if(c->qid.path == Qclone){
168         clone:
169                 strcpy(up->genbuf, "clone");
170                 devdir(c, (Qid){Qclone, 0, QTFILE}, up->genbuf, 0, eve, 0444, dp);
171                 return 1;
172         }
173         if(c->qid.path == Qprobes){
174         probes:
175                 strcpy(up->genbuf, "probes");
176                 devdir(c, (Qid){Qprobes, 0, QTFILE}, up->genbuf, 0, eve, 0444, dp);
177                 return 1;               
178         }
179         if(s >= nelem(dtracydir))
180                 return -1;
181         tab = &dtracydir[s];
182         path = QIDPATH(SLOT(c->qid), 0);
183         devdir(c, (Qid){tab->qid.path|path, tab->qid.vers, tab->qid.type}, tab->name, tab->length, eve, tab->perm, dp);
184         return 1;
185 }
186
187 static Walkqid*
188 dtracywalk(Chan *c, Chan *nc, char **name, int nname)
189 {
190         Walkqid *rc;
191
192         eqlock(&dtracylock);
193         if(waserror()){
194                 qunlock(&dtracylock);
195                 nexterror();
196         }
197         rc = devwalk(c, nc, name, nname, nil, 0, dtracygen);
198         qunlock(&dtracylock);
199         poperror();
200         return rc;
201 }
202
203 static int
204 dtracystat(Chan *c, uchar *dp, int n)
205 {
206         int rc;
207
208         eqlock(&dtracylock);
209         if(waserror()){
210                 qunlock(&dtracylock);
211                 nexterror();
212         }
213         rc = devstat(c, dp, n, nil, 0, dtracygen);
214         qunlock(&dtracylock);
215         poperror();
216         return rc;      
217 }
218
219 static Chan*
220 dtracyopen(Chan *c, int omode)
221 {
222         DTKChan *p;
223         Chan *ch;
224
225         eqlock(&dtracylock);
226         if(waserror()){
227                 qunlock(&dtracylock);
228                 nexterror();
229         }
230         if(c->qid.path == Qclone){
231                 if(!iseve()) error(Eperm);
232                 p = dtknew();
233                 c->qid.path = QIDPATH(p->idx, Qctl);
234         }
235         if(c->qid.path == Qprobes){
236                 p = nil;
237         }else{
238                 p = dtklook(SLOT(c->qid));
239                 if(SLOT(c->qid) >= 0 && p == nil) error(Enonexist);
240                 if(FILE(c->qid) != Qdir && !iseve()) error(Eperm);
241         }
242         ch = devopen(c, omode, nil, 0, dtracygen);
243         if(p != nil) p->ref++;
244         qunlock(&dtracylock);
245         poperror();
246         ch->aux = smalloc(sizeof(DTKAux));
247         return ch;
248 }
249
250 static void
251 dtracyclose(Chan *ch)
252 {
253         DTKAux *aux;
254         DTKChan *p;
255
256         if(ch->aux != nil){
257                 eqlock(&dtracylock);
258                 p = dtklook(SLOT(ch->qid));
259                 if(p != nil && --p->ref == 0)
260                         dtkfree(p);
261                 qunlock(&dtracylock);
262                 aux = ch->aux;
263                 free(aux->str);
264                 free(ch->aux);
265                 ch->aux = nil;
266         }
267 }
268
269 static int
270 epidread(DTKAux *aux, DTChan *c, char *a, long n, vlong off)
271 {
272         Fmt f;
273         DTEnab *e;
274
275         if(off == 0){
276                 free(aux->str);
277                 aux->str = nil;
278         }
279         if(aux->str == nil){
280                 fmtstrinit(&f);
281                 for(e = c->enab; e != nil; e = e->channext)
282                         fmtprint(&f, "%d %d %d %s\n", e->epid, e->gr->id, e->gr->reclen, e->prob->name);
283                 aux->str = fmtstrflush(&f);
284         }
285         return readstr(off, a, n, aux->str);
286 }
287
288 static long
289 lockedread(DTChan *c, void *a, long n, int(*readf)(DTChan *, void *, int))
290 {
291         long rc;
292
293         if(waserror()){
294                 qunlock(&dtracylock);
295                 nexterror();
296         }
297         eqlock(&dtracylock);
298         rc = readf(c, a, n);
299         qunlock(&dtracylock);
300         poperror();
301         return rc;
302 }
303
304 static long
305 handleread(DTChan *c, void *a, long n, int(*readf)(DTChan *, void *, int))
306 {
307         long rc, m;
308         int i;
309
310         for(;;){
311                 rc = lockedread(c, a, n, readf);
312                 if(rc < 0) return -1;
313                 if(rc > 0) break;
314                 tsleep(&up->sleep, return0, 0, 250);
315         }
316         m = rc;
317         for(i = 0; i < 3 && m < n/2; i++){
318                 tsleep(&up->sleep, return0, 0, 50);
319                 rc = lockedread(c, (uchar *)a + m, n - m, readf);
320                 if(rc < 0) break;
321                 m += rc;
322         }
323         return m;
324 }
325
326 static long
327 probesread(DTKAux *aux, char *a, long n, vlong off)
328 {
329         Fmt f;
330         DTProbe **l;
331         int i, nl;
332         
333         if(aux->str == nil){
334                 fmtstrinit(&f);
335                 nl = dtplist(&l);
336                 for(i = 0; i < nl; i++)
337                         fmtprint(&f, "%s\n", l[i]->name);
338                 dtfree(l);
339                 aux->str = fmtstrflush(&f);
340         }
341         return readstr(off, a, n, aux->str);
342 }
343
344 static long
345 dtracyread(Chan *c, void *a, long n, vlong off)
346 {
347         int rc;
348         DTKChan *p;
349         DTChan *ch;
350
351         eqlock(&dtracylock);
352         if(waserror()){
353                 qunlock(&dtracylock);
354                 nexterror();
355         }
356         if(SLOT(c->qid) == -1)
357                 switch((int)c->qid.path){
358                 case Qdir:
359                         rc = devdirread(c, a, n, nil, 0, dtracygen);
360                         goto out;
361                 case Qprobes:
362                         rc = probesread(c->aux, a, n, off);
363                         goto out;
364                 default:
365                         error(Egreg);
366                 }
367         p = dtklook(SLOT(c->qid));
368         if(p == nil) error(Enonexist);
369         switch(FILE(c->qid)){
370         case Qdir:
371                 rc = devdirread(c, a, n, nil, 0, dtracygen);
372                 break;
373         case Qctl:
374                 sprint(up->genbuf, "%d", p->idx);
375                 rc = readstr(off, a, n, up->genbuf);
376                 break;
377         case Qbuf:
378                 ch = p->ch;
379                 qunlock(&dtracylock);
380                 poperror();
381                 return handleread(ch, a, n, dtcread);
382         case Qaggbuf:
383                 ch = p->ch;
384                 qunlock(&dtracylock);
385                 poperror();
386                 return handleread(ch, a, n, dtcaggread);
387         case Qepid:
388                 rc = epidread(c->aux, p->ch, a, n, off);
389                 break;
390         default:
391                 error(Egreg);
392                 return 0;
393         }
394 out:
395         qunlock(&dtracylock);
396         poperror();
397         return rc;
398 }
399
400 static long
401 dtracywrite(Chan *c, void *a, long n, vlong)
402 {
403         int rc;
404         DTKChan *p;
405         Cmdbuf *cb;
406         Cmdtab *ct;
407
408         eqlock(&dtracylock);
409         if(waserror()){
410                 qunlock(&dtracylock);
411                 nexterror();
412         }
413         if(SLOT(c->qid) == -1)
414                 switch((int)c->qid.path){
415                 case Qdir:
416                         error(Eperm);
417                 default:
418                         error(Egreg);
419                 }
420         p = dtklook(SLOT(c->qid));
421         if(p == nil) error(Enonexist);
422         switch(FILE(c->qid)){
423         case Qdir:
424                 error(Eperm);
425                 return 0;
426         case Qctl:
427                 cb = parsecmd(a, n);
428                 if(waserror()){
429                         free(cb);
430                         nexterror();
431                 }
432                 ct = lookupcmd(cb, dtracyctlmsg, nelem(dtracyctlmsg));
433                 switch(ct->index){
434                 case CMstop: dtcrun(p->ch, DTCSTOP); break;
435                 case CMgo: dtcrun(p->ch, DTCGO); break;
436                 default:
437                         error(Egreg);
438                 }
439                 poperror();
440                 free(cb);
441                 rc = n;
442                 break;
443         case Qprog:
444                 {
445                         char *buf;
446                         
447                         buf = smalloc(n+1);
448                         if(waserror()){
449                                 free(buf);
450                                 nexterror();
451                         }
452                         memmove(buf, a, n);
453                         prog(p, buf);
454                         free(buf);
455                         poperror();
456                         rc = n;
457                         break;
458                 }
459         default:
460                 error(Egreg);
461                 return 0;
462         }
463         qunlock(&dtracylock);
464         poperror();
465         return rc;
466 }
467
468
469 Dev dtracydevtab = {
470         L'Δ',
471         "dtracy",
472         
473         devreset,
474         dtracyinit,
475         devshutdown,
476         dtracyattach,
477         dtracywalk,
478         dtracystat,
479         dtracyopen,
480         devcreate,
481         dtracyclose,
482         dtracyread,
483         devbread,
484         dtracywrite,
485         devbwrite,
486         devremove,
487         devwstat,
488 };
489
490 void *
491 dtmalloc(ulong n)
492 {
493         void *v;
494
495         v = smalloc(n);
496         setmalloctag(v, getcallerpc(&n));
497         return v;
498 }
499
500 void
501 dtfree(void *v)
502 {
503         free(v);
504 }
505
506 void *
507 dtrealloc(void *v, ulong n)
508 {
509         v = realloc(v, n);
510         if(v != nil)
511                 setrealloctag(v, getcallerpc(&v));
512         return v;
513 }
514
515 int
516 dtmachlock(int i)
517 {
518         while(i < 0) {
519                 i = dtmachlock(m->machno);
520                 if(i == m->machno)
521                         return i;
522                 dtmachunlock(i);
523                 i = -1;
524         }
525         ilock(&machlocks[i]);
526         return i;
527 }
528
529 void
530 dtmachunlock(int i)
531 {
532         iunlock(&machlocks[i]);
533 }
534
535 void
536 dtcoherence(void)
537 {
538         coherence();
539 }
540
541 uvlong
542 dttime(void)
543 {
544         return fastticks(nil);
545 }
546
547 uvlong
548 dtgetvar(int v)
549 {
550         switch(v){
551         case DTV_PID:
552                 return up != nil ? up->pid : 0;
553         default:
554                 return 0;
555         }
556 }
557
558 extern int peek(char *, char *, int);
559
560 int
561 dtpeek(uvlong addr, void *buf, int len)
562 {
563         uintptr a;
564         
565         a = addr;
566         if(len == 0) return 0;
567         if(a != addr || a > -(uintptr)len || len < 0) return -1;
568         if(up == nil || up->privatemem || a >= KZERO) return -1;
569         return peek((void *)a, buf, len) > 0 ? -1 : 0;
570 }