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