17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
57257d1braf * Common Development and Distribution License (the "License").
67257d1braf * You may not use this file except in compliance with the License.
77c478bdstevel@tonic-gate *
87c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bdstevel@tonic-gate * See the License for the specific language governing permissions
117c478bdstevel@tonic-gate * and limitations under the License.
127c478bdstevel@tonic-gate *
137c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bdstevel@tonic-gate *
197c478bdstevel@tonic-gate * CDDL HEADER END
207c478bdstevel@tonic-gate */
217257d1braf
227c478bdstevel@tonic-gate/*
237257d1braf * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bdstevel@tonic-gate * Use is subject to license terms.
257c478bdstevel@tonic-gate */
267c478bdstevel@tonic-gate
277c478bdstevel@tonic-gate/*	Copyright (c) 1988 AT&T	*/
287c478bdstevel@tonic-gate/*	  All Rights Reserved  	*/
297c478bdstevel@tonic-gate
307c478bdstevel@tonic-gate/*
317c478bdstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988
327c478bdstevel@tonic-gate * The Regents of the University of California
337c478bdstevel@tonic-gate * All Rights Reserved
347c478bdstevel@tonic-gate *
357c478bdstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from
367c478bdstevel@tonic-gate * software developed by the University of California, Berkeley, and its
377c478bdstevel@tonic-gate * contributors.
387c478bdstevel@tonic-gate */
397c478bdstevel@tonic-gate
407c478bdstevel@tonic-gate#pragma ident	"%Z%%M%	%I%	%E% SMI"
417c478bdstevel@tonic-gate
427c478bdstevel@tonic-gate/*
437c478bdstevel@tonic-gate *	Environment variable PROFDIR added such that:
447c478bdstevel@tonic-gate *		If PROFDIR doesn't exist, "mon.out" is produced as before.
457c478bdstevel@tonic-gate *		If PROFDIR = NULL, no profiling output is produced.
467c478bdstevel@tonic-gate *		If PROFDIR = string, "string/pid.progname" is produced,
477c478bdstevel@tonic-gate *		  where name consists of argv[0] suitably massaged.
487c478bdstevel@tonic-gate *
497c478bdstevel@tonic-gate *
507c478bdstevel@tonic-gate *	Routines:
517257d1braf *		(global) monitor	init, cleanup for prof(1)iling
527c478bdstevel@tonic-gate *		(global) _mcount	function call counter
537c478bdstevel@tonic-gate *		(global) _mcount_newent	call count entry manager
547c478bdstevel@tonic-gate *		(static) _mnewblock	call count block allocator
557c478bdstevel@tonic-gate *
567c478bdstevel@tonic-gate *
577c478bdstevel@tonic-gate *	Monitor(), coordinating with mcount(), mcount_newent() and mnewblock(),
587c478bdstevel@tonic-gate *	maintains a series of one or more blocks of prof-profiling
597c478bdstevel@tonic-gate *	information.  These blocks are added in response to calls to
607c478bdstevel@tonic-gate *	monitor() (explicitly or via mcrt[01]'s _start) and, via mcount()'s
617c478bdstevel@tonic-gate *	calls to mcount_newent() thence to mnewblock().
627c478bdstevel@tonic-gate *	The blocks are tracked via a linked list of block anchors,
637c478bdstevel@tonic-gate *	which each point to a block.
647c478bdstevel@tonic-gate *
657c478bdstevel@tonic-gate *
667c478bdstevel@tonic-gate *	An anchor points forward, backward and 'down' (to a block).
677c478bdstevel@tonic-gate *	A block has the profiling information, and consists of
687c478bdstevel@tonic-gate *	three regions: a header, a function call count array region,
697c478bdstevel@tonic-gate *	and an optional execution histogram region, as illustrated below.
707c478bdstevel@tonic-gate *
717c478bdstevel@tonic-gate *
727c478bdstevel@tonic-gate *		 "anchor"
737c478bdstevel@tonic-gate *		+========+
747c478bdstevel@tonic-gate *	prior<--|        |-->next anchor
757c478bdstevel@tonic-gate *	anchor	|        |
767c478bdstevel@tonic-gate *		+========+
777c478bdstevel@tonic-gate *		 |
787c478bdstevel@tonic-gate *		 |
797c478bdstevel@tonic-gate *		 V "block"
807c478bdstevel@tonic-gate *		+-----------+
817c478bdstevel@tonic-gate *		+  header   +
827c478bdstevel@tonic-gate *		+-----------+
837c478bdstevel@tonic-gate *		+           +
847c478bdstevel@tonic-gate *		+ fcn call  +	// data collected by mcount
857c478bdstevel@tonic-gate *		+  counts   +
867c478bdstevel@tonic-gate *		+  array    +
877c478bdstevel@tonic-gate *		+           +
887c478bdstevel@tonic-gate *		+-----------+
897c478bdstevel@tonic-gate *		+           +
907c478bdstevel@tonic-gate *		+ execution +	// data collected by system call,
917c478bdstevel@tonic-gate *		+ profile   +	// profil(2) (assumed ALWAYS specified
927c478bdstevel@tonic-gate *		+ histogram +	// by monitor()-caller, even if small;
937c478bdstevel@tonic-gate *		+           +	// never specified by mnewblock()).
947c478bdstevel@tonic-gate *		+-----------+
957c478bdstevel@tonic-gate *
967c478bdstevel@tonic-gate *	The first time monitor() is called, it sets up the chain
977c478bdstevel@tonic-gate *	by allocating an anchor and initializing countbase and countlimit
987c478bdstevel@tonic-gate *	to zero.  Everyone assumes that they start out zeroed.
997c478bdstevel@tonic-gate *
1007c478bdstevel@tonic-gate *	When a user (or _start from mcrt[01]) calls monitor(), they
1017c478bdstevel@tonic-gate *	register a buffer which contains the third region (either with
1027c478bdstevel@tonic-gate *	a meaningful size, or so short that profil-ing is being shut off).
1037c478bdstevel@tonic-gate *
1047c478bdstevel@tonic-gate *	For each fcn, the first time it calls mcount(), mcount calls
1057c478bdstevel@tonic-gate *	mcount_newent(), which parcels out the fcn call count entries
1067c478bdstevel@tonic-gate *	from the current block, until they are exausted; then it calls
1077c478bdstevel@tonic-gate *	mnewblock().
1087c478bdstevel@tonic-gate *
1097c478bdstevel@tonic-gate *	Mnewbloc() allocates a block Without a third region, and
1107c478bdstevel@tonic-gate *	links in a new associated anchor, adding a new anchor&block pair
1117c478bdstevel@tonic-gate *	to the linked list.  Each new mnewblock() block or user block,
1127c478bdstevel@tonic-gate *	is added to the list as it comes in, FIFO.
1137c478bdstevel@tonic-gate *
1147c478bdstevel@tonic-gate *	When monitor() is called to close up shop, it writes out
1157c478bdstevel@tonic-gate *	a summarizing header, ALL the fcn call counts from ALL
1167c478bdstevel@tonic-gate *	the blocks, and the Last specified execution histogram
1177c478bdstevel@tonic-gate *	(currently there is no neat way to accumulate that info).
1187c478bdstevel@tonic-gate *	This preserves all call count information, even when
1197c478bdstevel@tonic-gate *	new blocks are specified.
1207c478bdstevel@tonic-gate *
1217c478bdstevel@tonic-gate *	NOTE - no block passed to monitor() may be freed, until
1227c478bdstevel@tonic-gate *	it is called to clean up!!!!
1237c478bdstevel@tonic-gate *
1247c478bdstevel@tonic-gate */
1257c478bdstevel@tonic-gate
1267257d1braf#pragma weak _monitor = monitor
1277c478bdstevel@tonic-gate
1287257d1braf#include "lint.h"
1297c478bdstevel@tonic-gate#include "mtlib.h"
1307c478bdstevel@tonic-gate#include "libc.h"
1317c478bdstevel@tonic-gate#include <sys/types.h>
1327c478bdstevel@tonic-gate#include <string.h>
1337c478bdstevel@tonic-gate#include <stdlib.h>
1347c478bdstevel@tonic-gate#include <stdio.h>
1357c478bdstevel@tonic-gate#include <errno.h>
1367c478bdstevel@tonic-gate#include <mon.h>
1377c478bdstevel@tonic-gate#include <fcntl.h>
1387c478bdstevel@tonic-gate#include <unistd.h>
1397c478bdstevel@tonic-gate#include <thread.h>
1407c478bdstevel@tonic-gate#include <synch.h>
1417c478bdstevel@tonic-gate
1427c478bdstevel@tonic-gate#define	PROFDIR	"PROFDIR"
1437c478bdstevel@tonic-gate
1447c478bdstevel@tonic-gatestatic mutex_t mon_lock = DEFAULTMUTEX;
1457c478bdstevel@tonic-gate
1467c478bdstevel@tonic-gatechar **___Argv = NULL; /* initialized to argv array by mcrt0 (if loaded) */
1477c478bdstevel@tonic-gate
1487c478bdstevel@tonic-gate/*
1497c478bdstevel@tonic-gate * countbase and countlimit are used to parcel out
1507c478bdstevel@tonic-gate * the pc,count cells from the current block one at
1517c478bdstevel@tonic-gate * a time to each profiled function, the first time
1527c478bdstevel@tonic-gate * that function is called.
1537c478bdstevel@tonic-gate * When countbase reaches countlimit, mcount() calls
1547c478bdstevel@tonic-gate * mnewblock() to link in a new block.
1557c478bdstevel@tonic-gate *
1567c478bdstevel@tonic-gate * Only monitor/mcount/mcount_newent/mnewblock() should change these!!
1577c478bdstevel@tonic-gate * Correct that: only these routines are ABLE to change these;
1587c478bdstevel@tonic-gate * countbase/countlimit are now STATIC!
1597c478bdstevel@tonic-gate */
1607c478bdstevel@tonic-gatestatic char *countbase;		/* addr of next pc,count cell to use in block */
1617257d1brafstatic char *countlimit;	/* addr lim for cells (addr after last cell) */
1627c478bdstevel@tonic-gate
1637c478bdstevel@tonic-gatetypedef struct anchor	ANCHOR;
1647c478bdstevel@tonic-gate
1657c478bdstevel@tonic-gatestruct anchor {
1667c478bdstevel@tonic-gate	ANCHOR  *next, *prior;	/* forward, backward ptrs for list */
1677c478bdstevel@tonic-gate	struct hdr  *monBuffer;	/* 'down' ptr, to block */
1687c478bdstevel@tonic-gate	short  flags;		/* indicators - has histogram designation */
1697c478bdstevel@tonic-gate
1707c478bdstevel@tonic-gate	int  histSize;		/* if has region3, this is size. */
1717c478bdstevel@tonic-gate};
1727c478bdstevel@tonic-gate
1737c478bdstevel@tonic-gate#define	HAS_HISTOGRAM	0x0001		/* this buffer has a histogram */
1747c478bdstevel@tonic-gate
1757c478bdstevel@tonic-gatestatic ANCHOR 	*curAnchor = NULL;	/* addr of anchor for current block */
1767c478bdstevel@tonic-gatestatic ANCHOR    firstAnchor;		/* the first anchor to use */
1777c478bdstevel@tonic-gate					/* - hopefully the Only one needed */
1787c478bdstevel@tonic-gate					/* a speedup for most cases. */
1797c478bdstevel@tonic-gatestatic char *mon_out;
1807c478bdstevel@tonic-gate
1817c478bdstevel@tonic-gatestatic int writeBlocks(void);
1827c478bdstevel@tonic-gatestatic void _mnewblock(void);
1837c478bdstevel@tonic-gatestruct cnt *_mcount_newent(void);
1847c478bdstevel@tonic-gate
1857c478bdstevel@tonic-gate/*
1867c478bdstevel@tonic-gate * int (*alowpc)(), (*ahighpc)(); boundaries of text to be monitored
1877c478bdstevel@tonic-gate * WORD *buffer;	ptr to space for monitor data(WORDs)
1887c478bdstevel@tonic-gate * size_t bufsize;	size of above space(in WORDs)
1897c478bdstevel@tonic-gate * size_t nfunc;	max no. of functions whose calls are counted
1907c478bdstevel@tonic-gate *			(default nfunc is 300 on PDP11, 600 on others)
1917c478bdstevel@tonic-gate */
1927c478bdstevel@tonic-gatevoid
1937c478bdstevel@tonic-gatemonitor(int (*alowpc)(void), int (*ahighpc)(void), WORD *buffer,
1947c478bdstevel@tonic-gate	size_t bufsize, size_t nfunc)
1957c478bdstevel@tonic-gate{
1967c478bdstevel@tonic-gate	uint_t scale;
1977c478bdstevel@tonic-gate	long text;
1987c478bdstevel@tonic-gate	char *s;
1997c478bdstevel@tonic-gate	struct hdr *hdrp;
2007c478bdstevel@tonic-gate	ANCHOR  *newanchp;
2017c478bdstevel@tonic-gate	size_t	ssiz;
2027c478bdstevel@tonic-gate	int error;
2037c478bdstevel@tonic-gate	char	*lowpc = (char *)alowpc;
2047c478bdstevel@tonic-gate	char	*highpc = (char *)ahighpc;
2057c478bdstevel@tonic-gate
2067c478bdstevel@tonic-gate	lmutex_lock(&mon_lock);
2077c478bdstevel@tonic-gate
2087c478bdstevel@tonic-gate	if (lowpc == NULL) {		/* true only at the end */
2097c478bdstevel@tonic-gate		error = 0;
2107c478bdstevel@tonic-gate		if (curAnchor != NULL) { /* if anything was collected!.. */
2117c478bdstevel@tonic-gate			profil(NULL, 0, 0, 0);
2127c478bdstevel@tonic-gate			if (writeBlocks() == 0)
2137c478bdstevel@tonic-gate				error = errno;
2147c478bdstevel@tonic-gate		}
2157c478bdstevel@tonic-gate		lmutex_unlock(&mon_lock);
2167c478bdstevel@tonic-gate		if (error) {
2177c478bdstevel@tonic-gate			errno = error;
2187c478bdstevel@tonic-gate			perror(mon_out);
2197c478bdstevel@tonic-gate		}
2207c478bdstevel@tonic-gate		return;
2217c478bdstevel@tonic-gate	}
2227c478bdstevel@tonic-gate
2237c478bdstevel@tonic-gate	/*
2247c478bdstevel@tonic-gate	 * Ok - they want to submit a block for immediate use, for
2257c478bdstevel@tonic-gate	 *	function call count consumption, and execution profile
2267c478bdstevel@tonic-gate	 *	histogram computation.
2277c478bdstevel@tonic-gate	 * If the block fails sanity tests, just bag it.
2287c478bdstevel@tonic-gate	 * Next thing - get name to use. If PROFDIR is NULL, let's
2297c478bdstevel@tonic-gate	 *	get out now - they want No Profiling done.
2307c478bdstevel@tonic-gate	 *
2317c478bdstevel@tonic-gate	 * Otherwise:
2327c478bdstevel@tonic-gate	 * Set the block hdr cells.
2337c478bdstevel@tonic-gate	 * Get an anchor for the block, and link the anchor+block onto
2347c478bdstevel@tonic-gate	 *	the end of the chain.
2357c478bdstevel@tonic-gate	 * Init the grabba-cell externs (countbase/limit) for this block.
2367c478bdstevel@tonic-gate	 * Finally, call profil and return.
2377c478bdstevel@tonic-gate	 */
2387c478bdstevel@tonic-gate
2397c478bdstevel@tonic-gate	ssiz = ((sizeof (struct hdr) + nfunc * sizeof (struct cnt)) /
2407c478bdstevel@tonic-gate	    sizeof (WORD));
2417c478bdstevel@tonic-gate	if (ssiz >= bufsize || lowpc >= highpc) {
2427c478bdstevel@tonic-gate		lmutex_unlock(&mon_lock);
2437c478bdstevel@tonic-gate		return;
2447c478bdstevel@tonic-gate	}
2457c478bdstevel@tonic-gate
2467c478bdstevel@tonic-gate	if ((s = getenv(PROFDIR)) == NULL) { /* PROFDIR not in environment */
2477c478bdstevel@tonic-gate		mon_out = MON_OUT; /* use default "mon.out" */
2487c478bdstevel@tonic-gate	} else if (*s == '\0') { /* value of PROFDIR is NULL */
2497c478bdstevel@tonic-gate		lmutex_unlock(&mon_lock);
2507c478bdstevel@tonic-gate		return; /* no profiling on this run */
2517c478bdstevel@tonic-gate	} else { /* construct "PROFDIR/pid.progname" */
2527c478bdstevel@tonic-gate		int n;
2537c478bdstevel@tonic-gate		pid_t pid;
2547c478bdstevel@tonic-gate		char *name;
2557c478bdstevel@tonic-gate		size_t len;
2567c478bdstevel@tonic-gate
2577c478bdstevel@tonic-gate		len = strlen(s);
2587c478bdstevel@tonic-gate		/* 15 is space for /pid.mon.out\0, if necessary */
2597c478bdstevel@tonic-gate		if ((mon_out = libc_malloc(len + strlen(___Argv[0]) + 15))
2607c478bdstevel@tonic-gate		    == NULL) {
2617c478bdstevel@tonic-gate			lmutex_unlock(&mon_lock);
2627c478bdstevel@tonic-gate			perror("");
2637c478bdstevel@tonic-gate			return;
2647c478bdstevel@tonic-gate		}
2657c478bdstevel@tonic-gate		(void) strcpy(mon_out, s);
2667c478bdstevel@tonic-gate		name = mon_out + len;
2677c478bdstevel@tonic-gate		*name++ = '/'; /* two slashes won't hurt */
2687c478bdstevel@tonic-gate
2697c478bdstevel@tonic-gate		if ((pid = getpid()) <= 0) /* extra test just in case */
2707c478bdstevel@tonic-gate			pid = 1; /* getpid returns something inappropriate */
2717c478bdstevel@tonic-gate
2727c478bdstevel@tonic-gate		/* suppress leading zeros */
2737c478bdstevel@tonic-gate		for (n = 10000; n > pid; n /= 10)
2747c478bdstevel@tonic-gate			;
2757c478bdstevel@tonic-gate		for (; ; n /= 10) {
2767c478bdstevel@tonic-gate			*name++ = pid/n + '0';
2777c478bdstevel@tonic-gate			if (n == 1)
2787257d1braf				break;
2797c478bdstevel@tonic-gate			pid %= n;
2807c478bdstevel@tonic-gate		}
2817c478bdstevel@tonic-gate		*name++ = '.';
2827c478bdstevel@tonic-gate
2837c478bdstevel@tonic-gate		if (___Argv != NULL) {	/* mcrt0.s executed */
2847c478bdstevel@tonic-gate			if ((s = strrchr(___Argv[0], '/')) != NULL)
2857c478bdstevel@tonic-gate				(void) strcpy(name, s + 1);
2867c478bdstevel@tonic-gate			else
2877c478bdstevel@tonic-gate				(void) strcpy(name, ___Argv[0]);
2887c478bdstevel@tonic-gate		} else {
2897c478bdstevel@tonic-gate			(void) strcpy(name, MON_OUT);
2907c478bdstevel@tonic-gate		}
2917c478bdstevel@tonic-gate	}
2927c478bdstevel@tonic-gate
2937c478bdstevel@tonic-gate
2947c478bdstevel@tonic-gate	hdrp = (struct hdr *)(uintptr_t)buffer;	/* initialize 1st region */
2957c478bdstevel@tonic-gate	hdrp->lpc = lowpc;
2967c478bdstevel@tonic-gate	hdrp->hpc = highpc;
2977c478bdstevel@tonic-gate	hdrp->nfns = nfunc;
2987c478bdstevel@tonic-gate
2997c478bdstevel@tonic-gate	/* get an anchor for the block */
3007c478bdstevel@tonic-gate	newanchp = (curAnchor == NULL) ? &firstAnchor :
3017c478bdstevel@tonic-gate	    (ANCHOR *)libc_malloc(sizeof (ANCHOR));
3027c478bdstevel@tonic-gate
3037c478bdstevel@tonic-gate	if (newanchp == NULL) {
3047c478bdstevel@tonic-gate		lmutex_unlock(&mon_lock);
3057c478bdstevel@tonic-gate		perror("monitor");
3067c478bdstevel@tonic-gate		return;
3077c478bdstevel@tonic-gate	}
3087c478bdstevel@tonic-gate
3097c478bdstevel@tonic-gate	/* link anchor+block into chain */
3107c478bdstevel@tonic-gate	newanchp->monBuffer = hdrp;		/* new, down. */
3117c478bdstevel@tonic-gate	newanchp->next  = NULL;			/* new, forward to NULL. */
3127c478bdstevel@tonic-gate	newanchp->prior = curAnchor;		/* new, backward. */
3137c478bdstevel@tonic-gate	if (curAnchor != NULL)
3147c478bdstevel@tonic-gate		curAnchor->next = newanchp;	/* old, forward to new. */
3157c478bdstevel@tonic-gate	newanchp->flags = HAS_HISTOGRAM;	/* note it has a histgm area */
3167c478bdstevel@tonic-gate
3177c478bdstevel@tonic-gate	/* got it - enable use by mcount() */
3187c478bdstevel@tonic-gate	countbase  = (char *)buffer + sizeof (struct hdr);
3197257d1braf	countlimit = countbase + (nfunc * sizeof (struct cnt));
3207c478bdstevel@tonic-gate
3217c478bdstevel@tonic-gate	/* (set size of region 3) */
3227c478bdstevel@tonic-gate	newanchp->histSize = (int)
3237257d1braf	    (bufsize * sizeof (WORD) - (countlimit - (char *)buffer));
3247c478bdstevel@tonic-gate
3257c478bdstevel@tonic-gate
3267c478bdstevel@tonic-gate	/* done w/regions 1 + 2: setup 3  to activate profil processing. */
3277c478bdstevel@tonic-gate	buffer += ssiz;			/* move ptr past 2'nd region */
3287c478bdstevel@tonic-gate	bufsize -= ssiz;		/* no. WORDs in third region */
3297c478bdstevel@tonic-gate					/* no. WORDs of text */
3307c478bdstevel@tonic-gate	text = (highpc - lowpc + sizeof (WORD) - 1) / sizeof (WORD);
3317c478bdstevel@tonic-gate
3327c478bdstevel@tonic-gate	/*
3337c478bdstevel@tonic-gate	 * scale is a 16 bit fixed point fraction with the decimal
3347c478bdstevel@tonic-gate	 * point at the left
3357c478bdstevel@tonic-gate	 */
3367c478bdstevel@tonic-gate	if (bufsize < text) {
3377c478bdstevel@tonic-gate		/* make sure cast is done first! */
3387c478bdstevel@tonic-gate		double temp = (double)bufsize;
3397c478bdstevel@tonic-gate		scale = (uint_t)((temp * (long)0200000L) / text);
3407c478bdstevel@tonic-gate	} else {
3417c478bdstevel@tonic-gate		/* scale must be less than 1 */
3427c478bdstevel@tonic-gate		scale = 0xffff;
3437c478bdstevel@tonic-gate	}
3447c478bdstevel@tonic-gate	bufsize *= sizeof (WORD);	/* bufsize into # bytes */
3457c478bdstevel@tonic-gate	profil(buffer, bufsize, (ulong_t)lowpc, scale);
3467c478bdstevel@tonic-gate
3477c478bdstevel@tonic-gate
3487c478bdstevel@tonic-gate	curAnchor = newanchp;	/* make latest addition, the cur anchor */
3497c478bdstevel@tonic-gate	lmutex_unlock(&mon_lock);
3507c478bdstevel@tonic-gate}
3517c478bdstevel@tonic-gate
3527c478bdstevel@tonic-gate/*
3537c478bdstevel@tonic-gate * writeBlocks() - write accumulated profiling info, std fmt.
3547c478bdstevel@tonic-gate *
3557c478bdstevel@tonic-gate * This routine collects the function call counts, and the
3567c478bdstevel@tonic-gate * last specified profil buffer, and writes out one combined
3577c478bdstevel@tonic-gate * 'pseudo-block', as expected by current and former versions
3587c478bdstevel@tonic-gate * of prof.
3597c478bdstevel@tonic-gate */
3607c478bdstevel@tonic-gatestatic int
3617c478bdstevel@tonic-gatewriteBlocks(void)
3627c478bdstevel@tonic-gate{
3637c478bdstevel@tonic-gate	int fd;
3647c478bdstevel@tonic-gate	int ok;
3657c478bdstevel@tonic-gate	ANCHOR *ap;		/* temp anchor ptr */
3667c478bdstevel@tonic-gate	struct hdr sum;		/* summary header (for 'pseudo' block) */
3677c478bdstevel@tonic-gate	ANCHOR *histp;		/* anchor with histogram to use */
3687c478bdstevel@tonic-gate
3697c478bdstevel@tonic-gate	if ((fd = creat(mon_out, 0666)) < 0)
3707c478bdstevel@tonic-gate		return (0);
3717c478bdstevel@tonic-gate
3727c478bdstevel@tonic-gate	/*
3737c478bdstevel@tonic-gate	 * this loop (1) computes # funct cts total
3747c478bdstevel@tonic-gate	 *  (2) finds anchor of last block w / hist(histp)
3757c478bdstevel@tonic-gate	 */
3767c478bdstevel@tonic-gate	histp = NULL;
3777c478bdstevel@tonic-gate	for (sum.nfns = 0, ap = &firstAnchor; ap != NULL; ap = ap->next) {
3787c478bdstevel@tonic-gate		sum.nfns += ap->monBuffer->nfns; /* accum num of cells */
3797c478bdstevel@tonic-gate		if (ap->flags & HAS_HISTOGRAM)
3807c478bdstevel@tonic-gate			histp = ap;	 /* remember lastone with a histgm */
3817c478bdstevel@tonic-gate	}
3827c478bdstevel@tonic-gate
3837c478bdstevel@tonic-gate
3847c478bdstevel@tonic-gate	/* copy pc range from effective histgm */
3857c478bdstevel@tonic-gate	sum.lpc = histp->monBuffer->lpc;
3867c478bdstevel@tonic-gate	sum.hpc = histp->monBuffer->hpc;
3877c478bdstevel@tonic-gate
3887c478bdstevel@tonic-gate	ok = (write(fd, (char *)&sum, sizeof (sum)) == sizeof (sum));
3897c478bdstevel@tonic-gate
3907c478bdstevel@tonic-gate	if (ok) {		/* if the hdr went out ok.. */
3917c478bdstevel@tonic-gate		size_t amt;
3927c478bdstevel@tonic-gate		char *p;
3937c478bdstevel@tonic-gate
3947c478bdstevel@tonic-gate		/* write out the count arrays (region 2's) */
3957c478bdstevel@tonic-gate		for (ap = &firstAnchor; ok && ap != NULL; ap = ap->next) {
3967c478bdstevel@tonic-gate			amt = ap->monBuffer->nfns * sizeof (struct cnt);
3977c478bdstevel@tonic-gate			p = (char *)ap->monBuffer + sizeof (struct hdr);
3987c478bdstevel@tonic-gate
3997c478bdstevel@tonic-gate			ok = (write(fd, p, amt) == amt);
4007c478bdstevel@tonic-gate		}
4017c478bdstevel@tonic-gate
4027c478bdstevel@tonic-gate		/* count arrays out; write out histgm area */
4037c478bdstevel@tonic-gate		if (ok) {
4047c478bdstevel@tonic-gate			p = (char *)histp->monBuffer + sizeof (struct hdr) +
4057c478bdstevel@tonic-gate			    (histp->monBuffer->nfns * sizeof (struct cnt));
4067c478bdstevel@tonic-gate			amt = histp->histSize;
4077c478bdstevel@tonic-gate
4087c478bdstevel@tonic-gate			ok = (write(fd, p, amt) == amt);
4097c478bdstevel@tonic-gate
4107c478bdstevel@tonic-gate		}
4117c478bdstevel@tonic-gate	}
4127c478bdstevel@tonic-gate
4137c478bdstevel@tonic-gate	(void) close(fd);
4147c478bdstevel@tonic-gate
4157c478bdstevel@tonic-gate	return (ok);	/* indicate success */
4167c478bdstevel@tonic-gate}
4177c478bdstevel@tonic-gate
4187c478bdstevel@tonic-gate
4197c478bdstevel@tonic-gate/*
4207c478bdstevel@tonic-gate * mnewblock()-allocate and link in a new region1&2 block.
4217c478bdstevel@tonic-gate *
4227c478bdstevel@tonic-gate * This routine, called by mcount_newent(), allocates a new block
4237c478bdstevel@tonic-gate * containing only regions 1 & 2 (hdr and fcn call count array),
4247c478bdstevel@tonic-gate * and an associated anchor (see header comments), inits the
4257c478bdstevel@tonic-gate * header (region 1) of the block, links the anchor into the
4267c478bdstevel@tonic-gate * list, and resets the countbase/limit pointers.
4277c478bdstevel@tonic-gate *
4287c478bdstevel@tonic-gate * This routine cannot be called recursively, since (each) mcount
4297c478bdstevel@tonic-gate * has a local lock which prevents recursive calls to mcount_newent.
4307c478bdstevel@tonic-gate * See mcount_newent for more details.
4317c478bdstevel@tonic-gate *
4327c478bdstevel@tonic-gate */
4337c478bdstevel@tonic-gate
4347c478bdstevel@tonic-gate#define	THISMANYFCNS	(MPROGS0*2)
4357c478bdstevel@tonic-gate
4367c478bdstevel@tonic-gate/*
4377c478bdstevel@tonic-gate * call libc_malloc() to get an anchor & a regn1&2 block, together
4387c478bdstevel@tonic-gate */
4397c478bdstevel@tonic-gate#define	GETTHISMUCH	(sizeof (ANCHOR) + 	/* get an ANCHOR */  \
4407c478bdstevel@tonic-gate			(sizeof (struct hdr) +	/* get Region 1 */   \
4417c478bdstevel@tonic-gate			THISMANYFCNS * sizeof (struct cnt))) /* Region 2 */  \
4427c478bdstevel@tonic-gate						/* but No region 3 */
4437c478bdstevel@tonic-gate
4447c478bdstevel@tonic-gate
4457c478bdstevel@tonic-gatestatic void
4467c478bdstevel@tonic-gate_mnewblock(void)
4477c478bdstevel@tonic-gate{
4487c478bdstevel@tonic-gate	struct hdr *hdrp;
4497c478bdstevel@tonic-gate	ANCHOR	*newanchp;
4507c478bdstevel@tonic-gate	ANCHOR	*p;
4517c478bdstevel@tonic-gate
4527c478bdstevel@tonic-gate					/* get anchor And block, together */
4537c478bdstevel@tonic-gate	p = libc_malloc(GETTHISMUCH);
4547c478bdstevel@tonic-gate	if (p == NULL) {
4557c478bdstevel@tonic-gate		perror("mcount(mnewblock)");
4567c478bdstevel@tonic-gate		return;
4577c478bdstevel@tonic-gate	}
4587c478bdstevel@tonic-gate
4597c478bdstevel@tonic-gate	newanchp = p;
4607c478bdstevel@tonic-gate	hdrp = (struct hdr *)(p + 1);
4617c478bdstevel@tonic-gate
4627c478bdstevel@tonic-gate					/* initialize 1st region to dflts */
4637c478bdstevel@tonic-gate	hdrp->lpc = 0;
4647c478bdstevel@tonic-gate	hdrp->hpc = 0;
4657c478bdstevel@tonic-gate	hdrp->nfns = THISMANYFCNS;
4667c478bdstevel@tonic-gate
4677c478bdstevel@tonic-gate					/* link anchor+block into chain */
4687c478bdstevel@tonic-gate	newanchp->monBuffer = hdrp;		/* new, down. */
4697c478bdstevel@tonic-gate	newanchp->next  = NULL;			/* new, forward to NULL. */
4707c478bdstevel@tonic-gate	newanchp->prior = curAnchor;		/* new, backward. */
4717c478bdstevel@tonic-gate	if (curAnchor != NULL)
4727c478bdstevel@tonic-gate		curAnchor->next = newanchp;	/* old, forward to new. */
4737c478bdstevel@tonic-gate	newanchp->flags = 0;		/* note that it has NO histgm area */
4747c478bdstevel@tonic-gate
4757c478bdstevel@tonic-gate					/* got it - enable use by mcount() */
4767c478bdstevel@tonic-gate	countbase  = (char *)hdrp + sizeof (struct hdr);
4777257d1braf	countlimit = countbase + (THISMANYFCNS * sizeof (struct cnt));
4787c478bdstevel@tonic-gate
4797c478bdstevel@tonic-gate	newanchp->histSize = 0;	/* (set size of region 3.. to 0) */
4807c478bdstevel@tonic-gate
4817c478bdstevel@tonic-gate
4827c478bdstevel@tonic-gate	curAnchor = newanchp;		/* make latest addition, cur anchor */
4837c478bdstevel@tonic-gate}
4847c478bdstevel@tonic-gate
4857c478bdstevel@tonic-gate/*
4867c478bdstevel@tonic-gate * mcount_newent() -- call to get a new mcount call count entry.
4877c478bdstevel@tonic-gate *
4887c478bdstevel@tonic-gate * this function is called by _mcount to get a new call count entry
4897257d1braf * (struct cnt, in the region allocated by monitor()), or to return
4907c478bdstevel@tonic-gate * zero if profiling is off.
4917c478bdstevel@tonic-gate *
4927c478bdstevel@tonic-gate * This function acts as a funnel, an access function to make sure
4937c478bdstevel@tonic-gate * that all instances of mcount (the one in the a.out, and any in
4947c478bdstevel@tonic-gate * any shared objects) all get entries from the same array, and
4957c478bdstevel@tonic-gate * all know when profiling is off.
4967c478bdstevel@tonic-gate *
4977c478bdstevel@tonic-gate * NOTE: when mcount calls this function, it sets a private flag
4987c478bdstevel@tonic-gate * so that it does not call again until this function returns,
4997c478bdstevel@tonic-gate * thus preventing recursion.
5007c478bdstevel@tonic-gate *
5017c478bdstevel@tonic-gate * At Worst, the mcount in either a shared object or the a.out
5027c478bdstevel@tonic-gate * could call once, and then the mcount living in the shared object
5037c478bdstevel@tonic-gate * with monitor could call a second time (i.e. libc.so.1, although
5047c478bdstevel@tonic-gate * presently it does not have mcount in it).  This worst case
5057c478bdstevel@tonic-gate * would involve Two active calls to mcount_newent, which it can
5067c478bdstevel@tonic-gate * handle, since the second one would find a already-set value
5077c478bdstevel@tonic-gate * in countbase.
5087c478bdstevel@tonic-gate *
5097c478bdstevel@tonic-gate * The only unfortunate result is that No new call counts
5107c478bdstevel@tonic-gate * will be handed out until this function returns.
5117c478bdstevel@tonic-gate * Thus if libc_malloc or other routines called inductively by
5127c478bdstevel@tonic-gate * this routine have not yet been provided with a call count entry,
5137c478bdstevel@tonic-gate * they will not get one until this function call is completed.
5147c478bdstevel@tonic-gate * Thus a few calls to library routines during the course of
5157c478bdstevel@tonic-gate * profiling setup, may not be counted.
5167c478bdstevel@tonic-gate *
5177c478bdstevel@tonic-gate * NOTE: countbase points at the next available entry, and
5187c478bdstevel@tonic-gate * countlimit points past the last valid entry, in the current
5197c478bdstevel@tonic-gate * function call counts array.
5207c478bdstevel@tonic-gate *
5217c478bdstevel@tonic-gate *
5227c478bdstevel@tonic-gate * if profiling is off		// countbase==0
5237c478bdstevel@tonic-gate *   just return 0
5247c478bdstevel@tonic-gate *
5257c478bdstevel@tonic-gate * else
5267c478bdstevel@tonic-gate *   if need more entries	// because countbase points last valid entry
5277c478bdstevel@tonic-gate *     link in a new block, resetting countbase and countlimit
5287c478bdstevel@tonic-gate *   endif
5297c478bdstevel@tonic-gate *   if Got more entries
5307c478bdstevel@tonic-gate *     return pointer to the next available entry, and
5317c478bdstevel@tonic-gate *     update pointer-to-next-slot before you return.
5327c478bdstevel@tonic-gate *
5337c478bdstevel@tonic-gate *   else			// failed to get more entries
5347c478bdstevel@tonic-gate *     just return 0
5357c478bdstevel@tonic-gate *
5367c478bdstevel@tonic-gate *   endif
5377c478bdstevel@tonic-gate * endif
5387c478bdstevel@tonic-gate */
5397c478bdstevel@tonic-gate
5407c478bdstevel@tonic-gatestruct cnt *
5417c478bdstevel@tonic-gate_mcount_newent(void)
5427c478bdstevel@tonic-gate{
5437c478bdstevel@tonic-gate	if (countbase == 0)
5447c478bdstevel@tonic-gate		return (NULL);
5457c478bdstevel@tonic-gate
5467257d1braf	if (countbase >= countlimit)
5477c478bdstevel@tonic-gate		_mnewblock();		/* get a new block; set countbase */
5487c478bdstevel@tonic-gate
5497c478bdstevel@tonic-gate	if (countbase != 0) {
5507c478bdstevel@tonic-gate		struct cnt *cur_countbase = (struct cnt *)(uintptr_t)countbase;
5517c478bdstevel@tonic-gate
5527c478bdstevel@tonic-gate		countbase += sizeof (struct cnt);
5537c478bdstevel@tonic-gate		return (cur_countbase);
5547c478bdstevel@tonic-gate	}
5557c478bdstevel@tonic-gate	return (NULL);
5567c478bdstevel@tonic-gate}
557