xref: /illumos-gate/usr/src/cmd/sgs/prof/common/profv.c (revision b9bd317c)
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 }
915