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