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]
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.
35 * prof [-ChsVz] [-a | c | n | t] [-o | x] [-g | l]
38 * Where "prog" is the program that was profiled; "a.out" by default.
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.
69 #include "sys/param.h" /* for HZ */
76 #define Print (void) printf
77 #define Fprint (void) fprintf
80 /* Max positive difference between a fnpc and sl_addr for match */
82 /* Type if n_type field in file symbol table entry. */
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) */
92 #define CCADIFF 24 /* PIC prologue length=20 + 4 */
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 ###"
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.
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 */
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. */
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
);
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. */
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. */
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 */
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' */
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
253 #define BY_ADDRESS 0x1
254 #define BY_NCALLS 0x2
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
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
);
279 /* for the others: find each, print each. */
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.
296 getSnymEntry(char *sl_addr
)
301 for (p
= snymList
, i
= n_snyms
; --i
>= 0; p
++)
302 if (sl_addr
== p
->sym_addr
)
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 */
344 int fdigits
= 0; /* # of digits of precision for print msecs/call */
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 */
354 float t_tot
; /* Total time: PROFSEC(sum of all pcounts[i]) */
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
) {
363 int (*fcn
)(); /* For function to sort results. */
365 case 'm': /* Specify data file: -m file */
370 case 'T': /* Set trace flags: -T(octnum) */
371 debug_value
= (int)strtol(optarg
, 0, 8);
375 case 'n': /* Sort by symbol name. */
377 sort_flag
|= BY_NAME
;
380 case 't': /* Sort by decreasing time. */
382 sort_flag
|= BY_TIME
;
385 case 'c': /* Sort by decreasing # calls. */
387 sort_flag
|= BY_NCALLS
;
390 case 'a': /* Sort by increasing symbol address */
391 /* (don't have to -- it will be) */
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 */
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. */
409 case 'g': /* Include local T symbols as well as global */
413 case 'l': /* Do NOT include local T symbols */
417 case 'z': /* Print all symbols in profiling range, */
418 /* even if no time or # calls. */
419 flags
|= F_ZSYMS
; /* Set flag. */
422 case 'h': /* Suppress table header. */
426 case 's': /* Follow normal output with extra summary. */
427 flags
|= F_VERBOSE
; /* Set flag (...) */
431 (void) fprintf(stderr
, "prof: %s %s\n",
432 (const char *)SGU_PKG
, (const char *)SGU_REL
);
436 case 'C': /* demangle C++ names before printing. */
440 case '?': /* But no good. */
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
)
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.
465 /* Open monitor data file (has counts). */
466 if ((mon_iop
= fopen(mon_fn
, "r")) == NULL
)
469 DEBUG_LOC("main: before _symintOpen");
470 if ((ldptr
= _symintOpen(sym_fn
)) == NULL
) {
471 Perror("_symintOpen failed");
473 DEBUG_LOC("main: after _symintOpen");
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
);
489 /* Compute the file address of symbol table. Machine-dependent. */
491 DEBUG_EXP(printf("number of symbols (pf_nsyms) = %d\n",
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. */
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. */
524 break; /* Stop when find nonzero count. */
525 } while (--n_cc
> 0); /* Or all are zero. */
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)
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:
555 * i(pc) = ----------------
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
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. */
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
));
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.
597 while ((temp
>>= 1) > 0x7fff) {
606 Fprint(stderr
, "sf = %d, s_inv = %d bias = %d\n",
607 (long)sf
, pc_m
/ n_pc
, bias
);
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 */
622 Fprint(stderr
, "%u symbols, %u qualify\n", symttl
, n_syms
);
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;
640 _prof_Malloc(snymCapacity
, sizeof (struct snymEntry
));
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. */
660 if (debug_value
& 02)
661 Fprint(stderr
, "%-8.8s: %#8o\n", slp
->sl_name
, slp
->sl_addr
)
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
;
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
;
713 } while (n
> 0 && slp
->sl_addr
< ccp
->fnpc
);
717 if (debug_value
& 04) {
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
,
727 closest_symp
->sl_count
= ccp
->mcnt
; /* Copy count. */
730 } else if (ccp
->fnpc
< slp
->sl_addr
) {
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. */
780 /* Start addr of next region, low addr of overlap. */
782 /* First index into pcounts for this region and next region. */
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
);
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
));
815 t
= 0; /* Init time for this symbol. */
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
);
826 OLD_DEBUG(if (debug_value
& 010)
827 Fprint(stderr
, "%ld/(%.1f)", (pc1
- pc0
) * ticks
, DBL_ADDRPERCELL
)
831 /* Overlap with previous region? */
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. */
841 ticks
*(1-((double)(pc0
-pc00
) *sf
)/bias
));
843 /* Do not count this time when summing times */
844 /* wholly within the region. */
847 OLD_DEBUG(if (debug_value
& 010)
848 Fprint(stderr
, "%ld/(%.1f) + ", (pc0
- pc00
) * ticks
,
853 /* Init sum of counts for PCs not shared w/other */
857 /* Stop at first count that overlaps following */
859 for (i
= i0
; i
< i1
; 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? */
866 /* Yes. Get total count ... */
869 /* and accumulate proportion for addresses in */
870 /* range of this routine */
871 t
+= PROFSEC(((double)ticks
*
872 (pc1
- pc10
)*sf
)/bias
);
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
)
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
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.
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! */
931 int totsnyms
= 0; int totseries
= 0; struct slist
*lastslp
= slist
;
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
);
947 "Synonym series:\n1st->\t%s at address %x, ct=%ld, time=%f\n",
948 lastslp
->sl_name
, lastaddr
, lastslp
->sl_count
,
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;
969 "\t%s at address %x, ct=%ld, time=%f\n",
978 /* ok - bump count for snym, and note its Finding */
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
;
986 lastWasSnym
= thisIsSnym
;
989 if (debug_value
) lastslp
= slp
;
997 Fprint(stderr
, "Total #series %d, #synonyms %d\n", totseries
, totsnyms
);
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
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
;
1040 count
= slp
->sl_count
;
1044 /* if a snym and already reported, skip this entry */
1045 if (snymp
&& snymp
->snymReported
)
1047 /* Don't do entries with no action. */
1048 if (t0
== 0.0 && count
== 0 && !(flags
& F_ZSYMS
))
1050 if ((strcmp(slp
->sl_name
, "_mcount") == 0) ||
1051 (strcmp(slp
->sl_name
, "mcount") == 0)) {
1055 /* count number of entries (i.e. symbols) printed */
1057 n_nonzero
+= snymp
->howMany
; /* add for each snym */
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
);
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
,
1073 Print("%*s", 6-fdigits
, " ");
1078 * now print the name (or comma-seperate list of names,
1079 * for synonym symbols).
1082 printSnymNames(slp
, snymp
); /* print it, then */
1083 snymp
->snymReported
= 1; /* mark it Done */
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
)
1093 ", %d had zero time and zero call-counts\n",
1094 n_syms
- n_nonzero
);
1096 (void) putc('\n', stderr
);
1097 Fprint(stderr
, "%#lx scale factor\n", (long)sf
);
1100 _symintClose(ldptr
);
1102 Fprint(stderr
, "prof: no call counts captured\n");
1106 /* Return size of file associated with file descriptor fd. */
1113 if (fstat(fd
, &sbuf
) < 0) /* Status of open file. */
1115 return (sbuf
.st_size
); /* This is a long. */
1118 /* Read symbol entry. Return TRUE if satisfies conditions. */
1121 readnl(int symindex
)
1123 nl
= ldptr
->pf_symarr_p
[symindex
];
1127 if (debug_value
& 020) {
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
);
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).
1152 _prof_Malloc(int item_count
, int item_size
)
1156 if ((p
= malloc((unsigned)item_count
* (unsigned)item_size
)) == NULL
) {
1157 (void) fprintf(stderr
, "%s: Out of space\n", cmdname
);
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.
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
1190 p
= &s
[strlen(s
)]; /* End (+1) of string. */
1191 while (p
> s
&& *--p
== '/') /* Trim trailing '/'s. */
1193 p
++; /* New end (+1) of string. */
1194 while (p
> s
&& *--p
!= '/') /* Break backward on '/'. */
1196 if (*p
== '/') /* If found '/', point to 1st following. */
1199 p
= "/"; /* If NULL, must be "/". (?) */
1202 /* Here if unexpected read problem. */
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
);
1214 * Version of perror() that prints cmdname first.
1215 * Print system error message & exit.
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". */
1235 Fprint(stderr
, "%s: Internal error\n", cmdname
);
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
;
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 */
1306 #define FORMAT_BUF "%s\n\t\t\t\t\t [%s]"
1309 demangled_name(char *s
)
1314 name
= conv_demangle_name(s
);
1316 if (strcmp(name
, s
) == 0)
1319 if (format_buf
!= NULL
)
1322 len
= strlen(name
) + strlen(FORMAT_BUF
) + strlen(s
) + 1;
1323 format_buf
= malloc(len
);
1324 if (format_buf
== NULL
)
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 */
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
);
1344 return ("<<bad symbol name>>");
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
);
1357 (void) strcpy(&(strtable
[sp_used
]), name
);
1358 name
= &(strtable
[sp_used
]);
1366 (void) fprintf(stderr
,
1367 "usage: %s [-ChsVz] [-a | c | n | t] [-o | x] [-g | l]\n"
1368 "\t[-m mdata] [prog]\n",