xref: /illumos-gate/usr/src/cmd/sgs/prof/common/profv.c (revision 4226f635)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5b9bd317cSab  * Common Development and Distribution License (the "License").
6b9bd317cSab  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
2192ed1782Smike_s 
227c478bd9Sstevel@tonic-gate /*
23b9bd317cSab  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2492ed1782Smike_s  * Use is subject to license terms.
25*4226f635SJason King  *
26*4226f635SJason King  * Copyright 2018, Joyent, Inc.
277c478bd9Sstevel@tonic-gate  */
2892ed1782Smike_s 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * All routines in this file are for processing new-style, *versioned*
317c478bd9Sstevel@tonic-gate  * mon.out format. Together with rdelf.c, lookup.c and profv.h, these
327c478bd9Sstevel@tonic-gate  * form the complete set of files to profile new-style mon.out files.
337c478bd9Sstevel@tonic-gate  */
347c478bd9Sstevel@tonic-gate 
3592ed1782Smike_s #include <stdlib.h>
3692ed1782Smike_s #include <string.h>
37b9bd317cSab #include "conv.h"
387c478bd9Sstevel@tonic-gate #include "profv.h"
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate bool		time_in_ticks = FALSE;
417c478bd9Sstevel@tonic-gate size_t		n_pcsamples, n_accounted_ticks, n_zeros, total_funcs;
427c478bd9Sstevel@tonic-gate unsigned char	sort_flag;
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate mod_info_t	modules;
457c478bd9Sstevel@tonic-gate size_t		n_modules = 1;	/* always include the aout object */
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate struct stat	aout_stat, monout_stat;
487c478bd9Sstevel@tonic-gate profrec_t	*profsym;
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate int
cmp_by_name(const void * arg1,const void * arg2)5192ed1782Smike_s cmp_by_name(const void *arg1, const void *arg2)
527c478bd9Sstevel@tonic-gate {
5392ed1782Smike_s 	profrec_t *a = (profrec_t *)arg1;
5492ed1782Smike_s 	profrec_t *b = (profrec_t *)arg2;
5592ed1782Smike_s 
567c478bd9Sstevel@tonic-gate 	return (strcmp(a->demangled_name, b->demangled_name));
577c478bd9Sstevel@tonic-gate }
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate static void
setup_demangled_names(void)6092ed1782Smike_s setup_demangled_names(void)
617c478bd9Sstevel@tonic-gate {
62b9bd317cSab 	const char	*p;
63b9bd317cSab 	char	*nbp, *nbe, *namebuf;
6492ed1782Smike_s 	size_t	cur_len = 0, namebuf_sz = BUCKET_SZ;
6592ed1782Smike_s 	size_t	i, namelen;
667c478bd9Sstevel@tonic-gate 
6792ed1782Smike_s 	if ((namebuf = malloc(namebuf_sz)) == NULL) {
6892ed1782Smike_s 		(void) fprintf(stderr, "%s: can't allocate %d bytes\n",
69b9bd317cSab 		    cmdname, namebuf_sz);
707c478bd9Sstevel@tonic-gate 		exit(ERR_MEMORY);
717c478bd9Sstevel@tonic-gate 	}
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate 	nbp = namebuf;
747c478bd9Sstevel@tonic-gate 	nbe = namebuf + namebuf_sz;
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate 	for (i = 0; i < total_funcs; i++) {
77*4226f635SJason King 		p = conv_demangle_name(profsym[i].name);
78*4226f635SJason King 		if (p == profsym[i].name)
797c478bd9Sstevel@tonic-gate 			continue;
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 		namelen = strlen(p);
827c478bd9Sstevel@tonic-gate 		if ((nbp + namelen + 1) > nbe) {
837c478bd9Sstevel@tonic-gate 			namebuf_sz += BUCKET_SZ;
8492ed1782Smike_s 			namebuf = realloc(namebuf, namebuf_sz);
857c478bd9Sstevel@tonic-gate 			if (namebuf == NULL) {
8692ed1782Smike_s 				(void) fprintf(stderr,
8792ed1782Smike_s 				    "%s: can't alloc %d bytes\n",
8892ed1782Smike_s 				    cmdname, BUCKET_SZ);
897c478bd9Sstevel@tonic-gate 				exit(ERR_MEMORY);
907c478bd9Sstevel@tonic-gate 			}
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 			nbp = namebuf + cur_len;
937c478bd9Sstevel@tonic-gate 			nbe = namebuf + namebuf_sz;
947c478bd9Sstevel@tonic-gate 		}
957c478bd9Sstevel@tonic-gate 
9692ed1782Smike_s 		(void) strcpy(nbp, p);
977c478bd9Sstevel@tonic-gate 		profsym[i].demangled_name = nbp;
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate 		nbp += namelen + 1;
1007c478bd9Sstevel@tonic-gate 		cur_len += namelen + 1;
101*4226f635SJason King 		free((void *)p);
1027c478bd9Sstevel@tonic-gate 	}
1037c478bd9Sstevel@tonic-gate }
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate int
cmp_by_time(const void * arg1,const void * arg2)10692ed1782Smike_s cmp_by_time(const void *arg1, const void *arg2)
1077c478bd9Sstevel@tonic-gate {
10892ed1782Smike_s 	profrec_t *a = (profrec_t *)arg1;
10992ed1782Smike_s 	profrec_t *b = (profrec_t *)arg2;
11092ed1782Smike_s 
1117c478bd9Sstevel@tonic-gate 	if (a->percent_time > b->percent_time)
1127c478bd9Sstevel@tonic-gate 		return (-1);
1137c478bd9Sstevel@tonic-gate 	else if (a->percent_time < b->percent_time)
1147c478bd9Sstevel@tonic-gate 		return (1);
1157c478bd9Sstevel@tonic-gate 	else
1167c478bd9Sstevel@tonic-gate 		return (0);
1177c478bd9Sstevel@tonic-gate }
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate int
cmp_by_ncalls(const void * arg1,const void * arg2)12092ed1782Smike_s cmp_by_ncalls(const void *arg1, const void *arg2)
1217c478bd9Sstevel@tonic-gate {
12292ed1782Smike_s 	profrec_t *a = (profrec_t *)arg1;
12392ed1782Smike_s 	profrec_t *b = (profrec_t *)arg2;
12492ed1782Smike_s 
1257c478bd9Sstevel@tonic-gate 	if (a->ncalls > b->ncalls)
1267c478bd9Sstevel@tonic-gate 		return (-1);
1277c478bd9Sstevel@tonic-gate 	else if (a->ncalls < b->ncalls)
1287c478bd9Sstevel@tonic-gate 		return (1);
1297c478bd9Sstevel@tonic-gate 	else
1307c478bd9Sstevel@tonic-gate 		return (0);
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate }
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate static void
print_profile_data(void)13592ed1782Smike_s print_profile_data(void)
1367c478bd9Sstevel@tonic-gate {
1377c478bd9Sstevel@tonic-gate 	int		i;
13892ed1782Smike_s 	int		(*sort_func)(const void *, const void *);
1397c478bd9Sstevel@tonic-gate 	mod_info_t	*mi;
1407c478bd9Sstevel@tonic-gate 	double		cumsecs = 0;
1417c478bd9Sstevel@tonic-gate 	char		filler[20];
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	/*
1447c478bd9Sstevel@tonic-gate 	 * Sort the compiled data; the sort flags are mutually exclusive.
1457c478bd9Sstevel@tonic-gate 	 */
1467c478bd9Sstevel@tonic-gate 	switch (sort_flag) {
1477c478bd9Sstevel@tonic-gate 		case BY_NCALLS:
1487c478bd9Sstevel@tonic-gate 			sort_func = cmp_by_ncalls;
1497c478bd9Sstevel@tonic-gate 			break;
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 		case BY_NAME:
1527c478bd9Sstevel@tonic-gate 			if (Cflag)
1537c478bd9Sstevel@tonic-gate 				setup_demangled_names();
1547c478bd9Sstevel@tonic-gate 			sort_func = cmp_by_name;
1557c478bd9Sstevel@tonic-gate 			break;
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 		case BY_ADDRESS:
1587c478bd9Sstevel@tonic-gate 			sort_flag |= BY_ADDRESS;
1597c478bd9Sstevel@tonic-gate 			sort_func = NULL;	/* already sorted by addr */
1607c478bd9Sstevel@tonic-gate 			break;
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 		case BY_TIME:		/* default is to sort by time */
1637c478bd9Sstevel@tonic-gate 		default:
1647c478bd9Sstevel@tonic-gate 			sort_func = cmp_by_time;
1657c478bd9Sstevel@tonic-gate 	}
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	if (sort_func) {
16992ed1782Smike_s 		qsort(profsym, total_funcs, sizeof (profrec_t), sort_func);
1707c478bd9Sstevel@tonic-gate 	}
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	/*
1737c478bd9Sstevel@tonic-gate 	 * If we're sorting by name, and if it is a verbose print, we wouldn't
1747c478bd9Sstevel@tonic-gate 	 * have set up the print_mid fields yet.
1757c478bd9Sstevel@tonic-gate 	 */
1767c478bd9Sstevel@tonic-gate 	if ((flags & F_VERBOSE) && (sort_flag == BY_NAME)) {
1777c478bd9Sstevel@tonic-gate 		for (i = 0; i < total_funcs; i++) {
1787c478bd9Sstevel@tonic-gate 			/*
1797c478bd9Sstevel@tonic-gate 			 * same as previous or next (if there's one) ?
1807c478bd9Sstevel@tonic-gate 			 */
1817c478bd9Sstevel@tonic-gate 			if (i && (strcmp(profsym[i].demangled_name,
182b9bd317cSab 			    profsym[i-1].demangled_name) == 0)) {
1837c478bd9Sstevel@tonic-gate 				profsym[i].print_mid = TRUE;
1847c478bd9Sstevel@tonic-gate 			} else if ((i < (total_funcs - 1)) &&
185b9bd317cSab 			    (strcmp(profsym[i].demangled_name,
186b9bd317cSab 			    profsym[i+1].demangled_name) == 0)) {
1877c478bd9Sstevel@tonic-gate 				profsym[i].print_mid = TRUE;
1887c478bd9Sstevel@tonic-gate 			}
1897c478bd9Sstevel@tonic-gate 		}
1907c478bd9Sstevel@tonic-gate 	}
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	/*
1937c478bd9Sstevel@tonic-gate 	 * The actual printing part.
1947c478bd9Sstevel@tonic-gate 	 */
1957c478bd9Sstevel@tonic-gate 	if (!(flags & F_NHEAD)) {
1967c478bd9Sstevel@tonic-gate 		if (flags & F_PADDR)
19792ed1782Smike_s 			(void) printf("        %s", atitle);
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 		if (time_in_ticks)
20092ed1782Smike_s 			(void) puts(
20192ed1782Smike_s 			    " %Time   Tiks  Cumtiks  #Calls   tiks/call  Name");
2027c478bd9Sstevel@tonic-gate 		else
20392ed1782Smike_s 			(void) puts(
20492ed1782Smike_s 			    " %Time Seconds Cumsecs  #Calls   msec/call  Name");
2057c478bd9Sstevel@tonic-gate 	}
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	mi = NULL;
2087c478bd9Sstevel@tonic-gate 	for (i = 0; i < total_funcs; i++) {
2097c478bd9Sstevel@tonic-gate 		/*
2107c478bd9Sstevel@tonic-gate 		 * Since the same value may denote different symbols in
2117c478bd9Sstevel@tonic-gate 		 * different shared objects, it is debatable if it is
2127c478bd9Sstevel@tonic-gate 		 * meaningful to print addresses at all. Especially so
2137c478bd9Sstevel@tonic-gate 		 * if we were asked to sort by symbol addresses.
2147c478bd9Sstevel@tonic-gate 		 *
2157c478bd9Sstevel@tonic-gate 		 * If we've to sort by address, I think it is better to sort
2167c478bd9Sstevel@tonic-gate 		 * it on a per-module basis and if verbose mode is on too,
2177c478bd9Sstevel@tonic-gate 		 * print a newline to separate out modules.
2187c478bd9Sstevel@tonic-gate 		 */
2197c478bd9Sstevel@tonic-gate 		if ((flags & F_VERBOSE) && (sort_flag == BY_ADDRESS)) {
2207c478bd9Sstevel@tonic-gate 			if (mi != profsym[i].module) {
22192ed1782Smike_s 				(void) printf("\n");
2227c478bd9Sstevel@tonic-gate 				mi = profsym[i].module;
2237c478bd9Sstevel@tonic-gate 			}
2247c478bd9Sstevel@tonic-gate 		}
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 		if (flags & F_PADDR) {
2277c478bd9Sstevel@tonic-gate 			if (aformat[2] == 'x')
22892ed1782Smike_s 				(void) printf("%16llx ", profsym[i].addr);
2297c478bd9Sstevel@tonic-gate 			else
23092ed1782Smike_s 				(void) printf("%16llo ", profsym[i].addr);
2317c478bd9Sstevel@tonic-gate 		}
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 		cumsecs += profsym[i].seconds;
23492ed1782Smike_s 		(void) printf("%6.1f%8.2f%8.2f", profsym[i].percent_time,
235b9bd317cSab 		    profsym[i].seconds, cumsecs);
2367c478bd9Sstevel@tonic-gate 
23792ed1782Smike_s 		(void) printf("%8d%12.4f  ",
238b9bd317cSab 		    profsym[i].ncalls, profsym[i].msecs_per_call);
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 		if (profsym[i].print_mid)
24192ed1782Smike_s 			(void) printf("%d:", (profsym[i].module)->id);
2427c478bd9Sstevel@tonic-gate 
24392ed1782Smike_s 		(void) printf("%s\n", profsym[i].demangled_name);
2447c478bd9Sstevel@tonic-gate 	}
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	if (flags & F_PADDR)
24792ed1782Smike_s 		(void) sprintf(filler, "%16s", "");
2487c478bd9Sstevel@tonic-gate 	else
2497c478bd9Sstevel@tonic-gate 		filler[0] = 0;
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	if (flags & F_VERBOSE) {
25292ed1782Smike_s 		(void) puts("\n");
25392ed1782Smike_s 		(void) printf("%s   Total Object Modules     %7d\n",
254b9bd317cSab 		    filler, n_modules);
25592ed1782Smike_s 		(void) printf("%s   Qualified Symbols        %7d\n",
256b9bd317cSab 		    filler, total_funcs);
25792ed1782Smike_s 		(void) printf("%s   Symbols with zero usage  %7d\n",
258b9bd317cSab 		    filler, n_zeros);
25992ed1782Smike_s 		(void) printf("%s   Total pc-hits            %7d\n",
260b9bd317cSab 		    filler, n_pcsamples);
26192ed1782Smike_s 		(void) printf("%s   Accounted pc-hits        %7d\n",
262b9bd317cSab 		    filler, n_accounted_ticks);
2637c478bd9Sstevel@tonic-gate 		if ((!gflag) && (n_pcsamples - n_accounted_ticks)) {
26492ed1782Smike_s 			(void) printf("%s   Missed pc-hits (try -g)  %7d\n\n",
265b9bd317cSab 			    filler, n_pcsamples - n_accounted_ticks);
2667c478bd9Sstevel@tonic-gate 		} else {
26792ed1782Smike_s 			(void) printf("%s   Missed pc-hits           %7d\n\n",
268b9bd317cSab 			    filler, n_pcsamples - n_accounted_ticks);
2697c478bd9Sstevel@tonic-gate 		}
27092ed1782Smike_s 		(void) printf("%s   Module info\n", filler);
2717c478bd9Sstevel@tonic-gate 		for (mi = &modules; mi; mi = mi->next)
27292ed1782Smike_s 			(void) printf("%s      %d: `%s'\n", filler,
27392ed1782Smike_s 			    mi->id, mi->path);
2747c478bd9Sstevel@tonic-gate 	}
2757c478bd9Sstevel@tonic-gate }
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate int
name_cmp(const void * arg1,const void * arg2)27892ed1782Smike_s name_cmp(const void *arg1, const void *arg2)
2797c478bd9Sstevel@tonic-gate {
28092ed1782Smike_s 	profnames_t *a = (profnames_t *)arg1;
28192ed1782Smike_s 	profnames_t *b = (profnames_t *)arg2;
28292ed1782Smike_s 
2837c478bd9Sstevel@tonic-gate 	return (strcmp(a->name, b->name));
2847c478bd9Sstevel@tonic-gate }
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate static void
check_dupnames(void)28792ed1782Smike_s check_dupnames(void)
2887c478bd9Sstevel@tonic-gate {
2897c478bd9Sstevel@tonic-gate 	int		i;
2907c478bd9Sstevel@tonic-gate 	profnames_t	*pn;
2917c478bd9Sstevel@tonic-gate 
29292ed1782Smike_s 	pn = calloc(total_funcs, sizeof (profnames_t));
2937c478bd9Sstevel@tonic-gate 	if (pn == NULL) {
29492ed1782Smike_s 		(void) fprintf(stderr, "%s: no room for %d bytes\n",
29592ed1782Smike_s 		    cmdname, total_funcs * sizeof (profnames_t));
2967c478bd9Sstevel@tonic-gate 		exit(ERR_MEMORY);
2977c478bd9Sstevel@tonic-gate 	}
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	for (i = 0; i < total_funcs; i++) {
3007c478bd9Sstevel@tonic-gate 		pn[i].name = profsym[i].demangled_name;
3017c478bd9Sstevel@tonic-gate 		pn[i].pfrec = &profsym[i];
3027c478bd9Sstevel@tonic-gate 	}
3037c478bd9Sstevel@tonic-gate 
30492ed1782Smike_s 	qsort(pn, total_funcs, sizeof (profnames_t), name_cmp);
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	for (i = 0; i < total_funcs; i++) {
3077c478bd9Sstevel@tonic-gate 		/*
3087c478bd9Sstevel@tonic-gate 		 * same as previous or next (if there's one) ?
3097c478bd9Sstevel@tonic-gate 		 */
3107c478bd9Sstevel@tonic-gate 		if (i && (strcmp(pn[i].name, pn[i-1].name) == 0))
3117c478bd9Sstevel@tonic-gate 			(pn[i].pfrec)->print_mid = TRUE;
3127c478bd9Sstevel@tonic-gate 		else if ((i < (total_funcs - 1)) &&
313b9bd317cSab 		    (strcmp(pn[i].name, pn[i+1].name) == 0)) {
3147c478bd9Sstevel@tonic-gate 			(pn[i].pfrec)->print_mid = TRUE;
3157c478bd9Sstevel@tonic-gate 		}
3167c478bd9Sstevel@tonic-gate 	}
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	free(pn);
3197c478bd9Sstevel@tonic-gate }
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate static void
compute_times(nltype * nl,profrec_t * psym)3227c478bd9Sstevel@tonic-gate compute_times(nltype *nl, profrec_t *psym)
3237c478bd9Sstevel@tonic-gate {
3247c478bd9Sstevel@tonic-gate 	static int	first_time = TRUE;
3257c478bd9Sstevel@tonic-gate 	static long	hz;
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	if (first_time) {
3287c478bd9Sstevel@tonic-gate 		if ((hz = sysconf(_SC_CLK_TCK)) == -1)
3297c478bd9Sstevel@tonic-gate 			time_in_ticks = TRUE;
3307c478bd9Sstevel@tonic-gate 		first_time = FALSE;
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 	if (time_in_ticks) {
33492ed1782Smike_s 		psym->seconds = (double)nl->nticks;
3357c478bd9Sstevel@tonic-gate 		if (nl->ncalls) {
336b9bd317cSab 			psym->msecs_per_call = (double)nl->nticks /
337b9bd317cSab 			    (double)nl->ncalls;
3387c478bd9Sstevel@tonic-gate 		} else
339b9bd317cSab 			psym->msecs_per_call = (double)0.0;
3407c478bd9Sstevel@tonic-gate 	} else {
34192ed1782Smike_s 		psym->seconds = (double)nl->nticks / (double)hz;
3427c478bd9Sstevel@tonic-gate 		if (nl->ncalls) {
34392ed1782Smike_s 			psym->msecs_per_call =
34492ed1782Smike_s 			    ((double)psym->seconds * 1000.0) /
34592ed1782Smike_s 			    (double)nl->ncalls;
3467c478bd9Sstevel@tonic-gate 		} else
34792ed1782Smike_s 			psym->msecs_per_call = (double)0.0;
3487c478bd9Sstevel@tonic-gate 	}
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 	if (n_pcsamples) {
35192ed1782Smike_s 		psym->percent_time =
35292ed1782Smike_s 		    ((double)nl->nticks / (double)n_pcsamples) * 100;
3537c478bd9Sstevel@tonic-gate 	}
3547c478bd9Sstevel@tonic-gate }
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate static void
collect_profsyms(void)35792ed1782Smike_s collect_profsyms(void)
3587c478bd9Sstevel@tonic-gate {
3597c478bd9Sstevel@tonic-gate 	mod_info_t	*mi;
3607c478bd9Sstevel@tonic-gate 	nltype		*nl;
3617c478bd9Sstevel@tonic-gate 	size_t		i, ndx;
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next)
3657c478bd9Sstevel@tonic-gate 		total_funcs += mi->nfuncs;
3667c478bd9Sstevel@tonic-gate 
36792ed1782Smike_s 	profsym = calloc(total_funcs, sizeof (profrec_t));
3687c478bd9Sstevel@tonic-gate 	if (profsym == NULL) {
36992ed1782Smike_s 		(void) fprintf(stderr, "%s: no room for %d bytes\n",
37092ed1782Smike_s 		    cmdname, total_funcs * sizeof (profrec_t));
3717c478bd9Sstevel@tonic-gate 		exit(ERR_MEMORY);
3727c478bd9Sstevel@tonic-gate 	}
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 	ndx = 0;
3757c478bd9Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
3767c478bd9Sstevel@tonic-gate 		nl = mi->nl;
3777c478bd9Sstevel@tonic-gate 		for (i = 0; i < mi->nfuncs; i++) {
3787c478bd9Sstevel@tonic-gate 			/*
3797c478bd9Sstevel@tonic-gate 			 * I think F_ZSYMS doesn't make sense for the new
3807c478bd9Sstevel@tonic-gate 			 * mon.out format, since we don't have a profiling
3817c478bd9Sstevel@tonic-gate 			 * *range*, per se. But the man page demands it,
3827c478bd9Sstevel@tonic-gate 			 * so...
3837c478bd9Sstevel@tonic-gate 			 */
3847c478bd9Sstevel@tonic-gate 			if ((nl[i].ncalls == 0) && (nl[i].nticks == 0)) {
3857c478bd9Sstevel@tonic-gate 				n_zeros++;
3867c478bd9Sstevel@tonic-gate 				if (!(flags & F_ZSYMS))
3877c478bd9Sstevel@tonic-gate 					continue;
3887c478bd9Sstevel@tonic-gate 			}
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 			/*
3917c478bd9Sstevel@tonic-gate 			 * Initially, we set demangled_name to be
3927c478bd9Sstevel@tonic-gate 			 * the same as name. If Cflag is set, we later
3937c478bd9Sstevel@tonic-gate 			 * change this to be the demangled name ptr.
3947c478bd9Sstevel@tonic-gate 			 */
3957c478bd9Sstevel@tonic-gate 			profsym[ndx].addr = nl[i].value;
3967c478bd9Sstevel@tonic-gate 			profsym[ndx].ncalls = nl[i].ncalls;
3977c478bd9Sstevel@tonic-gate 			profsym[ndx].name = nl[i].name;
3987c478bd9Sstevel@tonic-gate 			profsym[ndx].demangled_name = nl[i].name;
3997c478bd9Sstevel@tonic-gate 			profsym[ndx].module = mi;
4007c478bd9Sstevel@tonic-gate 			profsym[ndx].print_mid = FALSE;
4017c478bd9Sstevel@tonic-gate 			compute_times(&nl[i], &profsym[ndx]);
4027c478bd9Sstevel@tonic-gate 			ndx++;
4037c478bd9Sstevel@tonic-gate 		}
4047c478bd9Sstevel@tonic-gate 	}
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	/*
4077c478bd9Sstevel@tonic-gate 	 * Adjust total_funcs to actual printable funcs
4087c478bd9Sstevel@tonic-gate 	 */
4097c478bd9Sstevel@tonic-gate 	total_funcs = ndx;
4107c478bd9Sstevel@tonic-gate }
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate static void
assign_pcsamples(mod_info_t * module,Address * pcsmpl,size_t n_samples)41392ed1782Smike_s assign_pcsamples(mod_info_t *module, Address *pcsmpl,
41492ed1782Smike_s     size_t n_samples)
4157c478bd9Sstevel@tonic-gate {
4167c478bd9Sstevel@tonic-gate 	Address		*pcptr, *pcse = pcsmpl + n_samples;
4177c478bd9Sstevel@tonic-gate 	Address		nxt_func;
4187c478bd9Sstevel@tonic-gate 	nltype		*nl;
4197c478bd9Sstevel@tonic-gate 	size_t		nticks;
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 	/* Locate the first pc-hit for this module */
4227c478bd9Sstevel@tonic-gate 	if ((pcptr = locate(pcsmpl, n_samples, module->load_base)) == NULL)
4237c478bd9Sstevel@tonic-gate 		return;			/* no pc-hits in this module */
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 	/* Assign all pc-hits in this module to appropriate functions */
4267c478bd9Sstevel@tonic-gate 	while ((pcptr < pcse) && (*pcptr < module->load_end)) {
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 		/* Update the corresponding function's time */
4297c478bd9Sstevel@tonic-gate 		if (nl = nllookup(module, *pcptr, &nxt_func)) {
4307c478bd9Sstevel@tonic-gate 			/*
4317c478bd9Sstevel@tonic-gate 			 * Collect all pc-hits in this function. Each
4327c478bd9Sstevel@tonic-gate 			 * pc-hit counts as 1 tick.
4337c478bd9Sstevel@tonic-gate 			 */
4347c478bd9Sstevel@tonic-gate 			nticks = 0;
4357c478bd9Sstevel@tonic-gate 			while ((pcptr < pcse) && (*pcptr < nxt_func)) {
4367c478bd9Sstevel@tonic-gate 				nticks++;
4377c478bd9Sstevel@tonic-gate 				pcptr++;
4387c478bd9Sstevel@tonic-gate 			}
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 			nl->nticks += nticks;
4417c478bd9Sstevel@tonic-gate 			n_accounted_ticks += nticks;
4427c478bd9Sstevel@tonic-gate 		} else {
4437c478bd9Sstevel@tonic-gate 			/*
4447c478bd9Sstevel@tonic-gate 			 * pc sample could not be assigned to function;
4457c478bd9Sstevel@tonic-gate 			 * probably in a PLT
4467c478bd9Sstevel@tonic-gate 			 */
4477c478bd9Sstevel@tonic-gate 			pcptr++;
4487c478bd9Sstevel@tonic-gate 		}
4497c478bd9Sstevel@tonic-gate 	}
4507c478bd9Sstevel@tonic-gate }
4517c478bd9Sstevel@tonic-gate 
45292ed1782Smike_s static int
pc_cmp(const void * arg1,const void * arg2)45392ed1782Smike_s pc_cmp(const void *arg1, const void *arg2)
4547c478bd9Sstevel@tonic-gate {
45592ed1782Smike_s 	Address *pc1 = (Address *)arg1;
45692ed1782Smike_s 	Address *pc2 = (Address *)arg2;
45792ed1782Smike_s 
4587c478bd9Sstevel@tonic-gate 	if (*pc1 > *pc2)
4597c478bd9Sstevel@tonic-gate 		return (1);
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 	if (*pc1 < *pc2)
4627c478bd9Sstevel@tonic-gate 		return (-1);
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 	return (0);
4657c478bd9Sstevel@tonic-gate }
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate static void
process_pcsamples(ProfBuffer * bufp)46892ed1782Smike_s process_pcsamples(ProfBuffer *bufp)
4697c478bd9Sstevel@tonic-gate {
4707c478bd9Sstevel@tonic-gate 	Address		*pc_samples;
4717c478bd9Sstevel@tonic-gate 	mod_info_t	*mi;
4727c478bd9Sstevel@tonic-gate 	size_t		nelem = bufp->bufsize;
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 	/* buffer with no pc samples ? */
4757c478bd9Sstevel@tonic-gate 	if (nelem == 0)
4767c478bd9Sstevel@tonic-gate 		return;
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate 	/* Allocate for the pcsample chunk */
4797c478bd9Sstevel@tonic-gate 	pc_samples = (Address *) calloc(nelem, sizeof (Address));
4807c478bd9Sstevel@tonic-gate 	if (pc_samples == NULL) {
48192ed1782Smike_s 		(void) fprintf(stderr, "%s: no room for %d sample pc's\n",
48292ed1782Smike_s 		    cmdname, nelem);
4837c478bd9Sstevel@tonic-gate 		exit(ERR_MEMORY);
4847c478bd9Sstevel@tonic-gate 	}
4857c478bd9Sstevel@tonic-gate 
48692ed1782Smike_s 	(void) memcpy(pc_samples, (caddr_t)bufp + bufp->buffer,
48792ed1782Smike_s 	    nelem * sizeof (Address));
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	/* Sort the pc samples */
49092ed1782Smike_s 	qsort(pc_samples, nelem, sizeof (Address), pc_cmp);
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 	/*
4937c478bd9Sstevel@tonic-gate 	 * Assign pcsamples to functions in the currently active
4947c478bd9Sstevel@tonic-gate 	 * module list
4957c478bd9Sstevel@tonic-gate 	 */
4967c478bd9Sstevel@tonic-gate 	for (mi = &modules; mi; mi = mi->next) {
4977c478bd9Sstevel@tonic-gate 		if (mi->active == FALSE)
4987c478bd9Sstevel@tonic-gate 			continue;
4997c478bd9Sstevel@tonic-gate 		assign_pcsamples(mi, pc_samples, nelem);
5007c478bd9Sstevel@tonic-gate 	}
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 	free(pc_samples);
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	/* Update total number of pcsamples read so far */
5057c478bd9Sstevel@tonic-gate 	n_pcsamples += nelem;
5067c478bd9Sstevel@tonic-gate }
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate static void
process_cgraph(ProfCallGraph * cgp)50992ed1782Smike_s process_cgraph(ProfCallGraph *cgp)
5107c478bd9Sstevel@tonic-gate {
5117c478bd9Sstevel@tonic-gate 	mod_info_t	*mi;
5127c478bd9Sstevel@tonic-gate 	Address		f_end;
5137c478bd9Sstevel@tonic-gate 	Index		callee_off;
5147c478bd9Sstevel@tonic-gate 	ProfFunction	*calleep;
5157c478bd9Sstevel@tonic-gate 	nltype		*nl;
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 	for (callee_off = cgp->functions; callee_off;
518b9bd317cSab 	    callee_off = calleep->next_to) {
5197c478bd9Sstevel@tonic-gate 
52092ed1782Smike_s 		/* LINTED: pointer cast */
52192ed1782Smike_s 		calleep = (ProfFunction *)((char *)cgp + callee_off);
5227c478bd9Sstevel@tonic-gate 		if (calleep->count == 0)
5237c478bd9Sstevel@tonic-gate 			continue;
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 		/*
5267c478bd9Sstevel@tonic-gate 		 * If we cannot identify a callee with a module, we
5277c478bd9Sstevel@tonic-gate 		 * cannot get to its namelist, just skip it.
5287c478bd9Sstevel@tonic-gate 		 */
5297c478bd9Sstevel@tonic-gate 		for (mi = &modules; mi; mi = mi->next) {
5307c478bd9Sstevel@tonic-gate 			if (mi->active == FALSE)
5317c478bd9Sstevel@tonic-gate 				continue;
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 			if (calleep->topc >= mi->load_base &&
534b9bd317cSab 			    calleep->topc < mi->load_end) {
5357c478bd9Sstevel@tonic-gate 				/*
5367c478bd9Sstevel@tonic-gate 				 * nllookup() returns the next lower entry
5377c478bd9Sstevel@tonic-gate 				 * point on a miss. So just make sure the
5387c478bd9Sstevel@tonic-gate 				 * callee's pc is not outside this function
5397c478bd9Sstevel@tonic-gate 				 */
5407c478bd9Sstevel@tonic-gate 				if (nl = nllookup(mi, calleep->topc, 0)) {
5417c478bd9Sstevel@tonic-gate 					f_end = mi->load_base + (nl->value -
542b9bd317cSab 					    mi->txt_origin) + nl->size;
5437c478bd9Sstevel@tonic-gate 					if (calleep->topc < f_end)
5447c478bd9Sstevel@tonic-gate 						nl->ncalls += calleep->count;
5457c478bd9Sstevel@tonic-gate 				}
5467c478bd9Sstevel@tonic-gate 			}
5477c478bd9Sstevel@tonic-gate 		}
5487c478bd9Sstevel@tonic-gate 	}
5497c478bd9Sstevel@tonic-gate }
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate static mod_info_t *
get_shobj_syms(char * pathname,GElf_Addr ld_base,GElf_Addr ld_end)5527c478bd9Sstevel@tonic-gate get_shobj_syms(char *pathname, GElf_Addr ld_base, GElf_Addr ld_end)
5537c478bd9Sstevel@tonic-gate {
5547c478bd9Sstevel@tonic-gate 	mod_info_t	*mi;
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	/* Create a new module element */
55792ed1782Smike_s 	if ((mi = malloc(sizeof (mod_info_t))) == NULL) {
55892ed1782Smike_s 		(void) fprintf(stderr, "%s: no room for %d bytes\n",
55992ed1782Smike_s 		    cmdname, sizeof (mod_info_t));
5607c478bd9Sstevel@tonic-gate 		exit(ERR_MEMORY);
5617c478bd9Sstevel@tonic-gate 	}
5627c478bd9Sstevel@tonic-gate 
56392ed1782Smike_s 	mi->path = malloc(strlen(pathname) + 1);
5647c478bd9Sstevel@tonic-gate 	if (mi->path == NULL) {
56592ed1782Smike_s 		(void) fprintf(stderr, "%s: can't allocate %d bytes\n",
56692ed1782Smike_s 		    cmdname, strlen(pathname) + 1);
5677c478bd9Sstevel@tonic-gate 		exit(ERR_MEMORY);
5687c478bd9Sstevel@tonic-gate 	}
56992ed1782Smike_s 	(void) strcpy(mi->path, pathname);
5707c478bd9Sstevel@tonic-gate 	mi->next = NULL;
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 	get_syms(pathname, mi);
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	/* and fill in info... */
5757c478bd9Sstevel@tonic-gate 	mi->id = n_modules + 1;
5767c478bd9Sstevel@tonic-gate 	mi->load_base = ld_base;
5777c478bd9Sstevel@tonic-gate 	mi->load_end = ld_end;
5787c478bd9Sstevel@tonic-gate 	mi->active = TRUE;
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	n_modules++;
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	return (mi);
5837c478bd9Sstevel@tonic-gate }
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate /*
5867c478bd9Sstevel@tonic-gate  * Two modules overlap each other if they don't lie completely *outside*
5877c478bd9Sstevel@tonic-gate  * each other.
5887c478bd9Sstevel@tonic-gate  */
5897c478bd9Sstevel@tonic-gate static bool
does_overlap(ProfModule * new,mod_info_t * old)59092ed1782Smike_s does_overlap(ProfModule *new, mod_info_t *old)
5917c478bd9Sstevel@tonic-gate {
5927c478bd9Sstevel@tonic-gate 	/* case 1: new module lies completely *before* the old one */
5937c478bd9Sstevel@tonic-gate 	if (new->startaddr < old->load_base && new->endaddr <= old->load_base)
5947c478bd9Sstevel@tonic-gate 		return (FALSE);
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 	/* case 2: new module lies completely *after* the old one */
5977c478bd9Sstevel@tonic-gate 	if (new->startaddr >= old->load_end && new->endaddr >= old->load_end)
5987c478bd9Sstevel@tonic-gate 		return (FALSE);
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 	/* probably a dlopen: the modules overlap each other */
6017c478bd9Sstevel@tonic-gate 	return (TRUE);
6027c478bd9Sstevel@tonic-gate }
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate static bool
is_same_as_aout(char * modpath,struct stat * buf)60592ed1782Smike_s is_same_as_aout(char *modpath, struct stat *buf)
6067c478bd9Sstevel@tonic-gate {
6077c478bd9Sstevel@tonic-gate 	if (stat(modpath, buf) == -1) {
6087c478bd9Sstevel@tonic-gate 		perror(modpath);
6097c478bd9Sstevel@tonic-gate 		exit(ERR_SYSCALL);
6107c478bd9Sstevel@tonic-gate 	}
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	if ((buf->st_dev == aout_stat.st_dev) &&
613b9bd317cSab 	    (buf->st_ino == aout_stat.st_ino)) {
6147c478bd9Sstevel@tonic-gate 		return (TRUE);
6157c478bd9Sstevel@tonic-gate 	} else
6167c478bd9Sstevel@tonic-gate 		return (FALSE);
6177c478bd9Sstevel@tonic-gate }
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate static void
process_modules(ProfModuleList * modlp)62092ed1782Smike_s process_modules(ProfModuleList *modlp)
6217c478bd9Sstevel@tonic-gate {
6227c478bd9Sstevel@tonic-gate 	ProfModule	*newmodp;
6237c478bd9Sstevel@tonic-gate 	mod_info_t	*mi, *last, *new_module;
6247c478bd9Sstevel@tonic-gate 	char		*so_path;
6257c478bd9Sstevel@tonic-gate 	bool		more_modules = TRUE;
6267c478bd9Sstevel@tonic-gate 	struct stat	so_statbuf;
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	/* Check version of module type object */
6297c478bd9Sstevel@tonic-gate 	if (modlp->version > PROF_MODULES_VER) {
63092ed1782Smike_s 		(void) fprintf(stderr,
63192ed1782Smike_s 		    "%s: unsupported version %d for modules\n",
63292ed1782Smike_s 		    cmdname, modlp->version);
6337c478bd9Sstevel@tonic-gate 		exit(ERR_INPUT);
6347c478bd9Sstevel@tonic-gate 	}
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	/*
6387c478bd9Sstevel@tonic-gate 	 * Scan the PROF_MODULES_T list and add modules to current list
6397c478bd9Sstevel@tonic-gate 	 * of modules, if they're not present already
6407c478bd9Sstevel@tonic-gate 	 */
64192ed1782Smike_s 	/* LINTED: pointer cast */
64292ed1782Smike_s 	newmodp = (ProfModule *)((caddr_t)modlp + modlp->modules);
6437c478bd9Sstevel@tonic-gate 	do {
6447c478bd9Sstevel@tonic-gate 		/*
6457c478bd9Sstevel@tonic-gate 		 * Since the aout could've been renamed after its run, we
6467c478bd9Sstevel@tonic-gate 		 * should see if current module overlaps aout. If it does, it
6477c478bd9Sstevel@tonic-gate 		 * is probably the renamed aout. We should also skip any other
6487c478bd9Sstevel@tonic-gate 		 * non-sharedobj's that we see (or should we report an error ?)
6497c478bd9Sstevel@tonic-gate 		 */
65092ed1782Smike_s 		so_path = (caddr_t)modlp + newmodp->path;
6517c478bd9Sstevel@tonic-gate 		if (does_overlap(newmodp, &modules) ||
652b9bd317cSab 		    is_same_as_aout(so_path, &so_statbuf) ||
653b9bd317cSab 		    (!is_shared_obj(so_path))) {
6547c478bd9Sstevel@tonic-gate 			if (!newmodp->next)
6557c478bd9Sstevel@tonic-gate 				more_modules = FALSE;
6567c478bd9Sstevel@tonic-gate 
65792ed1782Smike_s 			/* LINTED: pointer cast */
6587c478bd9Sstevel@tonic-gate 			newmodp = (ProfModule *)
65992ed1782Smike_s 			    ((caddr_t)modlp + newmodp->next);
6607c478bd9Sstevel@tonic-gate 			continue;
6617c478bd9Sstevel@tonic-gate 		}
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 		/*
6647c478bd9Sstevel@tonic-gate 		 * Check all modules (leave the first one, 'cos that
6657c478bd9Sstevel@tonic-gate 		 * is the program executable info). If this module is already
6667c478bd9Sstevel@tonic-gate 		 * there in the list, skip it.
6677c478bd9Sstevel@tonic-gate 		 */
6687c478bd9Sstevel@tonic-gate 		last = &modules;
66992ed1782Smike_s 		while ((mi = last->next) != NULL) {
6707c478bd9Sstevel@tonic-gate 			/*
6717c478bd9Sstevel@tonic-gate 			 * We expect the full pathname for all shared objects
6727c478bd9Sstevel@tonic-gate 			 * needed by the program executable. In this case, we
6737c478bd9Sstevel@tonic-gate 			 * simply need to compare the paths to see if they are
6747c478bd9Sstevel@tonic-gate 			 * the same file.
6757c478bd9Sstevel@tonic-gate 			 */
6767c478bd9Sstevel@tonic-gate 			if (strcmp(mi->path, so_path) == 0)
6777c478bd9Sstevel@tonic-gate 				break;
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 			/*
6807c478bd9Sstevel@tonic-gate 			 * Check if this new shared object will overlap any
6817c478bd9Sstevel@tonic-gate 			 * existing module. If yes, deactivate the old one.
6827c478bd9Sstevel@tonic-gate 			 */
6837c478bd9Sstevel@tonic-gate 			if (does_overlap(newmodp, mi))
6847c478bd9Sstevel@tonic-gate 				mi->active = FALSE;
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 			last = mi;
6877c478bd9Sstevel@tonic-gate 		}
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate 		/* Module already there, skip it */
6907c478bd9Sstevel@tonic-gate 		if (mi != NULL) {
6917c478bd9Sstevel@tonic-gate 			mi->load_base = newmodp->startaddr;
6927c478bd9Sstevel@tonic-gate 			mi->load_end = newmodp->endaddr;
6937c478bd9Sstevel@tonic-gate 			mi->active = TRUE;
6947c478bd9Sstevel@tonic-gate 			if (!newmodp->next)
6957c478bd9Sstevel@tonic-gate 				more_modules = FALSE;
6967c478bd9Sstevel@tonic-gate 
69792ed1782Smike_s 			/* LINTED: pointer cast */
6987c478bd9Sstevel@tonic-gate 			newmodp = (ProfModule *)
69992ed1782Smike_s 			    ((caddr_t)modlp + newmodp->next);
7007c478bd9Sstevel@tonic-gate 			continue;
7017c478bd9Sstevel@tonic-gate 		}
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 		/*
7047c478bd9Sstevel@tonic-gate 		 * Check if mon.out is outdated with respect to the new
7057c478bd9Sstevel@tonic-gate 		 * module we want to add
7067c478bd9Sstevel@tonic-gate 		 */
7077c478bd9Sstevel@tonic-gate 		if (monout_stat.st_mtime < so_statbuf.st_mtime) {
70892ed1782Smike_s 			(void) fprintf(stderr,
70992ed1782Smike_s 			    "%s: newer shared obj %s outdates profile info\n",
71092ed1782Smike_s 			    cmdname, so_path);
7117c478bd9Sstevel@tonic-gate 			exit(ERR_INPUT);
7127c478bd9Sstevel@tonic-gate 		}
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 		/* Create this module's nameslist */
7157c478bd9Sstevel@tonic-gate 		new_module = get_shobj_syms(so_path,
716b9bd317cSab 		    newmodp->startaddr, newmodp->endaddr);
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 		/* Add it to the tail of active module list */
7197c478bd9Sstevel@tonic-gate 		last->next = new_module;
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 		/*
7227c478bd9Sstevel@tonic-gate 		 * Move to the next module in the PROF_MODULES_T list
7237c478bd9Sstevel@tonic-gate 		 * (if present)
7247c478bd9Sstevel@tonic-gate 		 */
7257c478bd9Sstevel@tonic-gate 		if (!newmodp->next)
7267c478bd9Sstevel@tonic-gate 			more_modules = FALSE;
7277c478bd9Sstevel@tonic-gate 
72892ed1782Smike_s 		/* LINTED: pointer cast */
72992ed1782Smike_s 		newmodp = (ProfModule *)((caddr_t)modlp + newmodp->next);
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 	} while (more_modules);
7327c478bd9Sstevel@tonic-gate }
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate static void
process_mon_out(caddr_t memp,size_t fsz)7357c478bd9Sstevel@tonic-gate process_mon_out(caddr_t memp, size_t fsz)
7367c478bd9Sstevel@tonic-gate {
7377c478bd9Sstevel@tonic-gate 	ProfObject	*objp;
7387c478bd9Sstevel@tonic-gate 	caddr_t		file_end;
7397c478bd9Sstevel@tonic-gate 	bool		found_pcsamples = FALSE, found_cgraph = FALSE;
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	/*
7427c478bd9Sstevel@tonic-gate 	 * Save file end pointer and start after header
7437c478bd9Sstevel@tonic-gate 	 */
7447c478bd9Sstevel@tonic-gate 	file_end = memp + fsz;
74592ed1782Smike_s 	/* LINTED: pointer cast */
74692ed1782Smike_s 	objp = (ProfObject *)(memp + ((ProfHeader *)memp)->size);
74792ed1782Smike_s 	while ((caddr_t)objp < file_end) {
7487c478bd9Sstevel@tonic-gate 		switch (objp->type) {
7497c478bd9Sstevel@tonic-gate 			case PROF_MODULES_T :
75092ed1782Smike_s 				process_modules((ProfModuleList *)objp);
7517c478bd9Sstevel@tonic-gate 				break;
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 			case PROF_CALLGRAPH_T :
75492ed1782Smike_s 				process_cgraph((ProfCallGraph *)objp);
7557c478bd9Sstevel@tonic-gate 				found_cgraph = TRUE;
7567c478bd9Sstevel@tonic-gate 				break;
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 			case PROF_BUFFER_T :
75992ed1782Smike_s 				process_pcsamples((ProfBuffer *)objp);
7607c478bd9Sstevel@tonic-gate 				found_pcsamples = TRUE;
7617c478bd9Sstevel@tonic-gate 				break;
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 			default :
76492ed1782Smike_s 				(void) fprintf(stderr,
765b9bd317cSab 				    "%s: unknown prof object type=%d\n",
766b9bd317cSab 				    cmdname, objp->type);
7677c478bd9Sstevel@tonic-gate 				exit(ERR_INPUT);
7687c478bd9Sstevel@tonic-gate 		}
76992ed1782Smike_s 		/* LINTED: pointer cast */
77092ed1782Smike_s 		objp = (ProfObject *)((caddr_t)objp + objp->size);
7717c478bd9Sstevel@tonic-gate 	}
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 	if (!found_cgraph || !found_pcsamples) {
77492ed1782Smike_s 		(void) fprintf(stderr,
77592ed1782Smike_s 		    "%s: missing callgraph/pcsamples in `%s'\n",
77692ed1782Smike_s 		    cmdname, mon_fn);
7777c478bd9Sstevel@tonic-gate 		exit(ERR_INPUT);
7787c478bd9Sstevel@tonic-gate 	}
7797c478bd9Sstevel@tonic-gate 
78092ed1782Smike_s 	if ((caddr_t)objp > file_end) {
78192ed1782Smike_s 		(void) fprintf(stderr, "%s: malformed file `%s'\n",
78292ed1782Smike_s 		    cmdname, mon_fn);
7837c478bd9Sstevel@tonic-gate 		exit(ERR_INPUT);
7847c478bd9Sstevel@tonic-gate 	}
7857c478bd9Sstevel@tonic-gate }
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate static void
get_aout_syms(char * pathname,mod_info_t * mi)7887c478bd9Sstevel@tonic-gate get_aout_syms(char *pathname, mod_info_t *mi)
7897c478bd9Sstevel@tonic-gate {
79092ed1782Smike_s 	mi->path = malloc(strlen(pathname) + 1);
7917c478bd9Sstevel@tonic-gate 	if (mi->path == NULL) {
79292ed1782Smike_s 		(void) fprintf(stderr, "%s: can't allocate %d bytes\n",
79392ed1782Smike_s 		    cmdname, strlen(pathname) + 1);
7947c478bd9Sstevel@tonic-gate 		exit(ERR_MEMORY);
7957c478bd9Sstevel@tonic-gate 	}
7967c478bd9Sstevel@tonic-gate 
79792ed1782Smike_s 	(void) strcpy(mi->path, pathname);
7987c478bd9Sstevel@tonic-gate 	mi->next = NULL;
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 	get_syms(pathname, mi);
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 	mi->id = 1;
8037c478bd9Sstevel@tonic-gate 	mi->load_base = mi->txt_origin;
8047c478bd9Sstevel@tonic-gate 	mi->load_end = mi->data_end;
8057c478bd9Sstevel@tonic-gate 	mi->active = TRUE;
8067c478bd9Sstevel@tonic-gate }
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate void
profver(void)80992ed1782Smike_s profver(void)
8107c478bd9Sstevel@tonic-gate {
8117c478bd9Sstevel@tonic-gate 	int		fd;
8127c478bd9Sstevel@tonic-gate 	unsigned int	magic_num;
8137c478bd9Sstevel@tonic-gate 	bool		invalid_version;
8147c478bd9Sstevel@tonic-gate 	caddr_t		fmem;
8157c478bd9Sstevel@tonic-gate 	ProfHeader	prof_hdr;
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	/*
8187c478bd9Sstevel@tonic-gate 	 * Check the magic and see if this is versioned or *old-style*
8197c478bd9Sstevel@tonic-gate 	 * mon.out.
8207c478bd9Sstevel@tonic-gate 	 */
8217c478bd9Sstevel@tonic-gate 	if ((fd = open(mon_fn, O_RDONLY)) == -1) {
8227c478bd9Sstevel@tonic-gate 		perror(mon_fn);
8237c478bd9Sstevel@tonic-gate 		exit(ERR_SYSCALL);
8247c478bd9Sstevel@tonic-gate 	}
82592ed1782Smike_s 	if (read(fd, (char *)&magic_num, sizeof (unsigned int)) == -1) {
8267c478bd9Sstevel@tonic-gate 		perror("read");
8277c478bd9Sstevel@tonic-gate 		exit(ERR_SYSCALL);
8287c478bd9Sstevel@tonic-gate 	}
8297c478bd9Sstevel@tonic-gate 	if (magic_num != (unsigned int) PROF_MAGIC) {
83092ed1782Smike_s 		(void) close(fd);
8317c478bd9Sstevel@tonic-gate 		return;
8327c478bd9Sstevel@tonic-gate 	}
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate 	/*
8377c478bd9Sstevel@tonic-gate 	 * Check versioning info. For now, let's say we provide
8387c478bd9Sstevel@tonic-gate 	 * backward compatibility, so we accept all older versions.
8397c478bd9Sstevel@tonic-gate 	 */
84092ed1782Smike_s 	(void) lseek(fd, 0L, SEEK_SET);
84192ed1782Smike_s 	if (read(fd, (char *)&prof_hdr, sizeof (ProfHeader)) == -1) {
8427c478bd9Sstevel@tonic-gate 		perror("read");
8437c478bd9Sstevel@tonic-gate 		exit(ERR_SYSCALL);
8447c478bd9Sstevel@tonic-gate 	}
8457c478bd9Sstevel@tonic-gate 	invalid_version = FALSE;
8467c478bd9Sstevel@tonic-gate 	if (prof_hdr.h_major_ver > PROF_MAJOR_VERSION)
8477c478bd9Sstevel@tonic-gate 		invalid_version = TRUE;
8487c478bd9Sstevel@tonic-gate 	else if (prof_hdr.h_major_ver == PROF_MAJOR_VERSION) {
8497c478bd9Sstevel@tonic-gate 		if (prof_hdr.h_minor_ver > PROF_MINOR_VERSION)
8507c478bd9Sstevel@tonic-gate 		invalid_version = FALSE;
8517c478bd9Sstevel@tonic-gate 	}
8527c478bd9Sstevel@tonic-gate 	if (invalid_version) {
85392ed1782Smike_s 		(void) fprintf(stderr,
85492ed1782Smike_s 		    "%s: mon.out version %d.%d not supported\n",
85592ed1782Smike_s 		    cmdname, prof_hdr.h_major_ver, prof_hdr.h_minor_ver);
8567c478bd9Sstevel@tonic-gate 		exit(ERR_INPUT);
8577c478bd9Sstevel@tonic-gate 	}
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate 	/*
8627c478bd9Sstevel@tonic-gate 	 * Map mon.out onto memory.
8637c478bd9Sstevel@tonic-gate 	 */
8647c478bd9Sstevel@tonic-gate 	if (stat(mon_fn, &monout_stat) == -1) {
8657c478bd9Sstevel@tonic-gate 		perror(mon_fn);
8667c478bd9Sstevel@tonic-gate 		exit(ERR_SYSCALL);
8677c478bd9Sstevel@tonic-gate 	}
8687c478bd9Sstevel@tonic-gate 	if ((fmem = mmap(0, monout_stat.st_size,
869b9bd317cSab 	    PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
8707c478bd9Sstevel@tonic-gate 		perror("mmap");
8717c478bd9Sstevel@tonic-gate 		exit(ERR_SYSCALL);
8727c478bd9Sstevel@tonic-gate 	}
87392ed1782Smike_s 	(void) close(fd);
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 	/*
8777c478bd9Sstevel@tonic-gate 	 * Now, read program executable's symbol table. Also save it's
8787c478bd9Sstevel@tonic-gate 	 * stat in aout_stat for use while processing mon.out
8797c478bd9Sstevel@tonic-gate 	 */
8807c478bd9Sstevel@tonic-gate 	if (stat(sym_fn, &aout_stat) == -1) {
8817c478bd9Sstevel@tonic-gate 		perror(sym_fn);
8827c478bd9Sstevel@tonic-gate 		exit(ERR_SYSCALL);
8837c478bd9Sstevel@tonic-gate 	}
8847c478bd9Sstevel@tonic-gate 	get_aout_syms(sym_fn, &modules);
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 	/*
8877c478bd9Sstevel@tonic-gate 	 * Process the mon.out, all shared objects it references
8887c478bd9Sstevel@tonic-gate 	 * and collect statistics on ticks spent in each function,
8897c478bd9Sstevel@tonic-gate 	 * number of calls, etc.
8907c478bd9Sstevel@tonic-gate 	 */
8917c478bd9Sstevel@tonic-gate 	process_mon_out(fmem, monout_stat.st_size);
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 	/*
8947c478bd9Sstevel@tonic-gate 	 * Based on the flags and the statistics we've got, create
8957c478bd9Sstevel@tonic-gate 	 * a list of relevant symbols whose profiling details should
8967c478bd9Sstevel@tonic-gate 	 * be printed
8977c478bd9Sstevel@tonic-gate 	 */
8987c478bd9Sstevel@tonic-gate 	collect_profsyms();
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate 	/*
9017c478bd9Sstevel@tonic-gate 	 * Check for duplicate names in output. We need to print the
9027c478bd9Sstevel@tonic-gate 	 * module id's if verbose. Also, if we are sorting by name anyway,
9037c478bd9Sstevel@tonic-gate 	 * we don't need to check for duplicates here. We'll do that later.
9047c478bd9Sstevel@tonic-gate 	 */
9057c478bd9Sstevel@tonic-gate 	if ((flags & F_VERBOSE) && (sort_flag != BY_NAME))
9067c478bd9Sstevel@tonic-gate 		check_dupnames();
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate 	/*
9097c478bd9Sstevel@tonic-gate 	 * Print output
9107c478bd9Sstevel@tonic-gate 	 */
9117c478bd9Sstevel@tonic-gate 	print_profile_data();
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 
91492ed1782Smike_s 	(void) munmap(fmem, monout_stat.st_size);
9157c478bd9Sstevel@tonic-gate 	exit(0);
9167c478bd9Sstevel@tonic-gate }
917