xref: /illumos-gate/usr/src/cmd/sgs/gprof/common/gprof.c (revision 14448871)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51dd08564Sab  * Common Development and Distribution License (the "License").
61dd08564Sab  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
221dd08564Sab  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include	<sysexits.h>
277c478bd9Sstevel@tonic-gate #include	<stdlib.h>
2892ed1782Smike_s #include	<stdio.h>
297c478bd9Sstevel@tonic-gate #include	<unistd.h>
307c478bd9Sstevel@tonic-gate #include	"gprof.h"
317c478bd9Sstevel@tonic-gate #include	"profile.h"
327c478bd9Sstevel@tonic-gate 
33*14448871SToomas Soome bool		aflag;
34*14448871SToomas Soome bool		bflag;
35*14448871SToomas Soome bool		Bflag;
36*14448871SToomas Soome bool		cflag;
37*14448871SToomas Soome bool		Cflag;
38*14448871SToomas Soome bool		dflag;
39*14448871SToomas Soome bool		Dflag;
40*14448871SToomas Soome bool		eflag;
41*14448871SToomas Soome bool		Eflag;
42*14448871SToomas Soome bool		fflag;
43*14448871SToomas Soome bool		Fflag;
44*14448871SToomas Soome bool		lflag;
45*14448871SToomas Soome bool		sflag;
46*14448871SToomas Soome bool		zflag;
47*14448871SToomas Soome bool		nflag;
48*14448871SToomas Soome bool		rflag;
49*14448871SToomas Soome bool		first_file;
50*14448871SToomas Soome bool		old_style;
51*14448871SToomas Soome double		scale;
52*14448871SToomas Soome double		totime;
53*14448871SToomas Soome Size		n_pcsamples;
54*14448871SToomas Soome mod_info_t	modules;
55*14448871SToomas Soome pctype		s_lowpc;
56*14448871SToomas Soome pctype		s_highpc;
57*14448871SToomas Soome sztype		n_modules;
58*14448871SToomas Soome sztype		sampbytes;
59*14448871SToomas Soome sztype		nsamples;
60*14448871SToomas Soome unsigned short	*samples;
61*14448871SToomas Soome fl_info_t	aout_info;
62*14448871SToomas Soome fl_info_t	gmonout_info;
63*14448871SToomas Soome long		hz;
64*14448871SToomas Soome struct hdr	h;
65*14448871SToomas Soome unsigned char	*textspace;
66*14448871SToomas Soome int		debug;
67*14448871SToomas Soome int		number_funcs_toprint;
68*14448871SToomas Soome char		*a_outname;
69*14448871SToomas Soome char		*prog_name;
70*14448871SToomas Soome char		*gmonname;
717c478bd9Sstevel@tonic-gate char		*whoami = "gprof";
727c478bd9Sstevel@tonic-gate static pctype	lowpc, highpc;		/* range profiled, in UNIT's */
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate /*
757c478bd9Sstevel@tonic-gate  *	things which get -E excluded by default.
767c478bd9Sstevel@tonic-gate  */
777c478bd9Sstevel@tonic-gate static char *defaultEs[] = {
787c478bd9Sstevel@tonic-gate 	"mcount",
797c478bd9Sstevel@tonic-gate 	"__mcleanup",
8092ed1782Smike_s 	NULL
817c478bd9Sstevel@tonic-gate };
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate #ifdef DEBUG
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate static char *objname[] = {
867c478bd9Sstevel@tonic-gate 	"<invalid object>",
877c478bd9Sstevel@tonic-gate 	"PROF_BUFFER_T",
887c478bd9Sstevel@tonic-gate 	"PROF_CALLGRAPH_T",
897c478bd9Sstevel@tonic-gate 	"PROF_MODULES_T",
9092ed1782Smike_s 	NULL
917c478bd9Sstevel@tonic-gate };
927c478bd9Sstevel@tonic-gate #define	MAX_OBJTYPES	3
937c478bd9Sstevel@tonic-gate 
9492ed1782Smike_s #endif /* DEBUG */
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate void
done(void)9792ed1782Smike_s done(void)
987c478bd9Sstevel@tonic-gate {
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate 	exit(EX_OK);
1017c478bd9Sstevel@tonic-gate }
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate static pctype
max(pctype a,pctype b)1047c478bd9Sstevel@tonic-gate max(pctype a, pctype b)
1057c478bd9Sstevel@tonic-gate {
1067c478bd9Sstevel@tonic-gate 	if (a > b)
1077c478bd9Sstevel@tonic-gate 		return (a);
1087c478bd9Sstevel@tonic-gate 	return (b);
1097c478bd9Sstevel@tonic-gate }
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate static pctype
min(pctype a,pctype b)1127c478bd9Sstevel@tonic-gate min(pctype a, pctype b)
1137c478bd9Sstevel@tonic-gate {
1147c478bd9Sstevel@tonic-gate 	if (a < b)
1157c478bd9Sstevel@tonic-gate 		return (a);
1167c478bd9Sstevel@tonic-gate 	return (b);
1177c478bd9Sstevel@tonic-gate }
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate /*
1207c478bd9Sstevel@tonic-gate  *	calculate scaled entry point addresses (to save time in asgnsamples),
1217c478bd9Sstevel@tonic-gate  *	and possibly push the scaled entry points over the entry mask,
1227c478bd9Sstevel@tonic-gate  *	if it turns out that the entry point is in one bucket and the code
1237c478bd9Sstevel@tonic-gate  *	for a routine is in the next bucket.
1247c478bd9Sstevel@tonic-gate  *
1257c478bd9Sstevel@tonic-gate  */
1267c478bd9Sstevel@tonic-gate static void
alignentries(void)12792ed1782Smike_s alignentries(void)
1287c478bd9Sstevel@tonic-gate {
12992ed1782Smike_s 	struct nl *nlp;
1307c478bd9Sstevel@tonic-gate #ifdef DEBUG
1317c478bd9Sstevel@tonic-gate 	pctype			bucket_of_entry;
1327c478bd9Sstevel@tonic-gate 	pctype			bucket_of_code;
13392ed1782Smike_s #endif /* DEBUG */
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	/* for old-style gmon.out, nameslist is only in modules.nl */
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	for (nlp = modules.nl; nlp < modules.npe; nlp++) {
1387c478bd9Sstevel@tonic-gate 		nlp->svalue = nlp->value / sizeof (UNIT);
1397c478bd9Sstevel@tonic-gate #ifdef DEBUG
1407c478bd9Sstevel@tonic-gate 		bucket_of_entry = (nlp->svalue - lowpc) / scale;
1417c478bd9Sstevel@tonic-gate 		bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
1427c478bd9Sstevel@tonic-gate 		if (bucket_of_entry < bucket_of_code) {
1437c478bd9Sstevel@tonic-gate 			if (debug & SAMPLEDEBUG) {
14492ed1782Smike_s 				(void) printf(
14592ed1782Smike_s 				    "[alignentries] pushing svalue 0x%llx "
14692ed1782Smike_s 				    "to 0x%llx\n", nlp->svalue,
14792ed1782Smike_s 				    nlp->svalue + UNITS_TO_CODE);
1487c478bd9Sstevel@tonic-gate 			}
1497c478bd9Sstevel@tonic-gate 		}
15092ed1782Smike_s #endif /* DEBUG */
1517c478bd9Sstevel@tonic-gate 	}
1527c478bd9Sstevel@tonic-gate }
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate /*
1557c478bd9Sstevel@tonic-gate  *	old-style gmon.out
1567c478bd9Sstevel@tonic-gate  *	------------------
1577c478bd9Sstevel@tonic-gate  *
1587c478bd9Sstevel@tonic-gate  *	Assign samples to the procedures to which they belong.
1597c478bd9Sstevel@tonic-gate  *
1607c478bd9Sstevel@tonic-gate  *	There are three cases as to where pcl and pch can be
1617c478bd9Sstevel@tonic-gate  *	with respect to the routine entry addresses svalue0 and svalue1
1627c478bd9Sstevel@tonic-gate  *	as shown in the following diagram.  overlap computes the
1637c478bd9Sstevel@tonic-gate  *	distance between the arrows, the fraction of the sample
1647c478bd9Sstevel@tonic-gate  *	that is to be credited to the routine which starts at svalue0.
1657c478bd9Sstevel@tonic-gate  *
1667c478bd9Sstevel@tonic-gate  *	    svalue0                                         svalue1
1677c478bd9Sstevel@tonic-gate  *	       |                                               |
1687c478bd9Sstevel@tonic-gate  *	       v                                               v
1697c478bd9Sstevel@tonic-gate  *
1707c478bd9Sstevel@tonic-gate  *	       +-----------------------------------------------+
1717c478bd9Sstevel@tonic-gate  *	       |					       |
1727c478bd9Sstevel@tonic-gate  *	  |  ->|    |<-		->|         |<-		->|    |<-  |
1737c478bd9Sstevel@tonic-gate  *	  |         |		  |         |		  |         |
1747c478bd9Sstevel@tonic-gate  *	  +---------+		  +---------+		  +---------+
1757c478bd9Sstevel@tonic-gate  *
1767c478bd9Sstevel@tonic-gate  *	  ^         ^		  ^         ^		  ^         ^
1777c478bd9Sstevel@tonic-gate  *	  |         |		  |         |		  |         |
1787c478bd9Sstevel@tonic-gate  *	 pcl       pch		 pcl       pch		 pcl       pch
1797c478bd9Sstevel@tonic-gate  *
1807c478bd9Sstevel@tonic-gate  *	For the vax we assert that samples will never fall in the first
1817c478bd9Sstevel@tonic-gate  *	two bytes of any routine, since that is the entry mask,
1827c478bd9Sstevel@tonic-gate  *	thus we give call alignentries() to adjust the entry points if
1837c478bd9Sstevel@tonic-gate  *	the entry mask falls in one bucket but the code for the routine
1847c478bd9Sstevel@tonic-gate  *	doesn't start until the next bucket.  In conjunction with the
1857c478bd9Sstevel@tonic-gate  *	alignment of routine addresses, this should allow us to have
1867c478bd9Sstevel@tonic-gate  *	only one sample for every four bytes of text space and never
1877c478bd9Sstevel@tonic-gate  *	have any overlap (the two end cases, above).
1887c478bd9Sstevel@tonic-gate  */
1897c478bd9Sstevel@tonic-gate static void
asgnsamples(void)19092ed1782Smike_s asgnsamples(void)
1917c478bd9Sstevel@tonic-gate {
1927c478bd9Sstevel@tonic-gate 	sztype		i, j;
1937c478bd9Sstevel@tonic-gate 	unsigned_UNIT	ccnt;
1947c478bd9Sstevel@tonic-gate 	double		time;
1957c478bd9Sstevel@tonic-gate 	pctype		pcl, pch;
1967c478bd9Sstevel@tonic-gate 	pctype		overlap;
1977c478bd9Sstevel@tonic-gate 	pctype		svalue0, svalue1;
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	extern mod_info_t	modules;
2007c478bd9Sstevel@tonic-gate 	nltype		*nl = modules.nl;
2017c478bd9Sstevel@tonic-gate 	sztype		nname = modules.nname;
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	/* read samples and assign to namelist symbols */
2047c478bd9Sstevel@tonic-gate 	scale = highpc - lowpc;
2057c478bd9Sstevel@tonic-gate 	scale /= nsamples;
2067c478bd9Sstevel@tonic-gate 	alignentries();
2077c478bd9Sstevel@tonic-gate 	for (i = 0, j = 1; i < nsamples; i++) {
2087c478bd9Sstevel@tonic-gate 		ccnt = samples[i];
2097c478bd9Sstevel@tonic-gate 		if (ccnt == 0)
2107c478bd9Sstevel@tonic-gate 			continue;
2111dd08564Sab 		/*LINTED: E_ASSIGMENT_CAUSE_LOSS_PREC*/
2127c478bd9Sstevel@tonic-gate 		pcl = lowpc + scale * i;
2131dd08564Sab 		/*LINTED: E_ASSIGMENT_CAUSE_LOSS_PREC*/
2147c478bd9Sstevel@tonic-gate 		pch = lowpc + scale * (i + 1);
2157c478bd9Sstevel@tonic-gate 		time = ccnt;
2167c478bd9Sstevel@tonic-gate #ifdef DEBUG
2177c478bd9Sstevel@tonic-gate 		if (debug & SAMPLEDEBUG) {
21892ed1782Smike_s 			(void) printf(
21992ed1782Smike_s 			    "[asgnsamples] pcl 0x%llx pch 0x%llx ccnt %d\n",
2207c478bd9Sstevel@tonic-gate 			    pcl, pch, ccnt);
2217c478bd9Sstevel@tonic-gate 		}
22292ed1782Smike_s #endif /* DEBUG */
2237c478bd9Sstevel@tonic-gate 		totime += time;
2247c478bd9Sstevel@tonic-gate 		for (j = (j ? j - 1 : 0); j < nname; j++) {
2257c478bd9Sstevel@tonic-gate 			svalue0 = nl[j].svalue;
2267c478bd9Sstevel@tonic-gate 			svalue1 = nl[j+1].svalue;
2277c478bd9Sstevel@tonic-gate 			/*
2287c478bd9Sstevel@tonic-gate 			 *	if high end of tick is below entry address,
2297c478bd9Sstevel@tonic-gate 			 *	go for next tick.
2307c478bd9Sstevel@tonic-gate 			 */
2317c478bd9Sstevel@tonic-gate 			if (pch < svalue0)
2327c478bd9Sstevel@tonic-gate 				break;
2337c478bd9Sstevel@tonic-gate 			/*
2347c478bd9Sstevel@tonic-gate 			 *	if low end of tick into next routine,
2357c478bd9Sstevel@tonic-gate 			 *	go for next routine.
2367c478bd9Sstevel@tonic-gate 			 */
2377c478bd9Sstevel@tonic-gate 			if (pcl >= svalue1)
2387c478bd9Sstevel@tonic-gate 				continue;
2397c478bd9Sstevel@tonic-gate 			overlap = min(pch, svalue1) - max(pcl, svalue0);
2407c478bd9Sstevel@tonic-gate 			if (overlap != 0) {
2417c478bd9Sstevel@tonic-gate #ifdef DEBUG
2427c478bd9Sstevel@tonic-gate 				if (debug & SAMPLEDEBUG) {
24392ed1782Smike_s 					(void) printf("[asgnsamples] "
2447c478bd9Sstevel@tonic-gate 					    "(0x%llx->0x%llx-0x%llx) %s gets "
2457c478bd9Sstevel@tonic-gate 					    "%f ticks %lld overlap\n",
2467c478bd9Sstevel@tonic-gate 					    nl[j].value/sizeof (UNIT), svalue0,
2477c478bd9Sstevel@tonic-gate 					    svalue1, nl[j].name,
2487c478bd9Sstevel@tonic-gate 					    overlap * time / scale, overlap);
2497c478bd9Sstevel@tonic-gate 				}
25092ed1782Smike_s #endif /* DEBUG */
2517c478bd9Sstevel@tonic-gate 				nl[j].time += overlap * time / scale;
2527c478bd9Sstevel@tonic-gate 			}
2537c478bd9Sstevel@tonic-gate 		}
2547c478bd9Sstevel@tonic-gate 	}
2557c478bd9Sstevel@tonic-gate #ifdef DEBUG
2567c478bd9Sstevel@tonic-gate 	if (debug & SAMPLEDEBUG) {
25792ed1782Smike_s 		(void) printf("[asgnsamples] totime %f\n", totime);
2587c478bd9Sstevel@tonic-gate 	}
25992ed1782Smike_s #endif /* DEBUG */
2607c478bd9Sstevel@tonic-gate }
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate static void
dump_callgraph(FILE * fp,char * filename,unsigned long tarcs,unsigned long ncallees)26492ed1782Smike_s dump_callgraph(FILE *fp, char *filename, unsigned long tarcs,
26592ed1782Smike_s     unsigned long ncallees)
2667c478bd9Sstevel@tonic-gate {
2677c478bd9Sstevel@tonic-gate 	ProfCallGraph		prof_cgraph;
2687c478bd9Sstevel@tonic-gate 	ProfFunction		prof_func;
26992ed1782Smike_s 	arctype	*arcp;
2707c478bd9Sstevel@tonic-gate 	mod_info_t		*mi;
2717c478bd9Sstevel@tonic-gate 	nltype			*nlp;
2727c478bd9Sstevel@tonic-gate 	size_t			cur_offset;
2737c478bd9Sstevel@tonic-gate 	unsigned long		caller_id = 0, callee_id = 0;
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 	/*
2767c478bd9Sstevel@tonic-gate 	 * Write the callgraph header
2777c478bd9Sstevel@tonic-gate 	 */
2787c478bd9Sstevel@tonic-gate 	prof_cgraph.type = PROF_CALLGRAPH_T;
2797c478bd9Sstevel@tonic-gate 	prof_cgraph.version = PROF_CALLGRAPH_VER;
2807c478bd9Sstevel@tonic-gate 	prof_cgraph.functions = PROFCGRAPH_SZ;
2817c478bd9Sstevel@tonic-gate 	prof_cgraph.size = PROFCGRAPH_SZ + tarcs * PROFFUNC_SZ;
2827c478bd9Sstevel@tonic-gate 	if (fwrite(&prof_cgraph, sizeof (ProfCallGraph), 1, fp) != 1) {
2837c478bd9Sstevel@tonic-gate 		perror(filename);
2847c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
2857c478bd9Sstevel@tonic-gate 	}
28692ed1782Smike_s 	/* CONSTCOND */
2877c478bd9Sstevel@tonic-gate 	if (CGRAPH_FILLER)
28892ed1782Smike_s 		(void) fseek(fp, CGRAPH_FILLER, SEEK_CUR);
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	/* Current offset inside the callgraph object */
2917c478bd9Sstevel@tonic-gate 	cur_offset = prof_cgraph.functions;
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
2947c478bd9Sstevel@tonic-gate 		for (nlp = mi->nl; nlp < mi->npe; nlp++) {
2957c478bd9Sstevel@tonic-gate 			if (nlp->ncallers == 0)
2967c478bd9Sstevel@tonic-gate 				continue;
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 			/* If this is the last callee, set next_to to 0 */
2997c478bd9Sstevel@tonic-gate 			callee_id++;
3007c478bd9Sstevel@tonic-gate 			if (callee_id == ncallees)
3017c478bd9Sstevel@tonic-gate 				prof_func.next_to = 0;
3027c478bd9Sstevel@tonic-gate 			else {
3037c478bd9Sstevel@tonic-gate 				prof_func.next_to = cur_offset +
3041dd08564Sab 				    nlp->ncallers * PROFFUNC_SZ;
3057c478bd9Sstevel@tonic-gate 			}
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 			/*
3087c478bd9Sstevel@tonic-gate 			 * Dump this callee's raw arc information with all
3097c478bd9Sstevel@tonic-gate 			 * its callers
3107c478bd9Sstevel@tonic-gate 			 */
3117c478bd9Sstevel@tonic-gate 			caller_id = 1;
3127c478bd9Sstevel@tonic-gate 			for (arcp = nlp->parents; arcp;
3131dd08564Sab 			    arcp = arcp->arc_parentlist) {
3147c478bd9Sstevel@tonic-gate 				/*
3157c478bd9Sstevel@tonic-gate 				 * If no more callers for this callee, set
3167c478bd9Sstevel@tonic-gate 				 * next_from to 0
3177c478bd9Sstevel@tonic-gate 				 */
3187c478bd9Sstevel@tonic-gate 				if (caller_id == nlp->ncallers)
3197c478bd9Sstevel@tonic-gate 					prof_func.next_from = 0;
3207c478bd9Sstevel@tonic-gate 				else {
3217c478bd9Sstevel@tonic-gate 					prof_func.next_from = cur_offset +
3221dd08564Sab 					    PROFFUNC_SZ;
3237c478bd9Sstevel@tonic-gate 				}
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 				prof_func.frompc =
3261dd08564Sab 				    arcp->arc_parentp->module->load_base +
3271dd08564Sab 				    (arcp->arc_parentp->value -
3281dd08564Sab 				    arcp->arc_parentp->module->txt_origin);
3291dd08564Sab 				prof_func.topc = mi->load_base +
3301dd08564Sab 				    (nlp->value - mi->txt_origin);
3317c478bd9Sstevel@tonic-gate 				prof_func.count = arcp->arc_count;
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 				if (fwrite(&prof_func, sizeof (ProfFunction),
3351dd08564Sab 				    1, fp) != 1) {
3367c478bd9Sstevel@tonic-gate 					perror(filename);
3377c478bd9Sstevel@tonic-gate 					exit(EX_IOERR);
3387c478bd9Sstevel@tonic-gate 				}
33992ed1782Smike_s 				/* CONSTCOND */
3407c478bd9Sstevel@tonic-gate 				if (FUNC_FILLER)
34192ed1782Smike_s 					(void) fseek(fp, FUNC_FILLER, SEEK_CUR);
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 				cur_offset += PROFFUNC_SZ;
3447c478bd9Sstevel@tonic-gate 				caller_id++;
3457c478bd9Sstevel@tonic-gate 			}
3467c478bd9Sstevel@tonic-gate 		} /* for nlp... */
3477c478bd9Sstevel@tonic-gate 	} /* for mi... */
3487c478bd9Sstevel@tonic-gate }
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate /*
3517c478bd9Sstevel@tonic-gate  * To save all pc-hits in all the gmon.out's is infeasible, as this
3527c478bd9Sstevel@tonic-gate  * may become quite huge even with a small number of files to sum.
3537c478bd9Sstevel@tonic-gate  * Instead, we'll dump *fictitious hits* to correct functions
3547c478bd9Sstevel@tonic-gate  * by scanning module namelists. Again, since this is summing
3557c478bd9Sstevel@tonic-gate  * pc-hits, we may have to dump the pcsamples out in chunks if the
3567c478bd9Sstevel@tonic-gate  * number of pc-hits is high.
3577c478bd9Sstevel@tonic-gate  */
3587c478bd9Sstevel@tonic-gate static void
dump_hits(FILE * fp,char * filename,nltype * nlp)3597c478bd9Sstevel@tonic-gate dump_hits(FILE *fp, char *filename, nltype *nlp)
3607c478bd9Sstevel@tonic-gate {
3617c478bd9Sstevel@tonic-gate 	Address		*p, hitpc;
3627c478bd9Sstevel@tonic-gate 	size_t		i, nelem, ntowrite;
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	if ((nelem = nlp->nticks) > PROF_BUFFER_SIZE)
3657c478bd9Sstevel@tonic-gate 		nelem = PROF_BUFFER_SIZE;
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	if ((p = (Address *) calloc(nelem, sizeof (Address))) == NULL) {
36892ed1782Smike_s 		(void) fprintf(stderr, "%s: no room for %d pcsamples\n",
3691dd08564Sab 		    whoami, nelem);
3707c478bd9Sstevel@tonic-gate 		exit(EX_OSERR);
3717c478bd9Sstevel@tonic-gate 	}
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 	/*
3747c478bd9Sstevel@tonic-gate 	 * Set up *fictitious* hits (to function entry) buffer
3757c478bd9Sstevel@tonic-gate 	 */
3767c478bd9Sstevel@tonic-gate 	hitpc = nlp->module->load_base + (nlp->value - nlp->module->txt_origin);
3777c478bd9Sstevel@tonic-gate 	for (i = 0; i < nelem; i++)
3787c478bd9Sstevel@tonic-gate 		p[i] = hitpc;
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	for (ntowrite = nlp->nticks; ntowrite >= nelem; ntowrite -= nelem) {
3817c478bd9Sstevel@tonic-gate 		if (fwrite(p, nelem * sizeof (Address), 1, fp) != 1) {
3827c478bd9Sstevel@tonic-gate 			perror(filename);
3837c478bd9Sstevel@tonic-gate 			exit(EX_IOERR);
3847c478bd9Sstevel@tonic-gate 		}
3857c478bd9Sstevel@tonic-gate 	}
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	if (ntowrite) {
3887c478bd9Sstevel@tonic-gate 		if (fwrite(p, ntowrite * sizeof (Address), 1, fp) != 1) {
3897c478bd9Sstevel@tonic-gate 			perror(filename);
3907c478bd9Sstevel@tonic-gate 			exit(EX_IOERR);
3917c478bd9Sstevel@tonic-gate 		}
3927c478bd9Sstevel@tonic-gate 	}
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 	free(p);
3957c478bd9Sstevel@tonic-gate }
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate static void
dump_pcsamples(FILE * fp,char * filename,unsigned long * tarcs,unsigned long * ncallees)39892ed1782Smike_s dump_pcsamples(FILE *fp, char *filename, unsigned long *tarcs,
39992ed1782Smike_s     unsigned long *ncallees)
4007c478bd9Sstevel@tonic-gate {
4017c478bd9Sstevel@tonic-gate 	ProfBuffer		prof_buffer;
40292ed1782Smike_s 	arctype	*arcp;
4037c478bd9Sstevel@tonic-gate 	mod_info_t		*mi;
4047c478bd9Sstevel@tonic-gate 	nltype			*nlp;
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	prof_buffer.type = PROF_BUFFER_T;
4077c478bd9Sstevel@tonic-gate 	prof_buffer.version = PROF_BUFFER_VER;
4087c478bd9Sstevel@tonic-gate 	prof_buffer.buffer = PROFBUF_SZ;
4097c478bd9Sstevel@tonic-gate 	prof_buffer.bufsize = n_pcsamples;
4107c478bd9Sstevel@tonic-gate 	prof_buffer.size = PROFBUF_SZ + n_pcsamples * sizeof (Address);
4117c478bd9Sstevel@tonic-gate 	if (fwrite(&prof_buffer, sizeof (ProfBuffer), 1, fp) != 1) {
4127c478bd9Sstevel@tonic-gate 		perror(filename);
4137c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
4147c478bd9Sstevel@tonic-gate 	}
41592ed1782Smike_s 	/* CONSTCOND */
4167c478bd9Sstevel@tonic-gate 	if (BUF_FILLER)
41792ed1782Smike_s 		(void) fseek(fp, BUF_FILLER, SEEK_CUR);
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 	*tarcs = 0;
4207c478bd9Sstevel@tonic-gate 	*ncallees = 0;
4217c478bd9Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
4227c478bd9Sstevel@tonic-gate 		for (nlp = mi->nl; nlp < mi->npe; nlp++) {
4237c478bd9Sstevel@tonic-gate 			if (nlp->nticks)
4247c478bd9Sstevel@tonic-gate 				dump_hits(fp, filename, nlp);
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 			nlp->ncallers = 0;
4277c478bd9Sstevel@tonic-gate 			for (arcp = nlp->parents; arcp;
4281dd08564Sab 			    arcp = arcp->arc_parentlist) {
4297c478bd9Sstevel@tonic-gate 				(nlp->ncallers)++;
4307c478bd9Sstevel@tonic-gate 			}
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 			if (nlp->ncallers) {
4337c478bd9Sstevel@tonic-gate 				(*tarcs) += nlp->ncallers;
4347c478bd9Sstevel@tonic-gate 				(*ncallees)++;
4357c478bd9Sstevel@tonic-gate 			}
4367c478bd9Sstevel@tonic-gate 		}
4377c478bd9Sstevel@tonic-gate 	}
4387c478bd9Sstevel@tonic-gate }
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate static void
dump_modules(FILE * fp,char * filename,size_t pbuf_sz)4417c478bd9Sstevel@tonic-gate dump_modules(FILE *fp, char *filename, size_t pbuf_sz)
4427c478bd9Sstevel@tonic-gate {
4437c478bd9Sstevel@tonic-gate 	char		*pbuf, *p;
4447c478bd9Sstevel@tonic-gate 	size_t		namelen;
4457c478bd9Sstevel@tonic-gate 	Index		off_nxt, off_path;
4467c478bd9Sstevel@tonic-gate 	mod_info_t	*mi;
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 	ProfModuleList	prof_modlist;
4497c478bd9Sstevel@tonic-gate 	ProfModule	prof_mod;
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	/* Allocate for path strings buffer */
4527c478bd9Sstevel@tonic-gate 	pbuf_sz = CEIL(pbuf_sz, STRUCT_ALIGN);
45392ed1782Smike_s 	if ((p = pbuf = calloc(pbuf_sz, sizeof (char))) == NULL) {
45492ed1782Smike_s 		(void) fprintf(stderr, "%s: no room for %d bytes\n",
4551dd08564Sab 		    whoami, pbuf_sz * sizeof (char));
4567c478bd9Sstevel@tonic-gate 		exit(EX_OSERR);
4577c478bd9Sstevel@tonic-gate 	}
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	/* Dump out PROF_MODULE_T info for all non-aout modules */
4607c478bd9Sstevel@tonic-gate 	prof_modlist.type = PROF_MODULES_T;
4617c478bd9Sstevel@tonic-gate 	prof_modlist.version = PROF_MODULES_VER;
4627c478bd9Sstevel@tonic-gate 	prof_modlist.modules = PROFMODLIST_SZ;
4637c478bd9Sstevel@tonic-gate 	prof_modlist.size = PROFMODLIST_SZ + (n_modules - 1) * PROFMOD_SZ +
4641dd08564Sab 	    pbuf_sz;
4657c478bd9Sstevel@tonic-gate 	if (fwrite(&prof_modlist, sizeof (ProfModuleList), 1, fp) != 1) {
4667c478bd9Sstevel@tonic-gate 		perror(filename);
4677c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
4687c478bd9Sstevel@tonic-gate 	}
46992ed1782Smike_s 	/* CONSTCOND */
4707c478bd9Sstevel@tonic-gate 	if (MODLIST_FILLER)
47192ed1782Smike_s 		(void) fseek(fp, MODLIST_FILLER, SEEK_CUR);
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	/*
4747c478bd9Sstevel@tonic-gate 	 * Initialize offsets for ProfModule elements.
4757c478bd9Sstevel@tonic-gate 	 */
4767c478bd9Sstevel@tonic-gate 	off_nxt = PROFMODLIST_SZ + PROFMOD_SZ;
4777c478bd9Sstevel@tonic-gate 	off_path = PROFMODLIST_SZ + (n_modules - 1) * PROFMOD_SZ;
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	for (mi = modules.next; mi; mi = mi->next) {
4807c478bd9Sstevel@tonic-gate 		if (mi->next)
4817c478bd9Sstevel@tonic-gate 			prof_mod.next = off_nxt;
4827c478bd9Sstevel@tonic-gate 		else
4837c478bd9Sstevel@tonic-gate 			prof_mod.next = 0;
4847c478bd9Sstevel@tonic-gate 		prof_mod.path = off_path;
4857c478bd9Sstevel@tonic-gate 		prof_mod.startaddr = mi->load_base;
4867c478bd9Sstevel@tonic-gate 		prof_mod.endaddr = mi->load_end;
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 		if (fwrite(&prof_mod, sizeof (ProfModule), 1, fp) != 1) {
4897c478bd9Sstevel@tonic-gate 			perror(filename);
4907c478bd9Sstevel@tonic-gate 			exit(EX_IOERR);
4917c478bd9Sstevel@tonic-gate 		}
4927c478bd9Sstevel@tonic-gate 
49392ed1782Smike_s 		/* CONSTCOND */
4947c478bd9Sstevel@tonic-gate 		if (MOD_FILLER)
49592ed1782Smike_s 			(void) fseek(fp, MOD_FILLER, SEEK_CUR);
4967c478bd9Sstevel@tonic-gate 
49792ed1782Smike_s 		(void) strcpy(p, mi->name);
4987c478bd9Sstevel@tonic-gate 		namelen = strlen(mi->name);
4997c478bd9Sstevel@tonic-gate 		p += namelen + 1;
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 		/* Note that offset to every path str need not be aligned */
5027c478bd9Sstevel@tonic-gate 		off_nxt += PROFMOD_SZ;
5037c478bd9Sstevel@tonic-gate 		off_path += namelen + 1;
5047c478bd9Sstevel@tonic-gate 	}
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate 	/* Write out the module path strings */
5077c478bd9Sstevel@tonic-gate 	if (pbuf_sz) {
5087c478bd9Sstevel@tonic-gate 		if (fwrite(pbuf, pbuf_sz, 1, fp) != 1) {
5097c478bd9Sstevel@tonic-gate 			perror(filename);
5107c478bd9Sstevel@tonic-gate 			exit(EX_IOERR);
5117c478bd9Sstevel@tonic-gate 		}
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 		free(pbuf);
5147c478bd9Sstevel@tonic-gate 	}
5157c478bd9Sstevel@tonic-gate }
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate /*
5187c478bd9Sstevel@tonic-gate  * If we have inactive modules, their current load addresses may overlap with
5197c478bd9Sstevel@tonic-gate  * active ones, and so we've to assign fictitious, non-overlapping addresses
5207c478bd9Sstevel@tonic-gate  * to all modules before we dump them.
5217c478bd9Sstevel@tonic-gate  */
5227c478bd9Sstevel@tonic-gate static void
fixup_maps(size_t * pathsz)5237c478bd9Sstevel@tonic-gate fixup_maps(size_t *pathsz)
5247c478bd9Sstevel@tonic-gate {
5257c478bd9Sstevel@tonic-gate 	unsigned int	n_inactive = 0;
52692ed1782Smike_s 	Address		lbase = 0, lend;
5277c478bd9Sstevel@tonic-gate 	mod_info_t	*mi;
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	/* Pick the lowest load address among modules */
5307c478bd9Sstevel@tonic-gate 	*pathsz = 0;
5317c478bd9Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 		if (mi->active == FALSE)
5347c478bd9Sstevel@tonic-gate 			n_inactive++;
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 		if (mi == &modules || mi->load_base < lbase)
5377c478bd9Sstevel@tonic-gate 			lbase = mi->load_base;
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 		/*
5407c478bd9Sstevel@tonic-gate 		 * Return total path size of non-aout modules only
5417c478bd9Sstevel@tonic-gate 		 */
5427c478bd9Sstevel@tonic-gate 		if (mi != &modules)
5437c478bd9Sstevel@tonic-gate 			*pathsz = (*pathsz) + strlen(mi->name) + 1;
5447c478bd9Sstevel@tonic-gate 	}
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	/*
5477c478bd9Sstevel@tonic-gate 	 * All module info is in fine shape already if there are no
5487c478bd9Sstevel@tonic-gate 	 * inactive modules
5497c478bd9Sstevel@tonic-gate 	 */
5507c478bd9Sstevel@tonic-gate 	if (n_inactive == 0)
5517c478bd9Sstevel@tonic-gate 		return;
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	/*
5547c478bd9Sstevel@tonic-gate 	 * Assign fictitious load addresses to all (non-aout) modules so
5557c478bd9Sstevel@tonic-gate 	 * that sum info can be dumped out.
5567c478bd9Sstevel@tonic-gate 	 */
5577c478bd9Sstevel@tonic-gate 	for (mi = modules.next; mi; mi = mi->next) {
5587c478bd9Sstevel@tonic-gate 		lend = lbase + (mi->data_end - mi->txt_origin);
5597c478bd9Sstevel@tonic-gate 		if ((lbase < modules.load_base && lend < modules.load_base) ||
5607c478bd9Sstevel@tonic-gate 		    (lbase > modules.load_end && lend > modules.load_end)) {
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 			mi->load_base = lbase;
5637c478bd9Sstevel@tonic-gate 			mi->load_end = lend;
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 			/* just to give an appearance of reality */
5667c478bd9Sstevel@tonic-gate 			lbase = CEIL(lend + PGSZ, PGSZ);
5677c478bd9Sstevel@tonic-gate 		} else {
5687c478bd9Sstevel@tonic-gate 			/*
5697c478bd9Sstevel@tonic-gate 			 * can't use this lbase & lend pair, as it
5707c478bd9Sstevel@tonic-gate 			 * overlaps with aout's addresses
5717c478bd9Sstevel@tonic-gate 			 */
5727c478bd9Sstevel@tonic-gate 			mi->load_base = CEIL(modules.load_end + PGSZ, PGSZ);
5737c478bd9Sstevel@tonic-gate 			mi->load_end = mi->load_base + (lend - lbase);
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 			lbase = CEIL(mi->load_end + PGSZ, PGSZ);
5767c478bd9Sstevel@tonic-gate 		}
5777c478bd9Sstevel@tonic-gate 	}
5787c478bd9Sstevel@tonic-gate }
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate static void
dump_gprofhdr(FILE * fp,char * filename)5817c478bd9Sstevel@tonic-gate dump_gprofhdr(FILE *fp, char *filename)
5827c478bd9Sstevel@tonic-gate {
5837c478bd9Sstevel@tonic-gate 	ProfHeader	prof_hdr;
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate 	prof_hdr.h_magic = PROF_MAGIC;
5867c478bd9Sstevel@tonic-gate 	prof_hdr.h_major_ver = PROF_MAJOR_VERSION;
5877c478bd9Sstevel@tonic-gate 	prof_hdr.h_minor_ver = PROF_MINOR_VERSION;
5887c478bd9Sstevel@tonic-gate 	prof_hdr.size = PROFHDR_SZ;
5897c478bd9Sstevel@tonic-gate 	if (fwrite(&prof_hdr, sizeof (prof_hdr), 1, fp) != 1) {
5907c478bd9Sstevel@tonic-gate 		perror(filename);
5917c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
5927c478bd9Sstevel@tonic-gate 	}
5937c478bd9Sstevel@tonic-gate 
59492ed1782Smike_s 	/* CONSTCOND */
5957c478bd9Sstevel@tonic-gate 	if (HDR_FILLER)
59692ed1782Smike_s 		(void) fseek(fp, HDR_FILLER, SEEK_CUR);
5977c478bd9Sstevel@tonic-gate }
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate static void
dumpsum_ostyle(char * sumfile)6007c478bd9Sstevel@tonic-gate dumpsum_ostyle(char *sumfile)
6017c478bd9Sstevel@tonic-gate {
60292ed1782Smike_s 	nltype *nlp;
60392ed1782Smike_s 	arctype *arcp;
6047c478bd9Sstevel@tonic-gate 	struct rawarc arc;
6057c478bd9Sstevel@tonic-gate 	struct rawarc32 arc32;
6067c478bd9Sstevel@tonic-gate 	FILE *sfile;
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 	if ((sfile = fopen(sumfile, "w")) == NULL) {
6097c478bd9Sstevel@tonic-gate 		perror(sumfile);
6107c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
6117c478bd9Sstevel@tonic-gate 	}
6127c478bd9Sstevel@tonic-gate 	/*
6137c478bd9Sstevel@tonic-gate 	 * dump the header; use the last header read in
6147c478bd9Sstevel@tonic-gate 	 */
6157c478bd9Sstevel@tonic-gate 	if (Bflag) {
6161dd08564Sab 		if (fwrite(&h, sizeof (h), 1, sfile) != 1) {
6171dd08564Sab 			perror(sumfile);
6181dd08564Sab 			exit(EX_IOERR);
6191dd08564Sab 		}
6207c478bd9Sstevel@tonic-gate 	} else {
6211dd08564Sab 		struct hdr32 hdr;
6221dd08564Sab 		hdr.lowpc  = (pctype32)h.lowpc;
6231dd08564Sab 		hdr.highpc = (pctype32)h.highpc;
6241dd08564Sab 		hdr.ncnt   = (pctype32)h.ncnt;
6251dd08564Sab 		if (fwrite(&hdr, sizeof (hdr), 1, sfile) != 1) {
6261dd08564Sab 			perror(sumfile);
6271dd08564Sab 			exit(EX_IOERR);
6281dd08564Sab 		}
6297c478bd9Sstevel@tonic-gate 	}
6307c478bd9Sstevel@tonic-gate 	/*
6317c478bd9Sstevel@tonic-gate 	 * dump the samples
6327c478bd9Sstevel@tonic-gate 	 */
6337c478bd9Sstevel@tonic-gate 	if (fwrite(samples, sizeof (unsigned_UNIT), nsamples, sfile) !=
6347c478bd9Sstevel@tonic-gate 	    nsamples) {
6357c478bd9Sstevel@tonic-gate 		perror(sumfile);
6367c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
6377c478bd9Sstevel@tonic-gate 	}
6387c478bd9Sstevel@tonic-gate 	/*
6397c478bd9Sstevel@tonic-gate 	 * dump the normalized raw arc information. For old-style dumping,
6407c478bd9Sstevel@tonic-gate 	 * the only namelist is in modules.nl
6417c478bd9Sstevel@tonic-gate 	 */
6427c478bd9Sstevel@tonic-gate 	for (nlp = modules.nl; nlp < modules.npe; nlp++) {
6437c478bd9Sstevel@tonic-gate 		for (arcp = nlp->children; arcp;
6447c478bd9Sstevel@tonic-gate 		    arcp = arcp->arc_childlist) {
6457c478bd9Sstevel@tonic-gate 			if (Bflag) {
6461dd08564Sab 				arc.raw_frompc = arcp->arc_parentp->value;
6471dd08564Sab 				arc.raw_selfpc = arcp->arc_childp->value;
6481dd08564Sab 				arc.raw_count = arcp->arc_count;
6491dd08564Sab 				if (fwrite(&arc, sizeof (arc), 1, sfile) != 1) {
6501dd08564Sab 					perror(sumfile);
6511dd08564Sab 					exit(EX_IOERR);
6521dd08564Sab 				}
6537c478bd9Sstevel@tonic-gate 			} else {
6541dd08564Sab 				arc32.raw_frompc =
6551dd08564Sab 				    (pctype32)arcp->arc_parentp->value;
6561dd08564Sab 				arc32.raw_selfpc =
6571dd08564Sab 				    (pctype32)arcp->arc_childp->value;
6581dd08564Sab 				arc32.raw_count = (actype32)arcp->arc_count;
6591dd08564Sab 				if (fwrite(&arc32, sizeof (arc32), 1, sfile) !=
6601dd08564Sab 				    1) {
6611dd08564Sab 					perror(sumfile);
6621dd08564Sab 					exit(EX_IOERR);
6631dd08564Sab 				}
6647c478bd9Sstevel@tonic-gate 			}
6657c478bd9Sstevel@tonic-gate #ifdef DEBUG
6667c478bd9Sstevel@tonic-gate 			if (debug & SAMPLEDEBUG) {
66792ed1782Smike_s 				(void) printf(
66892ed1782Smike_s 				    "[dumpsum_ostyle] frompc 0x%llx selfpc "
6697c478bd9Sstevel@tonic-gate 				    "0x%llx count %lld\n", arc.raw_frompc,
6707c478bd9Sstevel@tonic-gate 				    arc.raw_selfpc, arc.raw_count);
6717c478bd9Sstevel@tonic-gate 			}
67292ed1782Smike_s #endif /* DEBUG */
6737c478bd9Sstevel@tonic-gate 		}
6747c478bd9Sstevel@tonic-gate 	}
67592ed1782Smike_s 	(void) fclose(sfile);
6767c478bd9Sstevel@tonic-gate }
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate /*
6797c478bd9Sstevel@tonic-gate  * dump out the gmon.sum file
6807c478bd9Sstevel@tonic-gate  */
6817c478bd9Sstevel@tonic-gate static void
dumpsum(char * sumfile)6827c478bd9Sstevel@tonic-gate dumpsum(char *sumfile)
6837c478bd9Sstevel@tonic-gate {
6847c478bd9Sstevel@tonic-gate 	FILE		*sfile;
6857c478bd9Sstevel@tonic-gate 	size_t		pathbuf_sz;
6867c478bd9Sstevel@tonic-gate 	unsigned long	total_arcs;	/* total number of arcs in all */
6877c478bd9Sstevel@tonic-gate 	unsigned long	ncallees;	/* no. of callees with parents */
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 	if (old_style) {
6907c478bd9Sstevel@tonic-gate 		dumpsum_ostyle(sumfile);
6917c478bd9Sstevel@tonic-gate 		return;
6927c478bd9Sstevel@tonic-gate 	}
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 	if ((sfile = fopen(sumfile, "w")) == NULL) {
6957c478bd9Sstevel@tonic-gate 		perror(sumfile);
6967c478bd9Sstevel@tonic-gate 		exit(EX_IOERR);
6977c478bd9Sstevel@tonic-gate 	}
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	/*
7007c478bd9Sstevel@tonic-gate 	 * Dump the new-style gprof header. Even if one of the original
7017c478bd9Sstevel@tonic-gate 	 * profiled-files was of a older version, the summed file is of
7027c478bd9Sstevel@tonic-gate 	 * current version only.
7037c478bd9Sstevel@tonic-gate 	 */
7047c478bd9Sstevel@tonic-gate 	dump_gprofhdr(sfile, sumfile);
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 	/*
7077c478bd9Sstevel@tonic-gate 	 * Fix up load-maps and dump out modules info
7087c478bd9Sstevel@tonic-gate 	 *
7097c478bd9Sstevel@tonic-gate 	 * Fix up module load maps so inactive modules get *some* address
7107c478bd9Sstevel@tonic-gate 	 * (and btw, could you get the total size of non-aout module path
7117c478bd9Sstevel@tonic-gate 	 * strings please ?)
7127c478bd9Sstevel@tonic-gate 	 */
7137c478bd9Sstevel@tonic-gate 	fixup_maps(&pathbuf_sz);
7147c478bd9Sstevel@tonic-gate 	dump_modules(sfile, sumfile, pathbuf_sz);
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 	/*
7187c478bd9Sstevel@tonic-gate 	 * Dump out the summ'd pcsamples
7197c478bd9Sstevel@tonic-gate 	 *
7207c478bd9Sstevel@tonic-gate 	 * For dumping call graph information later, we need certain
7217c478bd9Sstevel@tonic-gate 	 * statistics (like total arcs, number of callers for each node);
7227c478bd9Sstevel@tonic-gate 	 * collect these also while we are at it.
7237c478bd9Sstevel@tonic-gate 	 */
7247c478bd9Sstevel@tonic-gate 	dump_pcsamples(sfile, sumfile, &total_arcs, &ncallees);
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 	/*
7277c478bd9Sstevel@tonic-gate 	 * Dump out the summ'd call graph information
7287c478bd9Sstevel@tonic-gate 	 */
7297c478bd9Sstevel@tonic-gate 	dump_callgraph(sfile, sumfile, total_arcs, ncallees);
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 
73292ed1782Smike_s 	(void) fclose(sfile);
7337c478bd9Sstevel@tonic-gate }
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate static void
tally(mod_info_t * caller_mod,mod_info_t * callee_mod,struct rawarc * rawp)7367c478bd9Sstevel@tonic-gate tally(mod_info_t *caller_mod, mod_info_t *callee_mod, struct rawarc *rawp)
7377c478bd9Sstevel@tonic-gate {
7387c478bd9Sstevel@tonic-gate 	nltype		*parentp;
7397c478bd9Sstevel@tonic-gate 	nltype		*childp;
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	/*
7427c478bd9Sstevel@tonic-gate 	 * if count == 0 this is a null arc and
7437c478bd9Sstevel@tonic-gate 	 * we don't need to tally it.
7447c478bd9Sstevel@tonic-gate 	 */
7457c478bd9Sstevel@tonic-gate 	if (rawp->raw_count == 0)
7467c478bd9Sstevel@tonic-gate 		return;
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	/*
7497c478bd9Sstevel@tonic-gate 	 * Lookup the caller and callee pcs in namelists of
7507c478bd9Sstevel@tonic-gate 	 * appropriate modules
7517c478bd9Sstevel@tonic-gate 	 */
7527c478bd9Sstevel@tonic-gate 	parentp = nllookup(caller_mod, rawp->raw_frompc, NULL);
7537c478bd9Sstevel@tonic-gate 	childp = nllookup(callee_mod, rawp->raw_selfpc, NULL);
7547c478bd9Sstevel@tonic-gate 	if (childp && parentp) {
7557c478bd9Sstevel@tonic-gate 		if (!Dflag)
7567c478bd9Sstevel@tonic-gate 			childp->ncall += rawp->raw_count;
7577c478bd9Sstevel@tonic-gate 		else {
7587c478bd9Sstevel@tonic-gate 			if (first_file)
7597c478bd9Sstevel@tonic-gate 				childp->ncall += rawp->raw_count;
7607c478bd9Sstevel@tonic-gate 			else {
7617c478bd9Sstevel@tonic-gate 				childp->ncall -= rawp->raw_count;
7627c478bd9Sstevel@tonic-gate 				if (childp->ncall < 0)
7637c478bd9Sstevel@tonic-gate 					childp->ncall = 0;
7647c478bd9Sstevel@tonic-gate 			}
7657c478bd9Sstevel@tonic-gate 		}
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate #ifdef DEBUG
7687c478bd9Sstevel@tonic-gate 		if (debug & TALLYDEBUG) {
76992ed1782Smike_s 			(void) printf("[tally] arc from %s to %s traversed "
7707c478bd9Sstevel@tonic-gate 			    "%lld times\n", parentp->name,
7717c478bd9Sstevel@tonic-gate 			    childp->name, rawp->raw_count);
7727c478bd9Sstevel@tonic-gate 		}
77392ed1782Smike_s #endif /* DEBUG */
7747c478bd9Sstevel@tonic-gate 		addarc(parentp, childp, rawp->raw_count);
7757c478bd9Sstevel@tonic-gate 	}
7767c478bd9Sstevel@tonic-gate }
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate /*
7797c478bd9Sstevel@tonic-gate  * Look up a module's base address in a sorted list of pc-hits. Unlike
7807c478bd9Sstevel@tonic-gate  * nllookup(), this deals with misses by mapping them to the next *higher*
7817c478bd9Sstevel@tonic-gate  * pc-hit. This is so that we get into the module's first pc-hit rightaway,
7827c478bd9Sstevel@tonic-gate  * even if the module's entry-point (load_base) itself is not a hit.
7837c478bd9Sstevel@tonic-gate  */
7847c478bd9Sstevel@tonic-gate static Address *
locate(Address * pclist,size_t nelem,Address keypc)7857c478bd9Sstevel@tonic-gate locate(Address	*pclist, size_t nelem, Address keypc)
7867c478bd9Sstevel@tonic-gate {
7877c478bd9Sstevel@tonic-gate 	size_t	low = 0, middle, high = nelem - 1;
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	if (keypc <= pclist[low])
7907c478bd9Sstevel@tonic-gate 		return (pclist);
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 	if (keypc > pclist[high])
7937c478bd9Sstevel@tonic-gate 		return (NULL);
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate 	while (low != high) {
7967c478bd9Sstevel@tonic-gate 		middle = (high + low) >> 1;
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 		if ((pclist[middle] < keypc) && (pclist[middle + 1] >= keypc))
7997c478bd9Sstevel@tonic-gate 			return (&pclist[middle + 1]);
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 		if (pclist[middle] >= keypc)
8027c478bd9Sstevel@tonic-gate 			high = middle;
8037c478bd9Sstevel@tonic-gate 		else
8047c478bd9Sstevel@tonic-gate 			low = middle + 1;
8057c478bd9Sstevel@tonic-gate 	}
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	/* must never reach here! */
8087c478bd9Sstevel@tonic-gate 	return (NULL);
8097c478bd9Sstevel@tonic-gate }
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate static void
assign_pcsamples(mod_info_t * module,Address * pcsmpl,size_t n_samples)81292ed1782Smike_s assign_pcsamples(mod_info_t *module, Address *pcsmpl, size_t n_samples)
8137c478bd9Sstevel@tonic-gate {
8147c478bd9Sstevel@tonic-gate 	Address		*pcptr, *pcse = pcsmpl + n_samples;
8157c478bd9Sstevel@tonic-gate 	pctype		nxt_func;
8167c478bd9Sstevel@tonic-gate 	nltype		*fnl;
8177c478bd9Sstevel@tonic-gate 	size_t		func_nticks;
8187c478bd9Sstevel@tonic-gate #ifdef DEBUG
8197c478bd9Sstevel@tonic-gate 	size_t		n_hits_in_module = 0;
82092ed1782Smike_s #endif /* DEBUG */
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 	/* Locate the first pc-hit for this module */
8237c478bd9Sstevel@tonic-gate 	if ((pcptr = locate(pcsmpl, n_samples, module->load_base)) == NULL) {
8247c478bd9Sstevel@tonic-gate #ifdef DEBUG
8257c478bd9Sstevel@tonic-gate 		if (debug & PCSMPLDEBUG) {
82692ed1782Smike_s 			(void) printf("[assign_pcsamples] no pc-hits in\n");
82792ed1782Smike_s 			(void) printf(
82892ed1782Smike_s 			    "                   `%s'\n", module->name);
8297c478bd9Sstevel@tonic-gate 		}
83092ed1782Smike_s #endif /* DEBUG */
8317c478bd9Sstevel@tonic-gate 		return;			/* no pc-hits in this module */
8327c478bd9Sstevel@tonic-gate 	}
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 	/* Assign all pc-hits in this module to appropriate functions */
8357c478bd9Sstevel@tonic-gate 	while ((pcptr < pcse) && (*pcptr < module->load_end)) {
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 		/* Update the corresponding function's time */
8387c478bd9Sstevel@tonic-gate 		if (fnl = nllookup(module, (pctype) *pcptr, &nxt_func)) {
8397c478bd9Sstevel@tonic-gate 			/*
8407c478bd9Sstevel@tonic-gate 			 * Collect all pc-hits in this function. Each
8417c478bd9Sstevel@tonic-gate 			 * pc-hit counts as 1 tick.
8427c478bd9Sstevel@tonic-gate 			 */
8437c478bd9Sstevel@tonic-gate 			func_nticks = 0;
8447c478bd9Sstevel@tonic-gate 			while ((pcptr < pcse) && (*pcptr < nxt_func)) {
8457c478bd9Sstevel@tonic-gate 				func_nticks++;
8467c478bd9Sstevel@tonic-gate 				pcptr++;
8477c478bd9Sstevel@tonic-gate 			}
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 			if (func_nticks == 0)
8507c478bd9Sstevel@tonic-gate 				pcptr++;
8517c478bd9Sstevel@tonic-gate 			else {
8527c478bd9Sstevel@tonic-gate 				fnl->nticks += func_nticks;
8537c478bd9Sstevel@tonic-gate 				fnl->time += func_nticks;
8547c478bd9Sstevel@tonic-gate 				totime += func_nticks;
8557c478bd9Sstevel@tonic-gate 			}
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate #ifdef DEBUG
8587c478bd9Sstevel@tonic-gate 			n_hits_in_module += func_nticks;
85992ed1782Smike_s #endif /* DEBUG */
8607c478bd9Sstevel@tonic-gate 		} else {
8617c478bd9Sstevel@tonic-gate 			/*
8627c478bd9Sstevel@tonic-gate 			 * pc sample could not be assigned to function;
8637c478bd9Sstevel@tonic-gate 			 * probably in a PLT
8647c478bd9Sstevel@tonic-gate 			 */
8657c478bd9Sstevel@tonic-gate 			pcptr++;
8667c478bd9Sstevel@tonic-gate 		}
8677c478bd9Sstevel@tonic-gate 	}
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate #ifdef DEBUG
8707c478bd9Sstevel@tonic-gate 	if (debug & PCSMPLDEBUG) {
87192ed1782Smike_s 		(void) printf(
87292ed1782Smike_s 		    "[assign_pcsamples] %ld hits in\n", n_hits_in_module);
87392ed1782Smike_s 		(void) printf("                   `%s'\n", module->name);
8747c478bd9Sstevel@tonic-gate 	}
87592ed1782Smike_s #endif /* DEBUG */
8767c478bd9Sstevel@tonic-gate }
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate int
pc_cmp(const void * arg1,const void * arg2)87992ed1782Smike_s pc_cmp(const void *arg1, const void *arg2)
8807c478bd9Sstevel@tonic-gate {
88192ed1782Smike_s 	Address *pc1 = (Address *)arg1;
88292ed1782Smike_s 	Address *pc2 = (Address *)arg2;
88392ed1782Smike_s 
8847c478bd9Sstevel@tonic-gate 	if (*pc1 > *pc2)
8857c478bd9Sstevel@tonic-gate 		return (1);
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate 	if (*pc1 < *pc2)
8887c478bd9Sstevel@tonic-gate 		return (-1);
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 	return (0);
8917c478bd9Sstevel@tonic-gate }
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate static void
process_pcsamples(ProfBuffer * bufp)89492ed1782Smike_s process_pcsamples(ProfBuffer *bufp)
8957c478bd9Sstevel@tonic-gate {
8967c478bd9Sstevel@tonic-gate 	Address		*pc_samples;
8977c478bd9Sstevel@tonic-gate 	mod_info_t	*mi;
8987c478bd9Sstevel@tonic-gate 	caddr_t		p;
8997c478bd9Sstevel@tonic-gate 	size_t		chunk_size, nelem_read, nelem_to_read;
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate #ifdef DEBUG
9027c478bd9Sstevel@tonic-gate 	if (debug & PCSMPLDEBUG) {
90392ed1782Smike_s 		(void) printf(
90492ed1782Smike_s 		    "[process_pcsamples] number of pcsamples = %lld\n",
90592ed1782Smike_s 		    bufp->bufsize);
9067c478bd9Sstevel@tonic-gate 	}
90792ed1782Smike_s #endif /* DEBUG */
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	/* buffer with no pc samples ? */
9107c478bd9Sstevel@tonic-gate 	if (bufp->bufsize == 0)
9117c478bd9Sstevel@tonic-gate 		return;
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 	/*
9147c478bd9Sstevel@tonic-gate 	 * If we're processing pcsamples of a profile sum, we could have
9157c478bd9Sstevel@tonic-gate 	 * more than PROF_BUFFER_SIZE number of samples. In such a case,
9167c478bd9Sstevel@tonic-gate 	 * we must read the pcsamples in chunks.
9177c478bd9Sstevel@tonic-gate 	 */
9187c478bd9Sstevel@tonic-gate 	if ((chunk_size = bufp->bufsize) > PROF_BUFFER_SIZE)
9197c478bd9Sstevel@tonic-gate 		chunk_size = PROF_BUFFER_SIZE;
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 	/* Allocate for the pcsample chunk */
9227c478bd9Sstevel@tonic-gate 	pc_samples = (Address *) calloc(chunk_size, sizeof (Address));
9237c478bd9Sstevel@tonic-gate 	if (pc_samples == NULL) {
92492ed1782Smike_s 		(void) fprintf(stderr, "%s: no room for %d sample pc's\n",
9251dd08564Sab 		    whoami, chunk_size);
9267c478bd9Sstevel@tonic-gate 		exit(EX_OSERR);
9277c478bd9Sstevel@tonic-gate 	}
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 	/* Copy the current set of pcsamples */
9307c478bd9Sstevel@tonic-gate 	nelem_read = 0;
9317c478bd9Sstevel@tonic-gate 	nelem_to_read = bufp->bufsize;
93292ed1782Smike_s 	p = (char *)bufp + bufp->buffer;
9337c478bd9Sstevel@tonic-gate 
9347c478bd9Sstevel@tonic-gate 	while (nelem_read < nelem_to_read) {
93592ed1782Smike_s 		(void) memcpy((void *) pc_samples, p,
93692ed1782Smike_s 		    chunk_size * sizeof (Address));
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 		/* Sort the pc samples */
93992ed1782Smike_s 		qsort(pc_samples, chunk_size, sizeof (Address), pc_cmp);
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 		/*
9427c478bd9Sstevel@tonic-gate 		 * Assign pcsamples to functions in the currently active
9437c478bd9Sstevel@tonic-gate 		 * module list
9447c478bd9Sstevel@tonic-gate 		 */
9457c478bd9Sstevel@tonic-gate 		for (mi = &modules; mi; mi = mi->next) {
9467c478bd9Sstevel@tonic-gate 			if (mi->active == FALSE)
9477c478bd9Sstevel@tonic-gate 				continue;
9487c478bd9Sstevel@tonic-gate 			assign_pcsamples(mi, pc_samples, chunk_size);
9497c478bd9Sstevel@tonic-gate 		}
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 		p += (chunk_size * sizeof (Address));
9527c478bd9Sstevel@tonic-gate 		nelem_read += chunk_size;
9537c478bd9Sstevel@tonic-gate 
9547c478bd9Sstevel@tonic-gate 		if ((nelem_to_read - nelem_read) < chunk_size)
9557c478bd9Sstevel@tonic-gate 			chunk_size = nelem_to_read - nelem_read;
9567c478bd9Sstevel@tonic-gate 	}
9577c478bd9Sstevel@tonic-gate 
9587c478bd9Sstevel@tonic-gate 	free(pc_samples);
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 	/* Update total number of pcsamples read so far */
9617c478bd9Sstevel@tonic-gate 	n_pcsamples += bufp->bufsize;
9627c478bd9Sstevel@tonic-gate }
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate static mod_info_t *
find_module(Address addr)9657c478bd9Sstevel@tonic-gate find_module(Address addr)
9667c478bd9Sstevel@tonic-gate {
9677c478bd9Sstevel@tonic-gate 	mod_info_t	*mi;
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
9707c478bd9Sstevel@tonic-gate 		if (mi->active == FALSE)
9717c478bd9Sstevel@tonic-gate 			continue;
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 		if (addr >= mi->load_base && addr < mi->load_end)
9747c478bd9Sstevel@tonic-gate 			return (mi);
9757c478bd9Sstevel@tonic-gate 	}
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate 	return (NULL);
9787c478bd9Sstevel@tonic-gate }
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate static void
process_cgraph(ProfCallGraph * cgp)98192ed1782Smike_s process_cgraph(ProfCallGraph *cgp)
9827c478bd9Sstevel@tonic-gate {
9837c478bd9Sstevel@tonic-gate 	struct rawarc	arc;
9847c478bd9Sstevel@tonic-gate 	mod_info_t	*callee_mi, *caller_mi;
9857c478bd9Sstevel@tonic-gate 	ProfFunction	*calleep, *callerp;
9867c478bd9Sstevel@tonic-gate 	Index		caller_off, callee_off;
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	/*
9897c478bd9Sstevel@tonic-gate 	 * Note that *callee_off* increment in the for loop below
9907c478bd9Sstevel@tonic-gate 	 * uses *calleep* and *calleep* doesn't get set until the for loop
9917c478bd9Sstevel@tonic-gate 	 * is entered. We don't expect the increment to be executed before
9927c478bd9Sstevel@tonic-gate 	 * the loop body is executed atleast once, so this should be ok.
9937c478bd9Sstevel@tonic-gate 	 */
9947c478bd9Sstevel@tonic-gate 	for (callee_off = cgp->functions; callee_off;
9951dd08564Sab 	    callee_off = calleep->next_to) {
9967c478bd9Sstevel@tonic-gate 
99792ed1782Smike_s 		/* LINTED: pointer cast */
99892ed1782Smike_s 		calleep = (ProfFunction *)((char *)cgp + callee_off);
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate 		/*
10017c478bd9Sstevel@tonic-gate 		 * We could choose either to sort the {caller, callee}
10027c478bd9Sstevel@tonic-gate 		 * list twice and assign callee/caller to modules or inspect
10037c478bd9Sstevel@tonic-gate 		 * each callee/caller in the active modules list. Since
10047c478bd9Sstevel@tonic-gate 		 * the modules list is usually very small, we'l choose the
10057c478bd9Sstevel@tonic-gate 		 * latter.
10067c478bd9Sstevel@tonic-gate 		 */
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate 		/*
10097c478bd9Sstevel@tonic-gate 		 * If we cannot identify a callee with a module, there's
10107c478bd9Sstevel@tonic-gate 		 * no use worrying about who called it.
10117c478bd9Sstevel@tonic-gate 		 */
10127c478bd9Sstevel@tonic-gate 		if ((callee_mi = find_module(calleep->topc)) == NULL) {
10137c478bd9Sstevel@tonic-gate #ifdef DEBUG
10147c478bd9Sstevel@tonic-gate 			if (debug & CGRAPHDEBUG) {
101592ed1782Smike_s 				(void) printf(
101692ed1782Smike_s 				    "[process_cgraph] callee %#llx missed\n",
101792ed1782Smike_s 				    calleep->topc);
10187c478bd9Sstevel@tonic-gate 			}
101992ed1782Smike_s #endif /* DEBUG */
10207c478bd9Sstevel@tonic-gate 			continue;
10217c478bd9Sstevel@tonic-gate 		} else
10227c478bd9Sstevel@tonic-gate 			arc.raw_selfpc = calleep->topc;
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 		for (caller_off = callee_off; caller_off;
10251dd08564Sab 		    caller_off = callerp->next_from)  {
10267c478bd9Sstevel@tonic-gate 
102792ed1782Smike_s 			/* LINTED: pointer cast */
102892ed1782Smike_s 			callerp = (ProfFunction *)((char *)cgp + caller_off);
10297c478bd9Sstevel@tonic-gate 			if ((caller_mi = find_module(callerp->frompc)) ==
10301dd08564Sab 			    NULL) {
10317c478bd9Sstevel@tonic-gate #ifdef DEBUG
10327c478bd9Sstevel@tonic-gate 				if (debug & CGRAPHDEBUG) {
103392ed1782Smike_s 					(void) printf(
103492ed1782Smike_s 					    "[process_cgraph] caller %#llx "
103592ed1782Smike_s 					    "missed\n", callerp->frompc);
10367c478bd9Sstevel@tonic-gate 				}
103792ed1782Smike_s #endif /* DEBUG */
10387c478bd9Sstevel@tonic-gate 				continue;
10397c478bd9Sstevel@tonic-gate 			}
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 			arc.raw_frompc = callerp->frompc;
10427c478bd9Sstevel@tonic-gate 			arc.raw_count = callerp->count;
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate #ifdef DEBUG
10457c478bd9Sstevel@tonic-gate 			if (debug & CGRAPHDEBUG) {
104692ed1782Smike_s 				(void) printf(
104792ed1782Smike_s 				    "[process_cgraph] arc <%#llx, %#llx, "
104892ed1782Smike_s 				    "%lld>\n", arc.raw_frompc, arc.raw_selfpc,
104992ed1782Smike_s 				    arc.raw_count);
10507c478bd9Sstevel@tonic-gate 			}
105192ed1782Smike_s #endif /* DEBUG */
10527c478bd9Sstevel@tonic-gate 			tally(caller_mi, callee_mi, &arc);
10537c478bd9Sstevel@tonic-gate 		}
10547c478bd9Sstevel@tonic-gate 	}
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate #ifdef DEBUG
10577c478bd9Sstevel@tonic-gate 	puts("\n");
105892ed1782Smike_s #endif /* DEBUG */
10597c478bd9Sstevel@tonic-gate }
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate /*
10627c478bd9Sstevel@tonic-gate  * Two modules overlap each other if they don't lie completely *outside*
10637c478bd9Sstevel@tonic-gate  * each other.
10647c478bd9Sstevel@tonic-gate  */
10657c478bd9Sstevel@tonic-gate static bool
does_overlap(ProfModule * new,mod_info_t * old)10667c478bd9Sstevel@tonic-gate does_overlap(ProfModule *new, mod_info_t *old)
10677c478bd9Sstevel@tonic-gate {
10687c478bd9Sstevel@tonic-gate 	/* case 1: new module lies completely *before* the old one */
10697c478bd9Sstevel@tonic-gate 	if (new->startaddr < old->load_base && new->endaddr <= old->load_base)
10707c478bd9Sstevel@tonic-gate 		return (FALSE);
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 	/* case 2: new module lies completely *after* the old one */
10737c478bd9Sstevel@tonic-gate 	if (new->startaddr >= old->load_end && new->endaddr >= old->load_end)
10747c478bd9Sstevel@tonic-gate 		return (FALSE);
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate 	/* probably a dlopen: the modules overlap each other */
10777c478bd9Sstevel@tonic-gate 	return (TRUE);
10787c478bd9Sstevel@tonic-gate }
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate static bool
is_same_as_aout(char * modpath,struct stat * buf)10817c478bd9Sstevel@tonic-gate is_same_as_aout(char *modpath, struct stat *buf)
10827c478bd9Sstevel@tonic-gate {
10837c478bd9Sstevel@tonic-gate 	if (stat(modpath, buf) == -1) {
108492ed1782Smike_s 		(void) fprintf(stderr, "%s: can't get info on `%s'\n",
10851dd08564Sab 		    whoami, modpath);
10867c478bd9Sstevel@tonic-gate 		exit(EX_NOINPUT);
10877c478bd9Sstevel@tonic-gate 	}
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate 	if ((buf->st_dev == aout_info.dev) && (buf->st_ino == aout_info.ino))
10907c478bd9Sstevel@tonic-gate 		return (TRUE);
10917c478bd9Sstevel@tonic-gate 	else
10927c478bd9Sstevel@tonic-gate 		return (FALSE);
10937c478bd9Sstevel@tonic-gate }
10947c478bd9Sstevel@tonic-gate 
10957c478bd9Sstevel@tonic-gate static void
process_modules(ProfModuleList * modlp)109692ed1782Smike_s process_modules(ProfModuleList *modlp)
10977c478bd9Sstevel@tonic-gate {
10987c478bd9Sstevel@tonic-gate 	ProfModule	*newmodp;
10997c478bd9Sstevel@tonic-gate 	mod_info_t	*mi, *last, *new_module;
110092ed1782Smike_s 	char		*so_path;
11017c478bd9Sstevel@tonic-gate 	bool		more_modules = TRUE;
11027c478bd9Sstevel@tonic-gate 	struct stat	so_statbuf;
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate #ifdef DEBUG
11057c478bd9Sstevel@tonic-gate 	if (debug & MODULEDEBUG) {
110692ed1782Smike_s 		(void) printf("[process_modules] module obj version %u\n",
11071dd08564Sab 		    modlp->version);
11087c478bd9Sstevel@tonic-gate 	}
110992ed1782Smike_s #endif /* DEBUG */
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	/* Check version of module type object */
11127c478bd9Sstevel@tonic-gate 	if (modlp->version > PROF_MODULES_VER) {
111392ed1782Smike_s 		(void) fprintf(stderr, "%s: version %d for module type objects"
11141dd08564Sab 		    "is not supported\n", whoami, modlp->version);
11157c478bd9Sstevel@tonic-gate 		exit(EX_SOFTWARE);
11167c478bd9Sstevel@tonic-gate 	}
11177c478bd9Sstevel@tonic-gate 
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate 	/*
11207c478bd9Sstevel@tonic-gate 	 * Scan the PROF_MODULES_T list and add modules to current list
11217c478bd9Sstevel@tonic-gate 	 * of modules, if they're not present already
11227c478bd9Sstevel@tonic-gate 	 */
112392ed1782Smike_s 	/* LINTED: pointer cast */
112492ed1782Smike_s 	newmodp = (ProfModule *)((char *)modlp + modlp->modules);
11257c478bd9Sstevel@tonic-gate 	do {
11267c478bd9Sstevel@tonic-gate 		/*
11277c478bd9Sstevel@tonic-gate 		 * Since the prog could've been renamed after its run, we
11287c478bd9Sstevel@tonic-gate 		 * should see if this overlaps a.out. If it does, it is
11297c478bd9Sstevel@tonic-gate 		 * probably the renamed aout. We should also skip any other
11307c478bd9Sstevel@tonic-gate 		 * non-sharedobj's that we see (or should we report an error ?)
11317c478bd9Sstevel@tonic-gate 		 */
113292ed1782Smike_s 		so_path = (caddr_t)modlp + newmodp->path;
11337c478bd9Sstevel@tonic-gate 		if (does_overlap(newmodp, &modules) ||
11341dd08564Sab 		    is_same_as_aout(so_path, &so_statbuf) ||
11351dd08564Sab 		    (!is_shared_obj(so_path))) {
11367c478bd9Sstevel@tonic-gate 
11377c478bd9Sstevel@tonic-gate 			if (!newmodp->next)
11387c478bd9Sstevel@tonic-gate 				more_modules = FALSE;
11397c478bd9Sstevel@tonic-gate 
114092ed1782Smike_s 			/* LINTED: pointer cast */
11417c478bd9Sstevel@tonic-gate 			newmodp = (ProfModule *)
114292ed1782Smike_s 			    ((caddr_t)modlp + newmodp->next);
11437c478bd9Sstevel@tonic-gate #ifdef DEBUG
11447c478bd9Sstevel@tonic-gate 			if (debug & MODULEDEBUG) {
114592ed1782Smike_s 				(void) printf(
114692ed1782Smike_s 				    "[process_modules] `%s'\n", so_path);
114792ed1782Smike_s 				(void) printf("                  skipped\n");
11487c478bd9Sstevel@tonic-gate 			}
114992ed1782Smike_s #endif /* DEBUG */
11507c478bd9Sstevel@tonic-gate 			continue;
11517c478bd9Sstevel@tonic-gate 		}
11527c478bd9Sstevel@tonic-gate #ifdef DEBUG
11537c478bd9Sstevel@tonic-gate 		if (debug & MODULEDEBUG)
115492ed1782Smike_s 			(void) printf("[process_modules] `%s'...\n", so_path);
115592ed1782Smike_s #endif /* DEBUG */
11567c478bd9Sstevel@tonic-gate 
11577c478bd9Sstevel@tonic-gate 		/*
11587c478bd9Sstevel@tonic-gate 		 * Check all modules (leave the first one, 'cos that
11597c478bd9Sstevel@tonic-gate 		 * is the program executable info). If this module is already
11607c478bd9Sstevel@tonic-gate 		 * there in the list, update the load addresses and proceed.
11617c478bd9Sstevel@tonic-gate 		 */
11627c478bd9Sstevel@tonic-gate 		last = &modules;
116392ed1782Smike_s 		while ((mi = last->next) != NULL) {
11647c478bd9Sstevel@tonic-gate 			/*
11657c478bd9Sstevel@tonic-gate 			 * We expect the full pathname for all shared objects
11667c478bd9Sstevel@tonic-gate 			 * needed by the program executable. In this case, we
11677c478bd9Sstevel@tonic-gate 			 * simply need to compare the paths to see if they are
11687c478bd9Sstevel@tonic-gate 			 * the same file.
11697c478bd9Sstevel@tonic-gate 			 */
11707c478bd9Sstevel@tonic-gate 			if (strcmp(mi->name, so_path) == 0)
1171