report.c 7.26 KB
/*
 * Copyright 1995, Silicon Graphics, Inc.
 * All Rights Reserved.
 *
 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
 * the contents of this file may not be disclosed to third parties, copied or
 * duplicated in any form, in whole or in part, without the prior written
 * permission of Silicon Graphics, Inc.
 *
 * RESTRICTED RIGHTS LEGEND:
 * Use, duplication or disclosure by the Government is subject to restrictions
 * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
 * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
 * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
 * rights reserved under the Copyright Laws of the United States.
 *
 *
 *	report - print profile report
 *
 */

#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

#ifdef __sgi
#include <a.out.h>
typedef SYMR SYMENT;
#include <ldfcn.h>
#else
#include "libsym.h"
#endif

#include "gperf.h"


#ifdef __sgi
extern LDFILE	*Ldptr;
#endif


/*
 *     S t r u c t u r e s
 */
typedef struct {
	char        *name;
	symaddr_t   addr;
	unsigned	cnt;
} syment_t;


/*
 *     G l o b a l s
 */
static int Dump = 1;			/* if set then dump symbols used */
static int symcnt;				/* number of symbols */
static syment_t *symEntTbl = NULL;	/* list of addresses */


/*
 *     F o w a r d     R e f e r e n c e s
 */
static syment_t *rdsymtab(char *namelist);
static syment_t * bsearch_sym( symaddr_t addr, syment_t *symtab, int ntab);
static int compar_addr();
static int compar_cnt();
static float sum = 0.0;


/*
 *     F u n c t i o n s
 */
void
report( int numSegs, gPerfHisto *PerfHists, int tp, int overflow )
{
	register syment_t 	*symEnt;
	int 				i, p;
	float 				freq;
	char 				fmt_string[50];


	/*
	 *  If you haven't already, read symbol table
	 */
	if(symEntTbl == NULL)
		symEntTbl = rdsymtab(Namelist);

	/*
	 * sort the symbol entries by address
	 */
	qsort(symEntTbl, symcnt, sizeof (syment_t), compar_addr);


	/*
	 * Clear all the counts
	 */
	for(i = 0; i < symcnt; i++)
		symEntTbl[i].cnt = 0;

	/*
	 *  Sum counts for each function
	 */
	sum = overflow;

	/*
	 *  Get counts per symbol
	 */
	for (p = 0; p < numSegs; p++)  /* for each profiled section */
	{
		for (i = 0; i < PerfHists[p].numEntrs; i++)  /* for each possible entry */
		{
			if (PerfHists[p].histo[i]) /* if you got hits */
			{
				/* update the number of hits in the symEntTbl */
				(bsearch_sym(i*4+PerfHists[p].start, symEntTbl, symcnt))->cnt += 
								PerfHists[p].histo[i];
				/* update the total number of hits */
				sum += (float)PerfHists[p].histo[i];
			}
		}
	}


	/*
	 *  Sort by count
	 */
	qsort(symEntTbl, symcnt, sizeof (syment_t), compar_cnt);

	/*
	 *  Print Report
	 */
	printf("-------------------------------------------------------------\n");
	printf("Profile Report for %s\n", Namelist);
	printf("Number of text symbols: %d\n", symcnt);
	printf("Sample period: %d microseconds\n", tp);
	printf("Number of profiled segments: %d\n", numSegs);
	for (i = 0; i < numSegs; i++) {
	  printf("    Segment %d, start = %08x, end = %08x, size = %08x\n", 
		i, PerfHists[i].start, (int)PerfHists[i].start + 2*PerfHists[i].numEntrs - 4, PerfHists[i].numEntrs);
	}
	printf("-------------------------------------------------------------\n");
	printf("\n");

	for (symEnt = symEntTbl; symEnt < (symEntTbl+symcnt); symEnt++) 
	{
		if (PrintCounts)
			freq = (float)symEnt->cnt;
		else
			freq = (float)symEnt->cnt / sum * 100.0;

		if (freq >= Threshold)
		{
			sprintf(fmt_string,"...........................................");
			for (i = 0; i < 40; i++) {
				if (!symEnt->name || symEnt->name[i] == '\0')
					break;
				fmt_string[i] = symEnt->name[i];
			}
			printf("%08x  %s  %8.4f %%  ", symEnt->addr, fmt_string, 
					(float)symEnt->cnt / sum * 100.0);
			if (PrintCounts)
				printf("%5d  ", symEnt->cnt);
			if (PrintRealTime)
				printf("%10.6f sec", (float)symEnt->cnt * (float)tp / 1000000.0 );
			printf("\n");
		}
	}
	printf("Overflow  ");
	sprintf(fmt_string,"...........................................");
	printf("%s  %8.4f %%  ", fmt_string, (float)overflow/sum *100.0);
	if (PrintCounts)
		printf("%5d  ", overflow);
	if (PrintRealTime)
		printf("%10.6f sec", (float)overflow * (float)tp / 1000000.0 );
	printf("\n");

	if (ServerMode)
		return;
	else
		exit(0);
}


