17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7257d1b4Sraf * Common Development and Distribution License (the "License"). 6*7257d1b4Sraf * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 21*7257d1b4Sraf 227c478bd9Sstevel@tonic-gate /* 23*7257d1b4Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 287c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate /* 317c478bd9Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 327c478bd9Sstevel@tonic-gate * The Regents of the University of California 337c478bd9Sstevel@tonic-gate * All Rights Reserved 347c478bd9Sstevel@tonic-gate * 357c478bd9Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 367c478bd9Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 377c478bd9Sstevel@tonic-gate * contributors. 387c478bd9Sstevel@tonic-gate */ 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate /* 437c478bd9Sstevel@tonic-gate * Environment variable PROFDIR added such that: 447c478bd9Sstevel@tonic-gate * If PROFDIR doesn't exist, "mon.out" is produced as before. 457c478bd9Sstevel@tonic-gate * If PROFDIR = NULL, no profiling output is produced. 467c478bd9Sstevel@tonic-gate * If PROFDIR = string, "string/pid.progname" is produced, 477c478bd9Sstevel@tonic-gate * where name consists of argv[0] suitably massaged. 487c478bd9Sstevel@tonic-gate * 497c478bd9Sstevel@tonic-gate * 507c478bd9Sstevel@tonic-gate * Routines: 51*7257d1b4Sraf * (global) monitor init, cleanup for prof(1)iling 527c478bd9Sstevel@tonic-gate * (global) _mcount function call counter 537c478bd9Sstevel@tonic-gate * (global) _mcount_newent call count entry manager 547c478bd9Sstevel@tonic-gate * (static) _mnewblock call count block allocator 557c478bd9Sstevel@tonic-gate * 567c478bd9Sstevel@tonic-gate * 577c478bd9Sstevel@tonic-gate * Monitor(), coordinating with mcount(), mcount_newent() and mnewblock(), 587c478bd9Sstevel@tonic-gate * maintains a series of one or more blocks of prof-profiling 597c478bd9Sstevel@tonic-gate * information. These blocks are added in response to calls to 607c478bd9Sstevel@tonic-gate * monitor() (explicitly or via mcrt[01]'s _start) and, via mcount()'s 617c478bd9Sstevel@tonic-gate * calls to mcount_newent() thence to mnewblock(). 627c478bd9Sstevel@tonic-gate * The blocks are tracked via a linked list of block anchors, 637c478bd9Sstevel@tonic-gate * which each point to a block. 647c478bd9Sstevel@tonic-gate * 657c478bd9Sstevel@tonic-gate * 667c478bd9Sstevel@tonic-gate * An anchor points forward, backward and 'down' (to a block). 677c478bd9Sstevel@tonic-gate * A block has the profiling information, and consists of 687c478bd9Sstevel@tonic-gate * three regions: a header, a function call count array region, 697c478bd9Sstevel@tonic-gate * and an optional execution histogram region, as illustrated below. 707c478bd9Sstevel@tonic-gate * 717c478bd9Sstevel@tonic-gate * 727c478bd9Sstevel@tonic-gate * "anchor" 737c478bd9Sstevel@tonic-gate * +========+ 747c478bd9Sstevel@tonic-gate * prior<--| |-->next anchor 757c478bd9Sstevel@tonic-gate * anchor | | 767c478bd9Sstevel@tonic-gate * +========+ 777c478bd9Sstevel@tonic-gate * | 787c478bd9Sstevel@tonic-gate * | 797c478bd9Sstevel@tonic-gate * V "block" 807c478bd9Sstevel@tonic-gate * +-----------+ 817c478bd9Sstevel@tonic-gate * + header + 827c478bd9Sstevel@tonic-gate * +-----------+ 837c478bd9Sstevel@tonic-gate * + + 847c478bd9Sstevel@tonic-gate * + fcn call + // data collected by mcount 857c478bd9Sstevel@tonic-gate * + counts + 867c478bd9Sstevel@tonic-gate * + array + 877c478bd9Sstevel@tonic-gate * + + 887c478bd9Sstevel@tonic-gate * +-----------+ 897c478bd9Sstevel@tonic-gate * + + 907c478bd9Sstevel@tonic-gate * + execution + // data collected by system call, 917c478bd9Sstevel@tonic-gate * + profile + // profil(2) (assumed ALWAYS specified 927c478bd9Sstevel@tonic-gate * + histogram + // by monitor()-caller, even if small; 937c478bd9Sstevel@tonic-gate * + + // never specified by mnewblock()). 947c478bd9Sstevel@tonic-gate * +-----------+ 957c478bd9Sstevel@tonic-gate * 967c478bd9Sstevel@tonic-gate * The first time monitor() is called, it sets up the chain 977c478bd9Sstevel@tonic-gate * by allocating an anchor and initializing countbase and countlimit 987c478bd9Sstevel@tonic-gate * to zero. Everyone assumes that they start out zeroed. 997c478bd9Sstevel@tonic-gate * 1007c478bd9Sstevel@tonic-gate * When a user (or _start from mcrt[01]) calls monitor(), they 1017c478bd9Sstevel@tonic-gate * register a buffer which contains the third region (either with 1027c478bd9Sstevel@tonic-gate * a meaningful size, or so short that profil-ing is being shut off). 1037c478bd9Sstevel@tonic-gate * 1047c478bd9Sstevel@tonic-gate * For each fcn, the first time it calls mcount(), mcount calls 1057c478bd9Sstevel@tonic-gate * mcount_newent(), which parcels out the fcn call count entries 1067c478bd9Sstevel@tonic-gate * from the current block, until they are exausted; then it calls 1077c478bd9Sstevel@tonic-gate * mnewblock(). 1087c478bd9Sstevel@tonic-gate * 1097c478bd9Sstevel@tonic-gate * Mnewbloc() allocates a block Without a third region, and 1107c478bd9Sstevel@tonic-gate * links in a new associated anchor, adding a new anchor&block pair 1117c478bd9Sstevel@tonic-gate * to the linked list. Each new mnewblock() block or user block, 1127c478bd9Sstevel@tonic-gate * is added to the list as it comes in, FIFO. 1137c478bd9Sstevel@tonic-gate * 1147c478bd9Sstevel@tonic-gate * When monitor() is called to close up shop, it writes out 1157c478bd9Sstevel@tonic-gate * a summarizing header, ALL the fcn call counts from ALL 1167c478bd9Sstevel@tonic-gate * the blocks, and the Last specified execution histogram 1177c478bd9Sstevel@tonic-gate * (currently there is no neat way to accumulate that info). 1187c478bd9Sstevel@tonic-gate * This preserves all call count information, even when 1197c478bd9Sstevel@tonic-gate * new blocks are specified. 1207c478bd9Sstevel@tonic-gate * 1217c478bd9Sstevel@tonic-gate * NOTE - no block passed to monitor() may be freed, until 1227c478bd9Sstevel@tonic-gate * it is called to clean up!!!! 1237c478bd9Sstevel@tonic-gate * 1247c478bd9Sstevel@tonic-gate */ 1257c478bd9Sstevel@tonic-gate 126*7257d1b4Sraf #pragma weak _monitor = monitor 1277c478bd9Sstevel@tonic-gate 128*7257d1b4Sraf #include "lint.h" 1297c478bd9Sstevel@tonic-gate #include "mtlib.h" 1307c478bd9Sstevel@tonic-gate #include "libc.h" 1317c478bd9Sstevel@tonic-gate #include <sys/types.h> 1327c478bd9Sstevel@tonic-gate #include <string.h> 1337c478bd9Sstevel@tonic-gate #include <stdlib.h> 1347c478bd9Sstevel@tonic-gate #include <stdio.h> 1357c478bd9Sstevel@tonic-gate #include <errno.h> 1367c478bd9Sstevel@tonic-gate #include <mon.h> 1377c478bd9Sstevel@tonic-gate #include <fcntl.h> 1387c478bd9Sstevel@tonic-gate #include <unistd.h> 1397c478bd9Sstevel@tonic-gate #include <thread.h> 1407c478bd9Sstevel@tonic-gate #include <synch.h> 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate #define PROFDIR "PROFDIR" 1437c478bd9Sstevel@tonic-gate 1447c478bd9Sstevel@tonic-gate static mutex_t mon_lock = DEFAULTMUTEX; 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate char **___Argv = NULL; /* initialized to argv array by mcrt0 (if loaded) */ 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate /* 1497c478bd9Sstevel@tonic-gate * countbase and countlimit are used to parcel out 1507c478bd9Sstevel@tonic-gate * the pc,count cells from the current block one at 1517c478bd9Sstevel@tonic-gate * a time to each profiled function, the first time 1527c478bd9Sstevel@tonic-gate * that function is called. 1537c478bd9Sstevel@tonic-gate * When countbase reaches countlimit, mcount() calls 1547c478bd9Sstevel@tonic-gate * mnewblock() to link in a new block. 1557c478bd9Sstevel@tonic-gate * 1567c478bd9Sstevel@tonic-gate * Only monitor/mcount/mcount_newent/mnewblock() should change these!! 1577c478bd9Sstevel@tonic-gate * Correct that: only these routines are ABLE to change these; 1587c478bd9Sstevel@tonic-gate * countbase/countlimit are now STATIC! 1597c478bd9Sstevel@tonic-gate */ 1607c478bd9Sstevel@tonic-gate static char *countbase; /* addr of next pc,count cell to use in block */ 161*7257d1b4Sraf static char *countlimit; /* addr lim for cells (addr after last cell) */ 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate typedef struct anchor ANCHOR; 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate struct anchor { 1667c478bd9Sstevel@tonic-gate ANCHOR *next, *prior; /* forward, backward ptrs for list */ 1677c478bd9Sstevel@tonic-gate struct hdr *monBuffer; /* 'down' ptr, to block */ 1687c478bd9Sstevel@tonic-gate short flags; /* indicators - has histogram designation */ 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate int histSize; /* if has region3, this is size. */ 1717c478bd9Sstevel@tonic-gate }; 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate #define HAS_HISTOGRAM 0x0001 /* this buffer has a histogram */ 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate static ANCHOR *curAnchor = NULL; /* addr of anchor for current block */ 1767c478bd9Sstevel@tonic-gate static ANCHOR firstAnchor; /* the first anchor to use */ 1777c478bd9Sstevel@tonic-gate /* - hopefully the Only one needed */ 1787c478bd9Sstevel@tonic-gate /* a speedup for most cases. */ 1797c478bd9Sstevel@tonic-gate static char *mon_out; 1807c478bd9Sstevel@tonic-gate 1817c478bd9Sstevel@tonic-gate static int writeBlocks(void); 1827c478bd9Sstevel@tonic-gate static void _mnewblock(void); 1837c478bd9Sstevel@tonic-gate struct cnt *_mcount_newent(void); 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate /* 1867c478bd9Sstevel@tonic-gate * int (*alowpc)(), (*ahighpc)(); boundaries of text to be monitored 1877c478bd9Sstevel@tonic-gate * WORD *buffer; ptr to space for monitor data(WORDs) 1887c478bd9Sstevel@tonic-gate * size_t bufsize; size of above space(in WORDs) 1897c478bd9Sstevel@tonic-gate * size_t nfunc; max no. of functions whose calls are counted 1907c478bd9Sstevel@tonic-gate * (default nfunc is 300 on PDP11, 600 on others) 1917c478bd9Sstevel@tonic-gate */ 1927c478bd9Sstevel@tonic-gate void 1937c478bd9Sstevel@tonic-gate monitor(int (*alowpc)(void), int (*ahighpc)(void), WORD *buffer, 1947c478bd9Sstevel@tonic-gate size_t bufsize, size_t nfunc) 1957c478bd9Sstevel@tonic-gate { 1967c478bd9Sstevel@tonic-gate uint_t scale; 1977c478bd9Sstevel@tonic-gate long text; 1987c478bd9Sstevel@tonic-gate char *s; 1997c478bd9Sstevel@tonic-gate struct hdr *hdrp; 2007c478bd9Sstevel@tonic-gate ANCHOR *newanchp; 2017c478bd9Sstevel@tonic-gate size_t ssiz; 2027c478bd9Sstevel@tonic-gate int error; 2037c478bd9Sstevel@tonic-gate char *lowpc = (char *)alowpc; 2047c478bd9Sstevel@tonic-gate char *highpc = (char *)ahighpc; 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate lmutex_lock(&mon_lock); 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate if (lowpc == NULL) { /* true only at the end */ 2097c478bd9Sstevel@tonic-gate error = 0; 2107c478bd9Sstevel@tonic-gate if (curAnchor != NULL) { /* if anything was collected!.. */ 2117c478bd9Sstevel@tonic-gate profil(NULL, 0, 0, 0); 2127c478bd9Sstevel@tonic-gate if (writeBlocks() == 0) 2137c478bd9Sstevel@tonic-gate error = errno; 2147c478bd9Sstevel@tonic-gate } 2157c478bd9Sstevel@tonic-gate lmutex_unlock(&mon_lock); 2167c478bd9Sstevel@tonic-gate if (error) { 2177c478bd9Sstevel@tonic-gate errno = error; 2187c478bd9Sstevel@tonic-gate perror(mon_out); 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate return; 2217c478bd9Sstevel@tonic-gate } 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate /* 2247c478bd9Sstevel@tonic-gate * Ok - they want to submit a block for immediate use, for 2257c478bd9Sstevel@tonic-gate * function call count consumption, and execution profile 2267c478bd9Sstevel@tonic-gate * histogram computation. 2277c478bd9Sstevel@tonic-gate * If the block fails sanity tests, just bag it. 2287c478bd9Sstevel@tonic-gate * Next thing - get name to use. If PROFDIR is NULL, let's 2297c478bd9Sstevel@tonic-gate * get out now - they want No Profiling done. 2307c478bd9Sstevel@tonic-gate * 2317c478bd9Sstevel@tonic-gate * Otherwise: 2327c478bd9Sstevel@tonic-gate * Set the block hdr cells. 2337c478bd9Sstevel@tonic-gate * Get an anchor for the block, and link the anchor+block onto 2347c478bd9Sstevel@tonic-gate * the end of the chain. 2357c478bd9Sstevel@tonic-gate * Init the grabba-cell externs (countbase/limit) for this block. 2367c478bd9Sstevel@tonic-gate * Finally, call profil and return. 2377c478bd9Sstevel@tonic-gate */ 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate ssiz = ((sizeof (struct hdr) + nfunc * sizeof (struct cnt)) / 2407c478bd9Sstevel@tonic-gate sizeof (WORD)); 2417c478bd9Sstevel@tonic-gate if (ssiz >= bufsize || lowpc >= highpc) { 2427c478bd9Sstevel@tonic-gate lmutex_unlock(&mon_lock); 2437c478bd9Sstevel@tonic-gate return; 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate if ((s = getenv(PROFDIR)) == NULL) { /* PROFDIR not in environment */ 2477c478bd9Sstevel@tonic-gate mon_out = MON_OUT; /* use default "mon.out" */ 2487c478bd9Sstevel@tonic-gate } else if (*s == '\0') { /* value of PROFDIR is NULL */ 2497c478bd9Sstevel@tonic-gate lmutex_unlock(&mon_lock); 2507c478bd9Sstevel@tonic-gate return; /* no profiling on this run */ 2517c478bd9Sstevel@tonic-gate } else { /* construct "PROFDIR/pid.progname" */ 2527c478bd9Sstevel@tonic-gate int n; 2537c478bd9Sstevel@tonic-gate pid_t pid; 2547c478bd9Sstevel@tonic-gate char *name; 2557c478bd9Sstevel@tonic-gate size_t len; 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate len = strlen(s); 2587c478bd9Sstevel@tonic-gate /* 15 is space for /pid.mon.out\0, if necessary */ 2597c478bd9Sstevel@tonic-gate if ((mon_out = libc_malloc(len + strlen(___Argv[0]) + 15)) 2607c478bd9Sstevel@tonic-gate == NULL) { 2617c478bd9Sstevel@tonic-gate lmutex_unlock(&mon_lock); 2627c478bd9Sstevel@tonic-gate perror(""); 2637c478bd9Sstevel@tonic-gate return; 2647c478bd9Sstevel@tonic-gate } 2657c478bd9Sstevel@tonic-gate (void) strcpy(mon_out, s); 2667c478bd9Sstevel@tonic-gate name = mon_out + len; 2677c478bd9Sstevel@tonic-gate *name++ = '/'; /* two slashes won't hurt */ 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate if ((pid = getpid()) <= 0) /* extra test just in case */ 2707c478bd9Sstevel@tonic-gate pid = 1; /* getpid returns something inappropriate */ 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate /* suppress leading zeros */ 2737c478bd9Sstevel@tonic-gate for (n = 10000; n > pid; n /= 10) 2747c478bd9Sstevel@tonic-gate ; 2757c478bd9Sstevel@tonic-gate for (; ; n /= 10) { 2767c478bd9Sstevel@tonic-gate *name++ = pid/n + '0'; 2777c478bd9Sstevel@tonic-gate if (n == 1) 2787c478bd9Sstevel@tonic-gate break; 2797c478bd9Sstevel@tonic-gate pid %= n; 2807c478bd9Sstevel@tonic-gate } 2817c478bd9Sstevel@tonic-gate *name++ = '.'; 2827c478bd9Sstevel@tonic-gate 2837c478bd9Sstevel@tonic-gate if (___Argv != NULL) { /* mcrt0.s executed */ 2847c478bd9Sstevel@tonic-gate if ((s = strrchr(___Argv[0], '/')) != NULL) 2857c478bd9Sstevel@tonic-gate (void) strcpy(name, s + 1); 2867c478bd9Sstevel@tonic-gate else 2877c478bd9Sstevel@tonic-gate (void) strcpy(name, ___Argv[0]); 2887c478bd9Sstevel@tonic-gate } else { 2897c478bd9Sstevel@tonic-gate (void) strcpy(name, MON_OUT); 2907c478bd9Sstevel@tonic-gate } 2917c478bd9Sstevel@tonic-gate } 2927c478bd9Sstevel@tonic-gate 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate hdrp = (struct hdr *)(uintptr_t)buffer; /* initialize 1st region */ 2957c478bd9Sstevel@tonic-gate hdrp->lpc = lowpc; 2967c478bd9Sstevel@tonic-gate hdrp->hpc = highpc; 2977c478bd9Sstevel@tonic-gate hdrp->nfns = nfunc; 2987c478bd9Sstevel@tonic-gate 2997c478bd9Sstevel@tonic-gate /* get an anchor for the block */ 3007c478bd9Sstevel@tonic-gate newanchp = (curAnchor == NULL) ? &firstAnchor : 3017c478bd9Sstevel@tonic-gate (ANCHOR *)libc_malloc(sizeof (ANCHOR)); 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate if (newanchp == NULL) { 3047c478bd9Sstevel@tonic-gate lmutex_unlock(&mon_lock); 3057c478bd9Sstevel@tonic-gate perror("monitor"); 3067c478bd9Sstevel@tonic-gate return; 3077c478bd9Sstevel@tonic-gate } 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate /* link anchor+block into chain */ 3107c478bd9Sstevel@tonic-gate newanchp->monBuffer = hdrp; /* new, down. */ 3117c478bd9Sstevel@tonic-gate newanchp->next = NULL; /* new, forward to NULL. */ 3127c478bd9Sstevel@tonic-gate newanchp->prior = curAnchor; /* new, backward. */ 3137c478bd9Sstevel@tonic-gate if (curAnchor != NULL) 3147c478bd9Sstevel@tonic-gate curAnchor->next = newanchp; /* old, forward to new. */ 3157c478bd9Sstevel@tonic-gate newanchp->flags = HAS_HISTOGRAM; /* note it has a histgm area */ 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate /* got it - enable use by mcount() */ 3187c478bd9Sstevel@tonic-gate countbase = (char *)buffer + sizeof (struct hdr); 319*7257d1b4Sraf countlimit = countbase + (nfunc * sizeof (struct cnt)); 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate /* (set size of region 3) */ 3227c478bd9Sstevel@tonic-gate newanchp->histSize = (int) 323*7257d1b4Sraf (bufsize * sizeof (WORD) - (countlimit - (char *)buffer)); 3247c478bd9Sstevel@tonic-gate 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate /* done w/regions 1 + 2: setup 3 to activate profil processing. */ 3277c478bd9Sstevel@tonic-gate buffer += ssiz; /* move ptr past 2'nd region */ 3287c478bd9Sstevel@tonic-gate bufsize -= ssiz; /* no. WORDs in third region */ 3297c478bd9Sstevel@tonic-gate /* no. WORDs of text */ 3307c478bd9Sstevel@tonic-gate text = (highpc - lowpc + sizeof (WORD) - 1) / sizeof (WORD); 3317c478bd9Sstevel@tonic-gate 3327c478bd9Sstevel@tonic-gate /* 3337c478bd9Sstevel@tonic-gate * scale is a 16 bit fixed point fraction with the decimal 3347c478bd9Sstevel@tonic-gate * point at the left 3357c478bd9Sstevel@tonic-gate */ 3367c478bd9Sstevel@tonic-gate if (bufsize < text) { 3377c478bd9Sstevel@tonic-gate /* make sure cast is done first! */ 3387c478bd9Sstevel@tonic-gate double temp = (double)bufsize; 3397c478bd9Sstevel@tonic-gate scale = (uint_t)((temp * (long)0200000L) / text); 3407c478bd9Sstevel@tonic-gate } else { 3417c478bd9Sstevel@tonic-gate /* scale must be less than 1 */ 3427c478bd9Sstevel@tonic-gate scale = 0xffff; 3437c478bd9Sstevel@tonic-gate } 3447c478bd9Sstevel@tonic-gate bufsize *= sizeof (WORD); /* bufsize into # bytes */ 3457c478bd9Sstevel@tonic-gate profil(buffer, bufsize, (ulong_t)lowpc, scale); 3467c478bd9Sstevel@tonic-gate 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate curAnchor = newanchp; /* make latest addition, the cur anchor */ 3497c478bd9Sstevel@tonic-gate lmutex_unlock(&mon_lock); 3507c478bd9Sstevel@tonic-gate } 3517c478bd9Sstevel@tonic-gate 3527c478bd9Sstevel@tonic-gate /* 3537c478bd9Sstevel@tonic-gate * writeBlocks() - write accumulated profiling info, std fmt. 3547c478bd9Sstevel@tonic-gate * 3557c478bd9Sstevel@tonic-gate * This routine collects the function call counts, and the 3567c478bd9Sstevel@tonic-gate * last specified profil buffer, and writes out one combined 3577c478bd9Sstevel@tonic-gate * 'pseudo-block', as expected by current and former versions 3587c478bd9Sstevel@tonic-gate * of prof. 3597c478bd9Sstevel@tonic-gate */ 3607c478bd9Sstevel@tonic-gate static int 3617c478bd9Sstevel@tonic-gate writeBlocks(void) 3627c478bd9Sstevel@tonic-gate { 3637c478bd9Sstevel@tonic-gate int fd; 3647c478bd9Sstevel@tonic-gate int ok; 3657c478bd9Sstevel@tonic-gate ANCHOR *ap; /* temp anchor ptr */ 3667c478bd9Sstevel@tonic-gate struct hdr sum; /* summary header (for 'pseudo' block) */ 3677c478bd9Sstevel@tonic-gate ANCHOR *histp; /* anchor with histogram to use */ 3687c478bd9Sstevel@tonic-gate 3697c478bd9Sstevel@tonic-gate if ((fd = creat(mon_out, 0666)) < 0) 3707c478bd9Sstevel@tonic-gate return (0); 3717c478bd9Sstevel@tonic-gate 3727c478bd9Sstevel@tonic-gate /* 3737c478bd9Sstevel@tonic-gate * this loop (1) computes # funct cts total 3747c478bd9Sstevel@tonic-gate * (2) finds anchor of last block w / hist(histp) 3757c478bd9Sstevel@tonic-gate */ 3767c478bd9Sstevel@tonic-gate histp = NULL; 3777c478bd9Sstevel@tonic-gate for (sum.nfns = 0, ap = &firstAnchor; ap != NULL; ap = ap->next) { 3787c478bd9Sstevel@tonic-gate sum.nfns += ap->monBuffer->nfns; /* accum num of cells */ 3797c478bd9Sstevel@tonic-gate if (ap->flags & HAS_HISTOGRAM) 3807c478bd9Sstevel@tonic-gate histp = ap; /* remember lastone with a histgm */ 3817c478bd9Sstevel@tonic-gate } 3827c478bd9Sstevel@tonic-gate 3837c478bd9Sstevel@tonic-gate 3847c478bd9Sstevel@tonic-gate /* copy pc range from effective histgm */ 3857c478bd9Sstevel@tonic-gate sum.lpc = histp->monBuffer->lpc; 3867c478bd9Sstevel@tonic-gate sum.hpc = histp->monBuffer->hpc; 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate ok = (write(fd, (char *)&sum, sizeof (sum)) == sizeof (sum)); 3897c478bd9Sstevel@tonic-gate 3907c478bd9Sstevel@tonic-gate if (ok) { /* if the hdr went out ok.. */ 3917c478bd9Sstevel@tonic-gate size_t amt; 3927c478bd9Sstevel@tonic-gate char *p; 3937c478bd9Sstevel@tonic-gate 3947c478bd9Sstevel@tonic-gate /* write out the count arrays (region 2's) */ 3957c478bd9Sstevel@tonic-gate for (ap = &firstAnchor; ok && ap != NULL; ap = ap->next) { 3967c478bd9Sstevel@tonic-gate amt = ap->monBuffer->nfns * sizeof (struct cnt); 3977c478bd9Sstevel@tonic-gate p = (char *)ap->monBuffer + sizeof (struct hdr); 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate ok = (write(fd, p, amt) == amt); 4007c478bd9Sstevel@tonic-gate } 4017c478bd9Sstevel@tonic-gate 4027c478bd9Sstevel@tonic-gate /* count arrays out; write out histgm area */ 4037c478bd9Sstevel@tonic-gate if (ok) { 4047c478bd9Sstevel@tonic-gate p = (char *)histp->monBuffer + sizeof (struct hdr) + 4057c478bd9Sstevel@tonic-gate (histp->monBuffer->nfns * sizeof (struct cnt)); 4067c478bd9Sstevel@tonic-gate amt = histp->histSize; 4077c478bd9Sstevel@tonic-gate 4087c478bd9Sstevel@tonic-gate ok = (write(fd, p, amt) == amt); 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate } 4117c478bd9Sstevel@tonic-gate } 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate (void) close(fd); 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate return (ok); /* indicate success */ 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate /* 4207c478bd9Sstevel@tonic-gate * mnewblock()-allocate and link in a new region1&2 block. 4217c478bd9Sstevel@tonic-gate * 4227c478bd9Sstevel@tonic-gate * This routine, called by mcount_newent(), allocates a new block 4237c478bd9Sstevel@tonic-gate * containing only regions 1 & 2 (hdr and fcn call count array), 4247c478bd9Sstevel@tonic-gate * and an associated anchor (see header comments), inits the 4257c478bd9Sstevel@tonic-gate * header (region 1) of the block, links the anchor into the 4267c478bd9Sstevel@tonic-gate * list, and resets the countbase/limit pointers. 4277c478bd9Sstevel@tonic-gate * 4287c478bd9Sstevel@tonic-gate * This routine cannot be called recursively, since (each) mcount 4297c478bd9Sstevel@tonic-gate * has a local lock which prevents recursive calls to mcount_newent. 4307c478bd9Sstevel@tonic-gate * See mcount_newent for more details. 4317c478bd9Sstevel@tonic-gate * 4327c478bd9Sstevel@tonic-gate */ 4337c478bd9Sstevel@tonic-gate 4347c478bd9Sstevel@tonic-gate #define THISMANYFCNS (MPROGS0*2) 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate /* 4377c478bd9Sstevel@tonic-gate * call libc_malloc() to get an anchor & a regn1&2 block, together 4387c478bd9Sstevel@tonic-gate */ 4397c478bd9Sstevel@tonic-gate #define GETTHISMUCH (sizeof (ANCHOR) + /* get an ANCHOR */ \ 4407c478bd9Sstevel@tonic-gate (sizeof (struct hdr) + /* get Region 1 */ \ 4417c478bd9Sstevel@tonic-gate THISMANYFCNS * sizeof (struct cnt))) /* Region 2 */ \ 4427c478bd9Sstevel@tonic-gate /* but No region 3 */ 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate 4457c478bd9Sstevel@tonic-gate static void 4467c478bd9Sstevel@tonic-gate _mnewblock(void) 4477c478bd9Sstevel@tonic-gate { 4487c478bd9Sstevel@tonic-gate struct hdr *hdrp; 4497c478bd9Sstevel@tonic-gate ANCHOR *newanchp; 4507c478bd9Sstevel@tonic-gate ANCHOR *p; 4517c478bd9Sstevel@tonic-gate 4527c478bd9Sstevel@tonic-gate /* get anchor And block, together */ 4537c478bd9Sstevel@tonic-gate p = libc_malloc(GETTHISMUCH); 4547c478bd9Sstevel@tonic-gate if (p == NULL) { 4557c478bd9Sstevel@tonic-gate perror("mcount(mnewblock)"); 4567c478bd9Sstevel@tonic-gate return; 4577c478bd9Sstevel@tonic-gate } 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate newanchp = p; 4607c478bd9Sstevel@tonic-gate hdrp = (struct hdr *)(p + 1); 4617c478bd9Sstevel@tonic-gate 4627c478bd9Sstevel@tonic-gate /* initialize 1st region to dflts */ 4637c478bd9Sstevel@tonic-gate hdrp->lpc = 0; 4647c478bd9Sstevel@tonic-gate hdrp->hpc = 0; 4657c478bd9Sstevel@tonic-gate hdrp->nfns = THISMANYFCNS; 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate /* link anchor+block into chain */ 4687c478bd9Sstevel@tonic-gate newanchp->monBuffer = hdrp; /* new, down. */ 4697c478bd9Sstevel@tonic-gate newanchp->next = NULL; /* new, forward to NULL. */ 4707c478bd9Sstevel@tonic-gate newanchp->prior = curAnchor; /* new, backward. */ 4717c478bd9Sstevel@tonic-gate if (curAnchor != NULL) 4727c478bd9Sstevel@tonic-gate curAnchor->next = newanchp; /* old, forward to new. */ 4737c478bd9Sstevel@tonic-gate newanchp->flags = 0; /* note that it has NO histgm area */ 4747c478bd9Sstevel@tonic-gate 4757c478bd9Sstevel@tonic-gate /* got it - enable use by mcount() */ 4767c478bd9Sstevel@tonic-gate countbase = (char *)hdrp + sizeof (struct hdr); 477*7257d1b4Sraf countlimit = countbase + (THISMANYFCNS * sizeof (struct cnt)); 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate newanchp->histSize = 0; /* (set size of region 3.. to 0) */ 4807c478bd9Sstevel@tonic-gate 4817c478bd9Sstevel@tonic-gate 4827c478bd9Sstevel@tonic-gate curAnchor = newanchp; /* make latest addition, cur anchor */ 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate /* 4867c478bd9Sstevel@tonic-gate * mcount_newent() -- call to get a new mcount call count entry. 4877c478bd9Sstevel@tonic-gate * 4887c478bd9Sstevel@tonic-gate * this function is called by _mcount to get a new call count entry 489*7257d1b4Sraf * (struct cnt, in the region allocated by monitor()), or to return 4907c478bd9Sstevel@tonic-gate * zero if profiling is off. 4917c478bd9Sstevel@tonic-gate * 4927c478bd9Sstevel@tonic-gate * This function acts as a funnel, an access function to make sure 4937c478bd9Sstevel@tonic-gate * that all instances of mcount (the one in the a.out, and any in 4947c478bd9Sstevel@tonic-gate * any shared objects) all get entries from the same array, and 4957c478bd9Sstevel@tonic-gate * all know when profiling is off. 4967c478bd9Sstevel@tonic-gate * 4977c478bd9Sstevel@tonic-gate * NOTE: when mcount calls this function, it sets a private flag 4987c478bd9Sstevel@tonic-gate * so that it does not call again until this function returns, 4997c478bd9Sstevel@tonic-gate * thus preventing recursion. 5007c478bd9Sstevel@tonic-gate * 5017c478bd9Sstevel@tonic-gate * At Worst, the mcount in either a shared object or the a.out 5027c478bd9Sstevel@tonic-gate * could call once, and then the mcount living in the shared object 5037c478bd9Sstevel@tonic-gate * with monitor could call a second time (i.e. libc.so.1, although 5047c478bd9Sstevel@tonic-gate * presently it does not have mcount in it). This worst case 5057c478bd9Sstevel@tonic-gate * would involve Two active calls to mcount_newent, which it can 5067c478bd9Sstevel@tonic-gate * handle, since the second one would find a already-set value 5077c478bd9Sstevel@tonic-gate * in countbase. 5087c478bd9Sstevel@tonic-gate * 5097c478bd9Sstevel@tonic-gate * The only unfortunate result is that No new call counts 5107c478bd9Sstevel@tonic-gate * will be handed out until this function returns. 5117c478bd9Sstevel@tonic-gate * Thus if libc_malloc or other routines called inductively by 5127c478bd9Sstevel@tonic-gate * this routine have not yet been provided with a call count entry, 5137c478bd9Sstevel@tonic-gate * they will not get one until this function call is completed. 5147c478bd9Sstevel@tonic-gate * Thus a few calls to library routines during the course of 5157c478bd9Sstevel@tonic-gate * profiling setup, may not be counted. 5167c478bd9Sstevel@tonic-gate * 5177c478bd9Sstevel@tonic-gate * NOTE: countbase points at the next available entry, and 5187c478bd9Sstevel@tonic-gate * countlimit points past the last valid entry, in the current 5197c478bd9Sstevel@tonic-gate * function call counts array. 5207c478bd9Sstevel@tonic-gate * 5217c478bd9Sstevel@tonic-gate * 5227c478bd9Sstevel@tonic-gate * if profiling is off // countbase==0 5237c478bd9Sstevel@tonic-gate * just return 0 5247c478bd9Sstevel@tonic-gate * 5257c478bd9Sstevel@tonic-gate * else 5267c478bd9Sstevel@tonic-gate * if need more entries // because countbase points last valid entry 5277c478bd9Sstevel@tonic-gate * link in a new block, resetting countbase and countlimit 5287c478bd9Sstevel@tonic-gate * endif 5297c478bd9Sstevel@tonic-gate * if Got more entries 5307c478bd9Sstevel@tonic-gate * return pointer to the next available entry, and 5317c478bd9Sstevel@tonic-gate * update pointer-to-next-slot before you return. 5327c478bd9Sstevel@tonic-gate * 5337c478bd9Sstevel@tonic-gate * else // failed to get more entries 5347c478bd9Sstevel@tonic-gate * just return 0 5357c478bd9Sstevel@tonic-gate * 5367c478bd9Sstevel@tonic-gate * endif 5377c478bd9Sstevel@tonic-gate * endif 5387c478bd9Sstevel@tonic-gate */ 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate struct cnt * 5417c478bd9Sstevel@tonic-gate _mcount_newent(void) 5427c478bd9Sstevel@tonic-gate { 5437c478bd9Sstevel@tonic-gate if (countbase == 0) 5447c478bd9Sstevel@tonic-gate return (NULL); 5457c478bd9Sstevel@tonic-gate 546*7257d1b4Sraf if (countbase >= countlimit) 5477c478bd9Sstevel@tonic-gate _mnewblock(); /* get a new block; set countbase */ 5487c478bd9Sstevel@tonic-gate 5497c478bd9Sstevel@tonic-gate if (countbase != 0) { 5507c478bd9Sstevel@tonic-gate struct cnt *cur_countbase = (struct cnt *)(uintptr_t)countbase; 5517c478bd9Sstevel@tonic-gate 5527c478bd9Sstevel@tonic-gate countbase += sizeof (struct cnt); 5537c478bd9Sstevel@tonic-gate return (cur_countbase); 5547c478bd9Sstevel@tonic-gate } 5557c478bd9Sstevel@tonic-gate return (NULL); 5567c478bd9Sstevel@tonic-gate } 557