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 }