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