gmon-sol2.c revision 50397
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. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/* Mangled into a form that works on Sparc Solaris 2 by Mark Eichin
35 * for Cygnus Support, July 1992.
36 */
37
38#ifndef lint
39static char sccsid[] = "@(#)gmon.c	5.3 (Berkeley) 5/22/91";
40#endif /* not lint */
41
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <limits.h>
46#include <unistd.h>
47#include <fcntl.h>
48
49#if 0
50#include "sparc/gmon.h"
51#else
52struct phdr {
53  char *lpc;
54  char *hpc;
55  int ncnt;
56};
57#define HISTFRACTION 2
58#define HISTCOUNTER unsigned short
59#define HASHFRACTION 1
60#define ARCDENSITY 2
61#define MINARCS 50
62struct tostruct {
63  char *selfpc;
64  long count;
65  unsigned short link;
66};
67struct rawarc {
68    unsigned long       raw_frompc;
69    unsigned long       raw_selfpc;
70    long                raw_count;
71};
72#define ROUNDDOWN(x,y)  (((x)/(y))*(y))
73#define ROUNDUP(x,y)    ((((x)+(y)-1)/(y))*(y))
74
75#endif
76
77/* extern mcount() asm ("mcount"); */
78/*extern*/ char *minbrk /* asm ("minbrk") */;
79
80    /*
81     *	froms is actually a bunch of unsigned shorts indexing tos
82     */
83static int		profiling = 3;
84static unsigned short	*froms;
85static struct tostruct	*tos = 0;
86static long		tolimit = 0;
87static char		*s_lowpc = 0;
88static char		*s_highpc = 0;
89static unsigned long	s_textsize = 0;
90
91static int	ssiz;
92static char	*sbuf;
93static int	s_scale;
94    /* see profil(2) where this is describe (incorrectly) */
95#define		SCALE_1_TO_1	0x10000L
96
97#define	MSG "No space for profiling buffer(s)\n"
98
99static void moncontrol();
100
101void monstartup(lowpc, highpc)
102    char	*lowpc;
103    char	*highpc;
104{
105    int			monsize;
106    char		*buffer;
107    register int	o;
108
109	/*
110	 *	round lowpc and highpc to multiples of the density we're using
111	 *	so the rest of the scaling (here and in gprof) stays in ints.
112	 */
113    lowpc = (char *)
114	    ROUNDDOWN((unsigned)lowpc, HISTFRACTION*sizeof(HISTCOUNTER));
115    s_lowpc = lowpc;
116    highpc = (char *)
117	    ROUNDUP((unsigned)highpc, HISTFRACTION*sizeof(HISTCOUNTER));
118    s_highpc = highpc;
119    s_textsize = highpc - lowpc;
120    monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr);
121    buffer = sbrk( monsize );
122    if ( buffer == (char *) -1 ) {
123	write( 2 , MSG , sizeof(MSG) );
124	return;
125    }
126    froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION );
127    if ( froms == (unsigned short *) -1 ) {
128	write( 2 , MSG , sizeof(MSG) );
129	froms = 0;
130	return;
131    }
132    tolimit = s_textsize * ARCDENSITY / 100;
133    if ( tolimit < MINARCS ) {
134	tolimit = MINARCS;
135    } else if ( tolimit > 65534 ) {
136	tolimit = 65534;
137    }
138    tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) );
139    if ( tos == (struct tostruct *) -1 ) {
140	write( 2 , MSG , sizeof(MSG) );
141	froms = 0;
142	tos = 0;
143	return;
144    }
145    minbrk = sbrk(0);
146    tos[0].link = 0;
147    sbuf = buffer;
148    ssiz = monsize;
149    ( (struct phdr *) buffer ) -> lpc = lowpc;
150    ( (struct phdr *) buffer ) -> hpc = highpc;
151    ( (struct phdr *) buffer ) -> ncnt = ssiz;
152    monsize -= sizeof(struct phdr);
153    if ( monsize <= 0 )
154	return;
155    o = highpc - lowpc;
156    if( monsize < o )
157#ifndef hp300
158	s_scale = ( (float) monsize / o ) * SCALE_1_TO_1;
159#else /* avoid floating point */
160    {
161	int quot = o / monsize;
162
163	if (quot >= 0x10000)
164		s_scale = 1;
165	else if (quot >= 0x100)
166		s_scale = 0x10000 / quot;
167	else if (o >= 0x800000)
168		s_scale = 0x1000000 / (o / (monsize >> 8));
169	else
170		s_scale = 0x1000000 / ((o << 8) / monsize);
171    }
172#endif
173    else
174	s_scale = SCALE_1_TO_1;
175    moncontrol(1);
176}
177
178void
179_mcleanup()
180{
181    int			fd;
182    int			fromindex;
183    int			endfrom;
184    char		*frompc;
185    int			toindex;
186    struct rawarc	rawarc;
187    char		*profdir;
188    char		*proffile;
189    char		*progname;
190    char		 buf[PATH_MAX];
191    extern char	       **___Argv;
192
193    moncontrol(0);
194
195    if ((profdir = getenv("PROFDIR")) != NULL) {
196	/* If PROFDIR contains a null value, no profiling output is produced */
197	if (*profdir == '\0') {
198	    return;
199	}
200
201	progname=strrchr(___Argv[0], '/');
202	if (progname == NULL)
203	    progname=___Argv[0];
204	else
205	    progname++;
206
207	sprintf(buf, "%s/%ld.%s", profdir, getpid(), progname);
208	proffile = buf;
209    } else {
210	proffile = "gmon.out";
211    }
212
213    fd = creat( proffile, 0666 );
214    if ( fd < 0 ) {
215	perror( proffile );
216	return;
217    }
218#   ifdef DEBUG
219	fprintf( stderr , "[mcleanup] sbuf 0x%x ssiz %d\n" , sbuf , ssiz );
220#   endif DEBUG
221    write( fd , sbuf , ssiz );
222    endfrom = s_textsize / (HASHFRACTION * sizeof(*froms));
223    for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) {
224	if ( froms[fromindex] == 0 ) {
225	    continue;
226	}
227	frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms));
228	for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) {
229#	    ifdef DEBUG
230		fprintf( stderr ,
231			"[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" ,
232			frompc , tos[toindex].selfpc , tos[toindex].count );
233#	    endif DEBUG
234	    rawarc.raw_frompc = (unsigned long) frompc;
235	    rawarc.raw_selfpc = (unsigned long) tos[toindex].selfpc;
236	    rawarc.raw_count = tos[toindex].count;
237	    write( fd , &rawarc , sizeof rawarc );
238	}
239    }
240    close( fd );
241}
242
243/*
244 * The Sparc stack frame is only held together by the frame pointers
245 * in the register windows. According to the SVR4 SPARC ABI
246 * Supplement, Low Level System Information/Operating System
247 * Interface/Software Trap Types, a type 3 trap will flush all of the
248 * register windows to the stack, which will make it possible to walk
249 * the frames and find the return addresses.
250 * 	However, it seems awfully expensive to incur a trap (system
251 * call) for every function call. It turns out that "call" simply puts
252 * the return address in %o7 expecting the "save" in the procedure to
253 * shift it into %i7; this means that before the "save" occurs, %o7
254 * contains the address of the call to mcount, and %i7 still contains
255 * the caller above that. The asm mcount here simply saves those
256 * registers in argument registers and branches to internal_mcount,
257 * simulating a call with arguments.
258 * 	Kludges:
259 * 	1) the branch to internal_mcount is hard coded; it should be
260 * possible to tell asm to use the assembler-name of a symbol.
261 * 	2) in theory, the function calling mcount could have saved %i7
262 * somewhere and reused the register; in practice, I *think* this will
263 * break longjmp (and maybe the debugger) but I'm not certain. (I take
264 * some comfort in the knowledge that it will break the native mcount
265 * as well.)
266 * 	3) if builtin_return_address worked, this could be portable.
267 * However, it would really have to be optimized for arguments of 0
268 * and 1 and do something like what we have here in order to avoid the
269 * trap per function call performance hit.
270 * 	4) the atexit and monsetup calls prevent this from simply
271 * being a leaf routine that doesn't do a "save" (and would thus have
272 * access to %o7 and %i7 directly) but the call to write() at the end
273 * would have also prevented this.
274 *
275 * -- [eichin:19920702.1107EST]
276 */
277
278/* i7 == last ret, -> frompcindex */
279/* o7 == current ret, -> selfpc */
280/* Solaris 2 libraries use _mcount.  */
281asm(".global _mcount; _mcount: mov %i7,%o1; mov %o7,%o0;b,a internal_mcount");
282/* This is for compatibility with old versions of gcc which used mcount.  */
283asm(".global mcount; mcount: mov %i7,%o1; mov %o7,%o0;b,a internal_mcount");
284
285static void internal_mcount(selfpc, frompcindex)
286	register char			*selfpc;
287	register unsigned short		*frompcindex;
288{
289	register struct tostruct	*top;
290	register struct tostruct	*prevtop;
291	register long			toindex;
292	static char already_setup;
293
294	/*
295	 *	find the return address for mcount,
296	 *	and the return address for mcount's caller.
297	 */
298
299	if(!already_setup) {
300          extern etext();
301	  already_setup = 1;
302	  monstartup(0, etext);
303#ifdef USE_ONEXIT
304	  on_exit(_mcleanup, 0);
305#else
306	  atexit(_mcleanup);
307#endif
308	}
309	/*
310	 *	check that we are profiling
311	 *	and that we aren't recursively invoked.
312	 */
313	if (profiling) {
314		goto out;
315	}
316	profiling++;
317	/*
318	 *	check that frompcindex is a reasonable pc value.
319	 *	for example:	signal catchers get called from the stack,
320	 *			not from text space.  too bad.
321	 */
322	frompcindex = (unsigned short *)((long)frompcindex - (long)s_lowpc);
323	if ((unsigned long)frompcindex > s_textsize) {
324		goto done;
325	}
326	frompcindex =
327	    &froms[((long)frompcindex) / (HASHFRACTION * sizeof(*froms))];
328	toindex = *frompcindex;
329	if (toindex == 0) {
330		/*
331		 *	first time traversing this arc
332		 */
333		toindex = ++tos[0].link;
334		if (toindex >= tolimit) {
335			goto overflow;
336		}
337		*frompcindex = toindex;
338		top = &tos[toindex];
339		top->selfpc = selfpc;
340		top->count = 1;
341		top->link = 0;
342		goto done;
343	}
344	top = &tos[toindex];
345	if (top->selfpc == selfpc) {
346		/*
347		 *	arc at front of chain; usual case.
348		 */
349		top->count++;
350		goto done;
351	}
352	/*
353	 *	have to go looking down chain for it.
354	 *	top points to what we are looking at,
355	 *	prevtop points to previous top.
356	 *	we know it is not at the head of the chain.
357	 */
358	for (; /* goto done */; ) {
359		if (top->link == 0) {
360			/*
361			 *	top is end of the chain and none of the chain
362			 *	had top->selfpc == selfpc.
363			 *	so we allocate a new tostruct
364			 *	and link it to the head of the chain.
365			 */
366			toindex = ++tos[0].link;
367			if (toindex >= tolimit) {
368				goto overflow;
369			}
370			top = &tos[toindex];
371			top->selfpc = selfpc;
372			top->count = 1;
373			top->link = *frompcindex;
374			*frompcindex = toindex;
375			goto done;
376		}
377		/*
378		 *	otherwise, check the next arc on the chain.
379		 */
380		prevtop = top;
381		top = &tos[top->link];
382		if (top->selfpc == selfpc) {
383			/*
384			 *	there it is.
385			 *	increment its count
386			 *	move it to the head of the chain.
387			 */
388			top->count++;
389			toindex = prevtop->link;
390			prevtop->link = top->link;
391			top->link = *frompcindex;
392			*frompcindex = toindex;
393			goto done;
394		}
395
396	}
397done:
398	profiling--;
399	/* and fall through */
400out:
401	return;		/* normal return restores saved registers */
402
403overflow:
404	profiling++; /* halt further profiling */
405#   define	TOLIMIT	"mcount: tos overflow\n"
406	write(2, TOLIMIT, sizeof(TOLIMIT));
407	goto out;
408}
409
410/*
411 * Control profiling
412 *	profiling is what mcount checks to see if
413 *	all the data structures are ready.
414 */
415static void moncontrol(mode)
416    int mode;
417{
418    if (mode) {
419	/* start */
420	profil((unsigned short *)(sbuf + sizeof(struct phdr)),
421	       ssiz - sizeof(struct phdr),
422	       (int)s_lowpc, s_scale);
423	profiling = 0;
424    } else {
425	/* stop */
426	profil((unsigned short *)0, 0, 0, 0);
427	profiling = 3;
428    }
429}
430