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