/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Environment variable PROFDIR added such that:
* If PROFDIR doesn't exist, "mon.out" is produced as before.
* If PROFDIR = NULL, no profiling output is produced.
* If PROFDIR = string, "string/pid.progname" is produced,
* where name consists of argv[0] suitably massaged.
*
*
* Routines:
* (global) monitor init, cleanup for prof(1)iling
* (global) _mcount function call counter
* (global) _mcount_newent call count entry manager
* (static) _mnewblock call count block allocator
*
*
* Monitor(), coordinating with mcount(), mcount_newent() and mnewblock(),
* maintains a series of one or more blocks of prof-profiling
* information. These blocks are added in response to calls to
* monitor() (explicitly or via mcrt[01]'s _start) and, via mcount()'s
* calls to mcount_newent() thence to mnewblock().
* The blocks are tracked via a linked list of block anchors,
* which each point to a block.
*
*
* An anchor points forward, backward and 'down' (to a block).
* A block has the profiling information, and consists of
* three regions: a header, a function call count array region,
* and an optional execution histogram region, as illustrated below.
*
*
* "anchor"
* +========+
* prior<--| |-->next anchor
* anchor | |
* +========+
* |
* |
* V "block"
* +-----------+
* + header +
* +-----------+
* + +
* + fcn call + // data collected by mcount
* + counts +
* + array +
* + +
* +-----------+
* + +
* + execution + // data collected by system call,
* + profile + // profil(2) (assumed ALWAYS specified
* + histogram + // by monitor()-caller, even if small;
* + + // never specified by mnewblock()).
* +-----------+
*
* The first time monitor() is called, it sets up the chain
* by allocating an anchor and initializing countbase and countlimit
* to zero. Everyone assumes that they start out zeroed.
*
* When a user (or _start from mcrt[01]) calls monitor(), they
* register a buffer which contains the third region (either with
* a meaningful size, or so short that profil-ing is being shut off).
*
* For each fcn, the first time it calls mcount(), mcount calls
* mcount_newent(), which parcels out the fcn call count entries
* from the current block, until they are exausted; then it calls
* mnewblock().
*
* Mnewbloc() allocates a block Without a third region, and
* links in a new associated anchor, adding a new anchor&block pair
* to the linked list. Each new mnewblock() block or user block,
* is added to the list as it comes in, FIFO.
*
* When monitor() is called to close up shop, it writes out
* a summarizing header, ALL the fcn call counts from ALL
* the blocks, and the Last specified execution histogram
* (currently there is no neat way to accumulate that info).
* This preserves all call count information, even when
* new blocks are specified.
*
* NOTE - no block passed to monitor() may be freed, until
* it is called to clean up!!!!
*
*/
#include "lint.h"
#include "mtlib.h"
#include "libc.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <mon.h>
#include <fcntl.h>
#include <unistd.h>
#include <thread.h>
#include <synch.h>
/*
* countbase and countlimit are used to parcel out
* the pc,count cells from the current block one at
* a time to each profiled function, the first time
* that function is called.
* When countbase reaches countlimit, mcount() calls
* mnewblock() to link in a new block.
*
* Only monitor/mcount/mcount_newent/mnewblock() should change these!!
* Correct that: only these routines are ABLE to change these;
* countbase/countlimit are now STATIC!
*/
struct anchor {
};
/* - hopefully the Only one needed */
/* a speedup for most cases. */
static char *mon_out;
static int writeBlocks(void);
static void _mnewblock(void);
struct cnt *_mcount_newent(void);
/*
* int (*alowpc)(), (*ahighpc)(); boundaries of text to be monitored
* WORD *buffer; ptr to space for monitor data(WORDs)
* size_t bufsize; size of above space(in WORDs)
* size_t nfunc; max no. of functions whose calls are counted
* (default nfunc is 300 on PDP11, 600 on others)
*/
void
{
long text;
char *s;
int error;
error = 0;
if (writeBlocks() == 0)
}
if (error) {
}
return;
}
/*
* Ok - they want to submit a block for immediate use, for
* function call count consumption, and execution profile
* histogram computation.
* If the block fails sanity tests, just bag it.
* Next thing - get name to use. If PROFDIR is NULL, let's
* get out now - they want No Profiling done.
*
* Otherwise:
* Set the block hdr cells.
* Get an anchor for the block, and link the anchor+block onto
* the end of the chain.
* Finally, call profil and return.
*/
sizeof (WORD));
return;
}
} else if (*s == '\0') { /* value of PROFDIR is NULL */
return; /* no profiling on this run */
} else { /* construct "PROFDIR/pid.progname" */
int n;
char *name;
/* 15 is space for /pid.mon.out\0, if necessary */
== NULL) {
perror("");
return;
}
/* suppress leading zeros */
;
for (; ; n /= 10) {
if (n == 1)
break;
pid %= n;
}
*name++ = '.';
else
} else {
}
}
/* get an anchor for the block */
perror("monitor");
return;
}
/* link anchor+block into chain */
/* got it - enable use by mcount() */
/* (set size of region 3) */
/* no. WORDs of text */
/*
* scale is a 16 bit fixed point fraction with the decimal
* point at the left
*/
/* make sure cast is done first! */
} else {
/* scale must be less than 1 */
scale = 0xffff;
}
}
/*
* writeBlocks() - write accumulated profiling info, std fmt.
*
* This routine collects the function call counts, and the
* last specified profil buffer, and writes out one combined
* 'pseudo-block', as expected by current and former versions
* of prof.
*/
static int
writeBlocks(void)
{
int fd;
int ok;
return (0);
/*
* this loop (1) computes # funct cts total
* (2) finds anchor of last block w / hist(histp)
*/
}
/* copy pc range from effective histgm */
if (ok) { /* if the hdr went out ok.. */
char *p;
/* write out the count arrays (region 2's) */
}
/* count arrays out; write out histgm area */
if (ok) {
}
}
return (ok); /* indicate success */
}
/*
* mnewblock()-allocate and link in a new region1&2 block.
*
* This routine, called by mcount_newent(), allocates a new block
* containing only regions 1 & 2 (hdr and fcn call count array),
* and an associated anchor (see header comments), inits the
* header (region 1) of the block, links the anchor into the
*
* This routine cannot be called recursively, since (each) mcount
* has a local lock which prevents recursive calls to mcount_newent.
* See mcount_newent for more details.
*
*/
/*
* call libc_malloc() to get an anchor & a regn1&2 block, together
*/
(sizeof (struct hdr) + /* get Region 1 */ \
/* but No region 3 */
static void
_mnewblock(void)
{
ANCHOR *p;
/* get anchor And block, together */
p = libc_malloc(GETTHISMUCH);
if (p == NULL) {
perror("mcount(mnewblock)");
return;
}
newanchp = p;
/* initialize 1st region to dflts */
/* link anchor+block into chain */
/* got it - enable use by mcount() */
}
/*
* mcount_newent() -- call to get a new mcount call count entry.
*
* this function is called by _mcount to get a new call count entry
* (struct cnt, in the region allocated by monitor()), or to return
* zero if profiling is off.
*
* This function acts as a funnel, an access function to make sure
* that all instances of mcount (the one in the a.out, and any in
* any shared objects) all get entries from the same array, and
* all know when profiling is off.
*
* NOTE: when mcount calls this function, it sets a private flag
* so that it does not call again until this function returns,
* thus preventing recursion.
*
* At Worst, the mcount in either a shared object or the a.out
* could call once, and then the mcount living in the shared object
* with monitor could call a second time (i.e. libc.so.1, although
* presently it does not have mcount in it). This worst case
* would involve Two active calls to mcount_newent, which it can
* handle, since the second one would find a already-set value
* in countbase.
*
* The only unfortunate result is that No new call counts
* will be handed out until this function returns.
* Thus if libc_malloc or other routines called inductively by
* this routine have not yet been provided with a call count entry,
* they will not get one until this function call is completed.
* Thus a few calls to library routines during the course of
* profiling setup, may not be counted.
*
* NOTE: countbase points at the next available entry, and
* countlimit points past the last valid entry, in the current
* function call counts array.
*
*
* if profiling is off // countbase==0
* just return 0
*
* else
* if need more entries // because countbase points last valid entry
* link in a new block, resetting countbase and countlimit
* endif
* if Got more entries
* return pointer to the next available entry, and
* update pointer-to-next-slot before you return.
*
* else // failed to get more entries
* just return 0
*
* endif
* endif
*/
struct cnt *
_mcount_newent(void)
{
if (countbase == 0)
return (NULL);
if (countbase >= countlimit)
_mnewblock(); /* get a new block; set countbase */
if (countbase != 0) {
return (cur_countbase);
}
return (NULL);
}