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