xref: /onnv-gate/usr/src/cmd/sgs/gprof/common/gprof.c (revision 6951:59445bec7ef4)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*6951Sab196087  * Common Development and Distribution License (the "License").
6*6951Sab196087  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*6951Sab196087  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include	<sysexits.h>
290Sstevel@tonic-gate #include	<stdlib.h>
30211Smike_s #include	<stdio.h>
310Sstevel@tonic-gate #include	<unistd.h>
320Sstevel@tonic-gate #include	"gprof.h"
330Sstevel@tonic-gate #include	"profile.h"
340Sstevel@tonic-gate 
350Sstevel@tonic-gate char		*whoami = "gprof";
360Sstevel@tonic-gate static pctype	lowpc, highpc;		/* range profiled, in UNIT's */
370Sstevel@tonic-gate 
380Sstevel@tonic-gate /*
390Sstevel@tonic-gate  *	things which get -E excluded by default.
400Sstevel@tonic-gate  */
410Sstevel@tonic-gate static char *defaultEs[] = {
420Sstevel@tonic-gate 	"mcount",
430Sstevel@tonic-gate 	"__mcleanup",
44211Smike_s 	NULL
450Sstevel@tonic-gate };
460Sstevel@tonic-gate 
470Sstevel@tonic-gate #ifdef DEBUG
480Sstevel@tonic-gate 
490Sstevel@tonic-gate static char *objname[] = {
500Sstevel@tonic-gate 	"<invalid object>",
510Sstevel@tonic-gate 	"PROF_BUFFER_T",
520Sstevel@tonic-gate 	"PROF_CALLGRAPH_T",
530Sstevel@tonic-gate 	"PROF_MODULES_T",
54211Smike_s 	NULL
550Sstevel@tonic-gate };
560Sstevel@tonic-gate #define	MAX_OBJTYPES	3
570Sstevel@tonic-gate 
58211Smike_s #endif /* DEBUG */
590Sstevel@tonic-gate 
600Sstevel@tonic-gate void
done(void)61211Smike_s done(void)
620Sstevel@tonic-gate {
630Sstevel@tonic-gate 
640Sstevel@tonic-gate 	exit(EX_OK);
650Sstevel@tonic-gate }
660Sstevel@tonic-gate 
670Sstevel@tonic-gate static pctype
max(pctype a,pctype b)680Sstevel@tonic-gate max(pctype a, pctype b)
690Sstevel@tonic-gate {
700Sstevel@tonic-gate 	if (a > b)
710Sstevel@tonic-gate 		return (a);
720Sstevel@tonic-gate 	return (b);
730Sstevel@tonic-gate }
740Sstevel@tonic-gate 
750Sstevel@tonic-gate static pctype
min(pctype a,pctype b)760Sstevel@tonic-gate min(pctype a, pctype b)
770Sstevel@tonic-gate {
780Sstevel@tonic-gate 	if (a < b)
790Sstevel@tonic-gate 		return (a);
800Sstevel@tonic-gate 	return (b);
810Sstevel@tonic-gate }
820Sstevel@tonic-gate 
830Sstevel@tonic-gate /*
840Sstevel@tonic-gate  *	calculate scaled entry point addresses (to save time in asgnsamples),
850Sstevel@tonic-gate  *	and possibly push the scaled entry points over the entry mask,
860Sstevel@tonic-gate  *	if it turns out that the entry point is in one bucket and the code
870Sstevel@tonic-gate  *	for a routine is in the next bucket.
880Sstevel@tonic-gate  *
890Sstevel@tonic-gate  */
900Sstevel@tonic-gate static void
alignentries(void)91211Smike_s alignentries(void)
920Sstevel@tonic-gate {
93211Smike_s 	struct nl *nlp;
940Sstevel@tonic-gate #ifdef DEBUG
950Sstevel@tonic-gate 	pctype			bucket_of_entry;
960Sstevel@tonic-gate 	pctype			bucket_of_code;
97211Smike_s #endif /* DEBUG */
980Sstevel@tonic-gate 
990Sstevel@tonic-gate 	/* for old-style gmon.out, nameslist is only in modules.nl */
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate 	for (nlp = modules.nl; nlp < modules.npe; nlp++) {
1020Sstevel@tonic-gate 		nlp->svalue = nlp->value / sizeof (UNIT);
1030Sstevel@tonic-gate #ifdef DEBUG
1040Sstevel@tonic-gate 		bucket_of_entry = (nlp->svalue - lowpc) / scale;
1050Sstevel@tonic-gate 		bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
1060Sstevel@tonic-gate 		if (bucket_of_entry < bucket_of_code) {
1070Sstevel@tonic-gate 			if (debug & SAMPLEDEBUG) {
108211Smike_s 				(void) printf(
109211Smike_s 				    "[alignentries] pushing svalue 0x%llx "
110211Smike_s 				    "to 0x%llx\n", nlp->svalue,
111211Smike_s 				    nlp->svalue + UNITS_TO_CODE);
1120Sstevel@tonic-gate 			}
1130Sstevel@tonic-gate 		}
114211Smike_s #endif /* DEBUG */
1150Sstevel@tonic-gate 	}
1160Sstevel@tonic-gate }
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate /*
1190Sstevel@tonic-gate  *	old-style gmon.out
1200Sstevel@tonic-gate  *	------------------
1210Sstevel@tonic-gate  *
1220Sstevel@tonic-gate  *	Assign samples to the procedures to which they belong.
1230Sstevel@tonic-gate  *
1240Sstevel@tonic-gate  *	There are three cases as to where pcl and pch can be
1250Sstevel@tonic-gate  *	with respect to the routine entry addresses svalue0 and svalue1
1260Sstevel@tonic-gate  *	as shown in the following diagram.  overlap computes the
1270Sstevel@tonic-gate  *	distance between the arrows, the fraction of the sample
1280Sstevel@tonic-gate  *	that is to be credited to the routine which starts at svalue0.
1290Sstevel@tonic-gate  *
1300Sstevel@tonic-gate  *	    svalue0                                         svalue1
1310Sstevel@tonic-gate  *	       |                                               |
1320Sstevel@tonic-gate  *	       v                                               v
1330Sstevel@tonic-gate  *
1340Sstevel@tonic-gate  *	       +-----------------------------------------------+
1350Sstevel@tonic-gate  *	       |					       |
1360Sstevel@tonic-gate  *	  |  ->|    |<-		->|         |<-		->|    |<-  |
1370Sstevel@tonic-gate  *	  |         |		  |         |		  |         |
1380Sstevel@tonic-gate  *	  +---------+		  +---------+		  +---------+
1390Sstevel@tonic-gate  *
1400Sstevel@tonic-gate  *	  ^         ^		  ^         ^		  ^         ^
1410Sstevel@tonic-gate  *	  |         |		  |         |		  |         |
1420Sstevel@tonic-gate  *	 pcl       pch		 pcl       pch		 pcl       pch
1430Sstevel@tonic-gate  *
1440Sstevel@tonic-gate  *	For the vax we assert that samples will never fall in the first
1450Sstevel@tonic-gate  *	two bytes of any routine, since that is the entry mask,
1460Sstevel@tonic-gate  *	thus we give call alignentries() to adjust the entry points if
1470Sstevel@tonic-gate  *	the entry mask falls in one bucket but the code for the routine
1480Sstevel@tonic-gate  *	doesn't start until the next bucket.  In conjunction with the
1490Sstevel@tonic-gate  *	alignment of routine addresses, this should allow us to have
1500Sstevel@tonic-gate  *	only one sample for every four bytes of text space and never
1510Sstevel@tonic-gate  *	have any overlap (the two end cases, above).
1520Sstevel@tonic-gate  */
1530Sstevel@tonic-gate static void
asgnsamples(void)154211Smike_s asgnsamples(void)
1550Sstevel@tonic-gate {
1560Sstevel@tonic-gate 	sztype		i, j;
1570Sstevel@tonic-gate 	unsigned_UNIT	ccnt;
1580Sstevel@tonic-gate 	double		time;
1590Sstevel@tonic-gate 	pctype		pcl, pch;
1600Sstevel@tonic-gate 	pctype		overlap;
1610Sstevel@tonic-gate 	pctype		svalue0, svalue1;
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 	extern mod_info_t	modules;
1640Sstevel@tonic-gate 	nltype		*nl = modules.nl;
1650Sstevel@tonic-gate 	sztype		nname = modules.nname;
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	/* read samples and assign to namelist symbols */
1680Sstevel@tonic-gate 	scale = highpc - lowpc;
1690Sstevel@tonic-gate 	scale /= nsamples;
1700Sstevel@tonic-gate 	alignentries();
1710Sstevel@tonic-gate 	for (i = 0, j = 1; i < nsamples; i++) {
1720Sstevel@tonic-gate 		ccnt = samples[i];
1730Sstevel@tonic-gate 		if (ccnt == 0)
1740Sstevel@tonic-gate 			continue;
175*6951Sab196087 		/*LINTED: E_ASSIGMENT_CAUSE_LOSS_PREC*/
1760Sstevel@tonic-gate 		pcl = lowpc + scale * i;
177*6951Sab196087 		/*LINTED: E_ASSIGMENT_CAUSE_LOSS_PREC*/
1780Sstevel@tonic-gate 		pch = lowpc + scale * (i + 1);
1790Sstevel@tonic-gate 		time = ccnt;
1800Sstevel@tonic-gate #ifdef DEBUG
1810Sstevel@tonic-gate 		if (debug & SAMPLEDEBUG) {
182211Smike_s 			(void) printf(
183211Smike_s 			    "[asgnsamples] pcl 0x%llx pch 0x%llx ccnt %d\n",
1840Sstevel@tonic-gate 			    pcl, pch, ccnt);
1850Sstevel@tonic-gate 		}
186211Smike_s #endif /* DEBUG */
1870Sstevel@tonic-gate 		totime += time;
1880Sstevel@tonic-gate 		for (j = (j ? j - 1 : 0); j < nname; j++) {
1890Sstevel@tonic-gate 			svalue0 = nl[j].svalue;
1900Sstevel@tonic-gate 			svalue1 = nl[j+1].svalue;
1910Sstevel@tonic-gate 			/*
1920Sstevel@tonic-gate 			 *	if high end of tick is below entry address,
1930Sstevel@tonic-gate 			 *	go for next tick.
1940Sstevel@tonic-gate 			 */
1950Sstevel@tonic-gate 			if (pch < svalue0)
1960Sstevel@tonic-gate 				break;
1970Sstevel@tonic-gate 			/*
1980Sstevel@tonic-gate 			 *	if low end of tick into next routine,
1990Sstevel@tonic-gate 			 *	go for next routine.
2000Sstevel@tonic-gate 			 */
2010Sstevel@tonic-gate 			if (pcl >= svalue1)
2020Sstevel@tonic-gate 				continue;
2030Sstevel@tonic-gate 			overlap = min(pch, svalue1) - max(pcl, svalue0);
2040Sstevel@tonic-gate 			if (overlap != 0) {
2050Sstevel@tonic-gate #ifdef DEBUG
2060Sstevel@tonic-gate 				if (debug & SAMPLEDEBUG) {
207211Smike_s 					(void) printf("[asgnsamples] "
2080Sstevel@tonic-gate 					    "(0x%llx->0x%llx-0x%llx) %s gets "
2090Sstevel@tonic-gate 					    "%f ticks %lld overlap\n",
2100Sstevel@tonic-gate 					    nl[j].value/sizeof (UNIT), svalue0,
2110Sstevel@tonic-gate 					    svalue1, nl[j].name,
2120Sstevel@tonic-gate 					    overlap * time / scale, overlap);
2130Sstevel@tonic-gate 				}
214211Smike_s #endif /* DEBUG */
2150Sstevel@tonic-gate 				nl[j].time += overlap * time / scale;
2160Sstevel@tonic-gate 			}
2170Sstevel@tonic-gate 		}
2180Sstevel@tonic-gate 	}
2190Sstevel@tonic-gate #ifdef DEBUG
2200Sstevel@tonic-gate 	if (debug & SAMPLEDEBUG) {
221211Smike_s 		(void) printf("[asgnsamples] totime %f\n", totime);
2220Sstevel@tonic-gate 	}
223211Smike_s #endif /* DEBUG */
2240Sstevel@tonic-gate }
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate static void
dump_callgraph(FILE * fp,char * filename,unsigned long tarcs,unsigned long ncallees)228211Smike_s dump_callgraph(FILE *fp, char *filename, unsigned long tarcs,
229211Smike_s     unsigned long ncallees)
2300Sstevel@tonic-gate {
2310Sstevel@tonic-gate 	ProfCallGraph		prof_cgraph;
2320Sstevel@tonic-gate 	ProfFunction		prof_func;
233211Smike_s 	arctype	*arcp;
2340Sstevel@tonic-gate 	mod_info_t		*mi;
2350Sstevel@tonic-gate 	nltype			*nlp;
2360Sstevel@tonic-gate 	size_t			cur_offset;
2370Sstevel@tonic-gate 	unsigned long		caller_id = 0, callee_id = 0;
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	/*
2400Sstevel@tonic-gate 	 * Write the callgraph header
2410Sstevel@tonic-gate 	 */
2420Sstevel@tonic-gate 	prof_cgraph.type = PROF_CALLGRAPH_T;
2430Sstevel@tonic-gate 	prof_cgraph.version = PROF_CALLGRAPH_VER;
2440Sstevel@tonic-gate 	prof_cgraph.functions = PROFCGRAPH_SZ;
2450Sstevel@tonic-gate 	prof_cgraph.size = PROFCGRAPH_SZ + tarcs * PROFFUNC_SZ;
2460Sstevel@tonic-gate 	if (fwrite(&prof_cgraph, sizeof (ProfCallGraph), 1, fp) != 1) {
2470Sstevel@tonic-gate 		perror(filename);
2480Sstevel@tonic-gate 		exit(EX_IOERR);
2490Sstevel@tonic-gate 	}
250211Smike_s 	/* CONSTCOND */
2510Sstevel@tonic-gate 	if (CGRAPH_FILLER)
252211Smike_s 		(void) fseek(fp, CGRAPH_FILLER, SEEK_CUR);
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 	/* Current offset inside the callgraph object */
2550Sstevel@tonic-gate 	cur_offset = prof_cgraph.functions;
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
2580Sstevel@tonic-gate 		for (nlp = mi->nl; nlp < mi->npe; nlp++) {
2590Sstevel@tonic-gate 			if (nlp->ncallers == 0)
2600Sstevel@tonic-gate 				continue;
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 			/* If this is the last callee, set next_to to 0 */
2630Sstevel@tonic-gate 			callee_id++;
2640Sstevel@tonic-gate 			if (callee_id == ncallees)
2650Sstevel@tonic-gate 				prof_func.next_to = 0;
2660Sstevel@tonic-gate 			else {
2670Sstevel@tonic-gate 				prof_func.next_to = cur_offset +
268*6951Sab196087 				    nlp->ncallers * PROFFUNC_SZ;
2690Sstevel@tonic-gate 			}
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 			/*
2720Sstevel@tonic-gate 			 * Dump this callee's raw arc information with all
2730Sstevel@tonic-gate 			 * its callers
2740Sstevel@tonic-gate 			 */
2750Sstevel@tonic-gate 			caller_id = 1;
2760Sstevel@tonic-gate 			for (arcp = nlp->parents; arcp;
277*6951Sab196087 			    arcp = arcp->arc_parentlist) {
2780Sstevel@tonic-gate 				/*
2790Sstevel@tonic-gate 				 * If no more callers for this callee, set
2800Sstevel@tonic-gate 				 * next_from to 0
2810Sstevel@tonic-gate 				 */
2820Sstevel@tonic-gate 				if (caller_id == nlp->ncallers)
2830Sstevel@tonic-gate 					prof_func.next_from = 0;
2840Sstevel@tonic-gate 				else {
2850Sstevel@tonic-gate 					prof_func.next_from = cur_offset +
286*6951Sab196087 					    PROFFUNC_SZ;
2870Sstevel@tonic-gate 				}
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 				prof_func.frompc =
290*6951Sab196087 				    arcp->arc_parentp->module->load_base +
291*6951Sab196087 				    (arcp->arc_parentp->value -
292*6951Sab196087 				    arcp->arc_parentp->module->txt_origin);
293*6951Sab196087 				prof_func.topc = mi->load_base +
294*6951Sab196087 				    (nlp->value - mi->txt_origin);
2950Sstevel@tonic-gate 				prof_func.count = arcp->arc_count;
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 				if (fwrite(&prof_func, sizeof (ProfFunction),
299*6951Sab196087 				    1, fp) != 1) {
3000Sstevel@tonic-gate 					perror(filename);
3010Sstevel@tonic-gate 					exit(EX_IOERR);
3020Sstevel@tonic-gate 				}
303211Smike_s 				/* CONSTCOND */
3040Sstevel@tonic-gate 				if (FUNC_FILLER)
305211Smike_s 					(void) fseek(fp, FUNC_FILLER, SEEK_CUR);
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 				cur_offset += PROFFUNC_SZ;
3080Sstevel@tonic-gate 				caller_id++;
3090Sstevel@tonic-gate 			}
3100Sstevel@tonic-gate 		} /* for nlp... */
3110Sstevel@tonic-gate 	} /* for mi... */
3120Sstevel@tonic-gate }
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate /*
3150Sstevel@tonic-gate  * To save all pc-hits in all the gmon.out's is infeasible, as this
3160Sstevel@tonic-gate  * may become quite huge even with a small number of files to sum.
3170Sstevel@tonic-gate  * Instead, we'll dump *fictitious hits* to correct functions
3180Sstevel@tonic-gate  * by scanning module namelists. Again, since this is summing
3190Sstevel@tonic-gate  * pc-hits, we may have to dump the pcsamples out in chunks if the
3200Sstevel@tonic-gate  * number of pc-hits is high.
3210Sstevel@tonic-gate  */
3220Sstevel@tonic-gate static void
dump_hits(FILE * fp,char * filename,nltype * nlp)3230Sstevel@tonic-gate dump_hits(FILE *fp, char *filename, nltype *nlp)
3240Sstevel@tonic-gate {
3250Sstevel@tonic-gate 	Address		*p, hitpc;
3260Sstevel@tonic-gate 	size_t		i, nelem, ntowrite;
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	if ((nelem = nlp->nticks) > PROF_BUFFER_SIZE)
3290Sstevel@tonic-gate 		nelem = PROF_BUFFER_SIZE;
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	if ((p = (Address *) calloc(nelem, sizeof (Address))) == NULL) {
332211Smike_s 		(void) fprintf(stderr, "%s: no room for %d pcsamples\n",
333*6951Sab196087 		    whoami, nelem);
3340Sstevel@tonic-gate 		exit(EX_OSERR);
3350Sstevel@tonic-gate 	}
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 	/*
3380Sstevel@tonic-gate 	 * Set up *fictitious* hits (to function entry) buffer
3390Sstevel@tonic-gate 	 */
3400Sstevel@tonic-gate 	hitpc = nlp->module->load_base + (nlp->value - nlp->module->txt_origin);
3410Sstevel@tonic-gate 	for (i = 0; i < nelem; i++)
3420Sstevel@tonic-gate 		p[i] = hitpc;
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	for (ntowrite = nlp->nticks; ntowrite >= nelem; ntowrite -= nelem) {
3450Sstevel@tonic-gate 		if (fwrite(p, nelem * sizeof (Address), 1, fp) != 1) {
3460Sstevel@tonic-gate 			perror(filename);
3470Sstevel@tonic-gate 			exit(EX_IOERR);
3480Sstevel@tonic-gate 		}
3490Sstevel@tonic-gate 	}
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 	if (ntowrite) {
3520Sstevel@tonic-gate 		if (fwrite(p, ntowrite * sizeof (Address), 1, fp) != 1) {
3530Sstevel@tonic-gate 			perror(filename);
3540Sstevel@tonic-gate 			exit(EX_IOERR);
3550Sstevel@tonic-gate 		}
3560Sstevel@tonic-gate 	}
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	free(p);
3590Sstevel@tonic-gate }
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate static void
dump_pcsamples(FILE * fp,char * filename,unsigned long * tarcs,unsigned long * ncallees)362211Smike_s dump_pcsamples(FILE *fp, char *filename, unsigned long *tarcs,
363211Smike_s     unsigned long *ncallees)
3640Sstevel@tonic-gate {
3650Sstevel@tonic-gate 	ProfBuffer		prof_buffer;
366211Smike_s 	arctype	*arcp;
3670Sstevel@tonic-gate 	mod_info_t		*mi;
3680Sstevel@tonic-gate 	nltype			*nlp;
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	prof_buffer.type = PROF_BUFFER_T;
3710Sstevel@tonic-gate 	prof_buffer.version = PROF_BUFFER_VER;
3720Sstevel@tonic-gate 	prof_buffer.buffer = PROFBUF_SZ;
3730Sstevel@tonic-gate 	prof_buffer.bufsize = n_pcsamples;
3740Sstevel@tonic-gate 	prof_buffer.size = PROFBUF_SZ + n_pcsamples * sizeof (Address);
3750Sstevel@tonic-gate 	if (fwrite(&prof_buffer, sizeof (ProfBuffer), 1, fp) != 1) {
3760Sstevel@tonic-gate 		perror(filename);
3770Sstevel@tonic-gate 		exit(EX_IOERR);
3780Sstevel@tonic-gate 	}
379211Smike_s 	/* CONSTCOND */
3800Sstevel@tonic-gate 	if (BUF_FILLER)
381211Smike_s 		(void) fseek(fp, BUF_FILLER, SEEK_CUR);
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 	*tarcs = 0;
3840Sstevel@tonic-gate 	*ncallees = 0;
3850Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
3860Sstevel@tonic-gate 		for (nlp = mi->nl; nlp < mi->npe; nlp++) {
3870Sstevel@tonic-gate 			if (nlp->nticks)
3880Sstevel@tonic-gate 				dump_hits(fp, filename, nlp);
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 			nlp->ncallers = 0;
3910Sstevel@tonic-gate 			for (arcp = nlp->parents; arcp;
392*6951Sab196087 			    arcp = arcp->arc_parentlist) {
3930Sstevel@tonic-gate 				(nlp->ncallers)++;
3940Sstevel@tonic-gate 			}
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 			if (nlp->ncallers) {
3970Sstevel@tonic-gate 				(*tarcs) += nlp->ncallers;
3980Sstevel@tonic-gate 				(*ncallees)++;
3990Sstevel@tonic-gate 			}
4000Sstevel@tonic-gate 		}
4010Sstevel@tonic-gate 	}
4020Sstevel@tonic-gate }
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate static void
dump_modules(FILE * fp,char * filename,size_t pbuf_sz)4050Sstevel@tonic-gate dump_modules(FILE *fp, char *filename, size_t pbuf_sz)
4060Sstevel@tonic-gate {
4070Sstevel@tonic-gate 	char		*pbuf, *p;
4080Sstevel@tonic-gate 	size_t		namelen;
4090Sstevel@tonic-gate 	Index		off_nxt, off_path;
4100Sstevel@tonic-gate 	mod_info_t	*mi;
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 	ProfModuleList	prof_modlist;
4130Sstevel@tonic-gate 	ProfModule	prof_mod;
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	/* Allocate for path strings buffer */
4160Sstevel@tonic-gate 	pbuf_sz = CEIL(pbuf_sz, STRUCT_ALIGN);
417211Smike_s 	if ((p = pbuf = calloc(pbuf_sz, sizeof (char))) == NULL) {
418211Smike_s 		(void) fprintf(stderr, "%s: no room for %d bytes\n",
419*6951Sab196087 		    whoami, pbuf_sz * sizeof (char));
4200Sstevel@tonic-gate 		exit(EX_OSERR);
4210Sstevel@tonic-gate 	}
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 	/* Dump out PROF_MODULE_T info for all non-aout modules */
4240Sstevel@tonic-gate 	prof_modlist.type = PROF_MODULES_T;
4250Sstevel@tonic-gate 	prof_modlist.version = PROF_MODULES_VER;
4260Sstevel@tonic-gate 	prof_modlist.modules = PROFMODLIST_SZ;
4270Sstevel@tonic-gate 	prof_modlist.size = PROFMODLIST_SZ + (n_modules - 1) * PROFMOD_SZ +
428*6951Sab196087 	    pbuf_sz;
4290Sstevel@tonic-gate 	if (fwrite(&prof_modlist, sizeof (ProfModuleList), 1, fp) != 1) {
4300Sstevel@tonic-gate 		perror(filename);
4310Sstevel@tonic-gate 		exit(EX_IOERR);
4320Sstevel@tonic-gate 	}
433211Smike_s 	/* CONSTCOND */
4340Sstevel@tonic-gate 	if (MODLIST_FILLER)
435211Smike_s 		(void) fseek(fp, MODLIST_FILLER, SEEK_CUR);
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 	/*
4380Sstevel@tonic-gate 	 * Initialize offsets for ProfModule elements.
4390Sstevel@tonic-gate 	 */
4400Sstevel@tonic-gate 	off_nxt = PROFMODLIST_SZ + PROFMOD_SZ;
4410Sstevel@tonic-gate 	off_path = PROFMODLIST_SZ + (n_modules - 1) * PROFMOD_SZ;
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 	for (mi = modules.next; mi; mi = mi->next) {
4440Sstevel@tonic-gate 		if (mi->next)
4450Sstevel@tonic-gate 			prof_mod.next = off_nxt;
4460Sstevel@tonic-gate 		else
4470Sstevel@tonic-gate 			prof_mod.next = 0;
4480Sstevel@tonic-gate 		prof_mod.path = off_path;
4490Sstevel@tonic-gate 		prof_mod.startaddr = mi->load_base;
4500Sstevel@tonic-gate 		prof_mod.endaddr = mi->load_end;
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 		if (fwrite(&prof_mod, sizeof (ProfModule), 1, fp) != 1) {
4530Sstevel@tonic-gate 			perror(filename);
4540Sstevel@tonic-gate 			exit(EX_IOERR);
4550Sstevel@tonic-gate 		}
4560Sstevel@tonic-gate 
457211Smike_s 		/* CONSTCOND */
4580Sstevel@tonic-gate 		if (MOD_FILLER)
459211Smike_s 			(void) fseek(fp, MOD_FILLER, SEEK_CUR);
4600Sstevel@tonic-gate 
461211Smike_s 		(void) strcpy(p, mi->name);
4620Sstevel@tonic-gate 		namelen = strlen(mi->name);
4630Sstevel@tonic-gate 		p += namelen + 1;
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 		/* Note that offset to every path str need not be aligned */
4660Sstevel@tonic-gate 		off_nxt += PROFMOD_SZ;
4670Sstevel@tonic-gate 		off_path += namelen + 1;
4680Sstevel@tonic-gate 	}
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	/* Write out the module path strings */
4710Sstevel@tonic-gate 	if (pbuf_sz) {
4720Sstevel@tonic-gate 		if (fwrite(pbuf, pbuf_sz, 1, fp) != 1) {
4730Sstevel@tonic-gate 			perror(filename);
4740Sstevel@tonic-gate 			exit(EX_IOERR);
4750Sstevel@tonic-gate 		}
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 		free(pbuf);
4780Sstevel@tonic-gate 	}
4790Sstevel@tonic-gate }
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate /*
4820Sstevel@tonic-gate  * If we have inactive modules, their current load addresses may overlap with
4830Sstevel@tonic-gate  * active ones, and so we've to assign fictitious, non-overlapping addresses
4840Sstevel@tonic-gate  * to all modules before we dump them.
4850Sstevel@tonic-gate  */
4860Sstevel@tonic-gate static void
fixup_maps(size_t * pathsz)4870Sstevel@tonic-gate fixup_maps(size_t *pathsz)
4880Sstevel@tonic-gate {
4890Sstevel@tonic-gate 	unsigned int	n_inactive = 0;
490211Smike_s 	Address		lbase = 0, lend;
4910Sstevel@tonic-gate 	mod_info_t	*mi;
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 	/* Pick the lowest load address among modules */
4940Sstevel@tonic-gate 	*pathsz = 0;
4950Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 		if (mi->active == FALSE)
4980Sstevel@tonic-gate 			n_inactive++;
4990Sstevel@tonic-gate 
5000Sstevel@tonic-gate 		if (mi == &modules || mi->load_base < lbase)
5010Sstevel@tonic-gate 			lbase = mi->load_base;
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 		/*
5040Sstevel@tonic-gate 		 * Return total path size of non-aout modules only
5050Sstevel@tonic-gate 		 */
5060Sstevel@tonic-gate 		if (mi != &modules)
5070Sstevel@tonic-gate 			*pathsz = (*pathsz) + strlen(mi->name) + 1;
5080Sstevel@tonic-gate 	}
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	/*
5110Sstevel@tonic-gate 	 * All module info is in fine shape already if there are no
5120Sstevel@tonic-gate 	 * inactive modules
5130Sstevel@tonic-gate 	 */
5140Sstevel@tonic-gate 	if (n_inactive == 0)
5150Sstevel@tonic-gate 		return;
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 	/*
5180Sstevel@tonic-gate 	 * Assign fictitious load addresses to all (non-aout) modules so
5190Sstevel@tonic-gate 	 * that sum info can be dumped out.
5200Sstevel@tonic-gate 	 */
5210Sstevel@tonic-gate 	for (mi = modules.next; mi; mi = mi->next) {
5220Sstevel@tonic-gate 		lend = lbase + (mi->data_end - mi->txt_origin);
5230Sstevel@tonic-gate 		if ((lbase < modules.load_base && lend < modules.load_base) ||
5240Sstevel@tonic-gate 		    (lbase > modules.load_end && lend > modules.load_end)) {
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 			mi->load_base = lbase;
5270Sstevel@tonic-gate 			mi->load_end = lend;
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 			/* just to give an appearance of reality */
5300Sstevel@tonic-gate 			lbase = CEIL(lend + PGSZ, PGSZ);
5310Sstevel@tonic-gate 		} else {
5320Sstevel@tonic-gate 			/*
5330Sstevel@tonic-gate 			 * can't use this lbase & lend pair, as it
5340Sstevel@tonic-gate 			 * overlaps with aout's addresses
5350Sstevel@tonic-gate 			 */
5360Sstevel@tonic-gate 			mi->load_base = CEIL(modules.load_end + PGSZ, PGSZ);
5370Sstevel@tonic-gate 			mi->load_end = mi->load_base + (lend - lbase);
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 			lbase = CEIL(mi->load_end + PGSZ, PGSZ);
5400Sstevel@tonic-gate 		}
5410Sstevel@tonic-gate 	}
5420Sstevel@tonic-gate }
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate static void
dump_gprofhdr(FILE * fp,char * filename)5450Sstevel@tonic-gate dump_gprofhdr(FILE *fp, char *filename)
5460Sstevel@tonic-gate {
5470Sstevel@tonic-gate 	ProfHeader	prof_hdr;
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 	prof_hdr.h_magic = PROF_MAGIC;
5500Sstevel@tonic-gate 	prof_hdr.h_major_ver = PROF_MAJOR_VERSION;
5510Sstevel@tonic-gate 	prof_hdr.h_minor_ver = PROF_MINOR_VERSION;
5520Sstevel@tonic-gate 	prof_hdr.size = PROFHDR_SZ;
5530Sstevel@tonic-gate 	if (fwrite(&prof_hdr, sizeof (prof_hdr), 1, fp) != 1) {
5540Sstevel@tonic-gate 		perror(filename);
5550Sstevel@tonic-gate 		exit(EX_IOERR);
5560Sstevel@tonic-gate 	}
5570Sstevel@tonic-gate 
558211Smike_s 	/* CONSTCOND */
5590Sstevel@tonic-gate 	if (HDR_FILLER)
560211Smike_s 		(void) fseek(fp, HDR_FILLER, SEEK_CUR);
5610Sstevel@tonic-gate }
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate static void
dumpsum_ostyle(char * sumfile)5640Sstevel@tonic-gate dumpsum_ostyle(char *sumfile)
5650Sstevel@tonic-gate {
566211Smike_s 	nltype *nlp;
567211Smike_s 	arctype *arcp;
5680Sstevel@tonic-gate 	struct rawarc arc;
5690Sstevel@tonic-gate 	struct rawarc32 arc32;
5700Sstevel@tonic-gate 	FILE *sfile;
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 	if ((sfile = fopen(sumfile, "w")) == NULL) {
5730Sstevel@tonic-gate 		perror(sumfile);
5740Sstevel@tonic-gate 		exit(EX_IOERR);
5750Sstevel@tonic-gate 	}
5760Sstevel@tonic-gate 	/*
5770Sstevel@tonic-gate 	 * dump the header; use the last header read in
5780Sstevel@tonic-gate 	 */
5790Sstevel@tonic-gate 	if (Bflag) {
580*6951Sab196087 		if (fwrite(&h, sizeof (h), 1, sfile) != 1) {
581*6951Sab196087 			perror(sumfile);
582*6951Sab196087 			exit(EX_IOERR);
583*6951Sab196087 		}
5840Sstevel@tonic-gate 	} else {
585*6951Sab196087 		struct hdr32 hdr;
586*6951Sab196087 		hdr.lowpc  = (pctype32)h.lowpc;
587*6951Sab196087 		hdr.highpc = (pctype32)h.highpc;
588*6951Sab196087 		hdr.ncnt   = (pctype32)h.ncnt;
589*6951Sab196087 		if (fwrite(&hdr, sizeof (hdr), 1, sfile) != 1) {
590*6951Sab196087 			perror(sumfile);
591*6951Sab196087 			exit(EX_IOERR);
592*6951Sab196087 		}
5930Sstevel@tonic-gate 	}
5940Sstevel@tonic-gate 	/*
5950Sstevel@tonic-gate 	 * dump the samples
5960Sstevel@tonic-gate 	 */
5970Sstevel@tonic-gate 	if (fwrite(samples, sizeof (unsigned_UNIT), nsamples, sfile) !=
5980Sstevel@tonic-gate 	    nsamples) {
5990Sstevel@tonic-gate 		perror(sumfile);
6000Sstevel@tonic-gate 		exit(EX_IOERR);
6010Sstevel@tonic-gate 	}
6020Sstevel@tonic-gate 	/*
6030Sstevel@tonic-gate 	 * dump the normalized raw arc information. For old-style dumping,
6040Sstevel@tonic-gate 	 * the only namelist is in modules.nl
6050Sstevel@tonic-gate 	 */
6060Sstevel@tonic-gate 	for (nlp = modules.nl; nlp < modules.npe; nlp++) {
6070Sstevel@tonic-gate 		for (arcp = nlp->children; arcp;
6080Sstevel@tonic-gate 		    arcp = arcp->arc_childlist) {
6090Sstevel@tonic-gate 			if (Bflag) {
610*6951Sab196087 				arc.raw_frompc = arcp->arc_parentp->value;
611*6951Sab196087 				arc.raw_selfpc = arcp->arc_childp->value;
612*6951Sab196087 				arc.raw_count = arcp->arc_count;
613*6951Sab196087 				if (fwrite(&arc, sizeof (arc), 1, sfile) != 1) {
614*6951Sab196087 					perror(sumfile);
615*6951Sab196087 					exit(EX_IOERR);
616*6951Sab196087 				}
6170Sstevel@tonic-gate 			} else {
618*6951Sab196087 				arc32.raw_frompc =
619*6951Sab196087 				    (pctype32)arcp->arc_parentp->value;
620*6951Sab196087 				arc32.raw_selfpc =
621*6951Sab196087 				    (pctype32)arcp->arc_childp->value;
622*6951Sab196087 				arc32.raw_count = (actype32)arcp->arc_count;
623*6951Sab196087 				if (fwrite(&arc32, sizeof (arc32), 1, sfile) !=
624*6951Sab196087 				    1) {
625*6951Sab196087 					perror(sumfile);
626*6951Sab196087 					exit(EX_IOERR);
627*6951Sab196087 				}
6280Sstevel@tonic-gate 			}
6290Sstevel@tonic-gate #ifdef DEBUG
6300Sstevel@tonic-gate 			if (debug & SAMPLEDEBUG) {
631211Smike_s 				(void) printf(
632211Smike_s 				    "[dumpsum_ostyle] frompc 0x%llx selfpc "
6330Sstevel@tonic-gate 				    "0x%llx count %lld\n", arc.raw_frompc,
6340Sstevel@tonic-gate 				    arc.raw_selfpc, arc.raw_count);
6350Sstevel@tonic-gate 			}
636211Smike_s #endif /* DEBUG */
6370Sstevel@tonic-gate 		}
6380Sstevel@tonic-gate 	}
639211Smike_s 	(void) fclose(sfile);
6400Sstevel@tonic-gate }
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate /*
6430Sstevel@tonic-gate  * dump out the gmon.sum file
6440Sstevel@tonic-gate  */
6450Sstevel@tonic-gate static void
dumpsum(char * sumfile)6460Sstevel@tonic-gate dumpsum(char *sumfile)
6470Sstevel@tonic-gate {
6480Sstevel@tonic-gate 	FILE		*sfile;
6490Sstevel@tonic-gate 	size_t		pathbuf_sz;
6500Sstevel@tonic-gate 	unsigned long	total_arcs;	/* total number of arcs in all */
6510Sstevel@tonic-gate 	unsigned long	ncallees;	/* no. of callees with parents */
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	if (old_style) {
6540Sstevel@tonic-gate 		dumpsum_ostyle(sumfile);
6550Sstevel@tonic-gate 		return;
6560Sstevel@tonic-gate 	}
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 	if ((sfile = fopen(sumfile, "w")) == NULL) {
6590Sstevel@tonic-gate 		perror(sumfile);
6600Sstevel@tonic-gate 		exit(EX_IOERR);
6610Sstevel@tonic-gate 	}
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	/*
6640Sstevel@tonic-gate 	 * Dump the new-style gprof header. Even if one of the original
6650Sstevel@tonic-gate 	 * profiled-files was of a older version, the summed file is of
6660Sstevel@tonic-gate 	 * current version only.
6670Sstevel@tonic-gate 	 */
6680Sstevel@tonic-gate 	dump_gprofhdr(sfile, sumfile);
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	/*
6710Sstevel@tonic-gate 	 * Fix up load-maps and dump out modules info
6720Sstevel@tonic-gate 	 *
6730Sstevel@tonic-gate 	 * Fix up module load maps so inactive modules get *some* address
6740Sstevel@tonic-gate 	 * (and btw, could you get the total size of non-aout module path
6750Sstevel@tonic-gate 	 * strings please ?)
6760Sstevel@tonic-gate 	 */
6770Sstevel@tonic-gate 	fixup_maps(&pathbuf_sz);
6780Sstevel@tonic-gate 	dump_modules(sfile, sumfile, pathbuf_sz);
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	/*
6820Sstevel@tonic-gate 	 * Dump out the summ'd pcsamples
6830Sstevel@tonic-gate 	 *
6840Sstevel@tonic-gate 	 * For dumping call graph information later, we need certain
6850Sstevel@tonic-gate 	 * statistics (like total arcs, number of callers for each node);
6860Sstevel@tonic-gate 	 * collect these also while we are at it.
6870Sstevel@tonic-gate 	 */
6880Sstevel@tonic-gate 	dump_pcsamples(sfile, sumfile, &total_arcs, &ncallees);
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	/*
6910Sstevel@tonic-gate 	 * Dump out the summ'd call graph information
6920Sstevel@tonic-gate 	 */
6930Sstevel@tonic-gate 	dump_callgraph(sfile, sumfile, total_arcs, ncallees);
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate 
696211Smike_s 	(void) fclose(sfile);
6970Sstevel@tonic-gate }
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate static void
tally(mod_info_t * caller_mod,mod_info_t * callee_mod,struct rawarc * rawp)7000Sstevel@tonic-gate tally(mod_info_t *caller_mod, mod_info_t *callee_mod, struct rawarc *rawp)
7010Sstevel@tonic-gate {
7020Sstevel@tonic-gate 	nltype		*parentp;
7030Sstevel@tonic-gate 	nltype		*childp;
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 	/*
7060Sstevel@tonic-gate 	 * if count == 0 this is a null arc and
7070Sstevel@tonic-gate 	 * we don't need to tally it.
7080Sstevel@tonic-gate 	 */
7090Sstevel@tonic-gate 	if (rawp->raw_count == 0)
7100Sstevel@tonic-gate 		return;
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 	/*
7130Sstevel@tonic-gate 	 * Lookup the caller and callee pcs in namelists of
7140Sstevel@tonic-gate 	 * appropriate modules
7150Sstevel@tonic-gate 	 */
7160Sstevel@tonic-gate 	parentp = nllookup(caller_mod, rawp->raw_frompc, NULL);
7170Sstevel@tonic-gate 	childp = nllookup(callee_mod, rawp->raw_selfpc, NULL);
7180Sstevel@tonic-gate 	if (childp && parentp) {
7190Sstevel@tonic-gate 		if (!Dflag)
7200Sstevel@tonic-gate 			childp->ncall += rawp->raw_count;
7210Sstevel@tonic-gate 		else {
7220Sstevel@tonic-gate 			if (first_file)
7230Sstevel@tonic-gate 				childp->ncall += rawp->raw_count;
7240Sstevel@tonic-gate 			else {
7250Sstevel@tonic-gate 				childp->ncall -= rawp->raw_count;
7260Sstevel@tonic-gate 				if (childp->ncall < 0)
7270Sstevel@tonic-gate 					childp->ncall = 0;
7280Sstevel@tonic-gate 			}
7290Sstevel@tonic-gate 		}
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate #ifdef DEBUG
7320Sstevel@tonic-gate 		if (debug & TALLYDEBUG) {
733211Smike_s 			(void) printf("[tally] arc from %s to %s traversed "
7340Sstevel@tonic-gate 			    "%lld times\n", parentp->name,
7350Sstevel@tonic-gate 			    childp->name, rawp->raw_count);
7360Sstevel@tonic-gate 		}
737211Smike_s #endif /* DEBUG */
7380Sstevel@tonic-gate 		addarc(parentp, childp, rawp->raw_count);
7390Sstevel@tonic-gate 	}
7400Sstevel@tonic-gate }
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate /*
7430Sstevel@tonic-gate  * Look up a module's base address in a sorted list of pc-hits. Unlike
7440Sstevel@tonic-gate  * nllookup(), this deals with misses by mapping them to the next *higher*
7450Sstevel@tonic-gate  * pc-hit. This is so that we get into the module's first pc-hit rightaway,
7460Sstevel@tonic-gate  * even if the module's entry-point (load_base) itself is not a hit.
7470Sstevel@tonic-gate  */
7480Sstevel@tonic-gate static Address *
locate(Address * pclist,size_t nelem,Address keypc)7490Sstevel@tonic-gate locate(Address	*pclist, size_t nelem, Address keypc)
7500Sstevel@tonic-gate {
7510Sstevel@tonic-gate 	size_t	low = 0, middle, high = nelem - 1;
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate 	if (keypc <= pclist[low])
7540Sstevel@tonic-gate 		return (pclist);
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate 	if (keypc > pclist[high])
7570Sstevel@tonic-gate 		return (NULL);
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 	while (low != high) {
7600Sstevel@tonic-gate 		middle = (high + low) >> 1;
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 		if ((pclist[middle] < keypc) && (pclist[middle + 1] >= keypc))
7630Sstevel@tonic-gate 			return (&pclist[middle + 1]);
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate 		if (pclist[middle] >= keypc)
7660Sstevel@tonic-gate 			high = middle;
7670Sstevel@tonic-gate 		else
7680Sstevel@tonic-gate 			low = middle + 1;
7690Sstevel@tonic-gate 	}
7700Sstevel@tonic-gate 
7710Sstevel@tonic-gate 	/* must never reach here! */
7720Sstevel@tonic-gate 	return (NULL);
7730Sstevel@tonic-gate }
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate static void
assign_pcsamples(mod_info_t * module,Address * pcsmpl,size_t n_samples)776211Smike_s assign_pcsamples(mod_info_t *module, Address *pcsmpl, size_t n_samples)
7770Sstevel@tonic-gate {
7780Sstevel@tonic-gate 	Address		*pcptr, *pcse = pcsmpl + n_samples;
7790Sstevel@tonic-gate 	pctype		nxt_func;
7800Sstevel@tonic-gate 	nltype		*fnl;
7810Sstevel@tonic-gate 	size_t		func_nticks;
7820Sstevel@tonic-gate #ifdef DEBUG
7830Sstevel@tonic-gate 	size_t		n_hits_in_module = 0;
784211Smike_s #endif /* DEBUG */
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate 	/* Locate the first pc-hit for this module */
7870Sstevel@tonic-gate 	if ((pcptr = locate(pcsmpl, n_samples, module->load_base)) == NULL) {
7880Sstevel@tonic-gate #ifdef DEBUG
7890Sstevel@tonic-gate 		if (debug & PCSMPLDEBUG) {
790211Smike_s 			(void) printf("[assign_pcsamples] no pc-hits in\n");
791211Smike_s 			(void) printf(
792211Smike_s 			    "                   `%s'\n", module->name);
7930Sstevel@tonic-gate 		}
794211Smike_s #endif /* DEBUG */
7950Sstevel@tonic-gate 		return;			/* no pc-hits in this module */
7960Sstevel@tonic-gate 	}
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 	/* Assign all pc-hits in this module to appropriate functions */
7990Sstevel@tonic-gate 	while ((pcptr < pcse) && (*pcptr < module->load_end)) {
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 		/* Update the corresponding function's time */
8020Sstevel@tonic-gate 		if (fnl = nllookup(module, (pctype) *pcptr, &nxt_func)) {
8030Sstevel@tonic-gate 			/*
8040Sstevel@tonic-gate 			 * Collect all pc-hits in this function. Each
8050Sstevel@tonic-gate 			 * pc-hit counts as 1 tick.
8060Sstevel@tonic-gate 			 */
8070Sstevel@tonic-gate 			func_nticks = 0;
8080Sstevel@tonic-gate 			while ((pcptr < pcse) && (*pcptr < nxt_func)) {
8090Sstevel@tonic-gate 				func_nticks++;
8100Sstevel@tonic-gate 				pcptr++;
8110Sstevel@tonic-gate 			}
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 			if (func_nticks == 0)
8140Sstevel@tonic-gate 				pcptr++;
8150Sstevel@tonic-gate 			else {
8160Sstevel@tonic-gate 				fnl->nticks += func_nticks;
8170Sstevel@tonic-gate 				fnl->time += func_nticks;
8180Sstevel@tonic-gate 				totime += func_nticks;
8190Sstevel@tonic-gate 			}
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate #ifdef DEBUG
8220Sstevel@tonic-gate 			n_hits_in_module += func_nticks;
823211Smike_s #endif /* DEBUG */
8240Sstevel@tonic-gate 		} else {
8250Sstevel@tonic-gate 			/*
8260Sstevel@tonic-gate 			 * pc sample could not be assigned to function;
8270Sstevel@tonic-gate 			 * probably in a PLT
8280Sstevel@tonic-gate 			 */
8290Sstevel@tonic-gate 			pcptr++;
8300Sstevel@tonic-gate 		}
8310Sstevel@tonic-gate 	}
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate #ifdef DEBUG
8340Sstevel@tonic-gate 	if (debug & PCSMPLDEBUG) {
835211Smike_s 		(void) printf(
836211Smike_s 		    "[assign_pcsamples] %ld hits in\n", n_hits_in_module);
837211Smike_s 		(void) printf("                   `%s'\n", module->name);
8380Sstevel@tonic-gate 	}
839211Smike_s #endif /* DEBUG */
8400Sstevel@tonic-gate }
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate int
pc_cmp(const void * arg1,const void * arg2)843211Smike_s pc_cmp(const void *arg1, const void *arg2)
8440Sstevel@tonic-gate {
845211Smike_s 	Address *pc1 = (Address *)arg1;
846211Smike_s 	Address *pc2 = (Address *)arg2;
847211Smike_s 
8480Sstevel@tonic-gate 	if (*pc1 > *pc2)
8490Sstevel@tonic-gate 		return (1);
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	if (*pc1 < *pc2)
8520Sstevel@tonic-gate 		return (-1);
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 	return (0);
8550Sstevel@tonic-gate }
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate static void
process_pcsamples(ProfBuffer * bufp)858211Smike_s process_pcsamples(ProfBuffer *bufp)
8590Sstevel@tonic-gate {
8600Sstevel@tonic-gate 	Address		*pc_samples;
8610Sstevel@tonic-gate 	mod_info_t	*mi;
8620Sstevel@tonic-gate 	caddr_t		p;
8630Sstevel@tonic-gate 	size_t		chunk_size, nelem_read, nelem_to_read;
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate #ifdef DEBUG
8660Sstevel@tonic-gate 	if (debug & PCSMPLDEBUG) {
867211Smike_s 		(void) printf(
868211Smike_s 		    "[process_pcsamples] number of pcsamples = %lld\n",
869211Smike_s 		    bufp->bufsize);
8700Sstevel@tonic-gate 	}
871211Smike_s #endif /* DEBUG */
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate 	/* buffer with no pc samples ? */
8740Sstevel@tonic-gate 	if (bufp->bufsize == 0)
8750Sstevel@tonic-gate 		return;
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 	/*
8780Sstevel@tonic-gate 	 * If we're processing pcsamples of a profile sum, we could have
8790Sstevel@tonic-gate 	 * more than PROF_BUFFER_SIZE number of samples. In such a case,
8800Sstevel@tonic-gate 	 * we must read the pcsamples in chunks.
8810Sstevel@tonic-gate 	 */
8820Sstevel@tonic-gate 	if ((chunk_size = bufp->bufsize) > PROF_BUFFER_SIZE)
8830Sstevel@tonic-gate 		chunk_size = PROF_BUFFER_SIZE;
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 	/* Allocate for the pcsample chunk */
8860Sstevel@tonic-gate 	pc_samples = (Address *) calloc(chunk_size, sizeof (Address));
8870Sstevel@tonic-gate 	if (pc_samples == NULL) {
888211Smike_s 		(void) fprintf(stderr, "%s: no room for %d sample pc's\n",
889*6951Sab196087 		    whoami, chunk_size);
8900Sstevel@tonic-gate 		exit(EX_OSERR);
8910Sstevel@tonic-gate 	}
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 	/* Copy the current set of pcsamples */
8940Sstevel@tonic-gate 	nelem_read = 0;
8950Sstevel@tonic-gate 	nelem_to_read = bufp->bufsize;
896211Smike_s 	p = (char *)bufp + bufp->buffer;
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 	while (nelem_read < nelem_to_read) {
899211Smike_s 		(void) memcpy((void *) pc_samples, p,
900211Smike_s 		    chunk_size * sizeof (Address));
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 		/* Sort the pc samples */
903211Smike_s 		qsort(pc_samples, chunk_size, sizeof (Address), pc_cmp);
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 		/*
9060Sstevel@tonic-gate 		 * Assign pcsamples to functions in the currently active
9070Sstevel@tonic-gate 		 * module list
9080Sstevel@tonic-gate 		 */
9090Sstevel@tonic-gate 		for (mi = &modules; mi; mi = mi->next) {
9100Sstevel@tonic-gate 			if (mi->active == FALSE)
9110Sstevel@tonic-gate 				continue;
9120Sstevel@tonic-gate 			assign_pcsamples(mi, pc_samples, chunk_size);
9130Sstevel@tonic-gate 		}
9140Sstevel@tonic-gate 
9150Sstevel@tonic-gate 		p += (chunk_size * sizeof (Address));
9160Sstevel@tonic-gate 		nelem_read += chunk_size;
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate 		if ((nelem_to_read - nelem_read) < chunk_size)
9190Sstevel@tonic-gate 			chunk_size = nelem_to_read - nelem_read;
9200Sstevel@tonic-gate 	}
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 	free(pc_samples);
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate 	/* Update total number of pcsamples read so far */
9250Sstevel@tonic-gate 	n_pcsamples += bufp->bufsize;
9260Sstevel@tonic-gate }
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate static mod_info_t *
find_module(Address addr)9290Sstevel@tonic-gate find_module(Address addr)
9300Sstevel@tonic-gate {
9310Sstevel@tonic-gate 	mod_info_t	*mi;
9320Sstevel@tonic-gate 
9330Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
9340Sstevel@tonic-gate 		if (mi->active == FALSE)
9350Sstevel@tonic-gate 			continue;
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 		if (addr >= mi->load_base && addr < mi->load_end)
9380Sstevel@tonic-gate 			return (mi);
9390Sstevel@tonic-gate 	}
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 	return (NULL);
9420Sstevel@tonic-gate }
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate static void
process_cgraph(ProfCallGraph * cgp)945211Smike_s process_cgraph(ProfCallGraph *cgp)
9460Sstevel@tonic-gate {
9470Sstevel@tonic-gate 	struct rawarc	arc;
9480Sstevel@tonic-gate 	mod_info_t	*callee_mi, *caller_mi;
9490Sstevel@tonic-gate 	ProfFunction	*calleep, *callerp;
9500Sstevel@tonic-gate 	Index		caller_off, callee_off;
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate 	/*
9530Sstevel@tonic-gate 	 * Note that *callee_off* increment in the for loop below
9540Sstevel@tonic-gate 	 * uses *calleep* and *calleep* doesn't get set until the for loop
9550Sstevel@tonic-gate 	 * is entered. We don't expect the increment to be executed before
9560Sstevel@tonic-gate 	 * the loop body is executed atleast once, so this should be ok.
9570Sstevel@tonic-gate 	 */
9580Sstevel@tonic-gate 	for (callee_off = cgp->functions; callee_off;
959*6951Sab196087 	    callee_off = calleep->next_to) {
9600Sstevel@tonic-gate 
961211Smike_s 		/* LINTED: pointer cast */
962211Smike_s 		calleep = (ProfFunction *)((char *)cgp + callee_off);
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate 		/*
9650Sstevel@tonic-gate 		 * We could choose either to sort the {caller, callee}
9660Sstevel@tonic-gate 		 * list twice and assign callee/caller to modules or inspect
9670Sstevel@tonic-gate 		 * each callee/caller in the active modules list. Since
9680Sstevel@tonic-gate 		 * the modules list is usually very small, we'l choose the
9690Sstevel@tonic-gate 		 * latter.
9700Sstevel@tonic-gate 		 */
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 		/*
9730Sstevel@tonic-gate 		 * If we cannot identify a callee with a module, there's
9740Sstevel@tonic-gate 		 * no use worrying about who called it.
9750Sstevel@tonic-gate 		 */
9760Sstevel@tonic-gate 		if ((callee_mi = find_module(calleep->topc)) == NULL) {
9770Sstevel@tonic-gate #ifdef DEBUG
9780Sstevel@tonic-gate 			if (debug & CGRAPHDEBUG) {
979211Smike_s 				(void) printf(
980211Smike_s 				    "[process_cgraph] callee %#llx missed\n",
981211Smike_s 				    calleep->topc);
9820Sstevel@tonic-gate 			}
983211Smike_s #endif /* DEBUG */
9840Sstevel@tonic-gate 			continue;
9850Sstevel@tonic-gate 		} else
9860Sstevel@tonic-gate 			arc.raw_selfpc = calleep->topc;
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 		for (caller_off = callee_off; caller_off;
989*6951Sab196087 		    caller_off = callerp->next_from)  {
9900Sstevel@tonic-gate 
991211Smike_s 			/* LINTED: pointer cast */
992211Smike_s 			callerp = (ProfFunction *)((char *)cgp + caller_off);
9930Sstevel@tonic-gate 			if ((caller_mi = find_module(callerp->frompc)) ==
994*6951Sab196087 			    NULL) {
9950Sstevel@tonic-gate #ifdef DEBUG
9960Sstevel@tonic-gate 				if (debug & CGRAPHDEBUG) {
997211Smike_s 					(void) printf(
998211Smike_s 					    "[process_cgraph] caller %#llx "
999211Smike_s 					    "missed\n", callerp->frompc);
10000Sstevel@tonic-gate 				}
1001211Smike_s #endif /* DEBUG */
10020Sstevel@tonic-gate 				continue;
10030Sstevel@tonic-gate 			}
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 			arc.raw_frompc = callerp->frompc;
10060Sstevel@tonic-gate 			arc.raw_count = callerp->count;
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate #ifdef DEBUG
10090Sstevel@tonic-gate 			if (debug & CGRAPHDEBUG) {
1010211Smike_s 				(void) printf(
1011211Smike_s 				    "[process_cgraph] arc <%#llx, %#llx, "
1012211Smike_s 				    "%lld>\n", arc.raw_frompc, arc.raw_selfpc,
1013211Smike_s 				    arc.raw_count);
10140Sstevel@tonic-gate 			}
1015211Smike_s #endif /* DEBUG */
10160Sstevel@tonic-gate 			tally(caller_mi, callee_mi, &arc);
10170Sstevel@tonic-gate 		}
10180Sstevel@tonic-gate 	}
10190Sstevel@tonic-gate 
10200Sstevel@tonic-gate #ifdef DEBUG
10210Sstevel@tonic-gate 	puts("\n");
1022211Smike_s #endif /* DEBUG */
10230Sstevel@tonic-gate }
10240Sstevel@tonic-gate 
10250Sstevel@tonic-gate /*
10260Sstevel@tonic-gate  * Two modules overlap each other if they don't lie completely *outside*
10270Sstevel@tonic-gate  * each other.
10280Sstevel@tonic-gate  */
10290Sstevel@tonic-gate static bool
does_overlap(ProfModule * new,mod_info_t * old)10300Sstevel@tonic-gate does_overlap(ProfModule *new, mod_info_t *old)
10310Sstevel@tonic-gate {
10320Sstevel@tonic-gate 	/* case 1: new module lies completely *before* the old one */
10330Sstevel@tonic-gate 	if (new->startaddr < old->load_base && new->endaddr <= old->load_base)
10340Sstevel@tonic-gate 		return (FALSE);
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	/* case 2: new module lies completely *after* the old one */
10370Sstevel@tonic-gate 	if (new->startaddr >= old->load_end && new->endaddr >= old->load_end)
10380Sstevel@tonic-gate 		return (FALSE);
10390Sstevel@tonic-gate 
10400Sstevel@tonic-gate 	/* probably a dlopen: the modules overlap each other */
10410Sstevel@tonic-gate 	return (TRUE);
10420Sstevel@tonic-gate }
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate static bool
is_same_as_aout(char * modpath,struct stat * buf)10450Sstevel@tonic-gate is_same_as_aout(char *modpath, struct stat *buf)
10460Sstevel@tonic-gate {
10470Sstevel@tonic-gate 	if (stat(modpath, buf) == -1) {
1048211Smike_s 		(void) fprintf(stderr, "%s: can't get info on `%s'\n",
1049*6951Sab196087 		    whoami, modpath);
10500Sstevel@tonic-gate 		exit(EX_NOINPUT);
10510Sstevel@tonic-gate 	}
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate 	if ((buf->st_dev == aout_info.dev) && (buf->st_ino == aout_info.ino))
10540Sstevel@tonic-gate 		return (TRUE);
10550Sstevel@tonic-gate 	else
10560Sstevel@tonic-gate 		return (FALSE);
10570Sstevel@tonic-gate }
10580Sstevel@tonic-gate 
10590Sstevel@tonic-gate static void
process_modules(ProfModuleList * modlp)1060211Smike_s process_modules(ProfModuleList *modlp)
10610Sstevel@tonic-gate {
10620Sstevel@tonic-gate 	ProfModule	*newmodp;
10630Sstevel@tonic-gate 	mod_info_t	*mi, *last, *new_module;
1064211Smike_s 	char		*so_path;
10650Sstevel@tonic-gate 	bool		more_modules = TRUE;
10660Sstevel@tonic-gate 	struct stat	so_statbuf;
10670Sstevel@tonic-gate 
10680Sstevel@tonic-gate #ifdef DEBUG
10690Sstevel@tonic-gate 	if (debug & MODULEDEBUG) {
1070211Smike_s 		(void) printf("[process_modules] module obj version %u\n",
1071*6951Sab196087 		    modlp->version);
10720Sstevel@tonic-gate 	}
1073211Smike_s #endif /* DEBUG */
10740Sstevel@tonic-gate 
10750Sstevel@tonic-gate 	/* Check version of module type object */
10760Sstevel@tonic-gate 	if (modlp->version > PROF_MODULES_VER) {
1077211Smike_s 		(void) fprintf(stderr, "%s: version %d for module type objects"
1078*6951Sab196087 		    "is not supported\n", whoami, modlp->version);
10790Sstevel@tonic-gate 		exit(EX_SOFTWARE);
10800Sstevel@tonic-gate 	}
10810Sstevel@tonic-gate 
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate 	/*
10840Sstevel@tonic-gate 	 * Scan the PROF_MODULES_T list and add modules to current list
10850Sstevel@tonic-gate 	 * of modules, if they're not present already
10860Sstevel@tonic-gate 	 */
1087211Smike_s 	/* LINTED: pointer cast */
1088211Smike_s 	newmodp = (ProfModule *)((char *)modlp + modlp->modules);
10890Sstevel@tonic-gate 	do {
10900Sstevel@tonic-gate 		/*
10910Sstevel@tonic-gate 		 * Since the prog could've been renamed after its run, we
10920Sstevel@tonic-gate 		 * should see if this overlaps a.out. If it does, it is
10930Sstevel@tonic-gate 		 * probably the renamed aout. We should also skip any other
10940Sstevel@tonic-gate 		 * non-sharedobj's that we see (or should we report an error ?)
10950Sstevel@tonic-gate 		 */
1096211Smike_s 		so_path = (caddr_t)modlp + newmodp->path;
10970Sstevel@tonic-gate 		if (does_overlap(newmodp, &modules) ||
1098*6951Sab196087 		    is_same_as_aout(so_path, &so_statbuf) ||
1099*6951Sab196087 		    (!is_shared_obj(so_path))) {
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate 			if (!newmodp->next)
11020Sstevel@tonic-gate 				more_modules = FALSE;
11030Sstevel@tonic-gate 
1104211Smike_s 			/* LINTED: pointer cast */
11050Sstevel@tonic-gate 			newmodp = (ProfModule *)
1106211Smike_s 			    ((caddr_t)modlp + newmodp->next);
11070Sstevel@tonic-gate #ifdef DEBUG
11080Sstevel@tonic-gate 			if (debug & MODULEDEBUG) {
1109211Smike_s 				(void) printf(
1110211Smike_s 				    "[process_modules] `%s'\n", so_path);
1111211Smike_s 				(void) printf("                  skipped\n");
11120Sstevel@tonic-gate 			}
1113211Smike_s #endif /* DEBUG */
11140Sstevel@tonic-gate 			continue;
11150Sstevel@tonic-gate 		}
11160Sstevel@tonic-gate #ifdef DEBUG
11170Sstevel@tonic-gate 		if (debug & MODULEDEBUG)
1118211Smike_s 			(void) printf("[process_modules] `%s'...\n", so_path);
1119211Smike_s #endif /* DEBUG */
11200Sstevel@tonic-gate 
11210Sstevel@tonic-gate 		/*
11220Sstevel@tonic-gate 		 * Check all modules (leave the first one, 'cos that
11230Sstevel@tonic-gate 		 * is the program executable info). If this module is already
11240Sstevel@tonic-gate 		 * there in the list, update the load addresses and proceed.
11250Sstevel@tonic-gate 		 */
11260Sstevel@tonic-gate 		last = &modules;
1127211Smike_s 		while ((mi = last->next) != NULL) {
11280Sstevel@tonic-gate 			/*
11290Sstevel@tonic-gate 			 * We expect the full pathname for all shared objects
11300Sstevel@tonic-gate 			 * needed by the program executable. In this case, we
11310Sstevel@tonic-gate 			 * simply need to compare the paths to see if they are
11320Sstevel@tonic-gate 			 * the same file.
11330Sstevel@tonic-gate 			 */
11340Sstevel@tonic-gate 			if (strcmp(mi->name, so_path) == 0)
11350Sstevel@tonic-gate 				break;
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate 			/*
11380Sstevel@tonic-gate 			 * Check if this new shared object will overlap
11390Sstevel@tonic-gate 			 * any existing module. If yes, remove the old one
11400Sstevel@tonic-gate 			 * from the linked list (but don't free it, 'cos
11410Sstevel@tonic-gate 			 * there may be symbols referring to this module
11420Sstevel@tonic-gate 			 * still)
11430Sstevel@tonic-gate 			 */
11440Sstevel@tonic-gate 			if (does_overlap(newmodp, mi)) {
11450Sstevel@tonic-gate #ifdef DEBUG
11460Sstevel@tonic-gate 				if (debug & MODULEDEBUG) {
1147211Smike_s 					(void) printf(
1148211Smike_s 					    "[process_modules] `%s'\n",
1149211Smike_s 					    so_path);
1150211Smike_s 					(void) printf(
1151211Smike_s 					    "                  overlaps\n");
1152211Smike_s 					(void) printf(
1153211Smike_s 					    "                  `%s'\n",
1154211Smike_s 					    mi->name);
11550Sstevel@tonic-gate 				}
1156211Smike_s #endif /* DEBUG */
11570Sstevel@tonic-gate 				mi->active = FALSE;
11580Sstevel@tonic-gate 			}
11590Sstevel@tonic-gate 
11600Sstevel@tonic-gate 			last = mi;
11610Sstevel@tonic-gate 		}
11620Sstevel@tonic-gate 
11630Sstevel@tonic-gate 		/* Module already there, skip it */
11640Sstevel@tonic-gate 		if (mi != NULL) {
11650Sstevel@tonic-gate 			mi->load_base = newmodp->startaddr;
11660Sstevel@tonic-gate 			mi->load_end = newmodp->endaddr;
11670Sstevel@tonic-gate 			mi->active = TRUE;
11680Sstevel@tonic-gate 			if (!newmodp->next)
11690Sstevel@tonic-gate 				more_modules = FALSE;
11700Sstevel@tonic-gate 
1171211Smike_s 			/* LINTED: pointer cast */
11720Sstevel@tonic-gate 			newmodp = (ProfModule *)
1173211Smike_s 			    ((caddr_t)modlp + newmodp->next);
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate #ifdef DEBUG
11760Sstevel@tonic-gate 			if (debug & MODULEDEBUG) {
1177211Smike_s 				(void) printf("[process_modules] base=%#llx, "
1178*6951Sab196087 				    "end=%#llx\n", mi->load_base, mi->load_end);
11790Sstevel@tonic-gate 			}
1180211Smike_s #endif /* DEBUG */
11810Sstevel@tonic-gate 			continue;
11820Sstevel@tonic-gate 		}
11830Sstevel@tonic-gate 
11840Sstevel@tonic-gate 		/*
11850Sstevel@tonic-gate 		 * Check if gmon.out is outdated with respect to the new
11860Sstevel@tonic-gate 		 * module we want to add
11870Sstevel@tonic-gate 		 */
11880Sstevel@tonic-gate 		if (gmonout_info.mtime < so_statbuf.st_mtime) {
1189211Smike_s 			(void) fprintf(stderr,
1190211Smike_s 			    "%s: shared obj outdates prof info\n", whoami);
1191211Smike_s 			(void) fprintf(stderr, "\t(newer %s)\n", so_path);
11920Sstevel@tonic-gate 			exit(EX_NOINPUT);
11930Sstevel@tonic-gate 		}
11940Sstevel@tonic-gate 
11950Sstevel@tonic-gate 		/* Create a new module element */
1196211Smike_s 		new_module = malloc(sizeof (mod_info_t));
11970Sstevel@tonic-gate 		if (new_module == NULL) {
1198211Smike_s 			(void) fprintf(stderr, "%s: no room for %d bytes\n",
1199*6951Sab196087 			    whoami, sizeof (mod_info_t));
12000Sstevel@tonic-gate 			exit(EX_OSERR);
12010Sstevel@tonic-gate 		}
12020Sstevel@tonic-gate 
12030Sstevel@tonic-gate 		/* and fill in info... */
12040Sstevel@tonic-gate 		new_module->id = n_modules + 1;
12050Sstevel@tonic-gate 		new_module->load_base = newmodp->startaddr;
12060Sstevel@tonic-gate 		new_module->load_end = newmodp->endaddr;
1207211Smike_s 		new_module->name = malloc(strlen(so_path) + 1);
12080Sstevel@tonic-gate 		if (new_module->name == NULL) {
1209211Smike_s 			(void) fprintf(stderr, "%s: no room for %d bytes\n",
1210*6951Sab196087 			    whoami, strlen(so_path) + 1);
12110Sstevel@tonic-gate 			exit(EX_OSERR);
12120Sstevel@tonic-gate 		}
1213211Smike_s 		(void) strcpy(new_module->name, so_path);
12140Sstevel@tonic-gate #ifdef DEBUG
12150Sstevel@tonic-gate 		if (debug & MODULEDEBUG) {
1216211Smike_s 			(void) printf(
1217211Smike_s 			    "[process_modules] base=%#llx, end=%#llx\n",
1218211Smike_s 			    new_module->load_base, new_module->load_end);
12190Sstevel@tonic-gate 		}
1220211Smike_s #endif /* DEBUG */
12210Sstevel@tonic-gate 
12220Sstevel@tonic-gate 		/* Create this module's nameslist */
12230Sstevel@tonic-gate 		process_namelist(new_module);
12240Sstevel@tonic-gate 
12250Sstevel@tonic-gate 		/* Add it to the tail of active module list */
12260Sstevel@tonic-gate 		last->next = new_module;
12270Sstevel@tonic-gate 		n_modules++;
12280Sstevel@tonic-gate 
12290Sstevel@tonic-gate #ifdef DEBUG
12300Sstevel@tonic-gate 		if (debug & MODULEDEBUG) {
1231211Smike_s 			(void) printf(
1232211Smike_s 			    "[process_modules] total shared objects = %ld\n",
1233211Smike_s 			    n_modules - 1);
12340Sstevel@tonic-gate 		}
1235211Smike_s #endif /* DEBUG */
12360Sstevel@tonic-gate 		/*
12370Sstevel@tonic-gate 		 * Move to the next module in the PROF_MODULES_T list
12380Sstevel@tonic-gate 		 * (if present)
12390Sstevel@tonic-gate 		 */
12400Sstevel@tonic-gate 		if (!newmodp->next)
12410Sstevel@tonic-gate 			more_modules = FALSE;
12420Sstevel@tonic-gate 
1243211Smike_s 		/* LINTED: pointer cast */
1244211Smike_s 		newmodp = (ProfModule *)((caddr_t)modlp + newmodp->next);
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 	} while (more_modules);
12470Sstevel@tonic-gate }
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate static void
reset_active_modules(void)1250211Smike_s reset_active_modules(void)
12510Sstevel@tonic-gate {
12520Sstevel@tonic-gate 	mod_info_t	*mi;
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 	/* Except the executable, no other module should remain active */
12550Sstevel@tonic-gate 	for (mi = modules.next; mi; mi = mi->next)
12560Sstevel@tonic-gate 		mi->active = FALSE;
12570Sstevel@tonic-gate }
12580Sstevel@tonic-gate 
12590Sstevel@tonic-gate static void
getpfiledata(caddr_t memp,size_t fsz)1260211Smike_s getpfiledata(caddr_t memp, size_t fsz)
12610Sstevel@tonic-gate {
12620Sstevel@tonic-gate 	ProfObject	*objp;
12630Sstevel@tonic-gate 	caddr_t		file_end;
12640Sstevel@tonic-gate 	bool		found_pcsamples = FALSE, found_cgraph = FALSE;
12650Sstevel@tonic-gate 
12660Sstevel@tonic-gate 	/*
12670Sstevel@tonic-gate 	 * Before processing a new gmon.out, all modules except the
12680Sstevel@tonic-gate 	 * program executable must be made inactive, so that symbols
12690Sstevel@tonic-gate 	 * are searched only in the program executable, if we don't
12700Sstevel@tonic-gate 	 * find a MODULES_T object. Don't do it *after* we read a gmon.out,
12710Sstevel@tonic-gate 	 * because we need the active module data after we're done with
12720Sstevel@tonic-gate 	 * the last gmon.out, if we're doing summing.
12730Sstevel@tonic-gate 	 */
12740Sstevel@tonic-gate 	reset_active_modules();
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate 	file_end = memp + fsz;
1277211Smike_s 	/* LINTED: pointer cast */
1278211Smike_s 	objp = (ProfObject *)(memp + ((ProfHeader *)memp)->size);
1279211Smike_s 	while ((caddr_t)objp < file_end) {
12800Sstevel@tonic-gate #ifdef DEBUG
12810Sstevel@tonic-gate 		{
12820Sstevel@tonic-gate 			unsigned int	type = 0;
12830Sstevel@tonic-gate 
12840Sstevel@tonic-gate 			if (debug & MONOUTDEBUG) {
12850Sstevel@tonic-gate 				if (objp->type <= MAX_OBJTYPES)
12860Sstevel@tonic-gate 					type = objp->type;
12870Sstevel@tonic-gate 
1288211Smike_s 				(void) printf(
1289211Smike_s 				    "\n[getpfiledata] object %s [%#lx]\n",
1290*6951Sab196087 				    objname[type], objp->type);
12910Sstevel@tonic-gate 			}
12920Sstevel@tonic-gate 		}
1293211Smike_s #endif /* DEBUG */
12940Sstevel@tonic-gate 		switch (objp->type) {
12950Sstevel@tonic-gate 			case PROF_MODULES_T :
12960Sstevel@tonic-gate 				process_modules((ProfModuleList *) objp);
12970Sstevel@tonic-gate 				break;
12980Sstevel@tonic-gate 
12990Sstevel@tonic-gate 			case PROF_CALLGRAPH_T :
13000Sstevel@tonic-gate 				process_cgraph((ProfCallGraph *) objp);
13010Sstevel@tonic-gate 				found_cgraph = TRUE;
13020Sstevel@tonic-gate 				break;
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate 			case PROF_BUFFER_T :
13050Sstevel@tonic-gate 				process_pcsamples((ProfBuffer *) objp);
13060Sstevel@tonic-gate 				found_pcsamples = TRUE;
13070Sstevel@tonic-gate 				break;
13080Sstevel@tonic-gate 
13090Sstevel@tonic-gate 			default :
1310211Smike_s 				(void) fprintf(stderr,
1311*6951Sab196087 				    "%s: unknown prof object type=%d\n",
1312*6951Sab196087 				    whoami, objp->type);
13130Sstevel@tonic-gate 				exit(EX_SOFTWARE);
13140Sstevel@tonic-gate 		}
1315211Smike_s 		/* LINTED: pointer cast */
1316211Smike_s 		objp = (ProfObject *)((caddr_t)objp + objp->size);
13170Sstevel@tonic-gate 	}
13180Sstevel@tonic-gate 
13190Sstevel@tonic-gate 	if (!found_cgraph || !found_pcsamples) {
1320211Smike_s 		(void) fprintf(stderr,
1321*6951Sab196087 		    "%s: missing callgraph/pcsamples object\n", whoami);
13220Sstevel@tonic-gate 		exit(EX_SOFTWARE);
13230Sstevel@tonic-gate 	}
13240Sstevel@tonic-gate 
1325211Smike_s 	if ((caddr_t)objp > file_end) {
1326211Smike_s 		(void) fprintf(stderr, "%s: malformed profile file.\n", whoami);
13270Sstevel@tonic-gate 		exit(EX_SOFTWARE);
13280Sstevel@tonic-gate 	}
13290Sstevel@tonic-gate 
13300Sstevel@tonic-gate 	if (first_file)
13310Sstevel@tonic-gate 		first_file = FALSE;
13320Sstevel@tonic-gate }
13330Sstevel@tonic-gate 
13340Sstevel@tonic-gate static void
readarcs(FILE * pfile)1335211Smike_s readarcs(FILE *pfile)
13360Sstevel@tonic-gate {
13370Sstevel@tonic-gate 	/*
13380Sstevel@tonic-gate 	 *	the rest of the file consists of
13390Sstevel@tonic-gate 	 *	a bunch of <from,self,count> tuples.
13400Sstevel@tonic-gate 	 */
13410Sstevel@tonic-gate 	/* CONSTCOND */
13420Sstevel@tonic-gate 	while (1) {
13430Sstevel@tonic-gate 		struct rawarc	arc;
13440Sstevel@tonic-gate 
13450Sstevel@tonic-gate 		if (rflag) {
13460Sstevel@tonic-gate 			if (Bflag) {
13470Sstevel@tonic-gate 				L_cgarc64		rtld_arc64;
13480Sstevel@tonic-gate 
13490Sstevel@tonic-gate 				/*
13500Sstevel@tonic-gate 				 * If rflag is set then this is an profiled
13510Sstevel@tonic-gate 				 * image generated by rtld.  It needs to be
13520Sstevel@tonic-gate 				 * 'converted' to the standard data format.
13530Sstevel@tonic-gate 				 */
13540Sstevel@tonic-gate 				if (fread(&rtld_arc64,
1355*6951Sab196087 				    sizeof (L_cgarc64), 1, pfile) != 1)
13560Sstevel@tonic-gate 					break;
13570Sstevel@tonic-gate 
13580Sstevel@tonic-gate 				if (rtld_arc64.cg_from == PRF_OUTADDR64)
13590Sstevel@tonic-gate 					arc.raw_frompc = s_highpc + 0x10;
13600Sstevel@tonic-gate 				else
13610Sstevel@tonic-gate 					arc.raw_frompc =
13620Sstevel@tonic-gate 					    (pctype)rtld_arc64.cg_from;
13630Sstevel@tonic-gate 				arc.raw_selfpc = (pctype)rtld_arc64.cg_to;
13640Sstevel@tonic-gate 				arc.raw_count = (actype)rtld_arc64.cg_count;
13650Sstevel@tonic-gate 			} else {
13660Sstevel@tonic-gate 				L_cgarc		rtld_arc;
13670Sstevel@tonic-gate 
13680Sstevel@tonic-gate 				/*
13690Sstevel@tonic-gate 				 * If rflag is set then this is an profiled
13700Sstevel@tonic-gate 				 * image generated by rtld.  It needs to be
13710Sstevel@tonic-gate 				 * 'converted' to the standard data format.
13720Sstevel@tonic-gate 				 */
13730Sstevel@tonic-gate 				if (fread(&rtld_arc,
1374*6951Sab196087 				    sizeof (L_cgarc), 1, pfile) != 1)
13750Sstevel@tonic-gate 					break;
13760Sstevel@tonic-gate 
13770Sstevel@tonic-gate 				if (rtld_arc.cg_from == PRF_OUTADDR)
13780Sstevel@tonic-gate 					arc.raw_frompc = s_highpc + 0x10;
13790Sstevel@tonic-gate 				else
13800Sstevel@tonic-gate 					arc.raw_frompc = (pctype)
13810Sstevel@tonic-gate 					    (uintptr_t)rtld_arc.cg_from;
13820Sstevel@tonic-gate 				arc.raw_selfpc = (pctype)
13830Sstevel@tonic-gate 				    (uintptr_t)rtld_arc.cg_to;
13840Sstevel@tonic-gate 				arc.raw_count = (actype)rtld_arc.cg_count;
13850Sstevel@tonic-gate 			}
13860Sstevel@tonic-gate 		} else {
13870Sstevel@tonic-gate 			if (Bflag) {
13880Sstevel@tonic-gate 				if (fread(&arc, sizeof (struct rawarc), 1,
13890Sstevel@tonic-gate 				    pfile) != 1) {
13900Sstevel@tonic-gate 					break;
13910Sstevel@tonic-gate 				}
13920Sstevel@tonic-gate 			} else {
13930Sstevel@tonic-gate 				/*
13940Sstevel@tonic-gate 				 * If these aren't big %pc's, we need to read
13950Sstevel@tonic-gate 				 * into the 32-bit raw arc structure, and
13960Sstevel@tonic-gate 				 * assign the members into the actual arc.
13970Sstevel@tonic-gate 				 */
13980Sstevel@tonic-gate 				struct rawarc32 arc32;
13990Sstevel@tonic-gate 				if (fread(&arc32, sizeof (struct rawarc32),
14000Sstevel@tonic-gate 				    1, pfile) != 1)
14010Sstevel@tonic-gate 					break;
14020Sstevel@tonic-gate 				arc.raw_frompc = (pctype)arc32.raw_frompc;
14030Sstevel@tonic-gate 				arc.raw_selfpc = (pctype)arc32.raw_selfpc;
14040Sstevel@tonic-gate 				arc.raw_count  = (actype)arc32.raw_count;
14050Sstevel@tonic-gate 			}
14060Sstevel@tonic-gate 		}
14070Sstevel@tonic-gate 
14080Sstevel@tonic-gate #ifdef DEBUG
14090Sstevel@tonic-gate 		if (debug & SAMPLEDEBUG) {
1410211Smike_s 			(void) printf("[getpfile] frompc 0x%llx selfpc "
14110Sstevel@tonic-gate 			    "0x%llx count %lld\n", arc.raw_frompc,
14120Sstevel@tonic-gate 			    arc.raw_selfpc, arc.raw_count);
14130Sstevel@tonic-gate 		}
1414211Smike_s #endif /* DEBUG */
14150Sstevel@tonic-gate 		/*
14160Sstevel@tonic-gate 		 *	add this arc
14170Sstevel@tonic-gate 		 */
14180Sstevel@tonic-gate 		tally(&modules, &modules, &arc);
14190Sstevel@tonic-gate 	}
14200Sstevel@tonic-gate 	if (first_file)
14210Sstevel@tonic-gate 		first_file = FALSE;
14220Sstevel@tonic-gate }
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate static void
readsamples(FILE * pfile)14250Sstevel@tonic-gate readsamples(FILE *pfile)
14260Sstevel@tonic-gate {
14270Sstevel@tonic-gate 	sztype		i;
14280Sstevel@tonic-gate 	unsigned_UNIT	sample;
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate 	if (samples == 0) {
14310Sstevel@tonic-gate 		samples = (unsigned_UNIT *) calloc(nsamples,
14320Sstevel@tonic-gate 		    sizeof (unsigned_UNIT));
14330Sstevel@tonic-gate 		if (samples == 0) {
1434211Smike_s 			(void) fprintf(stderr,
1435211Smike_s 			    "%s: No room for %d sample pc's\n",
14360Sstevel@tonic-gate 			    whoami, sampbytes / sizeof (unsigned_UNIT));
14370Sstevel@tonic-gate 			exit(EX_OSERR);
14380Sstevel@tonic-gate 		}
14390Sstevel@tonic-gate 	}
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 	for (i = 0; i < nsamples; i++) {
1442211Smike_s 		(void) fread(&sample, sizeof (unsigned_UNIT), 1, pfile);
14430Sstevel@tonic-gate 		if (feof(pfile))
14440Sstevel@tonic-gate 			break;
14450Sstevel@tonic-gate 		samples[i] += sample;
14460Sstevel@tonic-gate 	}
14470Sstevel@tonic-gate 	if (i != nsamples) {
1448211Smike_s 		(void) fprintf(stderr,
1449211Smike_s 		    "%s: unexpected EOF after reading %d/%d samples\n",
14500Sstevel@tonic-gate 		    whoami, --i, nsamples);
14510Sstevel@tonic-gate 		exit(EX_IOERR);
14520Sstevel@tonic-gate 	}
14530Sstevel@tonic-gate }
14540Sstevel@tonic-gate 
14550Sstevel@tonic-gate static void *
handle_versioned(FILE * pfile,char * filename,size_t * fsz)14560Sstevel@tonic-gate handle_versioned(FILE *pfile, char *filename, size_t *fsz)
14570Sstevel@tonic-gate {
14580Sstevel@tonic-gate 	int		fd;
14590Sstevel@tonic-gate 	bool		invalid_version;
14600Sstevel@tonic-gate 	caddr_t		fmem;
14610Sstevel@tonic-gate 	struct stat	buf;
14620Sstevel@tonic-gate 	ProfHeader	prof_hdr;
1463211Smike_s 	off_t		lret;
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate 	/*
14660Sstevel@tonic-gate 	 * Check versioning info. For now, let's say we provide
14670Sstevel@tonic-gate 	 * backward compatibility, so we accept all older versions.
14680Sstevel@tonic-gate 	 */
14690Sstevel@tonic-gate 	if (fread(&prof_hdr, sizeof (ProfHeader), 1, pfile) == 0) {
14700Sstevel@tonic-gate 		perror("fread()");
14710Sstevel@tonic-gate 		exit(EX_IOERR);
14720Sstevel@tonic-gate 	}
14730Sstevel@tonic-gate 
14740Sstevel@tonic-gate 	invalid_version = FALSE;
14750Sstevel@tonic-gate 	if (prof_hdr.h_major_ver > PROF_MAJOR_VERSION)
14760Sstevel@tonic-gate 		invalid_version = TRUE;
14770Sstevel@tonic-gate 	else if (prof_hdr.h_major_ver == PROF_MAJOR_VERSION) {
14780Sstevel@tonic-gate 		if (prof_hdr.h_minor_ver > PROF_MINOR_VERSION)
14790Sstevel@tonic-gate 			invalid_version = FALSE;
14800Sstevel@tonic-gate 	}
14810Sstevel@tonic-gate 
14820Sstevel@tonic-gate 	if (invalid_version) {
1483211Smike_s 		(void) fprintf(stderr, "%s: version %d.%d not supported\n",
1484*6951Sab196087 		    whoami, prof_hdr.h_major_ver, prof_hdr.h_minor_ver);
14850Sstevel@tonic-gate 		exit(EX_SOFTWARE);
14860Sstevel@tonic-gate 	}
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate 	/*
14890Sstevel@tonic-gate 	 * Map gmon.out onto memory.
14900Sstevel@tonic-gate 	 */
1491211Smike_s 	(void) fclose(pfile);
14920Sstevel@tonic-gate 	if ((fd = open(filename, O_RDONLY)) == -1) {
14930Sstevel@tonic-gate 		perror(filename);
14940Sstevel@tonic-gate 		exit(EX_IOERR);
14950Sstevel@tonic-gate 	}
14960Sstevel@tonic-gate 
1497211Smike_s 	if ((lret = lseek(fd, 0, SEEK_END)) == -1) {
14980Sstevel@tonic-gate 		perror(filename);
14990Sstevel@tonic-gate 		exit(EX_IOERR);
15000Sstevel@tonic-gate 	}
1501211Smike_s 	*fsz = lret;
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate 	fmem = mmap(0, *fsz, PROT_READ, MAP_PRIVATE, fd, 0);
15040Sstevel@tonic-gate 	if (fmem == MAP_FAILED) {
1505*6951Sab196087 		(void) fprintf(stderr, "%s: can't map %s\n", whoami, filename);
1506*6951Sab196087 		exit(EX_IOERR);
15070Sstevel@tonic-gate 	}
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate 	/*
15100Sstevel@tonic-gate 	 * Before we close this fd, save this gmon.out's info to later verify
15110Sstevel@tonic-gate 	 * if the shared objects it references have changed since the time
15120Sstevel@tonic-gate 	 * they were used to generate this gmon.out
15130Sstevel@tonic-gate 	 */
15140Sstevel@tonic-gate 	if (fstat(fd, &buf) == -1) {
1515211Smike_s 		(void) fprintf(stderr, "%s: can't get info on `%s'\n",
1516*6951Sab196087 		    whoami, filename);
15170Sstevel@tonic-gate 		exit(EX_NOINPUT);
15180Sstevel@tonic-gate 	}
15190Sstevel@tonic-gate 	gmonout_info.dev = buf.st_dev;
15200Sstevel@tonic-gate 	gmonout_info.ino = buf.st_ino;
15210Sstevel@tonic-gate 	gmonout_info.mtime = buf.st_mtime;
15220Sstevel@tonic-gate 	gmonout_info.size = buf.st_size;
15230Sstevel@tonic-gate 
1524211Smike_s 	(void) close(fd);
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate 	return ((void *) fmem);
15270Sstevel@tonic-gate }
15280Sstevel@tonic-gate 
15290Sstevel@tonic-gate static void *
openpfile(char * filename,size_t * fsz)1530211Smike_s openpfile(char *filename, size_t *fsz)
15310Sstevel@tonic-gate {
15320Sstevel@tonic-gate 	struct hdr	tmp;
1533211Smike_s 	FILE		*pfile;
15340Sstevel@tonic-gate 	unsigned long	magic_num;
15351319Sab196087 	size_t		hdrsize;
15360Sstevel@tonic-gate 	static bool	first_time = TRUE;
15370Sstevel@tonic-gate 	extern bool	old_style;
15380Sstevel@tonic-gate 
15390Sstevel@tonic-gate 	if ((pfile = fopen(filename, "r")) == NULL) {
15400Sstevel@tonic-gate 		perror(filename);
15410Sstevel@tonic-gate 		exit(EX_IOERR);
15420Sstevel@tonic-gate 	}
15430Sstevel@tonic-gate 
15440Sstevel@tonic-gate 	/*
15450Sstevel@tonic-gate 	 * Read in the magic. Note that we changed the cast "unsigned long"
15460Sstevel@tonic-gate 	 * to "unsigned int" because that's how h_magic is defined in the
15470Sstevel@tonic-gate 	 * new format ProfHeader.
15480Sstevel@tonic-gate 	 */
15490Sstevel@tonic-gate 	if (fread(&magic_num, sizeof (unsigned int), 1, pfile) == 0) {
15500Sstevel@tonic-gate 		perror("fread()");
15510Sstevel@tonic-gate 		exit(EX_IOERR);
15520Sstevel@tonic-gate 	}
15530Sstevel@tonic-gate 
15540Sstevel@tonic-gate 	rewind(pfile);
15550Sstevel@tonic-gate 
15560Sstevel@tonic-gate 	/*
15570Sstevel@tonic-gate 	 * First check if this is versioned or *old-style* gmon.out
15580Sstevel@tonic-gate 	 */
15590Sstevel@tonic-gate 	if (magic_num == (unsigned int)PROF_MAGIC) {
15600Sstevel@tonic-gate 		if ((!first_time) && (old_style == TRUE)) {
1561211Smike_s 			(void) fprintf(stderr, "%s: can't mix old & new format "
1562*6951Sab196087 			    "profiled files\n", whoami);
15630Sstevel@tonic-gate 			exit(EX_SOFTWARE);
15640Sstevel@tonic-gate 		}
15650Sstevel@tonic-gate 		first_time = FALSE;
15660Sstevel@tonic-gate 		old_style = FALSE;
15670Sstevel@tonic-gate 		return (handle_versioned(pfile, filename, fsz));
15680Sstevel@tonic-gate 	}
15690Sstevel@tonic-gate 
15700Sstevel@tonic-gate 	if ((!first_time) && (old_style == FALSE)) {
1571211Smike_s 		(void) fprintf(stderr, "%s: can't mix old & new format "
1572*6951Sab196087 		    "profiled files\n", whoami);
15730Sstevel@tonic-gate 		exit(EX_SOFTWARE);
15740Sstevel@tonic-gate 	}
15750Sstevel@tonic-gate 
15760Sstevel@tonic-gate 	first_time = FALSE;
15770Sstevel@tonic-gate 	old_style = TRUE;
15780Sstevel@tonic-gate 	fsz = 0;
15790Sstevel@tonic-gate 
15800Sstevel@tonic-gate 	/*
15810Sstevel@tonic-gate 	 * Now, we need to determine if this is a run-time linker
15820Sstevel@tonic-gate 	 * profiled file or if it is a standard gmon.out.
15830Sstevel@tonic-gate 	 *
15840Sstevel@tonic-gate 	 * We do this by checking if magic matches PRF_MAGIC. If it
15850Sstevel@tonic-gate 	 * does, then this is a run-time linker profiled file, if it
15860Sstevel@tonic-gate 	 * doesn't, it must be a gmon.out file.
15870Sstevel@tonic-gate 	 */
15880Sstevel@tonic-gate 	if (magic_num == (unsigned long)PRF_MAGIC)
15890Sstevel@tonic-gate 		rflag = TRUE;
15900Sstevel@tonic-gate 	else
15910Sstevel@tonic-gate 		rflag = FALSE;
15920Sstevel@tonic-gate 
15931319Sab196087 	hdrsize = Bflag ? sizeof (struct hdr) : sizeof (struct hdr32);
15941319Sab196087 
15950Sstevel@tonic-gate 	if (rflag) {
15960Sstevel@tonic-gate 		if (Bflag) {
15970Sstevel@tonic-gate 			L_hdr64		l_hdr64;
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate 			/*
16000Sstevel@tonic-gate 			 * If the rflag is set then the input file is
16010Sstevel@tonic-gate 			 * rtld profiled data, we'll read it in and convert
16020Sstevel@tonic-gate 			 * it to the standard format (ie: make it look like
16030Sstevel@tonic-gate 			 * a gmon.out file).
16040Sstevel@tonic-gate 			 */
16050Sstevel@tonic-gate 			if (fread(&l_hdr64, sizeof (L_hdr64), 1, pfile) == 0) {
16060Sstevel@tonic-gate 				perror("fread()");
16070Sstevel@tonic-gate 				exit(EX_IOERR);
16080Sstevel@tonic-gate 			}
16090Sstevel@tonic-gate 			if (l_hdr64.hd_version != PRF_VERSION_64) {
1610211Smike_s 				(void) fprintf(stderr,
1611211Smike_s 				    "%s: expected version %d, "
16120Sstevel@tonic-gate 				    "got version %d when processing 64-bit "
16130Sstevel@tonic-gate 				    "run-time linker profiled file.\n",
16140Sstevel@tonic-gate 				    whoami, PRF_VERSION_64, l_hdr64.hd_version);
16150Sstevel@tonic-gate 				exit(EX_SOFTWARE);
16160Sstevel@tonic-gate 			}
16170Sstevel@tonic-gate 			tmp.lowpc = 0;
16180Sstevel@tonic-gate 			tmp.highpc = (pctype)l_hdr64.hd_hpc;
16191319Sab196087 			tmp.ncnt = hdrsize + l_hdr64.hd_psize;
16200Sstevel@tonic-gate 		} else {
16210Sstevel@tonic-gate 			L_hdr		l_hdr;
16220Sstevel@tonic-gate 
16230Sstevel@tonic-gate 			/*
16240Sstevel@tonic-gate 			 * If the rflag is set then the input file is
16250Sstevel@tonic-gate 			 * rtld profiled data, we'll read it in and convert
16260Sstevel@tonic-gate 			 * it to the standard format (ie: make it look like
16270Sstevel@tonic-gate 			 * a gmon.out file).
16280Sstevel@tonic-gate 			 */
16290Sstevel@tonic-gate 			if (fread(&l_hdr, sizeof (L_hdr), 1, pfile) == 0) {
16300Sstevel@tonic-gate 				perror("fread()");
16310Sstevel@tonic-gate 				exit(EX_IOERR);
16320Sstevel@tonic-gate 			}
16330Sstevel@tonic-gate 			if (l_hdr.hd_version != PRF_VERSION) {
1634211Smike_s 				(void) fprintf(stderr,
1635211Smike_s 				    "%s: expected version %d, "
16360Sstevel@tonic-gate 				    "got version %d when processing "
16370Sstevel@tonic-gate 				    "run-time linker profiled file.\n",
16380Sstevel@tonic-gate 				    whoami, PRF_VERSION, l_hdr.hd_version);
16390Sstevel@tonic-gate 				exit(EX_SOFTWARE);
16400Sstevel@tonic-gate 			}
16410Sstevel@tonic-gate 			tmp.lowpc = 0;
16420Sstevel@tonic-gate 			tmp.highpc = (pctype)(uintptr_t)l_hdr.hd_hpc;
16431319Sab196087 			tmp.ncnt = hdrsize + l_hdr.hd_psize;
16440Sstevel@tonic-gate 		}
16450Sstevel@tonic-gate 	} else {
16460Sstevel@tonic-gate 		if (Bflag) {
16470Sstevel@tonic-gate 			if (fread(&tmp, sizeof (struct hdr), 1, pfile) == 0) {
16480Sstevel@tonic-gate 				perror("fread()");
16490Sstevel@tonic-gate 				exit(EX_IOERR);
16500Sstevel@tonic-gate 			}
16510Sstevel@tonic-gate 		} else {
16520Sstevel@tonic-gate 			/*
16530Sstevel@tonic-gate 			 * If we're not reading big %pc's, we need to read
16540Sstevel@tonic-gate 			 * the 32-bit header, and assign the members to
16550Sstevel@tonic-gate 			 * the actual header.
16560Sstevel@tonic-gate 			 */
16570Sstevel@tonic-gate 			struct hdr32 hdr32;
16580Sstevel@tonic-gate 			if (fread(&hdr32, sizeof (hdr32), 1, pfile) == 0) {
16590Sstevel@tonic-gate 				perror("fread()");
16600Sstevel@tonic-gate 				exit(EX_IOERR);
16610Sstevel@tonic-gate 			}
16620Sstevel@tonic-gate 			tmp.lowpc = hdr32.lowpc;
16630Sstevel@tonic-gate 			tmp.highpc = hdr32.highpc;
16640Sstevel@tonic-gate 			tmp.ncnt = hdr32.ncnt;
16650Sstevel@tonic-gate 		}
16660Sstevel@tonic-gate 	}
16670Sstevel@tonic-gate 
16680Sstevel@tonic-gate 	/*
16690Sstevel@tonic-gate 	 * perform sanity check on profiled file we've opened.
16700Sstevel@tonic-gate 	 */
16710Sstevel@tonic-gate 	if (tmp.lowpc >= tmp.highpc) {
16720Sstevel@tonic-gate 		if (rflag)
1673211Smike_s 			(void) fprintf(stderr,
1674211Smike_s 			    "%s: badly formed profiled data.\n",
16750Sstevel@tonic-gate 			    filename);
16760Sstevel@tonic-gate 		else
1677211Smike_s 			(void) fprintf(stderr,
1678211Smike_s 			    "%s: badly formed gmon.out file.\n",
16790Sstevel@tonic-gate 			    filename);
16800Sstevel@tonic-gate 		exit(EX_SOFTWARE);
16810Sstevel@tonic-gate 	}
16820Sstevel@tonic-gate 
16830Sstevel@tonic-gate 	if (s_highpc != 0 && (tmp.lowpc != h.lowpc ||
16840Sstevel@tonic-gate 	    tmp.highpc != h.highpc || tmp.ncnt != h.ncnt)) {
1685211Smike_s 		(void) fprintf(stderr,
16860Sstevel@tonic-gate 		    "%s: incompatible with first gmon file\n",
16870Sstevel@tonic-gate 		    filename);
16880Sstevel@tonic-gate 		exit(EX_IOERR);
16890Sstevel@tonic-gate 	}
16900Sstevel@tonic-gate 	h = tmp;
16910Sstevel@tonic-gate 	s_lowpc = h.lowpc;
16920Sstevel@tonic-gate 	s_highpc = h.highpc;
16930Sstevel@tonic-gate 	lowpc = h.lowpc / sizeof (UNIT);
16940Sstevel@tonic-gate 	highpc = h.highpc / sizeof (UNIT);
16950Sstevel@tonic-gate 	sampbytes = h.ncnt > hdrsize ? h.ncnt - hdrsize : 0;
16960Sstevel@tonic-gate 	nsamples = sampbytes / sizeof (unsigned_UNIT);
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate #ifdef DEBUG
16990Sstevel@tonic-gate 	if (debug & SAMPLEDEBUG) {
1700211Smike_s 		(void) printf("[openpfile] hdr.lowpc 0x%llx hdr.highpc "
17010Sstevel@tonic-gate 		    "0x%llx hdr.ncnt %lld\n",
17020Sstevel@tonic-gate 		    h.lowpc, h.highpc, h.ncnt);
1703211Smike_s 		(void) printf(
1704211Smike_s 		    "[openpfile]   s_lowpc 0x%llx   s_highpc 0x%llx\n",
17050Sstevel@tonic-gate 		    s_lowpc, s_highpc);
1706211Smike_s 		(void) printf(
1707211Smike_s 		    "[openpfile]     lowpc 0x%llx     highpc 0x%llx\n",
17080Sstevel@tonic-gate 		    lowpc, highpc);
1709211Smike_s 		(void) printf("[openpfile] sampbytes %d nsamples %d\n",
17100Sstevel@tonic-gate 		    sampbytes, nsamples);
17110Sstevel@tonic-gate 	}
1712211Smike_s #endif /* DEBUG */
17130Sstevel@tonic-gate 
17140Sstevel@tonic-gate 	return ((void *) pfile);
17150Sstevel@tonic-gate }
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate /*
17180Sstevel@tonic-gate  * Information from a gmon.out file depends on whether it's versioned
17190Sstevel@tonic-gate  * or non-versioned, *old style* gmon.out. If old-style, it is in two
17200Sstevel@tonic-gate  * parts : an array of sampling hits within pc ranges, and the arcs. If
17210Sstevel@tonic-gate  * versioned, it contains a header, followed by any number of
17220Sstevel@tonic-gate  * modules/callgraph/pcsample_buffer objects.
17230Sstevel@tonic-gate  */
17240Sstevel@tonic-gate static void
getpfile(char * filename)17250Sstevel@tonic-gate getpfile(char *filename)
17260Sstevel@tonic-gate {
17270Sstevel@tonic-gate 	void		*handle;
17280Sstevel@tonic-gate 	size_t		fsz;
17290Sstevel@tonic-gate 
17300Sstevel@tonic-gate 	handle = openpfile(filename, &fsz);
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate 	if (old_style) {
1733211Smike_s 		readsamples((FILE *)handle);
1734211Smike_s 		readarcs((FILE *)handle);
1735211Smike_s 		(void) fclose((FILE *)handle);
17360Sstevel@tonic-gate 		return;
17370Sstevel@tonic-gate 	}
17380Sstevel@tonic-gate 
1739211Smike_s 	getpfiledata((caddr_t)handle, fsz);
1740211Smike_s 	(void) munmap(handle, fsz);
17410Sstevel@tonic-gate }
17420Sstevel@tonic-gate 
1743211Smike_s int
main(int argc,char ** argv)1744211Smike_s main(int argc, char **argv)
17450Sstevel@tonic-gate {
17460Sstevel@tonic-gate 	char	**sp;
17470Sstevel@tonic-gate 	nltype	**timesortnlp;
17480Sstevel@tonic-gate 	int		c;
17490Sstevel@tonic-gate 	int		errflg;
17500Sstevel@tonic-gate 
17510Sstevel@tonic-gate 	prog_name = *argv;  /* preserve program name */
17520Sstevel@tonic-gate 	debug = 0;
17530Sstevel@tonic-gate 	nflag = FALSE;
17540Sstevel@tonic-gate 	bflag = TRUE;
17550Sstevel@tonic-gate 	lflag = FALSE;
17560Sstevel@tonic-gate 	Cflag = FALSE;
17570Sstevel@tonic-gate 	first_file = TRUE;
17580Sstevel@tonic-gate 	rflag = FALSE;
17590Sstevel@tonic-gate 	Bflag = FALSE;
17600Sstevel@tonic-gate 	errflg = FALSE;
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "abd:CcDE:e:F:f:ln:sz")) != EOF)
17630Sstevel@tonic-gate 		switch (c) {
17640Sstevel@tonic-gate 		case 'a':
17650Sstevel@tonic-gate 			aflag = TRUE;
17660Sstevel@tonic-gate 			break;
17670Sstevel@tonic-gate 		case 'b':
17680Sstevel@tonic-gate 			bflag = FALSE;
17690Sstevel@tonic-gate 			break;
17700Sstevel@tonic-gate 		case 'c':
17710Sstevel@tonic-gate 			cflag = TRUE;
17720Sstevel@tonic-gate 			break;
17730Sstevel@tonic-gate 		case 'C':
17740Sstevel@tonic-gate 			Cflag = TRUE;
17750Sstevel@tonic-gate 			break;
17760Sstevel@tonic-gate 		case 'd':
17770Sstevel@tonic-gate 			dflag = TRUE;
17780Sstevel@tonic-gate 			debug |= atoi(optarg);
1779211Smike_s 			(void) printf("[main] debug = 0x%x\n", debug);
17800Sstevel@tonic-gate 			break;
17810Sstevel@tonic-gate 		case 'D':
17820Sstevel@tonic-gate 			Dflag = TRUE;
17830Sstevel@tonic-gate 			break;
17840Sstevel@tonic-gate 		case 'E':
17850Sstevel@tonic-gate 			addlist(Elist, optarg);
17860Sstevel@tonic-gate 			Eflag = TRUE;
17870Sstevel@tonic-gate 			addlist(elist, optarg);
17880Sstevel@tonic-gate 			eflag = TRUE;
17890Sstevel@tonic-gate 			break;
17900Sstevel@tonic-gate 		case 'e':
17910Sstevel@tonic-gate 			addlist(elist, optarg);
17920Sstevel@tonic-gate 			eflag = TRUE;
17930Sstevel@tonic-gate 			break;
17940Sstevel@tonic-gate 		case 'F':
17950Sstevel@tonic-gate 			addlist(Flist, optarg);
17960Sstevel@tonic-gate 			Fflag = TRUE;
17970Sstevel@tonic-gate 			addlist(flist, optarg);
17980Sstevel@tonic-gate 			fflag = TRUE;
17990Sstevel@tonic-gate 			break;
18000Sstevel@tonic-gate 		case 'f':
18010Sstevel@tonic-gate 			addlist(flist, optarg);
18020Sstevel@tonic-gate 			fflag = TRUE;
18030Sstevel@tonic-gate 			break;
18040Sstevel@tonic-gate 		case 'l':
18050Sstevel@tonic-gate 			lflag = TRUE;
18060Sstevel@tonic-gate 			break;
18070Sstevel@tonic-gate 		case 'n':
18080Sstevel@tonic-gate 			nflag = TRUE;
18090Sstevel@tonic-gate 			number_funcs_toprint = atoi(optarg);
18100Sstevel@tonic-gate 			break;
18110Sstevel@tonic-gate 		case 's':
18120Sstevel@tonic-gate 			sflag = TRUE;
18130Sstevel@tonic-gate 			break;
18140Sstevel@tonic-gate 		case 'z':
18150Sstevel@tonic-gate 			zflag = TRUE;
18160Sstevel@tonic-gate 			break;
18170Sstevel@tonic-gate 		case '?':
18180Sstevel@tonic-gate 			errflg++;
18190Sstevel@tonic-gate 
18200Sstevel@tonic-gate 		}
18210Sstevel@tonic-gate 
18220Sstevel@tonic-gate 	if (errflg) {
18230Sstevel@tonic-gate 		(void) fprintf(stderr,
18240Sstevel@tonic-gate 		    "usage: gprof [ -abcCDlsz ] [ -e function-name ] "
18250Sstevel@tonic-gate 		    "[ -E function-name ]\n\t[ -f function-name ] "
18260Sstevel@tonic-gate 		    "[ -F function-name  ]\n\t[  image-file  "
18270Sstevel@tonic-gate 		    "[ profile-file ... ] ]\n");
18280Sstevel@tonic-gate 		exit(EX_USAGE);
18290Sstevel@tonic-gate 	}
18300Sstevel@tonic-gate 
18310Sstevel@tonic-gate 	if (optind < argc) {
18320Sstevel@tonic-gate 		a_outname  = argv[optind++];
18330Sstevel@tonic-gate 	} else {
18340Sstevel@tonic-gate 		a_outname  = A_OUTNAME;
18350Sstevel@tonic-gate 	}
18360Sstevel@tonic-gate 	if (optind < argc) {
18370Sstevel@tonic-gate 		gmonname = argv[optind++];
18380Sstevel@tonic-gate 	} else {
18390Sstevel@tonic-gate 		gmonname = GMONNAME;
18400Sstevel@tonic-gate 	}
18410Sstevel@tonic-gate 	/*
18420Sstevel@tonic-gate 	 *	turn off default functions
18430Sstevel@tonic-gate 	 */
18440Sstevel@tonic-gate 	for (sp = &defaultEs[0]; *sp; sp++) {
18450Sstevel@tonic-gate 		Eflag = TRUE;
18460Sstevel@tonic-gate 		addlist(Elist, *sp);
18470Sstevel@tonic-gate 		eflag = TRUE;
18480Sstevel@tonic-gate 		addlist(elist, *sp);
18490Sstevel@tonic-gate 	}
18500Sstevel@tonic-gate 	/*
18510Sstevel@tonic-gate 	 *	how many ticks per second?
18520Sstevel@tonic-gate 	 *	if we can't tell, report time in ticks.
18530Sstevel@tonic-gate 	 */
18540Sstevel@tonic-gate 	hz = sysconf(_SC_CLK_TCK);
18550Sstevel@tonic-gate 	if (hz == -1) {
18560Sstevel@tonic-gate 		hz = 1;
1857211Smike_s 		(void) fprintf(stderr, "time is in ticks, not seconds\n");
18580Sstevel@tonic-gate 	}
18590Sstevel@tonic-gate 
18600Sstevel@tonic-gate 	getnfile(a_outname);
18610Sstevel@tonic-gate 
18620Sstevel@tonic-gate 	/*
18630Sstevel@tonic-gate 	 *	get information about mon.out file(s).
18640Sstevel@tonic-gate 	 */
18650Sstevel@tonic-gate 	do {
18660Sstevel@tonic-gate 		getpfile(gmonname);
18670Sstevel@tonic-gate 		if (optind < argc)
18680Sstevel@tonic-gate 			gmonname = argv[optind++];
18690Sstevel@tonic-gate 		else
18700Sstevel@tonic-gate 			optind++;
18710Sstevel@tonic-gate 	} while (optind <= argc);
18720Sstevel@tonic-gate 	/*
18730Sstevel@tonic-gate 	 *	dump out a gmon.sum file if requested
18740Sstevel@tonic-gate 	 */
18750Sstevel@tonic-gate 	if (sflag || Dflag)
18760Sstevel@tonic-gate 		dumpsum(GMONSUM);
18770Sstevel@tonic-gate 
18780Sstevel@tonic-gate 	if (old_style) {
18790Sstevel@tonic-gate 		/*
18800Sstevel@tonic-gate 		 *	assign samples to procedures
18810Sstevel@tonic-gate 		 */
18820Sstevel@tonic-gate 		asgnsamples();
18830Sstevel@tonic-gate 	}
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate 	/*
18860Sstevel@tonic-gate 	 *	assemble the dynamic profile
18870Sstevel@tonic-gate 	 */
18880Sstevel@tonic-gate 	timesortnlp = doarcs();
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate 	/*
18910Sstevel@tonic-gate 	 *	print the dynamic profile
18920Sstevel@tonic-gate 	 */
18930Sstevel@tonic-gate #ifdef DEBUG
18940Sstevel@tonic-gate 	if (debug & ANYDEBUG) {
18950Sstevel@tonic-gate 		/* raw output of all symbols in all their glory */
18960Sstevel@tonic-gate 		int i;
1897211Smike_s 		(void) printf(" Name, pc_entry_pt, svalue, tix_in_routine, "
18980Sstevel@tonic-gate 		    "#calls, selfcalls, index \n");
18990Sstevel@tonic-gate 		for (i = 0; i < modules.nname; i++) { 	/* Print each symbol */
19000Sstevel@tonic-gate 			if (timesortnlp[i]->name)
1901211Smike_s 				(void) printf(" %s ", timesortnlp[i]->name);
19020Sstevel@tonic-gate 			else
1903211Smike_s 				(void) printf(" <cycle> ");
1904211Smike_s 			(void) printf(" %lld ", timesortnlp[i]->value);
1905211Smike_s 			(void) printf(" %lld ", timesortnlp[i]->svalue);
1906211Smike_s 			(void) printf(" %f ", timesortnlp[i]->time);
1907211Smike_s 			(void) printf(" %lld ", timesortnlp[i]->ncall);
1908211Smike_s 			(void) printf(" %lld ", timesortnlp[i]->selfcalls);
1909211Smike_s 			(void) printf(" %d ", timesortnlp[i]->index);
1910211Smike_s 			(void) printf(" \n");
19110Sstevel@tonic-gate 		}
19120Sstevel@tonic-gate 	}
1913211Smike_s #endif /* DEBUG */
19140Sstevel@tonic-gate 
19150Sstevel@tonic-gate 	printgprof(timesortnlp);
19160Sstevel@tonic-gate 	/*
19170Sstevel@tonic-gate 	 *	print the flat profile
19180Sstevel@tonic-gate 	 */
19190Sstevel@tonic-gate 	printprof();
19200Sstevel@tonic-gate 	/*
19210Sstevel@tonic-gate 	 *	print the index
19220Sstevel@tonic-gate 	 */
19230Sstevel@tonic-gate 	printindex();
19240Sstevel@tonic-gate 
19250Sstevel@tonic-gate 	/*
19260Sstevel@tonic-gate 	 * print the modules
19270Sstevel@tonic-gate 	 */
19280Sstevel@tonic-gate 	printmodules();
19290Sstevel@tonic-gate 
19300Sstevel@tonic-gate 	done();
19310Sstevel@tonic-gate 	/* NOTREACHED */
19320Sstevel@tonic-gate 	return (0);
19330Sstevel@tonic-gate }
1934