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