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
cmp_by_name(const void * arg1,const void * arg2)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
setup_demangled_names(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
cmp_by_time(const void * arg1,const void * arg2)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
cmp_by_ncalls(const void * arg1,const void * arg2)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
print_profile_data(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
name_cmp(const void * arg1,const void * arg2)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
check_dupnames(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
compute_times(nltype * nl,profrec_t * psym)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
collect_profsyms(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
assign_pcsamples(mod_info_t * module,Address * pcsmpl,size_t n_samples)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
pc_cmp(const void * arg1,const void * arg2)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
process_pcsamples(ProfBuffer * bufp)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
process_cgraph(ProfCallGraph * cgp)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 *
get_shobj_syms(char * pathname,GElf_Addr ld_base,GElf_Addr ld_end)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
does_overlap(ProfModule * new,mod_info_t * old)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
is_same_as_aout(char * modpath,struct stat * buf)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
process_modules(ProfModuleList * modlp)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
process_mon_out(caddr_t memp,size_t fsz)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
get_aout_syms(char * pathname,mod_info_t * mi)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
profver(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 }
915