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