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