xref: /onnv-gate/usr/src/cmd/sgs/gprof/common/gprof.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include	<sysexits.h>
30*0Sstevel@tonic-gate #include	<stdlib.h>
31*0Sstevel@tonic-gate #include	<unistd.h>
32*0Sstevel@tonic-gate #include	"gprof.h"
33*0Sstevel@tonic-gate #include	"profile.h"
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate char		*whoami = "gprof";
36*0Sstevel@tonic-gate static pctype	lowpc, highpc;		/* range profiled, in UNIT's */
37*0Sstevel@tonic-gate 
38*0Sstevel@tonic-gate /*
39*0Sstevel@tonic-gate  *	things which get -E excluded by default.
40*0Sstevel@tonic-gate  */
41*0Sstevel@tonic-gate static char *defaultEs[] = {
42*0Sstevel@tonic-gate 	"mcount",
43*0Sstevel@tonic-gate 	"__mcleanup",
44*0Sstevel@tonic-gate 	0
45*0Sstevel@tonic-gate };
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate #ifdef DEBUG
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate static char *objname[] = {
50*0Sstevel@tonic-gate 	"<invalid object>",
51*0Sstevel@tonic-gate 	"PROF_BUFFER_T",
52*0Sstevel@tonic-gate 	"PROF_CALLGRAPH_T",
53*0Sstevel@tonic-gate 	"PROF_MODULES_T",
54*0Sstevel@tonic-gate 	0
55*0Sstevel@tonic-gate };
56*0Sstevel@tonic-gate #define	MAX_OBJTYPES	3
57*0Sstevel@tonic-gate 
58*0Sstevel@tonic-gate #endif DEBUG
59*0Sstevel@tonic-gate 
60*0Sstevel@tonic-gate void
61*0Sstevel@tonic-gate done()
62*0Sstevel@tonic-gate {
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate 	exit(EX_OK);
65*0Sstevel@tonic-gate }
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate static pctype
68*0Sstevel@tonic-gate max(pctype a, pctype b)
69*0Sstevel@tonic-gate {
70*0Sstevel@tonic-gate 	if (a > b)
71*0Sstevel@tonic-gate 		return (a);
72*0Sstevel@tonic-gate 	return (b);
73*0Sstevel@tonic-gate }
74*0Sstevel@tonic-gate 
75*0Sstevel@tonic-gate static pctype
76*0Sstevel@tonic-gate min(pctype a, pctype b)
77*0Sstevel@tonic-gate {
78*0Sstevel@tonic-gate 	if (a < b)
79*0Sstevel@tonic-gate 		return (a);
80*0Sstevel@tonic-gate 	return (b);
81*0Sstevel@tonic-gate }
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate /*
84*0Sstevel@tonic-gate  *	calculate scaled entry point addresses (to save time in asgnsamples),
85*0Sstevel@tonic-gate  *	and possibly push the scaled entry points over the entry mask,
86*0Sstevel@tonic-gate  *	if it turns out that the entry point is in one bucket and the code
87*0Sstevel@tonic-gate  *	for a routine is in the next bucket.
88*0Sstevel@tonic-gate  *
89*0Sstevel@tonic-gate  */
90*0Sstevel@tonic-gate static void
91*0Sstevel@tonic-gate alignentries()
92*0Sstevel@tonic-gate {
93*0Sstevel@tonic-gate 	register struct nl *	nlp;
94*0Sstevel@tonic-gate #ifdef DEBUG
95*0Sstevel@tonic-gate 	pctype			bucket_of_entry;
96*0Sstevel@tonic-gate 	pctype			bucket_of_code;
97*0Sstevel@tonic-gate #endif DEBUG
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate 	/* for old-style gmon.out, nameslist is only in modules.nl */
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate 	for (nlp = modules.nl; nlp < modules.npe; nlp++) {
102*0Sstevel@tonic-gate 		nlp->svalue = nlp->value / sizeof (UNIT);
103*0Sstevel@tonic-gate #ifdef DEBUG
104*0Sstevel@tonic-gate 		bucket_of_entry = (nlp->svalue - lowpc) / scale;
105*0Sstevel@tonic-gate 		bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
106*0Sstevel@tonic-gate 		if (bucket_of_entry < bucket_of_code) {
107*0Sstevel@tonic-gate 			if (debug & SAMPLEDEBUG) {
108*0Sstevel@tonic-gate 				printf("[alignentries] pushing svalue 0x%llx "
109*0Sstevel@tonic-gate 				"to 0x%llx\n", nlp->svalue,
110*0Sstevel@tonic-gate 				nlp->svalue + UNITS_TO_CODE);
111*0Sstevel@tonic-gate 			}
112*0Sstevel@tonic-gate 		}
113*0Sstevel@tonic-gate #endif DEBUG
114*0Sstevel@tonic-gate 	}
115*0Sstevel@tonic-gate }
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate /*
118*0Sstevel@tonic-gate  *	old-style gmon.out
119*0Sstevel@tonic-gate  *	------------------
120*0Sstevel@tonic-gate  *
121*0Sstevel@tonic-gate  *	Assign samples to the procedures to which they belong.
122*0Sstevel@tonic-gate  *
123*0Sstevel@tonic-gate  *	There are three cases as to where pcl and pch can be
124*0Sstevel@tonic-gate  *	with respect to the routine entry addresses svalue0 and svalue1
125*0Sstevel@tonic-gate  *	as shown in the following diagram.  overlap computes the
126*0Sstevel@tonic-gate  *	distance between the arrows, the fraction of the sample
127*0Sstevel@tonic-gate  *	that is to be credited to the routine which starts at svalue0.
128*0Sstevel@tonic-gate  *
129*0Sstevel@tonic-gate  *	    svalue0                                         svalue1
130*0Sstevel@tonic-gate  *	       |                                               |
131*0Sstevel@tonic-gate  *	       v                                               v
132*0Sstevel@tonic-gate  *
133*0Sstevel@tonic-gate  *	       +-----------------------------------------------+
134*0Sstevel@tonic-gate  *	       |					       |
135*0Sstevel@tonic-gate  *	  |  ->|    |<-		->|         |<-		->|    |<-  |
136*0Sstevel@tonic-gate  *	  |         |		  |         |		  |         |
137*0Sstevel@tonic-gate  *	  +---------+		  +---------+		  +---------+
138*0Sstevel@tonic-gate  *
139*0Sstevel@tonic-gate  *	  ^         ^		  ^         ^		  ^         ^
140*0Sstevel@tonic-gate  *	  |         |		  |         |		  |         |
141*0Sstevel@tonic-gate  *	 pcl       pch		 pcl       pch		 pcl       pch
142*0Sstevel@tonic-gate  *
143*0Sstevel@tonic-gate  *	For the vax we assert that samples will never fall in the first
144*0Sstevel@tonic-gate  *	two bytes of any routine, since that is the entry mask,
145*0Sstevel@tonic-gate  *	thus we give call alignentries() to adjust the entry points if
146*0Sstevel@tonic-gate  *	the entry mask falls in one bucket but the code for the routine
147*0Sstevel@tonic-gate  *	doesn't start until the next bucket.  In conjunction with the
148*0Sstevel@tonic-gate  *	alignment of routine addresses, this should allow us to have
149*0Sstevel@tonic-gate  *	only one sample for every four bytes of text space and never
150*0Sstevel@tonic-gate  *	have any overlap (the two end cases, above).
151*0Sstevel@tonic-gate  */
152*0Sstevel@tonic-gate static void
153*0Sstevel@tonic-gate asgnsamples()
154*0Sstevel@tonic-gate {
155*0Sstevel@tonic-gate 	sztype		i, j;
156*0Sstevel@tonic-gate 	unsigned_UNIT	ccnt;
157*0Sstevel@tonic-gate 	double		time;
158*0Sstevel@tonic-gate 	pctype		pcl, pch;
159*0Sstevel@tonic-gate 	pctype		overlap;
160*0Sstevel@tonic-gate 	pctype		svalue0, svalue1;
161*0Sstevel@tonic-gate 
162*0Sstevel@tonic-gate 	extern mod_info_t	modules;
163*0Sstevel@tonic-gate 	nltype		*nl = modules.nl;
164*0Sstevel@tonic-gate 	sztype		nname = modules.nname;
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 	/* read samples and assign to namelist symbols */
167*0Sstevel@tonic-gate 	scale = highpc - lowpc;
168*0Sstevel@tonic-gate 	scale /= nsamples;
169*0Sstevel@tonic-gate 	alignentries();
170*0Sstevel@tonic-gate 	for (i = 0, j = 1; i < nsamples; i++) {
171*0Sstevel@tonic-gate 		ccnt = samples[i];
172*0Sstevel@tonic-gate 		if (ccnt == 0)
173*0Sstevel@tonic-gate 			continue;
174*0Sstevel@tonic-gate 		pcl = lowpc + scale * i;
175*0Sstevel@tonic-gate 		pch = lowpc + scale * (i + 1);
176*0Sstevel@tonic-gate 		time = ccnt;
177*0Sstevel@tonic-gate #ifdef DEBUG
178*0Sstevel@tonic-gate 		if (debug & SAMPLEDEBUG) {
179*0Sstevel@tonic-gate 			printf("[asgnsamples] pcl 0x%llx pch 0x%llx ccnt %d\n",
180*0Sstevel@tonic-gate 			    pcl, pch, ccnt);
181*0Sstevel@tonic-gate 		}
182*0Sstevel@tonic-gate #endif DEBUG
183*0Sstevel@tonic-gate 		totime += time;
184*0Sstevel@tonic-gate 		for (j = (j ? j - 1 : 0); j < nname; j++) {
185*0Sstevel@tonic-gate 			svalue0 = nl[j].svalue;
186*0Sstevel@tonic-gate 			svalue1 = nl[j+1].svalue;
187*0Sstevel@tonic-gate 			/*
188*0Sstevel@tonic-gate 			 *	if high end of tick is below entry address,
189*0Sstevel@tonic-gate 			 *	go for next tick.
190*0Sstevel@tonic-gate 			 */
191*0Sstevel@tonic-gate 			if (pch < svalue0)
192*0Sstevel@tonic-gate 				break;
193*0Sstevel@tonic-gate 			/*
194*0Sstevel@tonic-gate 			 *	if low end of tick into next routine,
195*0Sstevel@tonic-gate 			 *	go for next routine.
196*0Sstevel@tonic-gate 			 */
197*0Sstevel@tonic-gate 			if (pcl >= svalue1)
198*0Sstevel@tonic-gate 				continue;
199*0Sstevel@tonic-gate 			overlap = min(pch, svalue1) - max(pcl, svalue0);
200*0Sstevel@tonic-gate 			if (overlap != 0) {
201*0Sstevel@tonic-gate #ifdef DEBUG
202*0Sstevel@tonic-gate 				if (debug & SAMPLEDEBUG) {
203*0Sstevel@tonic-gate 					printf("[asgnsamples] "
204*0Sstevel@tonic-gate 					    "(0x%llx->0x%llx-0x%llx) %s gets "
205*0Sstevel@tonic-gate 					    "%f ticks %lld overlap\n",
206*0Sstevel@tonic-gate 					    nl[j].value/sizeof (UNIT), svalue0,
207*0Sstevel@tonic-gate 					    svalue1, nl[j].name,
208*0Sstevel@tonic-gate 					    overlap * time / scale, overlap);
209*0Sstevel@tonic-gate 				}
210*0Sstevel@tonic-gate #endif DEBUG
211*0Sstevel@tonic-gate 				nl[j].time += overlap * time / scale;
212*0Sstevel@tonic-gate 			}
213*0Sstevel@tonic-gate 		}
214*0Sstevel@tonic-gate 	}
215*0Sstevel@tonic-gate #ifdef DEBUG
216*0Sstevel@tonic-gate 	if (debug & SAMPLEDEBUG) {
217*0Sstevel@tonic-gate 		printf("[asgnsamples] totime %f\n", totime);
218*0Sstevel@tonic-gate 	}
219*0Sstevel@tonic-gate #endif DEBUG
220*0Sstevel@tonic-gate }
221*0Sstevel@tonic-gate 
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate static void
224*0Sstevel@tonic-gate dump_callgraph(FILE *fp, char *filename,
225*0Sstevel@tonic-gate 				unsigned long tarcs, unsigned long ncallees)
226*0Sstevel@tonic-gate {
227*0Sstevel@tonic-gate 	ProfCallGraph		prof_cgraph;
228*0Sstevel@tonic-gate 	ProfFunction		prof_func;
229*0Sstevel@tonic-gate 	register arctype	*arcp;
230*0Sstevel@tonic-gate 	mod_info_t		*mi;
231*0Sstevel@tonic-gate 	nltype			*nlp;
232*0Sstevel@tonic-gate 	size_t			cur_offset;
233*0Sstevel@tonic-gate 	unsigned long		caller_id = 0, callee_id = 0;
234*0Sstevel@tonic-gate 
235*0Sstevel@tonic-gate 	/*
236*0Sstevel@tonic-gate 	 * Write the callgraph header
237*0Sstevel@tonic-gate 	 */
238*0Sstevel@tonic-gate 	prof_cgraph.type = PROF_CALLGRAPH_T;
239*0Sstevel@tonic-gate 	prof_cgraph.version = PROF_CALLGRAPH_VER;
240*0Sstevel@tonic-gate 	prof_cgraph.functions = PROFCGRAPH_SZ;
241*0Sstevel@tonic-gate 	prof_cgraph.size = PROFCGRAPH_SZ + tarcs * PROFFUNC_SZ;
242*0Sstevel@tonic-gate 	if (fwrite(&prof_cgraph, sizeof (ProfCallGraph), 1, fp) != 1) {
243*0Sstevel@tonic-gate 		perror(filename);
244*0Sstevel@tonic-gate 		exit(EX_IOERR);
245*0Sstevel@tonic-gate 	}
246*0Sstevel@tonic-gate 	if (CGRAPH_FILLER)
247*0Sstevel@tonic-gate 		fseek(fp, CGRAPH_FILLER, SEEK_CUR);
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 	/* Current offset inside the callgraph object */
250*0Sstevel@tonic-gate 	cur_offset = prof_cgraph.functions;
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
253*0Sstevel@tonic-gate 		for (nlp = mi->nl; nlp < mi->npe; nlp++) {
254*0Sstevel@tonic-gate 			if (nlp->ncallers == 0)
255*0Sstevel@tonic-gate 				continue;
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 			/* If this is the last callee, set next_to to 0 */
258*0Sstevel@tonic-gate 			callee_id++;
259*0Sstevel@tonic-gate 			if (callee_id == ncallees)
260*0Sstevel@tonic-gate 				prof_func.next_to = 0;
261*0Sstevel@tonic-gate 			else {
262*0Sstevel@tonic-gate 				prof_func.next_to = cur_offset +
263*0Sstevel@tonic-gate 						    nlp->ncallers * PROFFUNC_SZ;
264*0Sstevel@tonic-gate 			}
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate 			/*
267*0Sstevel@tonic-gate 			 * Dump this callee's raw arc information with all
268*0Sstevel@tonic-gate 			 * its callers
269*0Sstevel@tonic-gate 			 */
270*0Sstevel@tonic-gate 			caller_id = 1;
271*0Sstevel@tonic-gate 			for (arcp = nlp->parents; arcp;
272*0Sstevel@tonic-gate 					    arcp = arcp->arc_parentlist) {
273*0Sstevel@tonic-gate 				/*
274*0Sstevel@tonic-gate 				 * If no more callers for this callee, set
275*0Sstevel@tonic-gate 				 * next_from to 0
276*0Sstevel@tonic-gate 				 */
277*0Sstevel@tonic-gate 				if (caller_id == nlp->ncallers)
278*0Sstevel@tonic-gate 					prof_func.next_from = 0;
279*0Sstevel@tonic-gate 				else {
280*0Sstevel@tonic-gate 					prof_func.next_from = cur_offset +
281*0Sstevel@tonic-gate 								PROFFUNC_SZ;
282*0Sstevel@tonic-gate 				}
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 				prof_func.frompc =
285*0Sstevel@tonic-gate 					arcp->arc_parentp->module->load_base +
286*0Sstevel@tonic-gate 					(arcp->arc_parentp->value -
287*0Sstevel@tonic-gate 					arcp->arc_parentp->module->txt_origin);
288*0Sstevel@tonic-gate 				prof_func.topc =
289*0Sstevel@tonic-gate 					mi->load_base +
290*0Sstevel@tonic-gate 						(nlp->value - mi->txt_origin);
291*0Sstevel@tonic-gate 				prof_func.count = arcp->arc_count;
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 				if (fwrite(&prof_func, sizeof (ProfFunction),
295*0Sstevel@tonic-gate 								1, fp) != 1) {
296*0Sstevel@tonic-gate 					perror(filename);
297*0Sstevel@tonic-gate 					exit(EX_IOERR);
298*0Sstevel@tonic-gate 				}
299*0Sstevel@tonic-gate 				if (FUNC_FILLER)
300*0Sstevel@tonic-gate 					fseek(fp, FUNC_FILLER, SEEK_CUR);
301*0Sstevel@tonic-gate 
302*0Sstevel@tonic-gate 				cur_offset += PROFFUNC_SZ;
303*0Sstevel@tonic-gate 				caller_id++;
304*0Sstevel@tonic-gate 			}
305*0Sstevel@tonic-gate 		} /* for nlp... */
306*0Sstevel@tonic-gate 	} /* for mi... */
307*0Sstevel@tonic-gate }
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate /*
310*0Sstevel@tonic-gate  * To save all pc-hits in all the gmon.out's is infeasible, as this
311*0Sstevel@tonic-gate  * may become quite huge even with a small number of files to sum.
312*0Sstevel@tonic-gate  * Instead, we'll dump *fictitious hits* to correct functions
313*0Sstevel@tonic-gate  * by scanning module namelists. Again, since this is summing
314*0Sstevel@tonic-gate  * pc-hits, we may have to dump the pcsamples out in chunks if the
315*0Sstevel@tonic-gate  * number of pc-hits is high.
316*0Sstevel@tonic-gate  */
317*0Sstevel@tonic-gate static void
318*0Sstevel@tonic-gate dump_hits(FILE *fp, char *filename, nltype *nlp)
319*0Sstevel@tonic-gate {
320*0Sstevel@tonic-gate 	Address		*p, hitpc;
321*0Sstevel@tonic-gate 	size_t		i, nelem, ntowrite;
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate 	if ((nelem = nlp->nticks) > PROF_BUFFER_SIZE)
324*0Sstevel@tonic-gate 		nelem = PROF_BUFFER_SIZE;
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate 	if ((p = (Address *) calloc(nelem, sizeof (Address))) == NULL) {
327*0Sstevel@tonic-gate 		fprintf(stderr, "%s: no room for %ld pcsamples\n",
328*0Sstevel@tonic-gate 							    whoami, nelem);
329*0Sstevel@tonic-gate 		exit(EX_OSERR);
330*0Sstevel@tonic-gate 	}
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate 	/*
333*0Sstevel@tonic-gate 	 * Set up *fictitious* hits (to function entry) buffer
334*0Sstevel@tonic-gate 	 */
335*0Sstevel@tonic-gate 	hitpc = nlp->module->load_base + (nlp->value - nlp->module->txt_origin);
336*0Sstevel@tonic-gate 	for (i = 0; i < nelem; i++)
337*0Sstevel@tonic-gate 		p[i] = hitpc;
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate 	for (ntowrite = nlp->nticks; ntowrite >= nelem; ntowrite -= nelem) {
340*0Sstevel@tonic-gate 		if (fwrite(p, nelem * sizeof (Address), 1, fp) != 1) {
341*0Sstevel@tonic-gate 			perror(filename);
342*0Sstevel@tonic-gate 			exit(EX_IOERR);
343*0Sstevel@tonic-gate 		}
344*0Sstevel@tonic-gate 	}
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 	if (ntowrite) {
347*0Sstevel@tonic-gate 		if (fwrite(p, ntowrite * sizeof (Address), 1, fp) != 1) {
348*0Sstevel@tonic-gate 			perror(filename);
349*0Sstevel@tonic-gate 			exit(EX_IOERR);
350*0Sstevel@tonic-gate 		}
351*0Sstevel@tonic-gate 	}
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate 	free(p);
354*0Sstevel@tonic-gate }
355*0Sstevel@tonic-gate 
356*0Sstevel@tonic-gate static void
357*0Sstevel@tonic-gate dump_pcsamples(FILE *fp, char *filename,
358*0Sstevel@tonic-gate 				unsigned long *tarcs, unsigned long *ncallees)
359*0Sstevel@tonic-gate {
360*0Sstevel@tonic-gate 	ProfBuffer		prof_buffer;
361*0Sstevel@tonic-gate 	register arctype	*arcp;
362*0Sstevel@tonic-gate 	mod_info_t		*mi;
363*0Sstevel@tonic-gate 	nltype			*nlp;
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate 	prof_buffer.type = PROF_BUFFER_T;
366*0Sstevel@tonic-gate 	prof_buffer.version = PROF_BUFFER_VER;
367*0Sstevel@tonic-gate 	prof_buffer.buffer = PROFBUF_SZ;
368*0Sstevel@tonic-gate 	prof_buffer.bufsize = n_pcsamples;
369*0Sstevel@tonic-gate 	prof_buffer.size = PROFBUF_SZ + n_pcsamples * sizeof (Address);
370*0Sstevel@tonic-gate 	if (fwrite(&prof_buffer, sizeof (ProfBuffer), 1, fp) != 1) {
371*0Sstevel@tonic-gate 		perror(filename);
372*0Sstevel@tonic-gate 		exit(EX_IOERR);
373*0Sstevel@tonic-gate 	}
374*0Sstevel@tonic-gate 	if (BUF_FILLER)
375*0Sstevel@tonic-gate 		fseek(fp, BUF_FILLER, SEEK_CUR);
376*0Sstevel@tonic-gate 
377*0Sstevel@tonic-gate 	*tarcs = 0;
378*0Sstevel@tonic-gate 	*ncallees = 0;
379*0Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
380*0Sstevel@tonic-gate 		for (nlp = mi->nl; nlp < mi->npe; nlp++) {
381*0Sstevel@tonic-gate 			if (nlp->nticks)
382*0Sstevel@tonic-gate 				dump_hits(fp, filename, nlp);
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 			nlp->ncallers = 0;
385*0Sstevel@tonic-gate 			for (arcp = nlp->parents; arcp;
386*0Sstevel@tonic-gate 					    arcp = arcp->arc_parentlist) {
387*0Sstevel@tonic-gate 				(nlp->ncallers)++;
388*0Sstevel@tonic-gate 			}
389*0Sstevel@tonic-gate 
390*0Sstevel@tonic-gate 			if (nlp->ncallers) {
391*0Sstevel@tonic-gate 				(*tarcs) += nlp->ncallers;
392*0Sstevel@tonic-gate 				(*ncallees)++;
393*0Sstevel@tonic-gate 			}
394*0Sstevel@tonic-gate 		}
395*0Sstevel@tonic-gate 	}
396*0Sstevel@tonic-gate }
397*0Sstevel@tonic-gate 
398*0Sstevel@tonic-gate static void
399*0Sstevel@tonic-gate dump_modules(FILE *fp, char *filename, size_t pbuf_sz)
400*0Sstevel@tonic-gate {
401*0Sstevel@tonic-gate 	char		*pbuf, *p;
402*0Sstevel@tonic-gate 	size_t		namelen;
403*0Sstevel@tonic-gate 	Index		off_nxt, off_path;
404*0Sstevel@tonic-gate 	mod_info_t	*mi;
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 	ProfModuleList	prof_modlist;
407*0Sstevel@tonic-gate 	ProfModule	prof_mod;
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 	/* Allocate for path strings buffer */
410*0Sstevel@tonic-gate 	pbuf_sz = CEIL(pbuf_sz, STRUCT_ALIGN);
411*0Sstevel@tonic-gate 	if ((p = pbuf = (char *) calloc(pbuf_sz, sizeof (char))) == NULL) {
412*0Sstevel@tonic-gate 		fprintf(stderr, "%s: no room for %ld bytes\n",
413*0Sstevel@tonic-gate 					    whoami, pbuf_sz * sizeof (char));
414*0Sstevel@tonic-gate 		exit(EX_OSERR);
415*0Sstevel@tonic-gate 	}
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate 	/* Dump out PROF_MODULE_T info for all non-aout modules */
418*0Sstevel@tonic-gate 	prof_modlist.type = PROF_MODULES_T;
419*0Sstevel@tonic-gate 	prof_modlist.version = PROF_MODULES_VER;
420*0Sstevel@tonic-gate 	prof_modlist.modules = PROFMODLIST_SZ;
421*0Sstevel@tonic-gate 	prof_modlist.size = PROFMODLIST_SZ + (n_modules - 1) * PROFMOD_SZ +
422*0Sstevel@tonic-gate 								    pbuf_sz;
423*0Sstevel@tonic-gate 	if (fwrite(&prof_modlist, sizeof (ProfModuleList), 1, fp) != 1) {
424*0Sstevel@tonic-gate 		perror(filename);
425*0Sstevel@tonic-gate 		exit(EX_IOERR);
426*0Sstevel@tonic-gate 	}
427*0Sstevel@tonic-gate 	if (MODLIST_FILLER)
428*0Sstevel@tonic-gate 		fseek(fp, MODLIST_FILLER, SEEK_CUR);
429*0Sstevel@tonic-gate 
430*0Sstevel@tonic-gate 	/*
431*0Sstevel@tonic-gate 	 * Initialize offsets for ProfModule elements.
432*0Sstevel@tonic-gate 	 */
433*0Sstevel@tonic-gate 	off_nxt = PROFMODLIST_SZ + PROFMOD_SZ;
434*0Sstevel@tonic-gate 	off_path = PROFMODLIST_SZ + (n_modules - 1) * PROFMOD_SZ;
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate 	for (mi = modules.next; mi; mi = mi->next) {
437*0Sstevel@tonic-gate 		if (mi->next)
438*0Sstevel@tonic-gate 			prof_mod.next = off_nxt;
439*0Sstevel@tonic-gate 		else
440*0Sstevel@tonic-gate 			prof_mod.next = 0;
441*0Sstevel@tonic-gate 		prof_mod.path = off_path;
442*0Sstevel@tonic-gate 		prof_mod.startaddr = mi->load_base;
443*0Sstevel@tonic-gate 		prof_mod.endaddr = mi->load_end;
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate 		if (fwrite(&prof_mod, sizeof (ProfModule), 1, fp) != 1) {
446*0Sstevel@tonic-gate 			perror(filename);
447*0Sstevel@tonic-gate 			exit(EX_IOERR);
448*0Sstevel@tonic-gate 		}
449*0Sstevel@tonic-gate 
450*0Sstevel@tonic-gate 		if (MOD_FILLER)
451*0Sstevel@tonic-gate 			fseek(fp, MOD_FILLER, SEEK_CUR);
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 		strcpy(p, mi->name);
454*0Sstevel@tonic-gate 		namelen = strlen(mi->name);
455*0Sstevel@tonic-gate 		p += namelen + 1;
456*0Sstevel@tonic-gate 
457*0Sstevel@tonic-gate 		/* Note that offset to every path str need not be aligned */
458*0Sstevel@tonic-gate 		off_nxt += PROFMOD_SZ;
459*0Sstevel@tonic-gate 		off_path += namelen + 1;
460*0Sstevel@tonic-gate 	}
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	/* Write out the module path strings */
463*0Sstevel@tonic-gate 	if (pbuf_sz) {
464*0Sstevel@tonic-gate 		if (fwrite(pbuf, pbuf_sz, 1, fp) != 1) {
465*0Sstevel@tonic-gate 			perror(filename);
466*0Sstevel@tonic-gate 			exit(EX_IOERR);
467*0Sstevel@tonic-gate 		}
468*0Sstevel@tonic-gate 
469*0Sstevel@tonic-gate 		free(pbuf);
470*0Sstevel@tonic-gate 	}
471*0Sstevel@tonic-gate }
472*0Sstevel@tonic-gate 
473*0Sstevel@tonic-gate /*
474*0Sstevel@tonic-gate  * If we have inactive modules, their current load addresses may overlap with
475*0Sstevel@tonic-gate  * active ones, and so we've to assign fictitious, non-overlapping addresses
476*0Sstevel@tonic-gate  * to all modules before we dump them.
477*0Sstevel@tonic-gate  */
478*0Sstevel@tonic-gate static void
479*0Sstevel@tonic-gate fixup_maps(size_t *pathsz)
480*0Sstevel@tonic-gate {
481*0Sstevel@tonic-gate 	unsigned int	n_inactive = 0;
482*0Sstevel@tonic-gate 	Address		lbase, lend;
483*0Sstevel@tonic-gate 	mod_info_t	*mi;
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate 	/* Pick the lowest load address among modules */
486*0Sstevel@tonic-gate 	*pathsz = 0;
487*0Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate 		if (mi->active == FALSE)
490*0Sstevel@tonic-gate 			n_inactive++;
491*0Sstevel@tonic-gate 
492*0Sstevel@tonic-gate 		if (mi == &modules || mi->load_base < lbase)
493*0Sstevel@tonic-gate 			lbase = mi->load_base;
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate 		/*
496*0Sstevel@tonic-gate 		 * Return total path size of non-aout modules only
497*0Sstevel@tonic-gate 		 */
498*0Sstevel@tonic-gate 		if (mi != &modules)
499*0Sstevel@tonic-gate 			*pathsz = (*pathsz) + strlen(mi->name) + 1;
500*0Sstevel@tonic-gate 	}
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate 	/*
503*0Sstevel@tonic-gate 	 * All module info is in fine shape already if there are no
504*0Sstevel@tonic-gate 	 * inactive modules
505*0Sstevel@tonic-gate 	 */
506*0Sstevel@tonic-gate 	if (n_inactive == 0)
507*0Sstevel@tonic-gate 		return;
508*0Sstevel@tonic-gate 
509*0Sstevel@tonic-gate 	/*
510*0Sstevel@tonic-gate 	 * Assign fictitious load addresses to all (non-aout) modules so
511*0Sstevel@tonic-gate 	 * that sum info can be dumped out.
512*0Sstevel@tonic-gate 	 */
513*0Sstevel@tonic-gate 	for (mi = modules.next; mi; mi = mi->next) {
514*0Sstevel@tonic-gate 		lend = lbase + (mi->data_end - mi->txt_origin);
515*0Sstevel@tonic-gate 		if ((lbase < modules.load_base && lend < modules.load_base) ||
516*0Sstevel@tonic-gate 		    (lbase > modules.load_end && lend > modules.load_end)) {
517*0Sstevel@tonic-gate 
518*0Sstevel@tonic-gate 			mi->load_base = lbase;
519*0Sstevel@tonic-gate 			mi->load_end = lend;
520*0Sstevel@tonic-gate 
521*0Sstevel@tonic-gate 			/* just to give an appearance of reality */
522*0Sstevel@tonic-gate 			lbase = CEIL(lend + PGSZ, PGSZ);
523*0Sstevel@tonic-gate 		} else {
524*0Sstevel@tonic-gate 			/*
525*0Sstevel@tonic-gate 			 * can't use this lbase & lend pair, as it
526*0Sstevel@tonic-gate 			 * overlaps with aout's addresses
527*0Sstevel@tonic-gate 			 */
528*0Sstevel@tonic-gate 			mi->load_base = CEIL(modules.load_end + PGSZ, PGSZ);
529*0Sstevel@tonic-gate 			mi->load_end = mi->load_base + (lend - lbase);
530*0Sstevel@tonic-gate 
531*0Sstevel@tonic-gate 			lbase = CEIL(mi->load_end + PGSZ, PGSZ);
532*0Sstevel@tonic-gate 		}
533*0Sstevel@tonic-gate 	}
534*0Sstevel@tonic-gate }
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate static void
537*0Sstevel@tonic-gate dump_gprofhdr(FILE *fp, char *filename)
538*0Sstevel@tonic-gate {
539*0Sstevel@tonic-gate 	ProfHeader	prof_hdr;
540*0Sstevel@tonic-gate 
541*0Sstevel@tonic-gate 	prof_hdr.h_magic = PROF_MAGIC;
542*0Sstevel@tonic-gate 	prof_hdr.h_major_ver = PROF_MAJOR_VERSION;
543*0Sstevel@tonic-gate 	prof_hdr.h_minor_ver = PROF_MINOR_VERSION;
544*0Sstevel@tonic-gate 	prof_hdr.size = PROFHDR_SZ;
545*0Sstevel@tonic-gate 	if (fwrite(&prof_hdr, sizeof (prof_hdr), 1, fp) != 1) {
546*0Sstevel@tonic-gate 		perror(filename);
547*0Sstevel@tonic-gate 		exit(EX_IOERR);
548*0Sstevel@tonic-gate 	}
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate 	if (HDR_FILLER)
551*0Sstevel@tonic-gate 		fseek(fp, HDR_FILLER, SEEK_CUR);
552*0Sstevel@tonic-gate }
553*0Sstevel@tonic-gate 
554*0Sstevel@tonic-gate static void
555*0Sstevel@tonic-gate dumpsum_ostyle(char *sumfile)
556*0Sstevel@tonic-gate {
557*0Sstevel@tonic-gate 	register nltype *nlp;
558*0Sstevel@tonic-gate 	register arctype *arcp;
559*0Sstevel@tonic-gate 	struct rawarc arc;
560*0Sstevel@tonic-gate 	struct rawarc32 arc32;
561*0Sstevel@tonic-gate 	FILE *sfile;
562*0Sstevel@tonic-gate 
563*0Sstevel@tonic-gate 	if ((sfile = fopen(sumfile, "w")) == NULL) {
564*0Sstevel@tonic-gate 		perror(sumfile);
565*0Sstevel@tonic-gate 		exit(EX_IOERR);
566*0Sstevel@tonic-gate 	}
567*0Sstevel@tonic-gate 	/*
568*0Sstevel@tonic-gate 	 * dump the header; use the last header read in
569*0Sstevel@tonic-gate 	 */
570*0Sstevel@tonic-gate 	if (Bflag) {
571*0Sstevel@tonic-gate 	    if (fwrite(&h, sizeof (h), 1, sfile) != 1) {
572*0Sstevel@tonic-gate 		perror(sumfile);
573*0Sstevel@tonic-gate 		exit(EX_IOERR);
574*0Sstevel@tonic-gate 	    }
575*0Sstevel@tonic-gate 	} else {
576*0Sstevel@tonic-gate 	    struct hdr32 hdr;
577*0Sstevel@tonic-gate 	    hdr.lowpc  = (pctype32)h.lowpc;
578*0Sstevel@tonic-gate 	    hdr.highpc = (pctype32)h.highpc;
579*0Sstevel@tonic-gate 	    hdr.ncnt   = (pctype32)h.ncnt;
580*0Sstevel@tonic-gate 	    if (fwrite(&hdr, sizeof (hdr), 1, sfile) != 1) {
581*0Sstevel@tonic-gate 		perror(sumfile);
582*0Sstevel@tonic-gate 		exit(EX_IOERR);
583*0Sstevel@tonic-gate 	    }
584*0Sstevel@tonic-gate 	}
585*0Sstevel@tonic-gate 	/*
586*0Sstevel@tonic-gate 	 * dump the samples
587*0Sstevel@tonic-gate 	 */
588*0Sstevel@tonic-gate 	if (fwrite(samples, sizeof (unsigned_UNIT), nsamples, sfile) !=
589*0Sstevel@tonic-gate 	    nsamples) {
590*0Sstevel@tonic-gate 		perror(sumfile);
591*0Sstevel@tonic-gate 		exit(EX_IOERR);
592*0Sstevel@tonic-gate 	}
593*0Sstevel@tonic-gate 	/*
594*0Sstevel@tonic-gate 	 * dump the normalized raw arc information. For old-style dumping,
595*0Sstevel@tonic-gate 	 * the only namelist is in modules.nl
596*0Sstevel@tonic-gate 	 */
597*0Sstevel@tonic-gate 	for (nlp = modules.nl; nlp < modules.npe; nlp++) {
598*0Sstevel@tonic-gate 		for (arcp = nlp->children; arcp;
599*0Sstevel@tonic-gate 		    arcp = arcp->arc_childlist) {
600*0Sstevel@tonic-gate 			if (Bflag) {
601*0Sstevel@tonic-gate 			    arc.raw_frompc = arcp->arc_parentp->value;
602*0Sstevel@tonic-gate 			    arc.raw_selfpc = arcp->arc_childp->value;
603*0Sstevel@tonic-gate 			    arc.raw_count = arcp->arc_count;
604*0Sstevel@tonic-gate 			    if (fwrite(&arc, sizeof (arc), 1, sfile) != 1) {
605*0Sstevel@tonic-gate 				    perror(sumfile);
606*0Sstevel@tonic-gate 				    exit(EX_IOERR);
607*0Sstevel@tonic-gate 			    }
608*0Sstevel@tonic-gate 			} else {
609*0Sstevel@tonic-gate 			    arc32.raw_frompc =
610*0Sstevel@tonic-gate 				(pctype32)arcp->arc_parentp->value;
611*0Sstevel@tonic-gate 			    arc32.raw_selfpc =
612*0Sstevel@tonic-gate 				(pctype32)arcp->arc_childp->value;
613*0Sstevel@tonic-gate 			    arc32.raw_count = (actype32)arcp->arc_count;
614*0Sstevel@tonic-gate 			    if (fwrite(&arc32, sizeof (arc32), 1, sfile) != 1) {
615*0Sstevel@tonic-gate 				    perror(sumfile);
616*0Sstevel@tonic-gate 				    exit(EX_IOERR);
617*0Sstevel@tonic-gate 			    }
618*0Sstevel@tonic-gate 			}
619*0Sstevel@tonic-gate #ifdef DEBUG
620*0Sstevel@tonic-gate 			if (debug & SAMPLEDEBUG) {
621*0Sstevel@tonic-gate 				printf("[dumpsum_ostyle] frompc 0x%llx selfpc "
622*0Sstevel@tonic-gate 				    "0x%llx count %lld\n", arc.raw_frompc,
623*0Sstevel@tonic-gate 				    arc.raw_selfpc, arc.raw_count);
624*0Sstevel@tonic-gate 			}
625*0Sstevel@tonic-gate #endif DEBUG
626*0Sstevel@tonic-gate 		}
627*0Sstevel@tonic-gate 	}
628*0Sstevel@tonic-gate 	fclose(sfile);
629*0Sstevel@tonic-gate }
630*0Sstevel@tonic-gate 
631*0Sstevel@tonic-gate /*
632*0Sstevel@tonic-gate  * dump out the gmon.sum file
633*0Sstevel@tonic-gate  */
634*0Sstevel@tonic-gate static void
635*0Sstevel@tonic-gate dumpsum(char *sumfile)
636*0Sstevel@tonic-gate {
637*0Sstevel@tonic-gate 	FILE		*sfile;
638*0Sstevel@tonic-gate 	size_t		pathbuf_sz;
639*0Sstevel@tonic-gate 	unsigned long	total_arcs;	/* total number of arcs in all */
640*0Sstevel@tonic-gate 	unsigned long	ncallees;	/* no. of callees with parents */
641*0Sstevel@tonic-gate 
642*0Sstevel@tonic-gate 	if (old_style) {
643*0Sstevel@tonic-gate 		dumpsum_ostyle(sumfile);
644*0Sstevel@tonic-gate 		return;
645*0Sstevel@tonic-gate 	}
646*0Sstevel@tonic-gate 
647*0Sstevel@tonic-gate 	if ((sfile = fopen(sumfile, "w")) == NULL) {
648*0Sstevel@tonic-gate 		perror(sumfile);
649*0Sstevel@tonic-gate 		exit(EX_IOERR);
650*0Sstevel@tonic-gate 	}
651*0Sstevel@tonic-gate 
652*0Sstevel@tonic-gate 	/*
653*0Sstevel@tonic-gate 	 * Dump the new-style gprof header. Even if one of the original
654*0Sstevel@tonic-gate 	 * profiled-files was of a older version, the summed file is of
655*0Sstevel@tonic-gate 	 * current version only.
656*0Sstevel@tonic-gate 	 */
657*0Sstevel@tonic-gate 	dump_gprofhdr(sfile, sumfile);
658*0Sstevel@tonic-gate 
659*0Sstevel@tonic-gate 	/*
660*0Sstevel@tonic-gate 	 * Fix up load-maps and dump out modules info
661*0Sstevel@tonic-gate 	 *
662*0Sstevel@tonic-gate 	 * Fix up module load maps so inactive modules get *some* address
663*0Sstevel@tonic-gate 	 * (and btw, could you get the total size of non-aout module path
664*0Sstevel@tonic-gate 	 * strings please ?)
665*0Sstevel@tonic-gate 	 */
666*0Sstevel@tonic-gate 	fixup_maps(&pathbuf_sz);
667*0Sstevel@tonic-gate 	dump_modules(sfile, sumfile, pathbuf_sz);
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 
670*0Sstevel@tonic-gate 	/*
671*0Sstevel@tonic-gate 	 * Dump out the summ'd pcsamples
672*0Sstevel@tonic-gate 	 *
673*0Sstevel@tonic-gate 	 * For dumping call graph information later, we need certain
674*0Sstevel@tonic-gate 	 * statistics (like total arcs, number of callers for each node);
675*0Sstevel@tonic-gate 	 * collect these also while we are at it.
676*0Sstevel@tonic-gate 	 */
677*0Sstevel@tonic-gate 	dump_pcsamples(sfile, sumfile, &total_arcs, &ncallees);
678*0Sstevel@tonic-gate 
679*0Sstevel@tonic-gate 	/*
680*0Sstevel@tonic-gate 	 * Dump out the summ'd call graph information
681*0Sstevel@tonic-gate 	 */
682*0Sstevel@tonic-gate 	dump_callgraph(sfile, sumfile, total_arcs, ncallees);
683*0Sstevel@tonic-gate 
684*0Sstevel@tonic-gate 
685*0Sstevel@tonic-gate 	fclose(sfile);
686*0Sstevel@tonic-gate }
687*0Sstevel@tonic-gate 
688*0Sstevel@tonic-gate static void
689*0Sstevel@tonic-gate tally(mod_info_t *caller_mod, mod_info_t *callee_mod, struct rawarc *rawp)
690*0Sstevel@tonic-gate {
691*0Sstevel@tonic-gate 	nltype		*parentp;
692*0Sstevel@tonic-gate 	nltype		*childp;
693*0Sstevel@tonic-gate 
694*0Sstevel@tonic-gate 	/*
695*0Sstevel@tonic-gate 	 * if count == 0 this is a null arc and
696*0Sstevel@tonic-gate 	 * we don't need to tally it.
697*0Sstevel@tonic-gate 	 */
698*0Sstevel@tonic-gate 	if (rawp->raw_count == 0)
699*0Sstevel@tonic-gate 		return;
700*0Sstevel@tonic-gate 
701*0Sstevel@tonic-gate 	/*
702*0Sstevel@tonic-gate 	 * Lookup the caller and callee pcs in namelists of
703*0Sstevel@tonic-gate 	 * appropriate modules
704*0Sstevel@tonic-gate 	 */
705*0Sstevel@tonic-gate 	parentp = nllookup(caller_mod, rawp->raw_frompc, NULL);
706*0Sstevel@tonic-gate 	childp = nllookup(callee_mod, rawp->raw_selfpc, NULL);
707*0Sstevel@tonic-gate 	if (childp && parentp) {
708*0Sstevel@tonic-gate 		if (!Dflag)
709*0Sstevel@tonic-gate 			childp->ncall += rawp->raw_count;
710*0Sstevel@tonic-gate 		else {
711*0Sstevel@tonic-gate 			if (first_file)
712*0Sstevel@tonic-gate 				childp->ncall += rawp->raw_count;
713*0Sstevel@tonic-gate 			else {
714*0Sstevel@tonic-gate 				childp->ncall -= rawp->raw_count;
715*0Sstevel@tonic-gate 				if (childp->ncall < 0)
716*0Sstevel@tonic-gate 					childp->ncall = 0;
717*0Sstevel@tonic-gate 			}
718*0Sstevel@tonic-gate 		}
719*0Sstevel@tonic-gate 
720*0Sstevel@tonic-gate #ifdef DEBUG
721*0Sstevel@tonic-gate 		if (debug & TALLYDEBUG) {
722*0Sstevel@tonic-gate 			printf("[tally] arc from %s to %s traversed "
723*0Sstevel@tonic-gate 			    "%lld times\n", parentp->name,
724*0Sstevel@tonic-gate 			    childp->name, rawp->raw_count);
725*0Sstevel@tonic-gate 		}
726*0Sstevel@tonic-gate #endif DEBUG
727*0Sstevel@tonic-gate 		addarc(parentp, childp, rawp->raw_count);
728*0Sstevel@tonic-gate 	}
729*0Sstevel@tonic-gate }
730*0Sstevel@tonic-gate 
731*0Sstevel@tonic-gate /*
732*0Sstevel@tonic-gate  * Look up a module's base address in a sorted list of pc-hits. Unlike
733*0Sstevel@tonic-gate  * nllookup(), this deals with misses by mapping them to the next *higher*
734*0Sstevel@tonic-gate  * pc-hit. This is so that we get into the module's first pc-hit rightaway,
735*0Sstevel@tonic-gate  * even if the module's entry-point (load_base) itself is not a hit.
736*0Sstevel@tonic-gate  */
737*0Sstevel@tonic-gate static Address *
738*0Sstevel@tonic-gate locate(Address	*pclist, size_t nelem, Address keypc)
739*0Sstevel@tonic-gate {
740*0Sstevel@tonic-gate 	size_t	low = 0, middle, high = nelem - 1;
741*0Sstevel@tonic-gate 
742*0Sstevel@tonic-gate 	if (keypc <= pclist[low])
743*0Sstevel@tonic-gate 		return (pclist);
744*0Sstevel@tonic-gate 
745*0Sstevel@tonic-gate 	if (keypc > pclist[high])
746*0Sstevel@tonic-gate 		return (NULL);
747*0Sstevel@tonic-gate 
748*0Sstevel@tonic-gate 	while (low != high) {
749*0Sstevel@tonic-gate 		middle = (high + low) >> 1;
750*0Sstevel@tonic-gate 
751*0Sstevel@tonic-gate 		if ((pclist[middle] < keypc) && (pclist[middle + 1] >= keypc))
752*0Sstevel@tonic-gate 			return (&pclist[middle + 1]);
753*0Sstevel@tonic-gate 
754*0Sstevel@tonic-gate 		if (pclist[middle] >= keypc)
755*0Sstevel@tonic-gate 			high = middle;
756*0Sstevel@tonic-gate 		else
757*0Sstevel@tonic-gate 			low = middle + 1;
758*0Sstevel@tonic-gate 	}
759*0Sstevel@tonic-gate 
760*0Sstevel@tonic-gate 	/* must never reach here! */
761*0Sstevel@tonic-gate 	return (NULL);
762*0Sstevel@tonic-gate }
763*0Sstevel@tonic-gate 
764*0Sstevel@tonic-gate static void
765*0Sstevel@tonic-gate assign_pcsamples(module, pcsmpl, n_samples)
766*0Sstevel@tonic-gate mod_info_t	*module;
767*0Sstevel@tonic-gate Address		*pcsmpl;
768*0Sstevel@tonic-gate size_t		n_samples;
769*0Sstevel@tonic-gate {
770*0Sstevel@tonic-gate 	Address		*pcptr, *pcse = pcsmpl + n_samples;
771*0Sstevel@tonic-gate 	pctype		nxt_func;
772*0Sstevel@tonic-gate 	nltype		*fnl;
773*0Sstevel@tonic-gate 	size_t		func_nticks;
774*0Sstevel@tonic-gate #ifdef DEBUG
775*0Sstevel@tonic-gate 	size_t		n_hits_in_module = 0;
776*0Sstevel@tonic-gate #endif DEBUG
777*0Sstevel@tonic-gate 
778*0Sstevel@tonic-gate 	/* Locate the first pc-hit for this module */
779*0Sstevel@tonic-gate 	if ((pcptr = locate(pcsmpl, n_samples, module->load_base)) == NULL) {
780*0Sstevel@tonic-gate #ifdef DEBUG
781*0Sstevel@tonic-gate 		if (debug & PCSMPLDEBUG) {
782*0Sstevel@tonic-gate 			printf("[assign_pcsamples] no pc-hits in\n");
783*0Sstevel@tonic-gate 			printf("                   `%s'\n", module->name);
784*0Sstevel@tonic-gate 		}
785*0Sstevel@tonic-gate #endif DEBUG
786*0Sstevel@tonic-gate 		return;			/* no pc-hits in this module */
787*0Sstevel@tonic-gate 	}
788*0Sstevel@tonic-gate 
789*0Sstevel@tonic-gate 	/* Assign all pc-hits in this module to appropriate functions */
790*0Sstevel@tonic-gate 	while ((pcptr < pcse) && (*pcptr < module->load_end)) {
791*0Sstevel@tonic-gate 
792*0Sstevel@tonic-gate 		/* Update the corresponding function's time */
793*0Sstevel@tonic-gate 		if (fnl = nllookup(module, (pctype) *pcptr, &nxt_func)) {
794*0Sstevel@tonic-gate 			/*
795*0Sstevel@tonic-gate 			 * Collect all pc-hits in this function. Each
796*0Sstevel@tonic-gate 			 * pc-hit counts as 1 tick.
797*0Sstevel@tonic-gate 			 */
798*0Sstevel@tonic-gate 			func_nticks = 0;
799*0Sstevel@tonic-gate 			while ((pcptr < pcse) && (*pcptr < nxt_func)) {
800*0Sstevel@tonic-gate 				func_nticks++;
801*0Sstevel@tonic-gate 				pcptr++;
802*0Sstevel@tonic-gate 			}
803*0Sstevel@tonic-gate 
804*0Sstevel@tonic-gate 			if (func_nticks == 0)
805*0Sstevel@tonic-gate 				pcptr++;
806*0Sstevel@tonic-gate 			else {
807*0Sstevel@tonic-gate 				fnl->nticks += func_nticks;
808*0Sstevel@tonic-gate 				fnl->time += func_nticks;
809*0Sstevel@tonic-gate 				totime += func_nticks;
810*0Sstevel@tonic-gate 			}
811*0Sstevel@tonic-gate 
812*0Sstevel@tonic-gate #ifdef DEBUG
813*0Sstevel@tonic-gate 			n_hits_in_module += func_nticks;
814*0Sstevel@tonic-gate #endif DEBUG
815*0Sstevel@tonic-gate 		} else {
816*0Sstevel@tonic-gate 			/*
817*0Sstevel@tonic-gate 			 * pc sample could not be assigned to function;
818*0Sstevel@tonic-gate 			 * probably in a PLT
819*0Sstevel@tonic-gate 			 */
820*0Sstevel@tonic-gate 			pcptr++;
821*0Sstevel@tonic-gate 		}
822*0Sstevel@tonic-gate 	}
823*0Sstevel@tonic-gate 
824*0Sstevel@tonic-gate #ifdef DEBUG
825*0Sstevel@tonic-gate 	if (debug & PCSMPLDEBUG) {
826*0Sstevel@tonic-gate 		printf("[assign_pcsamples] %ld hits in\n", n_hits_in_module);
827*0Sstevel@tonic-gate 		printf("                   `%s'\n", module->name);
828*0Sstevel@tonic-gate 	}
829*0Sstevel@tonic-gate #endif DEBUG
830*0Sstevel@tonic-gate }
831*0Sstevel@tonic-gate 
832*0Sstevel@tonic-gate int
833*0Sstevel@tonic-gate pc_cmp(Address *pc1, Address *pc2)
834*0Sstevel@tonic-gate {
835*0Sstevel@tonic-gate 	if (*pc1 > *pc2)
836*0Sstevel@tonic-gate 		return (1);
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate 	if (*pc1 < *pc2)
839*0Sstevel@tonic-gate 		return (-1);
840*0Sstevel@tonic-gate 
841*0Sstevel@tonic-gate 	return (0);
842*0Sstevel@tonic-gate }
843*0Sstevel@tonic-gate 
844*0Sstevel@tonic-gate static void
845*0Sstevel@tonic-gate process_pcsamples(bufp)
846*0Sstevel@tonic-gate ProfBuffer	*bufp;
847*0Sstevel@tonic-gate {
848*0Sstevel@tonic-gate 	Address		*pc_samples;
849*0Sstevel@tonic-gate 	mod_info_t	*mi;
850*0Sstevel@tonic-gate 	caddr_t		p;
851*0Sstevel@tonic-gate 	size_t		chunk_size, nelem_read, nelem_to_read;
852*0Sstevel@tonic-gate 
853*0Sstevel@tonic-gate #ifdef DEBUG
854*0Sstevel@tonic-gate 	if (debug & PCSMPLDEBUG) {
855*0Sstevel@tonic-gate 		printf("[process_pcsamples] number of pcsamples = %lld\n",
856*0Sstevel@tonic-gate 							    bufp->bufsize);
857*0Sstevel@tonic-gate 	}
858*0Sstevel@tonic-gate #endif DEBUG
859*0Sstevel@tonic-gate 
860*0Sstevel@tonic-gate 	/* buffer with no pc samples ? */
861*0Sstevel@tonic-gate 	if (bufp->bufsize == 0)
862*0Sstevel@tonic-gate 		return;
863*0Sstevel@tonic-gate 
864*0Sstevel@tonic-gate 	/*
865*0Sstevel@tonic-gate 	 * If we're processing pcsamples of a profile sum, we could have
866*0Sstevel@tonic-gate 	 * more than PROF_BUFFER_SIZE number of samples. In such a case,
867*0Sstevel@tonic-gate 	 * we must read the pcsamples in chunks.
868*0Sstevel@tonic-gate 	 */
869*0Sstevel@tonic-gate 	if ((chunk_size = bufp->bufsize) > PROF_BUFFER_SIZE)
870*0Sstevel@tonic-gate 		chunk_size = PROF_BUFFER_SIZE;
871*0Sstevel@tonic-gate 
872*0Sstevel@tonic-gate 	/* Allocate for the pcsample chunk */
873*0Sstevel@tonic-gate 	pc_samples = (Address *) calloc(chunk_size, sizeof (Address));
874*0Sstevel@tonic-gate 	if (pc_samples == NULL) {
875*0Sstevel@tonic-gate 		fprintf(stderr, "%s: no room for %ld sample pc's\n",
876*0Sstevel@tonic-gate 							whoami, chunk_size);
877*0Sstevel@tonic-gate 		exit(EX_OSERR);
878*0Sstevel@tonic-gate 	}
879*0Sstevel@tonic-gate 
880*0Sstevel@tonic-gate 	/* Copy the current set of pcsamples */
881*0Sstevel@tonic-gate 	nelem_read = 0;
882*0Sstevel@tonic-gate 	nelem_to_read = bufp->bufsize;
883*0Sstevel@tonic-gate 	p = (char *) bufp + bufp->buffer;
884*0Sstevel@tonic-gate 
885*0Sstevel@tonic-gate 	while (nelem_read < nelem_to_read) {
886*0Sstevel@tonic-gate 		memcpy((void *) pc_samples, p, chunk_size * sizeof (Address));
887*0Sstevel@tonic-gate 
888*0Sstevel@tonic-gate 		/* Sort the pc samples */
889*0Sstevel@tonic-gate 		qsort(pc_samples, chunk_size, sizeof (Address),
890*0Sstevel@tonic-gate 				(int (*)(const void *, const void *)) pc_cmp);
891*0Sstevel@tonic-gate 
892*0Sstevel@tonic-gate 		/*
893*0Sstevel@tonic-gate 		 * Assign pcsamples to functions in the currently active
894*0Sstevel@tonic-gate 		 * module list
895*0Sstevel@tonic-gate 		 */
896*0Sstevel@tonic-gate 		for (mi = &modules; mi; mi = mi->next) {
897*0Sstevel@tonic-gate 			if (mi->active == FALSE)
898*0Sstevel@tonic-gate 				continue;
899*0Sstevel@tonic-gate 			assign_pcsamples(mi, pc_samples, chunk_size);
900*0Sstevel@tonic-gate 		}
901*0Sstevel@tonic-gate 
902*0Sstevel@tonic-gate 		p += (chunk_size * sizeof (Address));
903*0Sstevel@tonic-gate 		nelem_read += chunk_size;
904*0Sstevel@tonic-gate 
905*0Sstevel@tonic-gate 		if ((nelem_to_read - nelem_read) < chunk_size)
906*0Sstevel@tonic-gate 			chunk_size = nelem_to_read - nelem_read;
907*0Sstevel@tonic-gate 	}
908*0Sstevel@tonic-gate 
909*0Sstevel@tonic-gate 	free(pc_samples);
910*0Sstevel@tonic-gate 
911*0Sstevel@tonic-gate 	/* Update total number of pcsamples read so far */
912*0Sstevel@tonic-gate 	n_pcsamples += bufp->bufsize;
913*0Sstevel@tonic-gate }
914*0Sstevel@tonic-gate 
915*0Sstevel@tonic-gate static mod_info_t *
916*0Sstevel@tonic-gate find_module(Address addr)
917*0Sstevel@tonic-gate {
918*0Sstevel@tonic-gate 	mod_info_t	*mi;
919*0Sstevel@tonic-gate 
920*0Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
921*0Sstevel@tonic-gate 		if (mi->active == FALSE)
922*0Sstevel@tonic-gate 			continue;
923*0Sstevel@tonic-gate 
924*0Sstevel@tonic-gate 		if (addr >= mi->load_base && addr < mi->load_end)
925*0Sstevel@tonic-gate 			return (mi);
926*0Sstevel@tonic-gate 	}
927*0Sstevel@tonic-gate 
928*0Sstevel@tonic-gate 	return (NULL);
929*0Sstevel@tonic-gate }
930*0Sstevel@tonic-gate 
931*0Sstevel@tonic-gate static void
932*0Sstevel@tonic-gate process_cgraph(cgp)
933*0Sstevel@tonic-gate ProfCallGraph	*cgp;
934*0Sstevel@tonic-gate {
935*0Sstevel@tonic-gate 	struct rawarc	arc;
936*0Sstevel@tonic-gate 	mod_info_t	*callee_mi, *caller_mi;
937*0Sstevel@tonic-gate 	ProfFunction	*calleep, *callerp;
938*0Sstevel@tonic-gate 	Index		caller_off, callee_off;
939*0Sstevel@tonic-gate 
940*0Sstevel@tonic-gate 	/*
941*0Sstevel@tonic-gate 	 * Note that *callee_off* increment in the for loop below
942*0Sstevel@tonic-gate 	 * uses *calleep* and *calleep* doesn't get set until the for loop
943*0Sstevel@tonic-gate 	 * is entered. We don't expect the increment to be executed before
944*0Sstevel@tonic-gate 	 * the loop body is executed atleast once, so this should be ok.
945*0Sstevel@tonic-gate 	 */
946*0Sstevel@tonic-gate 	for (callee_off = cgp->functions; callee_off;
947*0Sstevel@tonic-gate 					    callee_off = calleep->next_to) {
948*0Sstevel@tonic-gate 
949*0Sstevel@tonic-gate 		calleep = (ProfFunction *) ((char *) cgp + callee_off);
950*0Sstevel@tonic-gate 
951*0Sstevel@tonic-gate 		/*
952*0Sstevel@tonic-gate 		 * We could choose either to sort the {caller, callee}
953*0Sstevel@tonic-gate 		 * list twice and assign callee/caller to modules or inspect
954*0Sstevel@tonic-gate 		 * each callee/caller in the active modules list. Since
955*0Sstevel@tonic-gate 		 * the modules list is usually very small, we'l choose the
956*0Sstevel@tonic-gate 		 * latter.
957*0Sstevel@tonic-gate 		 */
958*0Sstevel@tonic-gate 
959*0Sstevel@tonic-gate 		/*
960*0Sstevel@tonic-gate 		 * If we cannot identify a callee with a module, there's
961*0Sstevel@tonic-gate 		 * no use worrying about who called it.
962*0Sstevel@tonic-gate 		 */
963*0Sstevel@tonic-gate 		if ((callee_mi = find_module(calleep->topc)) == NULL) {
964*0Sstevel@tonic-gate #ifdef DEBUG
965*0Sstevel@tonic-gate 			if (debug & CGRAPHDEBUG) {
966*0Sstevel@tonic-gate 				printf("[process_cgraph] callee %#llx missed\n",
967*0Sstevel@tonic-gate 							    calleep->topc);
968*0Sstevel@tonic-gate 			}
969*0Sstevel@tonic-gate #endif DEBUG
970*0Sstevel@tonic-gate 			continue;
971*0Sstevel@tonic-gate 		} else
972*0Sstevel@tonic-gate 			arc.raw_selfpc = calleep->topc;
973*0Sstevel@tonic-gate 
974*0Sstevel@tonic-gate 		for (caller_off = callee_off; caller_off;
975*0Sstevel@tonic-gate 					caller_off = callerp->next_from)  {
976*0Sstevel@tonic-gate 
977*0Sstevel@tonic-gate 			callerp = (ProfFunction *) ((char *) cgp + caller_off);
978*0Sstevel@tonic-gate 			if ((caller_mi = find_module(callerp->frompc)) ==
979*0Sstevel@tonic-gate 									NULL) {
980*0Sstevel@tonic-gate #ifdef DEBUG
981*0Sstevel@tonic-gate 				if (debug & CGRAPHDEBUG) {
982*0Sstevel@tonic-gate 					printf("[process_cgraph] caller %#llx "
983*0Sstevel@tonic-gate 						"missed\n", callerp->frompc);
984*0Sstevel@tonic-gate 				}
985*0Sstevel@tonic-gate #endif DEBUG
986*0Sstevel@tonic-gate 				continue;
987*0Sstevel@tonic-gate 			}
988*0Sstevel@tonic-gate 
989*0Sstevel@tonic-gate 			arc.raw_frompc = callerp->frompc;
990*0Sstevel@tonic-gate 			arc.raw_count = callerp->count;
991*0Sstevel@tonic-gate 
992*0Sstevel@tonic-gate #ifdef DEBUG
993*0Sstevel@tonic-gate 			if (debug & CGRAPHDEBUG) {
994*0Sstevel@tonic-gate 				printf("[process_cgraph] arc <%#llx, %#llx, "
995*0Sstevel@tonic-gate 						"%lld>\n", arc.raw_frompc,
996*0Sstevel@tonic-gate 						arc.raw_selfpc, arc.raw_count);
997*0Sstevel@tonic-gate 			}
998*0Sstevel@tonic-gate #endif DEBUG
999*0Sstevel@tonic-gate 			tally(caller_mi, callee_mi, &arc);
1000*0Sstevel@tonic-gate 		}
1001*0Sstevel@tonic-gate 	}
1002*0Sstevel@tonic-gate 
1003*0Sstevel@tonic-gate #ifdef DEBUG
1004*0Sstevel@tonic-gate 	puts("\n");
1005*0Sstevel@tonic-gate #endif DEBUG
1006*0Sstevel@tonic-gate }
1007*0Sstevel@tonic-gate 
1008*0Sstevel@tonic-gate /*
1009*0Sstevel@tonic-gate  * Two modules overlap each other if they don't lie completely *outside*
1010*0Sstevel@tonic-gate  * each other.
1011*0Sstevel@tonic-gate  */
1012*0Sstevel@tonic-gate static bool
1013*0Sstevel@tonic-gate does_overlap(ProfModule *new, mod_info_t *old)
1014*0Sstevel@tonic-gate {
1015*0Sstevel@tonic-gate 	/* case 1: new module lies completely *before* the old one */
1016*0Sstevel@tonic-gate 	if (new->startaddr < old->load_base && new->endaddr <= old->load_base)
1017*0Sstevel@tonic-gate 		return (FALSE);
1018*0Sstevel@tonic-gate 
1019*0Sstevel@tonic-gate 	/* case 2: new module lies completely *after* the old one */
1020*0Sstevel@tonic-gate 	if (new->startaddr >= old->load_end && new->endaddr >= old->load_end)
1021*0Sstevel@tonic-gate 		return (FALSE);
1022*0Sstevel@tonic-gate 
1023*0Sstevel@tonic-gate 	/* probably a dlopen: the modules overlap each other */
1024*0Sstevel@tonic-gate 	return (TRUE);
1025*0Sstevel@tonic-gate }
1026*0Sstevel@tonic-gate 
1027*0Sstevel@tonic-gate static bool
1028*0Sstevel@tonic-gate is_same_as_aout(char *modpath, struct stat *buf)
1029*0Sstevel@tonic-gate {
1030*0Sstevel@tonic-gate 	if (stat(modpath, buf) == -1) {
1031*0Sstevel@tonic-gate 		fprintf(stderr, "%s: can't get info on `%s'\n",
1032*0Sstevel@tonic-gate 							whoami, modpath);
1033*0Sstevel@tonic-gate 		exit(EX_NOINPUT);
1034*0Sstevel@tonic-gate 	}
1035*0Sstevel@tonic-gate 
1036*0Sstevel@tonic-gate 	if ((buf->st_dev == aout_info.dev) && (buf->st_ino == aout_info.ino))
1037*0Sstevel@tonic-gate 		return (TRUE);
1038*0Sstevel@tonic-gate 	else
1039*0Sstevel@tonic-gate 		return (FALSE);
1040*0Sstevel@tonic-gate }
1041*0Sstevel@tonic-gate 
1042*0Sstevel@tonic-gate static void
1043*0Sstevel@tonic-gate process_modules(modlp)
1044*0Sstevel@tonic-gate ProfModuleList	*modlp;
1045*0Sstevel@tonic-gate {
1046*0Sstevel@tonic-gate 	ProfModule	*newmodp;
1047*0Sstevel@tonic-gate 	mod_info_t	*mi, *last, *new_module;
1048*0Sstevel@tonic-gate 	char		*so_path, *name;
1049*0Sstevel@tonic-gate 	bool		more_modules = TRUE;
1050*0Sstevel@tonic-gate 	struct stat	so_statbuf;
1051*0Sstevel@tonic-gate 
1052*0Sstevel@tonic-gate #ifdef DEBUG
1053*0Sstevel@tonic-gate 	if (debug & MODULEDEBUG) {
1054*0Sstevel@tonic-gate 		printf("[process_modules] module obj version %u\n",
1055*0Sstevel@tonic-gate 							    modlp->version);
1056*0Sstevel@tonic-gate 	}
1057*0Sstevel@tonic-gate #endif DEBUG
1058*0Sstevel@tonic-gate 
1059*0Sstevel@tonic-gate 	/* Check version of module type object */
1060*0Sstevel@tonic-gate 	if (modlp->version > PROF_MODULES_VER) {
1061*0Sstevel@tonic-gate 		fprintf(stderr, "%s: version %d for module type objects"
1062*0Sstevel@tonic-gate 				"is not supported\n", whoami, modlp->version);
1063*0Sstevel@tonic-gate 		exit(EX_SOFTWARE);
1064*0Sstevel@tonic-gate 	}
1065*0Sstevel@tonic-gate 
1066*0Sstevel@tonic-gate 
1067*0Sstevel@tonic-gate 	/*
1068*0Sstevel@tonic-gate 	 * Scan the PROF_MODULES_T list and add modules to current list
1069*0Sstevel@tonic-gate 	 * of modules, if they're not present already
1070*0Sstevel@tonic-gate 	 */
1071*0Sstevel@tonic-gate 	newmodp = (ProfModule *) ((char *) modlp + modlp->modules);
1072*0Sstevel@tonic-gate 	do {
1073*0Sstevel@tonic-gate 		/*
1074*0Sstevel@tonic-gate 		 * Since the prog could've been renamed after its run, we
1075*0Sstevel@tonic-gate 		 * should see if this overlaps a.out. If it does, it is
1076*0Sstevel@tonic-gate 		 * probably the renamed aout. We should also skip any other
1077*0Sstevel@tonic-gate 		 * non-sharedobj's that we see (or should we report an error ?)
1078*0Sstevel@tonic-gate 		 */
1079*0Sstevel@tonic-gate 		so_path = (caddr_t) modlp + newmodp->path;
1080*0Sstevel@tonic-gate 		if (does_overlap(newmodp, &modules) ||
1081*0Sstevel@tonic-gate 				    is_same_as_aout(so_path, &so_statbuf) ||
1082*0Sstevel@tonic-gate 						(!is_shared_obj(so_path))) {
1083*0Sstevel@tonic-gate 
1084*0Sstevel@tonic-gate 			if (!newmodp->next)
1085*0Sstevel@tonic-gate 				more_modules = FALSE;
1086*0Sstevel@tonic-gate 
1087*0Sstevel@tonic-gate 			newmodp = (ProfModule *)
1088*0Sstevel@tonic-gate 					((caddr_t) modlp + newmodp->next);
1089*0Sstevel@tonic-gate #ifdef DEBUG
1090*0Sstevel@tonic-gate 			if (debug & MODULEDEBUG) {
1091*0Sstevel@tonic-gate 				printf("[process_modules] `%s'\n", so_path);
1092*0Sstevel@tonic-gate 				printf("                  skipped\n");
1093*0Sstevel@tonic-gate 			}
1094*0Sstevel@tonic-gate #endif DEBUG
1095*0Sstevel@tonic-gate 			continue;
1096*0Sstevel@tonic-gate 		}
1097*0Sstevel@tonic-gate #ifdef DEBUG
1098*0Sstevel@tonic-gate 		if (debug & MODULEDEBUG)
1099*0Sstevel@tonic-gate 			printf("[process_modules] `%s'...\n", so_path);
1100*0Sstevel@tonic-gate #endif DEBUG
1101*0Sstevel@tonic-gate 
1102*0Sstevel@tonic-gate 		/*
1103*0Sstevel@tonic-gate 		 * Check all modules (leave the first one, 'cos that
1104*0Sstevel@tonic-gate 		 * is the program executable info). If this module is already
1105*0Sstevel@tonic-gate 		 * there in the list, update the load addresses and proceed.
1106*0Sstevel@tonic-gate 		 */
1107*0Sstevel@tonic-gate 		last = &modules;
1108*0Sstevel@tonic-gate 		while (mi = last->next) {
1109*0Sstevel@tonic-gate 			/*
1110*0Sstevel@tonic-gate 			 * We expect the full pathname for all shared objects
1111*0Sstevel@tonic-gate 			 * needed by the program executable. In this case, we
1112*0Sstevel@tonic-gate 			 * simply need to compare the paths to see if they are
1113*0Sstevel@tonic-gate 			 * the same file.
1114*0Sstevel@tonic-gate 			 */
1115*0Sstevel@tonic-gate 			if (strcmp(mi->name, so_path) == 0)
1116*0Sstevel@tonic-gate 				break;
1117*0Sstevel@tonic-gate 
1118*0Sstevel@tonic-gate 			/*
1119*0Sstevel@tonic-gate 			 * Check if this new shared object will overlap
1120*0Sstevel@tonic-gate 			 * any existing module. If yes, remove the old one
1121*0Sstevel@tonic-gate 			 * from the linked list (but don't free it, 'cos
1122*0Sstevel@tonic-gate 			 * there may be symbols referring to this module
1123*0Sstevel@tonic-gate 			 * still)
1124*0Sstevel@tonic-gate 			 */
1125*0Sstevel@tonic-gate 			if (does_overlap(newmodp, mi)) {
1126*0Sstevel@tonic-gate #ifdef DEBUG
1127*0Sstevel@tonic-gate 				if (debug & MODULEDEBUG) {
1128*0Sstevel@tonic-gate 					printf("[process_modules] `%s'\n",
1129*0Sstevel@tonic-gate 								    so_path);
1130*0Sstevel@tonic-gate 					printf("                  overlaps\n");
1131*0Sstevel@tonic-gate 					printf("                  `%s'\n",
1132*0Sstevel@tonic-gate 								    mi->name);
1133*0Sstevel@tonic-gate 				}
1134*0Sstevel@tonic-gate #endif DEBUG
1135*0Sstevel@tonic-gate 				mi->active = FALSE;
1136*0Sstevel@tonic-gate 			}
1137*0Sstevel@tonic-gate 
1138*0Sstevel@tonic-gate 			last = mi;
1139*0Sstevel@tonic-gate 		}
1140*0Sstevel@tonic-gate 
1141*0Sstevel@tonic-gate 		/* Module already there, skip it */
1142*0Sstevel@tonic-gate 		if (mi != NULL) {
1143*0Sstevel@tonic-gate 			mi->load_base = newmodp->startaddr;
1144*0Sstevel@tonic-gate 			mi->load_end = newmodp->endaddr;
1145*0Sstevel@tonic-gate 			mi->active = TRUE;
1146*0Sstevel@tonic-gate 			if (!newmodp->next)
1147*0Sstevel@tonic-gate 				more_modules = FALSE;
1148*0Sstevel@tonic-gate 
1149*0Sstevel@tonic-gate 			newmodp = (ProfModule *)
1150*0Sstevel@tonic-gate 					((caddr_t) modlp + newmodp->next);
1151*0Sstevel@tonic-gate 
1152*0Sstevel@tonic-gate #ifdef DEBUG
1153*0Sstevel@tonic-gate 			if (debug & MODULEDEBUG) {
1154*0Sstevel@tonic-gate 				printf("[process_modules] base=%#llx, "
1155*0Sstevel@tonic-gate 						"end=%#llx\n", mi->load_base,
1156*0Sstevel@tonic-gate 						mi->load_end);
1157*0Sstevel@tonic-gate 			}
1158*0Sstevel@tonic-gate #endif DEBUG
1159*0Sstevel@tonic-gate 			continue;
1160*0Sstevel@tonic-gate 		}
1161*0Sstevel@tonic-gate 
1162*0Sstevel@tonic-gate 		/*
1163*0Sstevel@tonic-gate 		 * Check if gmon.out is outdated with respect to the new
1164*0Sstevel@tonic-gate 		 * module we want to add
1165*0Sstevel@tonic-gate 		 */
1166*0Sstevel@tonic-gate 		if (gmonout_info.mtime < so_statbuf.st_mtime) {
1167*0Sstevel@tonic-gate 			fprintf(stderr, "%s: shared obj outdates prof info\n",
1168*0Sstevel@tonic-gate 								    whoami);
1169*0Sstevel@tonic-gate 			fprintf(stderr, "\t(newer %s)\n", so_path);
1170*0Sstevel@tonic-gate 			exit(EX_NOINPUT);
1171*0Sstevel@tonic-gate 		}
1172*0Sstevel@tonic-gate 
1173*0Sstevel@tonic-gate 		/* Create a new module element */
1174*0Sstevel@tonic-gate 		new_module = (mod_info_t *) malloc(sizeof (mod_info_t));
1175*0Sstevel@tonic-gate 		if (new_module == NULL) {
1176*0Sstevel@tonic-gate 			fprintf(stderr, "%s: no room for %ld bytes\n",
1177*0Sstevel@tonic-gate 						whoami, sizeof (mod_info_t));
1178*0Sstevel@tonic-gate 			exit(EX_OSERR);
1179*0Sstevel@tonic-gate 		}
1180*0Sstevel@tonic-gate 
1181*0Sstevel@tonic-gate 		/* and fill in info... */
1182*0Sstevel@tonic-gate 		new_module->id = n_modules + 1;
1183*0Sstevel@tonic-gate 		new_module->load_base = newmodp->startaddr;
1184*0Sstevel@tonic-gate 		new_module->load_end = newmodp->endaddr;
1185*0Sstevel@tonic-gate 		new_module->name = (char *) malloc(strlen(so_path) + 1);
1186*0Sstevel@tonic-gate 		if (new_module->name == NULL) {
1187*0Sstevel@tonic-gate 			fprintf(stderr, "%s: no room for %ld bytes\n",
1188*0Sstevel@tonic-gate 						whoami, strlen(so_path) + 1);
1189*0Sstevel@tonic-gate 			exit(EX_OSERR);
1190*0Sstevel@tonic-gate 		}
1191*0Sstevel@tonic-gate 		strcpy(new_module->name, so_path);
1192*0Sstevel@tonic-gate #ifdef DEBUG
1193*0Sstevel@tonic-gate 		if (debug & MODULEDEBUG) {
1194*0Sstevel@tonic-gate 			printf("[process_modules] base=%#llx, end=%#llx\n",
1195*0Sstevel@tonic-gate 				new_module->load_base, new_module->load_end);
1196*0Sstevel@tonic-gate 		}
1197*0Sstevel@tonic-gate #endif DEBUG
1198*0Sstevel@tonic-gate 
1199*0Sstevel@tonic-gate 		/* Create this module's nameslist */
1200*0Sstevel@tonic-gate 		process_namelist(new_module);
1201*0Sstevel@tonic-gate 
1202*0Sstevel@tonic-gate 		/* Add it to the tail of active module list */
1203*0Sstevel@tonic-gate 		last->next = new_module;
1204*0Sstevel@tonic-gate 		n_modules++;
1205*0Sstevel@tonic-gate 
1206*0Sstevel@tonic-gate #ifdef DEBUG
1207*0Sstevel@tonic-gate 		if (debug & MODULEDEBUG) {
1208*0Sstevel@tonic-gate 			printf("[process_modules] total shared objects = %ld\n",
1209*0Sstevel@tonic-gate 							    n_modules - 1);
1210*0Sstevel@tonic-gate 		}
1211*0Sstevel@tonic-gate #endif DEBUG
1212*0Sstevel@tonic-gate 		/*
1213*0Sstevel@tonic-gate 		 * Move to the next module in the PROF_MODULES_T list
1214*0Sstevel@tonic-gate 		 * (if present)
1215*0Sstevel@tonic-gate 		 */
1216*0Sstevel@tonic-gate 		if (!newmodp->next)
1217*0Sstevel@tonic-gate 			more_modules = FALSE;
1218*0Sstevel@tonic-gate 
1219*0Sstevel@tonic-gate 		newmodp = (ProfModule *) ((caddr_t) modlp + newmodp->next);
1220*0Sstevel@tonic-gate 
1221*0Sstevel@tonic-gate 	} while (more_modules);
1222*0Sstevel@tonic-gate }
1223*0Sstevel@tonic-gate 
1224*0Sstevel@tonic-gate static void
1225*0Sstevel@tonic-gate reset_active_modules()
1226*0Sstevel@tonic-gate {
1227*0Sstevel@tonic-gate 	mod_info_t	*mi;
1228*0Sstevel@tonic-gate 
1229*0Sstevel@tonic-gate 	/* Except the executable, no other module should remain active */
1230*0Sstevel@tonic-gate 	for (mi = modules.next; mi; mi = mi->next)
1231*0Sstevel@tonic-gate 		mi->active = FALSE;
1232*0Sstevel@tonic-gate }
1233*0Sstevel@tonic-gate 
1234*0Sstevel@tonic-gate static void
1235*0Sstevel@tonic-gate getpfiledata(memp, fsz)
1236*0Sstevel@tonic-gate caddr_t	memp;
1237*0Sstevel@tonic-gate size_t	fsz;
1238*0Sstevel@tonic-gate {
1239*0Sstevel@tonic-gate 	ProfObject	*objp;
1240*0Sstevel@tonic-gate 	caddr_t		file_end;
1241*0Sstevel@tonic-gate 	bool		found_pcsamples = FALSE, found_cgraph = FALSE;
1242*0Sstevel@tonic-gate 
1243*0Sstevel@tonic-gate 	/*
1244*0Sstevel@tonic-gate 	 * Before processing a new gmon.out, all modules except the
1245*0Sstevel@tonic-gate 	 * program executable must be made inactive, so that symbols
1246*0Sstevel@tonic-gate 	 * are searched only in the program executable, if we don't
1247*0Sstevel@tonic-gate 	 * find a MODULES_T object. Don't do it *after* we read a gmon.out,
1248*0Sstevel@tonic-gate 	 * because we need the active module data after we're done with
1249*0Sstevel@tonic-gate 	 * the last gmon.out, if we're doing summing.
1250*0Sstevel@tonic-gate 	 */
1251*0Sstevel@tonic-gate 	reset_active_modules();
1252*0Sstevel@tonic-gate 
1253*0Sstevel@tonic-gate 	file_end = memp + fsz;
1254*0Sstevel@tonic-gate 	objp = (ProfObject *) (memp + ((ProfHeader *) memp)->size);
1255*0Sstevel@tonic-gate 	while ((caddr_t) objp < file_end) {
1256*0Sstevel@tonic-gate #ifdef DEBUG
1257*0Sstevel@tonic-gate 		{
1258*0Sstevel@tonic-gate 			unsigned int	type = 0;
1259*0Sstevel@tonic-gate 
1260*0Sstevel@tonic-gate 			if (debug & MONOUTDEBUG) {
1261*0Sstevel@tonic-gate 				if (objp->type <= MAX_OBJTYPES)
1262*0Sstevel@tonic-gate 					type = objp->type;
1263*0Sstevel@tonic-gate 
1264*0Sstevel@tonic-gate 				printf("\n[getpfiledata] object %s [%#lx]\n",
1265*0Sstevel@tonic-gate 						objname[type], objp->type);
1266*0Sstevel@tonic-gate 			}
1267*0Sstevel@tonic-gate 		}
1268*0Sstevel@tonic-gate #endif DEBUG
1269*0Sstevel@tonic-gate 		switch (objp->type) {
1270*0Sstevel@tonic-gate 			case PROF_MODULES_T :
1271*0Sstevel@tonic-gate 				process_modules((ProfModuleList *) objp);
1272*0Sstevel@tonic-gate 				break;
1273*0Sstevel@tonic-gate 
1274*0Sstevel@tonic-gate 			case PROF_CALLGRAPH_T :
1275*0Sstevel@tonic-gate 				process_cgraph((ProfCallGraph *) objp);
1276*0Sstevel@tonic-gate 				found_cgraph = TRUE;
1277*0Sstevel@tonic-gate 				break;
1278*0Sstevel@tonic-gate 
1279*0Sstevel@tonic-gate 			case PROF_BUFFER_T :
1280*0Sstevel@tonic-gate 				process_pcsamples((ProfBuffer *) objp);
1281*0Sstevel@tonic-gate 				found_pcsamples = TRUE;
1282*0Sstevel@tonic-gate 				break;
1283*0Sstevel@tonic-gate 
1284*0Sstevel@tonic-gate 			default :
1285*0Sstevel@tonic-gate 				fprintf(stderr,
1286*0Sstevel@tonic-gate 					"%s: unknown prof object type=%d\n",
1287*0Sstevel@tonic-gate 							whoami, objp->type);
1288*0Sstevel@tonic-gate 				exit(EX_SOFTWARE);
1289*0Sstevel@tonic-gate 		}
1290*0Sstevel@tonic-gate 		objp = (ProfObject *) ((caddr_t) objp + objp->size);
1291*0Sstevel@tonic-gate 	}
1292*0Sstevel@tonic-gate 
1293*0Sstevel@tonic-gate 	if (!found_cgraph || !found_pcsamples) {
1294*0Sstevel@tonic-gate 		fprintf(stderr,
1295*0Sstevel@tonic-gate 			"%s: missing callgraph/pcsamples object\n", whoami);
1296*0Sstevel@tonic-gate 		exit(EX_SOFTWARE);
1297*0Sstevel@tonic-gate 	}
1298*0Sstevel@tonic-gate 
1299*0Sstevel@tonic-gate 	if ((caddr_t) objp > file_end) {
1300*0Sstevel@tonic-gate 		fprintf(stderr, "%s: malformed profile file.\n", whoami);
1301*0Sstevel@tonic-gate 		exit(EX_SOFTWARE);
1302*0Sstevel@tonic-gate 	}
1303*0Sstevel@tonic-gate 
1304*0Sstevel@tonic-gate 	if (first_file)
1305*0Sstevel@tonic-gate 		first_file = FALSE;
1306*0Sstevel@tonic-gate }
1307*0Sstevel@tonic-gate 
1308*0Sstevel@tonic-gate static void
1309*0Sstevel@tonic-gate readarcs(pfile)
1310*0Sstevel@tonic-gate FILE	*pfile;
1311*0Sstevel@tonic-gate {
1312*0Sstevel@tonic-gate 	/*
1313*0Sstevel@tonic-gate 	 *	the rest of the file consists of
1314*0Sstevel@tonic-gate 	 *	a bunch of <from,self,count> tuples.
1315*0Sstevel@tonic-gate 	 */
1316*0Sstevel@tonic-gate 	/* CONSTCOND */
1317*0Sstevel@tonic-gate 	while (1) {
1318*0Sstevel@tonic-gate 		struct rawarc	arc;
1319*0Sstevel@tonic-gate 
1320*0Sstevel@tonic-gate 		if (rflag) {
1321*0Sstevel@tonic-gate 			if (Bflag) {
1322*0Sstevel@tonic-gate 				L_cgarc64		rtld_arc64;
1323*0Sstevel@tonic-gate 
1324*0Sstevel@tonic-gate 				/*
1325*0Sstevel@tonic-gate 				 * If rflag is set then this is an profiled
1326*0Sstevel@tonic-gate 				 * image generated by rtld.  It needs to be
1327*0Sstevel@tonic-gate 				 * 'converted' to the standard data format.
1328*0Sstevel@tonic-gate 				 */
1329*0Sstevel@tonic-gate 				if (fread(&rtld_arc64,
1330*0Sstevel@tonic-gate 					    sizeof (L_cgarc64), 1, pfile) != 1)
1331*0Sstevel@tonic-gate 					break;
1332*0Sstevel@tonic-gate 
1333*0Sstevel@tonic-gate 				if (rtld_arc64.cg_from == PRF_OUTADDR64)
1334*0Sstevel@tonic-gate 					arc.raw_frompc = s_highpc + 0x10;
1335*0Sstevel@tonic-gate 				else
1336*0Sstevel@tonic-gate 					arc.raw_frompc =
1337*0Sstevel@tonic-gate 					    (pctype)rtld_arc64.cg_from;
1338*0Sstevel@tonic-gate 				arc.raw_selfpc = (pctype)rtld_arc64.cg_to;
1339*0Sstevel@tonic-gate 				arc.raw_count = (actype)rtld_arc64.cg_count;
1340*0Sstevel@tonic-gate 			} else {
1341*0Sstevel@tonic-gate 				L_cgarc		rtld_arc;
1342*0Sstevel@tonic-gate 
1343*0Sstevel@tonic-gate 				/*
1344*0Sstevel@tonic-gate 				 * If rflag is set then this is an profiled
1345*0Sstevel@tonic-gate 				 * image generated by rtld.  It needs to be
1346*0Sstevel@tonic-gate 				 * 'converted' to the standard data format.
1347*0Sstevel@tonic-gate 				 */
1348*0Sstevel@tonic-gate 				if (fread(&rtld_arc,
1349*0Sstevel@tonic-gate 					    sizeof (L_cgarc), 1, pfile) != 1)
1350*0Sstevel@tonic-gate 					break;
1351*0Sstevel@tonic-gate 
1352*0Sstevel@tonic-gate 				if (rtld_arc.cg_from == PRF_OUTADDR)
1353*0Sstevel@tonic-gate 					arc.raw_frompc = s_highpc + 0x10;
1354*0Sstevel@tonic-gate 				else
1355*0Sstevel@tonic-gate 					arc.raw_frompc = (pctype)
1356*0Sstevel@tonic-gate 					    (uintptr_t)rtld_arc.cg_from;
1357*0Sstevel@tonic-gate 				arc.raw_selfpc = (pctype)
1358*0Sstevel@tonic-gate 				    (uintptr_t)rtld_arc.cg_to;
1359*0Sstevel@tonic-gate 				arc.raw_count = (actype)rtld_arc.cg_count;
1360*0Sstevel@tonic-gate 			}
1361*0Sstevel@tonic-gate 		} else {
1362*0Sstevel@tonic-gate 			if (Bflag) {
1363*0Sstevel@tonic-gate 				if (fread(&arc, sizeof (struct rawarc), 1,
1364*0Sstevel@tonic-gate 				    pfile) != 1) {
1365*0Sstevel@tonic-gate 					break;
1366*0Sstevel@tonic-gate 				}
1367*0Sstevel@tonic-gate 			} else {
1368*0Sstevel@tonic-gate 				/*
1369*0Sstevel@tonic-gate 				 * If these aren't big %pc's, we need to read
1370*0Sstevel@tonic-gate 				 * into the 32-bit raw arc structure, and
1371*0Sstevel@tonic-gate 				 * assign the members into the actual arc.
1372*0Sstevel@tonic-gate 				 */
1373*0Sstevel@tonic-gate 				struct rawarc32 arc32;
1374*0Sstevel@tonic-gate 				if (fread(&arc32, sizeof (struct rawarc32),
1375*0Sstevel@tonic-gate 				    1, pfile) != 1)
1376*0Sstevel@tonic-gate 					break;
1377*0Sstevel@tonic-gate 				arc.raw_frompc = (pctype)arc32.raw_frompc;
1378*0Sstevel@tonic-gate 				arc.raw_selfpc = (pctype)arc32.raw_selfpc;
1379*0Sstevel@tonic-gate 				arc.raw_count  = (actype)arc32.raw_count;
1380*0Sstevel@tonic-gate 			}
1381*0Sstevel@tonic-gate 		}
1382*0Sstevel@tonic-gate 
1383*0Sstevel@tonic-gate #ifdef DEBUG
1384*0Sstevel@tonic-gate 		if (debug & SAMPLEDEBUG) {
1385*0Sstevel@tonic-gate 			printf("[getpfile] frompc 0x%llx selfpc "
1386*0Sstevel@tonic-gate 			    "0x%llx count %lld\n", arc.raw_frompc,
1387*0Sstevel@tonic-gate 			    arc.raw_selfpc, arc.raw_count);
1388*0Sstevel@tonic-gate 		}
1389*0Sstevel@tonic-gate #endif DEBUG
1390*0Sstevel@tonic-gate 		/*
1391*0Sstevel@tonic-gate 		 *	add this arc
1392*0Sstevel@tonic-gate 		 */
1393*0Sstevel@tonic-gate 		tally(&modules, &modules, &arc);
1394*0Sstevel@tonic-gate 	}
1395*0Sstevel@tonic-gate 	if (first_file)
1396*0Sstevel@tonic-gate 		first_file = FALSE;
1397*0Sstevel@tonic-gate }
1398*0Sstevel@tonic-gate 
1399*0Sstevel@tonic-gate static void
1400*0Sstevel@tonic-gate readsamples(FILE *pfile)
1401*0Sstevel@tonic-gate {
1402*0Sstevel@tonic-gate 	sztype		i;
1403*0Sstevel@tonic-gate 	unsigned_UNIT	sample;
1404*0Sstevel@tonic-gate 
1405*0Sstevel@tonic-gate 	if (samples == 0) {
1406*0Sstevel@tonic-gate 		samples = (unsigned_UNIT *) calloc(nsamples,
1407*0Sstevel@tonic-gate 		    sizeof (unsigned_UNIT));
1408*0Sstevel@tonic-gate 		if (samples == 0) {
1409*0Sstevel@tonic-gate 			fprintf(stderr, "%s: No room for %ld sample pc's\n",
1410*0Sstevel@tonic-gate 			    whoami, sampbytes / sizeof (unsigned_UNIT));
1411*0Sstevel@tonic-gate 			exit(EX_OSERR);
1412*0Sstevel@tonic-gate 		}
1413*0Sstevel@tonic-gate 	}
1414*0Sstevel@tonic-gate 
1415*0Sstevel@tonic-gate 	for (i = 0; i < nsamples; i++) {
1416*0Sstevel@tonic-gate 		fread(&sample, sizeof (unsigned_UNIT), 1, pfile);
1417*0Sstevel@tonic-gate 		if (feof(pfile))
1418*0Sstevel@tonic-gate 			break;
1419*0Sstevel@tonic-gate 		samples[i] += sample;
1420*0Sstevel@tonic-gate 	}
1421*0Sstevel@tonic-gate 	if (i != nsamples) {
1422*0Sstevel@tonic-gate 		fprintf(stderr,
1423*0Sstevel@tonic-gate 		    "%s: unexpected EOF after reading %ld/%ld samples\n",
1424*0Sstevel@tonic-gate 		    whoami, --i, nsamples);
1425*0Sstevel@tonic-gate 		exit(EX_IOERR);
1426*0Sstevel@tonic-gate 	}
1427*0Sstevel@tonic-gate }
1428*0Sstevel@tonic-gate 
1429*0Sstevel@tonic-gate static void *
1430*0Sstevel@tonic-gate handle_versioned(FILE *pfile, char *filename, size_t *fsz)
1431*0Sstevel@tonic-gate {
1432*0Sstevel@tonic-gate 	int		fd;
1433*0Sstevel@tonic-gate 	bool		invalid_version;
1434*0Sstevel@tonic-gate 	caddr_t		fmem;
1435*0Sstevel@tonic-gate 	struct stat	buf;
1436*0Sstevel@tonic-gate 	ProfHeader	prof_hdr;
1437*0Sstevel@tonic-gate 
1438*0Sstevel@tonic-gate 	/*
1439*0Sstevel@tonic-gate 	 * Check versioning info. For now, let's say we provide
1440*0Sstevel@tonic-gate 	 * backward compatibility, so we accept all older versions.
1441*0Sstevel@tonic-gate 	 */
1442*0Sstevel@tonic-gate 	if (fread(&prof_hdr, sizeof (ProfHeader), 1, pfile) == 0) {
1443*0Sstevel@tonic-gate 		perror("fread()");
1444*0Sstevel@tonic-gate 		exit(EX_IOERR);
1445*0Sstevel@tonic-gate 	}
1446*0Sstevel@tonic-gate 
1447*0Sstevel@tonic-gate 	invalid_version = FALSE;
1448*0Sstevel@tonic-gate 	if (prof_hdr.h_major_ver > PROF_MAJOR_VERSION)
1449*0Sstevel@tonic-gate 		invalid_version = TRUE;
1450*0Sstevel@tonic-gate 	else if (prof_hdr.h_major_ver == PROF_MAJOR_VERSION) {
1451*0Sstevel@tonic-gate 		if (prof_hdr.h_minor_ver > PROF_MINOR_VERSION)
1452*0Sstevel@tonic-gate 			invalid_version = FALSE;
1453*0Sstevel@tonic-gate 	}
1454*0Sstevel@tonic-gate 
1455*0Sstevel@tonic-gate 	if (invalid_version) {
1456*0Sstevel@tonic-gate 		fprintf(stderr, "%s: version %d.%d not supported\n",
1457*0Sstevel@tonic-gate 			whoami, prof_hdr.h_major_ver, prof_hdr.h_minor_ver);
1458*0Sstevel@tonic-gate 		exit(EX_SOFTWARE);
1459*0Sstevel@tonic-gate 	}
1460*0Sstevel@tonic-gate 
1461*0Sstevel@tonic-gate 	/*
1462*0Sstevel@tonic-gate 	 * Map gmon.out onto memory.
1463*0Sstevel@tonic-gate 	 */
1464*0Sstevel@tonic-gate 	fclose(pfile);
1465*0Sstevel@tonic-gate 	if ((fd = open(filename, O_RDONLY)) == -1) {
1466*0Sstevel@tonic-gate 		perror(filename);
1467*0Sstevel@tonic-gate 		exit(EX_IOERR);
1468*0Sstevel@tonic-gate 	}
1469*0Sstevel@tonic-gate 
1470*0Sstevel@tonic-gate 	if ((*fsz = lseek(fd, 0, SEEK_END)) == -1) {
1471*0Sstevel@tonic-gate 		perror(filename);
1472*0Sstevel@tonic-gate 		exit(EX_IOERR);
1473*0Sstevel@tonic-gate 	}
1474*0Sstevel@tonic-gate 
1475*0Sstevel@tonic-gate 	fmem = mmap(0, *fsz, PROT_READ, MAP_PRIVATE, fd, 0);
1476*0Sstevel@tonic-gate 	if (fmem == MAP_FAILED) {
1477*0Sstevel@tonic-gate 	    fprintf(stderr, "%s: can't map %s\n", whoami, filename);
1478*0Sstevel@tonic-gate 	    exit(EX_IOERR);
1479*0Sstevel@tonic-gate 	}
1480*0Sstevel@tonic-gate 
1481*0Sstevel@tonic-gate 	/*
1482*0Sstevel@tonic-gate 	 * Before we close this fd, save this gmon.out's info to later verify
1483*0Sstevel@tonic-gate 	 * if the shared objects it references have changed since the time
1484*0Sstevel@tonic-gate 	 * they were used to generate this gmon.out
1485*0Sstevel@tonic-gate 	 */
1486*0Sstevel@tonic-gate 	if (fstat(fd, &buf) == -1) {
1487*0Sstevel@tonic-gate 		fprintf(stderr, "%s: can't get info on `%s'\n",
1488*0Sstevel@tonic-gate 							whoami, filename);
1489*0Sstevel@tonic-gate 		exit(EX_NOINPUT);
1490*0Sstevel@tonic-gate 	}
1491*0Sstevel@tonic-gate 	gmonout_info.dev = buf.st_dev;
1492*0Sstevel@tonic-gate 	gmonout_info.ino = buf.st_ino;
1493*0Sstevel@tonic-gate 	gmonout_info.mtime = buf.st_mtime;
1494*0Sstevel@tonic-gate 	gmonout_info.size = buf.st_size;
1495*0Sstevel@tonic-gate 
1496*0Sstevel@tonic-gate 	close(fd);
1497*0Sstevel@tonic-gate 
1498*0Sstevel@tonic-gate 	return ((void *) fmem);
1499*0Sstevel@tonic-gate }
1500*0Sstevel@tonic-gate 
1501*0Sstevel@tonic-gate static void *
1502*0Sstevel@tonic-gate openpfile(filename, fsz)
1503*0Sstevel@tonic-gate char	*filename;
1504*0Sstevel@tonic-gate size_t	*fsz;
1505*0Sstevel@tonic-gate {
1506*0Sstevel@tonic-gate 	struct hdr	tmp;
1507*0Sstevel@tonic-gate 	FILE *		pfile;
1508*0Sstevel@tonic-gate 	unsigned long	magic_num;
1509*0Sstevel@tonic-gate 	size_t		hdrsize = sizeof (struct hdr);
1510*0Sstevel@tonic-gate 	static bool	first_time = TRUE;
1511*0Sstevel@tonic-gate 	extern bool	old_style;
1512*0Sstevel@tonic-gate 
1513*0Sstevel@tonic-gate 	if ((pfile = fopen(filename, "r")) == NULL) {
1514*0Sstevel@tonic-gate 		perror(filename);
1515*0Sstevel@tonic-gate 		exit(EX_IOERR);
1516*0Sstevel@tonic-gate 	}
1517*0Sstevel@tonic-gate 
1518*0Sstevel@tonic-gate 	/*
1519*0Sstevel@tonic-gate 	 * Read in the magic. Note that we changed the cast "unsigned long"
1520*0Sstevel@tonic-gate 	 * to "unsigned int" because that's how h_magic is defined in the
1521*0Sstevel@tonic-gate 	 * new format ProfHeader.
1522*0Sstevel@tonic-gate 	 */
1523*0Sstevel@tonic-gate 	if (fread(&magic_num, sizeof (unsigned int), 1, pfile) == 0) {
1524*0Sstevel@tonic-gate 		perror("fread()");
1525*0Sstevel@tonic-gate 		exit(EX_IOERR);
1526*0Sstevel@tonic-gate 	}
1527*0Sstevel@tonic-gate 
1528*0Sstevel@tonic-gate 	rewind(pfile);
1529*0Sstevel@tonic-gate 
1530*0Sstevel@tonic-gate 	/*
1531*0Sstevel@tonic-gate 	 * First check if this is versioned or *old-style* gmon.out
1532*0Sstevel@tonic-gate 	 */
1533*0Sstevel@tonic-gate 	if (magic_num == (unsigned int)PROF_MAGIC) {
1534*0Sstevel@tonic-gate 		if ((!first_time) && (old_style == TRUE)) {
1535*0Sstevel@tonic-gate 			fprintf(stderr, "%s: can't mix old & new format "
1536*0Sstevel@tonic-gate 						"profiled files\n", whoami);
1537*0Sstevel@tonic-gate 			exit(EX_SOFTWARE);
1538*0Sstevel@tonic-gate 		}
1539*0Sstevel@tonic-gate 		first_time = FALSE;
1540*0Sstevel@tonic-gate 		old_style = FALSE;
1541*0Sstevel@tonic-gate 		return (handle_versioned(pfile, filename, fsz));
1542*0Sstevel@tonic-gate 	}
1543*0Sstevel@tonic-gate 
1544*0Sstevel@tonic-gate 	if ((!first_time) && (old_style == FALSE)) {
1545*0Sstevel@tonic-gate 		fprintf(stderr, "%s: can't mix old & new format "
1546*0Sstevel@tonic-gate 						"profiled files\n", whoami);
1547*0Sstevel@tonic-gate 		exit(EX_SOFTWARE);
1548*0Sstevel@tonic-gate 	}
1549*0Sstevel@tonic-gate 
1550*0Sstevel@tonic-gate 	first_time = FALSE;
1551*0Sstevel@tonic-gate 	old_style = TRUE;
1552*0Sstevel@tonic-gate 	fsz = 0;
1553*0Sstevel@tonic-gate 
1554*0Sstevel@tonic-gate 	/*
1555*0Sstevel@tonic-gate 	 * Now, we need to determine if this is a run-time linker
1556*0Sstevel@tonic-gate 	 * profiled file or if it is a standard gmon.out.
1557*0Sstevel@tonic-gate 	 *
1558*0Sstevel@tonic-gate 	 * We do this by checking if magic matches PRF_MAGIC. If it
1559*0Sstevel@tonic-gate 	 * does, then this is a run-time linker profiled file, if it
1560*0Sstevel@tonic-gate 	 * doesn't, it must be a gmon.out file.
1561*0Sstevel@tonic-gate 	 */
1562*0Sstevel@tonic-gate 	if (magic_num == (unsigned long)PRF_MAGIC)
1563*0Sstevel@tonic-gate 		rflag = TRUE;
1564*0Sstevel@tonic-gate 	else
1565*0Sstevel@tonic-gate 		rflag = FALSE;
1566*0Sstevel@tonic-gate 
1567*0Sstevel@tonic-gate 	if (rflag) {
1568*0Sstevel@tonic-gate 		if (Bflag) {
1569*0Sstevel@tonic-gate 			L_hdr64		l_hdr64;
1570*0Sstevel@tonic-gate 
1571*0Sstevel@tonic-gate 			/*
1572*0Sstevel@tonic-gate 			 * If the rflag is set then the input file is
1573*0Sstevel@tonic-gate 			 * rtld profiled data, we'll read it in and convert
1574*0Sstevel@tonic-gate 			 * it to the standard format (ie: make it look like
1575*0Sstevel@tonic-gate 			 * a gmon.out file).
1576*0Sstevel@tonic-gate 			 */
1577*0Sstevel@tonic-gate 			if (fread(&l_hdr64, sizeof (L_hdr64), 1, pfile) == 0) {
1578*0Sstevel@tonic-gate 				perror("fread()");
1579*0Sstevel@tonic-gate 				exit(EX_IOERR);
1580*0Sstevel@tonic-gate 			}
1581*0Sstevel@tonic-gate 			if (l_hdr64.hd_version != PRF_VERSION_64) {
1582*0Sstevel@tonic-gate 				fprintf(stderr, "%s: expected version %d, "
1583*0Sstevel@tonic-gate 				    "got version %d when processing 64-bit "
1584*0Sstevel@tonic-gate 				    "run-time linker profiled file.\n",
1585*0Sstevel@tonic-gate 				    whoami, PRF_VERSION_64, l_hdr64.hd_version);
1586*0Sstevel@tonic-gate 				exit(EX_SOFTWARE);
1587*0Sstevel@tonic-gate 			}
1588*0Sstevel@tonic-gate 			tmp.lowpc = 0;
1589*0Sstevel@tonic-gate 			tmp.highpc = (pctype)l_hdr64.hd_hpc;
1590*0Sstevel@tonic-gate 			tmp.ncnt = sizeof (M_hdr64) + l_hdr64.hd_psize;
1591*0Sstevel@tonic-gate 		} else {
1592*0Sstevel@tonic-gate 			L_hdr		l_hdr;
1593*0Sstevel@tonic-gate 
1594*0Sstevel@tonic-gate 			/*
1595*0Sstevel@tonic-gate 			 * If the rflag is set then the input file is
1596*0Sstevel@tonic-gate 			 * rtld profiled data, we'll read it in and convert
1597*0Sstevel@tonic-gate 			 * it to the standard format (ie: make it look like
1598*0Sstevel@tonic-gate 			 * a gmon.out file).
1599*0Sstevel@tonic-gate 			 */
1600*0Sstevel@tonic-gate 			if (fread(&l_hdr, sizeof (L_hdr), 1, pfile) == 0) {
1601*0Sstevel@tonic-gate 				perror("fread()");
1602*0Sstevel@tonic-gate 				exit(EX_IOERR);
1603*0Sstevel@tonic-gate 			}
1604*0Sstevel@tonic-gate 			if (l_hdr.hd_version != PRF_VERSION) {
1605*0Sstevel@tonic-gate 				fprintf(stderr, "%s: expected version %d, "
1606*0Sstevel@tonic-gate 				    "got version %d when processing "
1607*0Sstevel@tonic-gate 				    "run-time linker profiled file.\n",
1608*0Sstevel@tonic-gate 				    whoami, PRF_VERSION, l_hdr.hd_version);
1609*0Sstevel@tonic-gate 				exit(EX_SOFTWARE);
1610*0Sstevel@tonic-gate 			}
1611*0Sstevel@tonic-gate 			tmp.lowpc = 0;
1612*0Sstevel@tonic-gate 			tmp.highpc = (pctype)(uintptr_t)l_hdr.hd_hpc;
1613*0Sstevel@tonic-gate 			tmp.ncnt = sizeof (M_hdr) + l_hdr.hd_psize;
1614*0Sstevel@tonic-gate 			hdrsize = sizeof (M_hdr);
1615*0Sstevel@tonic-gate 		}
1616*0Sstevel@tonic-gate 	} else {
1617*0Sstevel@tonic-gate 		if (Bflag) {
1618*0Sstevel@tonic-gate 			if (fread(&tmp, sizeof (struct hdr), 1, pfile) == 0) {
1619*0Sstevel@tonic-gate 				perror("fread()");
1620*0Sstevel@tonic-gate 				exit(EX_IOERR);
1621*0Sstevel@tonic-gate 			}
1622*0Sstevel@tonic-gate 		} else {
1623*0Sstevel@tonic-gate 			/*
1624*0Sstevel@tonic-gate 			 * If we're not reading big %pc's, we need to read
1625*0Sstevel@tonic-gate 			 * the 32-bit header, and assign the members to
1626*0Sstevel@tonic-gate 			 * the actual header.
1627*0Sstevel@tonic-gate 			 */
1628*0Sstevel@tonic-gate 			struct hdr32 hdr32;
1629*0Sstevel@tonic-gate 			if (fread(&hdr32, sizeof (hdr32), 1, pfile) == 0) {
1630*0Sstevel@tonic-gate 				perror("fread()");
1631*0Sstevel@tonic-gate 				exit(EX_IOERR);
1632*0Sstevel@tonic-gate 			}
1633*0Sstevel@tonic-gate 			tmp.lowpc = hdr32.lowpc;
1634*0Sstevel@tonic-gate 			tmp.highpc = hdr32.highpc;
1635*0Sstevel@tonic-gate 			tmp.ncnt = hdr32.ncnt;
1636*0Sstevel@tonic-gate 			hdrsize = sizeof (struct hdr32);
1637*0Sstevel@tonic-gate 		}
1638*0Sstevel@tonic-gate 	}
1639*0Sstevel@tonic-gate 
1640*0Sstevel@tonic-gate 	/*
1641*0Sstevel@tonic-gate 	 * perform sanity check on profiled file we've opened.
1642*0Sstevel@tonic-gate 	 */
1643*0Sstevel@tonic-gate 	if (tmp.lowpc >= tmp.highpc) {
1644*0Sstevel@tonic-gate 		if (rflag)
1645*0Sstevel@tonic-gate 			fprintf(stderr, "%s: badly formed profiled data.\n",
1646*0Sstevel@tonic-gate 			    filename);
1647*0Sstevel@tonic-gate 		else
1648*0Sstevel@tonic-gate 			fprintf(stderr, "%s: badly formed gmon.out file.\n",
1649*0Sstevel@tonic-gate 			    filename);
1650*0Sstevel@tonic-gate 		exit(EX_SOFTWARE);
1651*0Sstevel@tonic-gate 	}
1652*0Sstevel@tonic-gate 
1653*0Sstevel@tonic-gate 	if (s_highpc != 0 && (tmp.lowpc != h.lowpc ||
1654*0Sstevel@tonic-gate 	    tmp.highpc != h.highpc || tmp.ncnt != h.ncnt)) {
1655*0Sstevel@tonic-gate 		fprintf(stderr,
1656*0Sstevel@tonic-gate 		    "%s: incompatible with first gmon file\n",
1657*0Sstevel@tonic-gate 		    filename);
1658*0Sstevel@tonic-gate 		exit(EX_IOERR);
1659*0Sstevel@tonic-gate 	}
1660*0Sstevel@tonic-gate 	h = tmp;
1661*0Sstevel@tonic-gate 	s_lowpc = h.lowpc;
1662*0Sstevel@tonic-gate 	s_highpc = h.highpc;
1663*0Sstevel@tonic-gate 	lowpc = h.lowpc / sizeof (UNIT);
1664*0Sstevel@tonic-gate 	highpc = h.highpc / sizeof (UNIT);
1665*0Sstevel@tonic-gate 	sampbytes = h.ncnt > hdrsize ? h.ncnt - hdrsize : 0;
1666*0Sstevel@tonic-gate 	nsamples = sampbytes / sizeof (unsigned_UNIT);
1667*0Sstevel@tonic-gate 
1668*0Sstevel@tonic-gate #ifdef DEBUG
1669*0Sstevel@tonic-gate 	if (debug & SAMPLEDEBUG) {
1670*0Sstevel@tonic-gate 		printf("[openpfile] hdr.lowpc 0x%llx hdr.highpc "
1671*0Sstevel@tonic-gate 		    "0x%llx hdr.ncnt %lld\n",
1672*0Sstevel@tonic-gate 		    h.lowpc, h.highpc, h.ncnt);
1673*0Sstevel@tonic-gate 		printf("[openpfile]   s_lowpc 0x%llx   s_highpc 0x%llx\n",
1674*0Sstevel@tonic-gate 		    s_lowpc, s_highpc);
1675*0Sstevel@tonic-gate 		printf("[openpfile]     lowpc 0x%llx     highpc 0x%llx\n",
1676*0Sstevel@tonic-gate 		    lowpc, highpc);
1677*0Sstevel@tonic-gate 		printf("[openpfile] sampbytes %d nsamples %d\n",
1678*0Sstevel@tonic-gate 		    sampbytes, nsamples);
1679*0Sstevel@tonic-gate 	}
1680*0Sstevel@tonic-gate #endif DEBUG
1681*0Sstevel@tonic-gate 
1682*0Sstevel@tonic-gate 	return ((void *) pfile);
1683*0Sstevel@tonic-gate }
1684*0Sstevel@tonic-gate 
1685*0Sstevel@tonic-gate /*
1686*0Sstevel@tonic-gate  * Information from a gmon.out file depends on whether it's versioned
1687*0Sstevel@tonic-gate  * or non-versioned, *old style* gmon.out. If old-style, it is in two
1688*0Sstevel@tonic-gate  * parts : an array of sampling hits within pc ranges, and the arcs. If
1689*0Sstevel@tonic-gate  * versioned, it contains a header, followed by any number of
1690*0Sstevel@tonic-gate  * modules/callgraph/pcsample_buffer objects.
1691*0Sstevel@tonic-gate  */
1692*0Sstevel@tonic-gate static void
1693*0Sstevel@tonic-gate getpfile(char *filename)
1694*0Sstevel@tonic-gate {
1695*0Sstevel@tonic-gate 	void		*handle;
1696*0Sstevel@tonic-gate 	size_t		fsz;
1697*0Sstevel@tonic-gate 
1698*0Sstevel@tonic-gate 	handle = openpfile(filename, &fsz);
1699*0Sstevel@tonic-gate 
1700*0Sstevel@tonic-gate 	if (old_style) {
1701*0Sstevel@tonic-gate 		readsamples((FILE *) handle);
1702*0Sstevel@tonic-gate 		readarcs((FILE *) handle);
1703*0Sstevel@tonic-gate 		fclose((FILE *) handle);
1704*0Sstevel@tonic-gate 		return;
1705*0Sstevel@tonic-gate 	}
1706*0Sstevel@tonic-gate 
1707*0Sstevel@tonic-gate 	getpfiledata((caddr_t) handle, fsz);
1708*0Sstevel@tonic-gate 	munmap(handle, fsz);
1709*0Sstevel@tonic-gate }
1710*0Sstevel@tonic-gate 
1711*0Sstevel@tonic-gate main(int argc, char ** argv)
1712*0Sstevel@tonic-gate {
1713*0Sstevel@tonic-gate 	char	**sp;
1714*0Sstevel@tonic-gate 	nltype	**timesortnlp;
1715*0Sstevel@tonic-gate 	int		c;
1716*0Sstevel@tonic-gate 	int		errflg;
1717*0Sstevel@tonic-gate 	extern char	*optarg;
1718*0Sstevel@tonic-gate 	extern int	optind;
1719*0Sstevel@tonic-gate 
1720*0Sstevel@tonic-gate 	prog_name = *argv;  /* preserve program name */
1721*0Sstevel@tonic-gate 	debug = 0;
1722*0Sstevel@tonic-gate 	nflag = FALSE;
1723*0Sstevel@tonic-gate 	bflag = TRUE;
1724*0Sstevel@tonic-gate 	lflag = FALSE;
1725*0Sstevel@tonic-gate 	Cflag = FALSE;
1726*0Sstevel@tonic-gate 	first_file = TRUE;
1727*0Sstevel@tonic-gate 	rflag = FALSE;
1728*0Sstevel@tonic-gate 	Bflag = FALSE;
1729*0Sstevel@tonic-gate 	errflg = FALSE;
1730*0Sstevel@tonic-gate 
1731*0Sstevel@tonic-gate 	while ((c = getopt(argc, argv, "abd:CcDE:e:F:f:ln:sz")) != EOF)
1732*0Sstevel@tonic-gate 		switch (c) {
1733*0Sstevel@tonic-gate 		case 'a':
1734*0Sstevel@tonic-gate 			aflag = TRUE;
1735*0Sstevel@tonic-gate 			break;
1736*0Sstevel@tonic-gate 		case 'b':
1737*0Sstevel@tonic-gate 			bflag = FALSE;
1738*0Sstevel@tonic-gate 			break;
1739*0Sstevel@tonic-gate 		case 'c':
1740*0Sstevel@tonic-gate 			cflag = TRUE;
1741*0Sstevel@tonic-gate 			break;
1742*0Sstevel@tonic-gate 		case 'C':
1743*0Sstevel@tonic-gate 			Cflag = TRUE;
1744*0Sstevel@tonic-gate 			break;
1745*0Sstevel@tonic-gate 		case 'd':
1746*0Sstevel@tonic-gate 			dflag = TRUE;
1747*0Sstevel@tonic-gate 			debug |= atoi(optarg);
1748*0Sstevel@tonic-gate 			printf("[main] debug = 0x%x\n", debug);
1749*0Sstevel@tonic-gate 			break;
1750*0Sstevel@tonic-gate 		case 'D':
1751*0Sstevel@tonic-gate 			Dflag = TRUE;
1752*0Sstevel@tonic-gate 			break;
1753*0Sstevel@tonic-gate 		case 'E':
1754*0Sstevel@tonic-gate 			addlist(Elist, optarg);
1755*0Sstevel@tonic-gate 			Eflag = TRUE;
1756*0Sstevel@tonic-gate 			addlist(elist, optarg);
1757*0Sstevel@tonic-gate 			eflag = TRUE;
1758*0Sstevel@tonic-gate 			break;
1759*0Sstevel@tonic-gate 		case 'e':
1760*0Sstevel@tonic-gate 			addlist(elist, optarg);
1761*0Sstevel@tonic-gate 			eflag = TRUE;
1762*0Sstevel@tonic-gate 			break;
1763*0Sstevel@tonic-gate 		case 'F':
1764*0Sstevel@tonic-gate 			addlist(Flist, optarg);
1765*0Sstevel@tonic-gate 			Fflag = TRUE;
1766*0Sstevel@tonic-gate 			addlist(flist, optarg);
1767*0Sstevel@tonic-gate 			fflag = TRUE;
1768*0Sstevel@tonic-gate 			break;
1769*0Sstevel@tonic-gate 		case 'f':
1770*0Sstevel@tonic-gate 			addlist(flist, optarg);
1771*0Sstevel@tonic-gate 			fflag = TRUE;
1772*0Sstevel@tonic-gate 			break;
1773*0Sstevel@tonic-gate 		case 'l':
1774*0Sstevel@tonic-gate 			lflag = TRUE;
1775*0Sstevel@tonic-gate 			break;
1776*0Sstevel@tonic-gate 		case 'n':
1777*0Sstevel@tonic-gate 			nflag = TRUE;
1778*0Sstevel@tonic-gate 			number_funcs_toprint = atoi(optarg);
1779*0Sstevel@tonic-gate 			break;
1780*0Sstevel@tonic-gate 		case 's':
1781*0Sstevel@tonic-gate 			sflag = TRUE;
1782*0Sstevel@tonic-gate 			break;
1783*0Sstevel@tonic-gate 		case 'z':
1784*0Sstevel@tonic-gate 			zflag = TRUE;
1785*0Sstevel@tonic-gate 			break;
1786*0Sstevel@tonic-gate 		case '?':
1787*0Sstevel@tonic-gate 			errflg++;
1788*0Sstevel@tonic-gate 
1789*0Sstevel@tonic-gate 		}
1790*0Sstevel@tonic-gate 
1791*0Sstevel@tonic-gate 	if (errflg) {
1792*0Sstevel@tonic-gate 		(void) fprintf(stderr,
1793*0Sstevel@tonic-gate 		    "usage: gprof [ -abcCDlsz ] [ -e function-name ] "
1794*0Sstevel@tonic-gate 		    "[ -E function-name ]\n\t[ -f function-name ] "
1795*0Sstevel@tonic-gate 		    "[ -F function-name  ]\n\t[  image-file  "
1796*0Sstevel@tonic-gate 		    "[ profile-file ... ] ]\n");
1797*0Sstevel@tonic-gate 		exit(EX_USAGE);
1798*0Sstevel@tonic-gate 	}
1799*0Sstevel@tonic-gate 
1800*0Sstevel@tonic-gate 	if (optind < argc) {
1801*0Sstevel@tonic-gate 		a_outname  = argv[optind++];
1802*0Sstevel@tonic-gate 	} else {
1803*0Sstevel@tonic-gate 		a_outname  = A_OUTNAME;
1804*0Sstevel@tonic-gate 	}
1805*0Sstevel@tonic-gate 	if (optind < argc) {
1806*0Sstevel@tonic-gate 		gmonname = argv[optind++];
1807*0Sstevel@tonic-gate 	} else {
1808*0Sstevel@tonic-gate 		gmonname = GMONNAME;
1809*0Sstevel@tonic-gate 	}
1810*0Sstevel@tonic-gate 	/*
1811*0Sstevel@tonic-gate 	 *	turn off default functions
1812*0Sstevel@tonic-gate 	 */
1813*0Sstevel@tonic-gate 	for (sp = &defaultEs[0]; *sp; sp++) {
1814*0Sstevel@tonic-gate 		Eflag = TRUE;
1815*0Sstevel@tonic-gate 		addlist(Elist, *sp);
1816*0Sstevel@tonic-gate 		eflag = TRUE;
1817*0Sstevel@tonic-gate 		addlist(elist, *sp);
1818*0Sstevel@tonic-gate 	}
1819*0Sstevel@tonic-gate 	/*
1820*0Sstevel@tonic-gate 	 *	how many ticks per second?
1821*0Sstevel@tonic-gate 	 *	if we can't tell, report time in ticks.
1822*0Sstevel@tonic-gate 	 */
1823*0Sstevel@tonic-gate 	hz = sysconf(_SC_CLK_TCK);
1824*0Sstevel@tonic-gate 	if (hz == -1) {
1825*0Sstevel@tonic-gate 		hz = 1;
1826*0Sstevel@tonic-gate 		fprintf(stderr, "time is in ticks, not seconds\n");
1827*0Sstevel@tonic-gate 	}
1828*0Sstevel@tonic-gate 
1829*0Sstevel@tonic-gate 	getnfile(a_outname);
1830*0Sstevel@tonic-gate 
1831*0Sstevel@tonic-gate 	/*
1832*0Sstevel@tonic-gate 	 *	get information about mon.out file(s).
1833*0Sstevel@tonic-gate 	 */
1834*0Sstevel@tonic-gate 	do {
1835*0Sstevel@tonic-gate 		getpfile(gmonname);
1836*0Sstevel@tonic-gate 		if (optind < argc)
1837*0Sstevel@tonic-gate 			gmonname = argv[optind++];
1838*0Sstevel@tonic-gate 		else
1839*0Sstevel@tonic-gate 			optind++;
1840*0Sstevel@tonic-gate 	} while (optind <= argc);
1841*0Sstevel@tonic-gate 	/*
1842*0Sstevel@tonic-gate 	 *	dump out a gmon.sum file if requested
1843*0Sstevel@tonic-gate 	 */
1844*0Sstevel@tonic-gate 	if (sflag || Dflag)
1845*0Sstevel@tonic-gate 		dumpsum(GMONSUM);
1846*0Sstevel@tonic-gate 
1847*0Sstevel@tonic-gate 	if (old_style) {
1848*0Sstevel@tonic-gate 		/*
1849*0Sstevel@tonic-gate 		 *	assign samples to procedures
1850*0Sstevel@tonic-gate 		 */
1851*0Sstevel@tonic-gate 		asgnsamples();
1852*0Sstevel@tonic-gate 	}
1853*0Sstevel@tonic-gate 
1854*0Sstevel@tonic-gate 	/*
1855*0Sstevel@tonic-gate 	 *	assemble the dynamic profile
1856*0Sstevel@tonic-gate 	 */
1857*0Sstevel@tonic-gate 	timesortnlp = doarcs();
1858*0Sstevel@tonic-gate 
1859*0Sstevel@tonic-gate 	/*
1860*0Sstevel@tonic-gate 	 *	print the dynamic profile
1861*0Sstevel@tonic-gate 	 */
1862*0Sstevel@tonic-gate #ifdef DEBUG
1863*0Sstevel@tonic-gate 	if (debug & ANYDEBUG) {
1864*0Sstevel@tonic-gate 		/* raw output of all symbols in all their glory */
1865*0Sstevel@tonic-gate 		int i;
1866*0Sstevel@tonic-gate 		printf(" Name, pc_entry_pt, svalue, tix_in_routine, "
1867*0Sstevel@tonic-gate 		    "#calls, selfcalls, index \n");
1868*0Sstevel@tonic-gate 		for (i = 0; i < modules.nname; i++) { 	/* Print each symbol */
1869*0Sstevel@tonic-gate 			if (timesortnlp[i]->name)
1870*0Sstevel@tonic-gate 				printf(" %s ", timesortnlp[i]->name);
1871*0Sstevel@tonic-gate 			else
1872*0Sstevel@tonic-gate 				printf(" <cycle> ");
1873*0Sstevel@tonic-gate 			printf(" %lld ", timesortnlp[i]->value);
1874*0Sstevel@tonic-gate 			printf(" %lld ", timesortnlp[i]->svalue);
1875*0Sstevel@tonic-gate 			printf(" %f ", timesortnlp[i]->time);
1876*0Sstevel@tonic-gate 			printf(" %lld ", timesortnlp[i]->ncall);
1877*0Sstevel@tonic-gate 			printf(" %lld ", timesortnlp[i]->selfcalls);
1878*0Sstevel@tonic-gate 			printf(" %d ", timesortnlp[i]->index);
1879*0Sstevel@tonic-gate 			printf(" \n");
1880*0Sstevel@tonic-gate 		}
1881*0Sstevel@tonic-gate 	}
1882*0Sstevel@tonic-gate #endif DEBUG
1883*0Sstevel@tonic-gate 
1884*0Sstevel@tonic-gate 	printgprof(timesortnlp);
1885*0Sstevel@tonic-gate 	/*
1886*0Sstevel@tonic-gate 	 *	print the flat profile
1887*0Sstevel@tonic-gate 	 */
1888*0Sstevel@tonic-gate 	printprof();
1889*0Sstevel@tonic-gate 	/*
1890*0Sstevel@tonic-gate 	 *	print the index
1891*0Sstevel@tonic-gate 	 */
1892*0Sstevel@tonic-gate 	printindex();
1893*0Sstevel@tonic-gate 
1894*0Sstevel@tonic-gate 	/*
1895*0Sstevel@tonic-gate 	 * print the modules
1896*0Sstevel@tonic-gate 	 */
1897*0Sstevel@tonic-gate 	printmodules();
1898*0Sstevel@tonic-gate 
1899*0Sstevel@tonic-gate 	done();
1900*0Sstevel@tonic-gate 	/* NOTREACHED */
1901*0Sstevel@tonic-gate 	return (0);
1902*0Sstevel@tonic-gate }
1903