rlm@1: // VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. rlm@1: // Copyright (C) 1999-2003 Forgotten rlm@1: // Copyright (C) 2004 Forgotten and the VBA development team rlm@1: rlm@1: // This program is free software; you can redistribute it and/or modify rlm@1: // it under the terms of the GNU General Public License as published by rlm@1: // the Free Software Foundation; either version 2, or(at your option) rlm@1: // any later version. rlm@1: // rlm@1: // This program is distributed in the hope that it will be useful, rlm@1: // but WITHOUT ANY WARRANTY; without even the implied warranty of rlm@1: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the rlm@1: // GNU General Public License for more details. rlm@1: // rlm@1: // You should have received a copy of the GNU General Public License rlm@1: // along with this program; if not, write to the Free Software Foundation, rlm@1: // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. rlm@1: rlm@1: // adapted from gmon.c rlm@1: /*- rlm@1: * Copyright (c) 1991, 1998 The Regents of the University of California. rlm@1: * All rights reserved. rlm@1: * rlm@1: * Redistribution and use in source and binary forms, with or without rlm@1: * modification, are permitted provided that the following conditions rlm@1: * are met: rlm@1: * 1. Redistributions of source code must retain the above copyright rlm@1: * notice, this list of conditions and the following disclaimer. rlm@1: * 2. Redistributions in binary form must reproduce the above copyright rlm@1: * notice, this list of conditions and the following disclaimer in the rlm@1: * documentation and/or other materials provided with the distribution. rlm@1: * 3. [rescinded 22 July 1999] rlm@1: * 4. Neither the name of the University nor the names of its contributors rlm@1: * may be used to endorse or promote products derived from this software rlm@1: * without specific prior written permission. rlm@1: * rlm@1: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND rlm@1: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE rlm@1: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE rlm@1: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE rlm@1: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL rlm@1: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS rlm@1: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) rlm@1: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT rlm@1: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY rlm@1: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF rlm@1: * SUCH DAMAGE. rlm@1: */ rlm@1: rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: rlm@1: #include "gmon.h" rlm@1: #include "gmon_out.h" rlm@1: rlm@1: #include "../common/System.h" rlm@1: #include "../gba/GBA.h" rlm@1: #include "../gba/GBAGlobals.h" rlm@1: #include "../NLS.h" rlm@1: rlm@1: /* rlm@1: * froms is actually a bunch of unsigned shorts indexing tos rlm@1: */ rlm@1: static int profiling = 3; rlm@1: static unsigned short *froms; rlm@1: static struct tostruct *tos = 0; rlm@1: static long tolimit = 0; rlm@1: static u32 s_lowpc = 0; rlm@1: static u32 s_highpc = 0; rlm@1: static unsigned long s_textsize = 0; rlm@1: rlm@1: static int ssiz; rlm@1: static char *sbuf; rlm@1: static int s_scale; rlm@1: rlm@1: static int hz = 0; rlm@1: static int hist_num_bins = 0; rlm@1: static char hist_dimension[16] = "seconds"; rlm@1: static char hist_dimension_abbrev = 's'; rlm@1: rlm@1: /* see profil(2) where this is describe (incorrectly) */ rlm@1: #define SCALE_1_TO_1 0x10000L rlm@1: rlm@1: void profPut32(char *b, u32 v) rlm@1: { rlm@1: b[0] = v & 255; rlm@1: b[1] = (v >> 8) & 255; rlm@1: b[2] = (v >> 16) & 255; rlm@1: b[3] = (v >> 24) & 255; rlm@1: } rlm@1: rlm@1: void profPut16(char *b, u16 v) rlm@1: { rlm@1: b[0] = v & 255; rlm@1: b[1] = (v >> 8) & 255; rlm@1: } rlm@1: rlm@1: int profWrite8(FILE *f, u8 b) rlm@1: { rlm@1: if(fwrite(&b, 1, 1, f) != 1) rlm@1: return 1; rlm@1: return 0; rlm@1: } rlm@1: rlm@1: int profWrite32(FILE *f, u32 v) rlm@1: { rlm@1: char buf[4]; rlm@1: rlm@1: profPut32(buf, v); rlm@1: if(fwrite(buf, 1, 4, f) != 4) rlm@1: return 1; rlm@1: return 0; rlm@1: } rlm@1: rlm@1: int profWrite(FILE *f, char *buf, unsigned int n) rlm@1: { rlm@1: if(fwrite(buf, 1, n, f) != n) rlm@1: return 1; rlm@1: return 0; rlm@1: } rlm@1: rlm@1: /* Control profiling; rlm@1: profiling is what mcount checks to see if rlm@1: all the data structures are ready. */ rlm@1: rlm@1: void profControl(int mode) rlm@1: { rlm@1: if (mode) { rlm@1: /* start */ rlm@1: #ifdef PROFILING rlm@1: cpuProfil(sbuf, ssiz, (u32)s_lowpc, s_scale); rlm@1: #endif rlm@1: profiling = 0; rlm@1: } else { rlm@1: /* stop */ rlm@1: #ifdef PROFILING rlm@1: cpuProfil(NULL, 0, 0, 0); rlm@1: #endif rlm@1: profiling = 3; rlm@1: } rlm@1: } rlm@1: rlm@1: rlm@1: #define MSG N_("No space for profiling buffer(s)\n") rlm@1: rlm@1: void profStartup(u32 lowpc, u32 highpc) rlm@1: { rlm@1: int monsize; rlm@1: char *buffer; rlm@1: int o; rlm@1: rlm@1: /* rlm@1: * round lowpc and highpc to multiples of the density we're using rlm@1: * so the rest of the scaling (here and in gprof) stays in ints. rlm@1: */ rlm@1: lowpc = ROUNDDOWN(lowpc, HISTFRACTION*sizeof(HISTCOUNTER)); rlm@1: s_lowpc = lowpc; rlm@1: highpc = ROUNDUP(highpc, HISTFRACTION*sizeof(HISTCOUNTER)); rlm@1: s_highpc = highpc; rlm@1: s_textsize = highpc - lowpc; rlm@1: monsize = (s_textsize / HISTFRACTION); rlm@1: buffer = (char *)calloc(1, 2*monsize ); rlm@1: if ( buffer == NULL ) { rlm@1: systemMessage(0, MSG); rlm@1: return; rlm@1: } rlm@1: froms = (unsigned short *) calloc(1, 4*s_textsize / HASHFRACTION ); rlm@1: if ( froms == NULL ) { rlm@1: systemMessage(0, MSG); rlm@1: free(buffer); rlm@1: buffer = NULL; rlm@1: return; rlm@1: } rlm@1: tolimit = s_textsize * ARCDENSITY / 100; rlm@1: if ( tolimit < MINARCS ) { rlm@1: tolimit = MINARCS; rlm@1: } else if ( tolimit > 65534 ) { rlm@1: tolimit = 65534; rlm@1: } rlm@1: tos = (struct tostruct *) calloc(1, tolimit * sizeof( struct tostruct ) ); rlm@1: if ( tos == NULL ) { rlm@1: systemMessage(0, MSG); rlm@1: rlm@1: free(buffer); rlm@1: buffer = NULL; rlm@1: rlm@1: free(froms); rlm@1: froms = NULL; rlm@1: rlm@1: return; rlm@1: } rlm@1: tos[0].link = 0; rlm@1: sbuf = buffer; rlm@1: ssiz = monsize; rlm@1: if ( monsize <= 0 ) rlm@1: return; rlm@1: o = highpc - lowpc; rlm@1: if( monsize < o ) rlm@1: s_scale = (int)(( (float) monsize / o ) * SCALE_1_TO_1); rlm@1: else rlm@1: s_scale = SCALE_1_TO_1; rlm@1: profControl(1); rlm@1: } rlm@1: rlm@1: void profCleanup() rlm@1: { rlm@1: FILE *fd; rlm@1: int fromindex; rlm@1: int endfrom; rlm@1: u32 frompc; rlm@1: int toindex; rlm@1: struct gmon_hdr ghdr; rlm@1: rlm@1: profControl(0); rlm@1: fd = fopen( "gmon.out" , "wb" ); rlm@1: if ( fd == NULL ) { rlm@1: systemMessage( 0, "mcount: gmon.out" ); rlm@1: return; rlm@1: } rlm@1: rlm@1: memcpy(&ghdr.cookie[0], GMON_MAGIC, 4); rlm@1: profPut32((char *)ghdr.version, GMON_VERSION); rlm@1: rlm@1: if(fwrite(&ghdr, sizeof(ghdr), 1, fd) != 1) { rlm@1: systemMessage(0, "mcount: gmon.out header"); rlm@1: fclose(fd); rlm@1: return; rlm@1: } rlm@1: rlm@1: if(hz == 0) rlm@1: hz = 100; rlm@1: rlm@1: hist_num_bins = ssiz; rlm@1: rlm@1: if(profWrite8(fd, GMON_TAG_TIME_HIST) || rlm@1: profWrite32(fd, (u32)s_lowpc) || rlm@1: profWrite32(fd, (u32)s_highpc) || rlm@1: profWrite32(fd, hist_num_bins) || rlm@1: profWrite32(fd, hz) || rlm@1: profWrite(fd, hist_dimension, 15) || rlm@1: profWrite(fd, &hist_dimension_abbrev, 1)) { rlm@1: systemMessage(0, "mcount: gmon.out hist"); rlm@1: fclose(fd); rlm@1: return; rlm@1: } rlm@1: u16 *hist_sample = (u16 *)sbuf; rlm@1: rlm@1: u16 count; rlm@1: int i; rlm@1: rlm@1: for(i = 0; i < hist_num_bins; ++i) { rlm@1: profPut16((char *)&count, hist_sample[i]); rlm@1: rlm@1: if(fwrite(&count, sizeof(count), 1, fd) != 1) { rlm@1: systemMessage(0, "mcount: gmon.out sample"); rlm@1: fclose(fd); rlm@1: return; rlm@1: } rlm@1: } rlm@1: rlm@1: endfrom = s_textsize / (HASHFRACTION * sizeof(*froms)); rlm@1: for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) { rlm@1: if ( froms[fromindex] == 0 ) { rlm@1: continue; rlm@1: } rlm@1: frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms)); rlm@1: for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) { rlm@1: if(profWrite8(fd, GMON_TAG_CG_ARC) || rlm@1: profWrite32(fd, (u32)frompc) || rlm@1: profWrite32(fd, (u32)tos[toindex].selfpc) || rlm@1: profWrite32(fd, tos[toindex].count)) { rlm@1: systemMessage(0, "mcount: arc"); rlm@1: fclose(fd); rlm@1: return; rlm@1: } rlm@1: } rlm@1: } rlm@1: fclose(fd); rlm@1: } rlm@1: rlm@1: void profCount() rlm@1: { rlm@1: register u32 selfpc; rlm@1: register unsigned short *frompcindex; rlm@1: register struct tostruct *top; rlm@1: register struct tostruct *prevtop; rlm@1: register long toindex; rlm@1: rlm@1: /* rlm@1: * find the return address for mcount, rlm@1: * and the return address for mcount's caller. rlm@1: */ rlm@1: rlm@1: /* selfpc = pc pushed by mcount call. rlm@1: This identifies the function that was just entered. */ rlm@1: selfpc = (u32) reg[14].I; rlm@1: /* frompcindex = pc in preceding frame. rlm@1: This identifies the caller of the function just entered. */ rlm@1: frompcindex = (unsigned short *) reg[12].I; rlm@1: /* rlm@1: * check that we are profiling rlm@1: * and that we aren't recursively invoked. rlm@1: */ rlm@1: if (profiling) { rlm@1: goto out; rlm@1: } rlm@1: profiling++; rlm@1: /* rlm@1: * check that frompcindex is a reasonable pc value. rlm@1: * for example: signal catchers get called from the stack, rlm@1: * not from text space. too bad. rlm@1: */ rlm@1: frompcindex = (unsigned short *) ((long) frompcindex - (long) s_lowpc); rlm@1: if ((unsigned long) frompcindex > s_textsize) { rlm@1: goto done; rlm@1: } rlm@1: frompcindex = rlm@1: &froms[((long) frompcindex) / (HASHFRACTION * sizeof(*froms))]; rlm@1: toindex = *frompcindex; rlm@1: if (toindex == 0) { rlm@1: /* rlm@1: * first time traversing this arc rlm@1: */ rlm@1: toindex = ++tos[0].link; rlm@1: if (toindex >= tolimit) { rlm@1: goto overflow; rlm@1: } rlm@1: *frompcindex = (unsigned short)toindex; rlm@1: top = &tos[toindex]; rlm@1: top->selfpc = selfpc; rlm@1: top->count = 1; rlm@1: top->link = 0; rlm@1: goto done; rlm@1: } rlm@1: top = &tos[toindex]; rlm@1: if (top->selfpc == selfpc) { rlm@1: /* rlm@1: * arc at front of chain; usual case. rlm@1: */ rlm@1: top->count++; rlm@1: goto done; rlm@1: } rlm@1: /* rlm@1: * have to go looking down chain for it. rlm@1: * top points to what we are looking at, rlm@1: * prevtop points to previous top. rlm@1: * we know it is not at the head of the chain. rlm@1: */ rlm@1: for (; /* goto done */; ) { rlm@1: if (top->link == 0) { rlm@1: /* rlm@1: * top is end of the chain and none of the chain rlm@1: * had top->selfpc == selfpc. rlm@1: * so we allocate a new tostruct rlm@1: * and link it to the head of the chain. rlm@1: */ rlm@1: toindex = ++tos[0].link; rlm@1: if (toindex >= tolimit) { rlm@1: goto overflow; rlm@1: } rlm@1: top = &tos[toindex]; rlm@1: top->selfpc = selfpc; rlm@1: top->count = 1; rlm@1: top->link = *frompcindex; rlm@1: *frompcindex = (unsigned short)toindex; rlm@1: goto done; rlm@1: } rlm@1: /* rlm@1: * otherwise, check the next arc on the chain. rlm@1: */ rlm@1: prevtop = top; rlm@1: top = &tos[top->link]; rlm@1: if (top->selfpc == selfpc) { rlm@1: /* rlm@1: * there it is. rlm@1: * increment its count rlm@1: * move it to the head of the chain. rlm@1: */ rlm@1: top->count++; rlm@1: toindex = prevtop->link; rlm@1: prevtop->link = top->link; rlm@1: top->link = *frompcindex; rlm@1: *frompcindex = (unsigned short)toindex; rlm@1: goto done; rlm@1: } rlm@1: rlm@1: } rlm@1: done: rlm@1: profiling--; rlm@1: /* and fall through */ rlm@1: out: rlm@1: return; /* normal return restores saved registers */ rlm@1: rlm@1: overflow: rlm@1: profiling++; /* halt further profiling */ rlm@1: #define TOLIMIT "mcount: tos overflow\n" rlm@1: systemMessage(0, TOLIMIT); rlm@1: goto out; rlm@1: } rlm@1: rlm@1: void profSetHertz(int h) rlm@1: { rlm@1: hz = h; rlm@1: }