gmon-sol2.c revision 169689
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#include "tconfig.h" 57#include "tsystem.h" 58#include <fcntl.h> /* for creat() */ 59 60#ifdef DEBUG 61#include <stdio.h> 62#endif 63 64static void moncontrol (int); 65extern void monstartup (char *, char *); 66extern void _mcleanup (void); 67extern void internal_mcount (void); 68 69 70struct phdr { 71 char *lpc; 72 char *hpc; 73 int ncnt; 74}; 75 76 77#define HISTFRACTION 2 78#define HISTCOUNTER unsigned short 79#define HASHFRACTION 1 80#define ARCDENSITY 2 81#define MINARCS 50 82#define BASEADDRESS 0x8000000 /* On Solaris 2 X86 all executables start here 83 and not at 0 */ 84 85struct tostruct { 86 char *selfpc; 87 long count; 88 unsigned short link; 89}; 90 91struct rawarc { 92 unsigned long raw_frompc; 93 unsigned long raw_selfpc; 94 long raw_count; 95}; 96#define ROUNDDOWN(x,y) (((x)/(y))*(y)) 97#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y)) 98 99/* char *minbrk; */ 100 101 /* 102 * froms is actually a bunch of unsigned shorts indexing tos 103 */ 104static int profiling = 3; 105static unsigned short *froms; 106static struct tostruct *tos = 0; 107static long tolimit = 0; 108static char *s_lowpc = 0; 109static char *s_highpc = 0; 110static unsigned long s_textsize = 0; 111 112static int ssiz; 113static char *sbuf; 114static int s_scale; 115 /* see profil(2) where this is describe (incorrectly) */ 116#define SCALE_1_TO_1 0x10000L 117 118#define MSG "No space for profiling buffer(s)\n" 119 120extern int errno; 121 122void 123monstartup(char *lowpc, char *highpc) 124{ 125 int monsize; 126 char *buffer; 127 register int o; 128 129 /* 130 * round lowpc and highpc to multiples of the density we're using 131 * so the rest of the scaling (here and in gprof) stays in ints. 132 */ 133 lowpc = (char *) 134 ROUNDDOWN((unsigned long)lowpc, HISTFRACTION*sizeof(HISTCOUNTER)); 135 s_lowpc = lowpc; 136 highpc = (char *) 137 ROUNDUP((unsigned long)highpc, HISTFRACTION*sizeof(HISTCOUNTER)); 138 s_highpc = highpc; 139 s_textsize = highpc - lowpc; 140 monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr); 141 buffer = (char *) sbrk( monsize ); 142 if ( buffer == (char *) -1 ) { 143 write( 2 , MSG , sizeof(MSG) ); 144 return; 145 } 146 froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION ); 147 if ( froms == (unsigned short *) -1 ) { 148 write( 2 , MSG , sizeof(MSG) ); 149 froms = 0; 150 return; 151 } 152 tolimit = s_textsize * ARCDENSITY / 100; 153 if ( tolimit < MINARCS ) { 154 tolimit = MINARCS; 155 } else if ( tolimit > 65534 ) { 156 tolimit = 65534; 157 } 158 tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) ); 159 if ( tos == (struct tostruct *) -1 ) { 160 write( 2 , MSG , sizeof(MSG) ); 161 froms = 0; 162 tos = 0; 163 return; 164 } 165/* minbrk = (char *) sbrk(0);*/ 166 tos[0].link = 0; 167 sbuf = buffer; 168 ssiz = monsize; 169 ( (struct phdr *) buffer ) -> lpc = lowpc; 170 ( (struct phdr *) buffer ) -> hpc = highpc; 171 ( (struct phdr *) buffer ) -> ncnt = ssiz; 172 monsize -= sizeof(struct phdr); 173 if ( monsize <= 0 ) 174 return; 175 o = highpc - lowpc; 176 if( monsize < o ) 177#ifndef hp300 178 s_scale = ( (float) monsize / o ) * SCALE_1_TO_1; 179#else /* avoid floating point */ 180 { 181 int quot = o / monsize; 182 183 if (quot >= 0x10000) 184 s_scale = 1; 185 else if (quot >= 0x100) 186 s_scale = 0x10000 / quot; 187 else if (o >= 0x800000) 188 s_scale = 0x1000000 / (o / (monsize >> 8)); 189 else 190 s_scale = 0x1000000 / ((o << 8) / monsize); 191 } 192#endif 193 else 194 s_scale = SCALE_1_TO_1; 195 moncontrol(1); 196} 197 198void 199_mcleanup (void) 200{ 201 int fd; 202 int fromindex; 203 int endfrom; 204 char *frompc; 205 int toindex; 206 struct rawarc rawarc; 207 208 moncontrol(0); 209 fd = creat( "gmon.out" , 0666 ); 210 if ( fd < 0 ) { 211 perror( "mcount: gmon.out" ); 212 return; 213 } 214# ifdef DEBUG 215 fprintf( stderr , "[mcleanup] sbuf 0x%x ssiz %d\n" , sbuf , ssiz ); 216# endif /* DEBUG */ 217 218 write( fd , sbuf , ssiz ); 219 endfrom = s_textsize / (HASHFRACTION * sizeof(*froms)); 220 for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) { 221 if ( froms[fromindex] == 0 ) { 222 continue; 223 } 224 frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms)); 225 for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) { 226# ifdef DEBUG 227 fprintf( stderr , 228 "[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" , 229 frompc , tos[toindex].selfpc , tos[toindex].count ); 230# endif /* DEBUG */ 231 rawarc.raw_frompc = (unsigned long) frompc; 232 rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc; 233 rawarc.raw_count = tos[toindex].count; 234 write( fd , &rawarc , sizeof rawarc ); 235 } 236 } 237 close( fd ); 238} 239 240/* Solaris 2 libraries use _mcount. */ 241asm(".globl _mcount; _mcount: jmp internal_mcount"); 242/* This is for compatibility with old versions of gcc which used mcount. */ 243asm(".globl mcount; mcount: jmp internal_mcount"); 244 245void 246internal_mcount (void) 247{ 248 register char *selfpc; 249 register unsigned short *frompcindex; 250 register struct tostruct *top; 251 register struct tostruct *prevtop; 252 register long toindex; 253 static char already_setup; 254 255 /* 256 * find the return address for mcount, 257 * and the return address for mcount's caller. 258 */ 259 260 /* selfpc = pc pushed by mcount call. 261 This identifies the function that was just entered. */ 262 selfpc = (void *) __builtin_return_address (0); 263 /* frompcindex = pc in preceding frame. 264 This identifies the caller of the function just entered. */ 265 frompcindex = (void *) __builtin_return_address (1); 266 267 if(!already_setup) { 268 extern char etext[]; 269 already_setup = 1; 270/* monstartup(0, etext); */ 271 monstartup((char*)0x08040000, etext); 272#ifdef USE_ONEXIT 273 on_exit(_mcleanup, 0); 274#else 275 atexit(_mcleanup); 276#endif 277 } 278 /* 279 * check that we are profiling 280 * and that we aren't recursively invoked. 281 */ 282 if (profiling) { 283 goto out; 284 } 285 profiling++; 286 /* 287 * check that frompcindex is a reasonable pc value. 288 * for example: signal catchers get called from the stack, 289 * not from text space. too bad. 290 */ 291 frompcindex = (unsigned short *)((long)frompcindex - (long)s_lowpc); 292 if ((unsigned long)frompcindex > s_textsize) { 293 goto done; 294 } 295 frompcindex = 296 &froms[((long)frompcindex) / (HASHFRACTION * sizeof(*froms))]; 297 toindex = *frompcindex; 298 if (toindex == 0) { 299 /* 300 * first time traversing this arc 301 */ 302 toindex = ++tos[0].link; 303 if (toindex >= tolimit) { 304 goto overflow; 305 } 306 *frompcindex = toindex; 307 top = &tos[toindex]; 308 top->selfpc = selfpc; 309 top->count = 1; 310 top->link = 0; 311 goto done; 312 } 313 top = &tos[toindex]; 314 if (top->selfpc == selfpc) { 315 /* 316 * arc at front of chain; usual case. 317 */ 318 top->count++; 319 goto done; 320 } 321 /* 322 * have to go looking down chain for it. 323 * top points to what we are looking at, 324 * prevtop points to previous top. 325 * we know it is not at the head of the chain. 326 */ 327 for (; /* goto done */; ) { 328 if (top->link == 0) { 329 /* 330 * top is end of the chain and none of the chain 331 * had top->selfpc == selfpc. 332 * so we allocate a new tostruct 333 * and link it to the head of the chain. 334 */ 335 toindex = ++tos[0].link; 336 if (toindex >= tolimit) { 337 goto overflow; 338 } 339 top = &tos[toindex]; 340 top->selfpc = selfpc; 341 top->count = 1; 342 top->link = *frompcindex; 343 *frompcindex = toindex; 344 goto done; 345 } 346 /* 347 * otherwise, check the next arc on the chain. 348 */ 349 prevtop = top; 350 top = &tos[top->link]; 351 if (top->selfpc == selfpc) { 352 /* 353 * there it is. 354 * increment its count 355 * move it to the head of the chain. 356 */ 357 top->count++; 358 toindex = prevtop->link; 359 prevtop->link = top->link; 360 top->link = *frompcindex; 361 *frompcindex = toindex; 362 goto done; 363 } 364 365 } 366done: 367 profiling--; 368 /* and fall through */ 369out: 370 return; /* normal return restores saved registers */ 371 372overflow: 373 profiling++; /* halt further profiling */ 374# define TOLIMIT "mcount: tos overflow\n" 375 write(2, TOLIMIT, sizeof(TOLIMIT)); 376 goto out; 377} 378 379/* 380 * Control profiling 381 * profiling is what mcount checks to see if 382 * all the data structures are ready. 383 */ 384static void 385moncontrol(int mode) 386{ 387 if (mode) 388 { 389 /* start */ 390 profil((unsigned short *)(sbuf + sizeof(struct phdr)), 391 ssiz - sizeof(struct phdr), 392 (long)s_lowpc, s_scale); 393 394 profiling = 0; 395 } else { 396 /* stop */ 397 profil((unsigned short *)0, 0, 0, 0); 398 profiling = 3; 399 } 400} 401