xref: /inferno-os/os/port/devprof.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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 #include	<interp.h>
8 #include	<isa.h>
9 #include	"runt.h"
10 
11 static void	cpxec(Prog *);
12 static void memprof(int, Heap*, ulong);
13 
14 extern	Inst*	pc2dispc(Inst*, Module*);
15 
16 static	int	interval = 100;	/* Sampling interval in milliseconds */
17 
18 enum
19 {
20 	HSIZE	= 32,
21 };
22 
23 #define HASH(m)	((m)%HSIZE)
24 
25 /* cope with  multiple profilers some day */
26 
27 typedef struct Record Record;
28 struct Record
29 {
30 	int	id;
31 	char*	name;
32 	char*	path;
33 	Inst*	base;
34 	int	size;
35 	/*Module*	m;	*/
36 	ulong	mtime;
37 	Qid	qid;
38 	Record*	hash;
39 	Record*	link;
40 	ulong	bucket[1];
41 };
42 
43 struct
44 {
45 	Lock	l;
46 	vlong	time;
47 	Record*	hash[HSIZE];
48 	Record*	list;
49 } profile;
50 
51 typedef struct Pmod Pmod;
52 struct Pmod
53 {
54 	char*	name;
55 	Pmod*	link;
56 } *pmods;
57 
58 #define QSHIFT	4
59 #define QID(q)		((ulong)(q).path&0xf)
60 #define QPID(pid)	((pid)<<QSHIFT)
61 #define PID(q)		((q).vers)
62 #define PATH(q)	((ulong)(q).path&~((1<<QSHIFT)-1))
63 
64 enum
65 {
66 	Qdir,
67 	Qname,
68 	Qpath,
69 	Qhist,
70 	Qpctl,
71 	Qctl,
72 };
73 
74 Dirtab profdir[] =
75 {
76 	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,
77 	"name",		{Qname},	0,			0444,
78 	"path",		{Qpath},	0,			0444,
79 	"histogram",	{Qhist},	0,			0444,
80 	"pctl",		{Qpctl},	0,			0222,
81 	"ctl",			{Qctl},	0,			0222,
82 };
83 
84 enum{
85 	Pnil,	/* null profiler */
86 	Psam,	/* sampling profiler */
87 	Pcov,	/* coverage profiler */
88 	Pmem,	/* memory profiler */
89 };
90 
91 static int profiler = Pnil;
92 
93 static int ids;
94 static int samplefn;
95 
96 static void sampler(void*);
97 
98 static Record*
99 getrec(int id)
100 {
101 	Record *r;
102 
103 	for(r = profile.list; r != nil; r = r->link)
104 		if(r->id == id)
105 			break;
106 	return r;
107 }
108 
109 static void
110 addpmod(char *m)
111 {
112 	Pmod *p = malloc(sizeof(Pmod));
113 
114 	if(p == nil)
115 		return;
116 	p->name = malloc(strlen(m)+1);
117 	if(p->name == nil){
118 		free(p);
119 		return;
120 	}
121 	strcpy(p->name, m);
122 	p->link = pmods;
123 	pmods = p;
124 }
125 
126 static void
127 freepmods(void)
128 {
129 	Pmod *p, *np;
130 
131 	for(p = pmods; p != nil; p = np){
132 		free(p->name);
133 		np = p->link;
134 		free(p);
135 	}
136 	pmods = nil;
137 }
138 
139 static int
140 inpmods(char *m)
141 {
142 	Pmod *p;
143 
144 	for(p = pmods; p != nil; p = p->link)
145 		if(strcmp(p->name, m) == 0)
146 			return 1;
147 	return 0;
148 }
149 
150 static void
151 freeprof(void)
152 {
153 	int i;
154 	Record *r, *nr;
155 
156 	ids = 0;
157 	profiler = Pnil;
158 	freepmods();
159 	for(r = profile.list; r != nil; r = nr){
160 		free(r->path);
161 		nr = r->link;
162 		free(r);
163 	}
164 	profile.list = nil;
165 	profile.time = 0;
166 	for(i = 0; i < HSIZE; i++)
167 		profile.hash[i] = nil;
168 }
169 
170 static int
171 profgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp)
172 {
173 	Qid qid;
174 	Record *r;
175 	ulong path, perm, len;
176 	Dirtab *tab;
177 
178 	USED(name);
179 	USED(d);
180 	USED(nd);
181 
182 	if(s == DEVDOTDOT) {
183 		mkqid(&qid, Qdir, 0, QTDIR);
184 		devdir(c, qid, "#P", 0, eve, 0555, dp);
185 		return 1;
186 	}
187 
188 	if(c->qid.path == Qdir && c->qid.type & QTDIR) {
189 		acquire();
190 		if(s-- == 0){
191 			tab = &profdir[Qctl];
192 			mkqid(&qid, PATH(c->qid)|tab->qid.path, c->qid.vers, QTFILE);
193 			devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
194 			release();
195 			return 1;
196 		}
197 		r = profile.list;
198 		while(s-- && r != nil)
199 			r = r->link;
200 		if(r == nil) {
201 			release();
202 			return -1;
203 		}
204 		sprint(up->genbuf, "%.8lux", (ulong)r->id);
205 		mkqid(&qid, (r->id<<QSHIFT), r->id, QTDIR);
206 		devdir(c, qid, up->genbuf, 0, eve, DMDIR|0555, dp);
207 		release();
208 		return 1;
209 	}
210 	if(s >= nelem(profdir)-1)
211 		error(Enonexist);	/* was return -1; */
212 	tab = &profdir[s];
213 	path = PATH(c->qid);
214 
215 	acquire();
216 	r = getrec(PID(c->qid));
217 	if(r == nil) {
218 		release();
219 		error(Enonexist);	/* was return -1; */
220 	}
221 
222 	perm = tab->perm;
223 	len = tab->length;
224 	mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE);
225 	devdir(c, qid, tab->name, len, eve, perm, dp);
226 	release();
227 	return 1;
228 }
229 
230 static Chan*
231 profattach(char *spec)
232 {
233 	return devattach('P', spec);
234 }
235 
236 static Walkqid*
237 profwalk(Chan *c, Chan *nc, char **name, int nname)
238 {
239 	return devwalk(c, nc, name, nname, 0, 0, profgen);
240 }
241 
242 static int
243 profstat(Chan *c, uchar *db, int n)
244 {
245 	return devstat(c, db, n, 0, 0, profgen);
246 }
247 
248 static Chan*
249 profopen(Chan *c, int omode)
250 {
251 	int qid;
252 	Record *r;
253 
254 	if(c->qid.type & QTDIR) {
255 		if(omode != OREAD)
256 			error(Eisdir);
257 		c->mode = openmode(omode);
258 		c->flag |= COPEN;
259 		c->offset = 0;
260 		return c;
261 	}
262 
263 	if(omode&OTRUNC)
264 		error(Eperm);
265 
266 	qid = QID(c->qid);
267 	if(qid == Qctl || qid == Qpctl){
268 		if (omode != OWRITE)
269 			error(Eperm);
270 	}
271 	else{
272 		if(omode != OREAD)
273 			error(Eperm);
274 	}
275 
276 	if(qid != Qctl){
277 		acquire();
278 		r = getrec(PID(c->qid));
279 		release();
280 		if(r == nil)
281 			error(Ethread);
282 	}
283 
284 	c->offset = 0;
285 	c->flag |= COPEN;
286 	c->mode = openmode(omode);
287 	if(QID(c->qid) == Qhist)
288 		c->aux = nil;
289 	return c;
290 }
291 
292 static int
293 profwstat(Chan *c, uchar *dp, int n)
294 {
295 	Dir d;
296 	Record *r;
297 
298 	if(strcmp(up->env->user, eve))
299 		error(Eperm);
300 	if(c->qid.type & QTDIR)
301 		error(Eperm);
302 	acquire();
303 	r = getrec(PID(c->qid));
304 	release();
305 	if(r == nil)
306 		error(Ethread);
307 	n = convM2D(dp, n, &d, nil);
308 	if(n == 0)
309 		error(Eshortstat);
310 	d.mode &= 0777;
311 	/* TO DO: copy to c->aux->perm, once that exists */
312 	return n;
313 }
314 
315 static void
316 profclose(Chan *c)
317 {
318 	USED(c);
319 }
320 
321 static long
322 profread(Chan *c, void *va, long n, vlong offset)
323 {
324 	int i;
325 	Record *r;
326 	char *a = va;
327 
328 	if(c->qid.type & QTDIR)
329 		return devdirread(c, a, n, 0, 0, profgen);
330 	acquire();
331 	r = getrec(PID(c->qid));
332 	release();
333 	if(r == nil)
334 		error(Ethread);
335 	switch(QID(c->qid)){
336 	case Qname:
337 		return readstr(offset, va, n, r->name);
338 	case Qpath:
339 		return readstr(offset, va, n, r->path);
340 	case Qhist:
341 		i = (int)c->aux;
342 		while(i < r->size && r->bucket[i] == 0)
343 			i++;
344 		if(i >= r->size)
345 			return 0;
346 		c->aux = (void*)(i+1);
347 		if(n < 20)
348 			error(Etoosmall);
349 		return sprint(a, "%d %lud", i, r->bucket[i]);
350 	case Qctl:
351 		error(Eperm);
352 	}
353 	return 0;
354 }
355 
356 static long
357 profwrite(Chan *c, void *va, long n, vlong offset)
358 {
359 	int i;
360 	char *a = va;
361 	char buf[128], *fields[128];
362 
363 	USED(va);
364 	USED(n);
365 	USED(offset);
366 
367 	if(c->qid.type & QTDIR)
368 		error(Eisdir);
369 	switch(QID(c->qid)){
370 	case Qctl:
371 		if(n > sizeof(buf)-1)
372 			n = sizeof(buf)-1;
373 		memmove(buf, a, n);
374 		buf[n] = 0;
375 		i = getfields(buf, fields, nelem(fields), 1, " \t\n");
376 		if(i > 0 && strcmp(fields[0], "module") == 0){
377 			freepmods();
378 			while(--i > 0)
379 				addpmod(fields[i]);
380 			return n;
381 		}
382 		if(i == 1){
383 			if(strcmp(fields[0], "start") == 0){
384 				if(profiler == Pnil) {
385 					profiler = Psam;
386 					if(!samplefn){
387 						samplefn = 1;
388 						kproc("prof", sampler, 0, 0);
389 					}
390 				}
391 			}
392 			else if(strcmp(fields[0], "startmp") == 0){
393 				if(profiler == Pnil){
394 					profiler = Pmem;
395 					heapmonitor = memprof;
396 				}
397 			}
398 			else if(strcmp(fields[0], "stop") == 0)
399 				profiler = Pnil;
400 			else if(strcmp(fields[0], "end") == 0){
401 				profiler = Pnil;
402 				freeprof();
403 				interval = 100;
404 			}
405 			else
406 				error(Ebadarg);
407 		}
408 		else if (i == 2){
409 			if(strcmp(fields[0], "interval") == 0)
410 				interval = strtoul(fields[1], nil, 0);
411 			else if(strcmp(fields[0], "startcp") == 0){
412 				Prog *p;
413 
414 				acquire();
415 				p = progpid(strtoul(fields[1], nil, 0));
416 				if(p == nil){
417 					release();
418 					return -1;
419 				}
420 				if(profiler == Pnil){
421 					profiler = Pcov;
422 					p->xec = cpxec;
423 				}
424 				release();
425 			}
426 			else
427 				error(Ebadarg);
428 		}
429 		else
430 			error(Ebadarg);
431 		return n;
432 	default:
433 		error(Eperm);
434 	}
435 	return 0;
436 }
437 
438 static Record*
439 newmodule(Module *m, int vm, int scale, int origin)
440 {
441 	int dsize;
442 	Record *r, **l;
443 
444 	if(!vm)
445 		acquire();
446 	if((m->compiled && m->pctab == nil) || m->prog == nil) {
447 		if(!vm)
448 			release();
449 		return nil;
450 	}
451 /* print("newmodule %x %s %s %d %d %d\n", m, m->name, m->path, m->mtime, m->qid.path, m->qid.vers); */
452 	if(m->compiled)
453 		dsize = m->nprog * sizeof(r->bucket[0]);
454 	else
455 		dsize = (msize(m->prog)/sizeof(Inst)) * sizeof(r->bucket[0]);
456 	dsize *= scale;
457 	dsize += origin;
458 	r = malloc(sizeof(Record)+dsize);
459 	if(r == nil) {
460 		if(!vm)
461 			release();
462 		return nil;
463 	}
464 
465 	r->id = ++ids;
466 	if(ids == (1<<8)-1)
467 		ids = 0;
468 	kstrdup(&r->name, m->name);
469 	kstrdup(&r->path, m->path);
470 	r->base = m->prog;
471 	r->size = dsize/sizeof(r->bucket[0]);
472 	// r->m = m;
473 	r->mtime = m->mtime;
474 	r->qid.path = m->qid.path;
475 	r->qid.vers = m->qid.vers;
476 	memset(r->bucket, 0, dsize);
477 	r->link = profile.list;
478 	profile.list = r;
479 
480 	l = &profile.hash[HASH(m->mtime)];
481 	r->hash = *l;
482 	*l = r;
483 
484 	if(!vm)
485 		release();
486 	return r;
487 }
488 
489 static Record*
490 mlook(Module *m, int vm, int scale, int origin)
491 {
492 	Record *r;
493 
494 	for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){
495 		/* if(r->m == m){	/* bug - r->m could be old exited module */
496 		if(r->mtime == m->mtime && r->qid.path == m->qid.path && r->qid.vers == m->qid.vers && strcmp(r->name, m->name) == 0 && strcmp(r->path, m->path) == 0){
497 			r->base = m->prog;
498 			return r;
499 		}
500 	}
501 	if(pmods == nil || inpmods(m->name) || inpmods(m->path)){
502 		if(0 && profiler == Pmem)
503 			heapmonitor = nil;
504 		r = newmodule(m, vm, scale, origin);
505 		if(0 && profiler == Pmem)
506 			heapmonitor = memprof;
507 		return r;
508 	}
509 	return nil;
510 }
511 
512 static void
513 sampler(void* a)
514 {
515 	int i;
516 	Module *m;
517 	Record *r;
518 	Inst *p;
519 
520 	USED(a);
521 	for(;;) {
522 		tsleep(&up->sleep, return0, nil, interval);
523 		if(profiler != Psam)
524 			break;
525 		lock(&profile.l);
526 		profile.time += interval;
527 		if(R.M == H || (m = R.M->m) == nil){
528 			unlock(&profile.l);
529 			continue;
530 		}
531 		p = R.PC;
532 		r = mlook(m, 0, 1, 0);
533 		if(r == nil){
534 			unlock(&profile.l);
535 			continue;
536 		}
537 		if(m->compiled && m->pctab != nil)
538 			p = pc2dispc(p, m);
539 		if((i = p-r->base) >= 0 && i < r->size)
540 			r->bucket[i]++;
541 		unlock(&profile.l);
542 	}
543 	samplefn = 0;
544 	pexit("", 0);
545 }
546 
547 /*
548  *	coverage profiling
549  */
550 
551 static void
552 cpxec(Prog *p)
553 {
554 	int op, i;
555 	Module *m;
556 	Record *r;
557 	Prog *n;
558 
559 	R = p->R;
560 	R.MP = R.M->MP;
561 	R.IC = p->quanta;
562 
563 	if(p->kill != nil){
564 		char *m;
565 		m = p->kill;
566 		p->kill = nil;
567 		error(m);
568 	}
569 
570 	if(R.M->compiled)
571 		comvec();
572 	else{
573 		m = R.M->m;
574 		r = profiler == Pcov ? mlook(m, 1, 1, 0) : nil;
575 		do{
576 			dec[R.PC->add]();
577 			op = R.PC->op;
578 			if(r != nil){
579 				i = R.PC-r->base;
580 				if(i >= 0 && i < r->size)
581 					r->bucket[i]++;
582 			}
583 			R.PC++;
584 			optab[op]();
585 			if(op == ISPAWN || op == IMSPAWN){
586 				n = delruntail(Pdebug);	// any state will do
587 				n->xec = cpxec;
588 				addrun(n);
589 			}
590 			if(m != R.M->m){
591 				m = R.M->m;
592 				r = profiler == Pcov ? mlook(m, 1, 1, 0) : nil;
593 			}
594 		}while(--R.IC != 0);
595 	}
596 
597 	p->R = R;
598 }
599 
600 /* memory profiling */
601 
602 enum{
603 	Halloc,
604 	Hfree,
605 	Hgcfree,
606 };
607 
608 #define MPAD	sizeof(double)
609 
610 static void
611 memprof(int c, Heap *h, ulong n)
612 {
613 	int i, j, k;
614 	ulong kk, *b;
615 	Module *m;
616 	Record *r;
617 	Inst *p;
618 
619 /* print("%d %x %uld\n", c, h, n); */
620 	USED(h);
621 	if(profiler != Pmem){
622 		heapmonitor = nil;
623 		return;
624 	}
625 	lock(&profile.l);
626 	m = nil;
627 	if(c != Hgcfree && (R.M == H || (m = R.M->m) == nil)){
628 		unlock(&profile.l);
629 		return;
630 	}
631 	j = n;
632 	if(c == 0 || c == 4){		/* heap or main allocation */
633 		p = R.PC;
634 		if(m->compiled && m->pctab != nil)
635 			p = pc2dispc(p, m);
636 		if((r = mlook(m, 1, 2, 2)) == nil){
637 			unlock(&profile.l);
638 			return;
639 		}
640 		i = p-r->base;
641 		k = (r->id<<24) | i;
642 		if(c == 0){
643 			h->hprof = k;
644 			k = sizeof(Heap);
645 		}
646 		else{
647 			*(ulong*)h = k;
648 			k = MPAD;
649 		}
650 		/* 31 is pool quanta - dependency on alloc.c */
651 		j = ((j+k+BHDRSIZE+31)&~31) - (k+BHDRSIZE);
652 	}
653 	else{
654 		/* c == 1 is ref count free */
655 		/* c == 2 is gc free */
656 		/* c == 3 is main free */
657 		if(c == 3)
658 			k = *(ulong*)h;
659 		else
660 			k = h->hprof;
661 		if((r = getrec(k>>24)) == nil){
662 			unlock(&profile.l);
663 			return;
664 		}
665 		i = k&0xffffff;
666 		if(c == 3)
667 			j = msize(h)-MPAD;
668 		else
669 			j = hmsize(h)-sizeof(Heap);
670 		j = -j;
671 	}
672 	i = 2*(i+1);
673 	b = r->bucket;
674 	if(i >= 0 && i < r->size){
675 		if(0){
676 			if(c == 1){
677 				b[0] -= j;
678 				b[i] -= j;
679 			}
680 			else if(c == 2){
681 				b[1] -= j;
682 				b[i+1] -= j;
683 			}
684 		}
685 		else{
686 			b[0] += j;
687 			if((int)b[0] < 0)
688 				b[0] = 0;
689 			b[i] += j;
690 			if((int)b[i] < 0)
691 				b[i] = 0;
692 			if(j > 0){
693 				if((kk = b[0]) > b[1])
694 					b[1] = kk;
695 				if((kk = b[i]) > b[i+1])
696 					b[i+1] = kk;
697 			}
698 		}
699 	}
700 	unlock(&profile.l);
701 }
702 
703 Dev profdevtab = {
704 	'P',
705 	"prof",
706 
707 	devreset,
708 	devinit,
709 	devshutdown,
710 	profattach,
711 	profwalk,
712 	profstat,
713 	profopen,
714 	devcreate,
715 	profclose,
716 	profread,
717 	devbread,
718 	profwrite,
719 	devbwrite,
720 	devremove,
721 	profwstat
722 };
723