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 }