1162485Sjulian/*- 2162485Sjulian * Copyright (c) 1991 The Regents of the University of California. 3162485Sjulian * All rights reserved. 4162485Sjulian * 5162485Sjulian * Redistribution and use in source and binary forms, with or without 6162485Sjulian * modification, are permitted provided that the following conditions 7162485Sjulian * are met: 8162485Sjulian * 1. Redistributions of source code must retain the above copyright 9162485Sjulian * notice, this list of conditions and the following disclaimer. 10162485Sjulian * 2. Redistributions in binary form must reproduce the above copyright 11162485Sjulian * notice, this list of conditions and the following disclaimer in the 12162535Sjulian * documentation and/or other materials provided with the distribution. 13162485Sjulian * 3. [rescinded 22 July 1999] 14162485Sjulian * 4. Neither the name of the University nor the names of its contributors 15162485Sjulian * may be used to endorse or promote products derived from this software 16162485Sjulian * without specific prior written permission. 17162485Sjulian * 18162485Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19162485Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20162485Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21162485Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22162485Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23162485Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24162485Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25162485Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26162485Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27162485Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28162485Sjulian * SUCH DAMAGE. 29162485Sjulian */ 30162485Sjulian 31162485Sjulian/* 32162485Sjulian * This is a modified gmon.c by J.W.Hawtin <oolon@ankh.org>, 33162485Sjulian * 14/8/96 based on the original gmon.c in GCC and the hacked version 34162485Sjulian * solaris 2 sparc version (config/sparc/gmon-sol.c) by Mark Eichin. To do 35162485Sjulian * process profiling on solaris 2.X X86 36162485Sjulian * 37162485Sjulian * It must be used in conjunction with sol2-gc1.asm, which is used to start 38162485Sjulian * and stop process monitoring. 39162485Sjulian * 40162485Sjulian * Differences. 41162485Sjulian * 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 ( 68#ifdef __x86_64__ 69 char *, unsigned short * 70#else 71 void 72#endif 73 ); 74 75 76struct phdr { 77 char *lpc; 78 char *hpc; 79 int ncnt; 80}; 81 82 83#define HISTFRACTION 2 84#define HISTCOUNTER unsigned short 85#define HASHFRACTION 1 86#define ARCDENSITY 2 87#define MINARCS 50 88#define BASEADDRESS 0x8000000 /* On Solaris 2 X86 all executables start here 89 and not at 0 */ 90 91struct tostruct { 92 char *selfpc; 93 long count; 94 unsigned short link; 95}; 96 97struct rawarc { 98 unsigned long raw_frompc; 99 unsigned long raw_selfpc; 100 long raw_count; 101}; 102#define ROUNDDOWN(x,y) (((x)/(y))*(y)) 103#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y)) 104 105/* char *minbrk; */ 106 107typedef __SIZE_TYPE__ size_t; 108typedef __PTRDIFF_TYPE__ intptr_t; 109 110 /* 111 * froms is actually a bunch of unsigned shorts indexing tos 112 */ 113static int profiling = 3; 114static unsigned short *froms; 115static struct tostruct *tos = 0; 116static long tolimit = 0; 117static char *s_lowpc = 0; 118static char *s_highpc = 0; 119static size_t s_textsize = 0; 120 121static int ssiz; 122static char *sbuf; 123static int s_scale; 124 /* see profil(2) where this is describe (incorrectly) */ 125#define SCALE_1_TO_1 0x10000L 126 127#define MSG "No space for profiling buffer(s)\n" 128 129extern int errno; 130 131extern void *sbrk (intptr_t); 132 133void 134monstartup(char *lowpc, char *highpc) 135{ 136 size_t monsize; 137 char *buffer; 138 register size_t o; 139 140 /* 141 * round lowpc and highpc to multiples of the density we're using 142 * so the rest of the scaling (here and in gprof) stays in ints. 143 */ 144 lowpc = (char *) 145 ROUNDDOWN((size_t)lowpc, HISTFRACTION*sizeof(HISTCOUNTER)); 146 s_lowpc = lowpc; 147 highpc = (char *) 148 ROUNDUP((size_t)highpc, HISTFRACTION*sizeof(HISTCOUNTER)); 149 s_highpc = highpc; 150 s_textsize = highpc - lowpc; 151 monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr); 152 buffer = (char *) sbrk( monsize ); 153 if ( buffer == (char *) -1 ) { 154 write( 2 , MSG , sizeof(MSG) ); 155 return; 156 } 157 froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION ); 158 if ( froms == (unsigned short *) -1 ) { 159 write( 2 , MSG , sizeof(MSG) ); 160 froms = 0; 161 return; 162 } 163 tolimit = s_textsize * ARCDENSITY / 100; 164 if ( tolimit < MINARCS ) { 165 tolimit = MINARCS; 166 } else if ( tolimit > 65534 ) { 167 tolimit = 65534; 168 } 169 tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) ); 170 if ( tos == (struct tostruct *) -1 ) { 171 write( 2 , MSG , sizeof(MSG) ); 172 froms = 0; 173 tos = 0; 174 return; 175 } 176/* minbrk = (char *) sbrk(0);*/ 177 tos[0].link = 0; 178 sbuf = buffer; 179 ssiz = monsize; 180 ( (struct phdr *) buffer ) -> lpc = lowpc; 181 ( (struct phdr *) buffer ) -> hpc = highpc; 182 ( (struct phdr *) buffer ) -> ncnt = ssiz; 183 monsize -= sizeof(struct phdr); 184 if ( monsize <= 0 ) 185 return; 186 o = highpc - lowpc; 187 if( monsize < o ) 188#ifndef hp300 189 s_scale = ( (float) monsize / o ) * SCALE_1_TO_1; 190#else /* avoid floating point */ 191 { 192 int quot = o / monsize; 193 194 if (quot >= 0x10000) 195 s_scale = 1; 196 else if (quot >= 0x100) 197 s_scale = 0x10000 / quot; 198 else if (o >= 0x800000) 199 s_scale = 0x1000000 / (o / (monsize >> 8)); 200 else 201 s_scale = 0x1000000 / ((o << 8) / monsize); 202 } 203#endif 204 else 205 s_scale = SCALE_1_TO_1; 206 moncontrol(1); 207} 208 209void 210_mcleanup (void) 211{ 212 int fd; 213 int fromindex; 214 int endfrom; 215 char *frompc; 216 int toindex; 217 struct rawarc rawarc; 218 219 moncontrol(0); 220 fd = creat( "gmon.out" , 0666 ); 221 if ( fd < 0 ) { 222 perror( "mcount: gmon.out" ); 223 return; 224 } 225# ifdef DEBUG 226 fprintf( stderr , "[mcleanup] sbuf 0x%x ssiz %d\n" , sbuf , ssiz ); 227# endif /* DEBUG */ 228 229 write( fd , sbuf , ssiz ); 230 endfrom = s_textsize / (HASHFRACTION * sizeof(*froms)); 231 for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) { 232 if ( froms[fromindex] == 0 ) { 233 continue; 234 } 235 frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms)); 236 for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) { 237# ifdef DEBUG 238 fprintf( stderr , 239 "[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" , 240 frompc , tos[toindex].selfpc , tos[toindex].count ); 241# endif /* DEBUG */ 242 rawarc.raw_frompc = (unsigned long) frompc; 243 rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc; 244 rawarc.raw_count = tos[toindex].count; 245 write( fd , &rawarc , sizeof rawarc ); 246 } 247 } 248 close( fd ); 249} 250 251#ifdef __x86_64__ 252/* See GLIBC for additional information about this technique. */ 253asm(".globl _mcount\n" 254 "\t.type\t_mcount, @function\n" 255 "_mcount:\n" 256 /* The compiler calls _mcount after the prologue, and does not 257 save any of the registers. Therefore we must preserve all 258 seven registers which may contain function arguments. */ 259 "\tsubq\t$0x38,%rsp\n" 260 "\tmovq\t%rax,(%rsp)\n" 261 "\tmovq\t%rcx,0x08(%rsp)\n" 262 "\tmovq\t%rdx,0x10(%rsp)\n" 263 "\tmovq\t%rsi,0x18(%rsp)\n" 264 "\tmovq\t%rdi,0x20(%rsp)\n" 265 "\tmovq\t%r8,0x28(%rsp)\n" 266 "\tmovq\t%r9,0x30(%rsp)\n" 267 /* Get SELFPC (pushed by the call to this function) and 268 FROMPCINDEX (via the frame pointer. */ 269 "\tmovq\t0x38(%rsp),%rdi\n" 270 "\tmovq\t0x8(%rbp),%rsi\n" 271 "\tcall\tinternal_mcount\n" 272 /* Restore the saved registers. */ 273 "\tmovq\t0x30(%rsp),%r9\n" 274 "\tmovq\t0x28(%rsp),%r8\n" 275 "\tmovq\t0x20(%rsp),%rdi\n" 276 "\tmovq\t0x18(%rsp),%rsi\n" 277 "\tmovq\t0x10(%rsp),%rdx\n" 278 "\tmovq\t0x08(%rsp),%rcx\n" 279 "\tmovq\t(%rsp),%rax\n" 280 "\taddq\t$0x38,%rsp\n" 281 "\tretq\n" 282 ); 283#else 284/* Solaris 2 libraries use _mcount. */ 285asm(".globl _mcount; _mcount: jmp internal_mcount"); 286/* This is for compatibility with old versions of gcc which used mcount. */ 287asm(".globl mcount; mcount: jmp internal_mcount"); 288#endif 289 290void 291internal_mcount ( 292#ifdef __x86_64__ 293 char *selfpc, 294 unsigned short *frompcindex 295#else 296 void 297#endif 298 ) 299{ 300#ifndef __x86_64__ 301 register char *selfpc; 302 register unsigned short *frompcindex; 303#endif 304 register struct tostruct *top; 305 register struct tostruct *prevtop; 306 register long toindex; 307 static char already_setup; 308 309#ifndef __x86_64__ 310 /* 311 * find the return address for mcount, 312 * and the return address for mcount's caller. 313 */ 314 315 /* selfpc = pc pushed by mcount call. 316 This identifies the function that was just entered. */ 317 selfpc = (void *) __builtin_return_address (0); 318 /* frompcindex = pc in preceding frame. 319 This identifies the caller of the function just entered. */ 320 frompcindex = (void *) __builtin_return_address (1); 321#endif 322 323 if(!already_setup) { 324 extern char etext[]; 325 already_setup = 1; 326#ifdef __x86_64__ 327 monstartup(0, etext); 328#else 329 monstartup((char*)0x08040000, etext); 330#endif 331#ifdef USE_ONEXIT 332 on_exit(_mcleanup, 0); 333#else 334 atexit(_mcleanup); 335#endif 336 } 337 /* 338 * check that we are profiling 339 * and that we aren't recursively invoked. 340 */ 341 if (profiling) { 342 goto out; 343 } 344 profiling++; 345 /* 346 * check that frompcindex is a reasonable pc value. 347 * for example: signal catchers get called from the stack, 348 * not from text space. too bad. 349 */ 350 frompcindex = (unsigned short *)((long)frompcindex - (long)s_lowpc); 351 if ((unsigned long)frompcindex > s_textsize) { 352 goto done; 353 } 354 frompcindex = 355 &froms[((long)frompcindex) / (HASHFRACTION * sizeof(*froms))]; 356 toindex = *frompcindex; 357 if (toindex == 0) { 358 /* 359 * first time traversing this arc 360 */ 361 toindex = ++tos[0].link; 362 if (toindex >= tolimit) { 363 goto overflow; 364 } 365 *frompcindex = toindex; 366 top = &tos[toindex]; 367 top->selfpc = selfpc; 368 top->count = 1; 369 top->link = 0; 370 goto done; 371 } 372 top = &tos[toindex]; 373 if (top->selfpc == selfpc) { 374 /* 375 * arc at front of chain; usual case. 376 */ 377 top->count++; 378 goto done; 379 } 380 /* 381 * have to go looking down chain for it. 382 * top points to what we are looking at, 383 * prevtop points to previous top. 384 * we know it is not at the head of the chain. 385 */ 386 for (; /* goto done */; ) { 387 if (top->link == 0) { 388 /* 389 * top is end of the chain and none of the chain 390 * had top->selfpc == selfpc. 391 * so we allocate a new tostruct 392 * and link it to the head of the chain. 393 */ 394 toindex = ++tos[0].link; 395 if (toindex >= tolimit) { 396 goto overflow; 397 } 398 top = &tos[toindex]; 399 top->selfpc = selfpc; 400 top->count = 1; 401 top->link = *frompcindex; 402 *frompcindex = toindex; 403 goto done; 404 } 405 /* 406 * otherwise, check the next arc on the chain. 407 */ 408 prevtop = top; 409 top = &tos[top->link]; 410 if (top->selfpc == selfpc) { 411 /* 412 * there it is. 413 * increment its count 414 * move it to the head of the chain. 415 */ 416 top->count++; 417 toindex = prevtop->link; 418 prevtop->link = top->link; 419 top->link = *frompcindex; 420 *frompcindex = toindex; 421 goto done; 422 } 423 424 } 425done: 426 profiling--; 427 /* and fall through */ 428out: 429 return; /* normal return restores saved registers */ 430 431overflow: 432 profiling++; /* halt further profiling */ 433# define TOLIMIT "mcount: tos overflow\n" 434 write(2, TOLIMIT, sizeof(TOLIMIT)); 435 goto out; 436} 437 438/* 439 * Control profiling 440 * profiling is what mcount checks to see if 441 * all the data structures are ready. 442 */ 443static void 444moncontrol(int mode) 445{ 446 if (mode) 447 { 448 /* start */ 449 profil((unsigned short *)(sbuf + sizeof(struct phdr)), 450 ssiz - sizeof(struct phdr), 451 (size_t)s_lowpc, s_scale); 452 453 profiling = 0; 454 } else { 455 /* stop */ 456 profil((unsigned short *)0, 0, 0, 0); 457 profiling = 3; 458 } 459} 460