xref: /titanic_50/usr/src/cmd/sgs/prof/common/prof.c (revision d7ddd43c70ebe97a1118be9f663a54d0d1d89fe6)
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 2008 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 "conv.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 			;
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 	/* BEGIN CSTYLED */
590 OLD_DEBUG(if (debug_value) Fprint(stderr,
591 "low pc = %#o, high pc = %#o, range = %#o = %u\n\
592 call counts: %u, %u used; pc counters: %u\n",
593 pc_l, pc_h, pc_m, pc_m, head.nfns, n_cc, n_pc));
594 	/* END CSTYLED */
595 
596 	/*LINTED: E_ASSIGMENT_CAUSE_LOSS_PREC*/
597 	sf = (BIAS * (double)n_pc)/pc_m;
598 	/*
599 	 * Now adjust bias and sf so that there is no overflow
600 	 * when calculating indices.
601 	 */
602 	bias = BIAS;
603 	temp = pc_m;
604 	while ((temp >>= 1) > 0x7fff) {
605 		sf >>= 1;
606 		bias >>= 1;
607 	}
608 	s_inv = pc_m/n_pc;	/* Range of PCs mapped into one index. */
609 
610 	/* BEGIN CSTYLED */
611 OLD_DEBUG(
612 	if (debug_value) {
613 		Fprint(
614 			stderr,
615 			"sf = %d, s_inv = %d bias = %d\n",
616 			(long)sf, s_inv, bias);
617 	}
618 );
619 	/* END CSTYLED */
620 
621 		/* Prepare to read symbols from "a.out" (or whatever). */
622 	n_syms = 0;			/* Init count of qualified symbols. */
623 	n = symttl;			/* Total symbols. */
624 	while (--n >= 0)			/* Scan symbol table. */
625 		if (readnl(n))	/* Read and examine symbol, count qualifiers */
626 			n_syms++;
627 
628 	/* BEGIN CSTYLED */
629 OLD_DEBUG(
630 	if (debug_value) {
631 		Fprint(stderr, "%u symbols, %u qualify\n", symttl, n_syms);
632 	}
633 );
634 	/* END CSTYLED */
635 
636 		/* Allocate space for qualified symbols. */
637 
638 	slist = slp = _prof_Malloc(n_syms, sizeof (struct slist));
639 
640 		/*
641 		 * Allocate space for synonym symbols
642 		 * (i.e. symbols that refer to the same address).
643 		 * NB there can be no more than n_syms/2 addresses
644 		 * with symbols, That Have Aliases, that refer to them!
645 		 */
646 
647 	snymCapacity = n_syms/2;
648 	snymList = snymp =
649 	    _prof_Malloc(snymCapacity, sizeof (struct snymEntry));
650 	n_snyms = 0;
651 
652 /* OLD_DEBUG(debug_value &= ~020); */
653 
654 	/* Loop on number of qualified symbols. */
655 	for (n = n_syms, symct = 0; n > 0; symct++) {
656 		if (readnl(symct)) {	/* Get one. Check again. */
657 				/* Is qualified. Move name ... */
658 			slp->sl_name = getname(ldptr, nl);
659 
660 				/* and address into slist structure. */
661 			slp->sl_addr = (char *)nl.ps_sym.st_value;
662 			slp->sl_size = nl.ps_sym.st_size;
663 
664 				/* set other slist fields to zero. */
665 			slp->sl_time = 0.0;
666 			slp->sl_count = 0;
667 	/* BEGIN CSTYLED */
668 OLD_DEBUG(
669 	if (debug_value & 02)
670 		Fprint(stderr, "%-8.8s: %#8o\n", slp->sl_name, slp->sl_addr)
671 );
672 	/* END CSTYLED */
673 
674 			slp++;
675 			--n;
676 		}
677 	}
678 	/*
679 	 *
680 	 * Now attempt to match call counts with symbols.  To do this, it
681 	 * helps to first sort both the symbols and the call address/count
682 	 * pairs by ascending address, since they are generally not, to
683 	 * begin with.  The addresses associated with the counts are not,
684 	 * of course, the subroutine addresses associated with the symbols,
685 	 * but some address slightly past these. Therefore a given count
686 	 * address (in the fnpc field) is matched with the closest symbol
687 	 * address (sl_addr) that is:
688 	 *	(1) less than the fnpc value but,
689 	 *	(2) not more than the length of the function
690 	 * In other words, unreasonable matchups are avoided.
691 	 * Situations such as this could arise when static procedures are
692 	 * counted but the "-g" option was not given to this program,
693 	 * causing the symbol to fail to qualify.  Without this limitation,
694 	 * unmatched counts could be erroneously charged.
695 	 *
696 	 */
697 
698 
699 	ccp = ccounts;			/* Point to first call counter. */
700 	slp = slist;			/*   "		"   "   symbol. */
701 		/* Sort call counters and ... */
702 	qsort((char *)ccp, (unsigned)n_cc, sizeof (struct cnt), c_ccaddr);
703 		/* symbols by increasing address. */
704 	qsort((char *)slp, (unsigned)n_syms, sizeof (struct slist), c_sladdr);
705 	vn_cc = n_cc;			/* save this for verbose option */
706 
707 
708 		/* Loop to match up call counts & symbols. */
709 	for (n = n_syms; n > 0 && vn_cc > 0; ) {
710 		int	sz = slp->sl_size;
711 
712 		if (sz == 0)
713 			sz = slp[ 1 ].sl_addr - slp->sl_addr;
714 		if (slp->sl_addr < ccp->fnpc &&
715 		    ccp->fnpc <= slp->sl_addr + sz) {
716 					/* got a candidate: find Closest. */
717 			struct slist *closest_symp;
718 			do {
719 				closest_symp = slp;
720 				slp++;
721 				--n;
722 			} while (n > 0 && slp->sl_addr < ccp->fnpc);
723 
724 	/* BEGIN CSTYLED */
725 OLD_DEBUG(
726 if (debug_value & 04) {
727 	Fprint(stderr,
728 		"Routine %-8.8s @ %#8x+%-2d matches count address %#8x\n",
729 		closest_symp->sl_name,
730 		closest_symp->sl_addr,
731 		ccp->fnpc-slp->sl_addr,
732 		ccp->fnpc);
733 }
734 );
735 	/* END CSTYLED */
736 			closest_symp->sl_count = ccp->mcnt;  /* Copy count. */
737 			++ccp;
738 			--vn_cc;
739 		} else if (ccp->fnpc < slp->sl_addr) {
740 			++ccp;
741 			--vn_cc;
742 		} else {
743 			++slp;
744 			--n;
745 		}
746 	}
747 
748 	/*
749 	 *
750 	 * The distribution of times to addresses is done on a proportional
751 	 * basis as follows: The t counts in pcounts[i] correspond to clock
752 	 * ticks for values of pc in the range pc, pc+1, ..., pc+s_inv-1
753 	 * (odd addresses excluded for PDP11s). Without more detailed info,
754 	 * it must be assumed that there is no greater probability
755 	 * of the clock ticking for any particular pc in this range than for
756 	 * any other.  Thus the t counts are considered to be equally
757 	 * distributed over the addresses in the range, and that the time for
758 	 * any given address in the range is pcounts[i]/s_inv.
759 	 *
760 	 * The values of the symbols that qualify, bounded below and above
761 	 * by pc_l and pc_h, respectively, partition the profiling range into
762 	 * regions to which are assigned the total times associated with the
763 	 * addresses they contain in the following way:
764 	 *
765 	 * The sum of all pcounts[i] for which the corresponding addresses are
766 	 * wholly within the partition are charged to the partition (the
767 	 * subroutine whose address is the lower bound of the partition).
768 	 *
769 	 * If the range of addresses corresponding to a given t = pcounts[i]
770 	 * lies astraddle the boundary of a partition, e.g., for some k such
771 	 * that 0 < k < s_inv-1, the addresses pc, pc+1, ..., pc+k-1 are in
772 	 * the lower partition, and the addresses pc+k, pc+k+1, ..., pc+s_inv-1
773 	 * are in the next partition, then k*pcounts[i]/s_inv time is charged
774 	 * to the lower partition, and (s_inv-k) * pcounts[i]/s_inv time to the
775 	 * upper.  It is conceivable, in cases of large granularity or small
776 	 * subroutines, for a range corresponding to a given pcounts[i] to
777 	 * overlap three regions, completely containing the (small) middle one.
778 	 * The algorithm is adjusted appropriately in this case.
779 	 *
780 	 */
781 
782 
783 	pcp = pcounts;				/* Reset to base. */
784 	slp = slist;				/* Ditto. */
785 	t0 = 0.0;				/* Time accumulator. */
786 	for (n = 0; n < n_syms; n++) {		/* Loop on symbols. */
787 			/* Start addr of region, low addr of overlap. */
788 		char *pc0, *pc00;
789 			/* Start addr of next region, low addr of overlap. */
790 		char *pc1, *pc10;
791 		/* First index into pcounts for this region and next region. */
792 		int i0, i1;
793 		long ticks;
794 
795 			/* Address of symbol (subroutine). */
796 		pc0 = slp[n].sl_addr;
797 
798 			/* Address of next symbol, if any or top */
799 			/* of profile range, if not */
800 		pc1 = (n < n_syms - 1) ? slp[n+1].sl_addr : pc_h;
801 
802 			/* Lower bound of indices into pcounts for this range */
803 
804 		i0 = (((unsigned)pc0 - (unsigned)pc_l) * sf)/bias;
805 
806 			/* Upper bound (least or least + 1) of indices. */
807 		i1 = (((unsigned)pc1 - (unsigned)pc_l) * sf)/bias;
808 
809 		if (i1 >= n_pc)				/* If past top, */
810 			i1 = n_pc - 1;				/* adjust. */
811 
812 			/* Lowest addr for which count maps to pcounts[i0]; */
813 		pc00 =  pc_l + (unsigned long)((bias * i0)/sf);
814 
815 			/* Lowest addr for which count maps to pcounts[i1]. */
816 		pc10 =  pc_l + (unsigned long)((bias * i1)/sf);
817 
818 	/* BEGIN CSTYLED */
819 OLD_DEBUG(if (debug_value & 010) Fprint(stderr,
820 "%-8.8s\ti0 = %4d, pc00 = %#6o, pc0 = %#6o\n\
821 \t\ti1 = %4d, pc10 = %#6o, pc1 = %#6o\n\t\t",
822 slp[n].sl_name, i0, pc00, pc0, i1, pc10, pc1));
823 	/* END CSTYLED */
824 		t = 0;			/* Init time for this symbol. */
825 		if (i0 == i1) {
826 			/* Counter overlaps two areas? (unlikely */
827 			/* unless large granularity). */
828 			ticks = pcp[i0];	/* # Times (clock ticks). */
829 OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks));
830 
831 			    /* Time less that which overlaps adjacent areas */
832 			t += PROFSEC(ticks * ((double)(pc1 - pc0) * sf)/bias);
833 
834 	/* BEGIN CSTYLED */
835 OLD_DEBUG(if (debug_value & 010)
836 	Fprint(stderr, "%ld/(%.1f)", (pc1 - pc0) * ticks, DBL_ADDRPERCELL)
837 );
838 	/* END CSTYLED */
839 		} else {
840 				/* Overlap with previous region? */
841 			if (pc00 < pc0) {
842 				ticks = pcp[i0];
843 	/* BEGIN CSTYLED */
844 OLD_DEBUG(if (debug_value & 010)
845 	fprintf(stderr, "pc00 < pc0 ticks = %d\n", ticks));
846 
847 				/* Get time of overlapping area and */
848 				/* subtract proportion for lower region. */
849 				t += PROFSEC(
850 				ticks*(1-((double)(pc0-pc00) *sf)/bias));
851 
852 				/* Do not count this time when summing times */
853 				/*		wholly within the region. */
854 				i0++;
855 	/* BEGIN CSTYLED */
856 OLD_DEBUG(if (debug_value & 010)
857 	Fprint(stderr, "%ld/(%.1f) + ", (pc0 - pc00) * ticks,
858 		DBL_ADDRPERCELL));
859 	/* END CSTYLED */
860 			}
861 
862 			/* Init sum of counts for PCs not shared w/other */
863 			/*	routines. */
864 			ticks = 0;
865 
866 			/* Stop at first count that overlaps following */
867 			/*	routine. */
868 			for (i = i0; i < i1; i++)
869 				ticks += pcp[i];
870 
871 			t += PROFSEC(ticks); /* Convert to secs, add to total */
872 OLD_DEBUG(if (debug_value & 010) Fprint(stderr, "%ld", ticks));
873 			/* Some overlap with low addresses of next routine? */
874 			if (pc10 < pc1) {
875 					/* Yes. Get total count ... */
876 				ticks = pcp[i1];
877 
878 				/* and accumulate proportion for addresses in */
879 				/*		range of this routine */
880 				t += PROFSEC(((double)ticks *
881 				    (pc1 - pc10)*sf)/bias);
882 	/* BEGIN CSTYLED */
883 OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks));
884 OLD_DEBUG(if (debug_value & 010)
885 	Fprint(stderr, " + %ld/(%.1f)", (pc1 - pc10) * ticks, DBL_ADDRPERCELL)
886 );
887 	/* END CSTYLED */
888 			}
889 		}		/* End if (i0 == i1) ... else ... */
890 
891 		slp[n].sl_time = t;	/* Store time for this routine. */
892 		t0 += t;		/* Accumulate total time. */
893 OLD_DEBUG(if (debug_value & 010) Fprint(stderr, " ticks = %.2f msec\n", t));
894 	}	/* End for (n = 0; n < n_syms; n++) */
895 
896 	/* Final pass to total up time. */
897 	/* Sum ticks, then convert to seconds. */
898 
899 	for (n = n_pc, temp = 0; --n >= 0; temp += *(pcp++))
900 		;
901 
902 	t_tot = PROFSEC(temp);
903 
904 	/*
905 	 * Now, whilst we still have the symbols sorted
906 	 * in address order..
907 	 * Loop to record duplicates, so we can display
908 	 * synonym symbols correctly.
909 	 * Synonym symbols, or symbols with the same address,
910 	 * are to be displayed by prof on the same line, with
911 	 * one statistics line, as below:
912 	 *			... 255  ldaopen, ldaopen
913 	 * The way this will be implemented, is as follows:
914 	 *
915 	 * Pass 1 - while the symbols are in address order, we
916 	 *  do a pre-pass through them, to determine for which
917 	 *  addresses there are more than one symbol (i.e. synonyms).
918 	 *  During this prepass we collect summary statistics in
919 	 *  the synonym entry, for all the synonyms.
920 	 *
921 	 * 'Pass' 2 - while printing a report,  for each report line,
922 	 *  if the current symbol is a synonym symbol (i.e. in the
923 	 *  snymList) then we scan forward and pick up all the names
924 	 *  which map to this address, and print them too.
925 	 *  If the address' synonyms have already been printed, then
926 	 *  we just skip this symbol and go on to process the next.
927 	 *
928 	 */
929 
930 	{
931 	/* pass 1 */
932 	char *thisaddr;
933 	char *lastaddr = slist->sl_addr; /* use 1st sym as */
934 					/* 'last/prior symbol' */
935 	int lastWasSnym = 0;	/* 1st can't be snym yet-no aliases seen! */
936 	int thisIsSnym;
937 
938 	/* BEGIN CSTYLED */
939 OLD_DEBUG(
940 int totsnyms = 0; int totseries = 0; struct slist *lastslp = slist;
941 );
942 	/* END CSTYLED */
943 
944 	/* NB loop starts with 2nd symbol, loops over n_syms-1 symbols! */
945 	for (n = n_syms-1, slp = slist+1; --n >= 0; slp++) {
946 		thisaddr = slp->sl_addr;
947 		thisIsSnym = (thisaddr == lastaddr);
948 
949 		if (thisIsSnym) {
950 			/* gotta synonym */
951 			if (!lastWasSnym) {
952 	/* BEGIN CSTYLED */
953 OLD_DEBUG(
954 if (debug_value)  {
955 	Fprint(stderr,
956 		"Synonym series:\n1st->\t%s at address %x, ct=%ld, time=%f\n",
957 		lastslp->sl_name, lastaddr, lastslp->sl_count,
958 		lastslp->sl_time);
959 	totseries++;
960 	totsnyms++;
961 }
962 );
963 	/* END CSTYLED */
964 				/* this is the Second! of a series */
965 				snymp = (n_snyms++ == 0 ? snymList : snymp+1);
966 				snymp->howMany = 1; /* gotta count 1st one!! */
967 				snymp->sym_addr = slp->sl_addr;
968 				/* zero summary statistics */
969 				snymp->tot_sl_count = 0;
970 				snymp->tot_sl_time = 0.0;
971 				/* Offen the Reported flag */
972 				snymp->snymReported = 0;
973 			}
974 	/* BEGIN CSTYLED */
975 OLD_DEBUG(
976 if (debug_value)  {
977 	Fprint(stderr,
978 		"\t%s at address %x, ct=%ld, time=%f\n",
979 		slp->sl_name,
980 		thisaddr,
981 		slp->sl_count,
982 		slp->sl_time);
983 	totsnyms++;
984 }
985 );
986 	/* END CSTYLED */
987 			/* ok - bump count for snym, and note its Finding */
988 			snymp->howMany++;
989 			/* and update the summary statistics */
990 			snymp->tot_sl_count += slp->sl_count;
991 			snymp->tot_sl_time += slp->sl_time;
992 		}
993 		callTotal += slp->sl_count;
994 		lastaddr = thisaddr;
995 		lastWasSnym = thisIsSnym;
996 	/* BEGIN CSTYLED */
997 OLD_DEBUG(
998 if (debug_value) lastslp = slp;
999 );
1000 	/* END CSTYLED */
1001 
1002 	}
1003 	/* BEGIN CSTYLED */
1004 OLD_DEBUG(
1005 if (debug_value)  {
1006 	Fprint(stderr, "Total #series %d, #synonyms %d\n", totseries, totsnyms);
1007 }
1008 );
1009 	/* END CSTYLED */
1010 	}
1011 	/*
1012 	 * Most of the heavy work is done now.  Only minor stuff remains.
1013 	 * The symbols are currently in address order and must be re-sorted
1014 	 * if desired in a different order.  Report generating options
1015 	 * include "-o" or "-x": Include symbol address, which causes
1016 	 * another column
1017 	 * in the output; and "-z": Include symbols in report even if zero
1018 	 * time and call count.  Symbols not in profiling range are excluded
1019 	 * in any case.  Following the main body of the report, the "-s"
1020 	 * option causes certain additional information to be printed.
1021 	 */
1022 
1023 OLD_DEBUG(if (debug_value) Fprint(stderr,
1024 "Time unaccounted for: %.7G\n", t_tot - t0));
1025 
1026 	if (sort)	/* If comparison routine given then use it. */
1027 		qsort((char *)slist, (unsigned)n_syms,
1028 		    sizeof (struct slist), sort);
1029 
1030 	if (!(flags & F_NHEAD)) {
1031 		if (flags & F_PADDR)
1032 			Print("%s", atitle);	/* Title for addresses. */
1033 		(void) puts(" %Time Seconds Cumsecs  #Calls   msec/call  Name");
1034 	}
1035 	t = 0.0;			/* Init cumulative time. */
1036 	if (t_tot != 0.0)		/* Convert to percent. */
1037 		t_tot = 100.0/t_tot;	/* Prevent divide-by-zero fault */
1038 	n_nonzero = 0;	/* Number of symbols with nonzero time or # calls. */
1039 	for (n = n_syms, slp = slist; --n >= 0; slp++) {
1040 		long count;	 /* # Calls. */
1041 		/* t0, time in seconds. */
1042 
1043 		/* if a snym symbol, use summarized stats, else use indiv. */
1044 		if ((snymp = getSnymEntry(slp->sl_addr)) != 0) {
1045 			count = snymp->tot_sl_count;
1046 			t0 = snymp->tot_sl_time;
1047 
1048 		} else {
1049 			count = slp->sl_count;
1050 			t0 = slp->sl_time;
1051 		}
1052 
1053 		/* if a snym and already reported, skip this entry */
1054 		if (snymp && snymp->snymReported)
1055 			continue;
1056 		/* Don't do entries with no action. */
1057 		if (t0 == 0.0 && count == 0 && !(flags & F_ZSYMS))
1058 			continue;
1059 		if ((strcmp(slp->sl_name, "_mcount") == 0) ||
1060 		    (strcmp(slp->sl_name, "mcount") == 0)) {
1061 			count = callTotal;
1062 		}
1063 
1064 		/* count number of entries (i.e. symbols) printed */
1065 		if (snymp)
1066 			n_nonzero += snymp->howMany; /* add for each snym */
1067 		else
1068 			n_nonzero++;
1069 
1070 		if (flags & F_PADDR) { /* Printing address of symbol? */
1071 			/* LINTED: variable format */
1072 			Print(aformat, slp->sl_addr);
1073 		}
1074 		t += t0;	/*  move here; compiler bug  !! */
1075 		Print("%6.1f%8.2f%8.2f", t0 * t_tot, t0, t);
1076 		fdigits = 0;
1077 		if (count) {		/* Any calls recorded? */
1078 		/* Get reasonable number of fractional digits to print. */
1079 			fdigits = fprecision(count);
1080 			Print("%8ld%#*.*f", count, fdigits+8, fdigits,
1081 			    1000.0*t0/count);
1082 			Print("%*s", 6-fdigits, " ");
1083 		} else {
1084 			Print("%22s", " ");
1085 		}
1086 		/*
1087 		 * now print the name (or comma-seperate list of names,
1088 		 * for synonym symbols).
1089 		 */
1090 		if (snymp) {
1091 			printSnymNames(slp, snymp);	/* print it, then */
1092 			snymp->snymReported = 1;	/* mark it Done */
1093 		}
1094 		else
1095 			(void) puts(slp->sl_name);	/* print the one name */
1096 	}
1097 	if (flags & F_VERBOSE) {		/* Extra info? */
1098 		Fprint(stderr, "%5d/%d call counts used\n", n_cc, head.nfns);
1099 		Fprint(stderr, "%5d/%d symbols qualified", n_syms, symttl);
1100 		if (n_nonzero < n_syms)
1101 			Fprint(stderr,
1102 			    ", %d had zero time and zero call-counts\n",
1103 			    n_syms - n_nonzero);
1104 		else
1105 			(void) putc('\n', stderr);
1106 		Fprint(stderr, "%#lx scale factor\n", (long)sf);
1107 	}
1108 
1109 	_symintClose(ldptr);
1110 	} else {
1111 		Fprint(stderr, "prof: no call counts captured\n");
1112 	}
1113 	return (0);
1114 }
1115 /* Return size of file associated with file descriptor fd. */
1116 
1117 static off_t
1118 fsize(int fd)
1119 {
1120 	struct stat sbuf;
1121 
1122 	if (fstat(fd, &sbuf) < 0)  /* Status of open file. */
1123 		Perror("stat");
1124 	return (sbuf.st_size);			/* This is a long. */
1125 }
1126 
1127 /* Read symbol entry. Return TRUE if satisfies conditions. */
1128 
1129 static int
1130 readnl(int symindex)
1131 {
1132 	nl = ldptr->pf_symarr_p[symindex];
1133 
1134 	/* BEGIN CSTYLED */
1135 OLD_DEBUG(
1136 	if (debug_value & 020) {
1137 		Fprint(stderr,
1138 			"`%-8.8s'\tst_info=%#4o, value=%#8.6o\n",
1139 			ldptr->pf_symstr_p[nl.ps_sym.st_name],
1140 			(unsigned char) nl.ps_sym.st_info,
1141 			nl.ps_sym.st_value);
1142 	}
1143 );
1144 	/* END CSTYLED */
1145 
1146 	/*
1147 	 * TXTSYM accepts global (and local, if "-g" given) T-type symbols.
1148 	 * Only those in the profiling range are useful.
1149 	 */
1150 	return (nl.ps_sym.st_shndx < SHN_LORESERVE &&
1151 	    TXTSYM(nl.ps_sym.st_shndx, nl.ps_sym.st_info) &&
1152 	    (pc_l <= (char *)nl.ps_sym.st_value) &&
1153 	    ((char *)nl.ps_sym.st_value < pc_h));
1154 }
1155 /*
1156  * Error-checking memory allocators -
1157  * Guarantees good return (else none at all).
1158  */
1159 
1160 static void *
1161 _prof_Malloc(int item_count, int item_size)
1162 {
1163 	void *p;
1164 
1165 	if ((p = malloc((unsigned)item_count * (unsigned)item_size)) == NULL)  {
1166 		(void) fprintf(stderr, "%s: Out of space\n", cmdname);
1167 		exit(1);
1168 	}
1169 	return (p);
1170 }
1171 
1172 
1173 
1174 /*
1175  *	Given the quotient Q = N/D, where entier(N) == N and D > 0, an
1176  *	approximation of the "best" number of fractional digits to use
1177  *	in printing Q is f = entier(log10(D)), which is crudely produced
1178  *	by the following routine.
1179  */
1180 
1181 static int
1182 fprecision(long count)
1183 {
1184 	return (count < 10 ? 0 : count < 100 ? 1 : count < 1000 ? 2 :
1185 	    count < 10000 ? 3 : 4);
1186 }
1187 
1188 /*
1189  *	Return pointer to base name(name less path) of string s.
1190  *	Handles case of superfluous trailing '/'s, and unlikely
1191  *	case of s == "/".
1192  */
1193 
1194 static char *
1195 basename(char *s)
1196 {
1197 	char *p;
1198 
1199 	p = &s[strlen(s)];			/* End (+1) of string. */
1200 	while (p > s && *--p == '/')		/* Trim trailing '/'s. */
1201 		*p = '\0';
1202 	p++;					/* New end (+1) of string. */
1203 	while (p > s && *--p != '/')		/* Break backward on '/'. */
1204 		;
1205 	if (*p == '/')		/* If found '/', point to 1st following. */
1206 		p++;
1207 	if (*p == '\0')
1208 		p = "/";			/* If NULL, must be "/". (?) */
1209 	return (p);
1210 }
1211 /* Here if unexpected read problem. */
1212 
1213 static void
1214 eofon(FILE *iop, char *fn)
1215 {
1216 	if (ferror(iop))		/* Real error? */
1217 		Perror(fn);		/* Yes. */
1218 	Fprint(stderr, "%s: %s: Premature EOF\n", cmdname, fn);
1219 	exit(1);
1220 }
1221 
1222 /* Version of perror() that prints cmdname first. */
1223 
1224 static void
1225 Perror(char *s)
1226 {				/* Print system error message & exit. */
1227 	int err = errno;	/* Save current errno in case */
1228 
1229 	Fprint(stderr, "%s: ", cmdname);
1230 	errno = err;			/* Put real error back. */
1231 	perror(s);			/* Print message. */
1232 	_symintClose(ldptr);		/* cleanup symbol information */
1233 	exit(1);			/* Exit w/nonzero status. */
1234 }
1235 
1236 /* Here for things that "Should Never Happen". */
1237 
1238 static void
1239 snh(void)
1240 {
1241 	Fprint(stderr, "%s: Internal error\n", cmdname);
1242 	(void) abort();
1243 }
1244 
1245 /*
1246  *	Various comparison routines for qsort. Uses:
1247  *
1248  *	c_ccaddr	- Compare fnpc fields of cnt structs to put
1249  *				call counters in increasing address order.
1250  *	c_sladdr	- Sort slist structures on increasing address.
1251  *	c_time		-  "	 "	  "      " decreasing time.
1252  *	c_ncalls	-  "	 "	  "      " decreasing # calls.
1253  *	c_name		-  "	 "	  "      " increasing symbol name
1254  */
1255 
1256 #define	CMP2(v1, v2)	((v1) < (v2) ? -1 : (v1) == (v2) ? 0 : 1)
1257 #define	CMP1(v)		CMP2(v, 0)
1258 
1259 int
1260 c_ccaddr(const void *arg1, const void *arg2)
1261 {
1262 	struct cnt *p1 = (struct cnt *)arg1;
1263 	struct cnt *p2 = (struct cnt *)arg2;
1264 
1265 	return (CMP2(p1->fnpc, p2->fnpc));
1266 }
1267 
1268 int
1269 c_sladdr(const void *arg1, const void *arg2)
1270 {
1271 	struct slist *p1 = (struct slist *)arg1;
1272 	struct slist *p2 = (struct slist *)arg2;
1273 
1274 	return (CMP2(p1->sl_addr, p2->sl_addr));
1275 }
1276 
1277 int
1278 c_time(const void *arg1, const void *arg2)
1279 {
1280 	struct slist *p1 = (struct slist *)arg1;
1281 	struct slist *p2 = (struct slist *)arg2;
1282 	float dtime = p2->sl_time - p1->sl_time; /* Decreasing time. */
1283 
1284 	return (CMP1(dtime));
1285 }
1286 
1287 int
1288 c_ncalls(const void *arg1, const void *arg2)
1289 {
1290 	struct slist *p1 = (struct slist *)arg1;
1291 	struct slist *p2 = (struct slist *)arg2;
1292 	int diff = p2->sl_count - p1->sl_count;
1293 		/* Decreasing # calls. */
1294 	return (CMP1(diff));
1295 }
1296 
1297 int
1298 c_name(const void *arg1, const void *arg2)
1299 {
1300 	struct slist *p1 = (struct slist *)arg1;
1301 	struct slist *p2 = (struct slist *)arg2;
1302 	int diff;
1303 
1304 		/* flex names has variable length strings for names */
1305 	diff = strcmp(p1->sl_name, p2->sl_name);
1306 	return (CMP1(diff));
1307 }
1308 
1309 #define	STRSPACE 2400		/* guess at amount of string space */
1310 
1311 char *format_buf;
1312 #define	FORMAT_BUF	"%s\n\t\t\t\t\t    [%s]"
1313 
1314 static char *
1315 demangled_name(char *s)
1316 {
1317 	const char *name;
1318 	size_t	len;
1319 
1320 	name = conv_demangle_name(s);
1321 
1322 	if (strcmp(name, s) == 0)
1323 		return (s);
1324 
1325 	if (format_buf != NULL)
1326 		free(format_buf);
1327 
1328 	len = strlen(name) + strlen(FORMAT_BUF) + strlen(s) + 1;
1329 	format_buf = malloc(len);
1330 	if (format_buf == NULL)
1331 		return (s);
1332 	(void) snprintf(format_buf, len, FORMAT_BUF, name, s);
1333 	return (format_buf);
1334 }
1335 
1336 /* getname - get the name of a symbol in a permanent fashion */
1337 static char *
1338 getname(PROF_FILE *ldpter, PROF_SYMBOL symbol)
1339 {
1340 	static char *strtable = NULL;	/* space for names */
1341 	static int sp_used = 0;		/* space used so far */
1342 	static int size = 0;		/* size of string table */
1343 	char *name;			/* name to store */
1344 	int lth;			/* space needed for name */
1345 	int get;			/* amount of space to get */
1346 
1347 	name = elf_strptr(ldpter->pf_elf_p, ldpter->pf_symstr_ndx,
1348 	    symbol.ps_sym.st_name);
1349 	if (name == NULL)
1350 		return ("<<bad symbol name>>");
1351 
1352 	if (Cflag)
1353 		name = demangled_name(name);
1354 
1355 	lth = strlen(name) + 1;
1356 	if ((sp_used + lth) > size)  {	 /* if need more space */
1357 		/* just in case very long name */
1358 		get = lth > STRSPACE ? lth : STRSPACE;
1359 		strtable = _prof_Malloc(1, get);
1360 		size = get;
1361 		sp_used = 0;
1362 	}
1363 	(void) strcpy(&(strtable[sp_used]), name);
1364 	name = &(strtable[sp_used]);
1365 	sp_used += lth;
1366 	return (name);
1367 }
1368 
1369 static void
1370 usage(void)
1371 {
1372 	(void) fprintf(stderr,
1373 	    "usage: %s [-ChsVz] [-a | c | n | t]  [-o  |  x]   [-g  |  l]\n"
1374 	    "\t[-m mdata] [prog]\n",
1375 	    cmdname);
1376 	exit(1);
1377 }
1378