📄 nios_gprof.c
字号:
/*-
* Copyright (c) 1991 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. [rescinded 22 July 1999]
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* File: nios_gprof.c
* Author: Caio Villela
* Description: this file was adapted from the Solaris mcount.c
* module.
*/
/*
* The profiling code uses a timer named na_timer1 to periodically
* sample the program counter. The rate of sampling is set by
* the constant TIMER_SAMPLE_RATE, below, in interrupts
* per second.
*
* Each timer interrupt increments one of many "buckets",
* (counter in the profinfo struct) which represent ranges
* of code memory (code chunk size). The default for Nios 32
* is 2 bytes for a code chunk size, which is a single Nios
* instruction. This increases your data memory footprint
* by 100% of your code size. For Nios 16, due to memory
* restrictions, this has a default set to 8 (or 4 Nios
* instructions), causing the counter buffer to equal 25%
* of code size.
*
* You can, however, use a larger code chunk size (for instance,
* set HISTFRACTION to 16), with the negative side effect that
* the profile will occasionally attribute a sample to a different
* function than the program counter was actually in. This has the
* positive side effect of reducing the amount of memory you need.
*
* To change the code chunk size, assign a larger value to
* the constant HISTFRACTION, below.
*
* dvb 2001
*/
/* Mangled into a form that works on the Nios development environment
* for Altera Support, September 2001.
*/
#include "nios.h"
/* This specifies the timer sample rate in number of ints per second: */
#define TIMER_SAMPLE_RATE 10000
/* Define a few MACROS : */
#define PTR2LONG(x) ((long) ((unsigned int) x))
/* convert an addr to an index */
#define PROFIDX(pc, base, scale) \
({ \
unsigned int i = (pc - base) / 2; \
if (sizeof (unsigned long long int) > sizeof (unsigned int)) \
i = (unsigned long long int) i * scale / 65536; \
else \
i = i / 65536 * scale + i % 65536 * scale / 65536; \
i; \
})
/* convert an index into an address */
#define PROFADDR(idx, base, scale) \
((base) + ((((idx) << 16) / (scale)) << 1))
/* convert a bin size into a scale */
#define PROFSCALE(range, bins) (((bins) << 16) / ((range) >> 1))
typedef void *_NIOSHANDLE;
struct profinfo {
_NIOSHANDLE targthr; /* thread to profile */
_NIOSHANDLE profthr; /* profiling thread */
unsigned short *counter; /* profiling counters */
unsigned long lowpc, highpc; /* range to be profiled */
long scale; /* scale value of bins */
};
/* global profinfo for profil() call */
static struct profinfo prof;
static int not_enough_memory = 0;
/* Some function prototypes: */
static void moncontrol (int);
void monstartup (char *, char *);
void _mcleanup (void);
char * sbrk (int amount);
void nios_write (int stream, char *msg, int size_of_message);
void nios_profil (unsigned short *bigbuffer, unsigned long buff_size,
unsigned long base, long buf_scale);
void nios_profile_ctl(struct profinfo *, char *, unsigned long, unsigned long, long);
void profile_on (struct profinfo *p);
void profile_off (struct profinfo *p);
static void DoEnableTimerInterrupt(void);
static void DoDisableTimerInterrupt(void);
void nios_prof_hdr_timer_ISR(int context,int irq_number, int interruptee);
int bad_sbrk(void *ptr);
/* CBV - the elements in phdr need to be 32 bit long, not 16. The */
/* structure originaly defined the first 2 as char pointers, which */
/* works for nios32 but not nios16. In order to have the 16 bit gprof */
/* work as well, I am changing all the elements to long and casting */
/* their occurrences in the code. */
struct phdr {
long lpc;
long hpc;
long ncnt;
};
/* Since nios32 addresses a lot more memory, we can be a bit more wastefull */
#ifdef __nios32__
#define HISTFRACTION 2
#define HASHFRACTION 1
#define ARCDENSITY 2
#define MINARCS 50
#else /* for nios 16, use smaller arrays : */
#define HISTFRACTION 8
#define HASHFRACTION 4
#define ARCDENSITY 2
#define MINARCS 50
#endif
#define HISTCOUNTER unsigned short
struct tostruct {
char *selfpc;
long count;
unsigned short link;
};
struct rawarc {
unsigned long raw_frompc;
unsigned long raw_selfpc;
long raw_count;
};
#define ROUNDDOWN(x,y) (((x)/(y))*(y))
#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y))
/* see profil(2) where this is describe (incorrectly) */
#define SCALE_1_TO_1 0x10000L
#define MSG "No space for profiling buffer(s)\n"
char *minbrk;
/*
* froms is actually a bunch of unsigned shorts indexing tos
*/
static int profiling = 3;
static unsigned short *froms;
static struct tostruct *tos = 0;
static long tolimit = 0;
static char *s_lowpc = 0;
static char *s_highpc = 0;
static unsigned long s_textsize = 0;
static unsigned long current_stack = 0xFFFF;
static int ssiz;
static char *sbuf;
static long s_scale;
int bad_sbrk(void *ptr)
{
if(!ptr)
return 1;
if(ptr == (void *)-1)
return 1;
return 0;
}
void monstartup(lowpc, highpc)
char *lowpc;
char *highpc;
{
int monsize;
char *buffer;
char *tmp_ptr;
long o;
/*
* round lowpc and highpc to multiples of the density we're using
* so the rest of the scaling (here and in gprof) stays in ints.
*/
lowpc = (char *)
((unsigned int) ROUNDDOWN((unsigned int)lowpc,
HISTFRACTION*sizeof(HISTCOUNTER)));
s_lowpc = lowpc;
highpc = (char *)
((unsigned int) ROUNDUP((unsigned int)highpc,
HISTFRACTION*sizeof(HISTCOUNTER)));
s_highpc = highpc;
s_textsize = highpc - lowpc;
monsize = (s_textsize / HISTFRACTION) + sizeof(struct phdr);
buffer = (char *) sbrk (monsize);
if ( bad_sbrk(buffer) ) {
// printf("1 - Error allocating %d at 0x%08x \n", monsize, buffer);
not_enough_memory = 1;
printf("%s \n", MSG); /* no more room, print error message */
return;
}
tmp_ptr = buffer; /* zero out the memory ... */
for (o=0; o < monsize; o++) {
*tmp_ptr++ = 0;
}
froms = (unsigned short *) sbrk( s_textsize / HASHFRACTION );
if ( bad_sbrk(froms) ) {
// printf("2 - Error Allocating %d at 0x%08x \n",
// (short) ( s_textsize / HASHFRACTION ), (void *)froms);
printf("%s \n", MSG); /* no more room, print error message */
not_enough_memory = 1;
froms = 0;
return;
}
tmp_ptr = (unsigned char *)froms; /* zero out the memory ... */
for (o=0; o < (int)(s_textsize / HASHFRACTION); o++) {
*tmp_ptr++ = 0;
}
tolimit = s_textsize * ARCDENSITY / 100;
if ( tolimit < MINARCS ) {
tolimit = MINARCS;
} else if ( tolimit > 65534 ) {
tolimit = 65534;
}
tos = (struct tostruct *) sbrk( tolimit * sizeof( struct tostruct ) );
if ( bad_sbrk(tos)) {
// printf("3 - Error Allocating %d at 0x%08x \n",
// (short) ( tolimit * sizeof( struct tostruct )), tos);
printf("%s \n", MSG); /* no more room, print error message */
not_enough_memory = 1;
froms = 0;
tos = 0;
return;
}
tmp_ptr = (unsigned char *) tos; /* zero out the memory ... */
for (o=0; o < (int)(tolimit * sizeof( struct tostruct )); o++) {
*tmp_ptr++ = 0;
}
minbrk = (char *)sbrk(0);
tos[0].link = 0;
sbuf = buffer;
ssiz = monsize;
( (struct phdr *) buffer ) -> lpc = PTR2LONG (lowpc);
( (struct phdr *) buffer ) -> hpc = PTR2LONG (highpc);
( (struct phdr *) buffer ) -> ncnt = (long) ssiz;
monsize -= sizeof(struct phdr);
if ( monsize <= 0 )
return;
o = highpc - lowpc;
if( monsize < o )
{
long quot = o / monsize;
if (quot >= 0x10000)
s_scale = 1;
else if (quot >= 0x100)
s_scale = 0x10000 / quot;
else if (o >= 0x800000)
s_scale = 0x1000000 / (o / (monsize >> 8));
else
s_scale = 0x1000000 / ((o << 8) / monsize);
}
else
s_scale = SCALE_1_TO_1;
moncontrol(1);
}
void _mcleanup()
{
long fromindex;
long endfrom;
char *frompc;
long toindex;
struct rawarc rawarc;
moncontrol(0);
if (not_enough_memory) return; /* if the malloc did not work, forget it */
nios_write( 2 , sbuf , ssiz ); /* this is the array used by convert */
endfrom = s_textsize / (HASHFRACTION * sizeof(*froms));
for ( fromindex = 0 ; fromindex < endfrom ; fromindex++ ) {
if ( froms[fromindex] == 0 ) {
continue;
}
frompc = s_lowpc + (fromindex * HASHFRACTION * sizeof(*froms));
for (toindex=froms[fromindex]; toindex!=0; toindex=tos[toindex].link) {
rawarc.raw_frompc = PTR2LONG (frompc);
rawarc.raw_selfpc = PTR2LONG (tos[toindex].selfpc);
rawarc.raw_count = (long) tos[toindex].count;
nios_write( 2 , (char *) &rawarc , sizeof rawarc );
}
}
}
/*
* A "call" simply puts the return address in %o7 expecting the
* "save" in the procedure to shift it into %i7; this means that
* before the "save" occurs, %o7 contains the address of the call to
* nios_mcount, and %i7 still contains the caller above that.
* The asm mcount here simply saves those registers in argument
* registers and branches to internal_mcount, simulating a call
* with arguments.
* Kludges:
* 1) the branch to internal_mcount is hard coded; it should be
* possible to tell asm to use the assembler-name of a symbol.
* 2) in theory, the function calling mcount could have saved %i7
* somewhere and reused the register; in practice, I *think* this will
* break longjmp (and maybe the debugger) but I'm not certain. (I take
* some comfort in the knowledge that it will break the native mcount
* as well.)
* 3) if builtin_return_address worked, this could be portable.
* However, it would really have to be optimized for arguments of 0
* and 1 and do something like what we have here in order to avoid the
* trap per function call performance hit.
* 4) the atexit and monsetup calls prevent this from simply
* being a leaf routine that doesn't do a "save" (and would thus have
* access to %o7 and %i7 directly) but the call to nios_write() at the end
* would have also prevented this.
*
*/
static void internal_mcount (char *, unsigned short *);
/* i7 == last ret, -> frompcindex */
/* o7 == current ret, -> selfpc */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -