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