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