0
|
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, Version 1.0 only
|
|
6 |
* (the "License"). You may not use this file except in compliance
|
|
7 |
* with the License.
|
|
8 |
*
|
|
9 |
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
10 |
* or http://www.opensolaris.org/os/licensing.
|
|
11 |
* See the License for the specific language governing permissions
|
|
12 |
* and limitations under the License.
|
|
13 |
*
|
|
14 |
* When distributing Covered Code, include this CDDL HEADER in each
|
|
15 |
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
16 |
* If applicable, add the following below this CDDL HEADER, with the
|
|
17 |
* fields enclosed by brackets "[]" replaced with your own identifying
|
|
18 |
* information: Portions Copyright [yyyy] [name of copyright owner]
|
|
19 |
*
|
|
20 |
* CDDL HEADER END
|
|
21 |
*/
|
|
22 |
/*
|
|
23 |
* Copyright 2004 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 "synonyms.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 |
|
|
144 |
static mutex_t mon_lock = DEFAULTMUTEX;
|
|
145 |
|
|
146 |
char **___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 |
*/
|
|
160 |
static char *countbase; /* addr of next pc,count cell to use in block */
|
|
161 |
static char *_countlimit; /* addr lim for cells (addr after last cell) */
|
|
162 |
|
|
163 |
typedef struct anchor ANCHOR;
|
|
164 |
|
|
165 |
struct 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 |
|
|
175 |
static ANCHOR *curAnchor = NULL; /* addr of anchor for current block */
|
|
176 |
static ANCHOR firstAnchor; /* the first anchor to use */
|
|
177 |
/* - hopefully the Only one needed */
|
|
178 |
/* a speedup for most cases. */
|
|
179 |
static char *mon_out;
|
|
180 |
|
|
181 |
static int writeBlocks(void);
|
|
182 |
static void _mnewblock(void);
|
|
183 |
struct 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 |
*/
|
|
192 |
void
|
|
193 |
monitor(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 |
*/
|
|
360 |
static int
|
|
361 |
writeBlocks(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 |
|
|
445 |
static 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 |
|
|
540 |
struct 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 |
}
|