DataMuseum.dk

Presents historical artifacts from the history of:

Commodore CBM-900

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Commodore CBM-900

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦3dd12d4e9⟧ TextFile

    Length: 9173 (0x23d5)
    Types: TextFile
    Notes: UNIX file
    Names: »prof.c«

Derivation

└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
    └─⟦f4b8d8c84⟧ UNIX V7 Filesystem
        └─ ⟦this⟧ »cmd/prof.c« 

TextFile

/*
 * Prof interprets the mon.out files produced by monitor(3) and hence by
 * the cc(1) option -p.  The usage is as follows:
 *	prof [-a] [-b] [-c] [-s] [l.out_file [mon.out_file]]
 * The -a option indicates that all symbols should be used (as opposed to
 * only external symbols).  The -b option indicates that we should print
 * all bin information (to detect hot spots).  The -c option indicates
 * that all call information should be printed.  The -s option asks for stack
 * depth information.
 */
#include <stdio.h>
#include <l.out.h>
#include <const.h>
#include <canon.h>
#include "mon.h"


/*
 * Note that in setting PSCALE, one must guard against overflow.
 * Examine putdata carefully before changeing these constants.
 * Also note that certain divisibility properties are assumed
 * there reguarding PSCALE and HZ.
 */
#define PSCALE	((long)100)		/* pc count scale factor */
#define	SWIDTH	10			/* default symbol widht */
#define	EOS	'\0'			/* string terminator */
#define	REL	1			/* relative position for fseek */
#define	TRUE	(0 == 0)
#define	FALSE	(0 != 0)


typedef struct	symbol {
	char		name[NCPLN];
	vaddr_t		addr;
	long		pcount,		/* pc count, scaled by PSCALE */
			ccount;		/* number of times routine called */
}	symbol;

char		*lout	= "l.out",
		*monout	= "mon.out";
symbol		**dict;			/* NULL terminated list of symbols */
int		dsize,			/* number of symbols */
		allsyms	= FALSE,	/* iff we use all symbols */
		bindmp	= FALSE,	/* iff we should dump bin info */
		calldmp	= FALSE;	/* iff we should dump call info */
int		stkdmp = FALSE;		/* iff we should dump low stack mark */
long		tticks,			/* total number of clock ticks */
		tcalls;			/* total number of calls */
vaddr_t		lowpc;			/* lowest pc profiled */
unsigned	scale;			/* mon.out scale factor over 2 */
vaddr_t		stksz;			/* Stack size */


main(argc, argv)
int		argc;
register char	*argv[];
{
	register char	*cp,
			ch;

	for (cp=*++argv; cp != NULL  &&  *cp++ == '-'; cp=*++argv)
		while ((ch=*cp++) != EOS)
			switch (ch) {
				case 'a':
					allsyms = TRUE;
					break;
				case 'b':
					bindmp = TRUE;
					break;
				case 'c':
					calldmp = TRUE;
					break;
				case 's':
					stkdmp = TRUE;
					break;
				default:
					usage();
			}
	if (*argv != NULL)
		lout = *argv++;
	if (*argv != NULL)
		monout = *argv++;
	if (*argv != NULL)
		usage();
	getsyms();
	getdata();
	if (stkdmp)
		printf("%u bytes stack used\n", stksz);
	if (! bindmp  &&  ! calldmp)
		putdata();
	return(0);
}


die(str)
char	*str;
{
	fprintf(stderr, "%r\n", &str);
	exit(1);
}


usage()
{
	die("Usage: prof [-a] [-b] [-c] [-s] [l.out_file [mon.out_file]]");
}


/*
 * Getsyms reads in the symbols from an l.out file and sets dict to
 * an array of them, in sorted order.
 */
getsyms()
{
	FILE		*fp;
	long		skip;
	struct ldheader	hdr;
	int		cmpsym();

	fp = fopen(lout, "r");
	if (fp == NULL)
		die("Can't open %s", lout);
	if (fread(&hdr, sizeof hdr, 1, fp) != 1 || hdr.l_magic != L_MAGIC)
		die("%s not l.out file", lout);
	cansize(hdr.l_ssize[L_SHRI]);
	cansize(hdr.l_ssize[L_PRVI]);
	cansize(hdr.l_ssize[L_SHRD]);
	cansize(hdr.l_ssize[L_PRVD]);
	cansize(hdr.l_ssize[L_DEBUG]);
	cansize(hdr.l_ssize[L_SYM]);
	skip = hdr.l_ssize[L_SHRI] + hdr.l_ssize[L_PRVI]
		+ hdr.l_ssize[L_SHRD] + hdr.l_ssize[L_PRVD]
		+ hdr.l_ssize[L_DEBUG];
	fseek(fp, skip, REL);
	readsy((int)(hdr.l_ssize[L_SYM]/sizeof (struct ldsym)), fp);
	qsort(dict, dsize, sizeof *dict, cmpsym);
	fclose(fp);
}


