aboutsummaryrefslogtreecommitdiff
path: root/libraries/libprof
diff options
context:
space:
mode:
authorPaul Selkirk <paul@psgd.org>2017-05-05 22:58:34 -0400
committerPaul Selkirk <paul@psgd.org>2017-05-05 22:58:34 -0400
commit1815f1b2aa0a3ff0654f4eb65fdd0a5bdfe8c7b7 (patch)
tree34e28b0a0b2b1b862bb3039792a9504e227fd7ba /libraries/libprof
parent9cec66f9200cb573353928bd3292fb1f710e4b3c (diff)
Port profiling code, using a new SysTick hook and new CLI commands.
Diffstat (limited to 'libraries/libprof')
-rw-r--r--libraries/libprof/README.txt65
-rw-r--r--libraries/libprof/gmon.c61
-rw-r--r--libraries/libprof/profil.c22
3 files changed, 92 insertions, 56 deletions
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 <stdint.h>
#include <string.h>
-#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 <stdio.h>
#include <sys/types.h>
@@ -19,34 +19,34 @@
#include <string.h>
#include <stdint.h>
+#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 */
}