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