rlm@1
|
1 // VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
rlm@1
|
2 // Copyright (C) 1999-2003 Forgotten
|
rlm@1
|
3 // Copyright (C) 2004 Forgotten and the VBA development team
|
rlm@1
|
4
|
rlm@1
|
5 // This program is free software; you can redistribute it and/or modify
|
rlm@1
|
6 // it under the terms of the GNU General Public License as published by
|
rlm@1
|
7 // the Free Software Foundation; either version 2, or(at your option)
|
rlm@1
|
8 // any later version.
|
rlm@1
|
9 //
|
rlm@1
|
10 // This program is distributed in the hope that it will be useful,
|
rlm@1
|
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
rlm@1
|
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
rlm@1
|
13 // GNU General Public License for more details.
|
rlm@1
|
14 //
|
rlm@1
|
15 // You should have received a copy of the GNU General Public License
|
rlm@1
|
16 // along with this program; if not, write to the Free Software Foundation,
|
rlm@1
|
17 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
rlm@1
|
18
|
rlm@1
|
19 // adapted from gmon.c
|
rlm@1
|
20 /*-
|
rlm@1
|
21 * Copyright (c) 1991, 1998 The Regents of the University of California.
|
rlm@1
|
22 * All rights reserved.
|
rlm@1
|
23 *
|
rlm@1
|
24 * Redistribution and use in source and binary forms, with or without
|
rlm@1
|
25 * modification, are permitted provided that the following conditions
|
rlm@1
|
26 * are met:
|
rlm@1
|
27 * 1. Redistributions of source code must retain the above copyright
|
rlm@1
|
28 * notice, this list of conditions and the following disclaimer.
|
rlm@1
|
29 * 2. Redistributions in binary form must reproduce the above copyright
|
rlm@1
|
30 * notice, this list of conditions and the following disclaimer in the
|
rlm@1
|
31 * documentation and/or other materials provided with the distribution.
|
rlm@1
|
32 * 3. [rescinded 22 July 1999]
|
rlm@1
|
33 * 4. Neither the name of the University nor the names of its contributors
|
rlm@1
|
34 * may be used to endorse or promote products derived from this software
|
rlm@1
|
35 * without specific prior written permission.
|
rlm@1
|
36 *
|
rlm@1
|
37 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
rlm@1
|
38 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
rlm@1
|
39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
rlm@1
|
40 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
rlm@1
|
41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
rlm@1
|
42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
rlm@1
|
43 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
rlm@1
|
44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
rlm@1
|
45 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
rlm@1
|
46 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
rlm@1
|
47 * SUCH DAMAGE.
|
rlm@1
|
48 */
|
rlm@1
|
49
|
rlm@1
|
50 #include <stdlib.h>
|
rlm@1
|
51 #include <stdio.h>
|
rlm@1
|
52 #include <memory.h>
|
rlm@1
|
53
|
rlm@1
|
54 #include "gmon.h"
|
rlm@1
|
55 #include "gmon_out.h"
|
rlm@1
|
56
|
rlm@1
|
57 #include "../common/System.h"
|
rlm@1
|
58 #include "../gba/GBA.h"
|
rlm@1
|
59 #include "../gba/GBAGlobals.h"
|
rlm@1
|
60 #include "../NLS.h"
|
rlm@1
|
61
|
rlm@1
|
62 /*
|
rlm@1
|
63 * froms is actually a bunch of unsigned shorts indexing tos
|
rlm@1
|
64 */
|
rlm@1
|
65 static int profiling = 3;
|
rlm@1
|
66 static unsigned short *froms;
|
rlm@1
|
67 static struct tostruct *tos = 0;
|
rlm@1
|
68 static long tolimit = 0;
|
rlm@1
|
69 static u32 s_lowpc = 0;
|
rlm@1
|
70 static u32 s_highpc = 0;
|
rlm@1
|
71 static unsigned long s_textsize = 0;
|
rlm@1
|
72
|
rlm@1
|
73 static int ssiz;
|
rlm@1
|
74 static char *sbuf;
|
rlm@1
|
75 static int s_scale;
|
rlm@1
|
76
|
rlm@1
|
77 static int hz = 0;
|
rlm@1
|
78 static int hist_num_bins = 0;
|
rlm@1
|
79 static char hist_dimension[16] = "seconds";
|
rlm@1
|
80 static char hist_dimension_abbrev = 's';
|
rlm@1
|
81
|
rlm@1
|
82 /* see profil(2) where this is describe (incorrectly) */
|
rlm@1
|
83 #define SCALE_1_TO_1 0x10000L
|
rlm@1
|
84
|
rlm@1
|
85 void profPut32(char *b, u32 v)
|
rlm@1
|
86 {
|
rlm@1
|
87 b[0] = v & 255;
|
rlm@1
|
88 b[1] = (v >> 8) & 255;
|
rlm@1
|
89 b[2] = (v >> 16) & 255;
|
rlm@1
|
90 b[3] = (v >> 24) & 255;
|
rlm@1
|
91 }
|
rlm@1
|
92
|
rlm@1
|
93 void profPut16(char *b, u16 v)
|
rlm@1
|
94 {
|
rlm@1
|
95 b[0] = v & 255;
|
rlm@1
|
96 b[1] = (v >> 8) & 255;
|
rlm@1
|
97 }
|
rlm@1
|
98
|
rlm@1
|
99 int profWrite8(FILE *f, u8 b)
|
rlm@1
|
100 {
|
rlm@1
|
101 if(fwrite(&b, 1, 1, f) != 1)
|
rlm@1
|
102 return 1;
|
rlm@1
|
103 return 0;
|
rlm@1
|
104 }
|
rlm@1
|
105
|
rlm@1
|
106 int profWrite32(FILE *f, u32 v)
|
rlm@1
|
107 {
|
rlm@1
|
108 char buf[4];
|
rlm@1
|
109
|
rlm@1
|
110 profPut32(buf, v);
|
rlm@1
|
111 if(fwrite(buf, 1, 4, f) != 4)
|
rlm@1
|
112 return 1;
|
rlm@1
|
113 return 0;
|
rlm@1
|
114 }
|
rlm@1
|
115
|
rlm@1
|
116 int profWrite(FILE *f, char *buf, unsigned int n)
|
rlm@1
|
117 {
|
rlm@1
|
118 if(fwrite(buf, 1, n, f) != n)
|
rlm@1
|
119 return 1;
|
rlm@1
|
120 return 0;
|
rlm@1
|
121 }
|
rlm@1
|
122
|
rlm@1
|
123 /* Control profiling;
|
rlm@1
|
124 profiling is what mcount checks to see if
|
rlm@1
|
125 all the data structures are ready. */
|
rlm@1
|
126
|
rlm@1
|
127 void profControl(int mode)
|
rlm@1
|
128 {
|
rlm@1
|
129 if (mode) {
|
rlm@1
|
130 /* start */
|
rlm@1
|
131 #ifdef PROFILING
|
rlm@1
|
132 cpuProfil(sbuf, ssiz, (u32)s_lowpc, s_scale);
|
rlm@1
|
133 #endif
|
rlm@1
|
134 profiling = 0;
|
rlm@1
|
135 } else {
|
rlm@1
|
136 /* stop */
|
rlm@1
|
137 #ifdef PROFILING
|
rlm@1
|
138 cpuProfil(NULL, 0, 0, 0);
|
rlm@1
|
139 #endif
|
rlm@1
|
140 profiling = 3;
|
rlm@1
|
141 }
|
rlm@1
|
142 }
|
rlm@1
|
143
|
rlm@1
|
144
|
rlm@1
|
145 #define MSG N_("No space for profiling buffer(s)\n")
|
rlm@1
|
146
|
rlm@1
|
147 void profStartup(u32 lowpc, u32 highpc)
|
rlm@1
|
148 {
|
rlm@1
|
149 int monsize;
|
rlm@1
|
150 char *buffer;
|
rlm@1
|
151 int o;
|
rlm@1
|
152
|
rlm@1
|
153 /*
|
rlm@1
|
154 * round lowpc and highpc to multiples of the density we're using
|
rlm@1
|
155 * so the rest of the scaling (here and in gprof) stays in ints.
|
rlm@1
|
156 */
|
rlm@1
|
157 lowpc = ROUNDDOWN(lowpc, HISTFRACTION*sizeof(HISTCOUNTER));
|
rlm@1
|
158 s_lowpc = lowpc;
|
rlm@1
|
159 highpc = ROUNDUP(highpc, HISTFRACTION*sizeof(HISTCOUNTER));
|
rlm@1
|
160 s_highpc = highpc;
|
rlm@1
|
161 s_textsize = highpc - lowpc;
|
rlm@1
|
162 monsize = (s_textsize / HISTFRACTION);
|
rlm@1
|
163 buffer = (char *)calloc(1, 2*monsize );
|
rlm@1
|
164 if ( buffer == NULL ) {
|
rlm@1
|
165 systemMessage(0, MSG);
|
rlm@1
|
166 return;
|
rlm@1
|
167 }
|
rlm@1
|
168 froms = (unsigned short *) calloc(1, 4*s_textsize / HASHFRACTION );
|
rlm@1
|
169 if ( froms == NULL ) {
|
rlm@1
|
170 systemMessage(0, MSG);
|
rlm@1
|
171 free(buffer);
|
rlm@1
|
172 buffer = NULL;
|
rlm@1
|
173 return;
|
rlm@1
|
174 }
|
rlm@1
|
175 tolimit = s_textsize * ARCDENSITY / 100;
|
rlm@1
|
176 if ( tolimit < MINARCS ) {
|
rlm@1
|
177 tolimit = MINARCS;
|
rlm@1
|
178 } else if ( tolimit > 65534 ) {
|
rlm@1
|
179 tolimit = 65534;
|
rlm@1
|
180 }
|
rlm@1
|
181 tos = (struct tostruct *) calloc(1, tolimit * sizeof( struct tostruct ) );
|
rlm@1
|
182 if ( tos == NULL ) {
|
rlm@1
|
183 systemMessage(0, MSG);
|
rlm@1
|
184
|
rlm@1
|
185 free(buffer);
|
rlm@1
|
186 buffer = NULL;
|
rlm@1
|
187
|
rlm@1
|
188 free(froms);
|
rlm@1
|
189 froms = NULL;
|
rlm@1
|
190
|
rlm@1
|
191 return;
|
rlm@1
|
192 }
|
rlm@1
|
193 tos[0].link = 0;
|
rlm@1
|
194 sbuf = buffer;
|
rlm@1
|
195 ssiz = monsize;
|
rlm@1
|
196 if ( monsize <= 0 )
|
rlm@1
|
197 return;
|
rlm@1
|
198 o = highpc - lowpc;
|
rlm@1
|
199 if( monsize < o )
|
rlm@1
|
200 s_scale = (int)(( (float) monsize / o ) * SCALE_1_TO_1);
|
rlm@1
|
201 else
|
rlm@1
|
202 s_scale = SCALE_1_TO_1;
|
rlm@1
|
203 profControl(1);
|
rlm@1
|
204 }
|
rlm@1
|
205
|
rlm@1
|
206 void profCleanup()
|
rlm@1
|
207 {
|
rlm@1
|
208 FILE *fd;
|
rlm@1
|
209 int fromindex;
|
rlm@1
|
210 int endfrom;
|
rlm@1
|
211 u32 frompc;
|
rlm@1
|
212 int toindex;
|
rlm@1
|
213 struct gmon_hdr ghdr;
|
rlm@1
|
214
|
rlm@1
|
215 profControl(0);
|
rlm@1
|
216 fd = fopen( "gmon.out" , "wb" );
|
rlm@1
|
217 if ( fd == NULL ) {
|
rlm@1
|
218 systemMessage( 0, "mcount: gmon.out" );
|
rlm@1
|
219 return;
|
rlm@1
|
220 }
|
rlm@1
|
221
|
rlm@1
|
222 memcpy(&ghdr.cookie[0], GMON_MAGIC, 4);
|
rlm@1
|
223 profPut32((char *)ghdr.version, GMON_VERSION);
|
rlm@1
|
224
|
rlm@1
|
225 if(fwrite(&ghdr, sizeof(ghdr), 1, fd) != 1) {
|
rlm@1
|
226 systemMessage(0, "mcount: gmon.out header");
|
rlm@1
|
227 fclose(fd);
|
rlm@1
|
228 return;
|
rlm@1
|
229 }
|
rlm@1
|
230
|
rlm@1
|
231 if(hz == 0)
|
rlm@1
|
232 hz = 100;
|
rlm@1
|
233
|
rlm@1
|
234 hist_num_bins = ssiz;
|
rlm@1
|
235
|
rlm@1
|
236 if(profWrite8(fd, GMON_TAG_TIME_HIST) ||
|
rlm@1
|
237 profWrite32(fd, (u32)s_lowpc) ||
|
rlm@1
|
238 profWrite32(fd, (u32)s_highpc) ||
|
rlm@1
|
239 profWrite32(fd, hist_num_bins) ||
|
rlm@1
|
240 profWrite32(fd, hz) ||
|
rlm@1
|
241 profWrite(fd, hist_dimension, 15) ||
|
rlm@1
|
242 profWrite(fd, &hist_dimension_abbrev, 1)) {
|
rlm@1
|
243 systemMessage(0, "mcount: gmon.out hist");
|
rlm@1
|
244 fclose(fd);
|
rlm@1
|
245 return;
|
rlm@1
|
246 }
|
rlm@1
|
247 u16 *hist_sample = (u16 *)sbuf;
|
rlm@1
|
248
|
rlm@1
|
249 u16 count;
|
rlm@1
|
250 int i;
|
rlm@1
|
251
|
rlm@1
|
252 for(i = 0; i < hist_num_bins; ++i) {
|
rlm@1
|
253 profPut16((char *)&count, hist_sample[i]);
|
rlm@1
|
254
|
rlm@1
|
255 if(fwrite(&count, sizeof(count), 1, fd) != 1) {
|
rlm@1
|
256 systemMessage(0, "mcount: gmon.out sample");
|
rlm@1
|
257 fclose(fd);
|
rlm@1
|
258 return;
|
rlm@1
|
259 }
|
rlm@1
|
260 }
|
rlm@1
|
261
|
rlm@1
|
262 endfrom = s_textsize / (HASHFRACTION * sizeof(*froms));
|
rlm@1
|
263 for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) {
|
rlm@1
|
264 if ( froms[fromindex] == 0 ) {
|
rlm@1
|
265 continue;
|
rlm@1
|
266 }
|
rlm@1
|
267 frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms));
|
rlm@1
|
268 for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) {
|
rlm@1
|
269 if(profWrite8(fd, GMON_TAG_CG_ARC) ||
|
rlm@1
|
270 profWrite32(fd, (u32)frompc) ||
|
rlm@1
|
271 profWrite32(fd, (u32)tos[toindex].selfpc) ||
|
rlm@1
|
272 profWrite32(fd, tos[toindex].count)) {
|
rlm@1
|
273 systemMessage(0, "mcount: arc");
|
rlm@1
|
274 fclose(fd);
|
rlm@1
|
275 return;
|
rlm@1
|
276 }
|
rlm@1
|
277 }
|
rlm@1
|
278 }
|
rlm@1
|
279 fclose(fd);
|
rlm@1
|
280 }
|
rlm@1
|
281
|
rlm@1
|
282 void profCount()
|
rlm@1
|
283 {
|
rlm@1
|
284 register u32 selfpc;
|
rlm@1
|
285 register unsigned short *frompcindex;
|
rlm@1
|
286 register struct tostruct *top;
|
rlm@1
|
287 register struct tostruct *prevtop;
|
rlm@1
|
288 register long toindex;
|
rlm@1
|
289
|
rlm@1
|
290 /*
|
rlm@1
|
291 * find the return address for mcount,
|
rlm@1
|
292 * and the return address for mcount's caller.
|
rlm@1
|
293 */
|
rlm@1
|
294
|
rlm@1
|
295 /* selfpc = pc pushed by mcount call.
|
rlm@1
|
296 This identifies the function that was just entered. */
|
rlm@1
|
297 selfpc = (u32) reg[14].I;
|
rlm@1
|
298 /* frompcindex = pc in preceding frame.
|
rlm@1
|
299 This identifies the caller of the function just entered. */
|
rlm@1
|
300 frompcindex = (unsigned short *) reg[12].I;
|
rlm@1
|
301 /*
|
rlm@1
|
302 * check that we are profiling
|
rlm@1
|
303 * and that we aren't recursively invoked.
|
rlm@1
|
304 */
|
rlm@1
|
305 if (profiling) {
|
rlm@1
|
306 goto out;
|
rlm@1
|
307 }
|
rlm@1
|
308 profiling++;
|
rlm@1
|
309 /*
|
rlm@1
|
310 * check that frompcindex is a reasonable pc value.
|
rlm@1
|
311 * for example: signal catchers get called from the stack,
|
rlm@1
|
312 * not from text space. too bad.
|
rlm@1
|
313 */
|
rlm@1
|
314 frompcindex = (unsigned short *) ((long) frompcindex - (long) s_lowpc);
|
rlm@1
|
315 if ((unsigned long) frompcindex > s_textsize) {
|
rlm@1
|
316 goto done;
|
rlm@1
|
317 }
|
rlm@1
|
318 frompcindex =
|
rlm@1
|
319 &froms[((long) frompcindex) / (HASHFRACTION * sizeof(*froms))];
|
rlm@1
|
320 toindex = *frompcindex;
|
rlm@1
|
321 if (toindex == 0) {
|
rlm@1
|
322 /*
|
rlm@1
|
323 * first time traversing this arc
|
rlm@1
|
324 */
|
rlm@1
|
325 toindex = ++tos[0].link;
|
rlm@1
|
326 if (toindex >= tolimit) {
|
rlm@1
|
327 goto overflow;
|
rlm@1
|
328 }
|
rlm@1
|
329 *frompcindex = (unsigned short)toindex;
|
rlm@1
|
330 top = &tos[toindex];
|
rlm@1
|
331 top->selfpc = selfpc;
|
rlm@1
|
332 top->count = 1;
|
rlm@1
|
333 top->link = 0;
|
rlm@1
|
334 goto done;
|
rlm@1
|
335 }
|
rlm@1
|
336 top = &tos[toindex];
|
rlm@1
|
337 if (top->selfpc == selfpc) {
|
rlm@1
|
338 /*
|
rlm@1
|
339 * arc at front of chain; usual case.
|
rlm@1
|
340 */
|
rlm@1
|
341 top->count++;
|
rlm@1
|
342 goto done;
|
rlm@1
|
343 }
|
rlm@1
|
344 /*
|
rlm@1
|
345 * have to go looking down chain for it.
|
rlm@1
|
346 * top points to what we are looking at,
|
rlm@1
|
347 * prevtop points to previous top.
|
rlm@1
|
348 * we know it is not at the head of the chain.
|
rlm@1
|
349 */
|
rlm@1
|
350 for (; /* goto done */; ) {
|
rlm@1
|
351 if (top->link == 0) {
|
rlm@1
|
352 /*
|
rlm@1
|
353 * top is end of the chain and none of the chain
|
rlm@1
|
354 * had top->selfpc == selfpc.
|
rlm@1
|
355 * so we allocate a new tostruct
|
rlm@1
|
356 * and link it to the head of the chain.
|
rlm@1
|
357 */
|
rlm@1
|
358 toindex = ++tos[0].link;
|
rlm@1
|
359 if (toindex >= tolimit) {
|
rlm@1
|
360 goto overflow;
|
rlm@1
|
361 }
|
rlm@1
|
362 top = &tos[toindex];
|
rlm@1
|
363 top->selfpc = selfpc;
|
rlm@1
|
364 top->count = 1;
|
rlm@1
|
365 top->link = *frompcindex;
|
rlm@1
|
366 *frompcindex = (unsigned short)toindex;
|
rlm@1
|
367 goto done;
|
rlm@1
|
368 }
|
rlm@1
|
369 /*
|
rlm@1
|
370 * otherwise, check the next arc on the chain.
|
rlm@1
|
371 */
|
rlm@1
|
372 prevtop = top;
|
rlm@1
|
373 top = &tos[top->link];
|
rlm@1
|
374 if (top->selfpc == selfpc) {
|
rlm@1
|
375 /*
|
rlm@1
|
376 * there it is.
|
rlm@1
|
377 * increment its count
|
rlm@1
|
378 * move it to the head of the chain.
|
rlm@1
|
379 */
|
rlm@1
|
380 top->count++;
|
rlm@1
|
381 toindex = prevtop->link;
|
rlm@1
|
382 prevtop->link = top->link;
|
rlm@1
|
383 top->link = *frompcindex;
|
rlm@1
|
384 *frompcindex = (unsigned short)toindex;
|
rlm@1
|
385 goto done;
|
rlm@1
|
386 }
|
rlm@1
|
387
|
rlm@1
|
388 }
|
rlm@1
|
389 done:
|
rlm@1
|
390 profiling--;
|
rlm@1
|
391 /* and fall through */
|
rlm@1
|
392 out:
|
rlm@1
|
393 return; /* normal return restores saved registers */
|
rlm@1
|
394
|
rlm@1
|
395 overflow:
|
rlm@1
|
396 profiling++; /* halt further profiling */
|
rlm@1
|
397 #define TOLIMIT "mcount: tos overflow\n"
|
rlm@1
|
398 systemMessage(0, TOLIMIT);
|
rlm@1
|
399 goto out;
|
rlm@1
|
400 }
|
rlm@1
|
401
|
rlm@1
|
402 void profSetHertz(int h)
|
rlm@1
|
403 {
|
rlm@1
|
404 hz = h;
|
rlm@1
|
405 }
|