V10/cmd/prof.c

Compare this file to the similar file:
Show the results in this format:

#include "sys/types.h"
#include "sys/stat.h"
#include "a.out.h"
/* it wouldn't be better to get this out of sys/param.h */
#define HZ 60

typedef unsigned short Prof;	/* what the counts are stored in! */
/* a mon.out consists of a phdr, cnt structs pcnt, and the rest are
	Profs of pc clicks */
struct phdr {
	char	*lopc, *hipc;	/* void *? */
	int	cnt;
};
struct phdr hsv;
struct pcnt {
	char *	value;
	long	calls;
};
char flags[128];
char *xfile, *file;
struct nlist *names;
char *strings, *pbuf;
Prof *accum;
int accumsz;
typedef struct {
	char *loc;
	char *name;
	long calls;
	long clicks;
	unsigned char type;
} sym;
int nsym;
sym *syms, nilsym;
long lopct, hipct;	/* plot stuff */
double rscale, roffset;

main(argc, argv)
char **argv;
{	int c;
	extern int optind;
	extern char **optarg;
	char *p;
	
	while((c = getopt(argc, argv, "alnsvz")) != -1)
	switch(c) {
	case 'l': case 'n': case 's': case 'z':
		flags[c] = 1;
		continue;
	case 'v':
		flags['v'] = 1;
		p = argv[optind];
		if(*p != '-' || p[1] > '9' || p[1] < '0')
			continue;
		lopct = atoi(p+1);
		p = argv[++optind];
		if(*p != '-' || p[1] > '9' || p[1] < '0')
			continue;
		hipct = atoi(p+1);
		optind++;
		continue;
	case '?':
		fatal("usage: %s -[alnsz] files\n", argv[0]);
	}
	if(optind < argc)
		xfile = argv[optind++];
	else
		xfile = "a.out";
	readsyms();
	if(optind >= argc)
		doit("mon.out");
	else
		for(; optind < argc; optind++)
			doit(argv[optind]);
	output();
	exit(0);
}

doit(s)
char *s;
{	struct stat stb;
	int fd;

	file = s;
	fd = open(s, 0);
	if(fd < 0)
		fatal("couldn't open %s\n", s);
	fstat(fd, &stb);
	pbuf = (char *) malloc(stb.st_size);
	if(pbuf == 0)
		fatal("couldn't alloc %d bytes for %s\n", stb.st_size, s);
	if(read(fd, pbuf, stb.st_size) != stb.st_size)
		fatal("read of %s failed\n", s);
	if(stb.st_size <= sizeof(struct phdr))
		fatal("%s not a mon.out\n");
	close(fd);
	addcalls();
	addclicks(stb.st_size);
	free(pbuf);
}

addclicks(top)
{	int i, n, cnt, j;
	char *x;
	Prof *p;
	struct phdr *h;
	double scale;	/* the kernel uses 16 bit fractions */

	h = (struct phdr *)pbuf;
	n = ((struct phdr *)pbuf)->cnt;
	p = (Prof *) (pbuf + sizeof(struct phdr) + n * sizeof(struct pcnt));
	top -= sizeof(struct phdr) + n * sizeof(struct pcnt);
	cnt = top/sizeof(Prof);	/* number of counters */
	if(flags['s'] && !accumsz) {
		accum = (Prof *) malloc(cnt*sizeof(Prof));
		(void) memset(accum, 0, cnt*sizeof(Prof));
		accumsz = cnt;
		hsv = *h;
	}	/* we assume h is fixed across the files */
	scale = (h->hipc-h->lopc)/(double)cnt;
	/* in mcrt0.s it's 8 */
	for(j = i = 0; i < cnt; i++) {
		x = (char *)(h->lopc + (int)(scale*i));
		while(j < nsym && syms[j+1].loc < x)
			j++;
		if(j > nsym)
			fatal("cnts out of range %s\n", file);
		syms[j].clicks += p[i];
	}
	if(flags['s'])
	for(i = 0; i < accumsz; i++) {
		n = accum[i] + p[i];
		accum[i] = n;
		if(n != accum[i]) {
			mesg("overflow in accumulating counts, -s ignored\n");
			flags['s'] = 0;
			return;
		}
	}
}

addcalls()
{	int n, i;
	struct pcnt *p;

	p = (struct pcnt *)(pbuf + sizeof(struct phdr));
	n = ((struct phdr *)pbuf)->cnt;
	for(i = 0; i < n; i++, p++)
		addcall(p->value, p->calls);
}

addcall(x, n)
char *x;
{	int i;
	for(i = 0; i < nsym && syms[i+1].loc < x; i++)
		;
	if(i >= nsym) {
		mesg("cntr out of range (%s), ignored\n", file);
		return;
	}
	if(syms[i+1].loc == x)
		syms[i+1].calls += n;
	else
		syms[i].calls += n;
}

symnum(a, b)
register sym *a, *b;
{
	return(a->loc - b->loc);
}

