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 }