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