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 #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 "lint.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 } 557