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