xref: /illumos-gate/usr/src/lib/libc/port/gen/mon.c (revision 7c478bd95313f5f23a4c958a745db2134aa0324)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
28*7c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
29*7c478bd9Sstevel@tonic-gate 
30*7c478bd9Sstevel@tonic-gate /*
31*7c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
32*7c478bd9Sstevel@tonic-gate  * The Regents of the University of California
33*7c478bd9Sstevel@tonic-gate  * All Rights Reserved
34*7c478bd9Sstevel@tonic-gate  *
35*7c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
36*7c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
37*7c478bd9Sstevel@tonic-gate  * contributors.
38*7c478bd9Sstevel@tonic-gate  */
39*7c478bd9Sstevel@tonic-gate 
40*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
41*7c478bd9Sstevel@tonic-gate 
42*7c478bd9Sstevel@tonic-gate /*
43*7c478bd9Sstevel@tonic-gate  *	Environment variable PROFDIR added such that:
44*7c478bd9Sstevel@tonic-gate  *		If PROFDIR doesn't exist, "mon.out" is produced as before.
45*7c478bd9Sstevel@tonic-gate  *		If PROFDIR = NULL, no profiling output is produced.
46*7c478bd9Sstevel@tonic-gate  *		If PROFDIR = string, "string/pid.progname" is produced,
47*7c478bd9Sstevel@tonic-gate  *		  where name consists of argv[0] suitably massaged.
48*7c478bd9Sstevel@tonic-gate  *
49*7c478bd9Sstevel@tonic-gate  *
50*7c478bd9Sstevel@tonic-gate  *	Routines:
51*7c478bd9Sstevel@tonic-gate  *		(global) _monitor	init, cleanup for prof(1)iling
52*7c478bd9Sstevel@tonic-gate  *		(global) _mcount	function call counter
53*7c478bd9Sstevel@tonic-gate  *		(global) _mcount_newent	call count entry manager
54*7c478bd9Sstevel@tonic-gate  *		(static) _mnewblock	call count block allocator
55*7c478bd9Sstevel@tonic-gate  *
56*7c478bd9Sstevel@tonic-gate  *
57*7c478bd9Sstevel@tonic-gate  *	Monitor(), coordinating with mcount(), mcount_newent() and mnewblock(),
58*7c478bd9Sstevel@tonic-gate  *	maintains a series of one or more blocks of prof-profiling
59*7c478bd9Sstevel@tonic-gate  *	information.  These blocks are added in response to calls to
60*7c478bd9Sstevel@tonic-gate  *	monitor() (explicitly or via mcrt[01]'s _start) and, via mcount()'s
61*7c478bd9Sstevel@tonic-gate  *	calls to mcount_newent() thence to mnewblock().
62*7c478bd9Sstevel@tonic-gate  *	The blocks are tracked via a linked list of block anchors,
63*7c478bd9Sstevel@tonic-gate  *	which each point to a block.
64*7c478bd9Sstevel@tonic-gate  *
65*7c478bd9Sstevel@tonic-gate  *
66*7c478bd9Sstevel@tonic-gate  *	An anchor points forward, backward and 'down' (to a block).
67*7c478bd9Sstevel@tonic-gate  *	A block has the profiling information, and consists of
68*7c478bd9Sstevel@tonic-gate  *	three regions: a header, a function call count array region,
69*7c478bd9Sstevel@tonic-gate  *	and an optional execution histogram region, as illustrated below.
70*7c478bd9Sstevel@tonic-gate  *
71*7c478bd9Sstevel@tonic-gate  *
72*7c478bd9Sstevel@tonic-gate  *		 "anchor"
73*7c478bd9Sstevel@tonic-gate  *		+========+
74*7c478bd9Sstevel@tonic-gate  *	prior<--|        |-->next anchor
75*7c478bd9Sstevel@tonic-gate  *	anchor	|        |
76*7c478bd9Sstevel@tonic-gate  *		+========+
77*7c478bd9Sstevel@tonic-gate  *		 |
78*7c478bd9Sstevel@tonic-gate  *		 |
79*7c478bd9Sstevel@tonic-gate  *		 V "block"
80*7c478bd9Sstevel@tonic-gate  *		+-----------+
81*7c478bd9Sstevel@tonic-gate  *		+  header   +
82*7c478bd9Sstevel@tonic-gate  *		+-----------+
83*7c478bd9Sstevel@tonic-gate  *		+           +
84*7c478bd9Sstevel@tonic-gate  *		+ fcn call  +	// data collected by mcount
85*7c478bd9Sstevel@tonic-gate  *		+  counts   +
86*7c478bd9Sstevel@tonic-gate  *		+  array    +
87*7c478bd9Sstevel@tonic-gate  *		+           +
88*7c478bd9Sstevel@tonic-gate  *		+-----------+
89*7c478bd9Sstevel@tonic-gate  *		+           +
90*7c478bd9Sstevel@tonic-gate  *		+ execution +	// data collected by system call,
91*7c478bd9Sstevel@tonic-gate  *		+ profile   +	// profil(2) (assumed ALWAYS specified
92*7c478bd9Sstevel@tonic-gate  *		+ histogram +	// by monitor()-caller, even if small;
93*7c478bd9Sstevel@tonic-gate  *		+           +	// never specified by mnewblock()).
94*7c478bd9Sstevel@tonic-gate  *		+-----------+
95*7c478bd9Sstevel@tonic-gate  *
96*7c478bd9Sstevel@tonic-gate  *	The first time monitor() is called, it sets up the chain
97*7c478bd9Sstevel@tonic-gate  *	by allocating an anchor and initializing countbase and countlimit
98*7c478bd9Sstevel@tonic-gate  *	to zero.  Everyone assumes that they start out zeroed.
99*7c478bd9Sstevel@tonic-gate  *
100*7c478bd9Sstevel@tonic-gate  *	When a user (or _start from mcrt[01]) calls monitor(), they
101*7c478bd9Sstevel@tonic-gate  *	register a buffer which contains the third region (either with
102*7c478bd9Sstevel@tonic-gate  *	a meaningful size, or so short that profil-ing is being shut off).
103*7c478bd9Sstevel@tonic-gate  *
104*7c478bd9Sstevel@tonic-gate  *	For each fcn, the first time it calls mcount(), mcount calls
105*7c478bd9Sstevel@tonic-gate  *	mcount_newent(), which parcels out the fcn call count entries
106*7c478bd9Sstevel@tonic-gate  *	from the current block, until they are exausted; then it calls
107*7c478bd9Sstevel@tonic-gate  *	mnewblock().
108*7c478bd9Sstevel@tonic-gate  *
109*7c478bd9Sstevel@tonic-gate  *	Mnewbloc() allocates a block Without a third region, and
110*7c478bd9Sstevel@tonic-gate  *	links in a new associated anchor, adding a new anchor&block pair
111*7c478bd9Sstevel@tonic-gate  *	to the linked list.  Each new mnewblock() block or user block,
112*7c478bd9Sstevel@tonic-gate  *	is added to the list as it comes in, FIFO.
113*7c478bd9Sstevel@tonic-gate  *
114*7c478bd9Sstevel@tonic-gate  *	When monitor() is called to close up shop, it writes out
115*7c478bd9Sstevel@tonic-gate  *	a summarizing header, ALL the fcn call counts from ALL
116*7c478bd9Sstevel@tonic-gate  *	the blocks, and the Last specified execution histogram
117*7c478bd9Sstevel@tonic-gate  *	(currently there is no neat way to accumulate that info).
118*7c478bd9Sstevel@tonic-gate  *	This preserves all call count information, even when
119*7c478bd9Sstevel@tonic-gate  *	new blocks are specified.
120*7c478bd9Sstevel@tonic-gate  *
121*7c478bd9Sstevel@tonic-gate  *	NOTE - no block passed to monitor() may be freed, until
122*7c478bd9Sstevel@tonic-gate  *	it is called to clean up!!!!
123*7c478bd9Sstevel@tonic-gate  *
124*7c478bd9Sstevel@tonic-gate  */
125*7c478bd9Sstevel@tonic-gate 
126*7c478bd9Sstevel@tonic-gate #pragma weak monitor = _monitor
127*7c478bd9Sstevel@tonic-gate 
128*7c478bd9Sstevel@tonic-gate #include "synonyms.h"
129*7c478bd9Sstevel@tonic-gate #include "mtlib.h"
130*7c478bd9Sstevel@tonic-gate #include "libc.h"
131*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
132*7c478bd9Sstevel@tonic-gate #include <string.h>
133*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
134*7c478bd9Sstevel@tonic-gate #include <stdio.h>
135*7c478bd9Sstevel@tonic-gate #include <errno.h>
136*7c478bd9Sstevel@tonic-gate #include <mon.h>
137*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
138*7c478bd9Sstevel@tonic-gate #include <unistd.h>
139*7c478bd9Sstevel@tonic-gate #include <thread.h>
140*7c478bd9Sstevel@tonic-gate #include <synch.h>
141*7c478bd9Sstevel@tonic-gate 
142*7c478bd9Sstevel@tonic-gate #define	PROFDIR	"PROFDIR"
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate static mutex_t mon_lock = DEFAULTMUTEX;
145*7c478bd9Sstevel@tonic-gate 
146*7c478bd9Sstevel@tonic-gate char **___Argv = NULL; /* initialized to argv array by mcrt0 (if loaded) */
147*7c478bd9Sstevel@tonic-gate 
148*7c478bd9Sstevel@tonic-gate /*
149*7c478bd9Sstevel@tonic-gate  * countbase and countlimit are used to parcel out
150*7c478bd9Sstevel@tonic-gate  * the pc,count cells from the current block one at
151*7c478bd9Sstevel@tonic-gate  * a time to each profiled function, the first time
152*7c478bd9Sstevel@tonic-gate  * that function is called.
153*7c478bd9Sstevel@tonic-gate  * When countbase reaches countlimit, mcount() calls
154*7c478bd9Sstevel@tonic-gate  * mnewblock() to link in a new block.
155*7c478bd9Sstevel@tonic-gate  *
156*7c478bd9Sstevel@tonic-gate  * Only monitor/mcount/mcount_newent/mnewblock() should change these!!
157*7c478bd9Sstevel@tonic-gate  * Correct that: only these routines are ABLE to change these;
158*7c478bd9Sstevel@tonic-gate  * countbase/countlimit are now STATIC!
159*7c478bd9Sstevel@tonic-gate  */
160*7c478bd9Sstevel@tonic-gate static char *countbase;		/* addr of next pc,count cell to use in block */
161*7c478bd9Sstevel@tonic-gate static char *_countlimit;	/* addr lim for cells (addr after last cell) */
162*7c478bd9Sstevel@tonic-gate 
163*7c478bd9Sstevel@tonic-gate typedef struct anchor	ANCHOR;
164*7c478bd9Sstevel@tonic-gate 
165*7c478bd9Sstevel@tonic-gate struct anchor {
166*7c478bd9Sstevel@tonic-gate 	ANCHOR  *next, *prior;	/* forward, backward ptrs for list */
167*7c478bd9Sstevel@tonic-gate 	struct hdr  *monBuffer;	/* 'down' ptr, to block */
168*7c478bd9Sstevel@tonic-gate 	short  flags;		/* indicators - has histogram designation */
169*7c478bd9Sstevel@tonic-gate 
170*7c478bd9Sstevel@tonic-gate 	int  histSize;		/* if has region3, this is size. */
171*7c478bd9Sstevel@tonic-gate };
172*7c478bd9Sstevel@tonic-gate 
173*7c478bd9Sstevel@tonic-gate #define	HAS_HISTOGRAM	0x0001		/* this buffer has a histogram */
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate static ANCHOR 	*curAnchor = NULL;	/* addr of anchor for current block */
176*7c478bd9Sstevel@tonic-gate static ANCHOR    firstAnchor;		/* the first anchor to use */
177*7c478bd9Sstevel@tonic-gate 					/* - hopefully the Only one needed */
178*7c478bd9Sstevel@tonic-gate 					/* a speedup for most cases. */
179*7c478bd9Sstevel@tonic-gate static char *mon_out;
180*7c478bd9Sstevel@tonic-gate 
181*7c478bd9Sstevel@tonic-gate static int writeBlocks(void);
182*7c478bd9Sstevel@tonic-gate static void _mnewblock(void);
183*7c478bd9Sstevel@tonic-gate struct cnt *_mcount_newent(void);
184*7c478bd9Sstevel@tonic-gate 
185*7c478bd9Sstevel@tonic-gate /*
186*7c478bd9Sstevel@tonic-gate  * int (*alowpc)(), (*ahighpc)(); boundaries of text to be monitored
187*7c478bd9Sstevel@tonic-gate  * WORD *buffer;	ptr to space for monitor data(WORDs)
188*7c478bd9Sstevel@tonic-gate  * size_t bufsize;	size of above space(in WORDs)
189*7c478bd9Sstevel@tonic-gate  * size_t nfunc;	max no. of functions whose calls are counted
190*7c478bd9Sstevel@tonic-gate  *			(default nfunc is 300 on PDP11, 600 on others)
191*7c478bd9Sstevel@tonic-gate  */
192*7c478bd9Sstevel@tonic-gate void
193*7c478bd9Sstevel@tonic-gate monitor(int (*alowpc)(void), int (*ahighpc)(void), WORD *buffer,
194*7c478bd9Sstevel@tonic-gate 	size_t bufsize, size_t nfunc)
195*7c478bd9Sstevel@tonic-gate {
196*7c478bd9Sstevel@tonic-gate 	uint_t scale;
197*7c478bd9Sstevel@tonic-gate 	long text;
198*7c478bd9Sstevel@tonic-gate 	char *s;
199*7c478bd9Sstevel@tonic-gate 	struct hdr *hdrp;
200*7c478bd9Sstevel@tonic-gate 	ANCHOR  *newanchp;
201*7c478bd9Sstevel@tonic-gate 	size_t	ssiz;
202*7c478bd9Sstevel@tonic-gate 	int error;
203*7c478bd9Sstevel@tonic-gate 	char	*lowpc = (char *)alowpc;
204*7c478bd9Sstevel@tonic-gate 	char	*highpc = (char *)ahighpc;
205*7c478bd9Sstevel@tonic-gate 
206*7c478bd9Sstevel@tonic-gate 	lmutex_lock(&mon_lock);
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate 	if (lowpc == NULL) {		/* true only at the end */
209*7c478bd9Sstevel@tonic-gate 		error = 0;
210*7c478bd9Sstevel@tonic-gate 		if (curAnchor != NULL) { /* if anything was collected!.. */
211*7c478bd9Sstevel@tonic-gate 			profil(NULL, 0, 0, 0);
212*7c478bd9Sstevel@tonic-gate 			if (writeBlocks() == 0)
213*7c478bd9Sstevel@tonic-gate 				error = errno;
214*7c478bd9Sstevel@tonic-gate 		}
215*7c478bd9Sstevel@tonic-gate 		lmutex_unlock(&mon_lock);
216*7c478bd9Sstevel@tonic-gate 		if (error) {
217*7c478bd9Sstevel@tonic-gate 			errno = error;
218*7c478bd9Sstevel@tonic-gate 			perror(mon_out);
219*7c478bd9Sstevel@tonic-gate 		}
220*7c478bd9Sstevel@tonic-gate 		return;
221*7c478bd9Sstevel@tonic-gate 	}
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 	/*
224*7c478bd9Sstevel@tonic-gate 	 * Ok - they want to submit a block for immediate use, for
225*7c478bd9Sstevel@tonic-gate 	 *	function call count consumption, and execution profile
226*7c478bd9Sstevel@tonic-gate 	 *	histogram computation.
227*7c478bd9Sstevel@tonic-gate 	 * If the block fails sanity tests, just bag it.
228*7c478bd9Sstevel@tonic-gate 	 * Next thing - get name to use. If PROFDIR is NULL, let's
229*7c478bd9Sstevel@tonic-gate 	 *	get out now - they want No Profiling done.
230*7c478bd9Sstevel@tonic-gate 	 *
231*7c478bd9Sstevel@tonic-gate 	 * Otherwise:
232*7c478bd9Sstevel@tonic-gate 	 * Set the block hdr cells.
233*7c478bd9Sstevel@tonic-gate 	 * Get an anchor for the block, and link the anchor+block onto
234*7c478bd9Sstevel@tonic-gate 	 *	the end of the chain.
235*7c478bd9Sstevel@tonic-gate 	 * Init the grabba-cell externs (countbase/limit) for this block.
236*7c478bd9Sstevel@tonic-gate 	 * Finally, call profil and return.
237*7c478bd9Sstevel@tonic-gate 	 */
238*7c478bd9Sstevel@tonic-gate 
239*7c478bd9Sstevel@tonic-gate 	ssiz = ((sizeof (struct hdr) + nfunc * sizeof (struct cnt)) /
240*7c478bd9Sstevel@tonic-gate 	    sizeof (WORD));
241*7c478bd9Sstevel@tonic-gate 	if (ssiz >= bufsize || lowpc >= highpc) {
242*7c478bd9Sstevel@tonic-gate 		lmutex_unlock(&mon_lock);
243*7c478bd9Sstevel@tonic-gate 		return;
244*7c478bd9Sstevel@tonic-gate 	}
245*7c478bd9Sstevel@tonic-gate 
246*7c478bd9Sstevel@tonic-gate 	if ((s = getenv(PROFDIR)) == NULL) { /* PROFDIR not in environment */
247*7c478bd9Sstevel@tonic-gate 		mon_out = MON_OUT; /* use default "mon.out" */
248*7c478bd9Sstevel@tonic-gate 	} else if (*s == '\0') { /* value of PROFDIR is NULL */
249*7c478bd9Sstevel@tonic-gate 		lmutex_unlock(&mon_lock);
250*7c478bd9Sstevel@tonic-gate 		return; /* no profiling on this run */
251*7c478bd9Sstevel@tonic-gate 	} else { /* construct "PROFDIR/pid.progname" */
252*7c478bd9Sstevel@tonic-gate 		int n;
253*7c478bd9Sstevel@tonic-gate 		pid_t pid;
254*7c478bd9Sstevel@tonic-gate 		char *name;
255*7c478bd9Sstevel@tonic-gate 		size_t len;
256*7c478bd9Sstevel@tonic-gate 
257*7c478bd9Sstevel@tonic-gate 		len = strlen(s);
258*7c478bd9Sstevel@tonic-gate 		/* 15 is space for /pid.mon.out\0, if necessary */
259*7c478bd9Sstevel@tonic-gate 		if ((mon_out = libc_malloc(len + strlen(___Argv[0]) + 15))
260*7c478bd9Sstevel@tonic-gate 		    == NULL) {
261*7c478bd9Sstevel@tonic-gate 			lmutex_unlock(&mon_lock);
262*7c478bd9Sstevel@tonic-gate 			perror("");
263*7c478bd9Sstevel@tonic-gate 			return;
264*7c478bd9Sstevel@tonic-gate 		}
265*7c478bd9Sstevel@tonic-gate 		(void) strcpy(mon_out, s);
266*7c478bd9Sstevel@tonic-gate 		name = mon_out + len;
267*7c478bd9Sstevel@tonic-gate 		*name++ = '/'; /* two slashes won't hurt */
268*7c478bd9Sstevel@tonic-gate 
269*7c478bd9Sstevel@tonic-gate 		if ((pid = getpid()) <= 0) /* extra test just in case */
270*7c478bd9Sstevel@tonic-gate 			pid = 1; /* getpid returns something inappropriate */
271*7c478bd9Sstevel@tonic-gate 
272*7c478bd9Sstevel@tonic-gate 		/* suppress leading zeros */
273*7c478bd9Sstevel@tonic-gate 		for (n = 10000; n > pid; n /= 10)
274*7c478bd9Sstevel@tonic-gate 			;
275*7c478bd9Sstevel@tonic-gate 		for (; ; n /= 10) {
276*7c478bd9Sstevel@tonic-gate 			*name++ = pid/n + '0';
277*7c478bd9Sstevel@tonic-gate 			if (n == 1)
278*7c478bd9Sstevel@tonic-gate 			    break;
279*7c478bd9Sstevel@tonic-gate 			pid %= n;
280*7c478bd9Sstevel@tonic-gate 		}
281*7c478bd9Sstevel@tonic-gate 		*name++ = '.';
282*7c478bd9Sstevel@tonic-gate 
283*7c478bd9Sstevel@tonic-gate 		if (___Argv != NULL) {	/* mcrt0.s executed */
284*7c478bd9Sstevel@tonic-gate 			if ((s = strrchr(___Argv[0], '/')) != NULL)
285*7c478bd9Sstevel@tonic-gate 				(void) strcpy(name, s + 1);
286*7c478bd9Sstevel@tonic-gate 			else
287*7c478bd9Sstevel@tonic-gate 				(void) strcpy(name, ___Argv[0]);
288*7c478bd9Sstevel@tonic-gate 		} else {
289*7c478bd9Sstevel@tonic-gate 			(void) strcpy(name, MON_OUT);
290*7c478bd9Sstevel@tonic-gate 		}
291*7c478bd9Sstevel@tonic-gate 	}
292*7c478bd9Sstevel@tonic-gate 
293*7c478bd9Sstevel@tonic-gate 
294*7c478bd9Sstevel@tonic-gate 	hdrp = (struct hdr *)(uintptr_t)buffer;	/* initialize 1st region */
295*7c478bd9Sstevel@tonic-gate 	hdrp->lpc = lowpc;
296*7c478bd9Sstevel@tonic-gate 	hdrp->hpc = highpc;
297*7c478bd9Sstevel@tonic-gate 	hdrp->nfns = nfunc;
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate 	/* get an anchor for the block */
300*7c478bd9Sstevel@tonic-gate 	newanchp = (curAnchor == NULL) ? &firstAnchor :
301*7c478bd9Sstevel@tonic-gate 	    (ANCHOR *)libc_malloc(sizeof (ANCHOR));
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 	if (newanchp == NULL) {
304*7c478bd9Sstevel@tonic-gate 		lmutex_unlock(&mon_lock);
305*7c478bd9Sstevel@tonic-gate 		perror("monitor");
306*7c478bd9Sstevel@tonic-gate 		return;
307*7c478bd9Sstevel@tonic-gate 	}
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate 	/* link anchor+block into chain */
310*7c478bd9Sstevel@tonic-gate 	newanchp->monBuffer = hdrp;		/* new, down. */
311*7c478bd9Sstevel@tonic-gate 	newanchp->next  = NULL;			/* new, forward to NULL. */
312*7c478bd9Sstevel@tonic-gate 	newanchp->prior = curAnchor;		/* new, backward. */
313*7c478bd9Sstevel@tonic-gate 	if (curAnchor != NULL)
314*7c478bd9Sstevel@tonic-gate 		curAnchor->next = newanchp;	/* old, forward to new. */
315*7c478bd9Sstevel@tonic-gate 	newanchp->flags = HAS_HISTOGRAM;	/* note it has a histgm area */
316*7c478bd9Sstevel@tonic-gate 
317*7c478bd9Sstevel@tonic-gate 	/* got it - enable use by mcount() */
318*7c478bd9Sstevel@tonic-gate 	countbase  = (char *)buffer + sizeof (struct hdr);
319*7c478bd9Sstevel@tonic-gate 	_countlimit = countbase + (nfunc * sizeof (struct cnt));
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	/* (set size of region 3) */
322*7c478bd9Sstevel@tonic-gate 	newanchp->histSize = (int)
323*7c478bd9Sstevel@tonic-gate 	    (bufsize * sizeof (WORD) - (_countlimit - (char *)buffer));
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate 
326*7c478bd9Sstevel@tonic-gate 	/* done w/regions 1 + 2: setup 3  to activate profil processing. */
327*7c478bd9Sstevel@tonic-gate 	buffer += ssiz;			/* move ptr past 2'nd region */
328*7c478bd9Sstevel@tonic-gate 	bufsize -= ssiz;		/* no. WORDs in third region */
329*7c478bd9Sstevel@tonic-gate 					/* no. WORDs of text */
330*7c478bd9Sstevel@tonic-gate 	text = (highpc - lowpc + sizeof (WORD) - 1) / sizeof (WORD);
331*7c478bd9Sstevel@tonic-gate 
332*7c478bd9Sstevel@tonic-gate 	/*
333*7c478bd9Sstevel@tonic-gate 	 * scale is a 16 bit fixed point fraction with the decimal
334*7c478bd9Sstevel@tonic-gate 	 * point at the left
335*7c478bd9Sstevel@tonic-gate 	 */
336*7c478bd9Sstevel@tonic-gate 	if (bufsize < text) {
337*7c478bd9Sstevel@tonic-gate 		/* make sure cast is done first! */
338*7c478bd9Sstevel@tonic-gate 		double temp = (double)bufsize;
339*7c478bd9Sstevel@tonic-gate 		scale = (uint_t)((temp * (long)0200000L) / text);
340*7c478bd9Sstevel@tonic-gate 	} else {
341*7c478bd9Sstevel@tonic-gate 		/* scale must be less than 1 */
342*7c478bd9Sstevel@tonic-gate 		scale = 0xffff;
343*7c478bd9Sstevel@tonic-gate 	}
344*7c478bd9Sstevel@tonic-gate 	bufsize *= sizeof (WORD);	/* bufsize into # bytes */
345*7c478bd9Sstevel@tonic-gate 	profil(buffer, bufsize, (ulong_t)lowpc, scale);
346*7c478bd9Sstevel@tonic-gate 
347*7c478bd9Sstevel@tonic-gate 
348*7c478bd9Sstevel@tonic-gate 	curAnchor = newanchp;	/* make latest addition, the cur anchor */
349*7c478bd9Sstevel@tonic-gate 	lmutex_unlock(&mon_lock);
350*7c478bd9Sstevel@tonic-gate }
351*7c478bd9Sstevel@tonic-gate 
352*7c478bd9Sstevel@tonic-gate /*
353*7c478bd9Sstevel@tonic-gate  * writeBlocks() - write accumulated profiling info, std fmt.
354*7c478bd9Sstevel@tonic-gate  *
355*7c478bd9Sstevel@tonic-gate  * This routine collects the function call counts, and the
356*7c478bd9Sstevel@tonic-gate  * last specified profil buffer, and writes out one combined
357*7c478bd9Sstevel@tonic-gate  * 'pseudo-block', as expected by current and former versions
358*7c478bd9Sstevel@tonic-gate  * of prof.
359*7c478bd9Sstevel@tonic-gate  */
360*7c478bd9Sstevel@tonic-gate static int
361*7c478bd9Sstevel@tonic-gate writeBlocks(void)
362*7c478bd9Sstevel@tonic-gate {
363*7c478bd9Sstevel@tonic-gate 	int fd;
364*7c478bd9Sstevel@tonic-gate 	int ok;
365*7c478bd9Sstevel@tonic-gate 	ANCHOR *ap;		/* temp anchor ptr */
366*7c478bd9Sstevel@tonic-gate 	struct hdr sum;		/* summary header (for 'pseudo' block) */
367*7c478bd9Sstevel@tonic-gate 	ANCHOR *histp;		/* anchor with histogram to use */
368*7c478bd9Sstevel@tonic-gate 
369*7c478bd9Sstevel@tonic-gate 	if ((fd = creat(mon_out, 0666)) < 0)
370*7c478bd9Sstevel@tonic-gate 		return (0);
371*7c478bd9Sstevel@tonic-gate 
372*7c478bd9Sstevel@tonic-gate 	/*
373*7c478bd9Sstevel@tonic-gate 	 * this loop (1) computes # funct cts total
374*7c478bd9Sstevel@tonic-gate 	 *  (2) finds anchor of last block w / hist(histp)
375*7c478bd9Sstevel@tonic-gate 	 */
376*7c478bd9Sstevel@tonic-gate 	histp = NULL;
377*7c478bd9Sstevel@tonic-gate 	for (sum.nfns = 0, ap = &firstAnchor; ap != NULL; ap = ap->next) {
378*7c478bd9Sstevel@tonic-gate 		sum.nfns += ap->monBuffer->nfns; /* accum num of cells */
379*7c478bd9Sstevel@tonic-gate 		if (ap->flags & HAS_HISTOGRAM)
380*7c478bd9Sstevel@tonic-gate 			histp = ap;	 /* remember lastone with a histgm */
381*7c478bd9Sstevel@tonic-gate 	}
382*7c478bd9Sstevel@tonic-gate 
383*7c478bd9Sstevel@tonic-gate 
384*7c478bd9Sstevel@tonic-gate 	/* copy pc range from effective histgm */
385*7c478bd9Sstevel@tonic-gate 	sum.lpc = histp->monBuffer->lpc;
386*7c478bd9Sstevel@tonic-gate 	sum.hpc = histp->monBuffer->hpc;
387*7c478bd9Sstevel@tonic-gate 
388*7c478bd9Sstevel@tonic-gate 	ok = (write(fd, (char *)&sum, sizeof (sum)) == sizeof (sum));
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate 	if (ok) {		/* if the hdr went out ok.. */
391*7c478bd9Sstevel@tonic-gate 		size_t amt;
392*7c478bd9Sstevel@tonic-gate 		char *p;
393*7c478bd9Sstevel@tonic-gate 
394*7c478bd9Sstevel@tonic-gate 		/* write out the count arrays (region 2's) */
395*7c478bd9Sstevel@tonic-gate 		for (ap = &firstAnchor; ok && ap != NULL; ap = ap->next) {
396*7c478bd9Sstevel@tonic-gate 			amt = ap->monBuffer->nfns * sizeof (struct cnt);
397*7c478bd9Sstevel@tonic-gate 			p = (char *)ap->monBuffer + sizeof (struct hdr);
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate 			ok = (write(fd, p, amt) == amt);
400*7c478bd9Sstevel@tonic-gate 		}
401*7c478bd9Sstevel@tonic-gate 
402*7c478bd9Sstevel@tonic-gate 		/* count arrays out; write out histgm area */
403*7c478bd9Sstevel@tonic-gate 		if (ok) {
404*7c478bd9Sstevel@tonic-gate 			p = (char *)histp->monBuffer + sizeof (struct hdr) +
405*7c478bd9Sstevel@tonic-gate 			    (histp->monBuffer->nfns * sizeof (struct cnt));
406*7c478bd9Sstevel@tonic-gate 			amt = histp->histSize;
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate 			ok = (write(fd, p, amt) == amt);
409*7c478bd9Sstevel@tonic-gate 
410*7c478bd9Sstevel@tonic-gate 		}
411*7c478bd9Sstevel@tonic-gate 	}
412*7c478bd9Sstevel@tonic-gate 
413*7c478bd9Sstevel@tonic-gate 	(void) close(fd);
414*7c478bd9Sstevel@tonic-gate 
415*7c478bd9Sstevel@tonic-gate 	return (ok);	/* indicate success */
416*7c478bd9Sstevel@tonic-gate }
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate 
419*7c478bd9Sstevel@tonic-gate /*
420*7c478bd9Sstevel@tonic-gate  * mnewblock()-allocate and link in a new region1&2 block.
421*7c478bd9Sstevel@tonic-gate  *
422*7c478bd9Sstevel@tonic-gate  * This routine, called by mcount_newent(), allocates a new block
423*7c478bd9Sstevel@tonic-gate  * containing only regions 1 & 2 (hdr and fcn call count array),
424*7c478bd9Sstevel@tonic-gate  * and an associated anchor (see header comments), inits the
425*7c478bd9Sstevel@tonic-gate  * header (region 1) of the block, links the anchor into the
426*7c478bd9Sstevel@tonic-gate  * list, and resets the countbase/limit pointers.
427*7c478bd9Sstevel@tonic-gate  *
428*7c478bd9Sstevel@tonic-gate  * This routine cannot be called recursively, since (each) mcount
429*7c478bd9Sstevel@tonic-gate  * has a local lock which prevents recursive calls to mcount_newent.
430*7c478bd9Sstevel@tonic-gate  * See mcount_newent for more details.
431*7c478bd9Sstevel@tonic-gate  *
432*7c478bd9Sstevel@tonic-gate  */
433*7c478bd9Sstevel@tonic-gate 
434*7c478bd9Sstevel@tonic-gate #define	THISMANYFCNS	(MPROGS0*2)
435*7c478bd9Sstevel@tonic-gate 
436*7c478bd9Sstevel@tonic-gate /*
437*7c478bd9Sstevel@tonic-gate  * call libc_malloc() to get an anchor & a regn1&2 block, together
438*7c478bd9Sstevel@tonic-gate  */
439*7c478bd9Sstevel@tonic-gate #define	GETTHISMUCH	(sizeof (ANCHOR) + 	/* get an ANCHOR */  \
440*7c478bd9Sstevel@tonic-gate 			(sizeof (struct hdr) +	/* get Region 1 */   \
441*7c478bd9Sstevel@tonic-gate 			THISMANYFCNS * sizeof (struct cnt))) /* Region 2 */  \
442*7c478bd9Sstevel@tonic-gate 						/* but No region 3 */
443*7c478bd9Sstevel@tonic-gate 
444*7c478bd9Sstevel@tonic-gate 
445*7c478bd9Sstevel@tonic-gate static void
446*7c478bd9Sstevel@tonic-gate _mnewblock(void)
447*7c478bd9Sstevel@tonic-gate {
448*7c478bd9Sstevel@tonic-gate 	struct hdr *hdrp;
449*7c478bd9Sstevel@tonic-gate 	ANCHOR	*newanchp;
450*7c478bd9Sstevel@tonic-gate 	ANCHOR	*p;
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate 					/* get anchor And block, together */
453*7c478bd9Sstevel@tonic-gate 	p = libc_malloc(GETTHISMUCH);
454*7c478bd9Sstevel@tonic-gate 	if (p == NULL) {
455*7c478bd9Sstevel@tonic-gate 		perror("mcount(mnewblock)");
456*7c478bd9Sstevel@tonic-gate 		return;
457*7c478bd9Sstevel@tonic-gate 	}
458*7c478bd9Sstevel@tonic-gate 
459*7c478bd9Sstevel@tonic-gate 	newanchp = p;
460*7c478bd9Sstevel@tonic-gate 	hdrp = (struct hdr *)(p + 1);
461*7c478bd9Sstevel@tonic-gate 
462*7c478bd9Sstevel@tonic-gate 					/* initialize 1st region to dflts */
463*7c478bd9Sstevel@tonic-gate 	hdrp->lpc = 0;
464*7c478bd9Sstevel@tonic-gate 	hdrp->hpc = 0;
465*7c478bd9Sstevel@tonic-gate 	hdrp->nfns = THISMANYFCNS;
466*7c478bd9Sstevel@tonic-gate 
467*7c478bd9Sstevel@tonic-gate 					/* link anchor+block into chain */
468*7c478bd9Sstevel@tonic-gate 	newanchp->monBuffer = hdrp;		/* new, down. */
469*7c478bd9Sstevel@tonic-gate 	newanchp->next  = NULL;			/* new, forward to NULL. */
470*7c478bd9Sstevel@tonic-gate 	newanchp->prior = curAnchor;		/* new, backward. */
471*7c478bd9Sstevel@tonic-gate 	if (curAnchor != NULL)
472*7c478bd9Sstevel@tonic-gate 		curAnchor->next = newanchp;	/* old, forward to new. */
473*7c478bd9Sstevel@tonic-gate 	newanchp->flags = 0;		/* note that it has NO histgm area */
474*7c478bd9Sstevel@tonic-gate 
475*7c478bd9Sstevel@tonic-gate 					/* got it - enable use by mcount() */
476*7c478bd9Sstevel@tonic-gate 	countbase  = (char *)hdrp + sizeof (struct hdr);
477*7c478bd9Sstevel@tonic-gate 	_countlimit = countbase + (THISMANYFCNS * sizeof (struct cnt));
478*7c478bd9Sstevel@tonic-gate 
479*7c478bd9Sstevel@tonic-gate 	newanchp->histSize = 0;	/* (set size of region 3.. to 0) */
480*7c478bd9Sstevel@tonic-gate 
481*7c478bd9Sstevel@tonic-gate 
482*7c478bd9Sstevel@tonic-gate 	curAnchor = newanchp;		/* make latest addition, cur anchor */
483*7c478bd9Sstevel@tonic-gate }
484*7c478bd9Sstevel@tonic-gate 
485*7c478bd9Sstevel@tonic-gate /*
486*7c478bd9Sstevel@tonic-gate  * mcount_newent() -- call to get a new mcount call count entry.
487*7c478bd9Sstevel@tonic-gate  *
488*7c478bd9Sstevel@tonic-gate  * this function is called by _mcount to get a new call count entry
489*7c478bd9Sstevel@tonic-gate  * (struct cnt, in the region allocated by _monitor()), or to return
490*7c478bd9Sstevel@tonic-gate  * zero if profiling is off.
491*7c478bd9Sstevel@tonic-gate  *
492*7c478bd9Sstevel@tonic-gate  * This function acts as a funnel, an access function to make sure
493*7c478bd9Sstevel@tonic-gate  * that all instances of mcount (the one in the a.out, and any in
494*7c478bd9Sstevel@tonic-gate  * any shared objects) all get entries from the same array, and
495*7c478bd9Sstevel@tonic-gate  * all know when profiling is off.
496*7c478bd9Sstevel@tonic-gate  *
497*7c478bd9Sstevel@tonic-gate  * NOTE: when mcount calls this function, it sets a private flag
498*7c478bd9Sstevel@tonic-gate  * so that it does not call again until this function returns,
499*7c478bd9Sstevel@tonic-gate  * thus preventing recursion.
500*7c478bd9Sstevel@tonic-gate  *
501*7c478bd9Sstevel@tonic-gate  * At Worst, the mcount in either a shared object or the a.out
502*7c478bd9Sstevel@tonic-gate  * could call once, and then the mcount living in the shared object
503*7c478bd9Sstevel@tonic-gate  * with monitor could call a second time (i.e. libc.so.1, although
504*7c478bd9Sstevel@tonic-gate  * presently it does not have mcount in it).  This worst case
505*7c478bd9Sstevel@tonic-gate  * would involve Two active calls to mcount_newent, which it can
506*7c478bd9Sstevel@tonic-gate  * handle, since the second one would find a already-set value
507*7c478bd9Sstevel@tonic-gate  * in countbase.
508*7c478bd9Sstevel@tonic-gate  *
509*7c478bd9Sstevel@tonic-gate  * The only unfortunate result is that No new call counts
510*7c478bd9Sstevel@tonic-gate  * will be handed out until this function returns.
511*7c478bd9Sstevel@tonic-gate  * Thus if libc_malloc or other routines called inductively by
512*7c478bd9Sstevel@tonic-gate  * this routine have not yet been provided with a call count entry,
513*7c478bd9Sstevel@tonic-gate  * they will not get one until this function call is completed.
514*7c478bd9Sstevel@tonic-gate  * Thus a few calls to library routines during the course of
515*7c478bd9Sstevel@tonic-gate  * profiling setup, may not be counted.
516*7c478bd9Sstevel@tonic-gate  *
517*7c478bd9Sstevel@tonic-gate  * NOTE: countbase points at the next available entry, and
518*7c478bd9Sstevel@tonic-gate  * countlimit points past the last valid entry, in the current
519*7c478bd9Sstevel@tonic-gate  * function call counts array.
520*7c478bd9Sstevel@tonic-gate  *
521*7c478bd9Sstevel@tonic-gate  *
522*7c478bd9Sstevel@tonic-gate  * if profiling is off		// countbase==0
523*7c478bd9Sstevel@tonic-gate  *   just return 0
524*7c478bd9Sstevel@tonic-gate  *
525*7c478bd9Sstevel@tonic-gate  * else
526*7c478bd9Sstevel@tonic-gate  *   if need more entries	// because countbase points last valid entry
527*7c478bd9Sstevel@tonic-gate  *     link in a new block, resetting countbase and countlimit
528*7c478bd9Sstevel@tonic-gate  *   endif
529*7c478bd9Sstevel@tonic-gate  *   if Got more entries
530*7c478bd9Sstevel@tonic-gate  *     return pointer to the next available entry, and
531*7c478bd9Sstevel@tonic-gate  *     update pointer-to-next-slot before you return.
532*7c478bd9Sstevel@tonic-gate  *
533*7c478bd9Sstevel@tonic-gate  *   else			// failed to get more entries
534*7c478bd9Sstevel@tonic-gate  *     just return 0
535*7c478bd9Sstevel@tonic-gate  *
536*7c478bd9Sstevel@tonic-gate  *   endif
537*7c478bd9Sstevel@tonic-gate  * endif
538*7c478bd9Sstevel@tonic-gate  */
539*7c478bd9Sstevel@tonic-gate 
540*7c478bd9Sstevel@tonic-gate struct cnt *
541*7c478bd9Sstevel@tonic-gate _mcount_newent(void)
542*7c478bd9Sstevel@tonic-gate {
543*7c478bd9Sstevel@tonic-gate 	if (countbase == 0)
544*7c478bd9Sstevel@tonic-gate 		return (NULL);
545*7c478bd9Sstevel@tonic-gate 
546*7c478bd9Sstevel@tonic-gate 	if (countbase >= _countlimit)
547*7c478bd9Sstevel@tonic-gate 		_mnewblock();		/* get a new block; set countbase */
548*7c478bd9Sstevel@tonic-gate 
549*7c478bd9Sstevel@tonic-gate 	if (countbase != 0) {
550*7c478bd9Sstevel@tonic-gate 		struct cnt *cur_countbase = (struct cnt *)(uintptr_t)countbase;
551*7c478bd9Sstevel@tonic-gate 
552*7c478bd9Sstevel@tonic-gate 		countbase += sizeof (struct cnt);
553*7c478bd9Sstevel@tonic-gate 		return (cur_countbase);
554*7c478bd9Sstevel@tonic-gate 	}
555*7c478bd9Sstevel@tonic-gate 	return (NULL);
556*7c478bd9Sstevel@tonic-gate }
557