mirror of
https://github.com/pspdev/pspsdk.git
synced 2025-12-26 10:35:39 +00:00
first commit
This commit is contained in:
18
src/prof/Makefile.am
Normal file
18
src/prof/Makefile.am
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
libdir = @PSPSDK_LIBDIR@
|
||||
|
||||
CC = @PSP_CC@
|
||||
CCAS = $(CC)
|
||||
AR = @PSP_AR@
|
||||
RANLIB = @PSP_RANLIB@
|
||||
|
||||
INCLUDES = -I$(top_srcdir)/src/base -I$(top_srcdir)/src/debug -I$(top_srcdir)/src/user
|
||||
CFLAGS = @PSPSDK_CFLAGS@ -std=gnu99 -Wall -Wmissing-prototypes
|
||||
CCASFLAGS = $(CFLAGS) -I$(top_srcdir)/src/base -I$(top_srcdir)/src/kernel
|
||||
|
||||
libpspprofincludedir = @PSPSDK_INCLUDEDIR@
|
||||
libpspprofinclude_HEADERS = pspprof.h
|
||||
|
||||
lib_LIBRARIES = libpspprof.a
|
||||
libpspprof_a_SOURCES = prof.c mcount.s
|
||||
libpspprof_a_LIBADD =
|
||||
61
src/prof/mcount.s
Normal file
61
src/prof/mcount.s
Normal file
@@ -0,0 +1,61 @@
|
||||
.set noreorder
|
||||
.set noat
|
||||
|
||||
.global _mcount
|
||||
.ent _mcount
|
||||
|
||||
_mcount:
|
||||
|
||||
|
||||
# Generated code already substracts 8 bytes
|
||||
# We store our ra, at and a0-a3
|
||||
addu $29, $29, -40
|
||||
sd $31, 0($29) # store ra
|
||||
sd $1, 8($29) # at = ra of caller
|
||||
sd $4, 16($29)
|
||||
sd $5, 24($29)
|
||||
sd $6, 32($29)
|
||||
sd $7, 40($29)
|
||||
|
||||
# Make sure we're not recursively called when compiling __mcount()
|
||||
# With -pg
|
||||
la $4, _busy
|
||||
lw $5, 0($4)
|
||||
bnez $5, done
|
||||
nop
|
||||
|
||||
# Mark busy
|
||||
li $5, 1
|
||||
sw $5, 0($4)
|
||||
|
||||
# Call internal C handler
|
||||
move $4, $1
|
||||
move $5, $31
|
||||
jal __mcount
|
||||
nop
|
||||
|
||||
# Unmark busy
|
||||
la $4, _busy
|
||||
li $5, 0
|
||||
sw $5, 0($4)
|
||||
|
||||
done:
|
||||
|
||||
# Restore registers
|
||||
ld $31, 0($29)
|
||||
ld $1, 8($29)
|
||||
ld $4, 16($29)
|
||||
ld $5, 24($29)
|
||||
ld $6, 32($29)
|
||||
ld $7, 40($29)
|
||||
addu $29, $29, 48 # generated code substracts 8 bytes
|
||||
j $31
|
||||
move $31, $1 # restore caller's ra
|
||||
|
||||
_busy:
|
||||
.space 4
|
||||
|
||||
.end _mcount
|
||||
|
||||
.set reorder
|
||||
.set at
|
||||
256
src/prof/prof.c
Normal file
256
src/prof/prof.c
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* PSP Software Development Kit - http://www.pspdev.org
|
||||
* -----------------------------------------------------------------------
|
||||
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
|
||||
*
|
||||
* prof.c - Main profiler code
|
||||
*
|
||||
* Copyright (c) 2005 urchin <c64psp@gmail.com>
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define GMON_PROF_ON 0
|
||||
#define GMON_PROF_BUSY 1
|
||||
#define GMON_PROF_ERROR 2
|
||||
#define GMON_PROF_OFF 3
|
||||
|
||||
#define GMONVERSION 0x00051879
|
||||
|
||||
#include <pspthreadman.h>
|
||||
|
||||
/** gmon.out file header */
|
||||
struct gmonhdr
|
||||
{
|
||||
int lpc; /* lowest pc address */
|
||||
int hpc; /* highest pc address */
|
||||
int ncnt; /* size of samples + size of header */
|
||||
int version; /* version number */
|
||||
int profrate; /* profiling clock rate */
|
||||
int resv[3]; /* reserved */
|
||||
};
|
||||
|
||||
/** frompc -> selfpc graph */
|
||||
struct rawarc
|
||||
{
|
||||
unsigned int frompc;
|
||||
unsigned int selfpc;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
/** context */
|
||||
struct gmonparam
|
||||
{
|
||||
int state;
|
||||
unsigned int lowpc;
|
||||
unsigned int highpc;
|
||||
unsigned int textsize;
|
||||
unsigned int hashfraction;
|
||||
|
||||
int narcs;
|
||||
struct rawarc *arcs;
|
||||
|
||||
int nsamples;
|
||||
unsigned int *samples;
|
||||
|
||||
int timer;
|
||||
|
||||
unsigned int pc;
|
||||
};
|
||||
|
||||
/// holds context statistics
|
||||
static struct gmonparam gp;
|
||||
|
||||
/// one histogram per four bytes of text space
|
||||
#define HISTFRACTION 4
|
||||
|
||||
/// define sample frequency - 1000 hz = 1ms
|
||||
#define SAMPLE_FREQ 1000
|
||||
|
||||
/// have we allocated memory and registered already
|
||||
static int initialized = 0;
|
||||
|
||||
/// defined by linker
|
||||
extern int _ftext;
|
||||
extern int _etext;
|
||||
|
||||
/* forward declarations */
|
||||
void gprof_cleanup(void);
|
||||
void __mcount(unsigned int, unsigned int);
|
||||
static SceUInt timer_handler(SceUID uid, SceKernelSysClock *c1, SceKernelSysClock *c2, void *common);
|
||||
|
||||
/** Initializes pg library
|
||||
|
||||
After calculating the text size, initialize() allocates enough
|
||||
memory to allow fastest access to arc structures, and some more
|
||||
for sampling statistics. Note that this also installs a timer that
|
||||
runs at 1000 hert.
|
||||
*/
|
||||
static void initialize()
|
||||
{
|
||||
initialized = 1;
|
||||
|
||||
memset(&gp, '\0', sizeof(gp));
|
||||
gp.state = GMON_PROF_ON;
|
||||
gp.lowpc = (unsigned int)&_ftext;
|
||||
gp.highpc = (unsigned int)&_etext;
|
||||
gp.textsize = gp.highpc - gp.lowpc;
|
||||
gp.hashfraction = HISTFRACTION;
|
||||
|
||||
gp.narcs = (gp.textsize + gp.hashfraction - 1) / gp.hashfraction;
|
||||
gp.arcs = (struct rawarc *)malloc(sizeof(struct rawarc) * gp.narcs);
|
||||
if (gp.arcs == NULL)
|
||||
{
|
||||
gp.state = GMON_PROF_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
gp.nsamples = (gp.textsize + gp.hashfraction - 1) / gp.hashfraction;
|
||||
gp.samples = (unsigned int *)malloc(sizeof(unsigned int) * gp.nsamples);
|
||||
if (gp.samples == NULL)
|
||||
{
|
||||
free(gp.arcs);
|
||||
gp.arcs = 0;
|
||||
gp.state = GMON_PROF_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
memset((void *)gp.arcs, '\0', gp.narcs * (sizeof(struct rawarc)));
|
||||
memset((void *)gp.samples, '\0', gp.nsamples * (sizeof(unsigned int )));
|
||||
|
||||
gp.timer = sceKernelCreateVTimer("gprof timer", NULL);
|
||||
|
||||
SceKernelSysClock sc;
|
||||
sc.hi = 0;
|
||||
sc.low = SAMPLE_FREQ;
|
||||
|
||||
int thid = sceKernelGetThreadId();
|
||||
|
||||
SceKernelThreadInfo info;
|
||||
info.size = sizeof(info);
|
||||
int ret = sceKernelReferThreadStatus(thid, &info);
|
||||
|
||||
if(ret == 0)
|
||||
{
|
||||
void* timer_addr = timer_handler;
|
||||
if((info.attr & PSP_THREAD_ATTR_USER) == 0)
|
||||
{
|
||||
timer_addr += 0x80000000;
|
||||
}
|
||||
|
||||
ret = sceKernelSetVTimerHandler(gp.timer, &sc, timer_addr, NULL);
|
||||
}
|
||||
|
||||
if(ret == 0)
|
||||
{
|
||||
sceKernelStartVTimer(gp.timer);
|
||||
}
|
||||
}
|
||||
|
||||
/** Writes gmon.out dump file and stops profiling
|
||||
|
||||
Called from atexit() handler; will dump out a host:gmon.out file
|
||||
with all collected information.
|
||||
*/
|
||||
void gprof_cleanup()
|
||||
{
|
||||
FILE *fp;
|
||||
int i;
|
||||
struct gmonhdr hdr;
|
||||
|
||||
if (gp.state != GMON_PROF_ON)
|
||||
{
|
||||
/* profiling was disabled anyway */
|
||||
return;
|
||||
}
|
||||
|
||||
/* disable profiling before we make plenty of libc calls */
|
||||
gp.state = GMON_PROF_OFF;
|
||||
|
||||
sceKernelStopVTimer(gp.timer);
|
||||
|
||||
fp = fopen("gmon.out", "wb");
|
||||
hdr.lpc = gp.lowpc;
|
||||
hdr.hpc = gp.highpc;
|
||||
hdr.ncnt = sizeof(hdr) + (sizeof(unsigned int) * gp.nsamples);
|
||||
hdr.version = GMONVERSION;
|
||||
hdr.profrate = SAMPLE_FREQ;
|
||||
hdr.resv[0] = 0;
|
||||
hdr.resv[1] = 0;
|
||||
hdr.resv[2] = 0;
|
||||
fwrite(&hdr, 1, sizeof(hdr), fp);
|
||||
fwrite(gp.samples, gp.nsamples, sizeof(unsigned int), fp);
|
||||
|
||||
for (i=0; i<gp.narcs; i++)
|
||||
{
|
||||
if (gp.arcs[i].count > 0)
|
||||
{
|
||||
fwrite(gp.arcs + i, sizeof(struct rawarc), 1, fp);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/** Internal C handler for _mcount()
|
||||
@param frompc pc address of caller
|
||||
@param selfpc pc address of current function
|
||||
|
||||
Called from mcount.S to make life a bit easier. __mcount is called
|
||||
right before a function starts. GCC generates a tiny stub at the very
|
||||
beginning of each compiled routine, which eventually brings the
|
||||
control to here.
|
||||
*/
|
||||
void __mcount(unsigned int frompc, unsigned int selfpc)
|
||||
{
|
||||
int e;
|
||||
struct rawarc *arc;
|
||||
|
||||
if (initialized == 0)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
if (gp.state != GMON_PROF_ON)
|
||||
{
|
||||
/* returned off for some reason */
|
||||
return;
|
||||
}
|
||||
|
||||
frompc = frompc & 0x0FFFFFFF;
|
||||
selfpc = selfpc & 0x0FFFFFFF;
|
||||
|
||||
/* call might come from stack */
|
||||
if (frompc >= gp.lowpc && frompc <= gp.highpc)
|
||||
{
|
||||
gp.pc = selfpc;
|
||||
e = (frompc - gp.lowpc) / gp.hashfraction;
|
||||
arc = gp.arcs + e;
|
||||
arc->frompc = frompc;
|
||||
arc->selfpc = selfpc;
|
||||
arc->count++;
|
||||
}
|
||||
}
|
||||
|
||||
/** Internal timer handler
|
||||
*/
|
||||
static SceUInt timer_handler(SceUID uid, SceKernelSysClock *requested, SceKernelSysClock *actual, void *common)
|
||||
{
|
||||
unsigned int frompc = gp.pc;
|
||||
|
||||
if (gp.state == GMON_PROF_ON)
|
||||
{
|
||||
/* call might come from stack */
|
||||
if (frompc >= gp.lowpc && frompc <= gp.highpc)
|
||||
{
|
||||
int e = (frompc - gp.lowpc) / gp.hashfraction;
|
||||
gp.samples[e]++;
|
||||
}
|
||||
}
|
||||
|
||||
return SAMPLE_FREQ;
|
||||
}
|
||||
25
src/prof/pspprof.h
Normal file
25
src/prof/pspprof.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* PSP Software Development Kit - http://www.pspdev.org
|
||||
* -----------------------------------------------------------------------
|
||||
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
|
||||
*
|
||||
* pspprof.h - Prototypes for the profiler library
|
||||
*
|
||||
* Copyright (c) 2006 Urchin
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
#ifndef __PSPPROF_H__
|
||||
#define __PSPPROF_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void gprof_cleanup();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PSPPROF_H__ */
|
||||
Reference in New Issue
Block a user