PR target/55160
[official-gcc.git] / libgcc / config / gmon-sol2.c
blob7d6149665d0c3fa989f46fb34cba2b6632bddfbb
1 /*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
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.
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.
31 /* Mangled into a form that works on Solaris 2/SPARC by Mark Eichin
32 * for Cygnus Support, July 1992.
34 * Modified to support Solaris 2/x86 by J.W.Hawtin <oolon@ankh.org>, 14/8/96.
36 * It must be used in conjunction with sol2-gc1.S, which is used to start
37 * and stop process monitoring.
40 #include "tconfig.h"
41 #include "tsystem.h"
42 #include <fcntl.h> /* For creat. */
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);
53 struct phdr {
54 char *lpc;
55 char *hpc;
56 int ncnt;
59 #define HISTFRACTION 2
60 #define HISTCOUNTER unsigned short
61 #define HASHFRACTION 1
62 #define ARCDENSITY 2
63 #define MINARCS 50
65 struct tostruct {
66 char *selfpc;
67 long count;
68 unsigned short link;
71 struct rawarc {
72 unsigned long raw_frompc;
73 unsigned long raw_selfpc;
74 long raw_count;
77 #define ROUNDDOWN(x, y) (((x) / (y)) * (y))
78 #define ROUNDUP(x, y) ((((x) + (y) - 1) / (y)) * (y))
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;
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
95 #define MSG "No space for profiling buffer(s)\n"
97 void
98 monstartup (char *lowpc, char *highpc)
100 size_t monsize;
101 char *buffer;
102 size_t o;
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;
119 froms = sbrk (s_textsize / HASHFRACTION);
120 if (froms == (void *) -1) {
121 write (STDERR_FILENO, MSG, sizeof (MSG));
122 froms = NULL;
123 return;
125 tolimit = s_textsize * ARCDENSITY / 100;
126 if (tolimit < MINARCS) {
127 tolimit = MINARCS;
128 } else if (tolimit > 65534) {
129 tolimit = 65534;
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;
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);
155 void
156 _mcleanup (void)
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;
170 moncontrol (0);
172 if ((profdir = getenv ("PROFDIR")) != NULL) {
173 /* If PROFDIR contains a null value, no profiling output is produced. */
174 if (*profdir == '\0') {
175 return;
178 progname = strrchr (___Argv[0], '/');
179 if (progname == NULL)
180 progname = ___Argv[0];
181 else
182 progname++;
184 sprintf (buf, "%s/%ld.%s", profdir, (long) getpid (), progname);
185 proffile = buf;
186 } else {
187 proffile = "gmon.out";
190 fd = creat (proffile, 0666);
191 if (fd < 0) {
192 perror (proffile);
193 return;
195 #ifdef DEBUG
196 fprintf (stderr, "[mcleanup] sbuf %#x ssiz %d\n", sbuf, ssiz);
197 #endif /* DEBUG */
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;
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));
219 close (fd);
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.
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
300 static void
301 #ifdef __i386__
302 internal_mcount (void)
303 #else
304 internal_mcount (char *selfpc, unsigned short *frompcindex)
305 #endif
307 struct tostruct *top;
308 struct tostruct *prevtop;
309 long toindex;
310 static char already_setup;
312 #ifdef __i386__
313 char *selfpc;
314 unsigned short *frompcindex;
316 /* Find the return address for mcount and the return address for mcount's
317 caller. */
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
327 if(!already_setup) {
328 extern char etext[];
330 already_setup = 1;
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__
339 extern char _start[];
340 extern char _init[];
342 monstartup (_start < _init ? _start : _init, etext);
344 #endif
345 atexit (_mcleanup);
347 /* Check that we are profiling and that we aren't recursively invoked. */
348 if (profiling) {
349 goto out;
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;
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;
366 *frompcindex = toindex;
367 top = &tos[toindex];
368 top->selfpc = selfpc;
369 top->count = 1;
370 top->link = 0;
371 goto done;
373 top = &tos[toindex];
374 if (top->selfpc == selfpc) {
375 /* arc at front of chain; usual case. */
376 top->count++;
377 goto done;
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;
391 top = &tos[toindex];
392 top->selfpc = selfpc;
393 top->count = 1;
394 top->link = *frompcindex;
395 *frompcindex = toindex;
396 goto done;
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;
413 done:
414 profiling--;
415 /* ... and fall through. */
416 out:
417 /* Normal return restores saved registers. */
418 return;
420 overflow:
421 /* Halt further profiling. */
422 profiling++;
424 #define TOLIMIT "mcount: tos overflow\n"
425 write (STDERR_FILENO, TOLIMIT, sizeof (TOLIMIT));
426 goto out;
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)
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;