/*
 *  Compare function used by bsort
 *    sort by count
 */
static int
compar_cnt(x, y)
	syment_t *x, *y;
{
	if (x->cnt > y->cnt)
		return(1);
	else if (x->cnt == y->cnt)
		return(0);
	return(-1);
}

/*
 *  Compare function used by bsort
 */
static int
compar_addr(x, y)
	syment_t *x, *y;
{
	if (x->addr > y->addr)
		return(1);
	else if (x->addr == y->addr)
		return(0);
	return(-1);
}


#ifndef __sgi
static int maxsym;
static int
scanfunc(const Elf32_Sym* sym, const char* name, void* ctx) {
        syment_t** base = ctx;

	maxsym++;
	if (strcmp(name, "gcc2_compiled.") == 0 ||
	    strcmp(name, "__gnu_compiled_c") == 0) return 0;
	if(ELF32_ST_TYPE(sym->st_info) == STT_FUNC ||
	   (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
	    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE)) {
		syment_t *ip;
		*base = realloc(*base, (symcnt+1)*sizeof *ip);
		if (!*base) {
		    error("cannot allocate space for namelist");
		    exit(1);
		}
		ip = *base+symcnt;
		ip->addr = sym->st_value;
		ip->name = strdup(name);
		ip->cnt  = 0;
		symcnt++;
	}
	return 0;
}
#endif
/*
 *  Read symbol table and addresses of each symbol
 */
static syment_t *
rdsymtab( char *namelist)
{
#ifdef __sgi
	register int	i;
	register int maxsym;
	register syment_t *ip;
	SYMR ts;
	syment_t *addrbase;


	/* external symbols start at index isymMax and are iextMax long */
	maxsym = SYMHEADER(Ldptr).isymMax + SYMHEADER(Ldptr).iextMax;
	symcnt = 0;

	if((addrbase = (syment_t *) malloc (maxsym * sizeof(syment_t *) + 1)) 
				== NULL) {
		error("cannot allocate space for namelist");
		exit(1);
	}

	ip = addrbase;

	for(i = 0; i < maxsym; i++) {
		if(ldtbread(Ldptr, i, &ts) != SUCCESS) 
			error("cannot read symbol from namelist");
		if (i < SYMHEADER(Ldptr).isymMax && ts.st != stStatic &&
		    ts.st != stStaticProc)
		    	/* statics allowed, throw away other locals */
		    	continue;
		if(ts.sc == scText && (ts.st == stProc || ts.st == stStaticProc)) {
			ip->addr = ts.value;
			ip->name = strdup(ldgetname(Ldptr, &ts));
			ip->cnt  = 0;
		    ip++;
			symcnt++;
		}
	}

	if (Dump) {
		printf("%d text symbols out of %d total read from %s\n",
				symcnt, maxsym, namelist);
	}


	if(ldclose(Ldptr) != SUCCESS) {
		error("cannot ldclose namelist file");
		exit(1);
	}
#else
	syment_t* addrbase = 0;
	symcnt = 0;
	sym_scan(namelist, scanfunc, &addrbase);
	if (Dump) {
		printf("%d text symbols out of %d total read from %s\n",
				symcnt, maxsym, namelist);
	}
#endif

	return(addrbase);
}


/*
 *  return symbol entry for given address, finds address range
 *  that symbol lies within and return address of that function.
 */
static syment_t *
bsearch_sym( symaddr_t addr, syment_t *symtab, int ntab)
{
	int hi, lo, mid;

	lo = 0;
	hi = ntab - 1;

	while(lo <= hi) 
	{
		mid = (lo + hi) / 2;
		if (addr == symtab[mid].addr)
		  return(&symtab[mid]);
		if (addr < symtab[mid].addr)
		  hi = mid - 1;
		else
		  lo = mid + 1;
	}

	if(symtab[mid].addr > addr)
		return(&symtab[mid-1]);
	else
		return(&symtab[mid]);
}