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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1988 AT&T */ 23 /* All Rights Reserved */ 24 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Copyright 1993-2003 Sun Microsystems, Inc. All rights reserved. 30 * Use is subject to license terms. 31 */ 32 33 /* 34 * Program profiling report generator. 35 * 36 * Usage: 37 * 38 * prof [ -V ] [ -[ntca] ] [ -[ox] ] [ -g ] [ -l ] [ -z ] [ -s ] [ -C ] 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 inlcude 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 "sgs.h" 70 #include "symint.h" 71 #include "sys/param.h" /* for HZ */ 72 #include "mon.h" 73 #include "sys/stat.h" 74 #include "debug.h" 75 76 #define OLD_DEBUG(x) 77 78 #define PROC /* Mark procedure names. */ 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 /* extern char *realloc(), *strncpy(), *optarg; */ 152 extern int optind; 153 extern long strtol(); 154 extern void qsort(), exit(), perror(); 155 156 157 /* For symbol table entries read from program file. */ 158 PROF_SYMBOL nl; 159 160 /* Compare routines called from qsort() */ 161 162 int c_ccaddr(); /* Compare fnpc fields of cnt structures. */ 163 int c_sladdr(); /* Compare sl_addr fields of slist structures */ 164 int c_time(); /* " sl_time " " " */ 165 int c_name(); /* " sl_name " " " */ 166 int c_ncalls(); /* " sl_count " " " */ 167 168 /* Other stuff. */ 169 170 /* Return size of open file (arg is file descriptor) */ 171 off_t fsize(); 172 173 /* Memory allocation. Like malloc(), but no return if error. */ 174 char *_prof_Malloc(); 175 176 /* Scan past path part (if any) in the ... */ 177 char *basename(); 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 { 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 /* 248 * Sort flags. Mutually exclusive. These need to be identical to the ones 249 * defined in profv.h 250 */ 251 #define BY_ADDRESS 0x1 252 #define BY_NCALLS 0x2 253 #define BY_NAME 0x4 254 #define BY_TIME 0x8 255 256 extern unsigned char sort_flag; /* what type of sort ? */ 257 258 /* 259 * printSnymNames - print a comma-seperated list of snym names. 260 * This routine hunts down all the synonyms for the given 261 * symbol, and prints them as a comma-seperated list. 262 * NB we assume that all the synonyms _Follow_ this one, 263 * since they are only printed when the First one 264 * is seen. 265 */ 266 PROC 267 void 268 printSnymNames(slp, snymp) 269 struct slist *slp; 270 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 Print(", %s", slp->sl_name); 284 } 285 /* finally.. the trailing newline */ 286 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 PROC 296 struct snymEntry 297 *getSnymEntry(sl_addr) 298 char *sl_addr; 299 { 300 struct snymEntry *p; 301 int i; 302 303 for (p = snymList, i = n_snyms; --i >= 0; p++) 304 if (sl_addr == p->sym_addr) 305 return (p); 306 307 return ((struct snymEntry *)0); 308 } 309 310 311 PROC 312 main(argc, argv) 313 int argc; 314 char **argv; 315 { 316 char buffer[BUFSIZ]; /* buffer for printf */ 317 318 WORD *pcounts; /* Pointer to allocated area for */ 319 /* pcounts: PC clock hit counts */ 320 321 register WORD *pcp; /* For scanning pcounts. */ 322 323 struct cnt *ccounts; /* Pointer to allocated area for cnt */ 324 /* structures: subr PC-call counts. */ 325 326 register struct cnt *ccp; /* For scanning ccounts. */ 327 328 struct slist *slist; /* Pointer to allocated slist structures: */ 329 /* symbol name/address/time/call counts */ 330 331 register struct slist *slp; /* For scanning slist */ 332 333 int vn_cc, n_cc; /* Number of cnt structures in profile data */ 334 /* file (later # ones used). */ 335 336 int n_pc; /* Number of pcounts in profile data file. */ 337 338 int n_syms; /* Number of text symbols (of proper type) */ 339 /* that fill in range of profiling. */ 340 341 int n_nonzero; /* Number of (above symbols) actually printed */ 342 /* because nonzero time or # calls. */ 343 344 int symttl; /* Total # symbols in program file sym-table */ 345 346 int i; 347 348 int fdigits = 0; /* # of digits of precision for print msecs/call */ 349 350 register int n, symct; 351 352 long sf; /* Scale for index into pcounts: */ 353 /* i(pc) = ((pc - pc_l) * sf)/bias. */ 354 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 float profOverhead = 0.0; 363 int callTotal = 0; 364 char *getname(); /* get name from symbol */ 365 366 DEBUG_LOC("main: top"); 367 setbuf(stdout, buffer); 368 cmdname = basename(*argv); /* command name. */ 369 370 while ((n = getopt(argc, argv, "canthsglzoxT:m:VC")) != EOF) { 371 switch (n) { 372 int (*fcn)(); /* For function to sort results. */ 373 374 case 'm': /* Specify data file: -m file */ 375 mon_fn = optarg; 376 break; 377 378 #ifdef ddt 379 case 'T': /* Set trace flags: -T(octnum) */ 380 debug_value = (int)strtol(optarg, 0, 8); 381 break; 382 #endif 383 384 case 'n': /* Sort by symbol name. */ 385 fcn = c_name; 386 sort_flag |= BY_NAME; 387 goto check; 388 389 case 't': /* Sort by decreasing time. */ 390 fcn = c_time; 391 sort_flag |= BY_TIME; 392 goto check; 393 394 case 'c': /* Sort by decreasing # calls. */ 395 fcn = c_ncalls; 396 sort_flag |= BY_NCALLS; 397 goto check; 398 399 case 'a': /* Sort by increasing symbol address */ 400 /* (don't have to -- it will be) */ 401 fcn = NULL; 402 sort_flag |= BY_ADDRESS; 403 check: /* Here to check sort option conflicts. */ 404 if (sort != NULL && sort != fcn) { 405 Fprint(stderr, "%s: Warning: %c overrides" 406 " previous specification\n", cmdname, n); 407 } 408 sort = fcn; /* Store sort routine */ 409 flags |= F_SORT; /* Note have done so */ 410 break; 411 412 case 'o': /* Include symbol addresses in output. */ 413 case 'x': /* Include symbol addresses in output. */ 414 aformat[2] = n; /* 'o' or 'x' in format */ 415 flags |= F_PADDR; /* Set flag. */ 416 break; 417 418 case 'g': /* Include local T symbols as well as global */ 419 gflag = 1; 420 break; 421 422 case 'l': /* Do NOT include local T symbols */ 423 gflag = 0; 424 break; 425 426 case 'z': /* Print all symbols in profiling range, */ 427 /* even if no time or # calls. */ 428 flags |= F_ZSYMS; /* Set flag. */ 429 break; 430 431 case 'h': /* Suppress table header. */ 432 flags |= F_NHEAD; 433 break; 434 435 case 's': /* Follow normal output with extra summary. */ 436 flags |= F_VERBOSE; /* Set flag (...) */ 437 break; 438 439 case 'V': 440 (void) fprintf(stderr, "prof: %s %s\n", 441 (const char *)SGU_PKG, (const char *)SGU_REL); 442 VwasSpecified = 1; 443 break; 444 445 case 'C': /* demangle C++ names before printing. */ 446 Cflag = 1; 447 break; 448 449 case '?': /* But no good. */ 450 Fprint(stderr, 451 "%s: Unrecognized option: %c\n", cmdname, n); 452 exit(1); 453 454 } /* End switch (n) */ 455 } /* End while (getopt) */ 456 457 DEBUG_LOC("main: following getopt"); 458 459 /* if -V the only argument, just exit. */ 460 if (VwasSpecified && argc == 2 && !flags) 461 exit(0); 462 463 if (optind < argc) 464 sym_fn = argv[optind]; /* name other than `a.out' */ 465 466 if (sort == NULL && !(flags & F_SORT)) 467 /* If have not specified sort mode ... */ 468 sort = c_time; /* then sort by decreasing time. */ 469 470 /* 471 * profver() checks to see if the mon.out was "versioned" and if 472 * yes, processes it and exits; otherwise, we have an *old-style* 473 * mon.out and we process it the old way. 474 */ 475 profver(); 476 477 /* Open monitor data file (has counts). */ 478 if ((mon_iop = fopen(mon_fn, "r")) == NULL) 479 Perror(mon_fn); 480 481 DEBUG_LOC("main: before _symintOpen"); 482 if ((ldptr = _symintOpen(sym_fn)) == NULL) { 483 Perror("_symintOpen failed"); 484 } 485 DEBUG_LOC("main: after _symintOpen"); 486 filhdr = *ldptr; 487 488 scnhdrp = ldptr->pf_shdarr_p; 489 490 { 491 Elf_Kind k = elf_kind(filhdr.pf_elf_p); 492 493 DEBUG_EXP(printf("elf_kind = %d\n", k)); 494 DEBUG_EXP(printf("elf_type = %d\n", filhdr.pf_elfhd_p->e_type)); 495 if ((k != ELF_K_ELF) || (filhdr.pf_elfhd_p->e_type != ET_EXEC)) { 496 Fprint(stderr, "%s: %s: improper format\n", cmdname, sym_fn); 497 exit(1); 498 } 499 } 500 501 /* Compute the file address of symbol table. Machine-dependent. */ 502 503 DEBUG_EXP(printf("number of symbols (pf_nsyms) = %d\n", 504 filhdr.pf_nsyms)); 505 506 /* Number of symbols in file symbol table. */ 507 symttl = filhdr.pf_nsyms; 508 if (symttl == 0) { /* This is possible. */ 509 Fprint(stderr, "%s: %s: no symbols\n", cmdname, sym_fn); 510 exit(0); /* Note zero exit code. */ 511 } 512 /* Get size of file containing profiling data. Read header part. */ 513 n = fsize(fileno(mon_iop)); 514 if (fread((char *)&head, sizeof (struct hdr), 1, mon_iop) != 1) 515 eofon(mon_iop, mon_fn); /* Probably junk file. */ 516 517 /* Get # cnt structures (they follow header), */ 518 /* and allocate space for them. */ 519 520 n_cc = head.nfns; 521 ccounts = (struct cnt *)_prof_Malloc(n_cc, sizeof (struct cnt)); 522 523 /* Read the call addr-count pairs. */ 524 if (fread((char *)ccounts, sizeof (struct cnt), n_cc, mon_iop) != n_cc) 525 eofon(mon_iop, mon_fn); 526 527 /* 528 * Compute # PC counters (pcounts), which occupy whatever is left 529 * of the file after the header and call counts. 530 */ 531 532 n_pc = (n - sizeof (head) - n_cc * sizeof (struct cnt))/sizeof (WORD); 533 ccp = &ccounts[n_cc]; /* Point to last (+1) of call counters ... */ 534 do { /* and scan backward until find highest one used. */ 535 if ((--ccp)->mcnt) 536 break; /* Stop when find nonzero count. */ 537 } while (--n_cc > 0); /* Or all are zero. */ 538 539 if (n_cc > 0) { 540 541 /* If less than all cnt entries are used, return unused space. */ 542 if (n_cc < head.nfns) { 543 if ((ccounts = (struct cnt *)realloc((char *)ccounts, 544 (unsigned)n_cc * sizeof (struct cnt))) == NULL) 545 snh(); /* Should not fail when reducing size. */ 546 } 547 548 /* If more than 250 cnt entries used set verbose for warning */ 549 if (n_cc > (MPROGS0 * 5)/6) 550 flags |= F_VERBOSE; 551 552 /* Space for PC counts. */ 553 pcounts = (WORD *)_prof_Malloc(n_pc, sizeof (WORD)); 554 /* Read the PC counts from rest of MON_OUT file. */ 555 if (fread((char *)pcounts, sizeof (WORD), n_pc, mon_iop) != n_pc) 556 eofon(mon_iop, mon_fn); 557 /* 558 * 559 * Having gotten preliminaries out of the way, get down to business. 560 * The range pc_m of addresses over which profiling was done is 561 * computed from the low (pc_l) and high (pc_h) addresses, gotten 562 * from the MON_OUT header. From this and the number of clock 563 * tick counters, n_pc, is computed the so-called "scale", sf, used 564 * in the mapping of addresses to indices, as follows: 565 * 566 * (pc - pc_l) * sf 567 * i(pc) = ---------------- 568 * 0200000 569 * 570 * Also, the N-to-one value, s_inv, such that 571 * 572 * i(pc_l + K * s_inv + d) = K, for 0 <= d < s_inv 573 * 574 * Following this, the symbol table is scanned, and those symbols 575 * that qualify are counted. These are T-type symbols, excluding 576 * local (nonglobal) unless the "-g" option was given. Having thus 577 * determined the space requirements, space for symbols/times etc. 578 * is allocated, and the symbol table re-read, this time keeping 579 * qualified symbols. 580 * 581 * NB s_inv, as actually computed, is not sufficiently accurate 582 * (since it is truncated) for many calculations. Since it is 583 * logically equivalent to 1/(sf/bias), and the latter is much 584 * more accurate, therefore the latter will often appear in 585 * the code when 's_inv' is mentioned. dween 586 * 587 */ 588 589 590 pc_l = head.lpc; /* Low PC of range that was profiled. */ 591 pc_h = head.hpc; /* First address past range of profiling. */ 592 pc_m = pc_h - pc_l; /* Range of profiled addresses. */ 593 594 OLD_DEBUG(if (debug_value) Fprint(stderr, 595 "low pc = %#o, high pc = %#o, range = %#o = %u\n\ 596 call counts: %u, %u used; pc counters: %u\n", 597 pc_l, pc_h, pc_m, pc_m, head.nfns, n_cc, n_pc)); 598 599 sf = (BIAS * (double)n_pc)/pc_m; 600 /* 601 * Now adjust bias and sf so that there is no overflow 602 * when calculating indices. 603 */ 604 bias = BIAS; 605 temp = pc_m; 606 while ((temp >>= 1) > 0x7fff) { 607 sf >>= 1; 608 bias >>= 1; 609 } 610 s_inv = pc_m/n_pc; /* Range of PCs mapped into one index. */ 611 612 OLD_DEBUG( 613 if (debug_value) { 614 Fprint( 615 stderr, 616 "sf = %d, s_inv = %d bias = %d\n", 617 (long)sf, s_inv, bias); 618 } 619 ); 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 OLD_DEBUG( 629 if (debug_value) { 630 Fprint(stderr, "%u symbols, %u qualify\n", symttl, n_syms); 631 } 632 ); 633 634 /* Allocate space for qualified symbols. */ 635 636 slist = slp = 637 (struct slist *)_prof_Malloc(n_syms, sizeof (struct slist)); 638 639 /* 640 * Allocate space for synonym symbols 641 * (i.e. symbols that refer to the same address). 642 * NB there can be no more than n_syms/2 addresses 643 * with symbols, That Have Aliases, that refer to them! 644 */ 645 646 snymCapacity = n_syms/2; 647 snymList = snymp = 648 (struct snymEntry *)_prof_Malloc(snymCapacity, 649 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 OLD_DEBUG( 668 if (debug_value & 02) 669 Fprint(stderr, "%-8.8s: %#8o\n", slp->sl_name, slp->sl_addr) 670 ); 671 672 slp++; 673 --n; 674 } 675 } 676 /* 677 * 678 * Now attempt to match call counts with symbols. To do this, it 679 * helps to first sort both the symbols and the call address/count 680 * pairs by ascending address, since they are generally not, to 681 * begin with. The addresses associated with the counts are not, 682 * of course, the subroutine addresses associated with the symbols, 683 * but some address slightly past these. Therefore a given count 684 * address (in the fnpc field) is matched with the closest symbol 685 * address (sl_addr) that is: 686 * (1) less than the fnpc value but, 687 * (2) not more than the length of the function 688 * In other words, unreasonable matchups are avoided. 689 * Situations such as this could arise when static procedures are 690 * counted but the "-g" option was not given to this program, 691 * causing the symbol to fail to qualify. Without this limitation, 692 * unmatched counts could be erroneously charged. 693 * 694 */ 695 696 697 ccp = ccounts; /* Point to first call counter. */ 698 slp = slist; /* " " " symbol. */ 699 /* Sort call counters and ... */ 700 qsort((char *)ccp, (unsigned)n_cc, sizeof (struct cnt), c_ccaddr); 701 /* symbols by increasing address. */ 702 qsort((char *)slp, (unsigned)n_syms, sizeof (struct slist), c_sladdr); 703 vn_cc = n_cc; /* save this for verbose option */ 704 705 706 /* Loop to match up call counts & symbols. */ 707 for (n = n_syms; n > 0 && vn_cc > 0; ) { 708 int sz = slp->sl_size; 709 710 if (sz == 0) 711 sz = slp[ 1 ].sl_addr - slp->sl_addr; 712 if (slp->sl_addr < ccp->fnpc && 713 ccp->fnpc <= slp->sl_addr + sz) { 714 /* got a candidate: find Closest. */ 715 struct slist *closest_symp; 716 do { 717 closest_symp = slp; 718 slp++; 719 --n; 720 } while (n > 0 && slp->sl_addr < ccp->fnpc); 721 722 OLD_DEBUG( 723 if (debug_value & 04) { 724 Fprint(stderr, 725 "Routine %-8.8s @ %#8x+%-2d matches count address %#8x\n", 726 closest_symp->sl_name, 727 closest_symp->sl_addr, 728 ccp->fnpc-slp->sl_addr, 729 ccp->fnpc); 730 } 731 ); 732 closest_symp->sl_count = ccp->mcnt; /* Copy count. */ 733 ++ccp; 734 --vn_cc; 735 } else if (ccp->fnpc < slp->sl_addr) { 736 ++ccp; 737 --vn_cc; 738 } else { 739 ++slp; 740 --n; 741 } 742 } 743 744 /* 745 * 746 * The distribution of times to addresses is done on a proportional 747 * basis as follows: The t counts in pcounts[i] correspond to clock 748 * ticks for values of pc in the range pc, pc+1, ..., pc+s_inv-1 749 * (odd addresses excluded for PDP11s). Without more detailed info, 750 * it must be assumed that there is no greater probability 751 * of the clock ticking for any particular pc in this range than for 752 * any other. Thus the t counts are considered to be equally 753 * distributed over the addresses in the range, and that the time for 754 * any given address in the range is pcounts[i]/s_inv. 755 * 756 * The values of the symbols that qualify, bounded below and above 757 * by pc_l and pc_h, respectively, partition the profiling range into 758 * regions to which are assigned the total times associated with the 759 * addresses they contain in the following way: 760 * 761 * The sum of all pcounts[i] for which the corresponding addresses are 762 * wholly within the partition are charged to the partition (the 763 * subroutine whose address is the lower bound of the partition). 764 * 765 * If the range of addresses corresponding to a given t = pcounts[i] 766 * lies astraddle the boundary of a partition, e.g., for some k such 767 * that 0 < k < s_inv-1, the addresses pc, pc+1, ..., pc+k-1 are in 768 * the lower partition, and the addresses pc+k, pc+k+1, ..., pc+s_inv-1 769 * are in the next partition, then k*pcounts[i]/s_inv time is charged 770 * to the lower partition, and (s_inv-k) * pcounts[i]/s_inv time to the 771 * upper. It is conceivable, in cases of large granularity or small 772 * subroutines, for a range corresponding to a given pcounts[i] to 773 * overlap three regions, completely containing the (small) middle one. 774 * The algorithm is adjusted appropriately in this case. 775 * 776 */ 777 778 779 pcp = pcounts; /* Reset to base. */ 780 slp = slist; /* Ditto. */ 781 t0 = 0.0; /* Time accumulator. */ 782 for (n = 0; n < n_syms; n++) { /* Loop on symbols. */ 783 /* Start addr of region, low addr of overlap. */ 784 char *pc0, *pc00; 785 /* Start addr of next region, low addr of overlap. */ 786 char *pc1, *pc10; 787 /* First index into pcounts for this region and next region. */ 788 register int i0, i1; 789 long ticks; 790 791 /* Address of symbol (subroutine). */ 792 pc0 = slp[n].sl_addr; 793 794 /* Address of next symbol, if any or top */ 795 /* of profile range, if not */ 796 pc1 = (n < n_syms - 1) ? slp[n+1].sl_addr : pc_h; 797 798 /* Lower bound of indices into pcounts for this range */ 799 800 i0 = (((unsigned)pc0 - (unsigned)pc_l) * sf)/bias; 801 802 /* Upper bound (least or least + 1) of indices. */ 803 i1 = (((unsigned)pc1 - (unsigned)pc_l) * sf)/bias; 804 805 if (i1 >= n_pc) /* If past top, */ 806 i1 = n_pc - 1; /* adjust. */ 807 808 /* Lowest addr for which count maps to pcounts[i0]; */ 809 pc00 = pc_l + (unsigned long)((bias * i0)/sf); 810 811 /* Lowest addr for which count maps to pcounts[i1]. */ 812 pc10 = pc_l + (unsigned long)((bias * i1)/sf); 813 814 OLD_DEBUG(if (debug_value & 010) Fprint(stderr, 815 "%-8.8s\ti0 = %4d, pc00 = %#6o, pc0 = %#6o\n\ 816 \t\ti1 = %4d, pc10 = %#6o, pc1 = %#6o\n\t\t", 817 slp[n].sl_name, i0, pc00, pc0, i1, pc10, pc1)); 818 t = 0; /* Init time for this symbol. */ 819 if (i0 == i1) { 820 /* Counter overlaps two areas? (unlikely */ 821 /* unless large granularity). */ 822 ticks = pcp[i0]; /* # Times (clock ticks). */ 823 OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks)); 824 825 /* Time less that which overlaps adjacent areas */ 826 t += PROFSEC(ticks * ((double)(pc1 - pc0) * sf)/bias); 827 828 OLD_DEBUG(if (debug_value & 010) 829 Fprint(stderr, "%ld/(%.1f)", (pc1 - pc0) * ticks, DBL_ADDRPERCELL) 830 ); 831 } else { 832 /* Overlap with previous region? */ 833 if (pc00 < pc0) { 834 ticks = pcp[i0]; 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 OLD_DEBUG(if (debug_value & 010) 847 Fprint(stderr, "%ld/(%.1f) + ", (pc0 - pc00) * ticks, 848 DBL_ADDRPERCELL)); 849 } 850 851 /* Init sum of counts for PCs not shared w/other */ 852 /* routines. */ 853 ticks = 0; 854 855 /* Stop at first count that overlaps following */ 856 /* routine. */ 857 for (i = i0; i < i1; i++) 858 ticks += pcp[i]; 859 860 t += PROFSEC(ticks); /* Convert to secs, add to total */ 861 OLD_DEBUG(if (debug_value & 010) Fprint(stderr, "%ld", ticks)); 862 /* Some overlap with low addresses of next routine? */ 863 if (pc10 < pc1) { 864 /* Yes. Get total count ... */ 865 ticks = pcp[i1]; 866 867 /* and accumulate proportion for addresses in */ 868 /* range of this routine */ 869 t += PROFSEC(((double)ticks * 870 (pc1 - pc10)*sf)/bias); 871 OLD_DEBUG(if (debug_value & 010) fprintf(stderr, "ticks = %d\n", ticks)); 872 OLD_DEBUG(if (debug_value & 010) 873 Fprint(stderr, " + %ld/(%.1f)", (pc1 - pc10) * ticks, DBL_ADDRPERCELL) 874 ); 875 } 876 } /* End if (i0 == i1) ... else ... */ 877 878 slp[n].sl_time = t; /* Store time for this routine. */ 879 t0 += t; /* Accumulate total time. */ 880 OLD_DEBUG(if (debug_value & 010) Fprint(stderr, " ticks = %.2f msec\n", t)); 881 } /* End for (n = 0; n < n_syms; n++) */ 882 883 /* Final pass to total up time. */ 884 /* Sum ticks, then convert to seconds. */ 885 886 for (n = n_pc, temp = 0; --n >= 0; temp += *(pcp++)); 887 888 t_tot = PROFSEC(temp); 889 890 /* 891 * Now, whilst we still have the symbols sorted 892 * in address order.. 893 * Loop to record duplicates, so we can display 894 * synonym symbols correctly. 895 * Synonym symbols, or symbols with the same address, 896 * are to be displayed by prof on the same line, with 897 * one statistics line, as below: 898 * ... 255 ldaopen, ldaopen 899 * The way this will be implemented, is as follows: 900 * 901 * Pass 1 - while the symbols are in address order, we 902 * do a pre-pass through them, to determine for which 903 * addresses there are more than one symbol (i.e. synonyms). 904 * During this prepass we collect summary statistics in 905 * the synonym entry, for all the synonyms. 906 * 907 * 'Pass' 2 - while printing a report, for each report line, 908 * if the current symbol is a synonym symbol (i.e. in the 909 * snymList) then we scan forward and pick up all the names 910 * which map to this address, and print them too. 911 * If the address' synonyms have already been printed, then 912 * we just skip this symbol and go on to process the next. 913 * 914 */ 915 916 { 917 /* pass 1 */ 918 char *thisaddr; 919 char *lastaddr = slist->sl_addr; /* use 1st sym as */ 920 /* 'last/prior symbol' */ 921 int lastWasSnym = 0; /* 1st can't be snym yet-no aliases seen! */ 922 int thisIsSnym; 923 924 OLD_DEBUG( 925 int totsnyms = 0; int totseries = 0; struct slist *lastslp = slist; 926 ); 927 928 /* NB loop starts with 2nd symbol, loops over n_syms-1 symbols! */ 929 for (n = n_syms-1, slp = slist+1; --n >= 0; slp++) { 930 thisaddr = slp->sl_addr; 931 thisIsSnym = (thisaddr == lastaddr); 932 933 if (thisIsSnym) { 934 /* gotta synonym */ 935 if (!lastWasSnym) { 936 OLD_DEBUG( 937 if (debug_value) { 938 Fprint(stderr, 939 "Synonym series:\n1st->\t%s at address %x, ct=%ld, time=%f\n", 940 lastslp->sl_name, lastaddr, lastslp->sl_count, 941 lastslp->sl_time); 942 totseries++; 943 totsnyms++; 944 } 945 ); 946 /* this is the Second! of a series */ 947 snymp = (n_snyms++ == 0 ? snymList : snymp+1); 948 snymp->howMany = 1; /* gotta count 1st one!! */ 949 snymp->sym_addr = slp->sl_addr; 950 /* zero summary statistics */ 951 snymp->tot_sl_count = 0; 952 snymp->tot_sl_time = 0.0; 953 /* Offen the Reported flag */ 954 snymp->snymReported = 0; 955 } 956 OLD_DEBUG( 957 if (debug_value) { 958 Fprint(stderr, 959 "\t%s at address %x, ct=%ld, time=%f\n", 960 slp->sl_name, 961 thisaddr, 962 slp->sl_count, 963 slp->sl_time); 964 totsnyms++; 965 } 966 ); 967 /* ok - bump count for snym, and note its Finding */ 968 snymp->howMany++; 969 /* and update the summary statistics */ 970 snymp->tot_sl_count += slp->sl_count; 971 snymp->tot_sl_time += slp->sl_time; 972 } 973 callTotal += slp->sl_count; 974 lastaddr = thisaddr; 975 lastWasSnym = thisIsSnym; 976 OLD_DEBUG( 977 if (debug_value) lastslp = slp; 978 ); 979 980 } 981 OLD_DEBUG( 982 if (debug_value) { 983 Fprint(stderr, "Total #series %d, #synonyms %d\n", totseries, totsnyms); 984 } 985 ); 986 } 987 /* 988 * Most of the heavy work is done now. Only minor stuff remains. 989 * The symbols are currently in address order and must be re-sorted 990 * if desired in a different order. Report generating options 991 * include "-o" or "-x": Include symbol address, which causes 992 * another column 993 * in the output; and "-z": Include symbols in report even if zero 994 * time and call count. Symbols not in profiling range are excluded 995 * in any case. Following the main body of the report, the "-s" 996 * option causes certain additional information to be printed. 997 */ 998 999 OLD_DEBUG(if (debug_value) Fprint(stderr, 1000 "Time unaccounted for: %.7G\n", t_tot - t0)); 1001 1002 if (sort) /* If comparison routine given then use it. */ 1003 qsort((char *)slist, (unsigned)n_syms, 1004 sizeof (struct slist), sort); 1005 1006 if (!(flags & F_NHEAD)) { 1007 if (flags & F_PADDR) 1008 Print(atitle); /* Title for addresses. */ 1009 (void) puts(" %Time Seconds Cumsecs #Calls msec/call Name"); 1010 } 1011 t = 0.0; /* Init cumulative time. */ 1012 if (t_tot != 0.0) /* Convert to percent. */ 1013 t_tot = 100.0/t_tot; /* Prevent divide-by-zero fault */ 1014 n_nonzero = 0; /* Number of symbols with nonzero time or # calls. */ 1015 for (n = n_syms, slp = slist; --n >= 0; slp++) { 1016 long count; /* # Calls. */ 1017 /* t0, time in seconds. */ 1018 1019 /* if a snym symbol, use summarized stats, else use indiv. */ 1020 if ((snymp = getSnymEntry(slp->sl_addr)) != 0) { 1021 count = snymp->tot_sl_count; 1022 t0 = snymp->tot_sl_time; 1023 1024 } else { 1025 count = slp->sl_count; 1026 t0 = slp->sl_time; 1027 } 1028 1029 /* if a snym and already reported, skip this entry */ 1030 if (snymp && snymp->snymReported) 1031 continue; 1032 /* Don't do entries with no action. */ 1033 if (t0 == 0.0 && count == 0 && !(flags & F_ZSYMS)) 1034 continue; 1035 if ((strcmp(slp->sl_name, "_mcount") == 0) || 1036 (strcmp(slp->sl_name, "mcount") == 0)) { 1037 count = callTotal; 1038 } 1039 1040 /* count number of entries (i.e. symbols) printed */ 1041 if (snymp) 1042 n_nonzero += snymp->howMany; /* add for each snym */ 1043 else 1044 n_nonzero++; 1045 1046 if (flags & F_PADDR) /* Printing address of symbol? */ 1047 Print(aformat, slp->sl_addr); 1048 t += t0; /* move here; compiler bug !! */ 1049 Print("%6.1f%8.2f%8.2f", t0 * t_tot, t0, t); 1050 fdigits = 0; 1051 if (count) { /* Any calls recorded? */ 1052 /* Get reasonable number of fractional digits to print. */ 1053 fdigits = fprecision(count); 1054 Print("%8ld%#*.*f", count, fdigits+8, fdigits, 1055 1000.0*t0/count); 1056 Print("%*s", 6-fdigits, " "); 1057 } else { 1058 Print("%22s", " "); 1059 } 1060 /* 1061 * now print the name (or comma-seperate list of names, 1062 * for synonym symbols). 1063 */ 1064 if (snymp) { 1065 printSnymNames(slp, snymp); /* print it, then */ 1066 snymp->snymReported = 1; /* mark it Done */ 1067 } 1068 else 1069 (void) puts(slp->sl_name); /* print the one name */ 1070 } 1071 if (flags & F_VERBOSE) { /* Extra info? */ 1072 Fprint(stderr, "%5d/%d call counts used\n", n_cc, head.nfns); 1073 Fprint(stderr, "%5d/%d symbols qualified", n_syms, symttl); 1074 if (n_nonzero < n_syms) 1075 Fprint(stderr, 1076 ", %d had zero time and zero call-counts\n", 1077 n_syms - n_nonzero); 1078 else 1079 (void) putc('\n', stderr); 1080 Fprint(stderr, "%#x scale factor\n", (long)sf); 1081 } 1082 1083 _symintClose(ldptr); 1084 } else { 1085 Fprint(stderr, "prof: no call counts captured\n"); 1086 } 1087 exit(0); 1088 } 1089 /* Return size of file associated with file descriptor fd. */ 1090 1091 PROC off_t 1092 fsize(fd) 1093 { 1094 struct stat sbuf; 1095 1096 if (fstat(fd, &sbuf) < 0) /* Status of open file. */ 1097 Perror("stat"); 1098 return (sbuf.st_size); /* This is a long. */ 1099 } 1100 1101 /* Read symbol entry. Return TRUE if satisfies conditions. */ 1102 1103 PROC 1104 readnl(symindex) 1105 int symindex; 1106 { 1107 nl = ldptr->pf_symarr_p[symindex]; 1108 1109 OLD_DEBUG( 1110 if (debug_value & 020) { 1111 Fprint(stderr, 1112 "`%-8.8s'\tst_info=%#4o, value=%#8.6o\n", 1113 ldptr->pf_symstr_p[nl.ps_sym.st_name], 1114 (unsigned char) nl.ps_sym.st_info, 1115 nl.ps_sym.st_value); 1116 } 1117 ); 1118 /* 1119 * TXTSYM accepts global (and local, if "-g" given) T-type symbols. 1120 * Only those in the profiling range are useful. 1121 */ 1122 return (nl.ps_sym.st_shndx < SHN_LORESERVE && 1123 TXTSYM(nl.ps_sym.st_shndx, 1124 nl.ps_sym.st_info) && 1125 (pc_l <= (char *)nl.ps_sym.st_value) && 1126 ((char *)nl.ps_sym.st_value < pc_h)); 1127 } 1128 /* 1129 * Error-checking memory allocators - 1130 * Guarantees good return (else none at all). 1131 */ 1132 1133 PROC char * 1134 _prof_Malloc(item_count, item_size) 1135 int item_count; 1136 int item_size; 1137 { 1138 register char *p; 1139 1140 if ((p = malloc((unsigned)item_count * (unsigned)item_size)) == NULL) { 1141 (void) fprintf(stderr, "%s: Out of space\n", cmdname); 1142 exit(1); 1143 } 1144 return (p); 1145 } 1146 1147 1148 1149 /* 1150 * Given the quotient Q = N/D, where entier(N) == N and D > 0, an 1151 * approximation of the "best" number of fractional digits to use 1152 * in printing Q is f = entier(log10(D)), which is crudely produced 1153 * by the following routine. 1154 */ 1155 1156 PROC int 1157 fprecision(count) 1158 long count; 1159 { 1160 return (count < 10 ? 0 : count < 100 ? 1 : count < 1000 ? 2 : 1161 count < 10000 ? 3 : 4); 1162 } 1163 1164 /* 1165 * Return pointer to base name(name less path) of string s. 1166 * Handles case of superfluous trailing '/'s, and unlikely 1167 * case of s == "/". 1168 */ 1169 1170 PROC char * 1171 basename(s) 1172 register char *s; 1173 { 1174 register char *p; 1175 1176 p = &s[strlen(s)]; /* End (+1) of string. */ 1177 while (p > s && *--p == '/') /* Trim trailing '/'s. */ 1178 *p = '\0'; 1179 p++; /* New end (+1) of string. */ 1180 while (p > s && *--p != '/'); /* Break backward on '/'. */ 1181 if (*p == '/') /* If found '/', point to 1st following. */ 1182 p++; 1183 if (*p == '\0') 1184 p = "/"; /* If NULL, must be "/". (?) */ 1185 return (p); 1186 } 1187 /* Here if unexpected read problem. */ 1188 1189 PROC 1190 eofon(iop, fn) 1191 register FILE *iop; 1192 register char *fn; 1193 { 1194 if (ferror(iop)) /* Real error? */ 1195 Perror(fn); /* Yes. */ 1196 Fprint(stderr, "%s: %s: Premature EOF\n", cmdname, fn); 1197 exit(1); 1198 } 1199 1200 /* Version of perror() that prints cmdname first. */ 1201 1202 PROC 1203 Perror(s) 1204 char *s; 1205 { /* Print system error message & exit. */ 1206 register int err = errno; /* Save current errno in case */ 1207 1208 Fprint(stderr, "%s: ", cmdname); 1209 errno = err; /* Put real error back. */ 1210 perror(s); /* Print message. */ 1211 _symintClose(ldptr); /* cleanup symbol information */ 1212 exit(1); /* Exit w/nonzero status. */ 1213 } 1214 1215 /* Here for things that "Should Never Happen". */ 1216 1217 PROC 1218 snh() 1219 { 1220 Fprint(stderr, "%s: Internal error\n", cmdname); 1221 (void) abort(); 1222 } 1223 /* 1224 * Various comparison routines for qsort. Uses: 1225 * 1226 * c_ccaddr - Compare fnpc fields of cnt structs to put 1227 * call counters in increasing address order. 1228 * c_sladdr - Sort slist structures on increasing address. 1229 * c_time - " " " " decreasing time. 1230 * c_ncalls - " " " " decreasing # calls. 1231 * c_name - " " " " increasing symbol name 1232 */ 1233 1234 #define CMP2(v1, v2) ((v1) < (v2) ? -1 : (v1) == (v2) ? 0 : 1) 1235 #define CMP1(v) CMP2(v, 0) 1236 1237 PROC 1238 c_ccaddr(p1, p2) 1239 register struct cnt *p1, *p2; 1240 { 1241 return (CMP2(p1->fnpc, p2->fnpc)); 1242 } 1243 1244 PROC 1245 c_sladdr(p1, p2) 1246 register struct slist *p1, *p2; 1247 { 1248 return (CMP2(p1->sl_addr, p2->sl_addr)); 1249 } 1250 1251 PROC 1252 c_time(p1, p2) 1253 register struct slist *p1, *p2; 1254 { 1255 register float dtime = p2->sl_time - p1->sl_time; /* Decreasing time. */ 1256 1257 return (CMP1(dtime)); 1258 } 1259 1260 PROC 1261 c_ncalls(p1, p2) 1262 register struct slist *p1, *p2; 1263 { 1264 register int diff = p2->sl_count - p1->sl_count; 1265 /* Decreasing # calls. */ 1266 return (CMP1(diff)); 1267 } 1268 1269 PROC 1270 c_name(p1, p2) 1271 register struct slist *p1, *p2; 1272 { 1273 register int diff; 1274 1275 /* flex names has variable length strings for names */ 1276 diff = strcmp(p1->sl_name, p2->sl_name); 1277 return (CMP1(diff)); 1278 } 1279 1280 static int exotic(); 1281 static void parsename(); 1282 static void parse_fn_and_print(); 1283 1284 #define STRSPACE 2400 /* guess at amount of string space */ 1285 1286 char *format_buf; 1287 #define FORMAT_BUF "%s\n\t\t\t\t\t [%s]" 1288 1289 static char * 1290 demangled_name(s) 1291 char *s; 1292 { 1293 char *name; 1294 1295 name = (char *)sgs_demangle(s); 1296 1297 if (strcmp(name, s) == 0) 1298 return (s); 1299 1300 if (format_buf != NULL) 1301 free(format_buf); 1302 1303 format_buf = (char *)malloc(strlen(name) + 1304 strlen(FORMAT_BUF) + 1305 strlen(s) + 1); 1306 if (format_buf == NULL) 1307 return (s); 1308 (void) sprintf(format_buf, FORMAT_BUF, name, s); 1309 return (format_buf); 1310 } 1311 1312 /* getname - get the name of a symbol in a permanent fashion */ 1313 char * 1314 getname(ldpter, symbol) 1315 PROF_FILE *ldpter; 1316 PROF_SYMBOL symbol; 1317 { 1318 static char *strtable = NULL; /* space for names */ 1319 static int sp_used = 0; /* space used so far */ 1320 static int size = 0; /* size of string table */ 1321 char *name, *strcpy(); /* name to store */ 1322 int lth; /* space needed for name */ 1323 int get; /* amount of space to get */ 1324 1325 name = &(ldpter->pf_symstr_p)[symbol.ps_sym.st_name]; 1326 if (name == NULL) { 1327 return ("<<bad symbol name>>"); 1328 } 1329 1330 if (Cflag) 1331 name = (char *)demangled_name(name); 1332 1333 lth = strlen(name) + 1; 1334 if ((sp_used + lth) > size) { /* if need more space */ 1335 /* just in case very long name */ 1336 get = lth > STRSPACE ? lth : STRSPACE; 1337 strtable = _prof_Malloc(1, get); 1338 size = get; 1339 sp_used = 0; 1340 } 1341 (void) strcpy(&(strtable[sp_used]), name); 1342 name = &(strtable[sp_used]); 1343 sp_used += lth; 1344 return (name); 1345 } 1346 1347 static char n_buf[512]; 1348 static char d_buf[512]; 1349 static char p_buf[512]; 1350 static char *format = "%s\n\t\t\t\t\t [%s]"; 1351 1352 1353 static char *ctor_str = "static constructor function for %s"; 1354 static char *dtor_str = "static destructor function for %s"; 1355 static char *vtbl_str = "virtual table for %s"; 1356 static char *ptbl_str = "pointer to the virtual table vector for %s"; 1357 1358 /* 1359 * Return 1 when s is an exotic name, 0 otherwise. s remains unchanged, 1360 * the exotic name, if exists, is saved in d_buf. 1361 */ 1362 int 1363 exotic(s) 1364 char *s; 1365 { 1366 int tag = 0; 1367 if (strncmp(s, "__sti__", 7) == 0) { 1368 s += 7; tag = 1; 1369 parse_fn_and_print(ctor_str, s); 1370 } else if (strncmp(s, "__std__", 7) == 0) { 1371 s += 7; tag = 1; 1372 parse_fn_and_print(dtor_str, s); 1373 } else if (strncmp(s, "__vtbl__", 8) == 0) { 1374 char *printname; 1375 s += 8; tag = 1; 1376 parsename(s); 1377 sprintf(d_buf, vtbl_str, p_buf); 1378 } else if (strncmp(s, "__ptbl_vec__", 12) == 0) { 1379 s += 12; tag = 1; 1380 parse_fn_and_print(ptbl_str, s); 1381 } 1382 return (tag); 1383 } 1384 1385 void 1386 parsename(s) 1387 char *s; 1388 { 1389 register int len; 1390 char c, *orig = s; 1391 *p_buf = '\0'; 1392 strcat(p_buf, "class "); 1393 while (isdigit(*s)) s++; 1394 c = *s; 1395 *s = '\0'; 1396 len = atoi(orig); 1397 *s = c; 1398 if (*(s+len) == '\0') { /* only one class name */ 1399 strcat(p_buf, s); 1400 return; 1401 } else { /* two classname %drootname__%dchildname */ 1402 char *root, *child, *child_len_p; 1403 int child_len; 1404 root = s; 1405 child = s + len + 2; 1406 child_len_p = child; 1407 if (! isdigit(*child)) { /* ptbl file name */ 1408 /* %drootname__%filename */ 1409 char *p; /* kludge for getting rid of '_' in file name */ 1410 c = *(root + len); 1411 *(root + len) = '\0'; 1412 strcat(p_buf, root); 1413 *(root + len) = c; 1414 strcat(p_buf, " in "); 1415 for (p = child; *p != '_'; ++p); 1416 c = *p; 1417 *p = '.'; 1418 strcat(p_buf, child); 1419 *p = c; 1420 return; 1421 } 1422 1423 while (isdigit(*child)) child++; 1424 c = *child; 1425 *child = '\0'; 1426 child_len = atoi(child_len_p); 1427 *child = c; 1428 if (*(child + child_len) == '\0') { 1429 strcat(p_buf, child); 1430 strcat(p_buf, " derived from "); 1431 c = *(root + len); 1432 *(root + len) = '\0'; 1433 strcat(p_buf, root); 1434 *(root + len) = c; 1435 return; 1436 } else { /* %drootname__%dchildname__filename */ 1437 char *p; 1438 /* kludge for getting rid of '_' in file name */ 1439 c = *(child + child_len); 1440 *(child + child_len) = '\0'; 1441 strcat(p_buf, child); 1442 *(child+child_len) = c; 1443 strcat(p_buf, " derived from "); 1444 c = *(root + len); 1445 *(root + len) = '\0'; 1446 strcat(p_buf, root); 1447 *(root + len) = c; 1448 strcat(p_buf, " in "); 1449 for (p = child+child_len+2; *p != '_'; ++p); 1450 c = *p; 1451 *p = '.'; 1452 strcat(p_buf, child + child_len + 2); 1453 *p = c; 1454 return; 1455 } 1456 } 1457 } 1458 1459 void 1460 parse_fn_and_print(str, s) 1461 char *str, *s; 1462 { 1463 char c, *p1, *p2; 1464 int yes = 1; 1465 1466 if ((p1 = p2 = strstr(s, "_c_")) == NULL) 1467 if ((p1 = p2 = strstr(s, "_C_")) == NULL) 1468 if ((p1 = p2 = strstr(s, "_cc_")) == NULL) 1469 if ((p1 = p2 = strstr(s, "_cxx_")) == NULL) 1470 if ((p1 = p2 = strstr(s, "_h_")) 1471 == NULL) 1472 yes = 0; 1473 else 1474 p2 += 2; 1475 else 1476 p2 += 4; 1477 else 1478 p2 += 3; 1479 else 1480 p2 += 2; 1481 else 1482 p2 += 2; 1483 1484 if (yes) { 1485 *p1 = '.'; 1486 c = *p2; 1487 *p2 = '\0'; 1488 } 1489 1490 for (s = p1; *s != '_'; --s); 1491 ++s; 1492 sprintf(d_buf, str, s); 1493 1494 if (yes) { 1495 *p1 = '_'; 1496 *p2 = c; 1497 } 1498 } 1499