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