readsyms()
{	int fd, n, i, j;
	struct exec x;
	struct stat stb;
	struct nlist *np;

	fd = open(xfile, 0);
	if(fd < 0)
		fatal("couldn't read %s\n", xfile);
	if(read(fd, (char *)&x, sizeof(x)) != sizeof(x))
		fatal("can't read header of %s\n", xfile);
	if(!x.a_syms)
		fatal("%s has no symbol table\n", xfile);

	/* read the symbol table in */
	lseek(fd, N_SYMOFF(x), 0);
	names = (struct nlist *) malloc(x.a_syms);
	read(fd, (char *)names, x.a_syms);
	nsym = N_STROFF(x) - N_SYMOFF(x);
	nsym /= sizeof(struct nlist);	/* over estimated */

	/* read the wretched strings in */
	lseek(fd, N_STROFF(x), 0);
	fstat(fd, &stb);
	n = stb.st_size - N_STROFF(x);
	strings = (char *) malloc(n);
	if(read(fd, strings, n) != n)
		fatal("couldn't read string table from %s (%d)\n", xfile, n);

	/* copy the text symbols into the symbol table */
	syms = (sym *) malloc(nsym * sizeof(sym));
	for(i = 0, np = names; np < names + nsym; np++) {
		if((np->n_type & N_TYPE) != N_TEXT)
			continue;
		if(np->n_type & N_STAB)
			continue;
		syms[i] = nilsym;
		syms[i].type = np->n_type;
		syms[i].loc = (char *)np->n_value;
		syms[i].name = strings + np->n_un.n_strx;
		i++;
	}
	nsym = i;
	qsort((char *)syms, nsym, sizeof(sym), symnum);

	/* now decide which of the ones with the same loc we like */
	for(i = 0; i < nsym; i = j) {
		for(j = i+1; j < nsym; j++)
			if(syms[i].loc != syms[j].loc)
				break;
		choose(i, j);
	}
	/* and squash it down */
	for(i = j = 0; i < nsym; i++)
		if(syms[i].type)
			syms[j++] = syms[i];
	nsym = j;
	syms[nsym].loc = (char *)0x8fffffff;	/* a very big end */
	close(fd);
}

/* the expert system */
choose(i, j)
{	int n;
	if(j-i <= 1)
		return;
	/* if there is an external text one, take it */
	for(n = i; n < j; n++)
		if(syms[n].type & N_EXT)
			goto found;
	/* if there is one that starts with _, take it */
	for(n = i; n < j; n++)
		if(syms[n].name[0] == '_')
			goto found;
	/* if there is one without a . in it, take it */
	for(n = i; n < j; n++)
		if(!strchr(syms[n].name, '.'))
			goto found;
	/* first must be right, or at least ok */
	n = i;
found:
	for(; i < j; i++)
		if(i != n)
			syms[i].type = 0;
}

char ebuf[256];
mesg(s, a, b, c, d, e, f)
char *s;
{
	sprintf(ebuf, s, a, b, c, d, e, f);
	write(2, ebuf, strlen(ebuf));
}

fatal(s, a, b, c, d, e, f)
char *s;
{
	mesg(s, a, b, c, d, e, f);
	exit(1);
}

symcalls(a, b)
register sym *a, *b;
{
	return(b->calls - a->calls);
}

symcnt(a, b)
register sym *a, *b;
{
	return(b->clicks - a->clicks);	/* largest first */
}

output()
{	int i, tot, fd;
	double x, y;

	if(flags['v']) {
		plotout();
		return;
	}
	for(tot = i = 0; i < nsym; i++)
		tot += syms[i].clicks;
	if(tot == 0)
		tot = 1;
	if(flags['n'])
		qsort((char *)syms, nsym, sizeof(sym), symcalls);
	else if(!flags['l'])
		qsort((char *)syms, nsym, sizeof(sym), symcnt);
	printf("  %%   Time\tCalls\tName\n");
	for(i = 0; i < nsym; i++) {
		if(!syms[i].calls && !syms[i].clicks && !flags['z'])
			continue;
		x = syms[i].clicks;
		x /= HZ;
		y = syms[i].clicks/(double)tot;
		printf("%4.1f%8.3f %8d\t%s\n", (y*100), x, syms[i].calls,
			syms[i].name);
	}
	if(!flags['s'])
		return;
	fd = creat("mon.sum", 0666);
	if(fd < 0)
		fatal("can't create mon.sum\n");
	hsv.cnt = nsym;
	if(write(fd, (char *)&hsv, sizeof(hsv)) != sizeof(hsv))
		fatal("write failed\n");
	for(i = 0; i < nsym; i++) {	/* this is very inefficient */
		write(fd, (char *)&syms[i].loc, 4);	/* 4! */
		write(fd, (char *)&syms[i].calls, 4);
	}
	if(write(fd, (char *)accum, accumsz*sizeof(Prof)) != accumsz*sizeof(Prof))
		fatal("write failed\n");
	close(fd);
}

/* most of this is witchcraft */
plotout()
{	int i;
/*	double lastx, lasty;
	if(lopct >= 100 || lopct < 0)
		lopct = 0;
	if(hipct > 100 || hipct <= 0)
		hipct = 100;
	rscale = 100./(hipct-lopct);
	roffset = 2040.+40.8*lopct*rscale;
	openpl();
	erase();
	range(-2048, -2048, 2048, 2048);
	line(-2040, -2040, -2040, 2040);
	for(i = 0; i < 11; i++)
		line(-2040, 2040-i*408, 0, 2040-i*408);
	*/
}