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