xref: /onnv-gate/usr/src/lib/libc/port/gen/mon.c (revision 6812:febeba71273d)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*6812Sraf  * Common Development and Distribution License (the "License").
6*6812Sraf  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
21*6812Sraf 
220Sstevel@tonic-gate /*
23*6812Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
280Sstevel@tonic-gate /*	  All Rights Reserved  	*/
290Sstevel@tonic-gate 
300Sstevel@tonic-gate /*
310Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
320Sstevel@tonic-gate  * The Regents of the University of California
330Sstevel@tonic-gate  * All Rights Reserved
340Sstevel@tonic-gate  *
350Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
360Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
370Sstevel@tonic-gate  * contributors.
380Sstevel@tonic-gate  */
390Sstevel@tonic-gate 
400Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate  *	Environment variable PROFDIR added such that:
440Sstevel@tonic-gate  *		If PROFDIR doesn't exist, "mon.out" is produced as before.
450Sstevel@tonic-gate  *		If PROFDIR = NULL, no profiling output is produced.
460Sstevel@tonic-gate  *		If PROFDIR = string, "string/pid.progname" is produced,
470Sstevel@tonic-gate  *		  where name consists of argv[0] suitably massaged.
480Sstevel@tonic-gate  *
490Sstevel@tonic-gate  *
500Sstevel@tonic-gate  *	Routines:
51*6812Sraf  *		(global) monitor	init, cleanup for prof(1)iling
520Sstevel@tonic-gate  *		(global) _mcount	function call counter
530Sstevel@tonic-gate  *		(global) _mcount_newent	call count entry manager
540Sstevel@tonic-gate  *		(static) _mnewblock	call count block allocator
550Sstevel@tonic-gate  *
560Sstevel@tonic-gate  *
570Sstevel@tonic-gate  *	Monitor(), coordinating with mcount(), mcount_newent() and mnewblock(),
580Sstevel@tonic-gate  *	maintains a series of one or more blocks of prof-profiling
590Sstevel@tonic-gate  *	information.  These blocks are added in response to calls to
600Sstevel@tonic-gate  *	monitor() (explicitly or via mcrt[01]'s _start) and, via mcount()'s
610Sstevel@tonic-gate  *	calls to mcount_newent() thence to mnewblock().
620Sstevel@tonic-gate  *	The blocks are tracked via a linked list of block anchors,
630Sstevel@tonic-gate  *	which each point to a block.
640Sstevel@tonic-gate  *
650Sstevel@tonic-gate  *
660Sstevel@tonic-gate  *	An anchor points forward, backward and 'down' (to a block).
670Sstevel@tonic-gate  *	A block has the profiling information, and consists of
680Sstevel@tonic-gate  *	three regions: a header, a function call count array region,
690Sstevel@tonic-gate  *	and an optional execution histogram region, as illustrated below.
700Sstevel@tonic-gate  *
710Sstevel@tonic-gate  *
720Sstevel@tonic-gate  *		 "anchor"
730Sstevel@tonic-gate  *		+========+
740Sstevel@tonic-gate  *	prior<--|        |-->next anchor
750Sstevel@tonic-gate  *	anchor	|        |
760Sstevel@tonic-gate  *		+========+
770Sstevel@tonic-gate  *		 |
780Sstevel@tonic-gate  *		 |
790Sstevel@tonic-gate  *		 V "block"
800Sstevel@tonic-gate  *		+-----------+
810Sstevel@tonic-gate  *		+  header   +
820Sstevel@tonic-gate  *		+-----------+
830Sstevel@tonic-gate  *		+           +
840Sstevel@tonic-gate  *		+ fcn call  +	// data collected by mcount
850Sstevel@tonic-gate  *		+  counts   +
860Sstevel@tonic-gate  *		+  array    +
870Sstevel@tonic-gate  *		+           +
880Sstevel@tonic-gate  *		+-----------+
890Sstevel@tonic-gate  *		+           +
900Sstevel@tonic-gate  *		+ execution +	// data collected by system call,
910Sstevel@tonic-gate  *		+ profile   +	// profil(2) (assumed ALWAYS specified
920Sstevel@tonic-gate  *		+ histogram +	// by monitor()-caller, even if small;
930Sstevel@tonic-gate  *		+           +	// never specified by mnewblock()).
940Sstevel@tonic-gate  *		+-----------+
950Sstevel@tonic-gate  *
960Sstevel@tonic-gate  *	The first time monitor() is called, it sets up the chain
970Sstevel@tonic-gate  *	by allocating an anchor and initializing countbase and countlimit
980Sstevel@tonic-gate  *	to zero.  Everyone assumes that they start out zeroed.
990Sstevel@tonic-gate  *
1000Sstevel@tonic-gate  *	When a user (or _start from mcrt[01]) calls monitor(), they
1010Sstevel@tonic-gate  *	register a buffer which contains the third region (either with
1020Sstevel@tonic-gate  *	a meaningful size, or so short that profil-ing is being shut off).
1030Sstevel@tonic-gate  *
1040Sstevel@tonic-gate  *	For each fcn, the first time it calls mcount(), mcount calls
1050Sstevel@tonic-gate  *	mcount_newent(), which parcels out the fcn call count entries
1060Sstevel@tonic-gate  *	from the current block, until they are exausted; then it calls
1070Sstevel@tonic-gate  *	mnewblock().
1080Sstevel@tonic-gate  *
1090Sstevel@tonic-gate  *	Mnewbloc() allocates a block Without a third region, and
1100Sstevel@tonic-gate  *	links in a new associated anchor, adding a new anchor&block pair
1110Sstevel@tonic-gate  *	to the linked list.  Each new mnewblock() block or user block,
1120Sstevel@tonic-gate  *	is added to the list as it comes in, FIFO.
1130Sstevel@tonic-gate  *
1140Sstevel@tonic-gate  *	When monitor() is called to close up shop, it writes out
1150Sstevel@tonic-gate  *	a summarizing header, ALL the fcn call counts from ALL
1160Sstevel@tonic-gate  *	the blocks, and the Last specified execution histogram
1170Sstevel@tonic-gate  *	(currently there is no neat way to accumulate that info).
1180Sstevel@tonic-gate  *	This preserves all call count information, even when
1190Sstevel@tonic-gate  *	new blocks are specified.
1200Sstevel@tonic-gate  *
1210Sstevel@tonic-gate  *	NOTE - no block passed to monitor() may be freed, until
1220Sstevel@tonic-gate  *	it is called to clean up!!!!
1230Sstevel@tonic-gate  *
1240Sstevel@tonic-gate  */
1250Sstevel@tonic-gate 
126*6812Sraf #pragma weak _monitor = monitor
1270Sstevel@tonic-gate 
128*6812Sraf #include "lint.h"
1290Sstevel@tonic-gate #include "mtlib.h"
1300Sstevel@tonic-gate #include "libc.h"
1310Sstevel@tonic-gate #include <sys/types.h>
1320Sstevel@tonic-gate #include <string.h>
1330Sstevel@tonic-gate #include <stdlib.h>
1340Sstevel@tonic-gate #include <stdio.h>
1350Sstevel@tonic-gate #include <errno.h>
1360Sstevel@tonic-gate #include <mon.h>
1370Sstevel@tonic-gate #include <fcntl.h>
1380Sstevel@tonic-gate #include <unistd.h>
1390Sstevel@tonic-gate #include <thread.h>
1400Sstevel@tonic-gate #include <synch.h>
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate #define	PROFDIR	"PROFDIR"
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate static mutex_t mon_lock = DEFAULTMUTEX;
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate char **___Argv = NULL; /* initialized to argv array by mcrt0 (if loaded) */
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate /*
1490Sstevel@tonic-gate  * countbase and countlimit are used to parcel out
1500Sstevel@tonic-gate  * the pc,count cells from the current block one at
1510Sstevel@tonic-gate  * a time to each profiled function, the first time
1520Sstevel@tonic-gate  * that function is called.
1530Sstevel@tonic-gate  * When countbase reaches countlimit, mcount() calls
1540Sstevel@tonic-gate  * mnewblock() to link in a new block.
1550Sstevel@tonic-gate  *
1560Sstevel@tonic-gate  * Only monitor/mcount/mcount_newent/mnewblock() should change these!!
1570Sstevel@tonic-gate  * Correct that: only these routines are ABLE to change these;
1580Sstevel@tonic-gate  * countbase/countlimit are now STATIC!
1590Sstevel@tonic-gate  */
1600Sstevel@tonic-gate static char *countbase;		/* addr of next pc,count cell to use in block */
161*6812Sraf static char *countlimit;	/* addr lim for cells (addr after last cell) */
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate typedef struct anchor	ANCHOR;
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate struct anchor {
1660Sstevel@tonic-gate 	ANCHOR  *next, *prior;	/* forward, backward ptrs for list */
1670Sstevel@tonic-gate 	struct hdr  *monBuffer;	/* 'down' ptr, to block */
1680Sstevel@tonic-gate 	short  flags;		/* indicators - has histogram designation */
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	int  histSize;		/* if has region3, this is size. */
1710Sstevel@tonic-gate };
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate #define	HAS_HISTOGRAM	0x0001		/* this buffer has a histogram */
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate static ANCHOR 	*curAnchor = NULL;	/* addr of anchor for current block */
1760Sstevel@tonic-gate static ANCHOR    firstAnchor;		/* the first anchor to use */
1770Sstevel@tonic-gate 					/* - hopefully the Only one needed */
1780Sstevel@tonic-gate 					/* a speedup for most cases. */
1790Sstevel@tonic-gate static char *mon_out;
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate static int writeBlocks(void);
1820Sstevel@tonic-gate static void _mnewblock(void);
1830Sstevel@tonic-gate struct cnt *_mcount_newent(void);
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /*
1860Sstevel@tonic-gate  * int (*alowpc)(), (*ahighpc)(); boundaries of text to be monitored
1870Sstevel@tonic-gate  * WORD *buffer;	ptr to space for monitor data(WORDs)
1880Sstevel@tonic-gate  * size_t bufsize;	size of above space(in WORDs)
1890Sstevel@tonic-gate  * size_t nfunc;	max no. of functions whose calls are counted
1900Sstevel@tonic-gate  *			(default nfunc is 300 on PDP11, 600 on others)
1910Sstevel@tonic-gate  */
1920Sstevel@tonic-gate void
monitor(int (* alowpc)(void),int (* ahighpc)(void),WORD * buffer,size_t bufsize,size_t nfunc)1930Sstevel@tonic-gate monitor(int (*alowpc)(void), int (*ahighpc)(void), WORD *buffer,
1940Sstevel@tonic-gate 	size_t bufsize, size_t nfunc)
1950Sstevel@tonic-gate {
1960Sstevel@tonic-gate 	uint_t scale;
1970Sstevel@tonic-gate 	long text;
1980Sstevel@tonic-gate 	char *s;
1990Sstevel@tonic-gate 	struct hdr *hdrp;
2000Sstevel@tonic-gate 	ANCHOR  *newanchp;
2010Sstevel@tonic-gate 	size_t	ssiz;
2020Sstevel@tonic-gate 	int error;
2030Sstevel@tonic-gate 	char	*lowpc = (char *)alowpc;
2040Sstevel@tonic-gate 	char	*highpc = (char *)ahighpc;
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	lmutex_lock(&mon_lock);
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	if (lowpc == NULL) {		/* true only at the end */
2090Sstevel@tonic-gate 		error = 0;
2100Sstevel@tonic-gate 		if (curAnchor != NULL) { /* if anything was collected!.. */
2110Sstevel@tonic-gate 			profil(NULL, 0, 0, 0);
2120Sstevel@tonic-gate 			if (writeBlocks() == 0)
2130Sstevel@tonic-gate 				error = errno;
2140Sstevel@tonic-gate 		}
2150Sstevel@tonic-gate 		lmutex_unlock(&mon_lock);
2160Sstevel@tonic-gate 		if (error) {
2170Sstevel@tonic-gate 			errno = error;
2180Sstevel@tonic-gate 			perror(mon_out);
2190Sstevel@tonic-gate 		}
2200Sstevel@tonic-gate 		return;
2210Sstevel@tonic-gate 	}
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	/*
2240Sstevel@tonic-gate 	 * Ok - they want to submit a block for immediate use, for
2250Sstevel@tonic-gate 	 *	function call count consumption, and execution profile
2260Sstevel@tonic-gate 	 *	histogram computation.
2270Sstevel@tonic-gate 	 * If the block fails sanity tests, just bag it.
2280Sstevel@tonic-gate 	 * Next thing - get name to use. If PROFDIR is NULL, let's
2290Sstevel@tonic-gate 	 *	get out now - they want No Profiling done.
2300Sstevel@tonic-gate 	 *
2310Sstevel@tonic-gate 	 * Otherwise:
2320Sstevel@tonic-gate 	 * Set the block hdr cells.
2330Sstevel@tonic-gate 	 * Get an anchor for the block, and link the anchor+block onto
2340Sstevel@tonic-gate 	 *	the end of the chain.
2350Sstevel@tonic-gate 	 * Init the grabba-cell externs (countbase/limit) for this block.
2360Sstevel@tonic-gate 	 * Finally, call profil and return.
2370Sstevel@tonic-gate 	 */
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	ssiz = ((sizeof (struct hdr) + nfunc * sizeof (struct cnt)) /
2400Sstevel@tonic-gate 	    sizeof (WORD));
2410Sstevel@tonic-gate 	if (ssiz >= bufsize || lowpc >= highpc) {
2420Sstevel@tonic-gate 		lmutex_unlock(&mon_lock);
2430Sstevel@tonic-gate 		return;
2440Sstevel@tonic-gate 	}
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	if ((s = getenv(PROFDIR)) == NULL) { /* PROFDIR not in environment */
2470Sstevel@tonic-gate 		mon_out = MON_OUT; /* use default "mon.out" */
2480Sstevel@tonic-gate 	} else if (*s == '\0') { /* value of PROFDIR is NULL */
2490Sstevel@tonic-gate 		lmutex_unlock(&mon_lock);
2500Sstevel@tonic-gate 		return; /* no profiling on this run */
2510Sstevel@tonic-gate 	} else { /* construct "PROFDIR/pid.progname" */
2520Sstevel@tonic-gate 		int n;
2530Sstevel@tonic-gate 		pid_t pid;
2540Sstevel@tonic-gate 		char *name;
2550Sstevel@tonic-gate 		size_t len;
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 		len = strlen(s);
2580Sstevel@tonic-gate 		/* 15 is space for /pid.mon.out\0, if necessary */
2590Sstevel@tonic-gate 		if ((mon_out = libc_malloc(len + strlen(___Argv[0]) + 15))
2600Sstevel@tonic-gate 		    == NULL) {
2610Sstevel@tonic-gate 			lmutex_unlock(&mon_lock);
2620Sstevel@tonic-gate 			perror("");
2630Sstevel@tonic-gate 			return;
2640Sstevel@tonic-gate 		}
2650Sstevel@tonic-gate 		(void) strcpy(mon_out, s);
2660Sstevel@tonic-gate 		name = mon_out + len;
2670Sstevel@tonic-gate 		*name++ = '/'; /* two slashes won't hurt */
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 		if ((pid = getpid()) <= 0) /* extra test just in case */
2700Sstevel@tonic-gate 			pid = 1; /* getpid returns something inappropriate */
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 		/* suppress leading zeros */
2730Sstevel@tonic-gate 		for (n = 10000; n > pid; n /= 10)
2740Sstevel@tonic-gate 			;
2750Sstevel@tonic-gate 		for (; ; n /= 10) {
2760Sstevel@tonic-gate 			*name++ = pid/n + '0';
2770Sstevel@tonic-gate 			if (n == 1)
278*6812Sraf 				break;
2790Sstevel@tonic-gate 			pid %= n;
2800Sstevel@tonic-gate 		}
2810Sstevel@tonic-gate 		*name++ = '.';
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 		if (___Argv != NULL) {	/* mcrt0.s executed */
2840Sstevel@tonic-gate 			if ((s = strrchr(___Argv[0], '/')) != NULL)
2850Sstevel@tonic-gate 				(void) strcpy(name, s + 1);
2860Sstevel@tonic-gate 			else
2870Sstevel@tonic-gate 				(void) strcpy(name, ___Argv[0]);
2880Sstevel@tonic-gate 		} else {
2890Sstevel@tonic-gate 			(void) strcpy(name, MON_OUT);
2900Sstevel@tonic-gate 		}
2910Sstevel@tonic-gate 	}
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	hdrp = (struct hdr *)(uintptr_t)buffer;	/* initialize 1st region */
2950Sstevel@tonic-gate 	hdrp->lpc = lowpc;
2960Sstevel@tonic-gate 	hdrp->hpc = highpc;
2970Sstevel@tonic-gate 	hdrp->nfns = nfunc;
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	/* get an anchor for the block */
3000Sstevel@tonic-gate 	newanchp = (curAnchor == NULL) ? &firstAnchor :
3010Sstevel@tonic-gate 	    (ANCHOR *)libc_malloc(sizeof (ANCHOR));
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 	if (newanchp == NULL) {
3040Sstevel@tonic-gate 		lmutex_unlock(&mon_lock);
3050Sstevel@tonic-gate 		perror("monitor");
3060Sstevel@tonic-gate 		return;
3070Sstevel@tonic-gate 	}
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	/* link anchor+block into chain */
3100Sstevel@tonic-gate 	newanchp->monBuffer = hdrp;		/* new, down. */
3110Sstevel@tonic-gate 	newanchp->next  = NULL;			/* new, forward to NULL. */
3120Sstevel@tonic-gate 	newanchp->prior = curAnchor;		/* new, backward. */
3130Sstevel@tonic-gate 	if (curAnchor != NULL)
3140Sstevel@tonic-gate 		curAnchor->next = newanchp;	/* old, forward to new. */
3150Sstevel@tonic-gate 	newanchp->flags = HAS_HISTOGRAM;	/* note it has a histgm area */
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	/* got it - enable use by mcount() */
3180Sstevel@tonic-gate 	countbase  = (char *)buffer + sizeof (struct hdr);
319*6812Sraf 	countlimit = countbase + (nfunc * sizeof (struct cnt));
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	/* (set size of region 3) */
3220Sstevel@tonic-gate 	newanchp->histSize = (int)
323*6812Sraf 	    (bufsize * sizeof (WORD) - (countlimit - (char *)buffer));
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	/* done w/regions 1 + 2: setup 3  to activate profil processing. */
3270Sstevel@tonic-gate 	buffer += ssiz;			/* move ptr past 2'nd region */
3280Sstevel@tonic-gate 	bufsize -= ssiz;		/* no. WORDs in third region */
3290Sstevel@tonic-gate 					/* no. WORDs of text */
3300Sstevel@tonic-gate 	text = (highpc - lowpc + sizeof (WORD) - 1) / sizeof (WORD);
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	/*
3330Sstevel@tonic-gate 	 * scale is a 16 bit fixed point fraction with the decimal
3340Sstevel@tonic-gate 	 * point at the left
3350Sstevel@tonic-gate 	 */
3360Sstevel@tonic-gate 	if (bufsize < text) {
3370Sstevel@tonic-gate 		/* make sure cast is done first! */
3380Sstevel@tonic-gate 		double temp = (double)bufsize;
3390Sstevel@tonic-gate 		scale = (uint_t)((temp * (long)0200000L) / text);
3400Sstevel@tonic-gate 	} else {
3410Sstevel@tonic-gate 		/* scale must be less than 1 */
3420Sstevel@tonic-gate 		scale = 0xffff;
3430Sstevel@tonic-gate 	}
3440Sstevel@tonic-gate 	bufsize *= sizeof (WORD);	/* bufsize into # bytes */
3450Sstevel@tonic-gate 	profil(buffer, bufsize, (ulong_t)lowpc, scale);
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	curAnchor = newanchp;	/* make latest addition, the cur anchor */
3490Sstevel@tonic-gate 	lmutex_unlock(&mon_lock);
3500Sstevel@tonic-gate }
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate /*
3530Sstevel@tonic-gate  * writeBlocks() - write accumulated profiling info, std fmt.
3540Sstevel@tonic-gate  *
3550Sstevel@tonic-gate  * This routine collects the function call counts, and the
3560Sstevel@tonic-gate  * last specified profil buffer, and writes out one combined
3570Sstevel@tonic-gate  * 'pseudo-block', as expected by current and former versions
3580Sstevel@tonic-gate  * of prof.
3590Sstevel@tonic-gate  */
3600Sstevel@tonic-gate static int
writeBlocks(void)3610Sstevel@tonic-gate writeBlocks(void)
3620Sstevel@tonic-gate {
3630Sstevel@tonic-gate 	int fd;
3640Sstevel@tonic-gate 	int ok;
3650Sstevel@tonic-gate 	ANCHOR *ap;		/* temp anchor ptr */
3660Sstevel@tonic-gate 	struct hdr sum;		/* summary header (for 'pseudo' block) */
3670Sstevel@tonic-gate 	ANCHOR *histp;		/* anchor with histogram to use */
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate 	if ((fd = creat(mon_out, 0666)) < 0)
3700Sstevel@tonic-gate 		return (0);
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 	/*
3730Sstevel@tonic-gate 	 * this loop (1) computes # funct cts total
3740Sstevel@tonic-gate 	 *  (2) finds anchor of last block w / hist(histp)
3750Sstevel@tonic-gate 	 */
3760Sstevel@tonic-gate 	histp = NULL;
3770Sstevel@tonic-gate 	for (sum.nfns = 0, ap = &firstAnchor; ap != NULL; ap = ap->next) {
3780Sstevel@tonic-gate 		sum.nfns += ap->monBuffer->nfns; /* accum num of cells */
3790Sstevel@tonic-gate 		if (ap->flags & HAS_HISTOGRAM)
3800Sstevel@tonic-gate 			histp = ap;	 /* remember lastone with a histgm */
3810Sstevel@tonic-gate 	}
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	/* copy pc range from effective histgm */
3850Sstevel@tonic-gate 	sum.lpc = histp->monBuffer->lpc;
3860Sstevel@tonic-gate 	sum.hpc = histp->monBuffer->hpc;
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	ok = (write(fd, (char *)&sum, sizeof (sum)) == sizeof (sum));
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	if (ok) {		/* if the hdr went out ok.. */
3910Sstevel@tonic-gate 		size_t amt;
3920Sstevel@tonic-gate 		char *p;
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate 		/* write out the count arrays (region 2's) */
3950Sstevel@tonic-gate 		for (ap = &firstAnchor; ok && ap != NULL; ap = ap->next) {
3960Sstevel@tonic-gate 			amt = ap->monBuffer->nfns * sizeof (struct cnt);
3970Sstevel@tonic-gate 			p = (char *)ap->monBuffer + sizeof (struct hdr);
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 			ok = (write(fd, p, amt) == amt);
4000Sstevel@tonic-gate 		}
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 		/* count arrays out; write out histgm area */
4030Sstevel@tonic-gate 		if (ok) {
4040Sstevel@tonic-gate 			p = (char *)histp->monBuffer + sizeof (struct hdr) +
4050Sstevel@tonic-gate 			    (histp->monBuffer->nfns * sizeof (struct cnt));
4060Sstevel@tonic-gate 			amt = histp->histSize;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 			ok = (write(fd, p, amt) == amt);
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 		}
4110Sstevel@tonic-gate 	}
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 	(void) close(fd);
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	return (ok);	/* indicate success */
4160Sstevel@tonic-gate }
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate /*
4200Sstevel@tonic-gate  * mnewblock()-allocate and link in a new region1&2 block.
4210Sstevel@tonic-gate  *
4220Sstevel@tonic-gate  * This routine, called by mcount_newent(), allocates a new block
4230Sstevel@tonic-gate  * containing only regions 1 & 2 (hdr and fcn call count array),
4240Sstevel@tonic-gate  * and an associated anchor (see header comments), inits the
4250Sstevel@tonic-gate  * header (region 1) of the block, links the anchor into the
4260Sstevel@tonic-gate  * list, and resets the countbase/limit pointers.
4270Sstevel@tonic-gate  *
4280Sstevel@tonic-gate  * This routine cannot be called recursively, since (each) mcount
4290Sstevel@tonic-gate  * has a local lock which prevents recursive calls to mcount_newent.
4300Sstevel@tonic-gate  * See mcount_newent for more details.
4310Sstevel@tonic-gate  *
4320Sstevel@tonic-gate  */
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate #define	THISMANYFCNS	(MPROGS0*2)
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate /*
4370Sstevel@tonic-gate  * call libc_malloc() to get an anchor & a regn1&2 block, together
4380Sstevel@tonic-gate  */
4390Sstevel@tonic-gate #define	GETTHISMUCH	(sizeof (ANCHOR) + 	/* get an ANCHOR */  \
4400Sstevel@tonic-gate 			(sizeof (struct hdr) +	/* get Region 1 */   \
4410Sstevel@tonic-gate 			THISMANYFCNS * sizeof (struct cnt))) /* Region 2 */  \
4420Sstevel@tonic-gate 						/* but No region 3 */
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate static void
_mnewblock(void)4460Sstevel@tonic-gate _mnewblock(void)
4470Sstevel@tonic-gate {
4480Sstevel@tonic-gate 	struct hdr *hdrp;
4490Sstevel@tonic-gate 	ANCHOR	*newanchp;
4500Sstevel@tonic-gate 	ANCHOR	*p;
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 					/* get anchor And block, together */
4530Sstevel@tonic-gate 	p = libc_malloc(GETTHISMUCH);
4540Sstevel@tonic-gate 	if (p == NULL) {
4550Sstevel@tonic-gate 		perror("mcount(mnewblock)");
4560Sstevel@tonic-gate 		return;
4570Sstevel@tonic-gate 	}
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	newanchp = p;
4600Sstevel@tonic-gate 	hdrp = (struct hdr *)(p + 1);
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 					/* initialize 1st region to dflts */
4630Sstevel@tonic-gate 	hdrp->lpc = 0;
4640Sstevel@tonic-gate 	hdrp->hpc = 0;
4650Sstevel@tonic-gate 	hdrp->nfns = THISMANYFCNS;
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 					/* link anchor+block into chain */
4680Sstevel@tonic-gate 	newanchp->monBuffer = hdrp;		/* new, down. */
4690Sstevel@tonic-gate 	newanchp->next  = NULL;			/* new, forward to NULL. */
4700Sstevel@tonic-gate 	newanchp->prior = curAnchor;		/* new, backward. */
4710Sstevel@tonic-gate 	if (curAnchor != NULL)
4720Sstevel@tonic-gate 		curAnchor->next = newanchp;	/* old, forward to new. */
4730Sstevel@tonic-gate 	newanchp->flags = 0;		/* note that it has NO histgm area */
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate 					/* got it - enable use by mcount() */
4760Sstevel@tonic-gate 	countbase  = (char *)hdrp + sizeof (struct hdr);
477*6812Sraf 	countlimit = countbase + (THISMANYFCNS * sizeof (struct cnt));
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	newanchp->histSize = 0;	/* (set size of region 3.. to 0) */
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	curAnchor = newanchp;		/* make latest addition, cur anchor */
4830Sstevel@tonic-gate }
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate /*
4860Sstevel@tonic-gate  * mcount_newent() -- call to get a new mcount call count entry.
4870Sstevel@tonic-gate  *
4880Sstevel@tonic-gate  * this function is called by _mcount to get a new call count entry
489*6812Sraf  * (struct cnt, in the region allocated by monitor()), or to return
4900Sstevel@tonic-gate  * zero if profiling is off.
4910Sstevel@tonic-gate  *
4920Sstevel@tonic-gate  * This function acts as a funnel, an access function to make sure
4930Sstevel@tonic-gate  * that all instances of mcount (the one in the a.out, and any in
4940Sstevel@tonic-gate  * any shared objects) all get entries from the same array, and
4950Sstevel@tonic-gate  * all know when profiling is off.
4960Sstevel@tonic-gate  *
4970Sstevel@tonic-gate  * NOTE: when mcount calls this function, it sets a private flag
4980Sstevel@tonic-gate  * so that it does not call again until this function returns,
4990Sstevel@tonic-gate  * thus preventing recursion.
5000Sstevel@tonic-gate  *
5010Sstevel@tonic-gate  * At Worst, the mcount in either a shared object or the a.out
5020Sstevel@tonic-gate  * could call once, and then the mcount living in the shared object
5030Sstevel@tonic-gate  * with monitor could call a second time (i.e. libc.so.1, although
5040Sstevel@tonic-gate  * presently it does not have mcount in it).  This worst case
5050Sstevel@tonic-gate  * would involve Two active calls to mcount_newent, which it can
5060Sstevel@tonic-gate  * handle, since the second one would find a already-set value
5070Sstevel@tonic-gate  * in countbase.
5080Sstevel@tonic-gate  *
5090Sstevel@tonic-gate  * The only unfortunate result is that No new call counts
5100Sstevel@tonic-gate  * will be handed out until this function returns.
5110Sstevel@tonic-gate  * Thus if libc_malloc or other routines called inductively by
5120Sstevel@tonic-gate  * this routine have not yet been provided with a call count entry,
5130Sstevel@tonic-gate  * they will not get one until this function call is completed.
5140Sstevel@tonic-gate  * Thus a few calls to library routines during the course of
5150Sstevel@tonic-gate  * profiling setup, may not be counted.
5160Sstevel@tonic-gate  *
5170Sstevel@tonic-gate  * NOTE: countbase points at the next available entry, and
5180Sstevel@tonic-gate  * countlimit points past the last valid entry, in the current
5190Sstevel@tonic-gate  * function call counts array.
5200Sstevel@tonic-gate  *
5210Sstevel@tonic-gate  *
5220Sstevel@tonic-gate  * if profiling is off		// countbase==0
5230Sstevel@tonic-gate  *   just return 0
5240Sstevel@tonic-gate  *
5250Sstevel@tonic-gate  * else
5260Sstevel@tonic-gate  *   if need more entries	// because countbase points last valid entry
5270Sstevel@tonic-gate  *     link in a new block, resetting countbase and countlimit
5280Sstevel@tonic-gate  *   endif
5290Sstevel@tonic-gate  *   if Got more entries
5300Sstevel@tonic-gate  *     return pointer to the next available entry, and
5310Sstevel@tonic-gate  *     update pointer-to-next-slot before you return.
5320Sstevel@tonic-gate  *
5330Sstevel@tonic-gate  *   else			// failed to get more entries
5340Sstevel@tonic-gate  *     just return 0
5350Sstevel@tonic-gate  *
5360Sstevel@tonic-gate  *   endif
5370Sstevel@tonic-gate  * endif
5380Sstevel@tonic-gate  */
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate struct cnt *
_mcount_newent(void)5410Sstevel@tonic-gate _mcount_newent(void)
5420Sstevel@tonic-gate {
5430Sstevel@tonic-gate 	if (countbase == 0)
5440Sstevel@tonic-gate 		return (NULL);
5450Sstevel@tonic-gate 
546*6812Sraf 	if (countbase >= countlimit)
5470Sstevel@tonic-gate 		_mnewblock();		/* get a new block; set countbase */
5480Sstevel@tonic-gate 
5490Sstevel@tonic-gate 	if (countbase != 0) {
5500Sstevel@tonic-gate 		struct cnt *cur_countbase = (struct cnt *)(uintptr_t)countbase;
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 		countbase += sizeof (struct cnt);
5530Sstevel@tonic-gate 		return (cur_countbase);
5540Sstevel@tonic-gate 	}
5550Sstevel@tonic-gate 	return (NULL);
5560Sstevel@tonic-gate }
557