xref: /illumos-gate/usr/src/cmd/sgs/prof/common/prof.c (revision 9174bfaa)
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  * Copyright 2018 Jason King
26  */
27 
28 /*	Copyright (c) 1988 AT&T	*/
29 /*	  All Rights Reserved	*/
30 
31 /*
32  *	Program profiling report generator.
33  *
34  *	Usage:
35  *
36  *	prof [-ChsVz] [-a | c | n | t]  [-o  |  x]   [-g  |  l]
37  *	    [-m mdata] [prog]
38  *
39  *	Where "prog" is the program that was profiled; "a.out" by default.
40  *	Options are:
41  *
42  *	-n	Sort by symbol name.
43  *	-t	Sort by decreasing time.
44  *	-c	Sort by decreasing number of calls.
45  *	-a	Sort by increasing symbol address.
46  *
47  *	The options that determine the type of sorting are mutually exclusive.
48  *	Additional options are:
49  *
50  *	-o	Include symbol addresses in output (in octal).
51  *	-x	Include symbol addresses in output (in hexadecimal).
52  *	-g	Include non-global T-type symbols in output.
53  *	-l	Do NOT include local T-type symbols in output (default).
54  *	-z	Include all symbols in profiling range, even if zero
55  *			number of calls or time.
56  *	-h	Suppress table header.
57  *	-s	Follow report with additional statistical information.
58  *	-m mdata Use file "mdata" instead of MON_OUT for profiling data.
59  *	-V	print version information for prof (and exit, if only V spec'd)
60  *	-C	call C++ demangle routine to demangle names before printing.
61  */
62 
63 #include <stdio.h>
64 #include <string.h>
65 #include <errno.h>
66 #include <dlfcn.h>
67 #include <ctype.h>
68 #include "conv.h"
69 #include "symint.h"
70 #include "sys/param.h"			/* for HZ */
71 #include "mon.h"
72 #include "sys/stat.h"
73 #include "debug.h"
74 
75 
76 #define	OLD_DEBUG(x)
77 
78 OLD_DEBUG(static int debug_value);
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 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
printSnymNames(struct slist * slp,struct snymEntry * snymp)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 *
getSnymEntry(char * sl_addr)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
main(int argc,char ** argv)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 	unsigned pc_m;	/* Range of PCs profiled: pc_m = pc_h - pc_l */
355 
356 	float	t, t0;
357 	float	t_tot;	/* Total time: PROFSEC(sum of all pcounts[i]) */
358 	int	callTotal = 0;
359 
360 	DEBUG_LOC("main: top");
361 	setbuf(stdout, buffer);
362 	cmdname = basename(*argv);	/* command name. */
363 
364 	while ((n = getopt(argc, argv, "canthsglzoxT:m:VC")) != EOF) {
365 		switch (n) {
366 		int (*fcn)();	/* For function to sort results. */
367 
368 		case 'm':	/* Specify data file:	-m file */
369 			mon_fn = optarg;
370 			break;
371 
372 #ifdef ddt
373 		case 'T':	/* Set trace flags: -T(octnum) */
374 			debug_value = (int)strtol(optarg, 0, 8);
375 			break;
376 #endif
377 
378 		case 'n':	/* Sort by symbol name. */
379 			fcn = c_name;
380 			sort_flag |= BY_NAME;
381 			goto check;
382 
383 		case 't':	/* Sort by decreasing time. */
384 			fcn = c_time;
385 			sort_flag |= BY_TIME;
386 			goto check;
387 
388 		case 'c':	/* Sort by decreasing # calls. */
389 			fcn = c_ncalls;
390 			sort_flag |= BY_NCALLS;
391 			goto check;
392 
393 		case 'a':	/* Sort by increasing symbol address */
394 				/*		(don't have to -- it will be) */
395 			fcn = NULL;
396 			sort_flag |= BY_ADDRESS;
397 		check:		/* Here to check sort option conflicts. */
398 			if (sort != NULL && sort != fcn) {
399 				Fprint(stderr, "%s: Warning: %c overrides"
400 				" previous specification\n", cmdname, n);
401 			}
402 			sort = fcn;	/* Store sort routine */
403 			flags |= F_SORT; /* Note have done so */
404 			break;
405 
406 		case 'o':	/* Include symbol addresses in output. */
407 		case 'x':	/* Include symbol addresses in output. */
408 			aformat[2] = n;	/* 'o' or 'x' in format */
409 			flags |= F_PADDR;	/* Set flag. */
410 			break;
411 
412 		case 'g':	/* Include local T symbols as well as global */
413 			gflag = 1;
414 			break;
415 
416 		case 'l':	/* Do NOT include local T symbols */
417 			gflag = 0;
418 			break;
419 
420 		case 'z':	/* Print all symbols in profiling range, */
421 				/*	 even if no time or # calls. */
422 			flags |= F_ZSYMS;	/* Set flag. */
423 			break;
424 
425 		case 'h':	/* Suppress table header. */
426 			flags |= F_NHEAD;
427 			break;
428 
429 		case 's':	/* Follow normal output with extra summary. */
430 			flags |= F_VERBOSE;	/* Set flag (...) */
431 			break;
432 
433 		case 'V':
434 			(void) fprintf(stderr, "prof: %s %s\n",
435 			    (const char *)SGU_PKG, (const char *)SGU_REL);
436 			VwasSpecified = 1;
437 			break;
438 
439 		case 'C':	/* demangle C++ names before printing. */
440 			Cflag = 1;
441 			break;
442 
443 		case '?':	/* But no good. */
444 			usage();
445 		}	/* End switch (n) */
446 	}	/* End while (getopt) */
447 
448 	DEBUG_LOC("main: following getopt");
449 
450 	/* if -V the only argument, just exit. */
451 	if (VwasSpecified && argc == 2 && !flags)
452 		exit(0);
453 
454 	if (optind < argc)
455 		sym_fn = argv[optind];	/* name other than `a.out' */
456 
457 	if (sort == NULL && !(flags & F_SORT))
458 				/* If have not specified sort mode ... */
459 		sort = c_time;		/* then sort by decreasing time. */
460 
461 	/*
462 	 * profver() checks to see if the mon.out was "versioned" and if
463 	 * yes, processes it and exits; otherwise, we have an *old-style*
464 	 * mon.out and we process it the old way.
465 	 */
466 	profver();
467 
468 		/* Open monitor data file (has counts). */
469 	if ((mon_iop = fopen(mon_fn, "r")) == NULL)
470 		Perror(mon_fn);
471 
472 	DEBUG_LOC("main: before _symintOpen");
473 	if ((ldptr = _symintOpen(sym_fn)) == NULL) {
474 		Perror("_symintOpen failed");
475 	}
476 	DEBUG_LOC("main: after _symintOpen");
477 	filhdr = *ldptr;
478 
479 	scnhdrp = ldptr->pf_shdarr_p;
480 
481 	{
482 	Elf_Kind k = elf_kind(filhdr.pf_elf_p);
483 
484 	DEBUG_EXP(printf("elf_kind = %d\n", k));
485 	DEBUG_EXP(printf("elf_type = %d\n", filhdr.pf_elfhd_p->e_type));
486 	if ((k != ELF_K_ELF) || (filhdr.pf_elfhd_p->e_type != ET_EXEC)) {
487 		Fprint(stderr, "%s: %s: improper format\n", cmdname, sym_fn);
488 		exit(1);
489 	}
490 	}
491 
492 	/* Compute the file address of symbol table. Machine-dependent. */
493 
494 	DEBUG_EXP(printf("number of symbols (pf_nsyms) = %d\n",
495 	    filhdr.pf_nsyms));
496 
497 		/* Number of symbols in file symbol table. */
498 	symttl = filhdr.pf_nsyms;
499 	if (symttl == 0) {		/* This is possible. */
500 		Fprint(stderr, "%s: %s: no symbols\n", cmdname, sym_fn);
501 		exit(0);		/* Note zero exit code. */
502 	}
503 	/* Get size of file containing profiling data. Read header part. */
504 	n = fsize(fileno(mon_iop));
505 	if (fread((char *)&head, sizeof (struct hdr), 1, mon_iop) != 1)
506 		eofon(mon_iop, mon_fn);		/* Probably junk file. */
507 
508 	/* Get # cnt structures (they follow header), */
509 	/*		and allocate space for them. */
510 
511 	n_cc = head.nfns;
512 	ccounts = _prof_Malloc(n_cc, sizeof (struct cnt));
513 
514 		/* Read the call addr-count pairs. */
515 	if (fread((char *)ccounts, sizeof (struct cnt), n_cc, mon_iop) != n_cc)
516 		eofon(mon_iop, mon_fn);
517 
518 	/*
519 	 * Compute # PC counters (pcounts), which occupy whatever is left
520 	 * of the file after the header and call counts.
521 	 */
522 
523 	n_pc = (n - sizeof (head) - n_cc * sizeof (struct cnt))/sizeof (WORD);
524 	ccp = &ccounts[n_cc];	/* Point to last (+1) of call counters ... */
525 	do {		/* and scan backward until find highest one used. */
526 		if ((--ccp)->mcnt)
527 			break;		/* Stop when find nonzero count. */
528 	} while (--n_cc > 0);		/* Or all are zero. */
529 
530 	if (n_cc > 0) {
531 
532 	/* If less than all cnt entries are used, return unused space. */
533 	if (n_cc < head.nfns) {
534 		if ((ccounts = (struct cnt *)realloc((char *)ccounts,
535 		    (unsigned)n_cc * sizeof (struct cnt))) == NULL)
536 			snh();	/* Should not fail when reducing size. */
537 	}
538 
539 	/* If more than 250 cnt entries used set verbose for warning */
540 	if (n_cc > (MPROGS0 * 5)/6)
541 		flags |= F_VERBOSE;
542 
543 		/* Space for PC counts. */
544 	pcounts = (WORD *)_prof_Malloc(n_pc, sizeof (WORD));
545 		/* Read the PC counts from rest of MON_OUT file. */
546 	if (fread((char *)pcounts, sizeof (WORD), n_pc, mon_iop) != n_pc)
547 		eofon(mon_iop, mon_fn);
548 	/*
549 	 *
550 	 * Having gotten preliminaries out of the way, get down to business.
551 	 * The range pc_m of addresses over which profiling was done is
552 	 * computed from the low (pc_l) and high (pc_h) addresses, gotten
553 	 * from the MON_OUT header.  From this and the number of clock
554 	 * tick counters, n_pc, is computed the so-called "scale", sf, used
555 	 * in the mapping of addresses to indices, as follows:
556 	 *
557 	 *		(pc - pc_l) * sf
558 	 *	i(pc) = ----------------
559 	 *		  0200000
560 	 *
561 	 * Also, the N-to-one value, s_inv, such that
562 	 *
563 	 *	i(pc_l + K * s_inv + d) = K, for 0 <= d < s_inv
564 	 *
565 	 * Following this, the symbol table is scanned, and those symbols
566 	 * that qualify are counted.  These  are T-type symbols, excluding
567 	 * local (nonglobal) unless the "-g" option was given. Having thus
568 	 * determined the space requirements, space for symbols/times etc.
569 	 * is allocated, and the symbol table re-read, this time keeping
570 	 * qualified symbols.
571 	 *
572 	 * NB s_inv, as actually computed, is not sufficiently accurate
573 	 * (since it is truncated) for many calculations.  Since it is
574 	 * logically equivalent to 1/(sf/bias), and the latter is much
575 	 * more accurate, therefore the latter will often appear in
576 	 * the code when 's_inv' is mentioned.  dween
577 	 *
578 	 */
579 
580 
581 	pc_l = head.lpc;	/* Low PC of range that was profiled. */
582 	pc_h = head.hpc;	/* First address past range of profiling. */
583 	pc_m = pc_h - pc_l;	/* Range of profiled addresses. */
584 
585 	/* BEGIN CSTYLED */
586 OLD_DEBUG(if (debug_value) Fprint(stderr,
587 "low pc = %#o, high pc = %#o, range = %#o = %u\n\
588 call counts: %u, %u used; pc counters: %u\n",
589 pc_l, pc_h, pc_m, pc_m, head.nfns, n_cc, n_pc));
590 	/* END CSTYLED */
591 
592 	/*LINTED: E_ASSIGMENT_CAUSE_LOSS_PREC*/
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 
605 	/* BEGIN CSTYLED */
606 OLD_DEBUG(
607 	if (debug_value) {
608 
609 		Fprint(stderr, "sf = %d, s_inv = %d bias = %d\n",
610 		    (long)sf, pc_m / n_pc, bias);
611 	}
612 );
613 	/* END CSTYLED */
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 	/* BEGIN CSTYLED */
623 OLD_DEBUG(
624 	if (debug_value) {
625 		Fprint(stderr, "%u symbols, %u qualify\n", symttl, n_syms);
626 	}
627 );
628 	/* END CSTYLED */
629 
630 		/* Allocate space for qualified symbols. */
631 
632 	slist = slp = _prof_Malloc(n_syms, sizeof (struct slist));
633 
634 		/*
635 		 * Allocate space for synonym symbols
636 		 * (i.e. symbols that refer to the same address).
637 		 * NB there can be no more than n_syms/2 addresses
638 		 * with symbols, That Have Aliases, that refer to them!
639 		 */
640 
641 	snymCapacity = n_syms/2;
642 	snymList = snymp =
643 	    _prof_Malloc(snymCapacity, sizeof (struct snymEntry));
644 	n_snyms = 0;
645 
646 /* OLD_DEBUG(debug_value &= ~020); */
647 
648 	/* Loop on number of qualified symbols. */
649 	for (n = n_syms, symct = 0; n > 0; symct++) {
650 		if (readnl(symct)) {	/* Get one. Check again. */
651 				/* Is qualified. Move name ... */
652 			slp->sl_name = getname(ldptr, nl);
653 
654 				/* and address into slist structure. */
655 			slp->sl_addr = (char *)nl.ps_sym.st_value;
656 			slp->sl_size = nl.ps_sym.st_size;
657 
658 				/* set other slist fields to zero. */
659 			slp->sl_time = 0.0;
660 			slp->sl_count = 0;
661 	/* BEGIN CSTYLED */
662 OLD_DEBUG(
663 	if (debug_value & 02)
664 		Fprint(stderr, "%-8.8s: %#8o\n", slp->sl_name, slp->sl_addr)
665 );
666 	/* END CSTYLED */
667 
668 			slp++;
669 			--n;
670 		}
671 	}
672 	/*
673 	 *
674 	 * Now attempt to match call counts with symbols.  To do this, it
675 	 * helps to first sort both the symbols and the call address/count
676 	 * pairs by ascending address, since they are generally not, to
677 	 * begin with.  The addresses associated with the counts are not,
678 	 * of course, the subroutine addresses associated with the symbols,
679 	 * but some address slightly past these. Therefore a given count
680 	 * address (in the fnpc field) is matched with the closest symbol
681 	 * address (sl_addr) that is:
682 	 *	(1) less than the fnpc value but,
683 	 *	(2) not more than the length of the function
684 	 * In other words, unreasonable matchups are avoided.
685 	 * Situations such as this could arise when static procedures are
686 	 * counted but the "-g" option was not given to this program,
687 	 * causing the symbol to fail to qualify.  Without this limitation,
688 	 * unmatched counts could be erroneously charged.
689 	 *
690 	 */
691 
692 
693 	ccp = ccounts;			/* Point to first call counter. */
694 	slp = slist;			/*   "		"   "   symbol. */
695 		/* Sort call counters and ... */
696 	qsort((char *)ccp, (unsigned)n_cc, sizeof (struct cnt), c_ccaddr);
697 		/* symbols by increasing address. */
698 	qsort((char *)slp, (unsigned)n_syms, sizeof (struct slist), c_sladdr);
699 	vn_cc = n_cc;			/* save this for verbose option */
700 
701 
702 		/* Loop to match up call counts & symbols. */
703 	for (n = n_syms; n > 0 && vn_cc > 0; ) {
704 		int	sz = slp->sl_size;
705 
706 		if (sz == 0)
707 			sz = slp[ 1 ].sl_addr - slp->sl_addr;
708 		if (slp->sl_addr < ccp->fnpc &&
709 		    ccp->fnpc <= slp->sl_addr + sz) {
710 					/* got a candidate: find Closest. */
711 			struct slist *closest_symp;
712 			do {
713 				closest_symp = slp;
714 				slp++;
715 				--n;
716 			} while (n > 0 && slp->sl_addr < ccp->fnpc);
717 
718 	/* BEGIN CSTYLED */
719 OLD_DEBUG(
720 if (debug_value & 04) {
721 	Fprint(stderr,
722 		"Routine %-8.8s @ %#8x+%-2d matches count address %#8x\n",
723 		closest_symp->sl_name,
724 		closest_symp->sl_addr,
725 		ccp->fnpc-slp->sl_addr,
726 		ccp->fnpc);
727 }
728 );
729 	/* END CSTYLED */
730 			closest_symp->sl_count = ccp->mcnt;  /* Copy count. */
731 			++ccp;
732 			--vn_cc;
733 		} else if (ccp->fnpc < slp->sl_addr) {
734 			++ccp;
735 			--vn_cc;
736 		} else {
737 			++slp;
738 			--n;
739 		}
740 	}
741 
742 	/*
743 	 *
744 	 * The distribution of times to addresses is done on a proportional
745 	 * basis as follows: The t counts in pcounts[i] correspond to clock
746 	 * ticks for values of pc in the range pc, pc+1, ..., pc+s_inv-1
747 	 * (odd addresses excluded for PDP11s). Without more detailed info,
748 	 * it must be assumed that there is no greater probability
749 	 * of the clock ticking for any particular pc in this range than for
750 	 * any other.  Thus the t counts are considered to be equally
751 	 * distributed over the addresses in the range, and that the time for
752 	 * any given address in the range is pcounts[i]/s_inv.
753 	 *
754 	 * The values of the symbols that qualify, bounded below and above
755 	 * by pc_l and pc_h, respectively, partition the profiling range into
756 	 * regions to which are assigned the total times associated with the
757 	 * addresses they contain in the following way:
758 	 *
759 	 * The sum of all pcounts[i] for which the corresponding addresses are
760 	 * wholly within the partition are charged to the partition (the
761 	 * subroutine whose address is the lower bound of the partition).
762 	 *
763 	 * If the range of addresses corresponding to a given t = pcounts[i]
764 	 * lies astraddle the boundary of a partition, e.g., for some k such
765 	 * that 0 < k < s_inv-1, the addresses pc, pc+1, ..., pc+k-1 are in
766 	 * the lower partition, and the addresses pc+k, pc+k+1, ..., pc+s_inv-1
767 	 * are in the next partition, then k*pcounts[i]/s_inv time is charged
768 	 * to the lower partition, and (s_inv-k) * pcounts[i]/s_inv time to the
769 	 * upper.  It is conceivable, in cases of large granularity or small
770 	 * subroutines, for a range corresponding to a given pcounts[i] to
771 	 * overlap three regions, completely containing the (small) middle one.
772 	 * The algorithm is adjusted appropriately in this case.
773 	 *
774 	 */
775 
776 
777 	pcp = pcounts;				/* Reset to base. */
778 	slp = slist;				/* Ditto. */
779 	t0 = 0.0;				/* Time accumulator. */
780 	for (n = 0; n < n_syms; n++) {		/* Loop on symbols. */
781 			/* Start addr of region, low addr of overlap. */
782 		char *pc0, *pc00;
783 			/* Start addr of next region, low addr of overlap. */
784 		char *pc1, *pc10;
785 		/* First index into pcounts for this region and next region. */
786 		int i0, i1;
787 		long ticks;
788 
789 			/* Address of symbol (subroutine). */
790 		pc0 = slp[n].sl_addr;
791 
792 			/* Address of next symbol, if any or top */
793 			/* of profile range, if not */
794 		pc1 = (n < n_syms - 1) ? slp[n+1].sl_addr : pc_h;
795 
796 			/* Lower bound of indices into pcounts for this range */
797 
798 		i0 = (((unsigned)pc0 - (unsigned)pc_l) * sf)/bias;
799 
800 			/* Upper bound (least or least + 1) of indices. */
801 		i1 = (((unsigned)pc1 - (unsigned)pc_l) * sf)/bias;
802 
803 		if (i1 >= n_pc)				/* If past top, */
804 			i1 = n_pc - 1;				/* adjust. */
805 
806 			/* Lowest addr for which count maps to pcounts[i0]; */
807 		pc00 =  pc_l + (unsigned long)((bias * i0)/sf);
808 
809 			/* Lowest addr for which count maps to pcounts[i1]. */
810 		pc10 =  pc_l + (unsigned long)((bias * i1)/sf);
811 
812 	/* BEGIN CSTYLED */
813 OLD_DEBUG(if (debug_value & 010) Fprint(stderr,
814 "%-8.8s\ti0 = %4d, pc00 = %#6o, pc0 = %#6o\n\
815 \t\ti1 = %4d, pc10 = %#6o, pc1 = %#6o\n\t\t",
816 slp[n].sl_name, i0, pc00, pc0, i1, pc10, pc1));
817 	/* END CSTYLED */
818 		t = 0;			/* Init time for this symbol. */
819 		if (i0 == i1) {
820 			/* Counter overlaps two areas? (unlikely */
821 			/* unless large granularity). */
822 			ticks = pcp[i0];	/* # Times (clock ticks). */
823 OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks));
824 
825 			    /* Time less that which overlaps adjacent areas */
826 			t += PROFSEC(ticks * ((double)(pc1 - pc0) * sf)/bias);
827 
828 	/* BEGIN CSTYLED */
829 OLD_DEBUG(if (debug_value & 010)
830 	Fprint(stderr, "%ld/(%.1f)", (pc1 - pc0) * ticks, DBL_ADDRPERCELL)
831 );
832 	/* END CSTYLED */
833 		} else {
834 				/* Overlap with previous region? */
835 			if (pc00 < pc0) {
836 				ticks = pcp[i0];
837 	/* BEGIN CSTYLED */
838 OLD_DEBUG(if (debug_value & 010)
839 	fprintf(stderr, "pc00 < pc0 ticks = %d\n", ticks));
840 
841 				/* Get time of overlapping area and */
842 				/* subtract proportion for lower region. */
843 				t += PROFSEC(
844 				ticks*(1-((double)(pc0-pc00) *sf)/bias));
845 
846 				/* Do not count this time when summing times */
847 				/*		wholly within the region. */
848 				i0++;
849 	/* BEGIN CSTYLED */
850 OLD_DEBUG(if (debug_value & 010)
851 	Fprint(stderr, "%ld/(%.1f) + ", (pc0 - pc00) * ticks,
852 		DBL_ADDRPERCELL));
853 	/* END CSTYLED */
854 			}
855 
856 			/* Init sum of counts for PCs not shared w/other */
857 			/*	routines. */
858 			ticks = 0;
859 
860 			/* Stop at first count that overlaps following */
861 			/*	routine. */
862 			for (i = i0; i < i1; i++)
863 				ticks += pcp[i];
864 
865 			t += PROFSEC(ticks); /* Convert to secs, add to total */
866 OLD_DEBUG(if (debug_value & 010) Fprint(stderr, "%ld", ticks));
867 			/* Some overlap with low addresses of next routine? */
868 			if (pc10 < pc1) {
869 					/* Yes. Get total count ... */
870 				ticks = pcp[i1];
871 
872 				/* and accumulate proportion for addresses in */
873 				/*		range of this routine */
874 				t += PROFSEC(((double)ticks *
875 				    (pc1 - pc10)*sf)/bias);
876 	/* BEGIN CSTYLED */
877 OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks));
878 OLD_DEBUG(if (debug_value & 010)
879 	Fprint(stderr, " + %ld/(%.1f)", (pc1 - pc10) * ticks, DBL_ADDRPERCELL)
880 );
881 	/* END CSTYLED */
882 			}
883 		}		/* End if (i0 == i1) ... else ... */
884 
885 		slp[n].sl_time = t;	/* Store time for this routine. */
886 		t0 += t;		/* Accumulate total time. */
887 OLD_DEBUG(if (debug_value & 010) Fprint(stderr, " ticks = %.2f msec\n", t));
888 	}	/* End for (n = 0; n < n_syms; n++) */
889 
890 	/* Final pass to total up time. */
891 	/* Sum ticks, then convert to seconds. */
892 
893 	for (n = n_pc, temp = 0; --n >= 0; temp += *(pcp++))
894 		;
895 
896 	t_tot = PROFSEC(temp);
897 
898 	/*
899 	 * Now, whilst we still have the symbols sorted
900 	 * in address order..
901 	 * Loop to record duplicates, so we can display
902 	 * synonym symbols correctly.
903 	 * Synonym symbols, or symbols with the same address,
904 	 * are to be displayed by prof on the same line, with
905 	 * one statistics line, as below:
906 	 *			... 255  ldaopen, ldaopen
907 	 * The way this will be implemented, is as follows:
908 	 *
909 	 * Pass 1 - while the symbols are in address order, we
910 	 *  do a pre-pass through them, to determine for which
911 	 *  addresses there are more than one symbol (i.e. synonyms).
912 	 *  During this prepass we collect summary statistics in
913 	 *  the synonym entry, for all the synonyms.
914 	 *
915 	 * 'Pass' 2 - while printing a report,  for each report line,
916 	 *  if the current symbol is a synonym symbol (i.e. in the
917 	 *  snymList) then we scan forward and pick up all the names
918 	 *  which map to this address, and print them too.
919 	 *  If the address' synonyms have already been printed, then
920 	 *  we just skip this symbol and go on to process the next.
921 	 *
922 	 */
923 
924 	{
925 	/* pass 1 */
926 	char *thisaddr;
927 	char *lastaddr = slist->sl_addr; /* use 1st sym as */
928 					/* 'last/prior symbol' */
929 	int lastWasSnym = 0;	/* 1st can't be snym yet-no aliases seen! */
930 	int thisIsSnym;
931 
932 	/* BEGIN CSTYLED */
933 OLD_DEBUG(
934 int totsnyms = 0; int totseries = 0; struct slist *lastslp = slist;
935 );
936 	/* END CSTYLED */
937 
938 	/* NB loop starts with 2nd symbol, loops over n_syms-1 symbols! */
939 	for (n = n_syms-1, slp = slist+1; --n >= 0; slp++) {
940 		thisaddr = slp->sl_addr;
941 		thisIsSnym = (thisaddr == lastaddr);
942 
943 		if (thisIsSnym) {
944 			/* gotta synonym */
945 			if (!lastWasSnym) {
946 	/* BEGIN CSTYLED */
947 OLD_DEBUG(
948 if (debug_value)  {
949 	Fprint(stderr,
950 		"Synonym series:\n1st->\t%s at address %x, ct=%ld, time=%f\n",
951 		lastslp->sl_name, lastaddr, lastslp->sl_count,
952 		lastslp->sl_time);
953 	totseries++;
954 	totsnyms++;
955 }
956 );
957 	/* END CSTYLED */
958 				/* this is the Second! of a series */
959 				snymp = (n_snyms++ == 0 ? snymList : snymp+1);
960 				snymp->howMany = 1; /* gotta count 1st one!! */
961 				snymp->sym_addr = slp->sl_addr;
962 				/* zero summary statistics */
963 				snymp->tot_sl_count = 0;
964 				snymp->tot_sl_time = 0.0;
965 				/* Offen the Reported flag */
966 				snymp->snymReported = 0;
967 			}
968 	/* BEGIN CSTYLED */
969 OLD_DEBUG(
970 if (debug_value)  {
971 	Fprint(stderr,
972 		"\t%s at address %x, ct=%ld, time=%f\n",
973 		slp->sl_name,
974 		thisaddr,
975 		slp->sl_count,
976 		slp->sl_time);
977 	totsnyms++;
978 }
979 );
980 	/* END CSTYLED */
981 			/* ok - bump count for snym, and note its Finding */
982 			snymp->howMany++;
983 			/* and update the summary statistics */
984 			snymp->tot_sl_count += slp->sl_count;
985 			snymp->tot_sl_time += slp->sl_time;
986 		}
987 		callTotal += slp->sl_count;
988 		lastaddr = thisaddr;
989 		lastWasSnym = thisIsSnym;
990 	/* BEGIN CSTYLED */
991 OLD_DEBUG(
992 if (debug_value) lastslp = slp;
993 );
994 	/* END CSTYLED */
995 
996 	}
997 	/* BEGIN CSTYLED */
998 OLD_DEBUG(
999 if (debug_value)  {
1000 	Fprint(stderr, "Total #series %d, #synonyms %d\n", totseries, totsnyms);
1001 }
1002 );
1003 	/* END CSTYLED */
1004 	}
1005 	/*
1006 	 * Most of the heavy work is done now.  Only minor stuff remains.
1007 	 * The symbols are currently in address order and must be re-sorted
1008 	 * if desired in a different order.  Report generating options
1009 	 * include "-o" or "-x": Include symbol address, which causes
1010 	 * another column
1011 	 * in the output; and "-z": Include symbols in report even if zero
1012 	 * time and call count.  Symbols not in profiling range are excluded
1013 	 * in any case.  Following the main body of the report, the "-s"
1014 	 * option causes certain additional information to be printed.
1015 	 */
1016 
1017 	OLD_DEBUG(if (debug_value) Fprint(stderr,
1018 	    "Time unaccounted for: %.7G\n", t_tot - t0));
1019 
1020 	if (sort)	/* If comparison routine given then use it. */
1021 		qsort((char *)slist, (unsigned)n_syms,
1022 		    sizeof (struct slist), sort);
1023 
1024 	if (!(flags & F_NHEAD)) {
1025 		if (flags & F_PADDR)
1026 			Print("%s", atitle);	/* Title for addresses. */
1027 		(void) puts(" %Time Seconds Cumsecs  #Calls   msec/call  Name");
1028 	}
1029 	t = 0.0;			/* Init cumulative time. */
1030 	if (t_tot != 0.0)		/* Convert to percent. */
1031 		t_tot = 100.0/t_tot;	/* Prevent divide-by-zero fault */
1032 	n_nonzero = 0;	/* Number of symbols with nonzero time or # calls. */
1033 	for (n = n_syms, slp = slist; --n >= 0; slp++) {
1034 		long count;	 /* # Calls. */
1035 		/* t0, time in seconds. */
1036 
1037 		/* if a snym symbol, use summarized stats, else use indiv. */
1038 		if ((snymp = getSnymEntry(slp->sl_addr)) != 0) {
1039 			count = snymp->tot_sl_count;
1040 			t0 = snymp->tot_sl_time;
1041 
1042 		} else {
1043 			count = slp->sl_count;
1044 			t0 = slp->sl_time;
1045 		}
1046 
1047 		/* if a snym and already reported, skip this entry */
1048 		if (snymp && snymp->snymReported)
1049 			continue;
1050 		/* Don't do entries with no action. */
1051 		if (t0 == 0.0 && count == 0 && !(flags & F_ZSYMS))
1052 			continue;
1053 		if ((strcmp(slp->sl_name, "_mcount") == 0) ||
1054 		    (strcmp(slp->sl_name, "mcount") == 0)) {
1055 			count = callTotal;
1056 		}
1057 
1058 		/* count number of entries (i.e. symbols) printed */
1059 		if (snymp)
1060 			n_nonzero += snymp->howMany; /* add for each snym */
1061 		else
1062 			n_nonzero++;
1063 
1064 		if (flags & F_PADDR) { /* Printing address of symbol? */
1065 			/* LINTED: variable format */
1066 			Print(aformat, slp->sl_addr);
1067 		}
1068 		t += t0;	/*  move here; compiler bug  !! */
1069 		Print("%6.1f%8.2f%8.2f", t0 * t_tot, t0, t);
1070 		fdigits = 0;
1071 		if (count) {		/* Any calls recorded? */
1072 		/* Get reasonable number of fractional digits to print. */
1073 			fdigits = fprecision(count);
1074 			Print("%8ld%#*.*f", count, fdigits+8, fdigits,
1075 			    1000.0*t0/count);
1076 			Print("%*s", 6-fdigits, " ");
1077 		} else {
1078 			Print("%22s", " ");
1079 		}
1080 		/*
1081 		 * now print the name (or comma-seperate list of names,
1082 		 * for synonym symbols).
1083 		 */
1084 		if (snymp) {
1085 			printSnymNames(slp, snymp);	/* print it, then */
1086 			snymp->snymReported = 1;	/* mark it Done */
1087 		}
1088 		else
1089 			(void) puts(slp->sl_name);	/* print the one name */
1090 	}
1091 	if (flags & F_VERBOSE) {		/* Extra info? */
1092 		Fprint(stderr, "%5d/%d call counts used\n", n_cc, head.nfns);
1093 		Fprint(stderr, "%5d/%d symbols qualified", n_syms, symttl);
1094 		if (n_nonzero < n_syms)
1095 			Fprint(stderr,
1096 			    ", %d had zero time and zero call-counts\n",
1097 			    n_syms - n_nonzero);
1098 		else
1099 			(void) putc('\n', stderr);
1100 		Fprint(stderr, "%#lx scale factor\n", (long)sf);
1101 	}
1102 
1103 	_symintClose(ldptr);
1104 	} else {
1105 		Fprint(stderr, "prof: no call counts captured\n");
1106 	}
1107 	return (0);
1108 }
1109 /* Return size of file associated with file descriptor fd. */
1110 
1111 static off_t
fsize(int fd)1112 fsize(int fd)
1113 {
1114 	struct stat sbuf;
1115 
1116 	if (fstat(fd, &sbuf) < 0)  /* Status of open file. */
1117 		Perror("stat");
1118 	return (sbuf.st_size);			/* This is a long. */
1119 }
1120 
1121 /* Read symbol entry. Return TRUE if satisfies conditions. */
1122 
1123 static int
readnl(int symindex)1124 readnl(int symindex)
1125 {
1126 	nl = ldptr->pf_symarr_p[symindex];
1127 
1128 	/* BEGIN CSTYLED */
1129 OLD_DEBUG(
1130 	if (debug_value & 020) {
1131 		Fprint(stderr,
1132 			"`%-8.8s'\tst_info=%#4o, value=%#8.6o\n",
1133 			ldptr->pf_symstr_p[nl.ps_sym.st_name],
1134 			(unsigned char) nl.ps_sym.st_info,
1135 			nl.ps_sym.st_value);
1136 	}
1137 );
1138 	/* END CSTYLED */
1139 
1140 	/*
1141 	 * TXTSYM accepts global (and local, if "-g" given) T-type symbols.
1142 	 * Only those in the profiling range are useful.
1143 	 */
1144 	return (nl.ps_sym.st_shndx < SHN_LORESERVE &&
1145 	    TXTSYM(nl.ps_sym.st_shndx, nl.ps_sym.st_info) &&
1146 	    (pc_l <= (char *)nl.ps_sym.st_value) &&
1147 	    ((char *)nl.ps_sym.st_value < pc_h));
1148 }
1149 /*
1150  * Error-checking memory allocators -
1151  * Guarantees good return (else none at all).
1152  */
1153 
1154 static void *
_prof_Malloc(int item_count,int item_size)1155 _prof_Malloc(int item_count, int item_size)
1156 {
1157 	void *p;
1158 
1159 	if ((p = malloc((unsigned)item_count * (unsigned)item_size)) == NULL)  {
1160 		(void) fprintf(stderr, "%s: Out of space\n", cmdname);
1161 		exit(1);
1162 	}
1163 	return (p);
1164 }
1165 
1166 
1167 
1168 /*
1169  *	Given the quotient Q = N/D, where entier(N) == N and D > 0, an
1170  *	approximation of the "best" number of fractional digits to use
1171  *	in printing Q is f = entier(log10(D)), which is crudely produced
1172  *	by the following routine.
1173  */
1174 
1175 static int
fprecision(long count)1176 fprecision(long count)
1177 {
1178 	return (count < 10 ? 0 : count < 100 ? 1 : count < 1000 ? 2 :
1179 	    count < 10000 ? 3 : 4);
1180 }
1181 
1182 /*
1183  *	Return pointer to base name(name less path) of string s.
1184  *	Handles case of superfluous trailing '/'s, and unlikely
1185  *	case of s == "/".
1186  */
1187 
1188 static char *
basename(char * s)1189 basename(char *s)
1190 {
1191 	char *p;
1192 
1193 	p = &s[strlen(s)];			/* End (+1) of string. */
1194 	while (p > s && *--p == '/')		/* Trim trailing '/'s. */
1195 		*p = '\0';
1196 	p++;					/* New end (+1) of string. */
1197 	while (p > s && *--p != '/')		/* Break backward on '/'. */
1198 		;
1199 	if (*p == '/')		/* If found '/', point to 1st following. */
1200 		p++;
1201 	if (*p == '\0')
1202 		p = "/";			/* If NULL, must be "/". (?) */
1203 	return (p);
1204 }
1205 /* Here if unexpected read problem. */
1206 
1207 static void
eofon(FILE * iop,char * fn)1208 eofon(FILE *iop, char *fn)
1209 {
1210 	if (ferror(iop))		/* Real error? */
1211 		Perror(fn);		/* Yes. */
1212 	Fprint(stderr, "%s: %s: Premature EOF\n", cmdname, fn);
1213 	exit(1);
1214 }
1215 
1216 /*
1217  * Version of perror() that prints cmdname first.
1218  * Print system error message & exit.
1219  */
1220 
1221 static void
Perror(char * s)1222 Perror(char *s)
1223 {
1224 	int err = errno;	/* Save current errno in case */
1225 
1226 	Fprint(stderr, "%s: ", cmdname);
1227 	errno = err;			/* Put real error back. */
1228 	perror(s);			/* Print message. */
1229 	_symintClose(ldptr);		/* cleanup symbol information */
1230 	exit(1);			/* Exit w/nonzero status. */
1231 }
1232 
1233 /* Here for things that "Should Never Happen". */
1234 
1235 static void
snh(void)1236 snh(void)
1237 {
1238 	Fprint(stderr, "%s: Internal error\n", cmdname);
1239 	(void) abort();
1240 }
1241 
1242 /*
1243  *	Various comparison routines for qsort. Uses:
1244  *
1245  *	c_ccaddr	- Compare fnpc fields of cnt structs to put
1246  *				call counters in increasing address order.
1247  *	c_sladdr	- Sort slist structures on increasing address.
1248  *	c_time		-  "	 "	  "      " decreasing time.
1249  *	c_ncalls	-  "	 "	  "      " decreasing # calls.
1250  *	c_name		-  "	 "	  "      " increasing symbol name
1251  */
1252 
1253 #define	CMP2(v1, v2)	((v1) < (v2) ? -1 : (v1) == (v2) ? 0 : 1)
1254 #define	CMP1(v)		CMP2(v, 0)
1255 
1256 int
c_ccaddr(const void * arg1,const void * arg2)1257 c_ccaddr(const void *arg1, const void *arg2)
1258 {
1259 	struct cnt *p1 = (struct cnt *)arg1;
1260 	struct cnt *p2 = (struct cnt *)arg2;
1261 
1262 	return (CMP2(p1->fnpc, p2->fnpc));
1263 }
1264 
1265 int
c_sladdr(const void * arg1,const void * arg2)1266 c_sladdr(const void *arg1, const void *arg2)
1267 {
1268 	struct slist *p1 = (struct slist *)arg1;
1269 	struct slist *p2 = (struct slist *)arg2;
1270 
1271 	return (CMP2(p1->sl_addr, p2->sl_addr));
1272 }
1273 
1274 int
c_time(const void * arg1,const void * arg2)1275 c_time(const void *arg1, const void *arg2)
1276 {
1277 	struct slist *p1 = (struct slist *)arg1;
1278 	struct slist *p2 = (struct slist *)arg2;
1279 	float dtime = p2->sl_time - p1->sl_time; /* Decreasing time. */
1280 
1281 	return (CMP1(dtime));
1282 }
1283 
1284 int
c_ncalls(const void * arg1,const void * arg2)1285 c_ncalls(const void *arg1, const void *arg2)
1286 {
1287 	struct slist *p1 = (struct slist *)arg1;
1288 	struct slist *p2 = (struct slist *)arg2;
1289 	int diff = p2->sl_count - p1->sl_count;
1290 		/* Decreasing # calls. */
1291 	return (CMP1(diff));
1292 }
1293 
1294 int
c_name(const void * arg1,const void * arg2)1295 c_name(const void *arg1, const void *arg2)
1296 {
1297 	struct slist *p1 = (struct slist *)arg1;
1298 	struct slist *p2 = (struct slist *)arg2;
1299 	int diff;
1300 
1301 		/* flex names has variable length strings for names */
1302 	diff = strcmp(p1->sl_name, p2->sl_name);
1303 	return (CMP1(diff));
1304 }
1305 
1306 #define	STRSPACE 2400		/* guess at amount of string space */
1307 
1308 char *format_buf;
1309 #define	FORMAT_BUF	"%s\n\t\t\t\t\t    [%s]"
1310 
1311 static char *
demangled_name(char * s)1312 demangled_name(char *s)
1313 {
1314 	const char *name;
1315 	size_t	len;
1316 
1317 	if ((name = conv_demangle_name(s)) == s)
1318 		return (s);
1319 
1320 	if (format_buf != NULL)
1321 		free(format_buf);
1322 
1323 	len = strlen(name) + strlen(FORMAT_BUF) + strlen(s) + 1;
1324 	format_buf = malloc(len);
1325 	if (format_buf == NULL)
1326 		return (s);
1327 	(void) snprintf(format_buf, len, FORMAT_BUF, name, s);
1328 	free((void *)name);
1329 	return (format_buf);
1330 }
1331 
1332 /* getname - get the name of a symbol in a permanent fashion */
1333 static char *
getname(PROF_FILE * ldpter,PROF_SYMBOL symbol)1334 getname(PROF_FILE *ldpter, PROF_SYMBOL symbol)
1335 {
1336 	static char *strtable = NULL;	/* space for names */
1337 	static int sp_used = 0;		/* space used so far */
1338 	static int size = 0;		/* size of string table */
1339 	char *name;			/* name to store */
1340 	int lth;			/* space needed for name */
1341 	int get;			/* amount of space to get */
1342 
1343 	name = elf_strptr(ldpter->pf_elf_p, ldpter->pf_symstr_ndx,
1344 	    symbol.ps_sym.st_name);
1345 	if (name == NULL)
1346 		return ("<<bad symbol name>>");
1347 
1348 	if (Cflag)
1349 		name = demangled_name(name);
1350 
1351 	lth = strlen(name) + 1;
1352 	if ((sp_used + lth) > size)  {	 /* if need more space */
1353 		/* just in case very long name */
1354 		get = lth > STRSPACE ? lth : STRSPACE;
1355 		strtable = _prof_Malloc(1, get);
1356 		size = get;
1357 		sp_used = 0;
1358 	}
1359 	(void) strcpy(&(strtable[sp_used]), name);
1360 	name = &(strtable[sp_used]);
1361 	sp_used += lth;
1362 	return (name);
1363 }
1364 
1365 static void
usage(void)1366 usage(void)
1367 {
1368 	(void) fprintf(stderr,
1369 	    "usage: %s [-ChsVz] [-a | c | n | t]  [-o  |  x]   [-g  |  l]\n"
1370 	    "\t[-m mdata] [prog]\n",
1371 	    cmdname);
1372 	exit(1);
1373 }
1374