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