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