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