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