10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*3621Sab196087 * Common Development and Distribution License (the "License"). 6*3621Sab196087 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 21211Smike_s 22211Smike_s /* 23*3621Sab196087 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24211Smike_s * Use is subject to license terms. 25211Smike_s */ 26211Smike_s 270Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 280Sstevel@tonic-gate /* All Rights Reserved */ 290Sstevel@tonic-gate 300Sstevel@tonic-gate 310Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 320Sstevel@tonic-gate 330Sstevel@tonic-gate /* 340Sstevel@tonic-gate * Program profiling report generator. 350Sstevel@tonic-gate * 360Sstevel@tonic-gate * Usage: 370Sstevel@tonic-gate * 38211Smike_s * prof [-ChsVz] [-a | c | n | t] [-o | x] [-g | l] 39211Smike_s * [-m mdata] [prog] 400Sstevel@tonic-gate * 410Sstevel@tonic-gate * Where "prog" is the program that was profiled; "a.out" by default. 420Sstevel@tonic-gate * Options are: 430Sstevel@tonic-gate * 440Sstevel@tonic-gate * -n Sort by symbol name. 450Sstevel@tonic-gate * -t Sort by decreasing time. 460Sstevel@tonic-gate * -c Sort by decreasing number of calls. 470Sstevel@tonic-gate * -a Sort by increasing symbol address. 480Sstevel@tonic-gate * 490Sstevel@tonic-gate * The options that determine the type of sorting are mutually exclusive. 500Sstevel@tonic-gate * Additional options are: 510Sstevel@tonic-gate * 520Sstevel@tonic-gate * -o Include symbol addresses in output (in octal). 530Sstevel@tonic-gate * -x Include symbol addresses in output (in hexadecimal). 540Sstevel@tonic-gate * -g Include non-global T-type symbols in output. 55211Smike_s * -l Do NOT include local T-type symbols in output (default). 560Sstevel@tonic-gate * -z Include all symbols in profiling range, even if zero 570Sstevel@tonic-gate * number of calls or time. 580Sstevel@tonic-gate * -h Suppress table header. 590Sstevel@tonic-gate * -s Follow report with additional statistical information. 600Sstevel@tonic-gate * -m mdata Use file "mdata" instead of MON_OUT for profiling data. 610Sstevel@tonic-gate * -V print version information for prof (and exit, if only V spec'd) 620Sstevel@tonic-gate * -C call C++ demangle routine to demangle names before printing. 630Sstevel@tonic-gate */ 640Sstevel@tonic-gate 650Sstevel@tonic-gate #include <stdio.h> 660Sstevel@tonic-gate #include <string.h> 670Sstevel@tonic-gate #include <errno.h> 680Sstevel@tonic-gate #include <dlfcn.h> 69211Smike_s #include <ctype.h> 700Sstevel@tonic-gate #include "sgs.h" 710Sstevel@tonic-gate #include "symint.h" 720Sstevel@tonic-gate #include "sys/param.h" /* for HZ */ 730Sstevel@tonic-gate #include "mon.h" 740Sstevel@tonic-gate #include "sys/stat.h" 750Sstevel@tonic-gate #include "debug.h" 760Sstevel@tonic-gate 770Sstevel@tonic-gate #define OLD_DEBUG(x) 780Sstevel@tonic-gate 790Sstevel@tonic-gate #define Print (void) printf 800Sstevel@tonic-gate #define Fprint (void) fprintf 810Sstevel@tonic-gate 820Sstevel@tonic-gate #if vax 830Sstevel@tonic-gate /* Max positive difference between a fnpc and sl_addr for match */ 840Sstevel@tonic-gate #define CCADIFF 22 850Sstevel@tonic-gate /* Type if n_type field in file symbol table entry. */ 860Sstevel@tonic-gate #endif 870Sstevel@tonic-gate 880Sstevel@tonic-gate #if (u3b || u3b15 || u3b2 || i386) 890Sstevel@tonic-gate /* Max positive difference between a fnpc and sl_addr for match */ 900Sstevel@tonic-gate #define CCADIFF 20 /* ?? (16 would probably do) */ 910Sstevel@tonic-gate /* For u3b, the "type" is storage class + section number (no type_t) */ 920Sstevel@tonic-gate #endif 930Sstevel@tonic-gate 940Sstevel@tonic-gate #if (sparc) 950Sstevel@tonic-gate #define CCADIFF 24 /* PIC prologue length=20 + 4 */ 960Sstevel@tonic-gate #endif 970Sstevel@tonic-gate 980Sstevel@tonic-gate 990Sstevel@tonic-gate #define PROFSEC(ticks) ((double)(ticks)/HZ) /* Convert clock ticks to seconds */ 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate /* Title fragment used if symbol addresses in output ("-o" or "-x"). */ 1020Sstevel@tonic-gate char *atitle = " Address "; 1030Sstevel@tonic-gate /* Format for addresses in output */ 1040Sstevel@tonic-gate char *aformat = "%8o "; 1050Sstevel@tonic-gate 1060Sstevel@tonic-gate #if !(vax || u3b || u3b15 || u3b2 || i386 || sparc) 1070Sstevel@tonic-gate /* Make sure something we are set up for. Else lay egg. */ 1080Sstevel@tonic-gate #include "### No code for processor type ###" 1090Sstevel@tonic-gate #endif 1100Sstevel@tonic-gate 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate /* Shorthand to gimme the Precise #of addresses per cells */ 1130Sstevel@tonic-gate #define DBL_ADDRPERCELL (((double)bias)/sf) 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate 1160Sstevel@tonic-gate /* Used for unsigned fixed-point fraction with binary scale at */ 1170Sstevel@tonic-gate /* the left of 15'th bit (0 as least significant bit) . */ 1180Sstevel@tonic-gate #define BIAS ((long)0200000L) 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate /* 1210Sstevel@tonic-gate * TS1 insures that the symbols section is executable. 1220Sstevel@tonic-gate */ 1230Sstevel@tonic-gate #define TS1(s) (((s) > 0) && (scnhdrp[(s)-1].sh_flags & SHF_EXECINSTR)) 1240Sstevel@tonic-gate /* 1250Sstevel@tonic-gate * TS2 insures that the symbol should be reported. We want 1260Sstevel@tonic-gate * to report only those symbols that are functions (STT_FUNC) 1270Sstevel@tonic-gate * or "notype" (STT_NOTYPE... "printf", for example). Also, 1280Sstevel@tonic-gate * unless the gflag is set, the symbol must be global. 1290Sstevel@tonic-gate */ 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate #define TS2(i) \ 1320Sstevel@tonic-gate (((ELF32_ST_TYPE(i) == STT_FUNC) || \ 1330Sstevel@tonic-gate (ELF32_ST_TYPE(i) == STT_NOTYPE)) && \ 1340Sstevel@tonic-gate ((ELF32_ST_BIND(i) == STB_GLOBAL) || \ 1350Sstevel@tonic-gate (gflag && (ELF32_ST_BIND(i) == STB_LOCAL)))) 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate #define TXTSYM(s, i) (TS1(s) && TS2(i)) 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate int gflag = 0; /* replaces gmatch and gmask */ 1400Sstevel@tonic-gate int Cflag = 0; 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate PROF_FILE *ldptr; /* For program ("a.out") file. */ 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate FILE *mon_iop; /* For profile (MON_OUT) file. */ 1450Sstevel@tonic-gate char *sym_fn = "a.out"; /* Default program file name. */ 1460Sstevel@tonic-gate char *mon_fn = MON_OUT; /* Default profile file name. */ 1470Sstevel@tonic-gate /* May be changed by "-m file". */ 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate long bias; /* adjusted bias */ 1500Sstevel@tonic-gate long temp; /* for bias adjust */ 1510Sstevel@tonic-gate 152211Smike_s extern void profver(void); 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate /* For symbol table entries read from program file. */ 1550Sstevel@tonic-gate PROF_SYMBOL nl; 1560Sstevel@tonic-gate 1570Sstevel@tonic-gate /* Compare routines called from qsort() */ 1580Sstevel@tonic-gate 159211Smike_s int c_ccaddr(const void *arg1, const void *arg2); 160211Smike_s int c_sladdr(const void *arg1, const void *arg2); 161211Smike_s int c_time(const void *arg1, const void *arg2); 162211Smike_s int c_ncalls(const void *arg1, const void *arg2); 163211Smike_s int c_name(const void *arg1, const void *arg2); 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate /* Other stuff. */ 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate /* Return size of open file (arg is file descriptor) */ 168211Smike_s static off_t fsize(int fd); 169211Smike_s 170211Smike_s static void snh(void); 171211Smike_s static void Perror(char *s); 172211Smike_s static void eofon(FILE *iop, char *fn); 173211Smike_s static void usage(void); 174211Smike_s static char *getname(PROF_FILE *ldpter, PROF_SYMBOL symbol); 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate /* Memory allocation. Like malloc(), but no return if error. */ 177211Smike_s static void *_prof_Malloc(int item_count, int item_size); 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate /* Scan past path part (if any) in the ... */ 180211Smike_s static char *basename(char *s); 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate /* command name, for error messages. */ 1830Sstevel@tonic-gate char *cmdname; 1840Sstevel@tonic-gate /* Structure of subroutine call counters (cnt) is defined in mon.h. */ 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate /* Structure for header of mon.out (hdr) is defined in mon.h. */ 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate /* Local representation of symbols and call/time information. */ 1890Sstevel@tonic-gate struct slist { 1900Sstevel@tonic-gate char *sl_name; /* Symbol name. */ 1910Sstevel@tonic-gate char *sl_addr; /* Address. */ 1920Sstevel@tonic-gate long sl_size; /* size of symbol */ 1930Sstevel@tonic-gate long sl_count; /* Count of subroutine calls */ 1940Sstevel@tonic-gate float sl_time; /* Count of clock ticks in this routine, */ 1950Sstevel@tonic-gate /* converted to secs. */ 1960Sstevel@tonic-gate }; 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate /* local structure for tracking synonyms in our symbol list */ 199211Smike_s struct snymEntry { 2000Sstevel@tonic-gate char *sym_addr; /* address which has a synonym */ 2010Sstevel@tonic-gate int howMany; /* # of synonyms for this symbol */ 2020Sstevel@tonic-gate int snymReported; /* 'was printed in a report line already' */ 2030Sstevel@tonic-gate /* flag, */ 2040Sstevel@tonic-gate /* > 0 report line printed for these syns. */ 2050Sstevel@tonic-gate /* == 0 not printed yet. */ 2060Sstevel@tonic-gate long tot_sl_count; /* total subr calls for these snyms */ 2070Sstevel@tonic-gate float tot_sl_time; /* total clock ticks (a la sl_time) */ 2080Sstevel@tonic-gate }; 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate #define AOUTHSZ (filhdr.f_opthdr) 2120Sstevel@tonic-gate PROF_FILE filhdr; /* profile file descriptor */ 2130Sstevel@tonic-gate Elf32_Shdr *scnhdrp; /* pointer to first section header */ 2140Sstevel@tonic-gate /* (space by _prof_Malloc) */ 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate struct hdr head; /* Profile file (MON_OUT) header. */ 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate int (*sort)() = NULL; /* Compare routine for sorting output */ 2190Sstevel@tonic-gate /* symbols. Set by "-[acnt]". */ 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate int flags; /* Various flag bits. */ 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate char *pc_l; /* From head.lpc. */ 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate char *pc_h; /* " head.hpc. */ 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate short VwasSpecified = 0; /* 1 if -V was specified */ 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate /* 2300Sstevel@tonic-gate * Bit macro and flag bit definitions. These need to be identical to the 2310Sstevel@tonic-gate * set in profv.h. Any change here should be reflected in profv.c also. 2320Sstevel@tonic-gate */ 2330Sstevel@tonic-gate #define FBIT(pos) (01 << (pos)) /* Returns value with bit pos set. */ 2340Sstevel@tonic-gate #define F_SORT FBIT(0) /* Set if "-[acnt]" seen. */ 2350Sstevel@tonic-gate #define F_VERBOSE FBIT(1) /* Set if "-s" seen. */ 2360Sstevel@tonic-gate #define F_ZSYMS FBIT(2) /* Set if "-z" seen. */ 2370Sstevel@tonic-gate #define F_PADDR FBIT(3) /* Set if "-o" or "-x" seen. */ 2380Sstevel@tonic-gate #define F_NHEAD FBIT(4) /* Set if "-h" seen. */ 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate struct snymEntry *snymList; /* Pointer to allocated list of */ 2420Sstevel@tonic-gate /* synonym entries. */ 2430Sstevel@tonic-gate struct snymEntry *snymp; 2440Sstevel@tonic-gate /* for scanning entries. */ 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate int snymCapacity; /* #slots in snymList */ 2470Sstevel@tonic-gate int n_snyms; /* #used slots in snymList */ 2480Sstevel@tonic-gate 249211Smike_s static int readnl(int symindex); 250211Smike_s static int fprecision(long count); 251211Smike_s 2520Sstevel@tonic-gate /* 2530Sstevel@tonic-gate * Sort flags. Mutually exclusive. These need to be identical to the ones 2540Sstevel@tonic-gate * defined in profv.h 2550Sstevel@tonic-gate */ 2560Sstevel@tonic-gate #define BY_ADDRESS 0x1 2570Sstevel@tonic-gate #define BY_NCALLS 0x2 2580Sstevel@tonic-gate #define BY_NAME 0x4 2590Sstevel@tonic-gate #define BY_TIME 0x8 2600Sstevel@tonic-gate 2610Sstevel@tonic-gate extern unsigned char sort_flag; /* what type of sort ? */ 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate /* 2640Sstevel@tonic-gate * printSnymNames - print a comma-seperated list of snym names. 2650Sstevel@tonic-gate * This routine hunts down all the synonyms for the given 2660Sstevel@tonic-gate * symbol, and prints them as a comma-seperated list. 2670Sstevel@tonic-gate * NB we assume that all the synonyms _Follow_ this one, 2680Sstevel@tonic-gate * since they are only printed when the First one 2690Sstevel@tonic-gate * is seen. 2700Sstevel@tonic-gate */ 2710Sstevel@tonic-gate void 272211Smike_s printSnymNames(struct slist *slp, struct snymEntry *snymp) 2730Sstevel@tonic-gate { 2740Sstevel@tonic-gate /* how many snyms for this addr, total, and their shared address */ 2750Sstevel@tonic-gate int i = snymp->howMany; 2760Sstevel@tonic-gate char *sharedaddr = snymp->sym_addr; 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate /* put out first name - it counts as one, so decr count */ 2790Sstevel@tonic-gate (void) fputs(slp->sl_name, stdout); 2800Sstevel@tonic-gate i--; 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate /* for the others: find each, print each. */ 2830Sstevel@tonic-gate while (--i >= 0) { 2840Sstevel@tonic-gate while ((++slp)->sl_addr != sharedaddr); 2850Sstevel@tonic-gate Print(", %s", slp->sl_name); 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate /* finally.. the trailing newline */ 288211Smike_s (void) putchar('\n'); 2890Sstevel@tonic-gate } 2900Sstevel@tonic-gate 2910Sstevel@tonic-gate 2920Sstevel@tonic-gate /* 2930Sstevel@tonic-gate * getSnymEntry - see if addr was noted as a aliased address 2940Sstevel@tonic-gate * (i.e. a synonym symbol) and return the address of the 2950Sstevel@tonic-gate * snym entry if it was. 2960Sstevel@tonic-gate */ 297211Smike_s struct snymEntry * 298211Smike_s getSnymEntry(char *sl_addr) 2990Sstevel@tonic-gate { 3000Sstevel@tonic-gate struct snymEntry *p; 3010Sstevel@tonic-gate int i; 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate for (p = snymList, i = n_snyms; --i >= 0; p++) 3040Sstevel@tonic-gate if (sl_addr == p->sym_addr) 3050Sstevel@tonic-gate return (p); 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate return ((struct snymEntry *)0); 3080Sstevel@tonic-gate } 3090Sstevel@tonic-gate 3100Sstevel@tonic-gate 311211Smike_s int 312211Smike_s main(int argc, char **argv) 3130Sstevel@tonic-gate { 3140Sstevel@tonic-gate char buffer[BUFSIZ]; /* buffer for printf */ 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate WORD *pcounts; /* Pointer to allocated area for */ 3170Sstevel@tonic-gate /* pcounts: PC clock hit counts */ 3180Sstevel@tonic-gate 319211Smike_s WORD *pcp; /* For scanning pcounts. */ 3200Sstevel@tonic-gate 3210Sstevel@tonic-gate struct cnt *ccounts; /* Pointer to allocated area for cnt */ 3220Sstevel@tonic-gate /* structures: subr PC-call counts. */ 3230Sstevel@tonic-gate 324211Smike_s struct cnt *ccp; /* For scanning ccounts. */ 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate struct slist *slist; /* Pointer to allocated slist structures: */ 3270Sstevel@tonic-gate /* symbol name/address/time/call counts */ 3280Sstevel@tonic-gate 329211Smike_s struct slist *slp; /* For scanning slist */ 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate int vn_cc, n_cc; /* Number of cnt structures in profile data */ 3320Sstevel@tonic-gate /* file (later # ones used). */ 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate int n_pc; /* Number of pcounts in profile data file. */ 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate int n_syms; /* Number of text symbols (of proper type) */ 3370Sstevel@tonic-gate /* that fill in range of profiling. */ 3380Sstevel@tonic-gate 3390Sstevel@tonic-gate int n_nonzero; /* Number of (above symbols) actually printed */ 3400Sstevel@tonic-gate /* because nonzero time or # calls. */ 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate int symttl; /* Total # symbols in program file sym-table */ 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate int i; 3450Sstevel@tonic-gate 3460Sstevel@tonic-gate int fdigits = 0; /* # of digits of precision for print msecs/call */ 3470Sstevel@tonic-gate 348211Smike_s int n, symct; 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate long sf; /* Scale for index into pcounts: */ 3510Sstevel@tonic-gate /* i(pc) = ((pc - pc_l) * sf)/bias. */ 3520Sstevel@tonic-gate 353211Smike_s /* LINTED: set but not used */ 3540Sstevel@tonic-gate long s_inv; /* Inverse: i_inv(i) = */ 3550Sstevel@tonic-gate /* {pc00, pc00+1, ... pc00+s_inv-1}. */ 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate unsigned pc_m; /* Range of PCs profiled: pc_m = pc_h - pc_l */ 3580Sstevel@tonic-gate 3590Sstevel@tonic-gate float t, t0; 3600Sstevel@tonic-gate float t_tot; /* Total time: PROFSEC(sum of all pcounts[i]) */ 3610Sstevel@tonic-gate int callTotal = 0; 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate DEBUG_LOC("main: top"); 3640Sstevel@tonic-gate setbuf(stdout, buffer); 3650Sstevel@tonic-gate cmdname = basename(*argv); /* command name. */ 3660Sstevel@tonic-gate 3670Sstevel@tonic-gate while ((n = getopt(argc, argv, "canthsglzoxT:m:VC")) != EOF) { 3680Sstevel@tonic-gate switch (n) { 3690Sstevel@tonic-gate int (*fcn)(); /* For function to sort results. */ 3700Sstevel@tonic-gate 3710Sstevel@tonic-gate case 'm': /* Specify data file: -m file */ 3720Sstevel@tonic-gate mon_fn = optarg; 3730Sstevel@tonic-gate break; 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate #ifdef ddt 3760Sstevel@tonic-gate case 'T': /* Set trace flags: -T(octnum) */ 3770Sstevel@tonic-gate debug_value = (int)strtol(optarg, 0, 8); 3780Sstevel@tonic-gate break; 3790Sstevel@tonic-gate #endif 3800Sstevel@tonic-gate 3810Sstevel@tonic-gate case 'n': /* Sort by symbol name. */ 3820Sstevel@tonic-gate fcn = c_name; 3830Sstevel@tonic-gate sort_flag |= BY_NAME; 3840Sstevel@tonic-gate goto check; 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate case 't': /* Sort by decreasing time. */ 3870Sstevel@tonic-gate fcn = c_time; 3880Sstevel@tonic-gate sort_flag |= BY_TIME; 3890Sstevel@tonic-gate goto check; 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate case 'c': /* Sort by decreasing # calls. */ 3920Sstevel@tonic-gate fcn = c_ncalls; 3930Sstevel@tonic-gate sort_flag |= BY_NCALLS; 3940Sstevel@tonic-gate goto check; 3950Sstevel@tonic-gate 3960Sstevel@tonic-gate case 'a': /* Sort by increasing symbol address */ 3970Sstevel@tonic-gate /* (don't have to -- it will be) */ 3980Sstevel@tonic-gate fcn = NULL; 3990Sstevel@tonic-gate sort_flag |= BY_ADDRESS; 4000Sstevel@tonic-gate check: /* Here to check sort option conflicts. */ 4010Sstevel@tonic-gate if (sort != NULL && sort != fcn) { 4020Sstevel@tonic-gate Fprint(stderr, "%s: Warning: %c overrides" 4030Sstevel@tonic-gate " previous specification\n", cmdname, n); 4040Sstevel@tonic-gate } 4050Sstevel@tonic-gate sort = fcn; /* Store sort routine */ 4060Sstevel@tonic-gate flags |= F_SORT; /* Note have done so */ 4070Sstevel@tonic-gate break; 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate case 'o': /* Include symbol addresses in output. */ 4100Sstevel@tonic-gate case 'x': /* Include symbol addresses in output. */ 4110Sstevel@tonic-gate aformat[2] = n; /* 'o' or 'x' in format */ 4120Sstevel@tonic-gate flags |= F_PADDR; /* Set flag. */ 4130Sstevel@tonic-gate break; 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate case 'g': /* Include local T symbols as well as global */ 4160Sstevel@tonic-gate gflag = 1; 4170Sstevel@tonic-gate break; 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate case 'l': /* Do NOT include local T symbols */ 4200Sstevel@tonic-gate gflag = 0; 4210Sstevel@tonic-gate break; 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate case 'z': /* Print all symbols in profiling range, */ 4240Sstevel@tonic-gate /* even if no time or # calls. */ 4250Sstevel@tonic-gate flags |= F_ZSYMS; /* Set flag. */ 4260Sstevel@tonic-gate break; 4270Sstevel@tonic-gate 4280Sstevel@tonic-gate case 'h': /* Suppress table header. */ 4290Sstevel@tonic-gate flags |= F_NHEAD; 4300Sstevel@tonic-gate break; 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate case 's': /* Follow normal output with extra summary. */ 4330Sstevel@tonic-gate flags |= F_VERBOSE; /* Set flag (...) */ 4340Sstevel@tonic-gate break; 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate case 'V': 4370Sstevel@tonic-gate (void) fprintf(stderr, "prof: %s %s\n", 4380Sstevel@tonic-gate (const char *)SGU_PKG, (const char *)SGU_REL); 4390Sstevel@tonic-gate VwasSpecified = 1; 4400Sstevel@tonic-gate break; 4410Sstevel@tonic-gate 4420Sstevel@tonic-gate case 'C': /* demangle C++ names before printing. */ 4430Sstevel@tonic-gate Cflag = 1; 4440Sstevel@tonic-gate break; 4450Sstevel@tonic-gate 4460Sstevel@tonic-gate case '?': /* But no good. */ 447211Smike_s usage(); 4480Sstevel@tonic-gate } /* End switch (n) */ 4490Sstevel@tonic-gate } /* End while (getopt) */ 4500Sstevel@tonic-gate 4510Sstevel@tonic-gate DEBUG_LOC("main: following getopt"); 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate /* if -V the only argument, just exit. */ 4540Sstevel@tonic-gate if (VwasSpecified && argc == 2 && !flags) 4550Sstevel@tonic-gate exit(0); 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate if (optind < argc) 4580Sstevel@tonic-gate sym_fn = argv[optind]; /* name other than `a.out' */ 4590Sstevel@tonic-gate 4600Sstevel@tonic-gate if (sort == NULL && !(flags & F_SORT)) 4610Sstevel@tonic-gate /* If have not specified sort mode ... */ 4620Sstevel@tonic-gate sort = c_time; /* then sort by decreasing time. */ 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate /* 4650Sstevel@tonic-gate * profver() checks to see if the mon.out was "versioned" and if 4660Sstevel@tonic-gate * yes, processes it and exits; otherwise, we have an *old-style* 4670Sstevel@tonic-gate * mon.out and we process it the old way. 4680Sstevel@tonic-gate */ 4690Sstevel@tonic-gate profver(); 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate /* Open monitor data file (has counts). */ 4720Sstevel@tonic-gate if ((mon_iop = fopen(mon_fn, "r")) == NULL) 4730Sstevel@tonic-gate Perror(mon_fn); 4740Sstevel@tonic-gate 4750Sstevel@tonic-gate DEBUG_LOC("main: before _symintOpen"); 4760Sstevel@tonic-gate if ((ldptr = _symintOpen(sym_fn)) == NULL) { 4770Sstevel@tonic-gate Perror("_symintOpen failed"); 4780Sstevel@tonic-gate } 4790Sstevel@tonic-gate DEBUG_LOC("main: after _symintOpen"); 4800Sstevel@tonic-gate filhdr = *ldptr; 4810Sstevel@tonic-gate 4820Sstevel@tonic-gate scnhdrp = ldptr->pf_shdarr_p; 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate { 4850Sstevel@tonic-gate Elf_Kind k = elf_kind(filhdr.pf_elf_p); 4860Sstevel@tonic-gate 4870Sstevel@tonic-gate DEBUG_EXP(printf("elf_kind = %d\n", k)); 4880Sstevel@tonic-gate DEBUG_EXP(printf("elf_type = %d\n", filhdr.pf_elfhd_p->e_type)); 4890Sstevel@tonic-gate if ((k != ELF_K_ELF) || (filhdr.pf_elfhd_p->e_type != ET_EXEC)) { 4900Sstevel@tonic-gate Fprint(stderr, "%s: %s: improper format\n", cmdname, sym_fn); 4910Sstevel@tonic-gate exit(1); 4920Sstevel@tonic-gate } 4930Sstevel@tonic-gate } 4940Sstevel@tonic-gate 4950Sstevel@tonic-gate /* Compute the file address of symbol table. Machine-dependent. */ 4960Sstevel@tonic-gate 4970Sstevel@tonic-gate DEBUG_EXP(printf("number of symbols (pf_nsyms) = %d\n", 4980Sstevel@tonic-gate filhdr.pf_nsyms)); 4990Sstevel@tonic-gate 5000Sstevel@tonic-gate /* Number of symbols in file symbol table. */ 5010Sstevel@tonic-gate symttl = filhdr.pf_nsyms; 5020Sstevel@tonic-gate if (symttl == 0) { /* This is possible. */ 5030Sstevel@tonic-gate Fprint(stderr, "%s: %s: no symbols\n", cmdname, sym_fn); 5040Sstevel@tonic-gate exit(0); /* Note zero exit code. */ 5050Sstevel@tonic-gate } 5060Sstevel@tonic-gate /* Get size of file containing profiling data. Read header part. */ 5070Sstevel@tonic-gate n = fsize(fileno(mon_iop)); 5080Sstevel@tonic-gate if (fread((char *)&head, sizeof (struct hdr), 1, mon_iop) != 1) 5090Sstevel@tonic-gate eofon(mon_iop, mon_fn); /* Probably junk file. */ 5100Sstevel@tonic-gate 5110Sstevel@tonic-gate /* Get # cnt structures (they follow header), */ 5120Sstevel@tonic-gate /* and allocate space for them. */ 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate n_cc = head.nfns; 515211Smike_s ccounts = _prof_Malloc(n_cc, sizeof (struct cnt)); 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate /* Read the call addr-count pairs. */ 5180Sstevel@tonic-gate if (fread((char *)ccounts, sizeof (struct cnt), n_cc, mon_iop) != n_cc) 5190Sstevel@tonic-gate eofon(mon_iop, mon_fn); 5200Sstevel@tonic-gate 5210Sstevel@tonic-gate /* 5220Sstevel@tonic-gate * Compute # PC counters (pcounts), which occupy whatever is left 5230Sstevel@tonic-gate * of the file after the header and call counts. 5240Sstevel@tonic-gate */ 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate n_pc = (n - sizeof (head) - n_cc * sizeof (struct cnt))/sizeof (WORD); 5270Sstevel@tonic-gate ccp = &ccounts[n_cc]; /* Point to last (+1) of call counters ... */ 5280Sstevel@tonic-gate do { /* and scan backward until find highest one used. */ 5290Sstevel@tonic-gate if ((--ccp)->mcnt) 5300Sstevel@tonic-gate break; /* Stop when find nonzero count. */ 5310Sstevel@tonic-gate } while (--n_cc > 0); /* Or all are zero. */ 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate if (n_cc > 0) { 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate /* If less than all cnt entries are used, return unused space. */ 5360Sstevel@tonic-gate if (n_cc < head.nfns) { 5370Sstevel@tonic-gate if ((ccounts = (struct cnt *)realloc((char *)ccounts, 5380Sstevel@tonic-gate (unsigned)n_cc * sizeof (struct cnt))) == NULL) 5390Sstevel@tonic-gate snh(); /* Should not fail when reducing size. */ 5400Sstevel@tonic-gate } 5410Sstevel@tonic-gate 5420Sstevel@tonic-gate /* If more than 250 cnt entries used set verbose for warning */ 5430Sstevel@tonic-gate if (n_cc > (MPROGS0 * 5)/6) 5440Sstevel@tonic-gate flags |= F_VERBOSE; 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate /* Space for PC counts. */ 5470Sstevel@tonic-gate pcounts = (WORD *)_prof_Malloc(n_pc, sizeof (WORD)); 5480Sstevel@tonic-gate /* Read the PC counts from rest of MON_OUT file. */ 5490Sstevel@tonic-gate if (fread((char *)pcounts, sizeof (WORD), n_pc, mon_iop) != n_pc) 5500Sstevel@tonic-gate eofon(mon_iop, mon_fn); 5510Sstevel@tonic-gate /* 5520Sstevel@tonic-gate * 5530Sstevel@tonic-gate * Having gotten preliminaries out of the way, get down to business. 5540Sstevel@tonic-gate * The range pc_m of addresses over which profiling was done is 5550Sstevel@tonic-gate * computed from the low (pc_l) and high (pc_h) addresses, gotten 5560Sstevel@tonic-gate * from the MON_OUT header. From this and the number of clock 5570Sstevel@tonic-gate * tick counters, n_pc, is computed the so-called "scale", sf, used 5580Sstevel@tonic-gate * in the mapping of addresses to indices, as follows: 5590Sstevel@tonic-gate * 5600Sstevel@tonic-gate * (pc - pc_l) * sf 5610Sstevel@tonic-gate * i(pc) = ---------------- 5620Sstevel@tonic-gate * 0200000 5630Sstevel@tonic-gate * 5640Sstevel@tonic-gate * Also, the N-to-one value, s_inv, such that 5650Sstevel@tonic-gate * 5660Sstevel@tonic-gate * i(pc_l + K * s_inv + d) = K, for 0 <= d < s_inv 5670Sstevel@tonic-gate * 5680Sstevel@tonic-gate * Following this, the symbol table is scanned, and those symbols 5690Sstevel@tonic-gate * that qualify are counted. These are T-type symbols, excluding 5700Sstevel@tonic-gate * local (nonglobal) unless the "-g" option was given. Having thus 5710Sstevel@tonic-gate * determined the space requirements, space for symbols/times etc. 5720Sstevel@tonic-gate * is allocated, and the symbol table re-read, this time keeping 5730Sstevel@tonic-gate * qualified symbols. 5740Sstevel@tonic-gate * 5750Sstevel@tonic-gate * NB s_inv, as actually computed, is not sufficiently accurate 5760Sstevel@tonic-gate * (since it is truncated) for many calculations. Since it is 5770Sstevel@tonic-gate * logically equivalent to 1/(sf/bias), and the latter is much 5780Sstevel@tonic-gate * more accurate, therefore the latter will often appear in 5790Sstevel@tonic-gate * the code when 's_inv' is mentioned. dween 5800Sstevel@tonic-gate * 5810Sstevel@tonic-gate */ 5820Sstevel@tonic-gate 5830Sstevel@tonic-gate 5840Sstevel@tonic-gate pc_l = head.lpc; /* Low PC of range that was profiled. */ 5850Sstevel@tonic-gate pc_h = head.hpc; /* First address past range of profiling. */ 5860Sstevel@tonic-gate pc_m = pc_h - pc_l; /* Range of profiled addresses. */ 5870Sstevel@tonic-gate 5880Sstevel@tonic-gate OLD_DEBUG(if (debug_value) Fprint(stderr, 5890Sstevel@tonic-gate "low pc = %#o, high pc = %#o, range = %#o = %u\n\ 5900Sstevel@tonic-gate call counts: %u, %u used; pc counters: %u\n", 5910Sstevel@tonic-gate pc_l, pc_h, pc_m, pc_m, head.nfns, n_cc, n_pc)); 5920Sstevel@tonic-gate 5930Sstevel@tonic-gate sf = (BIAS * (double)n_pc)/pc_m; 5940Sstevel@tonic-gate /* 5950Sstevel@tonic-gate * Now adjust bias and sf so that there is no overflow 5960Sstevel@tonic-gate * when calculating indices. 5970Sstevel@tonic-gate */ 5980Sstevel@tonic-gate bias = BIAS; 5990Sstevel@tonic-gate temp = pc_m; 6000Sstevel@tonic-gate while ((temp >>= 1) > 0x7fff) { 6010Sstevel@tonic-gate sf >>= 1; 6020Sstevel@tonic-gate bias >>= 1; 6030Sstevel@tonic-gate } 6040Sstevel@tonic-gate s_inv = pc_m/n_pc; /* Range of PCs mapped into one index. */ 6050Sstevel@tonic-gate 6060Sstevel@tonic-gate OLD_DEBUG( 6070Sstevel@tonic-gate if (debug_value) { 6080Sstevel@tonic-gate Fprint( 6090Sstevel@tonic-gate stderr, 6100Sstevel@tonic-gate "sf = %d, s_inv = %d bias = %d\n", 6110Sstevel@tonic-gate (long)sf, s_inv, bias); 6120Sstevel@tonic-gate } 6130Sstevel@tonic-gate ); 6140Sstevel@tonic-gate 6150Sstevel@tonic-gate /* Prepare to read symbols from "a.out" (or whatever). */ 6160Sstevel@tonic-gate n_syms = 0; /* Init count of qualified symbols. */ 6170Sstevel@tonic-gate n = symttl; /* Total symbols. */ 6180Sstevel@tonic-gate while (--n >= 0) /* Scan symbol table. */ 6190Sstevel@tonic-gate if (readnl(n)) /* Read and examine symbol, count qualifiers */ 6200Sstevel@tonic-gate n_syms++; 6210Sstevel@tonic-gate 6220Sstevel@tonic-gate OLD_DEBUG( 6230Sstevel@tonic-gate if (debug_value) { 6240Sstevel@tonic-gate Fprint(stderr, "%u symbols, %u qualify\n", symttl, n_syms); 6250Sstevel@tonic-gate } 6260Sstevel@tonic-gate ); 6270Sstevel@tonic-gate 6280Sstevel@tonic-gate /* Allocate space for qualified symbols. */ 6290Sstevel@tonic-gate 630211Smike_s slist = slp = _prof_Malloc(n_syms, sizeof (struct slist)); 6310Sstevel@tonic-gate 6320Sstevel@tonic-gate /* 6330Sstevel@tonic-gate * Allocate space for synonym symbols 6340Sstevel@tonic-gate * (i.e. symbols that refer to the same address). 6350Sstevel@tonic-gate * NB there can be no more than n_syms/2 addresses 6360Sstevel@tonic-gate * with symbols, That Have Aliases, that refer to them! 6370Sstevel@tonic-gate */ 6380Sstevel@tonic-gate 6390Sstevel@tonic-gate snymCapacity = n_syms/2; 640211Smike_s snymList = snymp = _prof_Malloc(snymCapacity, 6410Sstevel@tonic-gate sizeof (struct snymEntry)); 6420Sstevel@tonic-gate n_snyms = 0; 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate /* OLD_DEBUG(debug_value &= ~020); */ 6450Sstevel@tonic-gate 6460Sstevel@tonic-gate /* Loop on number of qualified symbols. */ 6470Sstevel@tonic-gate for (n = n_syms, symct = 0; n > 0; symct++) { 6480Sstevel@tonic-gate if (readnl(symct)) { /* Get one. Check again. */ 6490Sstevel@tonic-gate /* Is qualified. Move name ... */ 6500Sstevel@tonic-gate slp->sl_name = getname(ldptr, nl); 6510Sstevel@tonic-gate 6520Sstevel@tonic-gate /* and address into slist structure. */ 6530Sstevel@tonic-gate slp->sl_addr = (char *)nl.ps_sym.st_value; 6540Sstevel@tonic-gate slp->sl_size = nl.ps_sym.st_size; 6550Sstevel@tonic-gate 6560Sstevel@tonic-gate /* set other slist fields to zero. */ 6570Sstevel@tonic-gate slp->sl_time = 0.0; 6580Sstevel@tonic-gate slp->sl_count = 0; 6590Sstevel@tonic-gate OLD_DEBUG( 6600Sstevel@tonic-gate if (debug_value & 02) 6610Sstevel@tonic-gate Fprint(stderr, "%-8.8s: %#8o\n", slp->sl_name, slp->sl_addr) 6620Sstevel@tonic-gate ); 6630Sstevel@tonic-gate 6640Sstevel@tonic-gate slp++; 6650Sstevel@tonic-gate --n; 6660Sstevel@tonic-gate } 6670Sstevel@tonic-gate } 6680Sstevel@tonic-gate /* 6690Sstevel@tonic-gate * 6700Sstevel@tonic-gate * Now attempt to match call counts with symbols. To do this, it 6710Sstevel@tonic-gate * helps to first sort both the symbols and the call address/count 6720Sstevel@tonic-gate * pairs by ascending address, since they are generally not, to 6730Sstevel@tonic-gate * begin with. The addresses associated with the counts are not, 6740Sstevel@tonic-gate * of course, the subroutine addresses associated with the symbols, 6750Sstevel@tonic-gate * but some address slightly past these. Therefore a given count 6760Sstevel@tonic-gate * address (in the fnpc field) is matched with the closest symbol 6770Sstevel@tonic-gate * address (sl_addr) that is: 6780Sstevel@tonic-gate * (1) less than the fnpc value but, 6790Sstevel@tonic-gate * (2) not more than the length of the function 6800Sstevel@tonic-gate * In other words, unreasonable matchups are avoided. 6810Sstevel@tonic-gate * Situations such as this could arise when static procedures are 6820Sstevel@tonic-gate * counted but the "-g" option was not given to this program, 6830Sstevel@tonic-gate * causing the symbol to fail to qualify. Without this limitation, 6840Sstevel@tonic-gate * unmatched counts could be erroneously charged. 6850Sstevel@tonic-gate * 6860Sstevel@tonic-gate */ 6870Sstevel@tonic-gate 6880Sstevel@tonic-gate 6890Sstevel@tonic-gate ccp = ccounts; /* Point to first call counter. */ 6900Sstevel@tonic-gate slp = slist; /* " " " symbol. */ 6910Sstevel@tonic-gate /* Sort call counters and ... */ 6920Sstevel@tonic-gate qsort((char *)ccp, (unsigned)n_cc, sizeof (struct cnt), c_ccaddr); 6930Sstevel@tonic-gate /* symbols by increasing address. */ 6940Sstevel@tonic-gate qsort((char *)slp, (unsigned)n_syms, sizeof (struct slist), c_sladdr); 6950Sstevel@tonic-gate vn_cc = n_cc; /* save this for verbose option */ 6960Sstevel@tonic-gate 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate /* Loop to match up call counts & symbols. */ 6990Sstevel@tonic-gate for (n = n_syms; n > 0 && vn_cc > 0; ) { 7000Sstevel@tonic-gate int sz = slp->sl_size; 7010Sstevel@tonic-gate 7020Sstevel@tonic-gate if (sz == 0) 7030Sstevel@tonic-gate sz = slp[ 1 ].sl_addr - slp->sl_addr; 7040Sstevel@tonic-gate if (slp->sl_addr < ccp->fnpc && 7050Sstevel@tonic-gate ccp->fnpc <= slp->sl_addr + sz) { 7060Sstevel@tonic-gate /* got a candidate: find Closest. */ 7070Sstevel@tonic-gate struct slist *closest_symp; 7080Sstevel@tonic-gate do { 7090Sstevel@tonic-gate closest_symp = slp; 7100Sstevel@tonic-gate slp++; 7110Sstevel@tonic-gate --n; 7120Sstevel@tonic-gate } while (n > 0 && slp->sl_addr < ccp->fnpc); 7130Sstevel@tonic-gate 7140Sstevel@tonic-gate OLD_DEBUG( 7150Sstevel@tonic-gate if (debug_value & 04) { 7160Sstevel@tonic-gate Fprint(stderr, 7170Sstevel@tonic-gate "Routine %-8.8s @ %#8x+%-2d matches count address %#8x\n", 7180Sstevel@tonic-gate closest_symp->sl_name, 7190Sstevel@tonic-gate closest_symp->sl_addr, 7200Sstevel@tonic-gate ccp->fnpc-slp->sl_addr, 7210Sstevel@tonic-gate ccp->fnpc); 7220Sstevel@tonic-gate } 7230Sstevel@tonic-gate ); 7240Sstevel@tonic-gate closest_symp->sl_count = ccp->mcnt; /* Copy count. */ 7250Sstevel@tonic-gate ++ccp; 7260Sstevel@tonic-gate --vn_cc; 7270Sstevel@tonic-gate } else if (ccp->fnpc < slp->sl_addr) { 7280Sstevel@tonic-gate ++ccp; 7290Sstevel@tonic-gate --vn_cc; 7300Sstevel@tonic-gate } else { 7310Sstevel@tonic-gate ++slp; 7320Sstevel@tonic-gate --n; 7330Sstevel@tonic-gate } 7340Sstevel@tonic-gate } 7350Sstevel@tonic-gate 7360Sstevel@tonic-gate /* 7370Sstevel@tonic-gate * 7380Sstevel@tonic-gate * The distribution of times to addresses is done on a proportional 7390Sstevel@tonic-gate * basis as follows: The t counts in pcounts[i] correspond to clock 7400Sstevel@tonic-gate * ticks for values of pc in the range pc, pc+1, ..., pc+s_inv-1 7410Sstevel@tonic-gate * (odd addresses excluded for PDP11s). Without more detailed info, 7420Sstevel@tonic-gate * it must be assumed that there is no greater probability 7430Sstevel@tonic-gate * of the clock ticking for any particular pc in this range than for 7440Sstevel@tonic-gate * any other. Thus the t counts are considered to be equally 7450Sstevel@tonic-gate * distributed over the addresses in the range, and that the time for 7460Sstevel@tonic-gate * any given address in the range is pcounts[i]/s_inv. 7470Sstevel@tonic-gate * 7480Sstevel@tonic-gate * The values of the symbols that qualify, bounded below and above 7490Sstevel@tonic-gate * by pc_l and pc_h, respectively, partition the profiling range into 7500Sstevel@tonic-gate * regions to which are assigned the total times associated with the 7510Sstevel@tonic-gate * addresses they contain in the following way: 7520Sstevel@tonic-gate * 7530Sstevel@tonic-gate * The sum of all pcounts[i] for which the corresponding addresses are 7540Sstevel@tonic-gate * wholly within the partition are charged to the partition (the 7550Sstevel@tonic-gate * subroutine whose address is the lower bound of the partition). 7560Sstevel@tonic-gate * 7570Sstevel@tonic-gate * If the range of addresses corresponding to a given t = pcounts[i] 7580Sstevel@tonic-gate * lies astraddle the boundary of a partition, e.g., for some k such 7590Sstevel@tonic-gate * that 0 < k < s_inv-1, the addresses pc, pc+1, ..., pc+k-1 are in 7600Sstevel@tonic-gate * the lower partition, and the addresses pc+k, pc+k+1, ..., pc+s_inv-1 7610Sstevel@tonic-gate * are in the next partition, then k*pcounts[i]/s_inv time is charged 7620Sstevel@tonic-gate * to the lower partition, and (s_inv-k) * pcounts[i]/s_inv time to the 7630Sstevel@tonic-gate * upper. It is conceivable, in cases of large granularity or small 7640Sstevel@tonic-gate * subroutines, for a range corresponding to a given pcounts[i] to 7650Sstevel@tonic-gate * overlap three regions, completely containing the (small) middle one. 7660Sstevel@tonic-gate * The algorithm is adjusted appropriately in this case. 7670Sstevel@tonic-gate * 7680Sstevel@tonic-gate */ 7690Sstevel@tonic-gate 7700Sstevel@tonic-gate 7710Sstevel@tonic-gate pcp = pcounts; /* Reset to base. */ 7720Sstevel@tonic-gate slp = slist; /* Ditto. */ 7730Sstevel@tonic-gate t0 = 0.0; /* Time accumulator. */ 7740Sstevel@tonic-gate for (n = 0; n < n_syms; n++) { /* Loop on symbols. */ 7750Sstevel@tonic-gate /* Start addr of region, low addr of overlap. */ 7760Sstevel@tonic-gate char *pc0, *pc00; 7770Sstevel@tonic-gate /* Start addr of next region, low addr of overlap. */ 7780Sstevel@tonic-gate char *pc1, *pc10; 7790Sstevel@tonic-gate /* First index into pcounts for this region and next region. */ 780211Smike_s int i0, i1; 7810Sstevel@tonic-gate long ticks; 7820Sstevel@tonic-gate 7830Sstevel@tonic-gate /* Address of symbol (subroutine). */ 7840Sstevel@tonic-gate pc0 = slp[n].sl_addr; 7850Sstevel@tonic-gate 7860Sstevel@tonic-gate /* Address of next symbol, if any or top */ 7870Sstevel@tonic-gate /* of profile range, if not */ 7880Sstevel@tonic-gate pc1 = (n < n_syms - 1) ? slp[n+1].sl_addr : pc_h; 7890Sstevel@tonic-gate 7900Sstevel@tonic-gate /* Lower bound of indices into pcounts for this range */ 7910Sstevel@tonic-gate 7920Sstevel@tonic-gate i0 = (((unsigned)pc0 - (unsigned)pc_l) * sf)/bias; 7930Sstevel@tonic-gate 7940Sstevel@tonic-gate /* Upper bound (least or least + 1) of indices. */ 7950Sstevel@tonic-gate i1 = (((unsigned)pc1 - (unsigned)pc_l) * sf)/bias; 7960Sstevel@tonic-gate 7970Sstevel@tonic-gate if (i1 >= n_pc) /* If past top, */ 7980Sstevel@tonic-gate i1 = n_pc - 1; /* adjust. */ 7990Sstevel@tonic-gate 8000Sstevel@tonic-gate /* Lowest addr for which count maps to pcounts[i0]; */ 8010Sstevel@tonic-gate pc00 = pc_l + (unsigned long)((bias * i0)/sf); 8020Sstevel@tonic-gate 8030Sstevel@tonic-gate /* Lowest addr for which count maps to pcounts[i1]. */ 8040Sstevel@tonic-gate pc10 = pc_l + (unsigned long)((bias * i1)/sf); 8050Sstevel@tonic-gate 8060Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) Fprint(stderr, 8070Sstevel@tonic-gate "%-8.8s\ti0 = %4d, pc00 = %#6o, pc0 = %#6o\n\ 8080Sstevel@tonic-gate \t\ti1 = %4d, pc10 = %#6o, pc1 = %#6o\n\t\t", 8090Sstevel@tonic-gate slp[n].sl_name, i0, pc00, pc0, i1, pc10, pc1)); 8100Sstevel@tonic-gate t = 0; /* Init time for this symbol. */ 8110Sstevel@tonic-gate if (i0 == i1) { 8120Sstevel@tonic-gate /* Counter overlaps two areas? (unlikely */ 8130Sstevel@tonic-gate /* unless large granularity). */ 8140Sstevel@tonic-gate ticks = pcp[i0]; /* # Times (clock ticks). */ 8150Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks)); 8160Sstevel@tonic-gate 8170Sstevel@tonic-gate /* Time less that which overlaps adjacent areas */ 8180Sstevel@tonic-gate t += PROFSEC(ticks * ((double)(pc1 - pc0) * sf)/bias); 8190Sstevel@tonic-gate 8200Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) 8210Sstevel@tonic-gate Fprint(stderr, "%ld/(%.1f)", (pc1 - pc0) * ticks, DBL_ADDRPERCELL) 8220Sstevel@tonic-gate ); 8230Sstevel@tonic-gate } else { 8240Sstevel@tonic-gate /* Overlap with previous region? */ 8250Sstevel@tonic-gate if (pc00 < pc0) { 8260Sstevel@tonic-gate ticks = pcp[i0]; 8270Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) 8280Sstevel@tonic-gate fprintf(stderr, "pc00 < pc0 ticks = %d\n", ticks)); 8290Sstevel@tonic-gate 8300Sstevel@tonic-gate /* Get time of overlapping area and */ 8310Sstevel@tonic-gate /* subtract proportion for lower region. */ 8320Sstevel@tonic-gate t += PROFSEC( 8330Sstevel@tonic-gate ticks*(1-((double)(pc0-pc00) *sf)/bias)); 8340Sstevel@tonic-gate 8350Sstevel@tonic-gate /* Do not count this time when summing times */ 8360Sstevel@tonic-gate /* wholly within the region. */ 8370Sstevel@tonic-gate i0++; 8380Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) 8390Sstevel@tonic-gate Fprint(stderr, "%ld/(%.1f) + ", (pc0 - pc00) * ticks, 8400Sstevel@tonic-gate DBL_ADDRPERCELL)); 8410Sstevel@tonic-gate } 8420Sstevel@tonic-gate 8430Sstevel@tonic-gate /* Init sum of counts for PCs not shared w/other */ 8440Sstevel@tonic-gate /* routines. */ 8450Sstevel@tonic-gate ticks = 0; 8460Sstevel@tonic-gate 8470Sstevel@tonic-gate /* Stop at first count that overlaps following */ 8480Sstevel@tonic-gate /* routine. */ 8490Sstevel@tonic-gate for (i = i0; i < i1; i++) 8500Sstevel@tonic-gate ticks += pcp[i]; 8510Sstevel@tonic-gate 8520Sstevel@tonic-gate t += PROFSEC(ticks); /* Convert to secs, add to total */ 8530Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) Fprint(stderr, "%ld", ticks)); 8540Sstevel@tonic-gate /* Some overlap with low addresses of next routine? */ 8550Sstevel@tonic-gate if (pc10 < pc1) { 8560Sstevel@tonic-gate /* Yes. Get total count ... */ 8570Sstevel@tonic-gate ticks = pcp[i1]; 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate /* and accumulate proportion for addresses in */ 8600Sstevel@tonic-gate /* range of this routine */ 8610Sstevel@tonic-gate t += PROFSEC(((double)ticks * 8620Sstevel@tonic-gate (pc1 - pc10)*sf)/bias); 8630Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks)); 8640Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) 8650Sstevel@tonic-gate Fprint(stderr, " + %ld/(%.1f)", (pc1 - pc10) * ticks, DBL_ADDRPERCELL) 8660Sstevel@tonic-gate ); 8670Sstevel@tonic-gate } 8680Sstevel@tonic-gate } /* End if (i0 == i1) ... else ... */ 8690Sstevel@tonic-gate 8700Sstevel@tonic-gate slp[n].sl_time = t; /* Store time for this routine. */ 8710Sstevel@tonic-gate t0 += t; /* Accumulate total time. */ 8720Sstevel@tonic-gate OLD_DEBUG(if (debug_value & 010) Fprint(stderr, " ticks = %.2f msec\n", t)); 8730Sstevel@tonic-gate } /* End for (n = 0; n < n_syms; n++) */ 8740Sstevel@tonic-gate 8750Sstevel@tonic-gate /* Final pass to total up time. */ 8760Sstevel@tonic-gate /* Sum ticks, then convert to seconds. */ 8770Sstevel@tonic-gate 8780Sstevel@tonic-gate for (n = n_pc, temp = 0; --n >= 0; temp += *(pcp++)); 8790Sstevel@tonic-gate 8800Sstevel@tonic-gate t_tot = PROFSEC(temp); 8810Sstevel@tonic-gate 8820Sstevel@tonic-gate /* 8830Sstevel@tonic-gate * Now, whilst we still have the symbols sorted 8840Sstevel@tonic-gate * in address order.. 8850Sstevel@tonic-gate * Loop to record duplicates, so we can display 8860Sstevel@tonic-gate * synonym symbols correctly. 8870Sstevel@tonic-gate * Synonym symbols, or symbols with the same address, 8880Sstevel@tonic-gate * are to be displayed by prof on the same line, with 8890Sstevel@tonic-gate * one statistics line, as below: 8900Sstevel@tonic-gate * ... 255 ldaopen, ldaopen 8910Sstevel@tonic-gate * The way this will be implemented, is as follows: 8920Sstevel@tonic-gate * 8930Sstevel@tonic-gate * Pass 1 - while the symbols are in address order, we 8940Sstevel@tonic-gate * do a pre-pass through them, to determine for which 8950Sstevel@tonic-gate * addresses there are more than one symbol (i.e. synonyms). 8960Sstevel@tonic-gate * During this prepass we collect summary statistics in 8970Sstevel@tonic-gate * the synonym entry, for all the synonyms. 8980Sstevel@tonic-gate * 8990Sstevel@tonic-gate * 'Pass' 2 - while printing a report, for each report line, 9000Sstevel@tonic-gate * if the current symbol is a synonym symbol (i.e. in the 9010Sstevel@tonic-gate * snymList) then we scan forward and pick up all the names 9020Sstevel@tonic-gate * which map to this address, and print them too. 9030Sstevel@tonic-gate * If the address' synonyms have already been printed, then 9040Sstevel@tonic-gate * we just skip this symbol and go on to process the next. 9050Sstevel@tonic-gate * 9060Sstevel@tonic-gate */ 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate { 9090Sstevel@tonic-gate /* pass 1 */ 9100Sstevel@tonic-gate char *thisaddr; 9110Sstevel@tonic-gate char *lastaddr = slist->sl_addr; /* use 1st sym as */ 9120Sstevel@tonic-gate /* 'last/prior symbol' */ 9130Sstevel@tonic-gate int lastWasSnym = 0; /* 1st can't be snym yet-no aliases seen! */ 9140Sstevel@tonic-gate int thisIsSnym; 9150Sstevel@tonic-gate 9160Sstevel@tonic-gate OLD_DEBUG( 9170Sstevel@tonic-gate int totsnyms = 0; int totseries = 0; struct slist *lastslp = slist; 9180Sstevel@tonic-gate ); 9190Sstevel@tonic-gate 9200Sstevel@tonic-gate /* NB loop starts with 2nd symbol, loops over n_syms-1 symbols! */ 9210Sstevel@tonic-gate for (n = n_syms-1, slp = slist+1; --n >= 0; slp++) { 9220Sstevel@tonic-gate thisaddr = slp->sl_addr; 9230Sstevel@tonic-gate thisIsSnym = (thisaddr == lastaddr); 9240Sstevel@tonic-gate 9250Sstevel@tonic-gate if (thisIsSnym) { 9260Sstevel@tonic-gate /* gotta synonym */ 9270Sstevel@tonic-gate if (!lastWasSnym) { 9280Sstevel@tonic-gate OLD_DEBUG( 9290Sstevel@tonic-gate if (debug_value) { 9300Sstevel@tonic-gate Fprint(stderr, 9310Sstevel@tonic-gate "Synonym series:\n1st->\t%s at address %x, ct=%ld, time=%f\n", 9320Sstevel@tonic-gate lastslp->sl_name, lastaddr, lastslp->sl_count, 9330Sstevel@tonic-gate lastslp->sl_time); 9340Sstevel@tonic-gate totseries++; 9350Sstevel@tonic-gate totsnyms++; 9360Sstevel@tonic-gate } 9370Sstevel@tonic-gate ); 9380Sstevel@tonic-gate /* this is the Second! of a series */ 9390Sstevel@tonic-gate snymp = (n_snyms++ == 0 ? snymList : snymp+1); 9400Sstevel@tonic-gate snymp->howMany = 1; /* gotta count 1st one!! */ 9410Sstevel@tonic-gate snymp->sym_addr = slp->sl_addr; 9420Sstevel@tonic-gate /* zero summary statistics */ 9430Sstevel@tonic-gate snymp->tot_sl_count = 0; 9440Sstevel@tonic-gate snymp->tot_sl_time = 0.0; 9450Sstevel@tonic-gate /* Offen the Reported flag */ 9460Sstevel@tonic-gate snymp->snymReported = 0; 9470Sstevel@tonic-gate } 9480Sstevel@tonic-gate OLD_DEBUG( 9490Sstevel@tonic-gate if (debug_value) { 9500Sstevel@tonic-gate Fprint(stderr, 9510Sstevel@tonic-gate "\t%s at address %x, ct=%ld, time=%f\n", 9520Sstevel@tonic-gate slp->sl_name, 9530Sstevel@tonic-gate thisaddr, 9540Sstevel@tonic-gate slp->sl_count, 9550Sstevel@tonic-gate slp->sl_time); 9560Sstevel@tonic-gate totsnyms++; 9570Sstevel@tonic-gate } 9580Sstevel@tonic-gate ); 9590Sstevel@tonic-gate /* ok - bump count for snym, and note its Finding */ 9600Sstevel@tonic-gate snymp->howMany++; 9610Sstevel@tonic-gate /* and update the summary statistics */ 9620Sstevel@tonic-gate snymp->tot_sl_count += slp->sl_count; 9630Sstevel@tonic-gate snymp->tot_sl_time += slp->sl_time; 9640Sstevel@tonic-gate } 9650Sstevel@tonic-gate callTotal += slp->sl_count; 9660Sstevel@tonic-gate lastaddr = thisaddr; 9670Sstevel@tonic-gate lastWasSnym = thisIsSnym; 9680Sstevel@tonic-gate OLD_DEBUG( 9690Sstevel@tonic-gate if (debug_value) lastslp = slp; 9700Sstevel@tonic-gate ); 9710Sstevel@tonic-gate 9720Sstevel@tonic-gate } 9730Sstevel@tonic-gate OLD_DEBUG( 9740Sstevel@tonic-gate if (debug_value) { 9750Sstevel@tonic-gate Fprint(stderr, "Total #series %d, #synonyms %d\n", totseries, totsnyms); 9760Sstevel@tonic-gate } 9770Sstevel@tonic-gate ); 9780Sstevel@tonic-gate } 9790Sstevel@tonic-gate /* 9800Sstevel@tonic-gate * Most of the heavy work is done now. Only minor stuff remains. 9810Sstevel@tonic-gate * The symbols are currently in address order and must be re-sorted 9820Sstevel@tonic-gate * if desired in a different order. Report generating options 9830Sstevel@tonic-gate * include "-o" or "-x": Include symbol address, which causes 9840Sstevel@tonic-gate * another column 9850Sstevel@tonic-gate * in the output; and "-z": Include symbols in report even if zero 9860Sstevel@tonic-gate * time and call count. Symbols not in profiling range are excluded 9870Sstevel@tonic-gate * in any case. Following the main body of the report, the "-s" 9880Sstevel@tonic-gate * option causes certain additional information to be printed. 9890Sstevel@tonic-gate */ 9900Sstevel@tonic-gate 9910Sstevel@tonic-gate OLD_DEBUG(if (debug_value) Fprint(stderr, 9920Sstevel@tonic-gate "Time unaccounted for: %.7G\n", t_tot - t0)); 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate if (sort) /* If comparison routine given then use it. */ 9950Sstevel@tonic-gate qsort((char *)slist, (unsigned)n_syms, 9960Sstevel@tonic-gate sizeof (struct slist), sort); 9970Sstevel@tonic-gate 9980Sstevel@tonic-gate if (!(flags & F_NHEAD)) { 9990Sstevel@tonic-gate if (flags & F_PADDR) 1000211Smike_s Print("%s", atitle); /* Title for addresses. */ 10010Sstevel@tonic-gate (void) puts(" %Time Seconds Cumsecs #Calls msec/call Name"); 10020Sstevel@tonic-gate } 10030Sstevel@tonic-gate t = 0.0; /* Init cumulative time. */ 10040Sstevel@tonic-gate if (t_tot != 0.0) /* Convert to percent. */ 10050Sstevel@tonic-gate t_tot = 100.0/t_tot; /* Prevent divide-by-zero fault */ 10060Sstevel@tonic-gate n_nonzero = 0; /* Number of symbols with nonzero time or # calls. */ 10070Sstevel@tonic-gate for (n = n_syms, slp = slist; --n >= 0; slp++) { 10080Sstevel@tonic-gate long count; /* # Calls. */ 10090Sstevel@tonic-gate /* t0, time in seconds. */ 10100Sstevel@tonic-gate 10110Sstevel@tonic-gate /* if a snym symbol, use summarized stats, else use indiv. */ 10120Sstevel@tonic-gate if ((snymp = getSnymEntry(slp->sl_addr)) != 0) { 10130Sstevel@tonic-gate count = snymp->tot_sl_count; 10140Sstevel@tonic-gate t0 = snymp->tot_sl_time; 10150Sstevel@tonic-gate 10160Sstevel@tonic-gate } else { 10170Sstevel@tonic-gate count = slp->sl_count; 10180Sstevel@tonic-gate t0 = slp->sl_time; 10190Sstevel@tonic-gate } 10200Sstevel@tonic-gate 10210Sstevel@tonic-gate /* if a snym and already reported, skip this entry */ 10220Sstevel@tonic-gate if (snymp && snymp->snymReported) 10230Sstevel@tonic-gate continue; 10240Sstevel@tonic-gate /* Don't do entries with no action. */ 10250Sstevel@tonic-gate if (t0 == 0.0 && count == 0 && !(flags & F_ZSYMS)) 10260Sstevel@tonic-gate continue; 10270Sstevel@tonic-gate if ((strcmp(slp->sl_name, "_mcount") == 0) || 10280Sstevel@tonic-gate (strcmp(slp->sl_name, "mcount") == 0)) { 10290Sstevel@tonic-gate count = callTotal; 10300Sstevel@tonic-gate } 10310Sstevel@tonic-gate 10320Sstevel@tonic-gate /* count number of entries (i.e. symbols) printed */ 10330Sstevel@tonic-gate if (snymp) 10340Sstevel@tonic-gate n_nonzero += snymp->howMany; /* add for each snym */ 10350Sstevel@tonic-gate else 10360Sstevel@tonic-gate n_nonzero++; 10370Sstevel@tonic-gate 1038211Smike_s if (flags & F_PADDR) { /* Printing address of symbol? */ 1039211Smike_s /* LINTED: variable format */ 10400Sstevel@tonic-gate Print(aformat, slp->sl_addr); 1041211Smike_s } 10420Sstevel@tonic-gate t += t0; /* move here; compiler bug !! */ 10430Sstevel@tonic-gate Print("%6.1f%8.2f%8.2f", t0 * t_tot, t0, t); 10440Sstevel@tonic-gate fdigits = 0; 10450Sstevel@tonic-gate if (count) { /* Any calls recorded? */ 10460Sstevel@tonic-gate /* Get reasonable number of fractional digits to print. */ 10470Sstevel@tonic-gate fdigits = fprecision(count); 10480Sstevel@tonic-gate Print("%8ld%#*.*f", count, fdigits+8, fdigits, 10490Sstevel@tonic-gate 1000.0*t0/count); 10500Sstevel@tonic-gate Print("%*s", 6-fdigits, " "); 10510Sstevel@tonic-gate } else { 10520Sstevel@tonic-gate Print("%22s", " "); 10530Sstevel@tonic-gate } 10540Sstevel@tonic-gate /* 10550Sstevel@tonic-gate * now print the name (or comma-seperate list of names, 10560Sstevel@tonic-gate * for synonym symbols). 10570Sstevel@tonic-gate */ 10580Sstevel@tonic-gate if (snymp) { 10590Sstevel@tonic-gate printSnymNames(slp, snymp); /* print it, then */ 10600Sstevel@tonic-gate snymp->snymReported = 1; /* mark it Done */ 10610Sstevel@tonic-gate } 10620Sstevel@tonic-gate else 10630Sstevel@tonic-gate (void) puts(slp->sl_name); /* print the one name */ 10640Sstevel@tonic-gate } 10650Sstevel@tonic-gate if (flags & F_VERBOSE) { /* Extra info? */ 10660Sstevel@tonic-gate Fprint(stderr, "%5d/%d call counts used\n", n_cc, head.nfns); 10670Sstevel@tonic-gate Fprint(stderr, "%5d/%d symbols qualified", n_syms, symttl); 10680Sstevel@tonic-gate if (n_nonzero < n_syms) 10690Sstevel@tonic-gate Fprint(stderr, 10700Sstevel@tonic-gate ", %d had zero time and zero call-counts\n", 10710Sstevel@tonic-gate n_syms - n_nonzero); 10720Sstevel@tonic-gate else 10730Sstevel@tonic-gate (void) putc('\n', stderr); 1074211Smike_s Fprint(stderr, "%#lx scale factor\n", (long)sf); 10750Sstevel@tonic-gate } 10760Sstevel@tonic-gate 10770Sstevel@tonic-gate _symintClose(ldptr); 10780Sstevel@tonic-gate } else { 10790Sstevel@tonic-gate Fprint(stderr, "prof: no call counts captured\n"); 10800Sstevel@tonic-gate } 1081211Smike_s return (0); 10820Sstevel@tonic-gate } 10830Sstevel@tonic-gate /* Return size of file associated with file descriptor fd. */ 10840Sstevel@tonic-gate 1085211Smike_s static off_t 1086211Smike_s fsize(int fd) 10870Sstevel@tonic-gate { 10880Sstevel@tonic-gate struct stat sbuf; 10890Sstevel@tonic-gate 10900Sstevel@tonic-gate if (fstat(fd, &sbuf) < 0) /* Status of open file. */ 10910Sstevel@tonic-gate Perror("stat"); 10920Sstevel@tonic-gate return (sbuf.st_size); /* This is a long. */ 10930Sstevel@tonic-gate } 10940Sstevel@tonic-gate 10950Sstevel@tonic-gate /* Read symbol entry. Return TRUE if satisfies conditions. */ 10960Sstevel@tonic-gate 1097211Smike_s static int 1098211Smike_s readnl(int symindex) 10990Sstevel@tonic-gate { 11000Sstevel@tonic-gate nl = ldptr->pf_symarr_p[symindex]; 11010Sstevel@tonic-gate 11020Sstevel@tonic-gate OLD_DEBUG( 11030Sstevel@tonic-gate if (debug_value & 020) { 11040Sstevel@tonic-gate Fprint(stderr, 11050Sstevel@tonic-gate "`%-8.8s'\tst_info=%#4o, value=%#8.6o\n", 11060Sstevel@tonic-gate ldptr->pf_symstr_p[nl.ps_sym.st_name], 11070Sstevel@tonic-gate (unsigned char) nl.ps_sym.st_info, 11080Sstevel@tonic-gate nl.ps_sym.st_value); 11090Sstevel@tonic-gate } 11100Sstevel@tonic-gate ); 11110Sstevel@tonic-gate /* 11120Sstevel@tonic-gate * TXTSYM accepts global (and local, if "-g" given) T-type symbols. 11130Sstevel@tonic-gate * Only those in the profiling range are useful. 11140Sstevel@tonic-gate */ 11150Sstevel@tonic-gate return (nl.ps_sym.st_shndx < SHN_LORESERVE && 11160Sstevel@tonic-gate TXTSYM(nl.ps_sym.st_shndx, 11170Sstevel@tonic-gate nl.ps_sym.st_info) && 11180Sstevel@tonic-gate (pc_l <= (char *)nl.ps_sym.st_value) && 11190Sstevel@tonic-gate ((char *)nl.ps_sym.st_value < pc_h)); 11200Sstevel@tonic-gate } 11210Sstevel@tonic-gate /* 11220Sstevel@tonic-gate * Error-checking memory allocators - 11230Sstevel@tonic-gate * Guarantees good return (else none at all). 11240Sstevel@tonic-gate */ 11250Sstevel@tonic-gate 1126211Smike_s static void * 1127211Smike_s _prof_Malloc(int item_count, int item_size) 11280Sstevel@tonic-gate { 1129211Smike_s void *p; 11300Sstevel@tonic-gate 11310Sstevel@tonic-gate if ((p = malloc((unsigned)item_count * (unsigned)item_size)) == NULL) { 11320Sstevel@tonic-gate (void) fprintf(stderr, "%s: Out of space\n", cmdname); 11330Sstevel@tonic-gate exit(1); 11340Sstevel@tonic-gate } 11350Sstevel@tonic-gate return (p); 11360Sstevel@tonic-gate } 11370Sstevel@tonic-gate 11380Sstevel@tonic-gate 11390Sstevel@tonic-gate 11400Sstevel@tonic-gate /* 11410Sstevel@tonic-gate * Given the quotient Q = N/D, where entier(N) == N and D > 0, an 11420Sstevel@tonic-gate * approximation of the "best" number of fractional digits to use 11430Sstevel@tonic-gate * in printing Q is f = entier(log10(D)), which is crudely produced 11440Sstevel@tonic-gate * by the following routine. 11450Sstevel@tonic-gate */ 11460Sstevel@tonic-gate 1147211Smike_s static int 1148211Smike_s fprecision(long count) 11490Sstevel@tonic-gate { 11500Sstevel@tonic-gate return (count < 10 ? 0 : count < 100 ? 1 : count < 1000 ? 2 : 11510Sstevel@tonic-gate count < 10000 ? 3 : 4); 11520Sstevel@tonic-gate } 11530Sstevel@tonic-gate 11540Sstevel@tonic-gate /* 11550Sstevel@tonic-gate * Return pointer to base name(name less path) of string s. 11560Sstevel@tonic-gate * Handles case of superfluous trailing '/'s, and unlikely 11570Sstevel@tonic-gate * case of s == "/". 11580Sstevel@tonic-gate */ 11590Sstevel@tonic-gate 1160211Smike_s static char * 1161211Smike_s basename(char *s) 11620Sstevel@tonic-gate { 1163211Smike_s char *p; 11640Sstevel@tonic-gate 11650Sstevel@tonic-gate p = &s[strlen(s)]; /* End (+1) of string. */ 11660Sstevel@tonic-gate while (p > s && *--p == '/') /* Trim trailing '/'s. */ 11670Sstevel@tonic-gate *p = '\0'; 11680Sstevel@tonic-gate p++; /* New end (+1) of string. */ 11690Sstevel@tonic-gate while (p > s && *--p != '/'); /* Break backward on '/'. */ 11700Sstevel@tonic-gate if (*p == '/') /* If found '/', point to 1st following. */ 11710Sstevel@tonic-gate p++; 11720Sstevel@tonic-gate if (*p == '\0') 11730Sstevel@tonic-gate p = "/"; /* If NULL, must be "/". (?) */ 11740Sstevel@tonic-gate return (p); 11750Sstevel@tonic-gate } 11760Sstevel@tonic-gate /* Here if unexpected read problem. */ 11770Sstevel@tonic-gate 1178211Smike_s static void 1179211Smike_s eofon(FILE *iop, char *fn) 11800Sstevel@tonic-gate { 11810Sstevel@tonic-gate if (ferror(iop)) /* Real error? */ 11820Sstevel@tonic-gate Perror(fn); /* Yes. */ 11830Sstevel@tonic-gate Fprint(stderr, "%s: %s: Premature EOF\n", cmdname, fn); 11840Sstevel@tonic-gate exit(1); 11850Sstevel@tonic-gate } 11860Sstevel@tonic-gate 11870Sstevel@tonic-gate /* Version of perror() that prints cmdname first. */ 11880Sstevel@tonic-gate 1189211Smike_s static void 1190211Smike_s Perror(char *s) 11910Sstevel@tonic-gate { /* Print system error message & exit. */ 1192211Smike_s int err = errno; /* Save current errno in case */ 11930Sstevel@tonic-gate 11940Sstevel@tonic-gate Fprint(stderr, "%s: ", cmdname); 11950Sstevel@tonic-gate errno = err; /* Put real error back. */ 11960Sstevel@tonic-gate perror(s); /* Print message. */ 11970Sstevel@tonic-gate _symintClose(ldptr); /* cleanup symbol information */ 11980Sstevel@tonic-gate exit(1); /* Exit w/nonzero status. */ 11990Sstevel@tonic-gate } 12000Sstevel@tonic-gate 12010Sstevel@tonic-gate /* Here for things that "Should Never Happen". */ 12020Sstevel@tonic-gate 1203211Smike_s static void 1204211Smike_s snh(void) 12050Sstevel@tonic-gate { 12060Sstevel@tonic-gate Fprint(stderr, "%s: Internal error\n", cmdname); 12070Sstevel@tonic-gate (void) abort(); 12080Sstevel@tonic-gate } 1209211Smike_s 12100Sstevel@tonic-gate /* 12110Sstevel@tonic-gate * Various comparison routines for qsort. Uses: 12120Sstevel@tonic-gate * 12130Sstevel@tonic-gate * c_ccaddr - Compare fnpc fields of cnt structs to put 12140Sstevel@tonic-gate * call counters in increasing address order. 12150Sstevel@tonic-gate * c_sladdr - Sort slist structures on increasing address. 12160Sstevel@tonic-gate * c_time - " " " " decreasing time. 12170Sstevel@tonic-gate * c_ncalls - " " " " decreasing # calls. 12180Sstevel@tonic-gate * c_name - " " " " increasing symbol name 12190Sstevel@tonic-gate */ 12200Sstevel@tonic-gate 12210Sstevel@tonic-gate #define CMP2(v1, v2) ((v1) < (v2) ? -1 : (v1) == (v2) ? 0 : 1) 12220Sstevel@tonic-gate #define CMP1(v) CMP2(v, 0) 12230Sstevel@tonic-gate 1224211Smike_s int 1225211Smike_s c_ccaddr(const void *arg1, const void *arg2) 12260Sstevel@tonic-gate { 1227211Smike_s struct cnt *p1 = (struct cnt *)arg1; 1228211Smike_s struct cnt *p2 = (struct cnt *)arg2; 1229211Smike_s 12300Sstevel@tonic-gate return (CMP2(p1->fnpc, p2->fnpc)); 12310Sstevel@tonic-gate } 12320Sstevel@tonic-gate 1233211Smike_s int 1234211Smike_s c_sladdr(const void *arg1, const void *arg2) 12350Sstevel@tonic-gate { 1236211Smike_s struct slist *p1 = (struct slist *)arg1; 1237211Smike_s struct slist *p2 = (struct slist *)arg2; 1238211Smike_s 12390Sstevel@tonic-gate return (CMP2(p1->sl_addr, p2->sl_addr)); 12400Sstevel@tonic-gate } 12410Sstevel@tonic-gate 1242211Smike_s int 1243211Smike_s c_time(const void *arg1, const void *arg2) 12440Sstevel@tonic-gate { 1245211Smike_s struct slist *p1 = (struct slist *)arg1; 1246211Smike_s struct slist *p2 = (struct slist *)arg2; 1247211Smike_s float dtime = p2->sl_time - p1->sl_time; /* Decreasing time. */ 12480Sstevel@tonic-gate 12490Sstevel@tonic-gate return (CMP1(dtime)); 12500Sstevel@tonic-gate } 12510Sstevel@tonic-gate 1252211Smike_s int 1253211Smike_s c_ncalls(const void *arg1, const void *arg2) 12540Sstevel@tonic-gate { 1255211Smike_s struct slist *p1 = (struct slist *)arg1; 1256211Smike_s struct slist *p2 = (struct slist *)arg2; 1257211Smike_s int diff = p2->sl_count - p1->sl_count; 12580Sstevel@tonic-gate /* Decreasing # calls. */ 12590Sstevel@tonic-gate return (CMP1(diff)); 12600Sstevel@tonic-gate } 12610Sstevel@tonic-gate 1262211Smike_s int 1263211Smike_s c_name(const void *arg1, const void *arg2) 12640Sstevel@tonic-gate { 1265211Smike_s struct slist *p1 = (struct slist *)arg1; 1266211Smike_s struct slist *p2 = (struct slist *)arg2; 1267211Smike_s int diff; 12680Sstevel@tonic-gate 12690Sstevel@tonic-gate /* flex names has variable length strings for names */ 12700Sstevel@tonic-gate diff = strcmp(p1->sl_name, p2->sl_name); 12710Sstevel@tonic-gate return (CMP1(diff)); 12720Sstevel@tonic-gate } 12730Sstevel@tonic-gate 12740Sstevel@tonic-gate #define STRSPACE 2400 /* guess at amount of string space */ 12750Sstevel@tonic-gate 12760Sstevel@tonic-gate char *format_buf; 12770Sstevel@tonic-gate #define FORMAT_BUF "%s\n\t\t\t\t\t [%s]" 12780Sstevel@tonic-gate 12790Sstevel@tonic-gate static char * 1280211Smike_s demangled_name(char *s) 12810Sstevel@tonic-gate { 12820Sstevel@tonic-gate char *name; 1283211Smike_s size_t len; 12840Sstevel@tonic-gate 12850Sstevel@tonic-gate name = (char *)sgs_demangle(s); 12860Sstevel@tonic-gate 12870Sstevel@tonic-gate if (strcmp(name, s) == 0) 12880Sstevel@tonic-gate return (s); 12890Sstevel@tonic-gate 12900Sstevel@tonic-gate if (format_buf != NULL) 12910Sstevel@tonic-gate free(format_buf); 12920Sstevel@tonic-gate 1293211Smike_s len = strlen(name) + strlen(FORMAT_BUF) + strlen(s) + 1; 1294211Smike_s format_buf = malloc(len); 12950Sstevel@tonic-gate if (format_buf == NULL) 12960Sstevel@tonic-gate return (s); 1297211Smike_s (void) snprintf(format_buf, len, FORMAT_BUF, name, s); 12980Sstevel@tonic-gate return (format_buf); 12990Sstevel@tonic-gate } 13000Sstevel@tonic-gate 13010Sstevel@tonic-gate /* getname - get the name of a symbol in a permanent fashion */ 1302211Smike_s static char * 1303211Smike_s getname(PROF_FILE *ldpter, PROF_SYMBOL symbol) 13040Sstevel@tonic-gate { 13050Sstevel@tonic-gate static char *strtable = NULL; /* space for names */ 13060Sstevel@tonic-gate static int sp_used = 0; /* space used so far */ 13070Sstevel@tonic-gate static int size = 0; /* size of string table */ 1308211Smike_s char *name; /* name to store */ 13090Sstevel@tonic-gate int lth; /* space needed for name */ 13100Sstevel@tonic-gate int get; /* amount of space to get */ 13110Sstevel@tonic-gate 1312*3621Sab196087 name = elf_strptr(ldpter->pf_elf_p, ldpter->pf_symstr_ndx, 1313*3621Sab196087 symbol.ps_sym.st_name); 1314*3621Sab196087 if (name == NULL) 13150Sstevel@tonic-gate return ("<<bad symbol name>>"); 13160Sstevel@tonic-gate 13170Sstevel@tonic-gate if (Cflag) 1318211Smike_s name = demangled_name(name); 13190Sstevel@tonic-gate 13200Sstevel@tonic-gate lth = strlen(name) + 1; 13210Sstevel@tonic-gate if ((sp_used + lth) > size) { /* if need more space */ 13220Sstevel@tonic-gate /* just in case very long name */ 13230Sstevel@tonic-gate get = lth > STRSPACE ? lth : STRSPACE; 13240Sstevel@tonic-gate strtable = _prof_Malloc(1, get); 13250Sstevel@tonic-gate size = get; 13260Sstevel@tonic-gate sp_used = 0; 13270Sstevel@tonic-gate } 13280Sstevel@tonic-gate (void) strcpy(&(strtable[sp_used]), name); 13290Sstevel@tonic-gate name = &(strtable[sp_used]); 13300Sstevel@tonic-gate sp_used += lth; 13310Sstevel@tonic-gate return (name); 13320Sstevel@tonic-gate } 13330Sstevel@tonic-gate 1334211Smike_s static void 1335211Smike_s usage(void) 13360Sstevel@tonic-gate { 1337211Smike_s (void) fprintf(stderr, 1338211Smike_s "usage: %s [-ChsVz] [-a | c | n | t] [-o | x] [-g | l]\n" 1339211Smike_s "\t[-m mdata] [prog]\n", 1340211Smike_s cmdname); 1341211Smike_s exit(1); 13420Sstevel@tonic-gate } 1343