gmon-sol2.c revision 96263
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/* Mangled into a form that works on Sparc Solaris 2 by Mark Eichin 32 * for Cygnus Support, July 1992. 33 */ 34 35#include "config.h" 36#include "system.h" 37 38#if 0 39#include "sparc/gmon.h" 40#else 41struct phdr { 42 char *lpc; 43 char *hpc; 44 int ncnt; 45}; 46#define HISTFRACTION 2 47#define HISTCOUNTER unsigned short 48#define HASHFRACTION 1 49#define ARCDENSITY 2 50#define MINARCS 50 51struct tostruct { 52 char *selfpc; 53 long count; 54 unsigned short link; 55}; 56struct rawarc { 57 unsigned long raw_frompc; 58 unsigned long raw_selfpc; 59 long raw_count; 60}; 61#define ROUNDDOWN(x,y) (((x)/(y))*(y)) 62#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y)) 63 64#endif 65 66/* extern mcount() asm ("mcount"); */ 67/*extern*/ char *minbrk /* asm ("minbrk") */; 68 69 /* 70 * froms is actually a bunch of unsigned shorts indexing tos 71 */ 72static int profiling = 3; 73static unsigned short *froms; 74static struct tostruct *tos = 0; 75static long tolimit = 0; 76static char *s_lowpc = 0; 77static char *s_highpc = 0; 78static unsigned long s_textsize = 0; 79 80static int ssiz; 81static char *sbuf; 82static int s_scale; 83 /* see profil(2) where this is describe (incorrectly) */ 84#define SCALE_1_TO_1 0x10000L 85 86#define MSG "No space for profiling buffer(s)\n" 87 88static void moncontrol PARAMS ((int)); 89extern void monstartup PARAMS ((char *, char *)); 90extern void _mcleanup PARAMS ((void)); 91 92void monstartup(lowpc, highpc) 93 char *lowpc; 94 char *highpc; 95{ 96 int monsize; 97 char *buffer; 98 register int o; 99 100 /* 101 * round lowpc and highpc to multiples of the density we're using 102 * so the rest of the scaling (here and in gprof) stays in ints. 103 */ 104 lowpc = (char *) 105 ROUNDDOWN((unsigned long)lowpc, HISTFRACTION*sizeof(HISTCOUNTER)); 106 s_lowpc = lowpc; 107 highpc = (char *) 108 ROUNDUP((unsigned long)highpc, HISTFRACTION*sizeof(HISTCOUNTER)); 109 s_highpc = highpc; 110 s_textsize = highpc - lowpc; 111 monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr); 112 buffer = sbrk( monsize ); 113 if ( buffer == (char *) -1 ) { 114 write( 2 , MSG , sizeof(MSG) ); 115 return; 116 } 117 froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION ); 118 if ( froms == (unsigned short *) -1 ) { 119 write( 2 , MSG , sizeof(MSG) ); 120 froms = 0; 121 return; 122 } 123 tolimit = s_textsize * ARCDENSITY / 100; 124 if ( tolimit < MINARCS ) { 125 tolimit = MINARCS; 126 } else if ( tolimit > 65534 ) { 127 tolimit = 65534; 128 } 129 tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) ); 130 if ( tos == (struct tostruct *) -1 ) { 131 write( 2 , MSG , sizeof(MSG) ); 132 froms = 0; 133 tos = 0; 134 return; 135 } 136 minbrk = sbrk(0); 137 tos[0].link = 0; 138 sbuf = buffer; 139 ssiz = monsize; 140 ( (struct phdr *) buffer ) -> lpc = lowpc; 141 ( (struct phdr *) buffer ) -> hpc = highpc; 142 ( (struct phdr *) buffer ) -> ncnt = ssiz; 143 monsize -= sizeof(struct phdr); 144 if ( monsize <= 0 ) 145 return; 146 o = highpc - lowpc; 147 if( monsize < o ) 148#ifndef hp300 149 s_scale = ( (float) monsize / o ) * SCALE_1_TO_1; 150#else /* avoid floating point */ 151 { 152 int quot = o / monsize; 153 154 if (quot >= 0x10000) 155 s_scale = 1; 156 else if (quot >= 0x100) 157 s_scale = 0x10000 / quot; 158 else if (o >= 0x800000) 159 s_scale = 0x1000000 / (o / (monsize >> 8)); 160 else 161 s_scale = 0x1000000 / ((o << 8) / monsize); 162 } 163#endif 164 else 165 s_scale = SCALE_1_TO_1; 166 moncontrol(1); 167} 168 169void 170_mcleanup() 171{ 172 int fd; 173 int fromindex; 174 int endfrom; 175 char *frompc; 176 int toindex; 177 struct rawarc rawarc; 178 char *profdir; 179 const char *proffile; 180 char *progname; 181 char buf[PATH_MAX]; 182 extern char **___Argv; 183 184 moncontrol(0); 185 186 if ((profdir = getenv("PROFDIR")) != NULL) { 187 /* If PROFDIR contains a null value, no profiling output is produced */ 188 if (*profdir == '\0') { 189 return; 190 } 191 192 progname=strrchr(___Argv[0], '/'); 193 if (progname == NULL) 194 progname=___Argv[0]; 195 else 196 progname++; 197 198 sprintf(buf, "%s/%ld.%s", profdir, (long) getpid(), progname); 199 proffile = buf; 200 } else { 201 proffile = "gmon.out"; 202 } 203 204 fd = creat( proffile, 0666 ); 205 if ( fd < 0 ) { 206 perror( proffile ); 207 return; 208 } 209# ifdef DEBUG 210 fprintf( stderr , "[mcleanup] sbuf 0x%x ssiz %d\n" , sbuf , ssiz ); 211# endif /* DEBUG */ 212 write( fd , sbuf , ssiz ); 213 endfrom = s_textsize / (HASHFRACTION * sizeof(*froms)); 214 for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) { 215 if ( froms[fromindex] == 0 ) { 216 continue; 217 } 218 frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms)); 219 for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) { 220# ifdef DEBUG 221 fprintf( stderr , 222 "[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" , 223 frompc , tos[toindex].selfpc , tos[toindex].count ); 224# endif /* DEBUG */ 225 rawarc.raw_frompc = (unsigned long) frompc; 226 rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc; 227 rawarc.raw_count = tos[toindex].count; 228 write( fd , &rawarc , sizeof rawarc ); 229 } 230 } 231 close( fd ); 232} 233 234/* 235 * The Sparc stack frame is only held together by the frame pointers 236 * in the register windows. According to the SVR4 SPARC ABI 237 * Supplement, Low Level System Information/Operating System 238 * Interface/Software Trap Types, a type 3 trap will flush all of the 239 * register windows to the stack, which will make it possible to walk 240 * the frames and find the return addresses. 241 * However, it seems awfully expensive to incur a trap (system 242 * call) for every function call. It turns out that "call" simply puts 243 * the return address in %o7 expecting the "save" in the procedure to 244 * shift it into %i7; this means that before the "save" occurs, %o7 245 * contains the address of the call to mcount, and %i7 still contains 246 * the caller above that. The asm mcount here simply saves those 247 * registers in argument registers and branches to internal_mcount, 248 * simulating a call with arguments. 249 * Kludges: 250 * 1) the branch to internal_mcount is hard coded; it should be 251 * possible to tell asm to use the assembler-name of a symbol. 252 * 2) in theory, the function calling mcount could have saved %i7 253 * somewhere and reused the register; in practice, I *think* this will 254 * break longjmp (and maybe the debugger) but I'm not certain. (I take 255 * some comfort in the knowledge that it will break the native mcount 256 * as well.) 257 * 3) if builtin_return_address worked, this could be portable. 258 * However, it would really have to be optimized for arguments of 0 259 * and 1 and do something like what we have here in order to avoid the 260 * trap per function call performance hit. 261 * 4) the atexit and monsetup calls prevent this from simply 262 * being a leaf routine that doesn't do a "save" (and would thus have 263 * access to %o7 and %i7 directly) but the call to write() at the end 264 * would have also prevented this. 265 * 266 * -- [eichin:19920702.1107EST] 267 */ 268 269static void internal_mcount PARAMS ((char *, unsigned short *)) ATTRIBUTE_UNUSED; 270 271/* i7 == last ret, -> frompcindex */ 272/* o7 == current ret, -> selfpc */ 273/* Solaris 2 libraries use _mcount. */ 274asm(".global _mcount; _mcount: mov %i7,%o1; mov %o7,%o0;b,a internal_mcount"); 275/* This is for compatibility with old versions of gcc which used mcount. */ 276asm(".global mcount; mcount: mov %i7,%o1; mov %o7,%o0;b,a internal_mcount"); 277 278static void internal_mcount(selfpc, frompcindex) 279 register char *selfpc; 280 register unsigned short *frompcindex; 281{ 282 register struct tostruct *top; 283 register struct tostruct *prevtop; 284 register long toindex; 285 static char already_setup; 286 287 /* 288 * find the return address for mcount, 289 * and the return address for mcount's caller. 290 */ 291 292 if(!already_setup) { 293 extern char etext[]; 294 extern char _start[]; 295 extern char _init[]; 296 already_setup = 1; 297 monstartup(_start < _init ? _start : _init, etext); 298#ifdef USE_ONEXIT 299 on_exit(_mcleanup, 0); 300#else 301 atexit(_mcleanup); 302#endif 303 } 304 /* 305 * check that we are profiling 306 * and that we aren't recursively invoked. 307 */ 308 if (profiling) { 309 goto out; 310 } 311 profiling++; 312 /* 313 * check that frompcindex is a reasonable pc value. 314 * for example: signal catchers get called from the stack, 315 * not from text space. too bad. 316 */ 317 frompcindex = (unsigned short *)((long)frompcindex - (long)s_lowpc); 318 if ((unsigned long)frompcindex > s_textsize) { 319 goto done; 320 } 321 frompcindex = 322 &froms[((long)frompcindex) / (HASHFRACTION * sizeof(*froms))]; 323 toindex = *frompcindex; 324 if (toindex == 0) { 325 /* 326 * first time traversing this arc 327 */ 328 toindex = ++tos[0].link; 329 if (toindex >= tolimit) { 330 goto overflow; 331 } 332 *frompcindex = toindex; 333 top = &tos[toindex]; 334 top->selfpc = selfpc; 335 top->count = 1; 336 top->link = 0; 337 goto done; 338 } 339 top = &tos[toindex]; 340 if (top->selfpc == selfpc) { 341 /* 342 * arc at front of chain; usual case. 343 */ 344 top->count++; 345 goto done; 346 } 347 /* 348 * have to go looking down chain for it. 349 * top points to what we are looking at, 350 * prevtop points to previous top. 351 * we know it is not at the head of the chain. 352 */ 353 for (; /* goto done */; ) { 354 if (top->link == 0) { 355 /* 356 * top is end of the chain and none of the chain 357 * had top->selfpc == selfpc. 358 * so we allocate a new tostruct 359 * and link it to the head of the chain. 360 */ 361 toindex = ++tos[0].link; 362 if (toindex >= tolimit) { 363 goto overflow; 364 } 365 top = &tos[toindex]; 366 top->selfpc = selfpc; 367 top->count = 1; 368 top->link = *frompcindex; 369 *frompcindex = toindex; 370 goto done; 371 } 372 /* 373 * otherwise, check the next arc on the chain. 374 */ 375 prevtop = top; 376 top = &tos[top->link]; 377 if (top->selfpc == selfpc) { 378 /* 379 * there it is. 380 * increment its count 381 * move it to the head of the chain. 382 */ 383 top->count++; 384 toindex = prevtop->link; 385 prevtop->link = top->link; 386 top->link = *frompcindex; 387 *frompcindex = toindex; 388 goto done; 389 } 390 391 } 392done: 393 profiling--; 394 /* and fall through */ 395out: 396 return; /* normal return restores saved registers */ 397 398overflow: 399 profiling++; /* halt further profiling */ 400# define TOLIMIT "mcount: tos overflow\n" 401 write(2, TOLIMIT, sizeof(TOLIMIT)); 402 goto out; 403} 404 405/* 406 * Control profiling 407 * profiling is what mcount checks to see if 408 * all the data structures are ready. 409 */ 410static void moncontrol(mode) 411 int mode; 412{ 413 if (mode) { 414 /* start */ 415 profil((unsigned short *)(sbuf + sizeof(struct phdr)), 416 ssiz - sizeof(struct phdr), 417 (long)s_lowpc, s_scale); 418 profiling = 0; 419 } else { 420 /* stop */ 421 profil((unsigned short *)0, 0, 0, 0); 422 profiling = 3; 423 } 424} 425