xref: /onnv-gate/usr/src/cmd/sgs/prof/common/prof.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 /*	Copyright (c) 1988 AT&T	*/
23*0Sstevel@tonic-gate /*	  All Rights Reserved  	*/
24*0Sstevel@tonic-gate 
25*0Sstevel@tonic-gate 
26*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*0Sstevel@tonic-gate 
28*0Sstevel@tonic-gate /*
29*0Sstevel@tonic-gate  * Copyright 1993-2003 Sun Microsystems, Inc.  All rights reserved.
30*0Sstevel@tonic-gate  * Use is subject to license terms.
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate /*
34*0Sstevel@tonic-gate  *	Program profiling report generator.
35*0Sstevel@tonic-gate  *
36*0Sstevel@tonic-gate  *	Usage:
37*0Sstevel@tonic-gate  *
38*0Sstevel@tonic-gate  *	prof [ -V ] [ -[ntca] ] [ -[ox] ] [ -g ] [ -l ] [ -z ] [ -s ] [ -C ]
39*0Sstevel@tonic-gate  *		[ -m mdata ] [ prog ]
40*0Sstevel@tonic-gate  *
41*0Sstevel@tonic-gate  *	Where "prog" is the program that was profiled; "a.out" by default.
42*0Sstevel@tonic-gate  *	Options are:
43*0Sstevel@tonic-gate  *
44*0Sstevel@tonic-gate  *	-n	Sort by symbol name.
45*0Sstevel@tonic-gate  *	-t	Sort by decreasing time.
46*0Sstevel@tonic-gate  *	-c	Sort by decreasing number of calls.
47*0Sstevel@tonic-gate  *	-a	Sort by increasing symbol address.
48*0Sstevel@tonic-gate  *
49*0Sstevel@tonic-gate  *	The options that determine the type of sorting are mutually exclusive.
50*0Sstevel@tonic-gate  *	Additional options are:
51*0Sstevel@tonic-gate  *
52*0Sstevel@tonic-gate  *	-o	Include symbol addresses in output (in octal).
53*0Sstevel@tonic-gate  *	-x	Include symbol addresses in output (in hexadecimal).
54*0Sstevel@tonic-gate  *	-g	Include non-global T-type symbols in output.
55*0Sstevel@tonic-gate  *	-l	Do NOT inlcude local T-type symbols in output (default).
56*0Sstevel@tonic-gate  *	-z	Include all symbols in profiling range, even if zero
57*0Sstevel@tonic-gate  *			number of calls or time.
58*0Sstevel@tonic-gate  *	-h	Suppress table header.
59*0Sstevel@tonic-gate  *	-s	Follow report with additional statistical information.
60*0Sstevel@tonic-gate  *	-m mdata Use file "mdata" instead of MON_OUT for profiling data.
61*0Sstevel@tonic-gate  *	-V	print version information for prof (and exit, if only V spec'd)
62*0Sstevel@tonic-gate  *	-C	call C++ demangle routine to demangle names before printing.
63*0Sstevel@tonic-gate  */
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate #include <stdio.h>
66*0Sstevel@tonic-gate #include <string.h>
67*0Sstevel@tonic-gate #include <errno.h>
68*0Sstevel@tonic-gate #include <dlfcn.h>
69*0Sstevel@tonic-gate #include "sgs.h"
70*0Sstevel@tonic-gate #include "symint.h"
71*0Sstevel@tonic-gate #include "sys/param.h"			/* for HZ */
72*0Sstevel@tonic-gate #include "mon.h"
73*0Sstevel@tonic-gate #include "sys/stat.h"
74*0Sstevel@tonic-gate #include "debug.h"
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate #define	OLD_DEBUG(x)
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate #define	PROC				/* Mark procedure names. */
79*0Sstevel@tonic-gate #define	Print	(void) printf
80*0Sstevel@tonic-gate #define	Fprint	(void) fprintf
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate #if vax
83*0Sstevel@tonic-gate 	/* Max positive difference between a fnpc and sl_addr for match */
84*0Sstevel@tonic-gate #define	CCADIFF	22
85*0Sstevel@tonic-gate 	/* Type if n_type field in file symbol table entry. */
86*0Sstevel@tonic-gate #endif
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate #if (u3b || u3b15 || u3b2 || i386)
89*0Sstevel@tonic-gate 	/* Max positive difference between a fnpc and sl_addr for match */
90*0Sstevel@tonic-gate #define	CCADIFF	20	/*  ?? (16 would probably do) */
91*0Sstevel@tonic-gate 	/* For u3b, the "type" is storage class + section number (no type_t) */
92*0Sstevel@tonic-gate #endif
93*0Sstevel@tonic-gate 
94*0Sstevel@tonic-gate #if (sparc)
95*0Sstevel@tonic-gate #define	CCADIFF 24	/* PIC prologue length=20 + 4 */
96*0Sstevel@tonic-gate #endif
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate #define	PROFSEC(ticks) ((double)(ticks)/HZ) /* Convert clock ticks to seconds */
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate 	/* Title fragment used if symbol addresses in output ("-o" or "-x"). */
102*0Sstevel@tonic-gate char *atitle = " Address ";
103*0Sstevel@tonic-gate 	/* Format for addresses in output */
104*0Sstevel@tonic-gate char *aformat = "%8o ";
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate #if !(vax || u3b || u3b15 || u3b2 || i386 || sparc)
107*0Sstevel@tonic-gate 	/* Make sure something we are set up for.  Else lay egg. */
108*0Sstevel@tonic-gate #include "### No code for processor type ###"
109*0Sstevel@tonic-gate #endif
110*0Sstevel@tonic-gate 
111*0Sstevel@tonic-gate 
112*0Sstevel@tonic-gate 	/* Shorthand to gimme the Precise #of addresses per cells */
113*0Sstevel@tonic-gate #define	DBL_ADDRPERCELL		(((double)bias)/sf)
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate 	/* Used for unsigned fixed-point fraction with binary scale at */
117*0Sstevel@tonic-gate 	/* the left of 15'th bit (0 as least significant bit) . */
118*0Sstevel@tonic-gate #define	BIAS		((long)0200000L)
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate /*
121*0Sstevel@tonic-gate  *	TS1 insures that the symbols section is executable.
122*0Sstevel@tonic-gate  */
123*0Sstevel@tonic-gate #define	TS1(s) (((s) > 0) && (scnhdrp[(s)-1].sh_flags & SHF_EXECINSTR))
124*0Sstevel@tonic-gate /*
125*0Sstevel@tonic-gate  *	TS2 insures that the symbol should be reported.  We want
126*0Sstevel@tonic-gate  *	to report only those symbols that are functions (STT_FUNC)
127*0Sstevel@tonic-gate  *	or "notype" (STT_NOTYPE... "printf", for example).  Also,
128*0Sstevel@tonic-gate  *	unless the gflag is set, the symbol must be global.
129*0Sstevel@tonic-gate  */
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate #define	TS2(i)	\
132*0Sstevel@tonic-gate 	(((ELF32_ST_TYPE(i) == STT_FUNC) ||		\
133*0Sstevel@tonic-gate 			(ELF32_ST_TYPE(i) == STT_NOTYPE)) &&	\
134*0Sstevel@tonic-gate 		((ELF32_ST_BIND(i) == STB_GLOBAL) ||		\
135*0Sstevel@tonic-gate 			(gflag && (ELF32_ST_BIND(i) == STB_LOCAL))))
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate #define	TXTSYM(s, i)	(TS1(s) && TS2(i))
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate int gflag = 0;			/*  replaces gmatch and gmask */
140*0Sstevel@tonic-gate int Cflag = 0;
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate PROF_FILE	*ldptr; 		/* For program ("a.out") file. */
143*0Sstevel@tonic-gate 
144*0Sstevel@tonic-gate FILE	*mon_iop;		/* For profile (MON_OUT) file. */
145*0Sstevel@tonic-gate char	*sym_fn = "a.out";	/* Default program file name. */
146*0Sstevel@tonic-gate char	*mon_fn = MON_OUT;	/* Default profile file name. */
147*0Sstevel@tonic-gate 				/* May be changed by "-m file". */
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate long bias;	/* adjusted bias */
150*0Sstevel@tonic-gate long temp;	/* for bias adjust */
151*0Sstevel@tonic-gate /* extern char *realloc(), *strncpy(), *optarg; */
152*0Sstevel@tonic-gate extern int optind;
153*0Sstevel@tonic-gate extern long strtol();
154*0Sstevel@tonic-gate extern void qsort(), exit(), perror();
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate 	/* For symbol table entries read from program file. */
158*0Sstevel@tonic-gate PROF_SYMBOL nl;
159*0Sstevel@tonic-gate 
160*0Sstevel@tonic-gate /* Compare routines called from qsort() */
161*0Sstevel@tonic-gate 
162*0Sstevel@tonic-gate int c_ccaddr();		/* Compare fnpc fields of cnt structures. */
163*0Sstevel@tonic-gate int c_sladdr();		/* Compare sl_addr fields of slist structures */
164*0Sstevel@tonic-gate int c_time();		/*	"  sl_time	"	"	"	*/
165*0Sstevel@tonic-gate int c_name();		/*	"  sl_name	" 	"	"	*/
166*0Sstevel@tonic-gate int c_ncalls();		/*	"  sl_count	"  	"	"	*/
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate /* Other stuff. */
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate /* Return size of open file (arg is file descriptor) */
171*0Sstevel@tonic-gate off_t	fsize();
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate 	/* Memory allocation. Like malloc(), but no return if error. */
174*0Sstevel@tonic-gate char	*_prof_Malloc();
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate 	/* Scan past path part (if any) in the ... */
177*0Sstevel@tonic-gate char	*basename();
178*0Sstevel@tonic-gate 
179*0Sstevel@tonic-gate 	/* command name, for error messages. */
180*0Sstevel@tonic-gate char	*cmdname;
181*0Sstevel@tonic-gate /* Structure of subroutine call counters (cnt) is defined in mon.h. */
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate /* Structure for header of mon.out (hdr) is defined in mon.h. */
184*0Sstevel@tonic-gate 
185*0Sstevel@tonic-gate 	/* Local representation of symbols and call/time information. */
186*0Sstevel@tonic-gate struct slist {
187*0Sstevel@tonic-gate 	char *sl_name;		/* Symbol name. */
188*0Sstevel@tonic-gate 	char *sl_addr;		/* Address. */
189*0Sstevel@tonic-gate 	long sl_size;		/* size of symbol */
190*0Sstevel@tonic-gate 	long sl_count;		/* Count of subroutine calls */
191*0Sstevel@tonic-gate 	float sl_time;		/* Count of clock ticks in this routine, */
192*0Sstevel@tonic-gate 				/*		converted to secs. */
193*0Sstevel@tonic-gate };
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate 	/* local structure for tracking synonyms in our symbol list */
196*0Sstevel@tonic-gate struct snymEntry
197*0Sstevel@tonic-gate {
198*0Sstevel@tonic-gate 	char	*sym_addr;	/* address which has a synonym */
199*0Sstevel@tonic-gate 	int	howMany;	/* # of synonyms for this symbol */
200*0Sstevel@tonic-gate 	int	snymReported;	/* 'was printed in a report line already'  */
201*0Sstevel@tonic-gate 				/* 	flag, */
202*0Sstevel@tonic-gate 				/*   > 0 report line printed for these syns. */
203*0Sstevel@tonic-gate 				/*  == 0 not printed yet. */
204*0Sstevel@tonic-gate 	long	tot_sl_count;	/* total subr calls for these snyms */
205*0Sstevel@tonic-gate 	float	tot_sl_time;	/* total clock ticks (a la sl_time) */
206*0Sstevel@tonic-gate };
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate 
209*0Sstevel@tonic-gate #define	AOUTHSZ		(filhdr.f_opthdr)
210*0Sstevel@tonic-gate PROF_FILE	filhdr;			/* profile file descriptor */
211*0Sstevel@tonic-gate Elf32_Shdr	*scnhdrp;	/* pointer to first section header */
212*0Sstevel@tonic-gate 					/* (space by _prof_Malloc) */
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate struct hdr head;	/* Profile file (MON_OUT) header. */
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate int	(*sort)() = NULL;	/* Compare routine for sorting output */
217*0Sstevel@tonic-gate 				/*	symbols.  Set by "-[acnt]". */
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate int	flags;		/* Various flag bits. */
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate char	*pc_l;		/* From head.lpc. */
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate char	*pc_h;		/*   "  head.hpc. */
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate short	VwasSpecified = 0;	/* 1 if -V was specified */
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate /*
228*0Sstevel@tonic-gate  * Bit macro and flag bit definitions. These need to be identical to the
229*0Sstevel@tonic-gate  * set in profv.h. Any change here should be reflected in profv.c also.
230*0Sstevel@tonic-gate  */
231*0Sstevel@tonic-gate #define	FBIT(pos)	(01 << (pos))	/* Returns value with bit pos set. */
232*0Sstevel@tonic-gate #define	F_SORT		FBIT(0)		/* Set if "-[acnt]" seen. */
233*0Sstevel@tonic-gate #define	F_VERBOSE	FBIT(1)		/* Set if "-s" seen. */
234*0Sstevel@tonic-gate #define	F_ZSYMS		FBIT(2)		/* Set if "-z" seen. */
235*0Sstevel@tonic-gate #define	F_PADDR		FBIT(3)		/* Set if "-o" or "-x" seen. */
236*0Sstevel@tonic-gate #define	F_NHEAD		FBIT(4)		/* Set if "-h" seen. */
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate struct snymEntry *snymList;	/* Pointer to allocated list of */
240*0Sstevel@tonic-gate 				/* synonym entries.  */
241*0Sstevel@tonic-gate struct snymEntry *snymp;
242*0Sstevel@tonic-gate 				/* for scanning entries. */
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate int snymCapacity;		/* #slots in snymList */
245*0Sstevel@tonic-gate int n_snyms;			/* #used slots in snymList */
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate /*
248*0Sstevel@tonic-gate  * Sort flags. Mutually exclusive. These need to be identical to the ones
249*0Sstevel@tonic-gate  * defined in profv.h
250*0Sstevel@tonic-gate  */
251*0Sstevel@tonic-gate #define	BY_ADDRESS	0x1
252*0Sstevel@tonic-gate #define	BY_NCALLS	0x2
253*0Sstevel@tonic-gate #define	BY_NAME		0x4
254*0Sstevel@tonic-gate #define	BY_TIME		0x8
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate extern unsigned char sort_flag;	/* what type of sort ? */
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate 	/*
259*0Sstevel@tonic-gate 	 * printSnymNames - print a comma-seperated list of snym names.
260*0Sstevel@tonic-gate 	 * This routine hunts down all the synonyms for the given
261*0Sstevel@tonic-gate 	 * symbol, and prints them as a comma-seperated list.
262*0Sstevel@tonic-gate 	 * NB we assume that all the synonyms _Follow_ this one,
263*0Sstevel@tonic-gate 	 * since they are only printed when the First one
264*0Sstevel@tonic-gate 	 * is seen.
265*0Sstevel@tonic-gate 	 */
266*0Sstevel@tonic-gate PROC
267*0Sstevel@tonic-gate void
268*0Sstevel@tonic-gate printSnymNames(slp, snymp)
269*0Sstevel@tonic-gate struct	slist		*slp;
270*0Sstevel@tonic-gate struct	snymEntry	*snymp;
271*0Sstevel@tonic-gate {
272*0Sstevel@tonic-gate 	/* how many snyms for this addr, total, and their shared address */
273*0Sstevel@tonic-gate 	int i = snymp->howMany;
274*0Sstevel@tonic-gate 	char *sharedaddr = snymp->sym_addr;
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate 	/* put out first name - it counts as one, so decr count */
277*0Sstevel@tonic-gate 	(void) fputs(slp->sl_name, stdout);
278*0Sstevel@tonic-gate 	i--;
279*0Sstevel@tonic-gate 
280*0Sstevel@tonic-gate 	/* for the others: find each, print each. */
281*0Sstevel@tonic-gate 	while (--i >= 0) {
282*0Sstevel@tonic-gate 		while ((++slp)->sl_addr != sharedaddr);
283*0Sstevel@tonic-gate 		Print(", %s", slp->sl_name);
284*0Sstevel@tonic-gate 	}
285*0Sstevel@tonic-gate 	/* finally.. the trailing newline */
286*0Sstevel@tonic-gate 	putchar('\n');
287*0Sstevel@tonic-gate }
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate 	/*
291*0Sstevel@tonic-gate 	 * getSnymEntry - see if addr was noted as a aliased address
292*0Sstevel@tonic-gate 	 * (i.e. a synonym symbol) and return the address of the
293*0Sstevel@tonic-gate 	 * snym entry if it was.
294*0Sstevel@tonic-gate 	 */
295*0Sstevel@tonic-gate PROC
296*0Sstevel@tonic-gate struct snymEntry
297*0Sstevel@tonic-gate *getSnymEntry(sl_addr)
298*0Sstevel@tonic-gate char *sl_addr;
299*0Sstevel@tonic-gate {
300*0Sstevel@tonic-gate 	struct snymEntry *p;
301*0Sstevel@tonic-gate 	int i;
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 	for (p = snymList, i = n_snyms; --i >= 0; p++)
304*0Sstevel@tonic-gate 		if (sl_addr == p->sym_addr)
305*0Sstevel@tonic-gate 			return (p);
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 	return ((struct snymEntry *)0);
308*0Sstevel@tonic-gate }
309*0Sstevel@tonic-gate 
310*0Sstevel@tonic-gate 
311*0Sstevel@tonic-gate PROC
312*0Sstevel@tonic-gate main(argc, argv)
313*0Sstevel@tonic-gate int argc;
314*0Sstevel@tonic-gate char **argv;
315*0Sstevel@tonic-gate {
316*0Sstevel@tonic-gate 	char buffer[BUFSIZ];	/* buffer for printf */
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate 	WORD *pcounts;	/* Pointer to allocated area for */
319*0Sstevel@tonic-gate 			/*	pcounts: PC clock hit counts */
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 	register WORD *pcp;	/* For scanning pcounts. */
322*0Sstevel@tonic-gate 
323*0Sstevel@tonic-gate 	struct cnt *ccounts;	/* Pointer to allocated area for cnt */
324*0Sstevel@tonic-gate 				/* structures: subr PC-call counts. */
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate 	register struct cnt *ccp;	/* For scanning ccounts. */
327*0Sstevel@tonic-gate 
328*0Sstevel@tonic-gate 	struct slist *slist;	/* Pointer to allocated slist structures: */
329*0Sstevel@tonic-gate 				/* symbol name/address/time/call counts */
330*0Sstevel@tonic-gate 
331*0Sstevel@tonic-gate 	register struct slist *slp;	/* For scanning slist */
332*0Sstevel@tonic-gate 
333*0Sstevel@tonic-gate 	int vn_cc, n_cc;	/* Number of cnt structures in profile data */
334*0Sstevel@tonic-gate 				/*	file (later # ones used). */
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate 	int n_pc;	/* Number of pcounts in profile data file. */
337*0Sstevel@tonic-gate 
338*0Sstevel@tonic-gate 	int n_syms;	/* Number of text symbols (of proper type) */
339*0Sstevel@tonic-gate 			/*	that fill in range of profiling. */
340*0Sstevel@tonic-gate 
341*0Sstevel@tonic-gate 	int n_nonzero;	/* Number of (above symbols) actually printed */
342*0Sstevel@tonic-gate 			/*	because nonzero time or # calls. */
343*0Sstevel@tonic-gate 
344*0Sstevel@tonic-gate 	int symttl;	/* Total # symbols in program file sym-table */
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate 	int i;
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 	int fdigits = 0; /* # of digits of precision for print msecs/call */
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 	register int n, symct;
351*0Sstevel@tonic-gate 
352*0Sstevel@tonic-gate 	long sf;	/* Scale for index into pcounts: */
353*0Sstevel@tonic-gate 			/*	i(pc) = ((pc - pc_l) * sf)/bias. */
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 	long s_inv;	/* Inverse: i_inv(i) = */
356*0Sstevel@tonic-gate 			/*		{pc00, pc00+1, ... pc00+s_inv-1}. */
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate 	unsigned pc_m;	/* Range of PCs profiled: pc_m = pc_h - pc_l */
359*0Sstevel@tonic-gate 
360*0Sstevel@tonic-gate 	float	t, t0;
361*0Sstevel@tonic-gate 	float	t_tot;	/* Total time: PROFSEC(sum of all pcounts[i]) */
362*0Sstevel@tonic-gate 	float	profOverhead = 0.0;
363*0Sstevel@tonic-gate 	int	callTotal = 0;
364*0Sstevel@tonic-gate 	char *getname();	/* get name from symbol */
365*0Sstevel@tonic-gate 
366*0Sstevel@tonic-gate 	DEBUG_LOC("main: top");
367*0Sstevel@tonic-gate 	setbuf(stdout, buffer);
368*0Sstevel@tonic-gate 	cmdname = basename(*argv);	/* command name. */
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate 	while ((n = getopt(argc, argv, "canthsglzoxT:m:VC")) != EOF) {
371*0Sstevel@tonic-gate 		switch (n) {
372*0Sstevel@tonic-gate 		int (*fcn)();	/* For function to sort results. */
373*0Sstevel@tonic-gate 
374*0Sstevel@tonic-gate 		case 'm':	/* Specify data file:	-m file */
375*0Sstevel@tonic-gate 			mon_fn = optarg;
376*0Sstevel@tonic-gate 			break;
377*0Sstevel@tonic-gate 
378*0Sstevel@tonic-gate #ifdef ddt
379*0Sstevel@tonic-gate 		case 'T':	/* Set trace flags: -T(octnum) */
380*0Sstevel@tonic-gate 			debug_value = (int)strtol(optarg, 0, 8);
381*0Sstevel@tonic-gate 			break;
382*0Sstevel@tonic-gate #endif
383*0Sstevel@tonic-gate 
384*0Sstevel@tonic-gate 		case 'n':	/* Sort by symbol name. */
385*0Sstevel@tonic-gate 			fcn = c_name;
386*0Sstevel@tonic-gate 			sort_flag |= BY_NAME;
387*0Sstevel@tonic-gate 			goto check;
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 		case 't':	/* Sort by decreasing time. */
390*0Sstevel@tonic-gate 			fcn = c_time;
391*0Sstevel@tonic-gate 			sort_flag |= BY_TIME;
392*0Sstevel@tonic-gate 			goto check;
393*0Sstevel@tonic-gate 
394*0Sstevel@tonic-gate 		case 'c':	/* Sort by decreasing # calls. */
395*0Sstevel@tonic-gate 			fcn = c_ncalls;
396*0Sstevel@tonic-gate 			sort_flag |= BY_NCALLS;
397*0Sstevel@tonic-gate 			goto check;
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate 		case 'a':	/* Sort by increasing symbol address */
400*0Sstevel@tonic-gate 				/*		(don't have to -- it will be) */
401*0Sstevel@tonic-gate 			fcn = NULL;
402*0Sstevel@tonic-gate 			sort_flag |= BY_ADDRESS;
403*0Sstevel@tonic-gate 		check:		/* Here to check sort option conflicts. */
404*0Sstevel@tonic-gate 			if (sort != NULL && sort != fcn) {
405*0Sstevel@tonic-gate 				Fprint(stderr, "%s: Warning: %c overrides"
406*0Sstevel@tonic-gate 				" previous specification\n", cmdname, n);
407*0Sstevel@tonic-gate 			}
408*0Sstevel@tonic-gate 			sort = fcn;	/* Store sort routine */
409*0Sstevel@tonic-gate 			flags |= F_SORT; /* Note have done so */
410*0Sstevel@tonic-gate 			break;
411*0Sstevel@tonic-gate 
412*0Sstevel@tonic-gate 		case 'o':	/* Include symbol addresses in output. */
413*0Sstevel@tonic-gate 		case 'x':	/* Include symbol addresses in output. */
414*0Sstevel@tonic-gate 			aformat[2] = n;	/* 'o' or 'x' in format */
415*0Sstevel@tonic-gate 			flags |= F_PADDR;	/* Set flag. */
416*0Sstevel@tonic-gate 			break;
417*0Sstevel@tonic-gate 
418*0Sstevel@tonic-gate 		case 'g':	/* Include local T symbols as well as global */
419*0Sstevel@tonic-gate 			gflag = 1;
420*0Sstevel@tonic-gate 			break;
421*0Sstevel@tonic-gate 
422*0Sstevel@tonic-gate 		case 'l':	/* Do NOT include local T symbols */
423*0Sstevel@tonic-gate 			gflag = 0;
424*0Sstevel@tonic-gate 			break;
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 		case 'z':	/* Print all symbols in profiling range, */
427*0Sstevel@tonic-gate 				/*	 even if no time or # calls. */
428*0Sstevel@tonic-gate 			flags |= F_ZSYMS;	/* Set flag. */
429*0Sstevel@tonic-gate 			break;
430*0Sstevel@tonic-gate 
431*0Sstevel@tonic-gate 		case 'h':	/* Suppress table header. */
432*0Sstevel@tonic-gate 			flags |= F_NHEAD;
433*0Sstevel@tonic-gate 			break;
434*0Sstevel@tonic-gate 
435*0Sstevel@tonic-gate 		case 's':	/* Follow normal output with extra summary. */
436*0Sstevel@tonic-gate 			flags |= F_VERBOSE;	/* Set flag (...) */
437*0Sstevel@tonic-gate 			break;
438*0Sstevel@tonic-gate 
439*0Sstevel@tonic-gate 		case 'V':
440*0Sstevel@tonic-gate 			(void) fprintf(stderr, "prof: %s %s\n",
441*0Sstevel@tonic-gate 			    (const char *)SGU_PKG, (const char *)SGU_REL);
442*0Sstevel@tonic-gate 			VwasSpecified = 1;
443*0Sstevel@tonic-gate 			break;
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate 		case 'C':	/* demangle C++ names before printing. */
446*0Sstevel@tonic-gate 			Cflag = 1;
447*0Sstevel@tonic-gate 			break;
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate 		case '?':	/* But no good. */
450*0Sstevel@tonic-gate 			Fprint(stderr,
451*0Sstevel@tonic-gate 			    "%s: Unrecognized option: %c\n", cmdname, n);
452*0Sstevel@tonic-gate 			exit(1);
453*0Sstevel@tonic-gate 
454*0Sstevel@tonic-gate 		}	/* End switch (n) */
455*0Sstevel@tonic-gate 	}	/* End while (getopt) */
456*0Sstevel@tonic-gate 
457*0Sstevel@tonic-gate 	DEBUG_LOC("main: following getopt");
458*0Sstevel@tonic-gate 
459*0Sstevel@tonic-gate 	/* if -V the only argument, just exit. */
460*0Sstevel@tonic-gate 	if (VwasSpecified && argc == 2 && !flags)
461*0Sstevel@tonic-gate 		exit(0);
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate 	if (optind < argc)
464*0Sstevel@tonic-gate 		sym_fn = argv[optind];	/* name other than `a.out' */
465*0Sstevel@tonic-gate 
466*0Sstevel@tonic-gate 	if (sort == NULL && !(flags & F_SORT))
467*0Sstevel@tonic-gate 				/* If have not specified sort mode ... */
468*0Sstevel@tonic-gate 		sort = c_time;		/* then sort by decreasing time. */
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 	/*
471*0Sstevel@tonic-gate 	 * profver() checks to see if the mon.out was "versioned" and if
472*0Sstevel@tonic-gate 	 * yes, processes it and exits; otherwise, we have an *old-style*
473*0Sstevel@tonic-gate 	 * mon.out and we process it the old way.
474*0Sstevel@tonic-gate 	 */
475*0Sstevel@tonic-gate 	profver();
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate 		/* Open monitor data file (has counts). */
478*0Sstevel@tonic-gate 	if ((mon_iop = fopen(mon_fn, "r")) == NULL)
479*0Sstevel@tonic-gate 		Perror(mon_fn);
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	DEBUG_LOC("main: before _symintOpen");
482*0Sstevel@tonic-gate 	if ((ldptr = _symintOpen(sym_fn)) == NULL) {
483*0Sstevel@tonic-gate 		Perror("_symintOpen failed");
484*0Sstevel@tonic-gate 	}
485*0Sstevel@tonic-gate 	DEBUG_LOC("main: after _symintOpen");
486*0Sstevel@tonic-gate 	filhdr = *ldptr;
487*0Sstevel@tonic-gate 
488*0Sstevel@tonic-gate 	scnhdrp = ldptr->pf_shdarr_p;
489*0Sstevel@tonic-gate 
490*0Sstevel@tonic-gate 	{
491*0Sstevel@tonic-gate 	Elf_Kind k = elf_kind(filhdr.pf_elf_p);
492*0Sstevel@tonic-gate 
493*0Sstevel@tonic-gate 	DEBUG_EXP(printf("elf_kind = %d\n", k));
494*0Sstevel@tonic-gate 	DEBUG_EXP(printf("elf_type = %d\n", filhdr.pf_elfhd_p->e_type));
495*0Sstevel@tonic-gate 	if ((k != ELF_K_ELF) || (filhdr.pf_elfhd_p->e_type != ET_EXEC)) {
496*0Sstevel@tonic-gate 		Fprint(stderr, "%s: %s: improper format\n", cmdname, sym_fn);
497*0Sstevel@tonic-gate 		exit(1);
498*0Sstevel@tonic-gate 	}
499*0Sstevel@tonic-gate 	}
500*0Sstevel@tonic-gate 
501*0Sstevel@tonic-gate 	/* Compute the file address of symbol table. Machine-dependent. */
502*0Sstevel@tonic-gate 
503*0Sstevel@tonic-gate 	DEBUG_EXP(printf("number of symbols (pf_nsyms) = %d\n",
504*0Sstevel@tonic-gate 		filhdr.pf_nsyms));
505*0Sstevel@tonic-gate 
506*0Sstevel@tonic-gate 		/* Number of symbols in file symbol table. */
507*0Sstevel@tonic-gate 	symttl = filhdr.pf_nsyms;
508*0Sstevel@tonic-gate 	if (symttl == 0) {		/* This is possible. */
509*0Sstevel@tonic-gate 		Fprint(stderr, "%s: %s: no symbols\n", cmdname, sym_fn);
510*0Sstevel@tonic-gate 		exit(0);		/* Note zero exit code. */
511*0Sstevel@tonic-gate 	}
512*0Sstevel@tonic-gate 	/* Get size of file containing profiling data. Read header part. */
513*0Sstevel@tonic-gate 	n = fsize(fileno(mon_iop));
514*0Sstevel@tonic-gate 	if (fread((char *)&head, sizeof (struct hdr), 1, mon_iop) != 1)
515*0Sstevel@tonic-gate 		eofon(mon_iop, mon_fn);		/* Probably junk file. */
516*0Sstevel@tonic-gate 
517*0Sstevel@tonic-gate 	/* Get # cnt structures (they follow header), */
518*0Sstevel@tonic-gate 	/*		and allocate space for them. */
519*0Sstevel@tonic-gate 
520*0Sstevel@tonic-gate 	n_cc = head.nfns;
521*0Sstevel@tonic-gate 	ccounts = (struct cnt *)_prof_Malloc(n_cc, sizeof (struct cnt));
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate 		/* Read the call addr-count pairs. */
524*0Sstevel@tonic-gate 	if (fread((char *)ccounts, sizeof (struct cnt), n_cc, mon_iop) != n_cc)
525*0Sstevel@tonic-gate 		eofon(mon_iop, mon_fn);
526*0Sstevel@tonic-gate 
527*0Sstevel@tonic-gate 	/*
528*0Sstevel@tonic-gate 	 * Compute # PC counters (pcounts), which occupy whatever is left
529*0Sstevel@tonic-gate 	 * of the file after the header and call counts.
530*0Sstevel@tonic-gate 	 */
531*0Sstevel@tonic-gate 
532*0Sstevel@tonic-gate 	n_pc = (n - sizeof (head) - n_cc * sizeof (struct cnt))/sizeof (WORD);
533*0Sstevel@tonic-gate 	ccp = &ccounts[n_cc];	/* Point to last (+1) of call counters ... */
534*0Sstevel@tonic-gate 	do {		/* and scan backward until find highest one used. */
535*0Sstevel@tonic-gate 		if ((--ccp)->mcnt)
536*0Sstevel@tonic-gate 			break;		/* Stop when find nonzero count. */
537*0Sstevel@tonic-gate 	} while (--n_cc > 0);		/* Or all are zero. */
538*0Sstevel@tonic-gate 
539*0Sstevel@tonic-gate 	if (n_cc > 0) {
540*0Sstevel@tonic-gate 
541*0Sstevel@tonic-gate 	/* If less than all cnt entries are used, return unused space. */
542*0Sstevel@tonic-gate 	if (n_cc < head.nfns) {
543*0Sstevel@tonic-gate 		if ((ccounts = (struct cnt *)realloc((char *)ccounts,
544*0Sstevel@tonic-gate 		    (unsigned)n_cc * sizeof (struct cnt))) == NULL)
545*0Sstevel@tonic-gate 			snh();	/* Should not fail when reducing size. */
546*0Sstevel@tonic-gate 	}
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate 	/* If more than 250 cnt entries used set verbose for warning */
549*0Sstevel@tonic-gate 	if (n_cc > (MPROGS0 * 5)/6)
550*0Sstevel@tonic-gate 		flags |= F_VERBOSE;
551*0Sstevel@tonic-gate 
552*0Sstevel@tonic-gate 		/* Space for PC counts. */
553*0Sstevel@tonic-gate 	pcounts = (WORD *)_prof_Malloc(n_pc, sizeof (WORD));
554*0Sstevel@tonic-gate 		/* Read the PC counts from rest of MON_OUT file. */
555*0Sstevel@tonic-gate 	if (fread((char *)pcounts, sizeof (WORD), n_pc, mon_iop) != n_pc)
556*0Sstevel@tonic-gate 		eofon(mon_iop, mon_fn);
557*0Sstevel@tonic-gate 	/*
558*0Sstevel@tonic-gate 	 *
559*0Sstevel@tonic-gate 	 * Having gotten preliminaries out of the way, get down to business.
560*0Sstevel@tonic-gate 	 * The range pc_m of addresses over which profiling was done is
561*0Sstevel@tonic-gate 	 * computed from the low (pc_l) and high (pc_h) addresses, gotten
562*0Sstevel@tonic-gate 	 * from the MON_OUT header.  From this and the number of clock
563*0Sstevel@tonic-gate 	 * tick counters, n_pc, is computed the so-called "scale", sf, used
564*0Sstevel@tonic-gate 	 * in the mapping of addresses to indices, as follows:
565*0Sstevel@tonic-gate 	 *
566*0Sstevel@tonic-gate 	 *		(pc - pc_l) * sf
567*0Sstevel@tonic-gate 	 *	i(pc) = ----------------
568*0Sstevel@tonic-gate 	 *		  0200000
569*0Sstevel@tonic-gate 	 *
570*0Sstevel@tonic-gate 	 * Also, the N-to-one value, s_inv, such that
571*0Sstevel@tonic-gate 	 *
572*0Sstevel@tonic-gate 	 *	i(pc_l + K * s_inv + d) = K, for 0 <= d < s_inv
573*0Sstevel@tonic-gate 	 *
574*0Sstevel@tonic-gate 	 * Following this, the symbol table is scanned, and those symbols
575*0Sstevel@tonic-gate 	 * that qualify are counted.  These  are T-type symbols, excluding
576*0Sstevel@tonic-gate 	 * local (nonglobal) unless the "-g" option was given. Having thus
577*0Sstevel@tonic-gate 	 * determined the space requirements, space for symbols/times etc.
578*0Sstevel@tonic-gate 	 * is allocated, and the symbol table re-read, this time keeping
579*0Sstevel@tonic-gate 	 * qualified symbols.
580*0Sstevel@tonic-gate 	 *
581*0Sstevel@tonic-gate 	 * NB s_inv, as actually computed, is not sufficiently accurate
582*0Sstevel@tonic-gate 	 * (since it is truncated) for many calculations.  Since it is
583*0Sstevel@tonic-gate 	 * logically equivalent to 1/(sf/bias), and the latter is much
584*0Sstevel@tonic-gate 	 * more accurate, therefore the latter will often appear in
585*0Sstevel@tonic-gate 	 * the code when 's_inv' is mentioned.  dween
586*0Sstevel@tonic-gate 	 *
587*0Sstevel@tonic-gate 	 */
588*0Sstevel@tonic-gate 
589*0Sstevel@tonic-gate 
590*0Sstevel@tonic-gate 	pc_l = head.lpc;	/* Low PC of range that was profiled. */
591*0Sstevel@tonic-gate 	pc_h = head.hpc;	/* First address past range of profiling. */
592*0Sstevel@tonic-gate 	pc_m = pc_h - pc_l;	/* Range of profiled addresses. */
593*0Sstevel@tonic-gate 
594*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value) Fprint(stderr,
595*0Sstevel@tonic-gate "low pc = %#o, high pc = %#o, range = %#o = %u\n\
596*0Sstevel@tonic-gate call counts: %u, %u used; pc counters: %u\n",
597*0Sstevel@tonic-gate pc_l, pc_h, pc_m, pc_m, head.nfns, n_cc, n_pc));
598*0Sstevel@tonic-gate 
599*0Sstevel@tonic-gate 	sf = (BIAS * (double)n_pc)/pc_m;
600*0Sstevel@tonic-gate 	/*
601*0Sstevel@tonic-gate 	 * Now adjust bias and sf so that there is no overflow
602*0Sstevel@tonic-gate 	 * when calculating indices.
603*0Sstevel@tonic-gate 	 */
604*0Sstevel@tonic-gate 	bias = BIAS;
605*0Sstevel@tonic-gate 	temp = pc_m;
606*0Sstevel@tonic-gate 	while ((temp >>= 1) > 0x7fff) {
607*0Sstevel@tonic-gate 		sf >>= 1;
608*0Sstevel@tonic-gate 		bias >>= 1;
609*0Sstevel@tonic-gate 	}
610*0Sstevel@tonic-gate 	s_inv = pc_m/n_pc;	/* Range of PCs mapped into one index. */
611*0Sstevel@tonic-gate 
612*0Sstevel@tonic-gate OLD_DEBUG(
613*0Sstevel@tonic-gate 	if (debug_value) {
614*0Sstevel@tonic-gate 		Fprint(
615*0Sstevel@tonic-gate 			stderr,
616*0Sstevel@tonic-gate 			"sf = %d, s_inv = %d bias = %d\n",
617*0Sstevel@tonic-gate 			(long)sf, s_inv, bias);
618*0Sstevel@tonic-gate 	}
619*0Sstevel@tonic-gate );
620*0Sstevel@tonic-gate 
621*0Sstevel@tonic-gate 		/* Prepare to read symbols from "a.out" (or whatever). */
622*0Sstevel@tonic-gate 	n_syms = 0;			/* Init count of qualified symbols. */
623*0Sstevel@tonic-gate 	n = symttl;			/* Total symbols. */
624*0Sstevel@tonic-gate 	while (--n >= 0)			/* Scan symbol table. */
625*0Sstevel@tonic-gate 		if (readnl(n))	/* Read and examine symbol, count qualifiers */
626*0Sstevel@tonic-gate 			n_syms++;
627*0Sstevel@tonic-gate 
628*0Sstevel@tonic-gate OLD_DEBUG(
629*0Sstevel@tonic-gate 	if (debug_value) {
630*0Sstevel@tonic-gate 		Fprint(stderr, "%u symbols, %u qualify\n", symttl, n_syms);
631*0Sstevel@tonic-gate 	}
632*0Sstevel@tonic-gate );
633*0Sstevel@tonic-gate 
634*0Sstevel@tonic-gate 		/* Allocate space for qualified symbols. */
635*0Sstevel@tonic-gate 
636*0Sstevel@tonic-gate 	slist = slp =
637*0Sstevel@tonic-gate 		(struct slist *)_prof_Malloc(n_syms, sizeof (struct slist));
638*0Sstevel@tonic-gate 
639*0Sstevel@tonic-gate 		/*
640*0Sstevel@tonic-gate 		 * Allocate space for synonym symbols
641*0Sstevel@tonic-gate 		 * (i.e. symbols that refer to the same address).
642*0Sstevel@tonic-gate 		 * NB there can be no more than n_syms/2 addresses
643*0Sstevel@tonic-gate 		 * with symbols, That Have Aliases, that refer to them!
644*0Sstevel@tonic-gate 		 */
645*0Sstevel@tonic-gate 
646*0Sstevel@tonic-gate 	snymCapacity = n_syms/2;
647*0Sstevel@tonic-gate 	snymList = snymp =
648*0Sstevel@tonic-gate 		(struct snymEntry *)_prof_Malloc(snymCapacity,
649*0Sstevel@tonic-gate 		sizeof (struct snymEntry));
650*0Sstevel@tonic-gate 	n_snyms = 0;
651*0Sstevel@tonic-gate 
652*0Sstevel@tonic-gate /* OLD_DEBUG(debug_value &= ~020); */
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate 	/* Loop on number of qualified symbols. */
655*0Sstevel@tonic-gate 	for (n = n_syms, symct = 0; n > 0; symct++) {
656*0Sstevel@tonic-gate 		if (readnl(symct)) {	/* Get one. Check again. */
657*0Sstevel@tonic-gate 				/* Is qualified. Move name ... */
658*0Sstevel@tonic-gate 			slp->sl_name = getname(ldptr, nl);
659*0Sstevel@tonic-gate 
660*0Sstevel@tonic-gate 				/* and address into slist structure. */
661*0Sstevel@tonic-gate 			slp->sl_addr = (char *)nl.ps_sym.st_value;
662*0Sstevel@tonic-gate 			slp->sl_size = nl.ps_sym.st_size;
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 				/* set other slist fields to zero. */
665*0Sstevel@tonic-gate 			slp->sl_time = 0.0;
666*0Sstevel@tonic-gate 			slp->sl_count = 0;
667*0Sstevel@tonic-gate OLD_DEBUG(
668*0Sstevel@tonic-gate 	if (debug_value & 02)
669*0Sstevel@tonic-gate 		Fprint(stderr, "%-8.8s: %#8o\n", slp->sl_name, slp->sl_addr)
670*0Sstevel@tonic-gate );
671*0Sstevel@tonic-gate 
672*0Sstevel@tonic-gate 			slp++;
673*0Sstevel@tonic-gate 			--n;
674*0Sstevel@tonic-gate 		}
675*0Sstevel@tonic-gate 	}
676*0Sstevel@tonic-gate 	/*
677*0Sstevel@tonic-gate 	 *
678*0Sstevel@tonic-gate 	 * Now attempt to match call counts with symbols.  To do this, it
679*0Sstevel@tonic-gate 	 * helps to first sort both the symbols and the call address/count
680*0Sstevel@tonic-gate 	 * pairs by ascending address, since they are generally not, to
681*0Sstevel@tonic-gate 	 * begin with.  The addresses associated with the counts are not,
682*0Sstevel@tonic-gate 	 * of course, the subroutine addresses associated with the symbols,
683*0Sstevel@tonic-gate 	 * but some address slightly past these. Therefore a given count
684*0Sstevel@tonic-gate 	 * address (in the fnpc field) is matched with the closest symbol
685*0Sstevel@tonic-gate 	 * address (sl_addr) that is:
686*0Sstevel@tonic-gate 	 *	(1) less than the fnpc value but,
687*0Sstevel@tonic-gate 	 *	(2) not more than the length of the function
688*0Sstevel@tonic-gate 	 * In other words, unreasonable matchups are avoided.
689*0Sstevel@tonic-gate 	 * Situations such as this could arise when static procedures are
690*0Sstevel@tonic-gate 	 * counted but the "-g" option was not given to this program,
691*0Sstevel@tonic-gate 	 * causing the symbol to fail to qualify.  Without this limitation,
692*0Sstevel@tonic-gate 	 * unmatched counts could be erroneously charged.
693*0Sstevel@tonic-gate 	 *
694*0Sstevel@tonic-gate 	 */
695*0Sstevel@tonic-gate 
696*0Sstevel@tonic-gate 
697*0Sstevel@tonic-gate 	ccp = ccounts;			/* Point to first call counter. */
698*0Sstevel@tonic-gate 	slp = slist;			/*   "		"   "   symbol. */
699*0Sstevel@tonic-gate 		/* Sort call counters and ... */
700*0Sstevel@tonic-gate 	qsort((char *)ccp, (unsigned)n_cc, sizeof (struct cnt), c_ccaddr);
701*0Sstevel@tonic-gate 		/* symbols by increasing address. */
702*0Sstevel@tonic-gate 	qsort((char *)slp, (unsigned)n_syms, sizeof (struct slist), c_sladdr);
703*0Sstevel@tonic-gate 	vn_cc = n_cc;			/* save this for verbose option */
704*0Sstevel@tonic-gate 
705*0Sstevel@tonic-gate 
706*0Sstevel@tonic-gate 		/* Loop to match up call counts & symbols. */
707*0Sstevel@tonic-gate 	for (n = n_syms; n > 0 && vn_cc > 0; ) {
708*0Sstevel@tonic-gate 		int	sz = slp->sl_size;
709*0Sstevel@tonic-gate 
710*0Sstevel@tonic-gate 		if (sz == 0)
711*0Sstevel@tonic-gate 		    sz = slp[ 1 ].sl_addr - slp->sl_addr;
712*0Sstevel@tonic-gate 		if (slp->sl_addr < ccp->fnpc &&
713*0Sstevel@tonic-gate 		    ccp->fnpc <= slp->sl_addr + sz) {
714*0Sstevel@tonic-gate 					/* got a candidate: find Closest. */
715*0Sstevel@tonic-gate 			struct slist *closest_symp;
716*0Sstevel@tonic-gate 			do {
717*0Sstevel@tonic-gate 				closest_symp = slp;
718*0Sstevel@tonic-gate 				slp++;
719*0Sstevel@tonic-gate 				--n;
720*0Sstevel@tonic-gate 			} while (n > 0 && slp->sl_addr < ccp->fnpc);
721*0Sstevel@tonic-gate 
722*0Sstevel@tonic-gate OLD_DEBUG(
723*0Sstevel@tonic-gate if (debug_value & 04) {
724*0Sstevel@tonic-gate 	Fprint(stderr,
725*0Sstevel@tonic-gate 		"Routine %-8.8s @ %#8x+%-2d matches count address %#8x\n",
726*0Sstevel@tonic-gate 		closest_symp->sl_name,
727*0Sstevel@tonic-gate 		closest_symp->sl_addr,
728*0Sstevel@tonic-gate 		ccp->fnpc-slp->sl_addr,
729*0Sstevel@tonic-gate 		ccp->fnpc);
730*0Sstevel@tonic-gate }
731*0Sstevel@tonic-gate );
732*0Sstevel@tonic-gate 			closest_symp->sl_count = ccp->mcnt;  /* Copy count. */
733*0Sstevel@tonic-gate 			++ccp;
734*0Sstevel@tonic-gate 			--vn_cc;
735*0Sstevel@tonic-gate 		} else if (ccp->fnpc < slp->sl_addr) {
736*0Sstevel@tonic-gate 			++ccp;
737*0Sstevel@tonic-gate 			--vn_cc;
738*0Sstevel@tonic-gate 		} else {
739*0Sstevel@tonic-gate 			++slp;
740*0Sstevel@tonic-gate 			--n;
741*0Sstevel@tonic-gate 		}
742*0Sstevel@tonic-gate 	}
743*0Sstevel@tonic-gate 
744*0Sstevel@tonic-gate 	/*
745*0Sstevel@tonic-gate 	 *
746*0Sstevel@tonic-gate 	 * The distribution of times to addresses is done on a proportional
747*0Sstevel@tonic-gate 	 * basis as follows: The t counts in pcounts[i] correspond to clock
748*0Sstevel@tonic-gate 	 * ticks for values of pc in the range pc, pc+1, ..., pc+s_inv-1
749*0Sstevel@tonic-gate 	 * (odd addresses excluded for PDP11s). Without more detailed info,
750*0Sstevel@tonic-gate 	 * it must be assumed that there is no greater probability
751*0Sstevel@tonic-gate 	 * of the clock ticking for any particular pc in this range than for
752*0Sstevel@tonic-gate 	 * any other.  Thus the t counts are considered to be equally
753*0Sstevel@tonic-gate 	 * distributed over the addresses in the range, and that the time for
754*0Sstevel@tonic-gate 	 * any given address in the range is pcounts[i]/s_inv.
755*0Sstevel@tonic-gate 	 *
756*0Sstevel@tonic-gate 	 * The values of the symbols that qualify, bounded below and above
757*0Sstevel@tonic-gate 	 * by pc_l and pc_h, respectively, partition the profiling range into
758*0Sstevel@tonic-gate 	 * regions to which are assigned the total times associated with the
759*0Sstevel@tonic-gate 	 * addresses they contain in the following way:
760*0Sstevel@tonic-gate 	 *
761*0Sstevel@tonic-gate 	 * The sum of all pcounts[i] for which the corresponding addresses are
762*0Sstevel@tonic-gate 	 * wholly within the partition are charged to the partition (the
763*0Sstevel@tonic-gate 	 * subroutine whose address is the lower bound of the partition).
764*0Sstevel@tonic-gate 	 *
765*0Sstevel@tonic-gate 	 * If the range of addresses corresponding to a given t = pcounts[i]
766*0Sstevel@tonic-gate 	 * lies astraddle the boundary of a partition, e.g., for some k such
767*0Sstevel@tonic-gate 	 * that 0 < k < s_inv-1, the addresses pc, pc+1, ..., pc+k-1 are in
768*0Sstevel@tonic-gate 	 * the lower partition, and the addresses pc+k, pc+k+1, ..., pc+s_inv-1
769*0Sstevel@tonic-gate 	 * are in the next partition, then k*pcounts[i]/s_inv time is charged
770*0Sstevel@tonic-gate 	 * to the lower partition, and (s_inv-k) * pcounts[i]/s_inv time to the
771*0Sstevel@tonic-gate 	 * upper.  It is conceivable, in cases of large granularity or small
772*0Sstevel@tonic-gate 	 * subroutines, for a range corresponding to a given pcounts[i] to
773*0Sstevel@tonic-gate 	 * overlap three regions, completely containing the (small) middle one.
774*0Sstevel@tonic-gate 	 * The algorithm is adjusted appropriately in this case.
775*0Sstevel@tonic-gate 	 *
776*0Sstevel@tonic-gate 	 */
777*0Sstevel@tonic-gate 
778*0Sstevel@tonic-gate 
779*0Sstevel@tonic-gate 	pcp = pcounts;				/* Reset to base. */
780*0Sstevel@tonic-gate 	slp = slist;				/* Ditto. */
781*0Sstevel@tonic-gate 	t0 = 0.0;				/* Time accumulator. */
782*0Sstevel@tonic-gate 	for (n = 0; n < n_syms; n++) {		/* Loop on symbols. */
783*0Sstevel@tonic-gate 			/* Start addr of region, low addr of overlap. */
784*0Sstevel@tonic-gate 		char *pc0, *pc00;
785*0Sstevel@tonic-gate 			/* Start addr of next region, low addr of overlap. */
786*0Sstevel@tonic-gate 		char *pc1, *pc10;
787*0Sstevel@tonic-gate 		/* First index into pcounts for this region and next region. */
788*0Sstevel@tonic-gate 		register int i0, i1;
789*0Sstevel@tonic-gate 		long ticks;
790*0Sstevel@tonic-gate 
791*0Sstevel@tonic-gate 			/* Address of symbol (subroutine). */
792*0Sstevel@tonic-gate 		pc0 = slp[n].sl_addr;
793*0Sstevel@tonic-gate 
794*0Sstevel@tonic-gate 			/* Address of next symbol, if any or top */
795*0Sstevel@tonic-gate 			/* of profile range, if not */
796*0Sstevel@tonic-gate 		pc1 = (n < n_syms - 1) ? slp[n+1].sl_addr : pc_h;
797*0Sstevel@tonic-gate 
798*0Sstevel@tonic-gate 			/* Lower bound of indices into pcounts for this range */
799*0Sstevel@tonic-gate 
800*0Sstevel@tonic-gate 		i0 = (((unsigned)pc0 - (unsigned)pc_l) * sf)/bias;
801*0Sstevel@tonic-gate 
802*0Sstevel@tonic-gate 			/* Upper bound (least or least + 1) of indices. */
803*0Sstevel@tonic-gate 		i1 = (((unsigned)pc1 - (unsigned)pc_l) * sf)/bias;
804*0Sstevel@tonic-gate 
805*0Sstevel@tonic-gate 		if (i1 >= n_pc)				/* If past top, */
806*0Sstevel@tonic-gate 			i1 = n_pc - 1;				/* adjust. */
807*0Sstevel@tonic-gate 
808*0Sstevel@tonic-gate 			/* Lowest addr for which count maps to pcounts[i0]; */
809*0Sstevel@tonic-gate 		pc00 =  pc_l + (unsigned long)((bias * i0)/sf);
810*0Sstevel@tonic-gate 
811*0Sstevel@tonic-gate 			/* Lowest addr for which count maps to pcounts[i1]. */
812*0Sstevel@tonic-gate 		pc10 =  pc_l + (unsigned long)((bias * i1)/sf);
813*0Sstevel@tonic-gate 
814*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) Fprint(stderr,
815*0Sstevel@tonic-gate "%-8.8s\ti0 = %4d, pc00 = %#6o, pc0 = %#6o\n\
816*0Sstevel@tonic-gate \t\ti1 = %4d, pc10 = %#6o, pc1 = %#6o\n\t\t",
817*0Sstevel@tonic-gate slp[n].sl_name, i0, pc00, pc0, i1, pc10, pc1));
818*0Sstevel@tonic-gate 		t = 0;			/* Init time for this symbol. */
819*0Sstevel@tonic-gate 		if (i0 == i1) {
820*0Sstevel@tonic-gate 			/* Counter overlaps two areas? (unlikely */
821*0Sstevel@tonic-gate 			/* unless large granularity). */
822*0Sstevel@tonic-gate 			ticks = pcp[i0];	/* # Times (clock ticks). */
823*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks));
824*0Sstevel@tonic-gate 
825*0Sstevel@tonic-gate 			    /* Time less that which overlaps adjacent areas */
826*0Sstevel@tonic-gate 			t += PROFSEC(ticks * ((double)(pc1 - pc0) * sf)/bias);
827*0Sstevel@tonic-gate 
828*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010)
829*0Sstevel@tonic-gate 	Fprint(stderr, "%ld/(%.1f)", (pc1 - pc0) * ticks, DBL_ADDRPERCELL)
830*0Sstevel@tonic-gate );
831*0Sstevel@tonic-gate 		} else {
832*0Sstevel@tonic-gate 				/* Overlap with previous region? */
833*0Sstevel@tonic-gate 			if (pc00 < pc0) {
834*0Sstevel@tonic-gate 				ticks = pcp[i0];
835*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010)
836*0Sstevel@tonic-gate 	fprintf(stderr, "pc00 < pc0 ticks = %d\n", ticks));
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate 				/* Get time of overlapping area and */
839*0Sstevel@tonic-gate 				/* subtract proportion for lower region. */
840*0Sstevel@tonic-gate 				t += PROFSEC(
841*0Sstevel@tonic-gate 				ticks*(1-((double)(pc0-pc00) *sf)/bias));
842*0Sstevel@tonic-gate 
843*0Sstevel@tonic-gate 				/* Do not count this time when summing times */
844*0Sstevel@tonic-gate 				/*		wholly within the region. */
845*0Sstevel@tonic-gate 				i0++;
846*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010)
847*0Sstevel@tonic-gate 	Fprint(stderr, "%ld/(%.1f) + ", (pc0 - pc00) * ticks,
848*0Sstevel@tonic-gate 		DBL_ADDRPERCELL));
849*0Sstevel@tonic-gate 			}
850*0Sstevel@tonic-gate 
851*0Sstevel@tonic-gate 			/* Init sum of counts for PCs not shared w/other */
852*0Sstevel@tonic-gate 			/*	routines. */
853*0Sstevel@tonic-gate 			ticks = 0;
854*0Sstevel@tonic-gate 
855*0Sstevel@tonic-gate 			/* Stop at first count that overlaps following */
856*0Sstevel@tonic-gate 			/*	routine. */
857*0Sstevel@tonic-gate 			for (i = i0; i < i1; i++)
858*0Sstevel@tonic-gate 				ticks += pcp[i];
859*0Sstevel@tonic-gate 
860*0Sstevel@tonic-gate 			t += PROFSEC(ticks); /* Convert to secs, add to total */
861*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) Fprint(stderr, "%ld", ticks));
862*0Sstevel@tonic-gate 			/* Some overlap with low addresses of next routine? */
863*0Sstevel@tonic-gate 			if (pc10 < pc1) {
864*0Sstevel@tonic-gate 					/* Yes. Get total count ... */
865*0Sstevel@tonic-gate 				ticks = pcp[i1];
866*0Sstevel@tonic-gate 
867*0Sstevel@tonic-gate 				/* and accumulate proportion for addresses in */
868*0Sstevel@tonic-gate 				/*		range of this routine */
869*0Sstevel@tonic-gate 				t += PROFSEC(((double)ticks *
870*0Sstevel@tonic-gate 					(pc1 - pc10)*sf)/bias);
871*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks));
872*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010)
873*0Sstevel@tonic-gate 	Fprint(stderr, " + %ld/(%.1f)", (pc1 - pc10) * ticks, DBL_ADDRPERCELL)
874*0Sstevel@tonic-gate );
875*0Sstevel@tonic-gate 			}
876*0Sstevel@tonic-gate 		}		/* End if (i0 == i1) ... else ... */
877*0Sstevel@tonic-gate 
878*0Sstevel@tonic-gate 		slp[n].sl_time = t;	/* Store time for this routine. */
879*0Sstevel@tonic-gate 		t0 += t;		/* Accumulate total time. */
880*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) Fprint(stderr, " ticks = %.2f msec\n", t));
881*0Sstevel@tonic-gate 	}	/* End for (n = 0; n < n_syms; n++) */
882*0Sstevel@tonic-gate 
883*0Sstevel@tonic-gate 	/* Final pass to total up time. */
884*0Sstevel@tonic-gate 	/* Sum ticks, then convert to seconds. */
885*0Sstevel@tonic-gate 
886*0Sstevel@tonic-gate 	for (n = n_pc, temp = 0; --n >= 0; temp += *(pcp++));
887*0Sstevel@tonic-gate 
888*0Sstevel@tonic-gate 	t_tot = PROFSEC(temp);
889*0Sstevel@tonic-gate 
890*0Sstevel@tonic-gate 	/*
891*0Sstevel@tonic-gate 	 * Now, whilst we still have the symbols sorted
892*0Sstevel@tonic-gate 	 * in address order..
893*0Sstevel@tonic-gate 	 * Loop to record duplicates, so we can display
894*0Sstevel@tonic-gate 	 * synonym symbols correctly.
895*0Sstevel@tonic-gate 	 * Synonym symbols, or symbols with the same address,
896*0Sstevel@tonic-gate 	 * are to be displayed by prof on the same line, with
897*0Sstevel@tonic-gate 	 * one statistics line, as below:
898*0Sstevel@tonic-gate 	 *			... 255  ldaopen, ldaopen
899*0Sstevel@tonic-gate 	 * The way this will be implemented, is as follows:
900*0Sstevel@tonic-gate 	 *
901*0Sstevel@tonic-gate 	 * Pass 1 - while the symbols are in address order, we
902*0Sstevel@tonic-gate 	 *  do a pre-pass through them, to determine for which
903*0Sstevel@tonic-gate 	 *  addresses there are more than one symbol (i.e. synonyms).
904*0Sstevel@tonic-gate 	 *  During this prepass we collect summary statistics in
905*0Sstevel@tonic-gate 	 *  the synonym entry, for all the synonyms.
906*0Sstevel@tonic-gate 	 *
907*0Sstevel@tonic-gate 	 * 'Pass' 2 - while printing a report,  for each report line,
908*0Sstevel@tonic-gate 	 *  if the current symbol is a synonym symbol (i.e. in the
909*0Sstevel@tonic-gate 	 *  snymList) then we scan forward and pick up all the names
910*0Sstevel@tonic-gate 	 *  which map to this address, and print them too.
911*0Sstevel@tonic-gate 	 *  If the address' synonyms have already been printed, then
912*0Sstevel@tonic-gate 	 *  we just skip this symbol and go on to process the next.
913*0Sstevel@tonic-gate 	 *
914*0Sstevel@tonic-gate 	 */
915*0Sstevel@tonic-gate 
916*0Sstevel@tonic-gate 	{
917*0Sstevel@tonic-gate 	/* pass 1 */
918*0Sstevel@tonic-gate 	char *thisaddr;
919*0Sstevel@tonic-gate 	char *lastaddr = slist->sl_addr; /* use 1st sym as */
920*0Sstevel@tonic-gate 					/* 'last/prior symbol' */
921*0Sstevel@tonic-gate 	int lastWasSnym = 0;	/* 1st can't be snym yet-no aliases seen! */
922*0Sstevel@tonic-gate 	int thisIsSnym;
923*0Sstevel@tonic-gate 
924*0Sstevel@tonic-gate OLD_DEBUG(
925*0Sstevel@tonic-gate int totsnyms = 0; int totseries = 0; struct slist *lastslp = slist;
926*0Sstevel@tonic-gate );
927*0Sstevel@tonic-gate 
928*0Sstevel@tonic-gate 	/* NB loop starts with 2nd symbol, loops over n_syms-1 symbols! */
929*0Sstevel@tonic-gate 	for (n = n_syms-1, slp = slist+1; --n >= 0; slp++) {
930*0Sstevel@tonic-gate 		thisaddr = slp->sl_addr;
931*0Sstevel@tonic-gate 		thisIsSnym = (thisaddr == lastaddr);
932*0Sstevel@tonic-gate 
933*0Sstevel@tonic-gate 		if (thisIsSnym) {
934*0Sstevel@tonic-gate 			/* gotta synonym */
935*0Sstevel@tonic-gate 			if (!lastWasSnym) {
936*0Sstevel@tonic-gate OLD_DEBUG(
937*0Sstevel@tonic-gate if (debug_value)  {
938*0Sstevel@tonic-gate 	Fprint(stderr,
939*0Sstevel@tonic-gate 		"Synonym series:\n1st->\t%s at address %x, ct=%ld, time=%f\n",
940*0Sstevel@tonic-gate 		lastslp->sl_name, lastaddr, lastslp->sl_count,
941*0Sstevel@tonic-gate 		lastslp->sl_time);
942*0Sstevel@tonic-gate 	totseries++;
943*0Sstevel@tonic-gate 	totsnyms++;
944*0Sstevel@tonic-gate }
945*0Sstevel@tonic-gate );
946*0Sstevel@tonic-gate 				/* this is the Second! of a series */
947*0Sstevel@tonic-gate 				snymp = (n_snyms++ == 0 ? snymList : snymp+1);
948*0Sstevel@tonic-gate 				snymp->howMany = 1; /* gotta count 1st one!! */
949*0Sstevel@tonic-gate 				snymp->sym_addr = slp->sl_addr;
950*0Sstevel@tonic-gate 				/* zero summary statistics */
951*0Sstevel@tonic-gate 				snymp->tot_sl_count = 0;
952*0Sstevel@tonic-gate 				snymp->tot_sl_time = 0.0;
953*0Sstevel@tonic-gate 				/* Offen the Reported flag */
954*0Sstevel@tonic-gate 				snymp->snymReported = 0;
955*0Sstevel@tonic-gate 			}
956*0Sstevel@tonic-gate OLD_DEBUG(
957*0Sstevel@tonic-gate if (debug_value)  {
958*0Sstevel@tonic-gate 	Fprint(stderr,
959*0Sstevel@tonic-gate 		"\t%s at address %x, ct=%ld, time=%f\n",
960*0Sstevel@tonic-gate 		slp->sl_name,
961*0Sstevel@tonic-gate 		thisaddr,
962*0Sstevel@tonic-gate 		slp->sl_count,
963*0Sstevel@tonic-gate 		slp->sl_time);
964*0Sstevel@tonic-gate 	totsnyms++;
965*0Sstevel@tonic-gate }
966*0Sstevel@tonic-gate );
967*0Sstevel@tonic-gate 			/* ok - bump count for snym, and note its Finding */
968*0Sstevel@tonic-gate 			snymp->howMany++;
969*0Sstevel@tonic-gate 			/* and update the summary statistics */
970*0Sstevel@tonic-gate 			snymp->tot_sl_count += slp->sl_count;
971*0Sstevel@tonic-gate 			snymp->tot_sl_time += slp->sl_time;
972*0Sstevel@tonic-gate 		}
973*0Sstevel@tonic-gate 		callTotal += slp->sl_count;
974*0Sstevel@tonic-gate 		lastaddr = thisaddr;
975*0Sstevel@tonic-gate 		lastWasSnym = thisIsSnym;
976*0Sstevel@tonic-gate OLD_DEBUG(
977*0Sstevel@tonic-gate if (debug_value) lastslp = slp;
978*0Sstevel@tonic-gate );
979*0Sstevel@tonic-gate 
980*0Sstevel@tonic-gate 	}
981*0Sstevel@tonic-gate OLD_DEBUG(
982*0Sstevel@tonic-gate if (debug_value)  {
983*0Sstevel@tonic-gate 	Fprint(stderr, "Total #series %d, #synonyms %d\n", totseries, totsnyms);
984*0Sstevel@tonic-gate }
985*0Sstevel@tonic-gate );
986*0Sstevel@tonic-gate 	}
987*0Sstevel@tonic-gate 	/*
988*0Sstevel@tonic-gate 	 * Most of the heavy work is done now.  Only minor stuff remains.
989*0Sstevel@tonic-gate 	 * The symbols are currently in address order and must be re-sorted
990*0Sstevel@tonic-gate 	 * if desired in a different order.  Report generating options
991*0Sstevel@tonic-gate 	 * include "-o" or "-x": Include symbol address, which causes
992*0Sstevel@tonic-gate 	 * another column
993*0Sstevel@tonic-gate 	 * in the output; and "-z": Include symbols in report even if zero
994*0Sstevel@tonic-gate 	 * time and call count.  Symbols not in profiling range are excluded
995*0Sstevel@tonic-gate 	 * in any case.  Following the main body of the report, the "-s"
996*0Sstevel@tonic-gate 	 * option causes certain additional information to be printed.
997*0Sstevel@tonic-gate 	 */
998*0Sstevel@tonic-gate 
999*0Sstevel@tonic-gate OLD_DEBUG(if (debug_value) Fprint(stderr,
1000*0Sstevel@tonic-gate "Time unaccounted for: %.7G\n", t_tot - t0));
1001*0Sstevel@tonic-gate 
1002*0Sstevel@tonic-gate 	if (sort)	/* If comparison routine given then use it. */
1003*0Sstevel@tonic-gate 		qsort((char *)slist, (unsigned)n_syms,
1004*0Sstevel@tonic-gate 			sizeof (struct slist), sort);
1005*0Sstevel@tonic-gate 
1006*0Sstevel@tonic-gate 	if (!(flags & F_NHEAD)) {
1007*0Sstevel@tonic-gate 		if (flags & F_PADDR)
1008*0Sstevel@tonic-gate 			Print(atitle);	/* Title for addresses. */
1009*0Sstevel@tonic-gate 		(void) puts(" %Time Seconds Cumsecs  #Calls   msec/call  Name");
1010*0Sstevel@tonic-gate 	}
1011*0Sstevel@tonic-gate 	t = 0.0;			/* Init cumulative time. */
1012*0Sstevel@tonic-gate 	if (t_tot != 0.0)		/* Convert to percent. */
1013*0Sstevel@tonic-gate 		t_tot = 100.0/t_tot;	/* Prevent divide-by-zero fault */
1014*0Sstevel@tonic-gate 	n_nonzero = 0;	/* Number of symbols with nonzero time or # calls. */
1015*0Sstevel@tonic-gate 	for (n = n_syms, slp = slist; --n >= 0; slp++) {
1016*0Sstevel@tonic-gate 		long count;	 /* # Calls. */
1017*0Sstevel@tonic-gate 		/* t0, time in seconds. */
1018*0Sstevel@tonic-gate 
1019*0Sstevel@tonic-gate 		/* if a snym symbol, use summarized stats, else use indiv. */
1020*0Sstevel@tonic-gate 		if ((snymp = getSnymEntry(slp->sl_addr)) != 0) {
1021*0Sstevel@tonic-gate 			count = snymp->tot_sl_count;
1022*0Sstevel@tonic-gate 			t0 = snymp->tot_sl_time;
1023*0Sstevel@tonic-gate 
1024*0Sstevel@tonic-gate 		} else {
1025*0Sstevel@tonic-gate 			count = slp->sl_count;
1026*0Sstevel@tonic-gate 			t0 = slp->sl_time;
1027*0Sstevel@tonic-gate 		}
1028*0Sstevel@tonic-gate 
1029*0Sstevel@tonic-gate 		/* if a snym and already reported, skip this entry */
1030*0Sstevel@tonic-gate 		if (snymp && snymp->snymReported)
1031*0Sstevel@tonic-gate 			continue;
1032*0Sstevel@tonic-gate 		/* Don't do entries with no action. */
1033*0Sstevel@tonic-gate 		if (t0 == 0.0 && count == 0 && !(flags & F_ZSYMS))
1034*0Sstevel@tonic-gate 			continue;
1035*0Sstevel@tonic-gate 		if ((strcmp(slp->sl_name, "_mcount") == 0) ||
1036*0Sstevel@tonic-gate 		    (strcmp(slp->sl_name, "mcount") == 0)) {
1037*0Sstevel@tonic-gate 		    count = callTotal;
1038*0Sstevel@tonic-gate 		}
1039*0Sstevel@tonic-gate 
1040*0Sstevel@tonic-gate 		/* count number of entries (i.e. symbols) printed */
1041*0Sstevel@tonic-gate 		if (snymp)
1042*0Sstevel@tonic-gate 			n_nonzero += snymp->howMany; /* add for each snym */
1043*0Sstevel@tonic-gate 		else
1044*0Sstevel@tonic-gate 			n_nonzero++;
1045*0Sstevel@tonic-gate 
1046*0Sstevel@tonic-gate 		if (flags & F_PADDR)	/* Printing address of symbol? */
1047*0Sstevel@tonic-gate 			Print(aformat, slp->sl_addr);
1048*0Sstevel@tonic-gate 		t += t0;	/*  move here; compiler bug  !! */
1049*0Sstevel@tonic-gate 		Print("%6.1f%8.2f%8.2f", t0 * t_tot, t0, t);
1050*0Sstevel@tonic-gate 		fdigits = 0;
1051*0Sstevel@tonic-gate 		if (count) {		/* Any calls recorded? */
1052*0Sstevel@tonic-gate 		/* Get reasonable number of fractional digits to print. */
1053*0Sstevel@tonic-gate 			fdigits = fprecision(count);
1054*0Sstevel@tonic-gate 			Print("%8ld%#*.*f", count, fdigits+8, fdigits,
1055*0Sstevel@tonic-gate 			    1000.0*t0/count);
1056*0Sstevel@tonic-gate 			Print("%*s", 6-fdigits, " ");
1057*0Sstevel@tonic-gate 		} else {
1058*0Sstevel@tonic-gate 			Print("%22s", " ");
1059*0Sstevel@tonic-gate 		}
1060*0Sstevel@tonic-gate 		/*
1061*0Sstevel@tonic-gate 		 * now print the name (or comma-seperate list of names,
1062*0Sstevel@tonic-gate 		 * for synonym symbols).
1063*0Sstevel@tonic-gate 		 */
1064*0Sstevel@tonic-gate 		if (snymp) {
1065*0Sstevel@tonic-gate 			printSnymNames(slp, snymp);	/* print it, then */
1066*0Sstevel@tonic-gate 			snymp->snymReported = 1;	/* mark it Done */
1067*0Sstevel@tonic-gate 		}
1068*0Sstevel@tonic-gate 		else
1069*0Sstevel@tonic-gate 			(void) puts(slp->sl_name);	/* print the one name */
1070*0Sstevel@tonic-gate 	}
1071*0Sstevel@tonic-gate 	if (flags & F_VERBOSE) {		/* Extra info? */
1072*0Sstevel@tonic-gate 		Fprint(stderr, "%5d/%d call counts used\n", n_cc, head.nfns);
1073*0Sstevel@tonic-gate 		Fprint(stderr, "%5d/%d symbols qualified", n_syms, symttl);
1074*0Sstevel@tonic-gate 		if (n_nonzero < n_syms)
1075*0Sstevel@tonic-gate 			Fprint(stderr,
1076*0Sstevel@tonic-gate 			    ", %d had zero time and zero call-counts\n",
1077*0Sstevel@tonic-gate 			    n_syms - n_nonzero);
1078*0Sstevel@tonic-gate 		else
1079*0Sstevel@tonic-gate 			(void) putc('\n', stderr);
1080*0Sstevel@tonic-gate 		Fprint(stderr, "%#x scale factor\n", (long)sf);
1081*0Sstevel@tonic-gate 	}
1082*0Sstevel@tonic-gate 
1083*0Sstevel@tonic-gate 	_symintClose(ldptr);
1084*0Sstevel@tonic-gate 	} else {
1085*0Sstevel@tonic-gate 	    Fprint(stderr, "prof: no call counts captured\n");
1086*0Sstevel@tonic-gate 	}
1087*0Sstevel@tonic-gate 	exit(0);
1088*0Sstevel@tonic-gate }
1089*0Sstevel@tonic-gate /* Return size of file associated with file descriptor fd. */
1090*0Sstevel@tonic-gate 
1091*0Sstevel@tonic-gate PROC off_t
1092*0Sstevel@tonic-gate fsize(fd)
1093*0Sstevel@tonic-gate {
1094*0Sstevel@tonic-gate 	struct stat sbuf;
1095*0Sstevel@tonic-gate 
1096*0Sstevel@tonic-gate 	if (fstat(fd, &sbuf) < 0)  /* Status of open file. */
1097*0Sstevel@tonic-gate 		Perror("stat");
1098*0Sstevel@tonic-gate 	return (sbuf.st_size);			/* This is a long. */
1099*0Sstevel@tonic-gate }
1100*0Sstevel@tonic-gate 
1101*0Sstevel@tonic-gate /* Read symbol entry. Return TRUE if satisfies conditions. */
1102*0Sstevel@tonic-gate 
1103*0Sstevel@tonic-gate PROC
1104*0Sstevel@tonic-gate readnl(symindex)
1105*0Sstevel@tonic-gate int symindex;
1106*0Sstevel@tonic-gate {
1107*0Sstevel@tonic-gate 	nl = ldptr->pf_symarr_p[symindex];
1108*0Sstevel@tonic-gate 
1109*0Sstevel@tonic-gate OLD_DEBUG(
1110*0Sstevel@tonic-gate 	if (debug_value & 020) {
1111*0Sstevel@tonic-gate 		Fprint(stderr,
1112*0Sstevel@tonic-gate 			"`%-8.8s'\tst_info=%#4o, value=%#8.6o\n",
1113*0Sstevel@tonic-gate 			ldptr->pf_symstr_p[nl.ps_sym.st_name],
1114*0Sstevel@tonic-gate 			(unsigned char) nl.ps_sym.st_info,
1115*0Sstevel@tonic-gate 			nl.ps_sym.st_value);
1116*0Sstevel@tonic-gate 	}
1117*0Sstevel@tonic-gate );
1118*0Sstevel@tonic-gate 	/*
1119*0Sstevel@tonic-gate 	 * TXTSYM accepts global (and local, if "-g" given) T-type symbols.
1120*0Sstevel@tonic-gate 	 * Only those in the profiling range are useful.
1121*0Sstevel@tonic-gate 	 */
1122*0Sstevel@tonic-gate 	return (nl.ps_sym.st_shndx < SHN_LORESERVE &&
1123*0Sstevel@tonic-gate 		TXTSYM(nl.ps_sym.st_shndx,
1124*0Sstevel@tonic-gate 			nl.ps_sym.st_info) &&
1125*0Sstevel@tonic-gate 		(pc_l <= (char *)nl.ps_sym.st_value) &&
1126*0Sstevel@tonic-gate 		((char *)nl.ps_sym.st_value < pc_h));
1127*0Sstevel@tonic-gate }
1128*0Sstevel@tonic-gate /*
1129*0Sstevel@tonic-gate  * Error-checking memory allocators -
1130*0Sstevel@tonic-gate  * Guarantees good return (else none at all).
1131*0Sstevel@tonic-gate  */
1132*0Sstevel@tonic-gate 
1133*0Sstevel@tonic-gate PROC char *
1134*0Sstevel@tonic-gate _prof_Malloc(item_count, item_size)
1135*0Sstevel@tonic-gate int item_count;
1136*0Sstevel@tonic-gate int item_size;
1137*0Sstevel@tonic-gate {
1138*0Sstevel@tonic-gate 	register char *p;
1139*0Sstevel@tonic-gate 
1140*0Sstevel@tonic-gate 	if ((p = malloc((unsigned)item_count * (unsigned)item_size)) == NULL)  {
1141*0Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: Out of space\n", cmdname);
1142*0Sstevel@tonic-gate 		exit(1);
1143*0Sstevel@tonic-gate 	}
1144*0Sstevel@tonic-gate 	return (p);
1145*0Sstevel@tonic-gate }
1146*0Sstevel@tonic-gate 
1147*0Sstevel@tonic-gate 
1148*0Sstevel@tonic-gate 
1149*0Sstevel@tonic-gate /*
1150*0Sstevel@tonic-gate  *	Given the quotient Q = N/D, where entier(N) == N and D > 0, an
1151*0Sstevel@tonic-gate  *	approximation of the "best" number of fractional digits to use
1152*0Sstevel@tonic-gate  *	in printing Q is f = entier(log10(D)), which is crudely produced
1153*0Sstevel@tonic-gate  *	by the following routine.
1154*0Sstevel@tonic-gate  */
1155*0Sstevel@tonic-gate 
1156*0Sstevel@tonic-gate PROC int
1157*0Sstevel@tonic-gate fprecision(count)
1158*0Sstevel@tonic-gate long count;
1159*0Sstevel@tonic-gate {
1160*0Sstevel@tonic-gate 	return (count < 10 ? 0 : count < 100 ? 1 : count < 1000 ? 2 :
1161*0Sstevel@tonic-gate 	    count < 10000 ? 3 : 4);
1162*0Sstevel@tonic-gate }
1163*0Sstevel@tonic-gate 
1164*0Sstevel@tonic-gate /*
1165*0Sstevel@tonic-gate  *	Return pointer to base name(name less path) of string s.
1166*0Sstevel@tonic-gate  *	Handles case of superfluous trailing '/'s, and unlikely
1167*0Sstevel@tonic-gate  *	case of s == "/".
1168*0Sstevel@tonic-gate  */
1169*0Sstevel@tonic-gate 
1170*0Sstevel@tonic-gate PROC char *
1171*0Sstevel@tonic-gate basename(s)
1172*0Sstevel@tonic-gate register char *s;
1173*0Sstevel@tonic-gate {
1174*0Sstevel@tonic-gate 	register char *p;
1175*0Sstevel@tonic-gate 
1176*0Sstevel@tonic-gate 	p = &s[strlen(s)];			/* End (+1) of string. */
1177*0Sstevel@tonic-gate 	while (p > s && *--p == '/')		/* Trim trailing '/'s. */
1178*0Sstevel@tonic-gate 		*p = '\0';
1179*0Sstevel@tonic-gate 	p++;					/* New end (+1) of string. */
1180*0Sstevel@tonic-gate 	while (p > s && *--p != '/');		/* Break backward on '/'. */
1181*0Sstevel@tonic-gate 	if (*p == '/')		/* If found '/', point to 1st following. */
1182*0Sstevel@tonic-gate 		p++;
1183*0Sstevel@tonic-gate 	if (*p == '\0')
1184*0Sstevel@tonic-gate 		p = "/";			/* If NULL, must be "/". (?) */
1185*0Sstevel@tonic-gate 	return (p);
1186*0Sstevel@tonic-gate }
1187*0Sstevel@tonic-gate /* Here if unexpected read problem. */
1188*0Sstevel@tonic-gate 
1189*0Sstevel@tonic-gate PROC
1190*0Sstevel@tonic-gate eofon(iop, fn)
1191*0Sstevel@tonic-gate register FILE *iop;
1192*0Sstevel@tonic-gate register char *fn;
1193*0Sstevel@tonic-gate {
1194*0Sstevel@tonic-gate 	if (ferror(iop))		/* Real error? */
1195*0Sstevel@tonic-gate 		Perror(fn);		/* Yes. */
1196*0Sstevel@tonic-gate 	Fprint(stderr, "%s: %s: Premature EOF\n", cmdname, fn);
1197*0Sstevel@tonic-gate 	exit(1);
1198*0Sstevel@tonic-gate }
1199*0Sstevel@tonic-gate 
1200*0Sstevel@tonic-gate /* Version of perror() that prints cmdname first. */
1201*0Sstevel@tonic-gate 
1202*0Sstevel@tonic-gate PROC
1203*0Sstevel@tonic-gate Perror(s)
1204*0Sstevel@tonic-gate char *s;
1205*0Sstevel@tonic-gate {				/* Print system error message & exit. */
1206*0Sstevel@tonic-gate 	register int err = errno;	/* Save current errno in case */
1207*0Sstevel@tonic-gate 
1208*0Sstevel@tonic-gate 	Fprint(stderr, "%s: ", cmdname);
1209*0Sstevel@tonic-gate 	errno = err;			/* Put real error back. */
1210*0Sstevel@tonic-gate 	perror(s);			/* Print message. */
1211*0Sstevel@tonic-gate 	_symintClose(ldptr);		/* cleanup symbol information */
1212*0Sstevel@tonic-gate 	exit(1);			/* Exit w/nonzero status. */
1213*0Sstevel@tonic-gate }
1214*0Sstevel@tonic-gate 
1215*0Sstevel@tonic-gate /* Here for things that "Should Never Happen". */
1216*0Sstevel@tonic-gate 
1217*0Sstevel@tonic-gate PROC
1218*0Sstevel@tonic-gate snh()
1219*0Sstevel@tonic-gate {
1220*0Sstevel@tonic-gate 	Fprint(stderr, "%s: Internal error\n", cmdname);
1221*0Sstevel@tonic-gate 	(void) abort();
1222*0Sstevel@tonic-gate }
1223*0Sstevel@tonic-gate /*
1224*0Sstevel@tonic-gate  *	Various comparison routines for qsort. Uses:
1225*0Sstevel@tonic-gate  *
1226*0Sstevel@tonic-gate  *	c_ccaddr	- Compare fnpc fields of cnt structs to put
1227*0Sstevel@tonic-gate  *				call counters in increasing address order.
1228*0Sstevel@tonic-gate  *	c_sladdr	- Sort slist structures on increasing address.
1229*0Sstevel@tonic-gate  *	c_time		-  "	 "	  "      " decreasing time.
1230*0Sstevel@tonic-gate  *	c_ncalls	-  "	 "	  "      " decreasing # calls.
1231*0Sstevel@tonic-gate  *	c_name		-  "	 "	  "      " increasing symbol name
1232*0Sstevel@tonic-gate  */
1233*0Sstevel@tonic-gate 
1234*0Sstevel@tonic-gate #define	CMP2(v1, v2)	((v1) < (v2) ? -1 : (v1) == (v2) ? 0 : 1)
1235*0Sstevel@tonic-gate #define	CMP1(v)		CMP2(v, 0)
1236*0Sstevel@tonic-gate 
1237*0Sstevel@tonic-gate PROC
1238*0Sstevel@tonic-gate c_ccaddr(p1, p2)
1239*0Sstevel@tonic-gate register struct cnt *p1, *p2;
1240*0Sstevel@tonic-gate {
1241*0Sstevel@tonic-gate 	return (CMP2(p1->fnpc, p2->fnpc));
1242*0Sstevel@tonic-gate }
1243*0Sstevel@tonic-gate 
1244*0Sstevel@tonic-gate PROC
1245*0Sstevel@tonic-gate c_sladdr(p1, p2)
1246*0Sstevel@tonic-gate register struct slist *p1, *p2;
1247*0Sstevel@tonic-gate {
1248*0Sstevel@tonic-gate 	return (CMP2(p1->sl_addr, p2->sl_addr));
1249*0Sstevel@tonic-gate }
1250*0Sstevel@tonic-gate 
1251*0Sstevel@tonic-gate PROC
1252*0Sstevel@tonic-gate c_time(p1, p2)
1253*0Sstevel@tonic-gate register struct slist *p1, *p2;
1254*0Sstevel@tonic-gate {
1255*0Sstevel@tonic-gate 	register float dtime = p2->sl_time - p1->sl_time; /* Decreasing time. */
1256*0Sstevel@tonic-gate 
1257*0Sstevel@tonic-gate 	return (CMP1(dtime));
1258*0Sstevel@tonic-gate }
1259*0Sstevel@tonic-gate 
1260*0Sstevel@tonic-gate PROC
1261*0Sstevel@tonic-gate c_ncalls(p1, p2)
1262*0Sstevel@tonic-gate register struct slist *p1, *p2;
1263*0Sstevel@tonic-gate {
1264*0Sstevel@tonic-gate 	register int diff = p2->sl_count - p1->sl_count;
1265*0Sstevel@tonic-gate 		/* Decreasing # calls. */
1266*0Sstevel@tonic-gate 	return (CMP1(diff));
1267*0Sstevel@tonic-gate }
1268*0Sstevel@tonic-gate 
1269*0Sstevel@tonic-gate PROC
1270*0Sstevel@tonic-gate c_name(p1, p2)
1271*0Sstevel@tonic-gate register struct slist *p1, *p2;
1272*0Sstevel@tonic-gate {
1273*0Sstevel@tonic-gate 	register int diff;
1274*0Sstevel@tonic-gate 
1275*0Sstevel@tonic-gate 		/* flex names has variable length strings for names */
1276*0Sstevel@tonic-gate 	diff = strcmp(p1->sl_name, p2->sl_name);
1277*0Sstevel@tonic-gate 	return (CMP1(diff));
1278*0Sstevel@tonic-gate }
1279*0Sstevel@tonic-gate 
1280*0Sstevel@tonic-gate static int exotic();
1281*0Sstevel@tonic-gate static void parsename();
1282*0Sstevel@tonic-gate static void parse_fn_and_print();
1283*0Sstevel@tonic-gate 
1284*0Sstevel@tonic-gate #define	STRSPACE 2400		/* guess at amount of string space */
1285*0Sstevel@tonic-gate 
1286*0Sstevel@tonic-gate char *format_buf;
1287*0Sstevel@tonic-gate #define	FORMAT_BUF	"%s\n\t\t\t\t\t    [%s]"
1288*0Sstevel@tonic-gate 
1289*0Sstevel@tonic-gate static char *
1290*0Sstevel@tonic-gate demangled_name(s)
1291*0Sstevel@tonic-gate char *s;
1292*0Sstevel@tonic-gate {
1293*0Sstevel@tonic-gate 	char *name;
1294*0Sstevel@tonic-gate 
1295*0Sstevel@tonic-gate 	name = (char *)sgs_demangle(s);
1296*0Sstevel@tonic-gate 
1297*0Sstevel@tonic-gate 	if (strcmp(name, s) == 0)
1298*0Sstevel@tonic-gate 		return (s);
1299*0Sstevel@tonic-gate 
1300*0Sstevel@tonic-gate 	if (format_buf != NULL)
1301*0Sstevel@tonic-gate 		free(format_buf);
1302*0Sstevel@tonic-gate 
1303*0Sstevel@tonic-gate 	format_buf = (char *)malloc(strlen(name) +
1304*0Sstevel@tonic-gate 		strlen(FORMAT_BUF) +
1305*0Sstevel@tonic-gate 		strlen(s) + 1);
1306*0Sstevel@tonic-gate 	if (format_buf == NULL)
1307*0Sstevel@tonic-gate 		return (s);
1308*0Sstevel@tonic-gate 	(void) sprintf(format_buf, FORMAT_BUF, name, s);
1309*0Sstevel@tonic-gate 	return (format_buf);
1310*0Sstevel@tonic-gate }
1311*0Sstevel@tonic-gate 
1312*0Sstevel@tonic-gate /* getname - get the name of a symbol in a permanent fashion */
1313*0Sstevel@tonic-gate char *
1314*0Sstevel@tonic-gate getname(ldpter, symbol)
1315*0Sstevel@tonic-gate PROF_FILE *ldpter;
1316*0Sstevel@tonic-gate PROF_SYMBOL symbol;
1317*0Sstevel@tonic-gate {
1318*0Sstevel@tonic-gate 	static char *strtable = NULL;	/* space for names */
1319*0Sstevel@tonic-gate 	static int sp_used = 0;		/* space used so far */
1320*0Sstevel@tonic-gate 	static int size = 0;		/* size of string table */
1321*0Sstevel@tonic-gate 	char *name, *strcpy();		/* name to store */
1322*0Sstevel@tonic-gate 	int lth;			/* space needed for name */
1323*0Sstevel@tonic-gate 	int get;			/* amount of space to get */
1324*0Sstevel@tonic-gate 
1325*0Sstevel@tonic-gate 	name = &(ldpter->pf_symstr_p)[symbol.ps_sym.st_name];
1326*0Sstevel@tonic-gate 	if (name == NULL)  {
1327*0Sstevel@tonic-gate 		return ("<<bad symbol name>>");
1328*0Sstevel@tonic-gate 	}
1329*0Sstevel@tonic-gate 
1330*0Sstevel@tonic-gate 	if (Cflag)
1331*0Sstevel@tonic-gate 		name = (char *)demangled_name(name);
1332*0Sstevel@tonic-gate 
1333*0Sstevel@tonic-gate 	lth = strlen(name) + 1;
1334*0Sstevel@tonic-gate 	if ((sp_used + lth) > size)  {	 /* if need more space */
1335*0Sstevel@tonic-gate 		/* just in case very long name */
1336*0Sstevel@tonic-gate 		get = lth > STRSPACE ? lth : STRSPACE;
1337*0Sstevel@tonic-gate 		strtable = _prof_Malloc(1, get);
1338*0Sstevel@tonic-gate 		size = get;
1339*0Sstevel@tonic-gate 		sp_used = 0;
1340*0Sstevel@tonic-gate 	}
1341*0Sstevel@tonic-gate 	(void) strcpy(&(strtable[sp_used]), name);
1342*0Sstevel@tonic-gate 	name = &(strtable[sp_used]);
1343*0Sstevel@tonic-gate 	sp_used += lth;
1344*0Sstevel@tonic-gate 	return (name);
1345*0Sstevel@tonic-gate }
1346*0Sstevel@tonic-gate 
1347*0Sstevel@tonic-gate static char n_buf[512];
1348*0Sstevel@tonic-gate static char d_buf[512];
1349*0Sstevel@tonic-gate static char p_buf[512];
1350*0Sstevel@tonic-gate static char *format = "%s\n\t\t\t\t\t    [%s]";
1351*0Sstevel@tonic-gate 
1352*0Sstevel@tonic-gate 
1353*0Sstevel@tonic-gate static char *ctor_str = "static constructor function for %s";
1354*0Sstevel@tonic-gate static char *dtor_str = "static destructor function for %s";
1355*0Sstevel@tonic-gate static char *vtbl_str = "virtual table for %s";
1356*0Sstevel@tonic-gate static char *ptbl_str = "pointer to the virtual table vector for %s";
1357*0Sstevel@tonic-gate 
1358*0Sstevel@tonic-gate /*
1359*0Sstevel@tonic-gate  * Return 1 when s is an exotic name, 0 otherwise.  s remains unchanged,
1360*0Sstevel@tonic-gate  * the exotic name, if exists, is saved in d_buf.
1361*0Sstevel@tonic-gate  */
1362*0Sstevel@tonic-gate int
1363*0Sstevel@tonic-gate exotic(s)
1364*0Sstevel@tonic-gate char *s;
1365*0Sstevel@tonic-gate {
1366*0Sstevel@tonic-gate 	int tag = 0;
1367*0Sstevel@tonic-gate 	if (strncmp(s, "__sti__", 7) == 0) {
1368*0Sstevel@tonic-gate 		s += 7; tag = 1;
1369*0Sstevel@tonic-gate 		parse_fn_and_print(ctor_str, s);
1370*0Sstevel@tonic-gate 	} else if (strncmp(s, "__std__", 7) == 0) {
1371*0Sstevel@tonic-gate 		s += 7; tag = 1;
1372*0Sstevel@tonic-gate 		parse_fn_and_print(dtor_str, s);
1373*0Sstevel@tonic-gate 	} else if (strncmp(s, "__vtbl__", 8) == 0) {
1374*0Sstevel@tonic-gate 		char *printname;
1375*0Sstevel@tonic-gate 		s += 8; tag = 1;
1376*0Sstevel@tonic-gate 		parsename(s);
1377*0Sstevel@tonic-gate 		sprintf(d_buf, vtbl_str, p_buf);
1378*0Sstevel@tonic-gate 	} else if (strncmp(s, "__ptbl_vec__", 12) == 0) {
1379*0Sstevel@tonic-gate 		s += 12; tag = 1;
1380*0Sstevel@tonic-gate 		parse_fn_and_print(ptbl_str, s);
1381*0Sstevel@tonic-gate 	}
1382*0Sstevel@tonic-gate 	return (tag);
1383*0Sstevel@tonic-gate }
1384*0Sstevel@tonic-gate 
1385*0Sstevel@tonic-gate void
1386*0Sstevel@tonic-gate parsename(s)
1387*0Sstevel@tonic-gate char *s;
1388*0Sstevel@tonic-gate {
1389*0Sstevel@tonic-gate 	register int len;
1390*0Sstevel@tonic-gate 	char c, *orig = s;
1391*0Sstevel@tonic-gate 	*p_buf = '\0';
1392*0Sstevel@tonic-gate 	strcat(p_buf, "class ");
1393*0Sstevel@tonic-gate 	while (isdigit(*s)) s++;
1394*0Sstevel@tonic-gate 	c = *s;
1395*0Sstevel@tonic-gate 	*s = '\0';
1396*0Sstevel@tonic-gate 	len = atoi(orig);
1397*0Sstevel@tonic-gate 	*s = c;
1398*0Sstevel@tonic-gate 	if (*(s+len) == '\0') { /* only one class name */
1399*0Sstevel@tonic-gate 		strcat(p_buf, s);
1400*0Sstevel@tonic-gate 		return;
1401*0Sstevel@tonic-gate 	} else { /* two classname  %drootname__%dchildname */
1402*0Sstevel@tonic-gate 	char *root, *child, *child_len_p;
1403*0Sstevel@tonic-gate 	int child_len;
1404*0Sstevel@tonic-gate 	root = s;
1405*0Sstevel@tonic-gate 	child = s + len + 2;
1406*0Sstevel@tonic-gate 	child_len_p = child;
1407*0Sstevel@tonic-gate 	if (! isdigit(*child)) { /* ptbl file name */
1408*0Sstevel@tonic-gate 		/*  %drootname__%filename */
1409*0Sstevel@tonic-gate 		char *p;    /* kludge for getting rid of '_' in file name */
1410*0Sstevel@tonic-gate 		c = *(root + len);
1411*0Sstevel@tonic-gate 		*(root + len) = '\0';
1412*0Sstevel@tonic-gate 		strcat(p_buf, root);
1413*0Sstevel@tonic-gate 		*(root + len) = c;
1414*0Sstevel@tonic-gate 		strcat(p_buf, " in ");
1415*0Sstevel@tonic-gate 		for (p = child; *p != '_'; ++p);
1416*0Sstevel@tonic-gate 			c = *p;
1417*0Sstevel@tonic-gate 			*p = '.';
1418*0Sstevel@tonic-gate 			strcat(p_buf, child);
1419*0Sstevel@tonic-gate 			*p = c;
1420*0Sstevel@tonic-gate 			return;
1421*0Sstevel@tonic-gate 		}
1422*0Sstevel@tonic-gate 
1423*0Sstevel@tonic-gate 		while (isdigit(*child)) child++;
1424*0Sstevel@tonic-gate 		c = *child;
1425*0Sstevel@tonic-gate 		*child = '\0';
1426*0Sstevel@tonic-gate 		child_len = atoi(child_len_p);
1427*0Sstevel@tonic-gate 		*child = c;
1428*0Sstevel@tonic-gate 		if (*(child + child_len) == '\0') {
1429*0Sstevel@tonic-gate 			strcat(p_buf, child);
1430*0Sstevel@tonic-gate 			strcat(p_buf, " derived from ");
1431*0Sstevel@tonic-gate 			c = *(root + len);
1432*0Sstevel@tonic-gate 			*(root + len) = '\0';
1433*0Sstevel@tonic-gate 			strcat(p_buf, root);
1434*0Sstevel@tonic-gate 			*(root + len) = c;
1435*0Sstevel@tonic-gate 			return;
1436*0Sstevel@tonic-gate 		} else { /* %drootname__%dchildname__filename */
1437*0Sstevel@tonic-gate 			char *p;
1438*0Sstevel@tonic-gate 				/* kludge for getting rid of '_' in file name */
1439*0Sstevel@tonic-gate 			c = *(child + child_len);
1440*0Sstevel@tonic-gate 			*(child + child_len) = '\0';
1441*0Sstevel@tonic-gate 			strcat(p_buf, child);
1442*0Sstevel@tonic-gate 			*(child+child_len) = c;
1443*0Sstevel@tonic-gate 			strcat(p_buf, " derived from ");
1444*0Sstevel@tonic-gate 			c = *(root + len);
1445*0Sstevel@tonic-gate 			*(root + len) = '\0';
1446*0Sstevel@tonic-gate 			strcat(p_buf,  root);
1447*0Sstevel@tonic-gate 			*(root + len) = c;
1448*0Sstevel@tonic-gate 			strcat(p_buf, " in ");
1449*0Sstevel@tonic-gate 			for (p = child+child_len+2; *p != '_'; ++p);
1450*0Sstevel@tonic-gate 			c = *p;
1451*0Sstevel@tonic-gate 			*p = '.';
1452*0Sstevel@tonic-gate 			strcat(p_buf, child + child_len + 2);
1453*0Sstevel@tonic-gate 			*p = c;
1454*0Sstevel@tonic-gate 			return;
1455*0Sstevel@tonic-gate 		}
1456*0Sstevel@tonic-gate 	}
1457*0Sstevel@tonic-gate }
1458*0Sstevel@tonic-gate 
1459*0Sstevel@tonic-gate void
1460*0Sstevel@tonic-gate parse_fn_and_print(str, s)
1461*0Sstevel@tonic-gate char *str, *s;
1462*0Sstevel@tonic-gate {
1463*0Sstevel@tonic-gate 	char c, *p1, *p2;
1464*0Sstevel@tonic-gate 	int yes = 1;
1465*0Sstevel@tonic-gate 
1466*0Sstevel@tonic-gate 	if ((p1 = p2 =  strstr(s, "_c_")) == NULL)
1467*0Sstevel@tonic-gate 		if ((p1 = p2 =  strstr(s, "_C_")) == NULL)
1468*0Sstevel@tonic-gate 			if ((p1 = p2 =  strstr(s, "_cc_")) == NULL)
1469*0Sstevel@tonic-gate 				if ((p1 = p2 =  strstr(s, "_cxx_")) == NULL)
1470*0Sstevel@tonic-gate 					if ((p1 = p2 =  strstr(s, "_h_"))
1471*0Sstevel@tonic-gate 							== NULL)
1472*0Sstevel@tonic-gate 						yes = 0;
1473*0Sstevel@tonic-gate 					else
1474*0Sstevel@tonic-gate 						p2 += 2;
1475*0Sstevel@tonic-gate 				else
1476*0Sstevel@tonic-gate 					p2 += 4;
1477*0Sstevel@tonic-gate 			else
1478*0Sstevel@tonic-gate 				p2 += 3;
1479*0Sstevel@tonic-gate 		else
1480*0Sstevel@tonic-gate 			p2 += 2;
1481*0Sstevel@tonic-gate 	else
1482*0Sstevel@tonic-gate 		p2 += 2;
1483*0Sstevel@tonic-gate 
1484*0Sstevel@tonic-gate 	if (yes) {
1485*0Sstevel@tonic-gate 		*p1 = '.';
1486*0Sstevel@tonic-gate 		c = *p2;
1487*0Sstevel@tonic-gate 		*p2 = '\0';
1488*0Sstevel@tonic-gate 	}
1489*0Sstevel@tonic-gate 
1490*0Sstevel@tonic-gate 	for (s = p1;  *s != '_';  --s);
1491*0Sstevel@tonic-gate 	++s;
1492*0Sstevel@tonic-gate 	sprintf(d_buf, str, s);
1493*0Sstevel@tonic-gate 
1494*0Sstevel@tonic-gate 	if (yes) {
1495*0Sstevel@tonic-gate 		*p1 = '_';
1496*0Sstevel@tonic-gate 		*p2 = c;
1497*0Sstevel@tonic-gate 	}
1498*0Sstevel@tonic-gate }
1499