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 
45 extern void monstartup (char *, char *);
46 extern void _mcleanup (void);
47 static void internal_mcount (char *, unsigned short *) __attribute__ ((used));
48 static void moncontrol (int);
49 
50 struct 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 
62 struct tostruct {
63   char *selfpc;
64   long count;
65   unsigned short link;
66 };
67 
68 struct 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.  */
78 static int profiling = 3;
79 static unsigned short *froms;
80 static struct tostruct *tos = NULL;
81 static long tolimit = 0;
82 static char *s_lowpc = NULL;
83 static char *s_highpc = NULL;
84 static size_t s_textsize = 0;
85 
86 static int ssiz;
87 static char *sbuf;
88 static 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 
94 void
monstartup(char * lowpc,char * highpc)95 monstartup (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 
152 void
_mcleanup(void)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__
221 asm(".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.  */
237 asm(".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]  */
299 asm(".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 
308 static void
internal_mcount(char * selfpc,unsigned short * frompcindex)309 internal_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.  */
426 static void
moncontrol(int mode)427 moncontrol (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