1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
|
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* or http://www.opensolaris.org/os/licensing.
* 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 2004 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!!!!
*
*/
#pragma weak monitor = _monitor
#include "synonyms.h"
#include "mtlib.h"
#include "libc.h"
#include <sys/types.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>
#define PROFDIR "PROFDIR"
static mutex_t mon_lock = DEFAULTMUTEX;
char **___Argv = NULL; /* initialized to argv array by mcrt0 (if loaded) */
/*
* 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!
*/
static char *countbase; /* addr of next pc,count cell to use in block */
static char *_countlimit; /* addr lim for cells (addr after last cell) */
typedef struct anchor ANCHOR;
struct anchor {
ANCHOR *next, *prior; /* forward, backward ptrs for list */
struct hdr *monBuffer; /* 'down' ptr, to block */
short flags; /* indicators - has histogram designation */
int histSize; /* if has region3, this is size. */
};
#define HAS_HISTOGRAM 0x0001 /* this buffer has a histogram */
static ANCHOR *curAnchor = NULL; /* addr of anchor for current block */
static ANCHOR firstAnchor; /* the first anchor to use */
/* - 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
monitor(int (*alowpc)(void), int (*ahighpc)(void), WORD *buffer,
size_t bufsize, size_t nfunc)
{
uint_t scale;
long text;
char *s;
struct hdr *hdrp;
ANCHOR *newanchp;
size_t ssiz;
int error;
char *lowpc = (char *)alowpc;
char *highpc = (char *)ahighpc;
lmutex_lock(&mon_lock);
if (lowpc == NULL) { /* true only at the end */
error = 0;
if (curAnchor != NULL) { /* if anything was collected!.. */
profil(NULL, 0, 0, 0);
if (writeBlocks() == 0)
error = errno;
}
lmutex_unlock(&mon_lock);
if (error) {
errno = error;
perror(mon_out);
}
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.
* Init the grabba-cell externs (countbase/limit) for this block.
* Finally, call profil and return.
*/
ssiz = ((sizeof (struct hdr) + nfunc * sizeof (struct cnt)) /
sizeof (WORD));
if (ssiz >= bufsize || lowpc >= highpc) {
lmutex_unlock(&mon_lock);
return;
}
if ((s = getenv(PROFDIR)) == NULL) { /* PROFDIR not in environment */
mon_out = MON_OUT; /* use default "mon.out" */
} else if (*s == '\0') { /* value of PROFDIR is NULL */
lmutex_unlock(&mon_lock);
return; /* no profiling on this run */
} else { /* construct "PROFDIR/pid.progname" */
int n;
pid_t pid;
char *name;
size_t len;
len = strlen(s);
/* 15 is space for /pid.mon.out\0, if necessary */
if ((mon_out = libc_malloc(len + strlen(___Argv[0]) + 15))
== NULL) {
lmutex_unlock(&mon_lock);
perror("");
return;
}
(void) strcpy(mon_out, s);
name = mon_out + len;
*name++ = '/'; /* two slashes won't hurt */
if ((pid = getpid()) <= 0) /* extra test just in case */
pid = 1; /* getpid returns something inappropriate */
/* suppress leading zeros */
for (n = 10000; n > pid; n /= 10)
;
for (; ; n /= 10) {
*name++ = pid/n + '0';
if (n == 1)
break;
pid %= n;
}
*name++ = '.';
if (___Argv != NULL) { /* mcrt0.s executed */
if ((s = strrchr(___Argv[0], '/')) != NULL)
(void) strcpy(name, s + 1);
else
(void) strcpy(name, ___Argv[0]);
} else {
(void) strcpy(name, MON_OUT);
}
}
hdrp = (struct hdr *)(uintptr_t)buffer; /* initialize 1st region */
hdrp->lpc = lowpc;
hdrp->hpc = highpc;
hdrp->nfns = nfunc;
/* get an anchor for the block */
newanchp = (curAnchor == NULL) ? &firstAnchor :
(ANCHOR *)libc_malloc(sizeof (ANCHOR));
if (newanchp == NULL) {
lmutex_unlock(&mon_lock);
perror("monitor");
return;
}
/* link anchor+block into chain */
newanchp->monBuffer = hdrp; /* new, down. */
newanchp->next = NULL; /* new, forward to NULL. */
newanchp->prior = curAnchor; /* new, backward. */
if (curAnchor != NULL)
curAnchor->next = newanchp; /* old, forward to new. */
newanchp->flags = HAS_HISTOGRAM; /* note it has a histgm area */
/* got it - enable use by mcount() */
countbase = (char *)buffer + sizeof (struct hdr);
_countlimit = countbase + (nfunc * sizeof (struct cnt));
/* (set size of region 3) */
newanchp->histSize = (int)
(bufsize * sizeof (WORD) - (_countlimit - (char *)buffer));
/* done w/regions 1 + 2: setup 3 to activate profil processing. */
buffer += ssiz; /* move ptr past 2'nd region */
bufsize -= ssiz; /* no. WORDs in third region */
/* no. WORDs of text */
text = (highpc - lowpc + sizeof (WORD) - 1) / sizeof (WORD);
/*
* scale is a 16 bit fixed point fraction with the decimal
* point at the left
*/
if (bufsize < text) {
/* make sure cast is done first! */
double temp = (double)bufsize;
scale = (uint_t)((temp * (long)0200000L) / text);
} else {
/* scale must be less than 1 */
scale = 0xffff;
}
bufsize *= sizeof (WORD); /* bufsize into # bytes */
profil(buffer, bufsize, (ulong_t)lowpc, scale);
curAnchor = newanchp; /* make latest addition, the cur anchor */
lmutex_unlock(&mon_lock);
}
/*
* 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;
ANCHOR *ap; /* temp anchor ptr */
struct hdr sum; /* summary header (for 'pseudo' block) */
ANCHOR *histp; /* anchor with histogram to use */
if ((fd = creat(mon_out, 0666)) < 0)
return (0);
/*
* this loop (1) computes # funct cts total
* (2) finds anchor of last block w / hist(histp)
*/
histp = NULL;
for (sum.nfns = 0, ap = &firstAnchor; ap != NULL; ap = ap->next) {
sum.nfns += ap->monBuffer->nfns; /* accum num of cells */
if (ap->flags & HAS_HISTOGRAM)
histp = ap; /* remember lastone with a histgm */
}
/* copy pc range from effective histgm */
sum.lpc = histp->monBuffer->lpc;
sum.hpc = histp->monBuffer->hpc;
ok = (write(fd, (char *)&sum, sizeof (sum)) == sizeof (sum));
if (ok) { /* if the hdr went out ok.. */
size_t amt;
char *p;
/* write out the count arrays (region 2's) */
for (ap = &firstAnchor; ok && ap != NULL; ap = ap->next) {
amt = ap->monBuffer->nfns * sizeof (struct cnt);
p = (char *)ap->monBuffer + sizeof (struct hdr);
ok = (write(fd, p, amt) == amt);
}
/* count arrays out; write out histgm area */
if (ok) {
p = (char *)histp->monBuffer + sizeof (struct hdr) +
(histp->monBuffer->nfns * sizeof (struct cnt));
amt = histp->histSize;
ok = (write(fd, p, amt) == amt);
}
}
(void) close(fd);
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
* list, and resets the countbase/limit pointers.
*
* 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.
*
*/
#define THISMANYFCNS (MPROGS0*2)
/*
* call libc_malloc() to get an anchor & a regn1&2 block, together
*/
#define GETTHISMUCH (sizeof (ANCHOR) + /* get an ANCHOR */ \
(sizeof (struct hdr) + /* get Region 1 */ \
THISMANYFCNS * sizeof (struct cnt))) /* Region 2 */ \
/* but No region 3 */
static void
_mnewblock(void)
{
struct hdr *hdrp;
ANCHOR *newanchp;
ANCHOR *p;
/* get anchor And block, together */
p = libc_malloc(GETTHISMUCH);
if (p == NULL) {
perror("mcount(mnewblock)");
return;
}
newanchp = p;
hdrp = (struct hdr *)(p + 1);
/* initialize 1st region to dflts */
hdrp->lpc = 0;
hdrp->hpc = 0;
hdrp->nfns = THISMANYFCNS;
/* link anchor+block into chain */
newanchp->monBuffer = hdrp; /* new, down. */
newanchp->next = NULL; /* new, forward to NULL. */
newanchp->prior = curAnchor; /* new, backward. */
if (curAnchor != NULL)
curAnchor->next = newanchp; /* old, forward to new. */
newanchp->flags = 0; /* note that it has NO histgm area */
/* got it - enable use by mcount() */
countbase = (char *)hdrp + sizeof (struct hdr);
_countlimit = countbase + (THISMANYFCNS * sizeof (struct cnt));
newanchp->histSize = 0; /* (set size of region 3.. to 0) */
curAnchor = newanchp; /* make latest addition, cur anchor */
}
/*
* 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) {
struct cnt *cur_countbase = (struct cnt *)(uintptr_t)countbase;
countbase += sizeof (struct cnt);
return (cur_countbase);
}
return (NULL);
}
|