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