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