8734 prof: variable 's_inv' set but not used
[unleashed.git] / usr / src / cmd / sgs / prof / common / prof.c
blob38a0aa0a9f4fb685b9045131b82f372f0121752c
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1988 AT&T */
28 /* All Rights Reserved */
31 * Program profiling report generator.
33 * Usage:
35 * prof [-ChsVz] [-a | c | n | t] [-o | x] [-g | l]
36 * [-m mdata] [prog]
38 * Where "prog" is the program that was profiled; "a.out" by default.
39 * Options are:
41 * -n Sort by symbol name.
42 * -t Sort by decreasing time.
43 * -c Sort by decreasing number of calls.
44 * -a Sort by increasing symbol address.
46 * The options that determine the type of sorting are mutually exclusive.
47 * Additional options are:
49 * -o Include symbol addresses in output (in octal).
50 * -x Include symbol addresses in output (in hexadecimal).
51 * -g Include non-global T-type symbols in output.
52 * -l Do NOT include local T-type symbols in output (default).
53 * -z Include all symbols in profiling range, even if zero
54 * number of calls or time.
55 * -h Suppress table header.
56 * -s Follow report with additional statistical information.
57 * -m mdata Use file "mdata" instead of MON_OUT for profiling data.
58 * -V print version information for prof (and exit, if only V spec'd)
59 * -C call C++ demangle routine to demangle names before printing.
62 #include <stdio.h>
63 #include <string.h>
64 #include <errno.h>
65 #include <dlfcn.h>
66 #include <ctype.h>
67 #include "conv.h"
68 #include "symint.h"
69 #include "sys/param.h" /* for HZ */
70 #include "mon.h"
71 #include "sys/stat.h"
72 #include "debug.h"
74 #define OLD_DEBUG(x)
76 #define Print (void) printf
77 #define Fprint (void) fprintf
79 #if vax
80 /* Max positive difference between a fnpc and sl_addr for match */
81 #define CCADIFF 22
82 /* Type if n_type field in file symbol table entry. */
83 #endif
85 #if (u3b || u3b15 || u3b2 || i386)
86 /* Max positive difference between a fnpc and sl_addr for match */
87 #define CCADIFF 20 /* ?? (16 would probably do) */
88 /* For u3b, the "type" is storage class + section number (no type_t) */
89 #endif
91 #if (sparc)
92 #define CCADIFF 24 /* PIC prologue length=20 + 4 */
93 #endif
96 #define PROFSEC(ticks) ((double)(ticks)/HZ) /* Convert clock ticks to seconds */
98 /* Title fragment used if symbol addresses in output ("-o" or "-x"). */
99 char *atitle = " Address ";
100 /* Format for addresses in output */
101 char *aformat = "%8o ";
103 #if !(vax || u3b || u3b15 || u3b2 || i386 || sparc)
104 /* Make sure something we are set up for. Else lay egg. */
105 #include "### No code for processor type ###"
106 #endif
109 /* Shorthand to gimme the Precise #of addresses per cells */
110 #define DBL_ADDRPERCELL (((double)bias)/sf)
113 /* Used for unsigned fixed-point fraction with binary scale at */
114 /* the left of 15'th bit (0 as least significant bit) . */
115 #define BIAS ((long)0200000L)
118 * TS1 insures that the symbols section is executable.
120 #define TS1(s) (((s) > 0) && (scnhdrp[(s)-1].sh_flags & SHF_EXECINSTR))
122 * TS2 insures that the symbol should be reported. We want
123 * to report only those symbols that are functions (STT_FUNC)
124 * or "notype" (STT_NOTYPE... "printf", for example). Also,
125 * unless the gflag is set, the symbol must be global.
128 #define TS2(i) \
129 (((ELF32_ST_TYPE(i) == STT_FUNC) || \
130 (ELF32_ST_TYPE(i) == STT_NOTYPE)) && \
131 ((ELF32_ST_BIND(i) == STB_GLOBAL) || \
132 (gflag && (ELF32_ST_BIND(i) == STB_LOCAL))))
134 #define TXTSYM(s, i) (TS1(s) && TS2(i))
136 int gflag = 0; /* replaces gmatch and gmask */
137 int Cflag = 0;
139 PROF_FILE *ldptr; /* For program ("a.out") file. */
141 FILE *mon_iop; /* For profile (MON_OUT) file. */
142 char *sym_fn = "a.out"; /* Default program file name. */
143 char *mon_fn = MON_OUT; /* Default profile file name. */
144 /* May be changed by "-m file". */
146 long bias; /* adjusted bias */
147 long temp; /* for bias adjust */
149 extern void profver(void);
151 /* For symbol table entries read from program file. */
152 PROF_SYMBOL nl;
154 /* Compare routines called from qsort() */
156 int c_ccaddr(const void *arg1, const void *arg2);
157 int c_sladdr(const void *arg1, const void *arg2);
158 int c_time(const void *arg1, const void *arg2);
159 int c_ncalls(const void *arg1, const void *arg2);
160 int c_name(const void *arg1, const void *arg2);
162 /* Other stuff. */
164 /* Return size of open file (arg is file descriptor) */
165 static off_t fsize(int fd);
167 static void snh(void);
168 static void Perror(char *s);
169 static void eofon(FILE *iop, char *fn);
170 static void usage(void);
171 static char *getname(PROF_FILE *ldpter, PROF_SYMBOL symbol);
173 /* Memory allocation. Like malloc(), but no return if error. */
174 static void *_prof_Malloc(int item_count, int item_size);
176 /* Scan past path part (if any) in the ... */
177 static char *basename(char *s);
179 /* command name, for error messages. */
180 char *cmdname;
181 /* Structure of subroutine call counters (cnt) is defined in mon.h. */
183 /* Structure for header of mon.out (hdr) is defined in mon.h. */
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. */
195 /* local structure for tracking synonyms in our symbol list */
196 struct snymEntry {
197 char *sym_addr; /* address which has a synonym */
198 int howMany; /* # of synonyms for this symbol */
199 int snymReported; /* 'was printed in a report line already' */
200 /* flag, */
201 /* > 0 report line printed for these syns. */
202 /* == 0 not printed yet. */
203 long tot_sl_count; /* total subr calls for these snyms */
204 float tot_sl_time; /* total clock ticks (a la sl_time) */
208 #define AOUTHSZ (filhdr.f_opthdr)
209 PROF_FILE filhdr; /* profile file descriptor */
210 Elf32_Shdr *scnhdrp; /* pointer to first section header */
211 /* (space by _prof_Malloc) */
213 struct hdr head; /* Profile file (MON_OUT) header. */
215 int (*sort)() = NULL; /* Compare routine for sorting output */
216 /* symbols. Set by "-[acnt]". */
218 int flags; /* Various flag bits. */
220 char *pc_l; /* From head.lpc. */
222 char *pc_h; /* " head.hpc. */
224 short VwasSpecified = 0; /* 1 if -V was specified */
227 * Bit macro and flag bit definitions. These need to be identical to the
228 * set in profv.h. Any change here should be reflected in profv.c also.
230 #define FBIT(pos) (01 << (pos)) /* Returns value with bit pos set. */
231 #define F_SORT FBIT(0) /* Set if "-[acnt]" seen. */
232 #define F_VERBOSE FBIT(1) /* Set if "-s" seen. */
233 #define F_ZSYMS FBIT(2) /* Set if "-z" seen. */
234 #define F_PADDR FBIT(3) /* Set if "-o" or "-x" seen. */
235 #define F_NHEAD FBIT(4) /* Set if "-h" seen. */
238 struct snymEntry *snymList; /* Pointer to allocated list of */
239 /* synonym entries. */
240 struct snymEntry *snymp;
241 /* for scanning entries. */
243 int snymCapacity; /* #slots in snymList */
244 int n_snyms; /* #used slots in snymList */
246 static int readnl(int symindex);
247 static int fprecision(long count);
250 * Sort flags. Mutually exclusive. These need to be identical to the ones
251 * defined in profv.h
253 #define BY_ADDRESS 0x1
254 #define BY_NCALLS 0x2
255 #define BY_NAME 0x4
256 #define BY_TIME 0x8
258 extern unsigned char sort_flag; /* what type of sort ? */
261 * printSnymNames - print a comma-seperated list of snym names.
262 * This routine hunts down all the synonyms for the given
263 * symbol, and prints them as a comma-seperated list.
264 * NB we assume that all the synonyms _Follow_ this one,
265 * since they are only printed when the First one
266 * is seen.
268 void
269 printSnymNames(struct slist *slp, struct snymEntry *snymp)
271 /* how many snyms for this addr, total, and their shared address */
272 int i = snymp->howMany;
273 char *sharedaddr = snymp->sym_addr;
275 /* put out first name - it counts as one, so decr count */
276 (void) fputs(slp->sl_name, stdout);
277 i--;
279 /* for the others: find each, print each. */
280 while (--i >= 0) {
281 while ((++slp)->sl_addr != sharedaddr)
283 Print(", %s", slp->sl_name);
285 /* finally.. the trailing newline */
286 (void) putchar('\n');
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.
295 struct snymEntry *
296 getSnymEntry(char *sl_addr)
298 struct snymEntry *p;
299 int i;
301 for (p = snymList, i = n_snyms; --i >= 0; p++)
302 if (sl_addr == p->sym_addr)
303 return (p);
305 return ((struct snymEntry *)0);
310 main(int argc, char **argv)
312 char buffer[BUFSIZ]; /* buffer for printf */
314 WORD *pcounts; /* Pointer to allocated area for */
315 /* pcounts: PC clock hit counts */
317 WORD *pcp; /* For scanning pcounts. */
319 struct cnt *ccounts; /* Pointer to allocated area for cnt */
320 /* structures: subr PC-call counts. */
322 struct cnt *ccp; /* For scanning ccounts. */
324 struct slist *slist; /* Pointer to allocated slist structures: */
325 /* symbol name/address/time/call counts */
327 struct slist *slp; /* For scanning slist */
329 int vn_cc, n_cc; /* Number of cnt structures in profile data */
330 /* file (later # ones used). */
332 int n_pc; /* Number of pcounts in profile data file. */
334 int n_syms; /* Number of text symbols (of proper type) */
335 /* that fill in range of profiling. */
337 int n_nonzero; /* Number of (above symbols) actually printed */
338 /* because nonzero time or # calls. */
340 int symttl; /* Total # symbols in program file sym-table */
342 int i;
344 int fdigits = 0; /* # of digits of precision for print msecs/call */
346 int n, symct;
348 long sf; /* Scale for index into pcounts: */
349 /* i(pc) = ((pc - pc_l) * sf)/bias. */
351 unsigned pc_m; /* Range of PCs profiled: pc_m = pc_h - pc_l */
353 float t, t0;
354 float t_tot; /* Total time: PROFSEC(sum of all pcounts[i]) */
355 int callTotal = 0;
357 DEBUG_LOC("main: top");
358 setbuf(stdout, buffer);
359 cmdname = basename(*argv); /* command name. */
361 while ((n = getopt(argc, argv, "canthsglzoxT:m:VC")) != EOF) {
362 switch (n) {
363 int (*fcn)(); /* For function to sort results. */
365 case 'm': /* Specify data file: -m file */
366 mon_fn = optarg;
367 break;
369 #ifdef ddt
370 case 'T': /* Set trace flags: -T(octnum) */
371 debug_value = (int)strtol(optarg, 0, 8);
372 break;
373 #endif
375 case 'n': /* Sort by symbol name. */
376 fcn = c_name;
377 sort_flag |= BY_NAME;
378 goto check;
380 case 't': /* Sort by decreasing time. */
381 fcn = c_time;
382 sort_flag |= BY_TIME;
383 goto check;
385 case 'c': /* Sort by decreasing # calls. */
386 fcn = c_ncalls;
387 sort_flag |= BY_NCALLS;
388 goto check;
390 case 'a': /* Sort by increasing symbol address */
391 /* (don't have to -- it will be) */
392 fcn = NULL;
393 sort_flag |= BY_ADDRESS;
394 check: /* Here to check sort option conflicts. */
395 if (sort != NULL && sort != fcn) {
396 Fprint(stderr, "%s: Warning: %c overrides"
397 " previous specification\n", cmdname, n);
399 sort = fcn; /* Store sort routine */
400 flags |= F_SORT; /* Note have done so */
401 break;
403 case 'o': /* Include symbol addresses in output. */
404 case 'x': /* Include symbol addresses in output. */
405 aformat[2] = n; /* 'o' or 'x' in format */
406 flags |= F_PADDR; /* Set flag. */
407 break;
409 case 'g': /* Include local T symbols as well as global */
410 gflag = 1;
411 break;
413 case 'l': /* Do NOT include local T symbols */
414 gflag = 0;
415 break;
417 case 'z': /* Print all symbols in profiling range, */
418 /* even if no time or # calls. */
419 flags |= F_ZSYMS; /* Set flag. */
420 break;
422 case 'h': /* Suppress table header. */
423 flags |= F_NHEAD;
424 break;
426 case 's': /* Follow normal output with extra summary. */
427 flags |= F_VERBOSE; /* Set flag (...) */
428 break;
430 case 'V':
431 (void) fprintf(stderr, "prof: %s %s\n",
432 (const char *)SGU_PKG, (const char *)SGU_REL);
433 VwasSpecified = 1;
434 break;
436 case 'C': /* demangle C++ names before printing. */
437 Cflag = 1;
438 break;
440 case '?': /* But no good. */
441 usage();
442 } /* End switch (n) */
443 } /* End while (getopt) */
445 DEBUG_LOC("main: following getopt");
447 /* if -V the only argument, just exit. */
448 if (VwasSpecified && argc == 2 && !flags)
449 exit(0);
451 if (optind < argc)
452 sym_fn = argv[optind]; /* name other than `a.out' */
454 if (sort == NULL && !(flags & F_SORT))
455 /* If have not specified sort mode ... */
456 sort = c_time; /* then sort by decreasing time. */
459 * profver() checks to see if the mon.out was "versioned" and if
460 * yes, processes it and exits; otherwise, we have an *old-style*
461 * mon.out and we process it the old way.
463 profver();
465 /* Open monitor data file (has counts). */
466 if ((mon_iop = fopen(mon_fn, "r")) == NULL)
467 Perror(mon_fn);
469 DEBUG_LOC("main: before _symintOpen");
470 if ((ldptr = _symintOpen(sym_fn)) == NULL) {
471 Perror("_symintOpen failed");
473 DEBUG_LOC("main: after _symintOpen");
474 filhdr = *ldptr;
476 scnhdrp = ldptr->pf_shdarr_p;
479 Elf_Kind k = elf_kind(filhdr.pf_elf_p);
481 DEBUG_EXP(printf("elf_kind = %d\n", k));
482 DEBUG_EXP(printf("elf_type = %d\n", filhdr.pf_elfhd_p->e_type));
483 if ((k != ELF_K_ELF) || (filhdr.pf_elfhd_p->e_type != ET_EXEC)) {
484 Fprint(stderr, "%s: %s: improper format\n", cmdname, sym_fn);
485 exit(1);
489 /* Compute the file address of symbol table. Machine-dependent. */
491 DEBUG_EXP(printf("number of symbols (pf_nsyms) = %d\n",
492 filhdr.pf_nsyms));
494 /* Number of symbols in file symbol table. */
495 symttl = filhdr.pf_nsyms;
496 if (symttl == 0) { /* This is possible. */
497 Fprint(stderr, "%s: %s: no symbols\n", cmdname, sym_fn);
498 exit(0); /* Note zero exit code. */
500 /* Get size of file containing profiling data. Read header part. */
501 n = fsize(fileno(mon_iop));
502 if (fread((char *)&head, sizeof (struct hdr), 1, mon_iop) != 1)
503 eofon(mon_iop, mon_fn); /* Probably junk file. */
505 /* Get # cnt structures (they follow header), */
506 /* and allocate space for them. */
508 n_cc = head.nfns;
509 ccounts = _prof_Malloc(n_cc, sizeof (struct cnt));
511 /* Read the call addr-count pairs. */
512 if (fread((char *)ccounts, sizeof (struct cnt), n_cc, mon_iop) != n_cc)
513 eofon(mon_iop, mon_fn);
516 * Compute # PC counters (pcounts), which occupy whatever is left
517 * of the file after the header and call counts.
520 n_pc = (n - sizeof (head) - n_cc * sizeof (struct cnt))/sizeof (WORD);
521 ccp = &ccounts[n_cc]; /* Point to last (+1) of call counters ... */
522 do { /* and scan backward until find highest one used. */
523 if ((--ccp)->mcnt)
524 break; /* Stop when find nonzero count. */
525 } while (--n_cc > 0); /* Or all are zero. */
527 if (n_cc > 0) {
529 /* If less than all cnt entries are used, return unused space. */
530 if (n_cc < head.nfns) {
531 if ((ccounts = (struct cnt *)realloc((char *)ccounts,
532 (unsigned)n_cc * sizeof (struct cnt))) == NULL)
533 snh(); /* Should not fail when reducing size. */
536 /* If more than 250 cnt entries used set verbose for warning */
537 if (n_cc > (MPROGS0 * 5)/6)
538 flags |= F_VERBOSE;
540 /* Space for PC counts. */
541 pcounts = (WORD *)_prof_Malloc(n_pc, sizeof (WORD));
542 /* Read the PC counts from rest of MON_OUT file. */
543 if (fread((char *)pcounts, sizeof (WORD), n_pc, mon_iop) != n_pc)
544 eofon(mon_iop, mon_fn);
547 * Having gotten preliminaries out of the way, get down to business.
548 * The range pc_m of addresses over which profiling was done is
549 * computed from the low (pc_l) and high (pc_h) addresses, gotten
550 * from the MON_OUT header. From this and the number of clock
551 * tick counters, n_pc, is computed the so-called "scale", sf, used
552 * in the mapping of addresses to indices, as follows:
554 * (pc - pc_l) * sf
555 * i(pc) = ----------------
556 * 0200000
558 * Also, the N-to-one value, s_inv, such that
560 * i(pc_l + K * s_inv + d) = K, for 0 <= d < s_inv
562 * Following this, the symbol table is scanned, and those symbols
563 * that qualify are counted. These are T-type symbols, excluding
564 * local (nonglobal) unless the "-g" option was given. Having thus
565 * determined the space requirements, space for symbols/times etc.
566 * is allocated, and the symbol table re-read, this time keeping
567 * qualified symbols.
569 * NB s_inv, as actually computed, is not sufficiently accurate
570 * (since it is truncated) for many calculations. Since it is
571 * logically equivalent to 1/(sf/bias), and the latter is much
572 * more accurate, therefore the latter will often appear in
573 * the code when 's_inv' is mentioned. dween
578 pc_l = head.lpc; /* Low PC of range that was profiled. */
579 pc_h = head.hpc; /* First address past range of profiling. */
580 pc_m = pc_h - pc_l; /* Range of profiled addresses. */
582 /* BEGIN CSTYLED */
583 OLD_DEBUG(if (debug_value) Fprint(stderr,
584 "low pc = %#o, high pc = %#o, range = %#o = %u\n\
585 call counts: %u, %u used; pc counters: %u\n",
586 pc_l, pc_h, pc_m, pc_m, head.nfns, n_cc, n_pc));
587 /* END CSTYLED */
589 /*LINTED: E_ASSIGMENT_CAUSE_LOSS_PREC*/
590 sf = (BIAS * (double)n_pc)/pc_m;
592 * Now adjust bias and sf so that there is no overflow
593 * when calculating indices.
595 bias = BIAS;
596 temp = pc_m;
597 while ((temp >>= 1) > 0x7fff) {
598 sf >>= 1;
599 bias >>= 1;
602 /* BEGIN CSTYLED */
603 OLD_DEBUG(
604 if (debug_value) {
606 Fprint(stderr, "sf = %d, s_inv = %d bias = %d\n",
607 (long)sf, pc_m / n_pc, bias);
610 /* END CSTYLED */
612 /* Prepare to read symbols from "a.out" (or whatever). */
613 n_syms = 0; /* Init count of qualified symbols. */
614 n = symttl; /* Total symbols. */
615 while (--n >= 0) /* Scan symbol table. */
616 if (readnl(n)) /* Read and examine symbol, count qualifiers */
617 n_syms++;
619 /* BEGIN CSTYLED */
620 OLD_DEBUG(
621 if (debug_value) {
622 Fprint(stderr, "%u symbols, %u qualify\n", symttl, n_syms);
625 /* END CSTYLED */
627 /* Allocate space for qualified symbols. */
629 slist = slp = _prof_Malloc(n_syms, sizeof (struct slist));
632 * Allocate space for synonym symbols
633 * (i.e. symbols that refer to the same address).
634 * NB there can be no more than n_syms/2 addresses
635 * with symbols, That Have Aliases, that refer to them!
638 snymCapacity = n_syms/2;
639 snymList = snymp =
640 _prof_Malloc(snymCapacity, sizeof (struct snymEntry));
641 n_snyms = 0;
643 /* OLD_DEBUG(debug_value &= ~020); */
645 /* Loop on number of qualified symbols. */
646 for (n = n_syms, symct = 0; n > 0; symct++) {
647 if (readnl(symct)) { /* Get one. Check again. */
648 /* Is qualified. Move name ... */
649 slp->sl_name = getname(ldptr, nl);
651 /* and address into slist structure. */
652 slp->sl_addr = (char *)nl.ps_sym.st_value;
653 slp->sl_size = nl.ps_sym.st_size;
655 /* set other slist fields to zero. */
656 slp->sl_time = 0.0;
657 slp->sl_count = 0;
658 /* BEGIN CSTYLED */
659 OLD_DEBUG(
660 if (debug_value & 02)
661 Fprint(stderr, "%-8.8s: %#8o\n", slp->sl_name, slp->sl_addr)
663 /* END CSTYLED */
665 slp++;
666 --n;
671 * Now attempt to match call counts with symbols. To do this, it
672 * helps to first sort both the symbols and the call address/count
673 * pairs by ascending address, since they are generally not, to
674 * begin with. The addresses associated with the counts are not,
675 * of course, the subroutine addresses associated with the symbols,
676 * but some address slightly past these. Therefore a given count
677 * address (in the fnpc field) is matched with the closest symbol
678 * address (sl_addr) that is:
679 * (1) less than the fnpc value but,
680 * (2) not more than the length of the function
681 * In other words, unreasonable matchups are avoided.
682 * Situations such as this could arise when static procedures are
683 * counted but the "-g" option was not given to this program,
684 * causing the symbol to fail to qualify. Without this limitation,
685 * unmatched counts could be erroneously charged.
690 ccp = ccounts; /* Point to first call counter. */
691 slp = slist; /* " " " symbol. */
692 /* Sort call counters and ... */
693 qsort((char *)ccp, (unsigned)n_cc, sizeof (struct cnt), c_ccaddr);
694 /* symbols by increasing address. */
695 qsort((char *)slp, (unsigned)n_syms, sizeof (struct slist), c_sladdr);
696 vn_cc = n_cc; /* save this for verbose option */
699 /* Loop to match up call counts & symbols. */
700 for (n = n_syms; n > 0 && vn_cc > 0; ) {
701 int sz = slp->sl_size;
703 if (sz == 0)
704 sz = slp[ 1 ].sl_addr - slp->sl_addr;
705 if (slp->sl_addr < ccp->fnpc &&
706 ccp->fnpc <= slp->sl_addr + sz) {
707 /* got a candidate: find Closest. */
708 struct slist *closest_symp;
709 do {
710 closest_symp = slp;
711 slp++;
712 --n;
713 } while (n > 0 && slp->sl_addr < ccp->fnpc);
715 /* BEGIN CSTYLED */
716 OLD_DEBUG(
717 if (debug_value & 04) {
718 Fprint(stderr,
719 "Routine %-8.8s @ %#8x+%-2d matches count address %#8x\n",
720 closest_symp->sl_name,
721 closest_symp->sl_addr,
722 ccp->fnpc-slp->sl_addr,
723 ccp->fnpc);
726 /* END CSTYLED */
727 closest_symp->sl_count = ccp->mcnt; /* Copy count. */
728 ++ccp;
729 --vn_cc;
730 } else if (ccp->fnpc < slp->sl_addr) {
731 ++ccp;
732 --vn_cc;
733 } else {
734 ++slp;
735 --n;
741 * The distribution of times to addresses is done on a proportional
742 * basis as follows: The t counts in pcounts[i] correspond to clock
743 * ticks for values of pc in the range pc, pc+1, ..., pc+s_inv-1
744 * (odd addresses excluded for PDP11s). Without more detailed info,
745 * it must be assumed that there is no greater probability
746 * of the clock ticking for any particular pc in this range than for
747 * any other. Thus the t counts are considered to be equally
748 * distributed over the addresses in the range, and that the time for
749 * any given address in the range is pcounts[i]/s_inv.
751 * The values of the symbols that qualify, bounded below and above
752 * by pc_l and pc_h, respectively, partition the profiling range into
753 * regions to which are assigned the total times associated with the
754 * addresses they contain in the following way:
756 * The sum of all pcounts[i] for which the corresponding addresses are
757 * wholly within the partition are charged to the partition (the
758 * subroutine whose address is the lower bound of the partition).
760 * If the range of addresses corresponding to a given t = pcounts[i]
761 * lies astraddle the boundary of a partition, e.g., for some k such
762 * that 0 < k < s_inv-1, the addresses pc, pc+1, ..., pc+k-1 are in
763 * the lower partition, and the addresses pc+k, pc+k+1, ..., pc+s_inv-1
764 * are in the next partition, then k*pcounts[i]/s_inv time is charged
765 * to the lower partition, and (s_inv-k) * pcounts[i]/s_inv time to the
766 * upper. It is conceivable, in cases of large granularity or small
767 * subroutines, for a range corresponding to a given pcounts[i] to
768 * overlap three regions, completely containing the (small) middle one.
769 * The algorithm is adjusted appropriately in this case.
774 pcp = pcounts; /* Reset to base. */
775 slp = slist; /* Ditto. */
776 t0 = 0.0; /* Time accumulator. */
777 for (n = 0; n < n_syms; n++) { /* Loop on symbols. */
778 /* Start addr of region, low addr of overlap. */
779 char *pc0, *pc00;
780 /* Start addr of next region, low addr of overlap. */
781 char *pc1, *pc10;
782 /* First index into pcounts for this region and next region. */
783 int i0, i1;
784 long ticks;
786 /* Address of symbol (subroutine). */
787 pc0 = slp[n].sl_addr;
789 /* Address of next symbol, if any or top */
790 /* of profile range, if not */
791 pc1 = (n < n_syms - 1) ? slp[n+1].sl_addr : pc_h;
793 /* Lower bound of indices into pcounts for this range */
795 i0 = (((unsigned)pc0 - (unsigned)pc_l) * sf)/bias;
797 /* Upper bound (least or least + 1) of indices. */
798 i1 = (((unsigned)pc1 - (unsigned)pc_l) * sf)/bias;
800 if (i1 >= n_pc) /* If past top, */
801 i1 = n_pc - 1; /* adjust. */
803 /* Lowest addr for which count maps to pcounts[i0]; */
804 pc00 = pc_l + (unsigned long)((bias * i0)/sf);
806 /* Lowest addr for which count maps to pcounts[i1]. */
807 pc10 = pc_l + (unsigned long)((bias * i1)/sf);
809 /* BEGIN CSTYLED */
810 OLD_DEBUG(if (debug_value & 010) Fprint(stderr,
811 "%-8.8s\ti0 = %4d, pc00 = %#6o, pc0 = %#6o\n\
812 \t\ti1 = %4d, pc10 = %#6o, pc1 = %#6o\n\t\t",
813 slp[n].sl_name, i0, pc00, pc0, i1, pc10, pc1));
814 /* END CSTYLED */
815 t = 0; /* Init time for this symbol. */
816 if (i0 == i1) {
817 /* Counter overlaps two areas? (unlikely */
818 /* unless large granularity). */
819 ticks = pcp[i0]; /* # Times (clock ticks). */
820 OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks));
822 /* Time less that which overlaps adjacent areas */
823 t += PROFSEC(ticks * ((double)(pc1 - pc0) * sf)/bias);
825 /* BEGIN CSTYLED */
826 OLD_DEBUG(if (debug_value & 010)
827 Fprint(stderr, "%ld/(%.1f)", (pc1 - pc0) * ticks, DBL_ADDRPERCELL)
829 /* END CSTYLED */
830 } else {
831 /* Overlap with previous region? */
832 if (pc00 < pc0) {
833 ticks = pcp[i0];
834 /* BEGIN CSTYLED */
835 OLD_DEBUG(if (debug_value & 010)
836 fprintf(stderr, "pc00 < pc0 ticks = %d\n", ticks));
838 /* Get time of overlapping area and */
839 /* subtract proportion for lower region. */
840 t += PROFSEC(
841 ticks*(1-((double)(pc0-pc00) *sf)/bias));
843 /* Do not count this time when summing times */
844 /* wholly within the region. */
845 i0++;
846 /* BEGIN CSTYLED */
847 OLD_DEBUG(if (debug_value & 010)
848 Fprint(stderr, "%ld/(%.1f) + ", (pc0 - pc00) * ticks,
849 DBL_ADDRPERCELL));
850 /* END CSTYLED */
853 /* Init sum of counts for PCs not shared w/other */
854 /* routines. */
855 ticks = 0;
857 /* Stop at first count that overlaps following */
858 /* routine. */
859 for (i = i0; i < i1; i++)
860 ticks += pcp[i];
862 t += PROFSEC(ticks); /* Convert to secs, add to total */
863 OLD_DEBUG(if (debug_value & 010) Fprint(stderr, "%ld", ticks));
864 /* Some overlap with low addresses of next routine? */
865 if (pc10 < pc1) {
866 /* Yes. Get total count ... */
867 ticks = pcp[i1];
869 /* and accumulate proportion for addresses in */
870 /* range of this routine */
871 t += PROFSEC(((double)ticks *
872 (pc1 - pc10)*sf)/bias);
873 /* BEGIN CSTYLED */
874 OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks));
875 OLD_DEBUG(if (debug_value & 010)
876 Fprint(stderr, " + %ld/(%.1f)", (pc1 - pc10) * ticks, DBL_ADDRPERCELL)
878 /* END CSTYLED */
880 } /* End if (i0 == i1) ... else ... */
882 slp[n].sl_time = t; /* Store time for this routine. */
883 t0 += t; /* Accumulate total time. */
884 OLD_DEBUG(if (debug_value & 010) Fprint(stderr, " ticks = %.2f msec\n", t));
885 } /* End for (n = 0; n < n_syms; n++) */
887 /* Final pass to total up time. */
888 /* Sum ticks, then convert to seconds. */
890 for (n = n_pc, temp = 0; --n >= 0; temp += *(pcp++))
893 t_tot = PROFSEC(temp);
896 * Now, whilst we still have the symbols sorted
897 * in address order..
898 * Loop to record duplicates, so we can display
899 * synonym symbols correctly.
900 * Synonym symbols, or symbols with the same address,
901 * are to be displayed by prof on the same line, with
902 * one statistics line, as below:
903 * ... 255 ldaopen, ldaopen
904 * The way this will be implemented, is as follows:
906 * Pass 1 - while the symbols are in address order, we
907 * do a pre-pass through them, to determine for which
908 * addresses there are more than one symbol (i.e. synonyms).
909 * During this prepass we collect summary statistics in
910 * the synonym entry, for all the synonyms.
912 * 'Pass' 2 - while printing a report, for each report line,
913 * if the current symbol is a synonym symbol (i.e. in the
914 * snymList) then we scan forward and pick up all the names
915 * which map to this address, and print them too.
916 * If the address' synonyms have already been printed, then
917 * we just skip this symbol and go on to process the next.
922 /* pass 1 */
923 char *thisaddr;
924 char *lastaddr = slist->sl_addr; /* use 1st sym as */
925 /* 'last/prior symbol' */
926 int lastWasSnym = 0; /* 1st can't be snym yet-no aliases seen! */
927 int thisIsSnym;
929 /* BEGIN CSTYLED */
930 OLD_DEBUG(
931 int totsnyms = 0; int totseries = 0; struct slist *lastslp = slist;
933 /* END CSTYLED */
935 /* NB loop starts with 2nd symbol, loops over n_syms-1 symbols! */
936 for (n = n_syms-1, slp = slist+1; --n >= 0; slp++) {
937 thisaddr = slp->sl_addr;
938 thisIsSnym = (thisaddr == lastaddr);
940 if (thisIsSnym) {
941 /* gotta synonym */
942 if (!lastWasSnym) {
943 /* BEGIN CSTYLED */
944 OLD_DEBUG(
945 if (debug_value) {
946 Fprint(stderr,
947 "Synonym series:\n1st->\t%s at address %x, ct=%ld, time=%f\n",
948 lastslp->sl_name, lastaddr, lastslp->sl_count,
949 lastslp->sl_time);
950 totseries++;
951 totsnyms++;
954 /* END CSTYLED */
955 /* this is the Second! of a series */
956 snymp = (n_snyms++ == 0 ? snymList : snymp+1);
957 snymp->howMany = 1; /* gotta count 1st one!! */
958 snymp->sym_addr = slp->sl_addr;
959 /* zero summary statistics */
960 snymp->tot_sl_count = 0;
961 snymp->tot_sl_time = 0.0;
962 /* Offen the Reported flag */
963 snymp->snymReported = 0;
965 /* BEGIN CSTYLED */
966 OLD_DEBUG(
967 if (debug_value) {
968 Fprint(stderr,
969 "\t%s at address %x, ct=%ld, time=%f\n",
970 slp->sl_name,
971 thisaddr,
972 slp->sl_count,
973 slp->sl_time);
974 totsnyms++;
977 /* END CSTYLED */
978 /* ok - bump count for snym, and note its Finding */
979 snymp->howMany++;
980 /* and update the summary statistics */
981 snymp->tot_sl_count += slp->sl_count;
982 snymp->tot_sl_time += slp->sl_time;
984 callTotal += slp->sl_count;
985 lastaddr = thisaddr;
986 lastWasSnym = thisIsSnym;
987 /* BEGIN CSTYLED */
988 OLD_DEBUG(
989 if (debug_value) lastslp = slp;
991 /* END CSTYLED */
994 /* BEGIN CSTYLED */
995 OLD_DEBUG(
996 if (debug_value) {
997 Fprint(stderr, "Total #series %d, #synonyms %d\n", totseries, totsnyms);
1000 /* END CSTYLED */
1003 * Most of the heavy work is done now. Only minor stuff remains.
1004 * The symbols are currently in address order and must be re-sorted
1005 * if desired in a different order. Report generating options
1006 * include "-o" or "-x": Include symbol address, which causes
1007 * another column
1008 * in the output; and "-z": Include symbols in report even if zero
1009 * time and call count. Symbols not in profiling range are excluded
1010 * in any case. Following the main body of the report, the "-s"
1011 * option causes certain additional information to be printed.
1014 OLD_DEBUG(if (debug_value) Fprint(stderr,
1015 "Time unaccounted for: %.7G\n", t_tot - t0));
1017 if (sort) /* If comparison routine given then use it. */
1018 qsort((char *)slist, (unsigned)n_syms,
1019 sizeof (struct slist), sort);
1021 if (!(flags & F_NHEAD)) {
1022 if (flags & F_PADDR)
1023 Print("%s", atitle); /* Title for addresses. */
1024 (void) puts(" %Time Seconds Cumsecs #Calls msec/call Name");
1026 t = 0.0; /* Init cumulative time. */
1027 if (t_tot != 0.0) /* Convert to percent. */
1028 t_tot = 100.0/t_tot; /* Prevent divide-by-zero fault */
1029 n_nonzero = 0; /* Number of symbols with nonzero time or # calls. */
1030 for (n = n_syms, slp = slist; --n >= 0; slp++) {
1031 long count; /* # Calls. */
1032 /* t0, time in seconds. */
1034 /* if a snym symbol, use summarized stats, else use indiv. */
1035 if ((snymp = getSnymEntry(slp->sl_addr)) != 0) {
1036 count = snymp->tot_sl_count;
1037 t0 = snymp->tot_sl_time;
1039 } else {
1040 count = slp->sl_count;
1041 t0 = slp->sl_time;
1044 /* if a snym and already reported, skip this entry */
1045 if (snymp && snymp->snymReported)
1046 continue;
1047 /* Don't do entries with no action. */
1048 if (t0 == 0.0 && count == 0 && !(flags & F_ZSYMS))
1049 continue;
1050 if ((strcmp(slp->sl_name, "_mcount") == 0) ||
1051 (strcmp(slp->sl_name, "mcount") == 0)) {
1052 count = callTotal;
1055 /* count number of entries (i.e. symbols) printed */
1056 if (snymp)
1057 n_nonzero += snymp->howMany; /* add for each snym */
1058 else
1059 n_nonzero++;
1061 if (flags & F_PADDR) { /* Printing address of symbol? */
1062 /* LINTED: variable format */
1063 Print(aformat, slp->sl_addr);
1065 t += t0; /* move here; compiler bug !! */
1066 Print("%6.1f%8.2f%8.2f", t0 * t_tot, t0, t);
1067 fdigits = 0;
1068 if (count) { /* Any calls recorded? */
1069 /* Get reasonable number of fractional digits to print. */
1070 fdigits = fprecision(count);
1071 Print("%8ld%#*.*f", count, fdigits+8, fdigits,
1072 1000.0*t0/count);
1073 Print("%*s", 6-fdigits, " ");
1074 } else {
1075 Print("%22s", " ");
1078 * now print the name (or comma-seperate list of names,
1079 * for synonym symbols).
1081 if (snymp) {
1082 printSnymNames(slp, snymp); /* print it, then */
1083 snymp->snymReported = 1; /* mark it Done */
1085 else
1086 (void) puts(slp->sl_name); /* print the one name */
1088 if (flags & F_VERBOSE) { /* Extra info? */
1089 Fprint(stderr, "%5d/%d call counts used\n", n_cc, head.nfns);
1090 Fprint(stderr, "%5d/%d symbols qualified", n_syms, symttl);
1091 if (n_nonzero < n_syms)
1092 Fprint(stderr,
1093 ", %d had zero time and zero call-counts\n",
1094 n_syms - n_nonzero);
1095 else
1096 (void) putc('\n', stderr);
1097 Fprint(stderr, "%#lx scale factor\n", (long)sf);
1100 _symintClose(ldptr);
1101 } else {
1102 Fprint(stderr, "prof: no call counts captured\n");
1104 return (0);
1106 /* Return size of file associated with file descriptor fd. */
1108 static off_t
1109 fsize(int fd)
1111 struct stat sbuf;
1113 if (fstat(fd, &sbuf) < 0) /* Status of open file. */
1114 Perror("stat");
1115 return (sbuf.st_size); /* This is a long. */
1118 /* Read symbol entry. Return TRUE if satisfies conditions. */
1120 static int
1121 readnl(int symindex)
1123 nl = ldptr->pf_symarr_p[symindex];
1125 /* BEGIN CSTYLED */
1126 OLD_DEBUG(
1127 if (debug_value & 020) {
1128 Fprint(stderr,
1129 "`%-8.8s'\tst_info=%#4o, value=%#8.6o\n",
1130 ldptr->pf_symstr_p[nl.ps_sym.st_name],
1131 (unsigned char) nl.ps_sym.st_info,
1132 nl.ps_sym.st_value);
1135 /* END CSTYLED */
1138 * TXTSYM accepts global (and local, if "-g" given) T-type symbols.
1139 * Only those in the profiling range are useful.
1141 return (nl.ps_sym.st_shndx < SHN_LORESERVE &&
1142 TXTSYM(nl.ps_sym.st_shndx, nl.ps_sym.st_info) &&
1143 (pc_l <= (char *)nl.ps_sym.st_value) &&
1144 ((char *)nl.ps_sym.st_value < pc_h));
1147 * Error-checking memory allocators -
1148 * Guarantees good return (else none at all).
1151 static void *
1152 _prof_Malloc(int item_count, int item_size)
1154 void *p;
1156 if ((p = malloc((unsigned)item_count * (unsigned)item_size)) == NULL) {
1157 (void) fprintf(stderr, "%s: Out of space\n", cmdname);
1158 exit(1);
1160 return (p);
1166 * Given the quotient Q = N/D, where entier(N) == N and D > 0, an
1167 * approximation of the "best" number of fractional digits to use
1168 * in printing Q is f = entier(log10(D)), which is crudely produced
1169 * by the following routine.
1172 static int
1173 fprecision(long count)
1175 return (count < 10 ? 0 : count < 100 ? 1 : count < 1000 ? 2 :
1176 count < 10000 ? 3 : 4);
1180 * Return pointer to base name(name less path) of string s.
1181 * Handles case of superfluous trailing '/'s, and unlikely
1182 * case of s == "/".
1185 static char *
1186 basename(char *s)
1188 char *p;
1190 p = &s[strlen(s)]; /* End (+1) of string. */
1191 while (p > s && *--p == '/') /* Trim trailing '/'s. */
1192 *p = '\0';
1193 p++; /* New end (+1) of string. */
1194 while (p > s && *--p != '/') /* Break backward on '/'. */
1196 if (*p == '/') /* If found '/', point to 1st following. */
1197 p++;
1198 if (*p == '\0')
1199 p = "/"; /* If NULL, must be "/". (?) */
1200 return (p);
1202 /* Here if unexpected read problem. */
1204 static void
1205 eofon(FILE *iop, char *fn)
1207 if (ferror(iop)) /* Real error? */
1208 Perror(fn); /* Yes. */
1209 Fprint(stderr, "%s: %s: Premature EOF\n", cmdname, fn);
1210 exit(1);
1214 * Version of perror() that prints cmdname first.
1215 * Print system error message & exit.
1218 static void
1219 Perror(char *s)
1221 int err = errno; /* Save current errno in case */
1223 Fprint(stderr, "%s: ", cmdname);
1224 errno = err; /* Put real error back. */
1225 perror(s); /* Print message. */
1226 _symintClose(ldptr); /* cleanup symbol information */
1227 exit(1); /* Exit w/nonzero status. */
1230 /* Here for things that "Should Never Happen". */
1232 static void
1233 snh(void)
1235 Fprint(stderr, "%s: Internal error\n", cmdname);
1236 (void) abort();
1240 * Various comparison routines for qsort. Uses:
1242 * c_ccaddr - Compare fnpc fields of cnt structs to put
1243 * call counters in increasing address order.
1244 * c_sladdr - Sort slist structures on increasing address.
1245 * c_time - " " " " decreasing time.
1246 * c_ncalls - " " " " decreasing # calls.
1247 * c_name - " " " " increasing symbol name
1250 #define CMP2(v1, v2) ((v1) < (v2) ? -1 : (v1) == (v2) ? 0 : 1)
1251 #define CMP1(v) CMP2(v, 0)
1254 c_ccaddr(const void *arg1, const void *arg2)
1256 struct cnt *p1 = (struct cnt *)arg1;
1257 struct cnt *p2 = (struct cnt *)arg2;
1259 return (CMP2(p1->fnpc, p2->fnpc));
1263 c_sladdr(const void *arg1, const void *arg2)
1265 struct slist *p1 = (struct slist *)arg1;
1266 struct slist *p2 = (struct slist *)arg2;
1268 return (CMP2(p1->sl_addr, p2->sl_addr));
1272 c_time(const void *arg1, const void *arg2)
1274 struct slist *p1 = (struct slist *)arg1;
1275 struct slist *p2 = (struct slist *)arg2;
1276 float dtime = p2->sl_time - p1->sl_time; /* Decreasing time. */
1278 return (CMP1(dtime));
1282 c_ncalls(const void *arg1, const void *arg2)
1284 struct slist *p1 = (struct slist *)arg1;
1285 struct slist *p2 = (struct slist *)arg2;
1286 int diff = p2->sl_count - p1->sl_count;
1287 /* Decreasing # calls. */
1288 return (CMP1(diff));
1292 c_name(const void *arg1, const void *arg2)
1294 struct slist *p1 = (struct slist *)arg1;
1295 struct slist *p2 = (struct slist *)arg2;
1296 int diff;
1298 /* flex names has variable length strings for names */
1299 diff = strcmp(p1->sl_name, p2->sl_name);
1300 return (CMP1(diff));
1303 #define STRSPACE 2400 /* guess at amount of string space */
1305 char *format_buf;
1306 #define FORMAT_BUF "%s\n\t\t\t\t\t [%s]"
1308 static char *
1309 demangled_name(char *s)
1311 const char *name;
1312 size_t len;
1314 name = conv_demangle_name(s);
1316 if (strcmp(name, s) == 0)
1317 return (s);
1319 if (format_buf != NULL)
1320 free(format_buf);
1322 len = strlen(name) + strlen(FORMAT_BUF) + strlen(s) + 1;
1323 format_buf = malloc(len);
1324 if (format_buf == NULL)
1325 return (s);
1326 (void) snprintf(format_buf, len, FORMAT_BUF, name, s);
1327 return (format_buf);
1330 /* getname - get the name of a symbol in a permanent fashion */
1331 static char *
1332 getname(PROF_FILE *ldpter, PROF_SYMBOL symbol)
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 */
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>>");
1346 if (Cflag)
1347 name = demangled_name(name);
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;
1357 (void) strcpy(&(strtable[sp_used]), name);
1358 name = &(strtable[sp_used]);
1359 sp_used += lth;
1360 return (name);
1363 static void
1364 usage(void)
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);