/*
 * Cmpsym compares the two symbols `sp1' and `sp2', and returns an
 * int corresponding to the relative order of the address fields.
 */
cmpsym(sp1, sp2)
symbol	**sp1,
	**sp2;
{
	register vaddr_t	adr1,
				adr2;

	adr1 = (*sp1)->addr;
	adr2 = (*sp2)->addr;
	if (adr1 > adr2)
		return (1);
	else if (adr1 == adr2)
		return (0);
	else
		return (-1);
}


/*
 * Readsy reads in nsyms ldsyms from the FILE fp.  It sets dict to
 * an array of the resulting symbols.
 */
readsy(nsyms, fp)
register int	nsyms;
FILE		*fp;
{
	register symbol	**dpp,
			*dp;
	struct ldsym	lsym;
	char		*alloc();

	dict = dpp = (symbol **)alloc((nsyms + 1) * sizeof *dpp);
	while (--nsyms >= 0) {
		if (fread(&lsym, sizeof lsym, 1, fp) != 1)
			die("Unexpected end of file on %s", lout);
		if ((lsym.ls_type & ~L_GLOBAL) > L_BSSI)
			continue;
		if ((lsym.ls_type & L_GLOBAL) == 0 && ! allsyms)
			continue;
		dp = (symbol *)alloc(sizeof *dp);
		strncpy(dp->name, lsym.ls_id, NCPLN);
		dp->addr = lsym.ls_addr;
		dp->pcount = dp->ccount = 0;
		*dpp++ = dp;
	}
	*dpp = NULL;
	dsize = dpp - dict;
	dict = (symbol *)realloc(dict, (dpp + 1 - dict) * sizeof *dpp);
}


/*
 * Alloc is simply an interface to malloc which exits if there is no
 * room.
 */
char	*
alloc(size)
unsigned	size;
{
	char	*result;

	result = (char *)malloc(size);
	if (result == NULL)
		die("Out of space");
	return (result);
}


/*
 * Getdata reads in the mon.out file and puts the information into
 * the dictionary.
 */
getdata()
{
	FILE		*fp;
	struct m_hdr	hdr;

	fp = fopen(monout, "r");
	if (fp == NULL)
		die("Can't open %s", monout);
	if (fread(&hdr, sizeof hdr, 1, fp) != 1)
		die("%s not mon.out file", monout);
	scale = hdr.m_scale/2;
	lowpc = hdr.m_lowpc;
	stksz = hdr.m_hisp - hdr.m_lowsp;
	if (calldmp  ||  ! bindmp)
		getcdata(fp, hdr.m_nfuncs);
	else
		fseek(fp, hdr.m_nfuncs * (long)sizeof (struct m_func), REL);
	if (bindmp  ||  ! calldmp)
		getpdata(fp, hdr.m_nbins);
}


/*
 * Getcdata reads in the function call information from the mon.out file.
 */
getcdata(fp, nfnc)
FILE	*fp;
register unsigned	nfnc;
{
	register symbol	**dpp,
			*dp;
	struct m_func	buf;

	while (nfnc-- != 0) {
		if (fread(&buf, sizeof buf, 1, fp) != 1)
			die("unexpected end of file on %s", monout);
		for (dpp=dict; (dp=*++dpp) != NULL && dp->addr <= buf.m_addr;)
			;
		if (calldmp)
			printf("%4ld %*.*s+%u\n", buf.m_ncalls, SWIDTH, NCPLN,
				dpp[-1]->name, buf.m_addr - dpp[-1]->addr);
		tcalls += buf.m_ncalls;
		dpp[-1]->ccount += buf.m_ncalls;
	}
}


