gmon-sol2.c revision 90075
1/*- 2 * Copyright (c) 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. [rescinded 22 July 1999] 14 * 4. Neither the name of the University nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31/* 32 * This is a modified gmon.c by J.W.Hawtin <oolon@ankh.org>, 33 * 14/8/96 based on the original gmon.c in GCC and the hacked version 34 * solaris 2 sparc version (config/sparc/gmon-sol.c) by Mark Eichin. To do 35 * process profiling on solaris 2.X X86 36 * 37 * It must be used in conjunction with sol2-gc1.asm, which is used to start 38 * and stop process monitoring. 39 * 40 * Differences. 41 * 42 * On Solaris 2 _mcount is called by library functions not mcount, so support 43 * has been added for both. 44 * 45 * Also the prototype for profil() is different 46 * 47 * Solaris 2 does not seem to have char *minbrk whcih allows the setting of 48 * the minimum SBRK region so this code has been removed and lets pray malloc 49 * does not mess it up. 50 * 51 * Notes 52 * 53 * This code could easily be integrated with the original gmon.c and perhaps 54 * should be. 55 */ 56 57#ifndef lint 58static char sccsid[] = "@(#)gmon.c 5.3 (Berkeley) 5/22/91"; 59#endif /* not lint */ 60 61#if 0 62#include <unistd.h> 63 64#endif 65#ifdef DEBUG 66#include <stdio.h> 67#endif 68 69#if 0 70#include "i386/gmon.h" 71#else 72 73struct phdr { 74 char *lpc; 75 char *hpc; 76 int ncnt; 77}; 78 79 80#define HISTFRACTION 2 81#define HISTCOUNTER unsigned short 82#define HASHFRACTION 1 83#define ARCDENSITY 2 84#define MINARCS 50 85#define BASEADDRESS 0x8000000 /* On Solaris 2 X86 all executables start here 86 and not at 0 */ 87 88struct tostruct { 89 char *selfpc; 90 long count; 91 unsigned short link; 92}; 93struct rawarc { 94 unsigned long raw_frompc; 95 unsigned long raw_selfpc; 96 long raw_count; 97}; 98#define ROUNDDOWN(x,y) (((x)/(y))*(y)) 99#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y)) 100#endif 101 102/* char *minbrk; */ 103 104#ifdef __alpha 105extern char *sbrk (); 106#endif 107 108 /* 109 * froms is actually a bunch of unsigned shorts indexing tos 110 */ 111static int profiling = 3; 112static unsigned short *froms; 113static struct tostruct *tos = 0; 114static long tolimit = 0; 115static char *s_lowpc = 0; 116static char *s_highpc = 0; 117static unsigned long s_textsize = 0; 118 119static int ssiz; 120static char *sbuf; 121static int s_scale; 122 /* see profil(2) where this is describe (incorrectly) */ 123#define SCALE_1_TO_1 0x10000L 124 125#define MSG "No space for profiling buffer(s)\n" 126 127extern int errno; 128 129monstartup(lowpc, highpc) 130 char *lowpc; 131 char *highpc; 132{ 133 int monsize; 134 char *buffer; 135 register int o; 136 137 /* 138 * round lowpc and highpc to multiples of the density we're using 139 * so the rest of the scaling (here and in gprof) stays in ints. 140 */ 141 lowpc = (char *) 142 ROUNDDOWN((unsigned)lowpc, HISTFRACTION*sizeof(HISTCOUNTER)); 143 s_lowpc = lowpc; 144 highpc = (char *) 145 ROUNDUP((unsigned)highpc, HISTFRACTION*sizeof(HISTCOUNTER)); 146 s_highpc = highpc; 147 s_textsize = highpc - lowpc; 148 monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr); 149 buffer = (char *) sbrk( monsize ); 150 if ( buffer == (char *) -1 ) { 151 write( 2 , MSG , sizeof(MSG) ); 152 return; 153 } 154 froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION ); 155 if ( froms == (unsigned short *) -1 ) { 156 write( 2 , MSG , sizeof(MSG) ); 157 froms = 0; 158 return; 159 } 160 tolimit = s_textsize * ARCDENSITY / 100; 161 if ( tolimit < MINARCS ) { 162 tolimit = MINARCS; 163 } else if ( tolimit > 65534 ) { 164 tolimit = 65534; 165 } 166 tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) ); 167 if ( tos == (struct tostruct *) -1 ) { 168 write( 2 , MSG , sizeof(MSG) ); 169 froms = 0; 170 tos = 0; 171 return; 172 } 173/* minbrk = (char *) sbrk(0);*/ 174 tos[0].link = 0; 175 sbuf = buffer; 176 ssiz = monsize; 177 ( (struct phdr *) buffer ) -> lpc = lowpc; 178 ( (struct phdr *) buffer ) -> hpc = highpc; 179 ( (struct phdr *) buffer ) -> ncnt = ssiz; 180 monsize -= sizeof(struct phdr); 181 if ( monsize <= 0 ) 182 return; 183 o = highpc - lowpc; 184 if( monsize < o ) 185#ifndef hp300 186 s_scale = ( (float) monsize / o ) * SCALE_1_TO_1; 187#else /* avoid floating point */ 188 { 189 int quot = o / monsize; 190 191 if (quot >= 0x10000) 192 s_scale = 1; 193 else if (quot >= 0x100) 194 s_scale = 0x10000 / quot; 195 else if (o >= 0x800000) 196 s_scale = 0x1000000 / (o / (monsize >> 8)); 197 else 198 s_scale = 0x1000000 / ((o << 8) / monsize); 199 } 200#endif 201 else 202 s_scale = SCALE_1_TO_1; 203 moncontrol(1); 204} 205 206_mcleanup() 207{ 208 int fd; 209 int fromindex; 210 int endfrom; 211 char *frompc; 212 int toindex; 213 struct rawarc rawarc; 214 215 moncontrol(0); 216 fd = creat( "gmon.out" , 0666 ); 217 if ( fd < 0 ) { 218 perror( "mcount: gmon.out" ); 219 return; 220 } 221# ifdef DEBUG 222 fprintf( stderr , "[mcleanup] sbuf 0x%x ssiz %d\n" , sbuf , ssiz ); 223# endif DEBUG 224 225 write( fd , sbuf , ssiz ); 226 endfrom = s_textsize / (HASHFRACTION * sizeof(*froms)); 227 for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) { 228 if ( froms[fromindex] == 0 ) { 229 continue; 230 } 231 frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms)); 232 for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) { 233# ifdef DEBUG 234 fprintf( stderr , 235 "[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" , 236 frompc , tos[toindex].selfpc , tos[toindex].count ); 237# endif DEBUG 238 rawarc.raw_frompc = (unsigned long) frompc; 239 rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc; 240 rawarc.raw_count = tos[toindex].count; 241 write( fd , &rawarc , sizeof rawarc ); 242 } 243 } 244 close( fd ); 245} 246 247/* Solaris 2 libraries use _mcount. */ 248asm(".globl _mcount; _mcount: jmp internal_mcount"); 249/* This is for compatibility with old versions of gcc which used mcount. */ 250asm(".globl mcount; mcount: jmp internal_mcount"); 251 252internal_mcount() 253{ 254 register char *selfpc; 255 register unsigned short *frompcindex; 256 register struct tostruct *top; 257 register struct tostruct *prevtop; 258 register long toindex; 259 static char already_setup; 260 261 /* 262 * find the return address for mcount, 263 * and the return address for mcount's caller. 264 */ 265 266 /* selfpc = pc pushed by mcount call. 267 This identifies the function that was just entered. */ 268 selfpc = (void *) __builtin_return_address (0); 269 /* frompcindex = pc in preceding frame. 270 This identifies the caller of the function just entered. */ 271 frompcindex = (void *) __builtin_return_address (1); 272 273 if(!already_setup) { 274 extern etext(); 275 already_setup = 1; 276/* monstartup(0, etext); */ 277 monstartup(0x08040000, etext); 278#ifdef USE_ONEXIT 279 on_exit(_mcleanup, 0); 280#else 281 atexit(_mcleanup); 282#endif 283 } 284 /* 285 * check that we are profiling 286 * and that we aren't recursively invoked. 287 */ 288 if (profiling) { 289 goto out; 290 } 291 profiling++; 292 /* 293 * check that frompcindex is a reasonable pc value. 294 * for example: signal catchers get called from the stack, 295 * not from text space. too bad. 296 */ 297 frompcindex = (unsigned short *)((long)frompcindex - (long)s_lowpc); 298 if ((unsigned long)frompcindex > s_textsize) { 299 goto done; 300 } 301 frompcindex = 302 &froms[((long)frompcindex) / (HASHFRACTION * sizeof(*froms))]; 303 toindex = *frompcindex; 304 if (toindex == 0) { 305 /* 306 * first time traversing this arc 307 */ 308 toindex = ++tos[0].link; 309 if (toindex >= tolimit) { 310 goto overflow; 311 } 312 *frompcindex = toindex; 313 top = &tos[toindex]; 314 top->selfpc = selfpc; 315 top->count = 1; 316 top->link = 0; 317 goto done; 318 } 319 top = &tos[toindex]; 320 if (top->selfpc == selfpc) { 321 /* 322 * arc at front of chain; usual case. 323 */ 324 top->count++; 325 goto done; 326 } 327 /* 328 * have to go looking down chain for it. 329 * top points to what we are looking at, 330 * prevtop points to previous top. 331 * we know it is not at the head of the chain. 332 */ 333 for (; /* goto done */; ) { 334 if (top->link == 0) { 335 /* 336 * top is end of the chain and none of the chain 337 * had top->selfpc == selfpc. 338 * so we allocate a new tostruct 339 * and link it to the head of the chain. 340 */ 341 toindex = ++tos[0].link; 342 if (toindex >= tolimit) { 343 goto overflow; 344 } 345 top = &tos[toindex]; 346 top->selfpc = selfpc; 347 top->count = 1; 348 top->link = *frompcindex; 349 *frompcindex = toindex; 350 goto done; 351 } 352 /* 353 * otherwise, check the next arc on the chain. 354 */ 355 prevtop = top; 356 top = &tos[top->link]; 357 if (top->selfpc == selfpc) { 358 /* 359 * there it is. 360 * increment its count 361 * move it to the head of the chain. 362 */ 363 top->count++; 364 toindex = prevtop->link; 365 prevtop->link = top->link; 366 top->link = *frompcindex; 367 *frompcindex = toindex; 368 goto done; 369 } 370 371 } 372done: 373 profiling--; 374 /* and fall through */ 375out: 376 return; /* normal return restores saved registers */ 377 378overflow: 379 profiling++; /* halt further profiling */ 380# define TOLIMIT "mcount: tos overflow\n" 381 write(2, TOLIMIT, sizeof(TOLIMIT)); 382 goto out; 383} 384 385/* 386 * Control profiling 387 * profiling is what mcount checks to see if 388 * all the data structures are ready. 389 */ 390moncontrol(mode) 391 int mode; 392{ 393 if (mode) 394 { 395 /* start */ 396 profil((unsigned short *)(sbuf + sizeof(struct phdr)), 397 ssiz - sizeof(struct phdr), 398 (int)s_lowpc, s_scale); 399 400 profiling = 0; 401 } else { 402 /* stop */ 403 profil((unsigned short *)0, 0, 0, 0); 404 profiling = 3; 405 } 406} 407