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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * All routines in this file are for processing new-style, *versioned* 31 * mon.out format. Together with rdelf.c, lookup.c and profv.h, these 32 * form the complete set of files to profile new-style mon.out files. 33 */ 34 35 #include <stdlib.h> 36 #include <string.h> 37 #include "conv.h" 38 #include "profv.h" 39 40 bool time_in_ticks = FALSE; 41 size_t n_pcsamples, n_accounted_ticks, n_zeros, total_funcs; 42 unsigned char sort_flag; 43 44 mod_info_t modules; 45 size_t n_modules = 1; /* always include the aout object */ 46 47 struct stat aout_stat, monout_stat; 48 profrec_t *profsym; 49 50 int 51 cmp_by_name(const void *arg1, const void *arg2) 52 { 53 profrec_t *a = (profrec_t *)arg1; 54 profrec_t *b = (profrec_t *)arg2; 55 56 return (strcmp(a->demangled_name, b->demangled_name)); 57 } 58 59 static void 60 setup_demangled_names(void) 61 { 62 const char *p; 63 char *nbp, *nbe, *namebuf; 64 size_t cur_len = 0, namebuf_sz = BUCKET_SZ; 65 size_t i, namelen; 66 67 if ((namebuf = malloc(namebuf_sz)) == NULL) { 68 (void) fprintf(stderr, "%s: can't allocate %d bytes\n", 69 cmdname, namebuf_sz); 70 exit(ERR_MEMORY); 71 } 72 73 nbp = namebuf; 74 nbe = namebuf + namebuf_sz; 75 76 for (i = 0; i < total_funcs; i++) { 77 if ((p = conv_demangle_name(profsym[i].name)) == NULL) 78 continue; 79 80 namelen = strlen(p); 81 if ((nbp + namelen + 1) > nbe) { 82 namebuf_sz += BUCKET_SZ; 83 namebuf = realloc(namebuf, namebuf_sz); 84 if (namebuf == NULL) { 85 (void) fprintf(stderr, 86 "%s: can't alloc %d bytes\n", 87 cmdname, BUCKET_SZ); 88 exit(ERR_MEMORY); 89 } 90 91 nbp = namebuf + cur_len; 92 nbe = namebuf + namebuf_sz; 93 } 94 95 (void) strcpy(nbp, p); 96 profsym[i].demangled_name = nbp; 97 98 nbp += namelen + 1; 99 cur_len += namelen + 1; 100 } 101 } 102 103 int 104 cmp_by_time(const void *arg1, const void *arg2) 105 { 106 profrec_t *a = (profrec_t *)arg1; 107 profrec_t *b = (profrec_t *)arg2; 108 109 if (a->percent_time > b->percent_time) 110 return (-1); 111 else if (a->percent_time < b->percent_time) 112 return (1); 113 else 114 return (0); 115 } 116 117 int 118 cmp_by_ncalls(const void *arg1, const void *arg2) 119 { 120 profrec_t *a = (profrec_t *)arg1; 121 profrec_t *b = (profrec_t *)arg2; 122 123 if (a->ncalls > b->ncalls) 124 return (-1); 125 else if (a->ncalls < b->ncalls) 126 return (1); 127 else 128 return (0); 129 130 } 131 132 static void 133 print_profile_data(void) 134 { 135 int i; 136 int (*sort_func)(const void *, const void *); 137 mod_info_t *mi; 138 double cumsecs = 0; 139 char filler[20]; 140 141 /* 142 * Sort the compiled data; the sort flags are mutually exclusive. 143 */ 144 switch (sort_flag) { 145 case BY_NCALLS: 146 sort_func = cmp_by_ncalls; 147 break; 148 149 case BY_NAME: 150 if (Cflag) 151 setup_demangled_names(); 152 sort_func = cmp_by_name; 153 break; 154 155 case BY_ADDRESS: 156 sort_flag |= BY_ADDRESS; 157 sort_func = NULL; /* already sorted by addr */ 158 break; 159 160 case BY_TIME: /* default is to sort by time */ 161 default: 162 sort_func = cmp_by_time; 163 } 164 165 166 if (sort_func) { 167 qsort(profsym, total_funcs, sizeof (profrec_t), sort_func); 168 } 169 170 /* 171 * If we're sorting by name, and if it is a verbose print, we wouldn't 172 * have set up the print_mid fields yet. 173 */ 174 if ((flags & F_VERBOSE) && (sort_flag == BY_NAME)) { 175 for (i = 0; i < total_funcs; i++) { 176 /* 177 * same as previous or next (if there's one) ? 178 */ 179 if (i && (strcmp(profsym[i].demangled_name, 180 profsym[i-1].demangled_name) == 0)) { 181 profsym[i].print_mid = TRUE; 182 } else if ((i < (total_funcs - 1)) && 183 (strcmp(profsym[i].demangled_name, 184 profsym[i+1].demangled_name) == 0)) { 185 profsym[i].print_mid = TRUE; 186 } 187 } 188 } 189 190 /* 191 * The actual printing part. 192 */ 193 if (!(flags & F_NHEAD)) { 194 if (flags & F_PADDR) 195 (void) printf(" %s", atitle); 196 197 if (time_in_ticks) 198 (void) puts( 199 " %Time Tiks Cumtiks #Calls tiks/call Name"); 200 else 201 (void) puts( 202 " %Time Seconds Cumsecs #Calls msec/call Name"); 203 } 204 205 mi = NULL; 206 for (i = 0; i < total_funcs; i++) { 207 /* 208 * Since the same value may denote different symbols in 209 * different shared objects, it is debatable if it is 210 * meaningful to print addresses at all. Especially so 211 * if we were asked to sort by symbol addresses. 212 * 213 * If we've to sort by address, I think it is better to sort 214 * it on a per-module basis and if verbose mode is on too, 215 * print a newline to separate out modules. 216 */ 217 if ((flags & F_VERBOSE) && (sort_flag == BY_ADDRESS)) { 218 if (mi != profsym[i].module) { 219 (void) printf("\n"); 220 mi = profsym[i].module; 221 } 222 } 223 224 if (flags & F_PADDR) { 225 if (aformat[2] == 'x') 226 (void) printf("%16llx ", profsym[i].addr); 227 else 228 (void) printf("%16llo ", profsym[i].addr); 229 } 230 231 cumsecs += profsym[i].seconds; 232 (void) printf("%6.1f%8.2f%8.2f", profsym[i].percent_time, 233 profsym[i].seconds, cumsecs); 234 235 (void) printf("%8d%12.4f ", 236 profsym[i].ncalls, profsym[i].msecs_per_call); 237 238 if (profsym[i].print_mid) 239 (void) printf("%d:", (profsym[i].module)->id); 240 241 (void) printf("%s\n", profsym[i].demangled_name); 242 } 243 244 if (flags & F_PADDR) 245 (void) sprintf(filler, "%16s", ""); 246 else 247 filler[0] = 0; 248 249 if (flags & F_VERBOSE) { 250 (void) puts("\n"); 251 (void) printf("%s Total Object Modules %7d\n", 252 filler, n_modules); 253 (void) printf("%s Qualified Symbols %7d\n", 254 filler, total_funcs); 255 (void) printf("%s Symbols with zero usage %7d\n", 256 filler, n_zeros); 257 (void) printf("%s Total pc-hits %7d\n", 258 filler, n_pcsamples); 259 (void) printf("%s Accounted pc-hits %7d\n", 260 filler, n_accounted_ticks); 261 if ((!gflag) && (n_pcsamples - n_accounted_ticks)) { 262 (void) printf("%s Missed pc-hits (try -g) %7d\n\n", 263 filler, n_pcsamples - n_accounted_ticks); 264 } else { 265 (void) printf("%s Missed pc-hits %7d\n\n", 266 filler, n_pcsamples - n_accounted_ticks); 267 } 268 (void) printf("%s Module info\n", filler); 269 for (mi = &modules; mi; mi = mi->next) 270 (void) printf("%s %d: `%s'\n", filler, 271 mi->id, mi->path); 272 } 273 } 274 275 int 276 name_cmp(const void *arg1, const void *arg2) 277 { 278 profnames_t *a = (profnames_t *)arg1; 279 profnames_t *b = (profnames_t *)arg2; 280 281 return (strcmp(a->name, b->name)); 282 } 283 284 static void 285 check_dupnames(void) 286 { 287 int i; 288 profnames_t *pn; 289 290 pn = calloc(total_funcs, sizeof (profnames_t)); 291 if (pn == NULL) { 292 (void) fprintf(stderr, "%s: no room for %d bytes\n", 293 cmdname, total_funcs * sizeof (profnames_t)); 294 exit(ERR_MEMORY); 295 } 296 297 for (i = 0; i < total_funcs; i++) { 298 pn[i].name = profsym[i].demangled_name; 299 pn[i].pfrec = &profsym[i]; 300 } 301 302 qsort(pn, total_funcs, sizeof (profnames_t), name_cmp); 303 304 for (i = 0; i < total_funcs; i++) { 305 /* 306 * same as previous or next (if there's one) ? 307 */ 308 if (i && (strcmp(pn[i].name, pn[i-1].name) == 0)) 309 (pn[i].pfrec)->print_mid = TRUE; 310 else if ((i < (total_funcs - 1)) && 311 (strcmp(pn[i].name, pn[i+1].name) == 0)) { 312 (pn[i].pfrec)->print_mid = TRUE; 313 } 314 } 315 316 free(pn); 317 } 318 319 static void 320 compute_times(nltype *nl, profrec_t *psym) 321 { 322 static int first_time = TRUE; 323 static long hz; 324 325 if (first_time) { 326 if ((hz = sysconf(_SC_CLK_TCK)) == -1) 327 time_in_ticks = TRUE; 328 first_time = FALSE; 329 } 330 331 if (time_in_ticks) { 332 psym->seconds = (double)nl->nticks; 333 if (nl->ncalls) { 334 psym->msecs_per_call = (double)nl->nticks / 335 (double)nl->ncalls; 336 } else 337 psym->msecs_per_call = (double)0.0; 338 } else { 339 psym->seconds = (double)nl->nticks / (double)hz; 340 if (nl->ncalls) { 341 psym->msecs_per_call = 342 ((double)psym->seconds * 1000.0) / 343 (double)nl->ncalls; 344 } else 345 psym->msecs_per_call = (double)0.0; 346 } 347 348 if (n_pcsamples) { 349 psym->percent_time = 350 ((double)nl->nticks / (double)n_pcsamples) * 100; 351 } 352 } 353 354 static void 355 collect_profsyms(void) 356 { 357 mod_info_t *mi; 358 nltype *nl; 359 size_t i, ndx; 360 361 362 for (mi = &modules; mi; mi = mi->next) 363 total_funcs += mi->nfuncs; 364 365 profsym = calloc(total_funcs, sizeof (profrec_t)); 366 if (profsym == NULL) { 367 (void) fprintf(stderr, "%s: no room for %d bytes\n", 368 cmdname, total_funcs * sizeof (profrec_t)); 369 exit(ERR_MEMORY); 370 } 371 372 ndx = 0; 373 for (mi = &modules; mi; mi = mi->next) { 374 nl = mi->nl; 375 for (i = 0; i < mi->nfuncs; i++) { 376 /* 377 * I think F_ZSYMS doesn't make sense for the new 378 * mon.out format, since we don't have a profiling 379 * *range*, per se. But the man page demands it, 380 * so... 381 */ 382 if ((nl[i].ncalls == 0) && (nl[i].nticks == 0)) { 383 n_zeros++; 384 if (!(flags & F_ZSYMS)) 385 continue; 386 } 387 388 /* 389 * Initially, we set demangled_name to be 390 * the same as name. If Cflag is set, we later 391 * change this to be the demangled name ptr. 392 */ 393 profsym[ndx].addr = nl[i].value; 394 profsym[ndx].ncalls = nl[i].ncalls; 395 profsym[ndx].name = nl[i].name; 396 profsym[ndx].demangled_name = nl[i].name; 397 profsym[ndx].module = mi; 398 profsym[ndx].print_mid = FALSE; 399 compute_times(&nl[i], &profsym[ndx]); 400 ndx++; 401 } 402 } 403 404 /* 405 * Adjust total_funcs to actual printable funcs 406 */ 407 total_funcs = ndx; 408 } 409 410 static void 411 assign_pcsamples(mod_info_t *module, Address *pcsmpl, 412 size_t n_samples) 413 { 414 Address *pcptr, *pcse = pcsmpl + n_samples; 415 Address nxt_func; 416 nltype *nl; 417 size_t nticks; 418 419 /* Locate the first pc-hit for this module */ 420 if ((pcptr = locate(pcsmpl, n_samples, module->load_base)) == NULL) 421 return; /* no pc-hits in this module */ 422 423 /* Assign all pc-hits in this module to appropriate functions */ 424 while ((pcptr < pcse) && (*pcptr < module->load_end)) { 425 426 /* Update the corresponding function's time */ 427 if (nl = nllookup(module, *pcptr, &nxt_func)) { 428 /* 429 * Collect all pc-hits in this function. Each 430 * pc-hit counts as 1 tick. 431 */ 432 nticks = 0; 433 while ((pcptr < pcse) && (*pcptr < nxt_func)) { 434 nticks++; 435 pcptr++; 436 } 437 438 nl->nticks += nticks; 439 n_accounted_ticks += nticks; 440 } else { 441 /* 442 * pc sample could not be assigned to function; 443 * probably in a PLT 444 */ 445 pcptr++; 446 } 447 } 448 } 449 450 static int 451 pc_cmp(const void *arg1, const void *arg2) 452 { 453 Address *pc1 = (Address *)arg1; 454 Address *pc2 = (Address *)arg2; 455 456 if (*pc1 > *pc2) 457 return (1); 458 459 if (*pc1 < *pc2) 460 return (-1); 461 462 return (0); 463 } 464 465 static void 466 process_pcsamples(ProfBuffer *bufp) 467 { 468 Address *pc_samples; 469 mod_info_t *mi; 470 size_t nelem = bufp->bufsize; 471 472 /* buffer with no pc samples ? */ 473 if (nelem == 0) 474 return; 475 476 /* Allocate for the pcsample chunk */ 477 pc_samples = (Address *) calloc(nelem, sizeof (Address)); 478 if (pc_samples == NULL) { 479 (void) fprintf(stderr, "%s: no room for %d sample pc's\n", 480 cmdname, nelem); 481 exit(ERR_MEMORY); 482 } 483 484 (void) memcpy(pc_samples, (caddr_t)bufp + bufp->buffer, 485 nelem * sizeof (Address)); 486 487 /* Sort the pc samples */ 488 qsort(pc_samples, nelem, sizeof (Address), pc_cmp); 489 490 /* 491 * Assign pcsamples to functions in the currently active 492 * module list 493 */ 494 for (mi = &modules; mi; mi = mi->next) { 495 if (mi->active == FALSE) 496 continue; 497 assign_pcsamples(mi, pc_samples, nelem); 498 } 499 500 free(pc_samples); 501 502 /* Update total number of pcsamples read so far */ 503 n_pcsamples += nelem; 504 } 505 506 static void 507 process_cgraph(ProfCallGraph *cgp) 508 { 509 mod_info_t *mi; 510 Address f_end; 511 Index callee_off; 512 ProfFunction *calleep; 513 nltype *nl; 514 515 for (callee_off = cgp->functions; callee_off; 516 callee_off = calleep->next_to) { 517 518 /* LINTED: pointer cast */ 519 calleep = (ProfFunction *)((char *)cgp + callee_off); 520 if (calleep->count == 0) 521 continue; 522 523 /* 524 * If we cannot identify a callee with a module, we 525 * cannot get to its namelist, just skip it. 526 */ 527 for (mi = &modules; mi; mi = mi->next) { 528 if (mi->active == FALSE) 529 continue; 530 531 if (calleep->topc >= mi->load_base && 532 calleep->topc < mi->load_end) { 533 /* 534 * nllookup() returns the next lower entry 535 * point on a miss. So just make sure the 536 * callee's pc is not outside this function 537 */ 538 if (nl = nllookup(mi, calleep->topc, 0)) { 539 f_end = mi->load_base + (nl->value - 540 mi->txt_origin) + nl->size; 541 if (calleep->topc < f_end) 542 nl->ncalls += calleep->count; 543 } 544 } 545 } 546 } 547 } 548 549 static mod_info_t * 550 get_shobj_syms(char *pathname, GElf_Addr ld_base, GElf_Addr ld_end) 551 { 552 mod_info_t *mi; 553 554 /* Create a new module element */ 555 if ((mi = malloc(sizeof (mod_info_t))) == NULL) { 556 (void) fprintf(stderr, "%s: no room for %d bytes\n", 557 cmdname, sizeof (mod_info_t)); 558 exit(ERR_MEMORY); 559 } 560 561 mi->path = malloc(strlen(pathname) + 1); 562 if (mi->path == NULL) { 563 (void) fprintf(stderr, "%s: can't allocate %d bytes\n", 564 cmdname, strlen(pathname) + 1); 565 exit(ERR_MEMORY); 566 } 567 (void) strcpy(mi->path, pathname); 568 mi->next = NULL; 569 570 get_syms(pathname, mi); 571 572 /* and fill in info... */ 573 mi->id = n_modules + 1; 574 mi->load_base = ld_base; 575 mi->load_end = ld_end; 576 mi->active = TRUE; 577 578 n_modules++; 579 580 return (mi); 581 } 582 583 /* 584 * Two modules overlap each other if they don't lie completely *outside* 585 * each other. 586 */ 587 static bool 588 does_overlap(ProfModule *new, mod_info_t *old) 589 { 590 /* case 1: new module lies completely *before* the old one */ 591 if (new->startaddr < old->load_base && new->endaddr <= old->load_base) 592 return (FALSE); 593 594 /* case 2: new module lies completely *after* the old one */ 595 if (new->startaddr >= old->load_end && new->endaddr >= old->load_end) 596 return (FALSE); 597 598 /* probably a dlopen: the modules overlap each other */ 599 return (TRUE); 600 } 601 602 static bool 603 is_same_as_aout(char *modpath, struct stat *buf) 604 { 605 if (stat(modpath, buf) == -1) { 606 perror(modpath); 607 exit(ERR_SYSCALL); 608 } 609 610 if ((buf->st_dev == aout_stat.st_dev) && 611 (buf->st_ino == aout_stat.st_ino)) { 612 return (TRUE); 613 } else 614 return (FALSE); 615 } 616 617 static void 618 process_modules(ProfModuleList *modlp) 619 { 620 ProfModule *newmodp; 621 mod_info_t *mi, *last, *new_module; 622 char *so_path; 623 bool more_modules = TRUE; 624 struct stat so_statbuf; 625 626 /* Check version of module type object */ 627 if (modlp->version > PROF_MODULES_VER) { 628 (void) fprintf(stderr, 629 "%s: unsupported version %d for modules\n", 630 cmdname, modlp->version); 631 exit(ERR_INPUT); 632 } 633 634 635 /* 636 * Scan the PROF_MODULES_T list and add modules to current list 637 * of modules, if they're not present already 638 */ 639 /* LINTED: pointer cast */ 640 newmodp = (ProfModule *)((caddr_t)modlp + modlp->modules); 641 do { 642 /* 643 * Since the aout could've been renamed after its run, we 644 * should see if current module overlaps aout. If it does, it 645 * is probably the renamed aout. We should also skip any other 646 * non-sharedobj's that we see (or should we report an error ?) 647 */ 648 so_path = (caddr_t)modlp + newmodp->path; 649 if (does_overlap(newmodp, &modules) || 650 is_same_as_aout(so_path, &so_statbuf) || 651 (!is_shared_obj(so_path))) { 652 if (!newmodp->next) 653 more_modules = FALSE; 654 655 /* LINTED: pointer cast */ 656 newmodp = (ProfModule *) 657 ((caddr_t)modlp + newmodp->next); 658 continue; 659 } 660 661 /* 662 * Check all modules (leave the first one, 'cos that 663 * is the program executable info). If this module is already 664 * there in the list, skip it. 665 */ 666 last = &modules; 667 while ((mi = last->next) != NULL) { 668 /* 669 * We expect the full pathname for all shared objects 670 * needed by the program executable. In this case, we 671 * simply need to compare the paths to see if they are 672 * the same file. 673 */ 674 if (strcmp(mi->path, so_path) == 0) 675 break; 676 677 /* 678 * Check if this new shared object will overlap any 679 * existing module. If yes, deactivate the old one. 680 */ 681 if (does_overlap(newmodp, mi)) 682 mi->active = FALSE; 683 684 last = mi; 685 } 686 687 /* Module already there, skip it */ 688 if (mi != NULL) { 689 mi->load_base = newmodp->startaddr; 690 mi->load_end = newmodp->endaddr; 691 mi->active = TRUE; 692 if (!newmodp->next) 693 more_modules = FALSE; 694 695 /* LINTED: pointer cast */ 696 newmodp = (ProfModule *) 697 ((caddr_t)modlp + newmodp->next); 698 continue; 699 } 700 701 /* 702 * Check if mon.out is outdated with respect to the new 703 * module we want to add 704 */ 705 if (monout_stat.st_mtime < so_statbuf.st_mtime) { 706 (void) fprintf(stderr, 707 "%s: newer shared obj %s outdates profile info\n", 708 cmdname, so_path); 709 exit(ERR_INPUT); 710 } 711 712 /* Create this module's nameslist */ 713 new_module = get_shobj_syms(so_path, 714 newmodp->startaddr, newmodp->endaddr); 715 716 /* Add it to the tail of active module list */ 717 last->next = new_module; 718 719 /* 720 * Move to the next module in the PROF_MODULES_T list 721 * (if present) 722 */ 723 if (!newmodp->next) 724 more_modules = FALSE; 725 726 /* LINTED: pointer cast */ 727 newmodp = (ProfModule *)((caddr_t)modlp + newmodp->next); 728 729 } while (more_modules); 730 } 731 732 static void 733 process_mon_out(caddr_t memp, size_t fsz) 734 { 735 ProfObject *objp; 736 caddr_t file_end; 737 bool found_pcsamples = FALSE, found_cgraph = FALSE; 738 739 /* 740 * Save file end pointer and start after header 741 */ 742 file_end = memp + fsz; 743 /* LINTED: pointer cast */ 744 objp = (ProfObject *)(memp + ((ProfHeader *)memp)->size); 745 while ((caddr_t)objp < file_end) { 746 switch (objp->type) { 747 case PROF_MODULES_T : 748 process_modules((ProfModuleList *)objp); 749 break; 750 751 case PROF_CALLGRAPH_T : 752 process_cgraph((ProfCallGraph *)objp); 753 found_cgraph = TRUE; 754 break; 755 756 case PROF_BUFFER_T : 757 process_pcsamples((ProfBuffer *)objp); 758 found_pcsamples = TRUE; 759 break; 760 761 default : 762 (void) fprintf(stderr, 763 "%s: unknown prof object type=%d\n", 764 cmdname, objp->type); 765 exit(ERR_INPUT); 766 } 767 /* LINTED: pointer cast */ 768 objp = (ProfObject *)((caddr_t)objp + objp->size); 769 } 770 771 if (!found_cgraph || !found_pcsamples) { 772 (void) fprintf(stderr, 773 "%s: missing callgraph/pcsamples in `%s'\n", 774 cmdname, mon_fn); 775 exit(ERR_INPUT); 776 } 777 778 if ((caddr_t)objp > file_end) { 779 (void) fprintf(stderr, "%s: malformed file `%s'\n", 780 cmdname, mon_fn); 781 exit(ERR_INPUT); 782 } 783 } 784 785 static void 786 get_aout_syms(char *pathname, mod_info_t *mi) 787 { 788 mi->path = malloc(strlen(pathname) + 1); 789 if (mi->path == NULL) { 790 (void) fprintf(stderr, "%s: can't allocate %d bytes\n", 791 cmdname, strlen(pathname) + 1); 792 exit(ERR_MEMORY); 793 } 794 795 (void) strcpy(mi->path, pathname); 796 mi->next = NULL; 797 798 get_syms(pathname, mi); 799 800 mi->id = 1; 801 mi->load_base = mi->txt_origin; 802 mi->load_end = mi->data_end; 803 mi->active = TRUE; 804 } 805 806 void 807 profver(void) 808 { 809 int fd; 810 unsigned int magic_num; 811 bool invalid_version; 812 caddr_t fmem; 813 ProfHeader prof_hdr; 814 815 /* 816 * Check the magic and see if this is versioned or *old-style* 817 * mon.out. 818 */ 819 if ((fd = open(mon_fn, O_RDONLY)) == -1) { 820 perror(mon_fn); 821 exit(ERR_SYSCALL); 822 } 823 if (read(fd, (char *)&magic_num, sizeof (unsigned int)) == -1) { 824 perror("read"); 825 exit(ERR_SYSCALL); 826 } 827 if (magic_num != (unsigned int) PROF_MAGIC) { 828 (void) close(fd); 829 return; 830 } 831 832 833 834 /* 835 * Check versioning info. For now, let's say we provide 836 * backward compatibility, so we accept all older versions. 837 */ 838 (void) lseek(fd, 0L, SEEK_SET); 839 if (read(fd, (char *)&prof_hdr, sizeof (ProfHeader)) == -1) { 840 perror("read"); 841 exit(ERR_SYSCALL); 842 } 843 invalid_version = FALSE; 844 if (prof_hdr.h_major_ver > PROF_MAJOR_VERSION) 845 invalid_version = TRUE; 846 else if (prof_hdr.h_major_ver == PROF_MAJOR_VERSION) { 847 if (prof_hdr.h_minor_ver > PROF_MINOR_VERSION) 848 invalid_version = FALSE; 849 } 850 if (invalid_version) { 851 (void) fprintf(stderr, 852 "%s: mon.out version %d.%d not supported\n", 853 cmdname, prof_hdr.h_major_ver, prof_hdr.h_minor_ver); 854 exit(ERR_INPUT); 855 } 856 857 858 859 /* 860 * Map mon.out onto memory. 861 */ 862 if (stat(mon_fn, &monout_stat) == -1) { 863 perror(mon_fn); 864 exit(ERR_SYSCALL); 865 } 866 if ((fmem = mmap(0, monout_stat.st_size, 867 PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { 868 perror("mmap"); 869 exit(ERR_SYSCALL); 870 } 871 (void) close(fd); 872 873 874 /* 875 * Now, read program executable's symbol table. Also save it's 876 * stat in aout_stat for use while processing mon.out 877 */ 878 if (stat(sym_fn, &aout_stat) == -1) { 879 perror(sym_fn); 880 exit(ERR_SYSCALL); 881 } 882 get_aout_syms(sym_fn, &modules); 883 884 /* 885 * Process the mon.out, all shared objects it references 886 * and collect statistics on ticks spent in each function, 887 * number of calls, etc. 888 */ 889 process_mon_out(fmem, monout_stat.st_size); 890 891 /* 892 * Based on the flags and the statistics we've got, create 893 * a list of relevant symbols whose profiling details should 894 * be printed 895 */ 896 collect_profsyms(); 897 898 /* 899 * Check for duplicate names in output. We need to print the 900 * module id's if verbose. Also, if we are sorting by name anyway, 901 * we don't need to check for duplicates here. We'll do that later. 902 */ 903 if ((flags & F_VERBOSE) && (sort_flag != BY_NAME)) 904 check_dupnames(); 905 906 /* 907 * Print output 908 */ 909 print_profile_data(); 910 911 912 (void) munmap(fmem, monout_stat.st_size); 913 exit(0); 914 } 915