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