/*
 * Getpdata reads in the profileing data and increments the corresponding
 * symbols pcount fields.
 * Note that the global scale must contain the mon.out scale divided
 * by 2.
 */
getpdata(fp, nbins)
FILE		*fp;
unsigned	nbins;
{
	register symbol	**dpp;
	vaddr_t		high,
			low;
	int		highr,
			inc,
			incr;
	short		tick;
	symbol		**credit();

	high = lowpc;
	highr = 0;
	inc = ((long)1<<16) / scale;
	incr = ((long)1<<16) % scale;
	if (incr != 0) {
		++inc;
		incr -= scale;
	}
	for (dpp=dict; nbins > 0; --nbins) {
		low = high;
		high += inc;
		highr += incr;
		if (-highr >= scale) {
			--high;
			highr += scale;
		}
		if (fread(&tick, sizeof tick, 1, fp) != 1)
			die("Unexpected end of file on %s\n", monout);
		if (tick == 0)
			continue;
		tticks += tick;
		dpp = credit(tick, low, high, dpp);
	}
}


symbol	**
credit(tick, low, high, dpp)
int	tick;
vaddr_t	low,
	high;
symbol	**dpp;
{
	register unsigned	overlap;
	register symbol		*cur,
				*nxt;
	unsigned		binlen;

	binlen = high - low;
	nxt = *dpp;
	if (nxt == NULL  ||  nxt->addr >= high) {
		if (bindmp)
			printf("%3d %06o %06o\n", tick, low, high-1);
		return(dpp);
	}
	do {
		cur = nxt;
		nxt = *++dpp;
	} while (nxt != NULL  &&  nxt->addr <= low);
	if (bindmp)
		printf("%3d %*.*s+%-4u ", tick, SWIDTH, NCPLN, cur->name,
			low - cur->addr);
	do {
		if (nxt != NULL  &&  nxt->addr < high)
			overlap = nxt->addr;
		else
			overlap = high;
		if (cur->addr > low)
			overlap -= cur->addr;
		else
			overlap -= low;
		cur->pcount += (PSCALE*overlap*tick + binlen/2) / binlen;
		cur = nxt;
		nxt = *++dpp;
	} while (cur != NULL && cur->addr < high);
	dpp -= 2;
	if (bindmp)
		printf("%*.*s+%u\n", SWIDTH, NCPLN, dpp[0]->name,
			high - 1 - dpp[0]->addr);
	return (dpp);
}


/*
 * Putdata prints out the results which have been tabulated in the
 * dictionary.
 */
putdata()
{
	register symbol	**dpp,
			*dp;
	int		cmpdata();
	char		*centi();

	qsort(dict, dsize, sizeof *dict, cmpdata);
	for (dpp=dict; (dp=*dpp++) != NULL;) {
		if (dp->pcount == 0 && dp->ccount == 0)
			continue;
		printf("%-*.*s", SWIDTH, NCPLN, dp->name);
		centi(dp->pcount, (PSCALE * tticks)/100, 2);
		putchar('%');
		if (dp->ccount != 0) {
			printf(" %7ld ", dp->ccount);
			centi((1000*dp->pcount) / (PSCALE*10),
				(HZ*dp->ccount) / 10, 3);
		}
		putchar('\n');
	}
}


/*
 * Cmpdata compares the data in the dictionary entries `sp1'
 * and `sp2'.  It returns an int which reflects which entry should
 * be listed first.  If the value is positive, `sp2' should occur
 * first.  If it is zero, it makes no difference.  If it is negative,
 * `sp1' should occur first.
 */
cmpdata(sp1, sp2)
symbol	**sp1,
	**sp2;
{
	register symbol	*adr1,
			*adr2;
	long		rel;

	adr1 = *sp1;
	adr2 = *sp2;
	rel = adr2->pcount - adr1->pcount;
	if (rel == 0)
		rel = adr2->ccount - adr1->ccount;
	if (rel > 0)
		return (1);
	else if (rel < 0)
		return (-1);
	else
		return (strncmp(adr1->name, adr2->name, NCPLN));
}


/*
 * Centi prints out on standard output `num' / `den' to two places, with
 * atleast `width' places to the left of the decimal point.
 */
char	*
centi(num, den, width)
long	num,
	den;
int	width;
{
	long	cv;

	cv = (num*100 + den/2) / den;
	printf("%*ld.%02d", width, cv/100, (int)(cv%100));
}