/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: prfld.c,v 1.10.2.1 92/06/24 13:45:18 marker Exp $"

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

/*
 *	prfld - load profiler with sorted kernel text addresses
 */

#include <stdio.h>
#include <sys/errno.h>
#include <a.out.h>
typedef SYMR SYMENT;
#include <ldfcn.h>

int N_TEXT;
int symcnt;			/* number of symbols */
SYMENT *stbl;			/* start of symbol table */
char *namelist = "/unix";	/* namelist file */
extern int errno;

struct buflist {
	int value;
	struct buflist *next;
};

main(argc, argv)
char **argv;
{
	register int *ip, prf;
	register SYMENT *sp;
	int  maxprf, extra = 0, more = 0;	/* max symbols */
	
	int *taddr, *tp, *tend, ntaddr;
	int compar();
	int	ai;
	char	**ap;
	int	unique_addr = 0;
	int	truncate_list = 0;

	ai = argc - 1;
	ap = argv + 1;
	while (ai > 0) {
		if (! strcmp(*ap,"-U")) {
			ai--;
			ap++;
			unique_addr = 1;
		} else if (! strcmp(*ap,"-T")) {
			ai--;
			ap++;
			truncate_list = 1;
		} else
			break;
	};
	if (ai > 0) {
		namelist = *ap;
		ai--;
		ap++;
	};
	if (ai != 0)
		error("usage: prfld [-U] [-T] [/unix]");
	if((prf = open("/dev/prf", 1)) < 0)
		error("cannot open /dev/prf");
	if ((maxprf = ioctl(prf, 4, 0)) < 0)
		error("ioctl failed");
	taddr = (int *) malloc(sizeof(int) * maxprf);
	rdsymtab();
	ip = taddr;
	*ip++ = 0;
	
	tend = taddr+maxprf;
	for(sp = stbl; --symcnt; sp++) {
		if (ip >= tend) {
			if (truncate_list)
				break;
			extra = 1;
		};
		if(sp->sc == scText && sp->st== stProc) {
			if (unique_addr) {
				for (tp = taddr; tp < ip; tp++)
					if (*tp == sp->value)
						break;
				if (tp == ip) {
					if (extra) 
						more++;
					else 
						*ip++ = sp->value;
				}
			} else
				if (extra) {
					more++;
				} else				
					*ip++ = sp->value;
		};
	};
	if (extra) {
		more += maxprf;
		printf("too many text symbols - need to configure maxprf to be at least %d\n", more);
		exit(1);
	}
	ntaddr = ip - taddr;
	qsort(taddr, ntaddr, sizeof (int), compar);
	if(write(prf, taddr, ntaddr*sizeof(int)) != ntaddr*sizeof(int))
		switch(errno) {
		case ENOSPC:
			error("insufficient space in system for addresses");
		case E2BIG:
			error("unaligned data or insufficient addresses");
		case EBUSY:
			error("profiler is enabled");
		case EINVAL:
			error("text addresses not sorted properly");
		case ENOMEM:
			error("malloc failed during the write call");
		default:
			error("cannot load profiler addresses");
		}
	exit(0);
}

compar(x, y)
	register  unsigned  *x, *y;
{
	if(*x > *y)
		return(1);
	else if(*x == *y)
		return(0);
	return(-1);
}

error(s)
char *s;
{
	printf("error: %s\n", s);
	exit(1);
}

rdsymtab()
{
	LDFILE	*ldptr = NULL;
	int	i;

	if((ldptr = (LDFILE *)ldopen(namelist, ldptr)) == NULL)
		error("cannot open namelist file");
	/* external symbols start at index isymMax and are iextMax long */
	symcnt = SYMHEADER(ldptr).iextMax; 
	if((stbl = (SYMR *) malloc (symcnt * sizeof(SYMR)))
		== NULL)
		error("cannot allocate space for namelist");
	for(i = 0; i < symcnt; i++) {
		if(ldtbread(ldptr, i + SYMHEADER(ldptr).isymMax, &stbl[i]) 
			!= SUCCESS) 
			error("cannot read symbol from namelist");
	}
	if(ldclose(ldptr) != SUCCESS)
		error("cannot ldclose namelist file");
}
