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