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