From 2c78a2cfeb3d2480d87f0108dd42e61b072b4005 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Wed, 17 Jul 2019 13:00:49 -0400 Subject: [PATCH] basic working implementation with manual calls --- Makefile | 6 +++-- libprof.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ libprof.h | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 libprof.h diff --git a/Makefile b/Makefile index 510e178..1ea5803 100644 --- a/Makefile +++ b/Makefile @@ -2,9 +2,10 @@ # libprof Makefile cflags := -m3 -mb -ffreestanding -nostdlib -fstrict-volatile-bitfields -Wall \ - -Wextra -Os -std=c11 -target := sh3eb-elf + -Wextra -Os -I . +target ?= sh3eb-elf lib := libprof.a +header := libprof.h prefix := $(shell $(target)-gcc -print-search-dirs | grep install \ | sed 's/install: //') @@ -42,3 +43,4 @@ distclean: clean install: cp $(lib) $(prefix) + cp $(header) $(prefix)/include diff --git a/libprof.c b/libprof.c index 5a896c7..4ab4ad9 100644 --- a/libprof.c +++ b/libprof.c @@ -1,2 +1,73 @@ #include +#include #include +#include +#include + +#include + +/* Recursion depth of each function currently being executed */ +uint8_t *prof_rec = NULL; +/* Time that has elapsed within each function; the value for a given function + is only relevant when it is not executing, due to optimizations */ +uint32_t *prof_elapsed = NULL; +/* Timer counter */ +uint32_t volatile *prof_tcnt = NULL; +/* Timer ID */ +static int prof_timer; + +/* prof_init(): Initialize the profiler's data and timer */ +int prof_init(int n, int timer) +{ + if((unsigned)timer >= 3) return 1; + + prof_rec = malloc(n * sizeof *prof_rec); + prof_elapsed = malloc(n * sizeof *prof_elapsed); + + int status = timer_setup(timer, 0xffffffff, timer_Po_4, NULL, NULL); + + if(!prof_rec || !prof_elapsed || status < 0) + { + prof_quit(); + return 1; + } + + /* Fix the configuration done by gint by disabling the interrupt */ + if(isSH3()) + { + SH7705_TMU.TMU[timer].TCR.UNIE = 0; + prof_tcnt = &SH7705_TMU.TMU[timer].TCNT; + } + else + { + SH7305_TMU.TMU[timer].TCR.UNIE = 0; + prof_tcnt = &SH7305_TMU.TMU[timer].TCNT; + } + + timer_start(timer); + prof_timer = timer; + + return 0; +} + +/* prof_quit(): Free the profiler's data and timer */ +void prof_quit(void) +{ + timer_stop(prof_timer); + + free(prof_rec); + free(prof_time); +} + +//--- +// Post-measurement analysis +//--- + +/* prof_time(): Time spent in a given context, in microseconds */ +uint32_t prof_time(int ctx) +{ + int Pphi = clock_freq()->Pphi_f; + uint64_t time = prof_elapsed[ctx]; + + return (time * 4 * 1000000) / Pphi; +} diff --git a/libprof.h b/libprof.h new file mode 100644 index 0000000..804ea26 --- /dev/null +++ b/libprof.h @@ -0,0 +1,75 @@ +//--- +// libprof: A manual profiling library for gint +//--- + +#ifndef LIBPROF_LIBPROF +#define LIBPROF_LIBPROF + +#include + +//--- +// Initialization +//--- + +/* prof_init(): Initialize the profiler's data and timer + + Initializes [prof_rec] and [prof_time] (see below) with enough elements to + hold all the context IDs. Context IDs should be numbered from 0 to [n-1]; + due to speed requirements array bounds are not checked so be careful. + + Also starts a timer to count time. The timer ID must be set to 0, 1 or 2 as + the standard TMU is the most tweakable and precise. libprof automatically + selects an accurate timer configuration. + + @n Number of different contexts (functions) that will be measured + @timer Timer ID, see to select one + Returns non-zero if a setup error occurs. */ +int prof_init(int n, int timer); + +/* prof_quit(): Free the profiler's data and timer */ +void prof_quit(void); + +//--- +// Runtime time measurement +//--- + +/* Recursion depth of each function currently being executed */ +extern uint8_t *prof_rec; +/* Time that has elapsed within each function; the value for a given function + is only relevant when it is not executing, due to optimizations */ +extern uint32_t *prof_elapsed; +/* Timer counter */ +extern uint32_t volatile *prof_tcnt; + +/* prof_enter(): Start counting time for a function + This macro should be called at the start of the context scope. If the + function was already executing then the deepest instance in the stack is + used instead of creating a new counter. */ +#define prof_enter(ctx) { \ + if(!prof_rec[ctx]++) prof_elapsed[ctx] += *prof_tcnt; \ +} + +/* prof_leave(): Stop counting time for a function + This should be called at the end of the context scope; it only actually + stops if there is no deeper instance of the context in the stack. If there + are not as exactly as many prof_leave()'s as prof_enter()'s then the + resulting time measure will not be relevant at all. */ +#define prof_leave(ctx) { \ + if(!--prof_rec[ctx]) prof_elapsed[ctx] -= *prof_tcnt; \ +} + +/* prof_clear(): Clear a context's counter + This operation is defined only if the context is not being profiled. */ +#define prof_clear(ctx) { \ + prof_elapsed[ctx] = 0; \ +} + +//--- +// Post-measurement analysis +//--- + +/* prof_time(): Time spent in a given context, in microseconds + Should only be called when the context is not currently executing. */ +uint32_t prof_time(int ctx); + +#endif /* LIBPROF_LIBPROF */