From 358d038067b10330011ef4e6596ae37842d516fe Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Fri, 5 May 2017 22:58:34 -0400 Subject: Port profiling code, using a new SysTick hook and new CLI commands. --- libraries/libprof/README.txt | 65 ++++++++++++++++++++++++++++++++++++++++---- libraries/libprof/gmon.c | 61 ++++++++++++++--------------------------- libraries/libprof/profil.c | 22 +++++++-------- 3 files changed, 92 insertions(+), 56 deletions(-) (limited to 'libraries/libprof') diff --git a/libraries/libprof/README.txt b/libraries/libprof/README.txt index 2df8b96..da138c2 100644 --- a/libraries/libprof/README.txt +++ b/libraries/libprof/README.txt @@ -1,6 +1,61 @@ -Copied from https://github.com/ErichStyger/mcuoneclipse.git, -directory Examples/KDS/FRDM-K64F120M/FRDM-K64F_Profiling/Profiling, -commit 9b7eedddd8b24968128582aedc63be95b61f782c, -dated Mon Jan 9 16:56:17 2017 +0100. -(This is in turn adapted from Cygwin, and can be found in newlib distributions.) +Profiling the Cryptech Alpha +============================ +Origin +------ + +This code was copied from https://github.com/ErichStyger/mcuoneclipse.git, +directory Examples/KDS/FRDM-K64F120M/FRDM-K64F_Profiling/Profiling, commit +9b7eedddd8b24968128582aedc63be95b61f782c, dated Mon Jan 9 16:56:17 2017 +0100. + +References +---------- + +I recommend reading both of these to understand how the profiling code works. + +[1]: https://mcuoneclipse.com/2015/08/23/tutorial-using-gnu-profiling-gprof-with-arm-cortex-m/ +"Tutorial: Using GNU Profiling (gprof) with ARM Cortex-M" + +[2]: http://bgamari.github.io/posts/2014-10-31-semihosting.html +"Semihosting with ARM, GCC, and OpenOCD" + +How to build +------------ + +From the top level, run + + make DO_PROFILING=1 hsm + +By default, all code is profiled, *except* the profiling code itself, +because that would cause fatal recursion. + +How to run +---------- + +You need to start OpenOCD on the host, and enable semihosting, at least +before you try to use it as a remote file system. + +I recommend executing the following in the projects/hsm directory, so that +gmon.out ends up in the same directory as hsm.elf. + +Start OpenOCD: + + $ openocd -f /usr/share/openocd/scripts/board/stm32f4discovery.cfg & + +Connect to OpenOCD: + + $ telnet localhost 4444 + +In the OpenOCD console, enable semihosting: + + > arm semihosting enable + +In the CLI, type `profile start`, then start the unit test or whatever +will be exercising the hsm. Afterwards, in the CLI, type `profile stop`. + +After invoking `profile stop`, it takes almost 2 minutes to write gmon.out +over OpenOCD to the host. + +In the projects/hsm directory, run gprof to analyse the gmon.out file: + + $ gprof hsm.elf >gprof.txt diff --git a/libraries/libprof/gmon.c b/libraries/libprof/gmon.c index 2be8bb2..458028b 100644 --- a/libraries/libprof/gmon.c +++ b/libraries/libprof/gmon.c @@ -41,7 +41,6 @@ #include #include -#define MINUS_ONE_P (-1) #define bzero(ptr,size) memset (ptr, 0, size); #define ERR(s) write(2, s, sizeof(s)) @@ -53,43 +52,30 @@ static int s_scale; static void moncontrol(int mode); -/* required for gcc ARM Embedded 4.9-2015-q2 */ -#if 0 -void *_sbrk(int incr) { - extern char __HeapLimit; /* Defined by the linker */ - static char *heap_end = 0; - char *prev_heap_end; - - if (heap_end==0) { - heap_end = &__HeapLimit; - } - prev_heap_end = heap_end; - heap_end += incr; - return (void *)prev_heap_end; -} -#endif - -static void *fake_sbrk(int size) { - void *rv = malloc(size); - if (rv) { - return rv; - } else { - return (void *) MINUS_ONE_P; - } -} - void monstartup (size_t lowpc, size_t highpc) { register size_t o; char *cp; struct gmonparam *p = &_gmonparam; + if (already_setup) { + /* zero out cp as value will be added there */ + bzero(p->tos, p->kcountsize + p->fromssize + p->tossize); + moncontrol(1); /* start */ + return; + } + already_setup = 1; + + /* enable semihosting, for eventual output */ + extern void initialise_monitor_handles(void); + initialise_monitor_handles(); + /* * 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. */ p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER)); - p->textsize = p->highpc - p->lowpc; + p->textsize = p->highpc - p->lowpc + 0x20; p->kcountsize = p->textsize / HISTFRACTION; p->fromssize = p->textsize / HASHFRACTION; p->tolimit = p->textsize * ARCDENSITY / 100; @@ -100,8 +86,9 @@ void monstartup (size_t lowpc, size_t highpc) { } p->tossize = p->tolimit * sizeof(struct tostruct); - cp = fake_sbrk(p->kcountsize + p->fromssize + p->tossize); - if (cp == (char *)MINUS_ONE_P) { + extern void *hal_allocate_static_memory(const size_t size); + cp = hal_allocate_static_memory(p->kcountsize + p->fromssize + p->tossize); + if (cp == NULL) { ERR("monstartup: out of memory\n"); return; } @@ -142,14 +129,13 @@ void monstartup (size_t lowpc, size_t highpc) { void _mcleanup(void) { static const char gmon_out[] = "gmon.out"; int fd; - int hz; int fromindex; int endfrom; size_t frompc; int toindex; struct rawarc rawarc; struct gmonparam *p = &_gmonparam; - struct gmonhdr gmonhdr, *hdr; + struct gmonhdr gmonhdr = {0}, *hdr; const char *proffile; #ifdef DEBUG int log, len; @@ -159,7 +145,6 @@ void _mcleanup(void) { if (p->state == GMON_PROF_ERROR) { ERR("_mcleanup: tos overflow\n"); } - hz = PROF_HZ; moncontrol(0); /* stop */ proffile = gmon_out; fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0666); @@ -174,7 +159,7 @@ void _mcleanup(void) { return; } len = sprintf(dbuf, "[mcleanup1] kcount 0x%x ssiz %d\n", - p->kcount, p->kcountsize); + (unsigned int)p->kcount, p->kcountsize); write(log, dbuf, len); #endif hdr = (struct gmonhdr *)&gmonhdr; @@ -182,7 +167,8 @@ void _mcleanup(void) { hdr->hpc = p->highpc; hdr->ncnt = p->kcountsize + sizeof(gmonhdr); hdr->version = GMONVERSION; - hdr->profrate = hz; + hdr->profrate = PROF_HZ; + hdr->spare[0] = hdr->spare[1] = hdr->spare[2] = 0; write(fd, (char *)hdr, sizeof *hdr); write(fd, p->kcount, p->kcountsize); endfrom = p->fromssize / sizeof(*p->froms); @@ -195,7 +181,7 @@ void _mcleanup(void) { for (toindex = p->froms[fromindex]; toindex != 0; toindex = p->tos[toindex].link) { #ifdef DEBUG len = sprintf(dbuf, - "[mcleanup2] frompc 0x%x selfpc 0x%x count %d\n" , + "[mcleanup2] frompc 0x%x selfpc 0x%x count %ld\n" , frompc, p->tos[toindex].selfpc, p->tos[toindex].count); write(log, dbuf, len); @@ -234,11 +220,6 @@ void _mcount_internal(uint32_t *frompcindex, uint32_t *selfpc) { register long toindex; struct gmonparam *p = &_gmonparam; - if (!already_setup) { - extern char __etext; /* end of text/code symbol, defined by linker */ - already_setup = 1; - monstartup(0x410, (uint32_t)&__etext); - } /* * check that we are profiling * and that we aren't recursively invoked. diff --git a/libraries/libprof/profil.c b/libraries/libprof/profil.c index 24ede21..07761dd 100644 --- a/libraries/libprof/profil.c +++ b/libraries/libprof/profil.c @@ -9,7 +9,7 @@ details. */ /* - * This file is taken from Cygwin distribution, adopted to be used for bare embeeded targets. + * This file is taken from Cygwin distribution, adapted to be used for bare embedded targets. */ #include #include @@ -19,34 +19,34 @@ #include #include +#include "stm32f4xx_hal.h" /* __get_MSP */ + /* global profinfo for profil() call */ static struct profinfo prof = { PROFILE_NOT_INIT, 0, 0, 0, 0 }; -/* sample the current program counter */ -void SysTick_Handler(void) { - void OSA_SysTick_Handler(void); - static size_t pc, idx; +extern void set_SysTick_hook(void (*hook)(void)); - OSA_SysTick_Handler(); /* call normal Kinetis SDK SysTick handler */ - if (prof.state==PROFILE_ON) { - pc = ((uint32_t*)(__builtin_frame_address(0)))[14]; /* get SP and use it to get the return address from stack */ - if (pc >= prof.lowpc && pc < prof.highpc) { - idx = PROFIDX (pc, prof.lowpc, prof.scale); +/* sample the current program counter */ +static void SysTick_hook(void) { + size_t pc = (size_t)((uint32_t *)__get_MSP())[5]; + if (pc >= prof.lowpc && pc < prof.highpc) { + size_t idx = PROFIDX (pc, prof.lowpc, prof.scale); prof.counter[idx]++; - } } } /* Stop profiling to the profiling buffer pointed to by p. */ static int profile_off (struct profinfo *p) { + set_SysTick_hook(NULL); p->state = PROFILE_OFF; return 0; } /* Create a timer thread and pass it a pointer P to the profiling buffer. */ static int profile_on (struct profinfo *p) { + set_SysTick_hook(SysTick_hook); p->state = PROFILE_ON; return 0; /* ok */ } -- cgit v1.2.3