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