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