150397Sobrien/*- 250397Sobrien * Copyright (c) 1991 The Regents of the University of California. 350397Sobrien * All rights reserved. 450397Sobrien * 550397Sobrien * Redistribution and use in source and binary forms, with or without 650397Sobrien * modification, are permitted provided that the following conditions 750397Sobrien * are met: 850397Sobrien * 1. Redistributions of source code must retain the above copyright 950397Sobrien * notice, this list of conditions and the following disclaimer. 1050397Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1150397Sobrien * notice, this list of conditions and the following disclaimer in the 1250397Sobrien * documentation and/or other materials provided with the distribution. 1390075Sobrien * 3. [rescinded 22 July 1999] 1450397Sobrien * 4. Neither the name of the University nor the names of its contributors 1550397Sobrien * may be used to endorse or promote products derived from this software 1650397Sobrien * without specific prior written permission. 1750397Sobrien * 1850397Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1950397Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2050397Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2150397Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2250397Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2350397Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2450397Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2550397Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2650397Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2750397Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2850397Sobrien * SUCH DAMAGE. 2950397Sobrien */ 3050397Sobrien 3150397Sobrien/* 3250397Sobrien * This is a modified gmon.c by J.W.Hawtin <oolon@ankh.org>, 3350397Sobrien * 14/8/96 based on the original gmon.c in GCC and the hacked version 3450397Sobrien * solaris 2 sparc version (config/sparc/gmon-sol.c) by Mark Eichin. To do 3550397Sobrien * process profiling on solaris 2.X X86 3650397Sobrien * 3750397Sobrien * It must be used in conjunction with sol2-gc1.asm, which is used to start 3850397Sobrien * and stop process monitoring. 3950397Sobrien * 4050397Sobrien * Differences. 4150397Sobrien * 4250397Sobrien * On Solaris 2 _mcount is called by library functions not mcount, so support 4350397Sobrien * has been added for both. 4450397Sobrien * 4550397Sobrien * Also the prototype for profil() is different 4650397Sobrien * 4750397Sobrien * Solaris 2 does not seem to have char *minbrk whcih allows the setting of 4850397Sobrien * the minimum SBRK region so this code has been removed and lets pray malloc 4950397Sobrien * does not mess it up. 5050397Sobrien * 5150397Sobrien * Notes 5250397Sobrien * 5350397Sobrien * This code could easily be integrated with the original gmon.c and perhaps 5450397Sobrien * should be. 5550397Sobrien */ 56169689Skan#include "tconfig.h" 57169689Skan#include "tsystem.h" 58169689Skan#include <fcntl.h> /* for creat() */ 5950397Sobrien 6050397Sobrien#ifdef DEBUG 6150397Sobrien#include <stdio.h> 6250397Sobrien#endif 6350397Sobrien 64169689Skanstatic void moncontrol (int); 65169689Skanextern void monstartup (char *, char *); 66169689Skanextern void _mcleanup (void); 67169689Skanextern void internal_mcount (void); 6850397Sobrien 69169689Skan 7050397Sobrienstruct phdr { 7150397Sobrien char *lpc; 7250397Sobrien char *hpc; 7350397Sobrien int ncnt; 7450397Sobrien}; 7550397Sobrien 7650397Sobrien 7750397Sobrien#define HISTFRACTION 2 7850397Sobrien#define HISTCOUNTER unsigned short 7950397Sobrien#define HASHFRACTION 1 8050397Sobrien#define ARCDENSITY 2 8150397Sobrien#define MINARCS 50 8250397Sobrien#define BASEADDRESS 0x8000000 /* On Solaris 2 X86 all executables start here 8350397Sobrien and not at 0 */ 8450397Sobrien 8550397Sobrienstruct tostruct { 8650397Sobrien char *selfpc; 8750397Sobrien long count; 8850397Sobrien unsigned short link; 8950397Sobrien}; 90169689Skan 9150397Sobrienstruct rawarc { 9250397Sobrien unsigned long raw_frompc; 9350397Sobrien unsigned long raw_selfpc; 9450397Sobrien long raw_count; 9550397Sobrien}; 9650397Sobrien#define ROUNDDOWN(x,y) (((x)/(y))*(y)) 9750397Sobrien#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y)) 9850397Sobrien 9950397Sobrien/* char *minbrk; */ 10050397Sobrien 10150397Sobrien /* 10250397Sobrien * froms is actually a bunch of unsigned shorts indexing tos 10350397Sobrien */ 10450397Sobrienstatic int profiling = 3; 10550397Sobrienstatic unsigned short *froms; 10650397Sobrienstatic struct tostruct *tos = 0; 10750397Sobrienstatic long tolimit = 0; 10850397Sobrienstatic char *s_lowpc = 0; 10950397Sobrienstatic char *s_highpc = 0; 11050397Sobrienstatic unsigned long s_textsize = 0; 11150397Sobrien 11250397Sobrienstatic int ssiz; 11350397Sobrienstatic char *sbuf; 11450397Sobrienstatic int s_scale; 11550397Sobrien /* see profil(2) where this is describe (incorrectly) */ 11650397Sobrien#define SCALE_1_TO_1 0x10000L 11750397Sobrien 11850397Sobrien#define MSG "No space for profiling buffer(s)\n" 11950397Sobrien 12050397Sobrienextern int errno; 12150397Sobrien 122169689Skanvoid 123169689Skanmonstartup(char *lowpc, char *highpc) 12450397Sobrien{ 12550397Sobrien int monsize; 12650397Sobrien char *buffer; 12750397Sobrien register int o; 12850397Sobrien 12950397Sobrien /* 13050397Sobrien * round lowpc and highpc to multiples of the density we're using 13150397Sobrien * so the rest of the scaling (here and in gprof) stays in ints. 13250397Sobrien */ 13350397Sobrien lowpc = (char *) 134169689Skan ROUNDDOWN((unsigned long)lowpc, HISTFRACTION*sizeof(HISTCOUNTER)); 13550397Sobrien s_lowpc = lowpc; 13650397Sobrien highpc = (char *) 137169689Skan ROUNDUP((unsigned long)highpc, HISTFRACTION*sizeof(HISTCOUNTER)); 13850397Sobrien s_highpc = highpc; 13950397Sobrien s_textsize = highpc - lowpc; 14050397Sobrien monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr); 14150397Sobrien buffer = (char *) sbrk( monsize ); 14250397Sobrien if ( buffer == (char *) -1 ) { 14350397Sobrien write( 2 , MSG , sizeof(MSG) ); 14450397Sobrien return; 14550397Sobrien } 14650397Sobrien froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION ); 14750397Sobrien if ( froms == (unsigned short *) -1 ) { 14850397Sobrien write( 2 , MSG , sizeof(MSG) ); 14950397Sobrien froms = 0; 15050397Sobrien return; 15150397Sobrien } 15250397Sobrien tolimit = s_textsize * ARCDENSITY / 100; 15350397Sobrien if ( tolimit < MINARCS ) { 15450397Sobrien tolimit = MINARCS; 15550397Sobrien } else if ( tolimit > 65534 ) { 15650397Sobrien tolimit = 65534; 15750397Sobrien } 15850397Sobrien tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) ); 15950397Sobrien if ( tos == (struct tostruct *) -1 ) { 16050397Sobrien write( 2 , MSG , sizeof(MSG) ); 16150397Sobrien froms = 0; 16250397Sobrien tos = 0; 16350397Sobrien return; 16450397Sobrien } 16550397Sobrien/* minbrk = (char *) sbrk(0);*/ 16650397Sobrien tos[0].link = 0; 16750397Sobrien sbuf = buffer; 16850397Sobrien ssiz = monsize; 16950397Sobrien ( (struct phdr *) buffer ) -> lpc = lowpc; 17050397Sobrien ( (struct phdr *) buffer ) -> hpc = highpc; 17150397Sobrien ( (struct phdr *) buffer ) -> ncnt = ssiz; 17250397Sobrien monsize -= sizeof(struct phdr); 17350397Sobrien if ( monsize <= 0 ) 17450397Sobrien return; 17550397Sobrien o = highpc - lowpc; 17650397Sobrien if( monsize < o ) 17750397Sobrien#ifndef hp300 17850397Sobrien s_scale = ( (float) monsize / o ) * SCALE_1_TO_1; 17950397Sobrien#else /* avoid floating point */ 18050397Sobrien { 18150397Sobrien int quot = o / monsize; 18250397Sobrien 18350397Sobrien if (quot >= 0x10000) 18450397Sobrien s_scale = 1; 18550397Sobrien else if (quot >= 0x100) 18650397Sobrien s_scale = 0x10000 / quot; 18750397Sobrien else if (o >= 0x800000) 18850397Sobrien s_scale = 0x1000000 / (o / (monsize >> 8)); 18950397Sobrien else 19050397Sobrien s_scale = 0x1000000 / ((o << 8) / monsize); 19150397Sobrien } 19250397Sobrien#endif 19350397Sobrien else 19450397Sobrien s_scale = SCALE_1_TO_1; 19550397Sobrien moncontrol(1); 19650397Sobrien} 19750397Sobrien 198169689Skanvoid 199169689Skan_mcleanup (void) 20050397Sobrien{ 20150397Sobrien int fd; 20250397Sobrien int fromindex; 20350397Sobrien int endfrom; 20450397Sobrien char *frompc; 20550397Sobrien int toindex; 20650397Sobrien struct rawarc rawarc; 20750397Sobrien 20850397Sobrien moncontrol(0); 20950397Sobrien fd = creat( "gmon.out" , 0666 ); 21050397Sobrien if ( fd < 0 ) { 21150397Sobrien perror( "mcount: gmon.out" ); 21250397Sobrien return; 21350397Sobrien } 21450397Sobrien# ifdef DEBUG 21550397Sobrien fprintf( stderr , "[mcleanup] sbuf 0x%x ssiz %d\n" , sbuf , ssiz ); 216169689Skan# endif /* DEBUG */ 21750397Sobrien 21850397Sobrien write( fd , sbuf , ssiz ); 21950397Sobrien endfrom = s_textsize / (HASHFRACTION * sizeof(*froms)); 22050397Sobrien for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) { 22150397Sobrien if ( froms[fromindex] == 0 ) { 22250397Sobrien continue; 22350397Sobrien } 22450397Sobrien frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms)); 22550397Sobrien for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) { 22650397Sobrien# ifdef DEBUG 22750397Sobrien fprintf( stderr , 22850397Sobrien "[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" , 22950397Sobrien frompc , tos[toindex].selfpc , tos[toindex].count ); 230169689Skan# endif /* DEBUG */ 23150397Sobrien rawarc.raw_frompc = (unsigned long) frompc; 23250397Sobrien rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc; 23350397Sobrien rawarc.raw_count = tos[toindex].count; 23450397Sobrien write( fd , &rawarc , sizeof rawarc ); 23550397Sobrien } 23650397Sobrien } 23750397Sobrien close( fd ); 23850397Sobrien} 23950397Sobrien 24050397Sobrien/* Solaris 2 libraries use _mcount. */ 24150397Sobrienasm(".globl _mcount; _mcount: jmp internal_mcount"); 24250397Sobrien/* This is for compatibility with old versions of gcc which used mcount. */ 24350397Sobrienasm(".globl mcount; mcount: jmp internal_mcount"); 24450397Sobrien 245169689Skanvoid 246169689Skaninternal_mcount (void) 24750397Sobrien{ 24850397Sobrien register char *selfpc; 24950397Sobrien register unsigned short *frompcindex; 25050397Sobrien register struct tostruct *top; 25150397Sobrien register struct tostruct *prevtop; 25250397Sobrien register long toindex; 25350397Sobrien static char already_setup; 25450397Sobrien 25550397Sobrien /* 25650397Sobrien * find the return address for mcount, 25750397Sobrien * and the return address for mcount's caller. 25850397Sobrien */ 25950397Sobrien 26050397Sobrien /* selfpc = pc pushed by mcount call. 26150397Sobrien This identifies the function that was just entered. */ 26250397Sobrien selfpc = (void *) __builtin_return_address (0); 26350397Sobrien /* frompcindex = pc in preceding frame. 26450397Sobrien This identifies the caller of the function just entered. */ 26550397Sobrien frompcindex = (void *) __builtin_return_address (1); 26650397Sobrien 26750397Sobrien if(!already_setup) { 268169689Skan extern char etext[]; 26950397Sobrien already_setup = 1; 27050397Sobrien/* monstartup(0, etext); */ 271169689Skan monstartup((char*)0x08040000, etext); 27250397Sobrien#ifdef USE_ONEXIT 27350397Sobrien on_exit(_mcleanup, 0); 27450397Sobrien#else 27550397Sobrien atexit(_mcleanup); 27650397Sobrien#endif 27750397Sobrien } 27850397Sobrien /* 27950397Sobrien * check that we are profiling 28050397Sobrien * and that we aren't recursively invoked. 28150397Sobrien */ 28250397Sobrien if (profiling) { 28350397Sobrien goto out; 28450397Sobrien } 28550397Sobrien profiling++; 28650397Sobrien /* 28750397Sobrien * check that frompcindex is a reasonable pc value. 28850397Sobrien * for example: signal catchers get called from the stack, 28950397Sobrien * not from text space. too bad. 29050397Sobrien */ 29150397Sobrien frompcindex = (unsigned short *)((long)frompcindex - (long)s_lowpc); 29250397Sobrien if ((unsigned long)frompcindex > s_textsize) { 29350397Sobrien goto done; 29450397Sobrien } 29550397Sobrien frompcindex = 29650397Sobrien &froms[((long)frompcindex) / (HASHFRACTION * sizeof(*froms))]; 29750397Sobrien toindex = *frompcindex; 29850397Sobrien if (toindex == 0) { 29950397Sobrien /* 30050397Sobrien * first time traversing this arc 30150397Sobrien */ 30250397Sobrien toindex = ++tos[0].link; 30350397Sobrien if (toindex >= tolimit) { 30450397Sobrien goto overflow; 30550397Sobrien } 30650397Sobrien *frompcindex = toindex; 30750397Sobrien top = &tos[toindex]; 30850397Sobrien top->selfpc = selfpc; 30950397Sobrien top->count = 1; 31050397Sobrien top->link = 0; 31150397Sobrien goto done; 31250397Sobrien } 31350397Sobrien top = &tos[toindex]; 31450397Sobrien if (top->selfpc == selfpc) { 31550397Sobrien /* 31650397Sobrien * arc at front of chain; usual case. 31750397Sobrien */ 31850397Sobrien top->count++; 31950397Sobrien goto done; 32050397Sobrien } 32150397Sobrien /* 32250397Sobrien * have to go looking down chain for it. 32350397Sobrien * top points to what we are looking at, 32450397Sobrien * prevtop points to previous top. 32550397Sobrien * we know it is not at the head of the chain. 32650397Sobrien */ 32750397Sobrien for (; /* goto done */; ) { 32850397Sobrien if (top->link == 0) { 32950397Sobrien /* 33050397Sobrien * top is end of the chain and none of the chain 33150397Sobrien * had top->selfpc == selfpc. 33250397Sobrien * so we allocate a new tostruct 33350397Sobrien * and link it to the head of the chain. 33450397Sobrien */ 33550397Sobrien toindex = ++tos[0].link; 33650397Sobrien if (toindex >= tolimit) { 33750397Sobrien goto overflow; 33850397Sobrien } 33950397Sobrien top = &tos[toindex]; 34050397Sobrien top->selfpc = selfpc; 34150397Sobrien top->count = 1; 34250397Sobrien top->link = *frompcindex; 34350397Sobrien *frompcindex = toindex; 34450397Sobrien goto done; 34550397Sobrien } 34650397Sobrien /* 34750397Sobrien * otherwise, check the next arc on the chain. 34850397Sobrien */ 34950397Sobrien prevtop = top; 35050397Sobrien top = &tos[top->link]; 35150397Sobrien if (top->selfpc == selfpc) { 35250397Sobrien /* 35350397Sobrien * there it is. 35450397Sobrien * increment its count 35550397Sobrien * move it to the head of the chain. 35650397Sobrien */ 35750397Sobrien top->count++; 35850397Sobrien toindex = prevtop->link; 35950397Sobrien prevtop->link = top->link; 36050397Sobrien top->link = *frompcindex; 36150397Sobrien *frompcindex = toindex; 36250397Sobrien goto done; 36350397Sobrien } 36450397Sobrien 36550397Sobrien } 36650397Sobriendone: 36750397Sobrien profiling--; 36850397Sobrien /* and fall through */ 36950397Sobrienout: 37050397Sobrien return; /* normal return restores saved registers */ 37150397Sobrien 37250397Sobrienoverflow: 37350397Sobrien profiling++; /* halt further profiling */ 37450397Sobrien# define TOLIMIT "mcount: tos overflow\n" 37550397Sobrien write(2, TOLIMIT, sizeof(TOLIMIT)); 37650397Sobrien goto out; 37750397Sobrien} 37850397Sobrien 37950397Sobrien/* 38050397Sobrien * Control profiling 38150397Sobrien * profiling is what mcount checks to see if 38250397Sobrien * all the data structures are ready. 38350397Sobrien */ 384169689Skanstatic void 385169689Skanmoncontrol(int mode) 38650397Sobrien{ 38750397Sobrien if (mode) 38850397Sobrien { 38950397Sobrien /* start */ 39050397Sobrien profil((unsigned short *)(sbuf + sizeof(struct phdr)), 39150397Sobrien ssiz - sizeof(struct phdr), 392169689Skan (long)s_lowpc, s_scale); 39350397Sobrien 39450397Sobrien profiling = 0; 39550397Sobrien } else { 39650397Sobrien /* stop */ 39750397Sobrien profil((unsigned short *)0, 0, 0, 0); 39850397Sobrien profiling = 3; 39950397Sobrien } 40050397Sobrien} 401