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 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 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 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 * 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