From 1815f1b2aa0a3ff0654f4eb65fdd0a5bdfe8c7b7 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. --- Makefile | 15 ++++- libraries/libprof/README.txt | 65 ++++++++++++++++++++-- libraries/libprof/gmon.c | 61 +++++++------------- libraries/libprof/profil.c | 22 ++++---- .../TARGET_CRYPTECH_ALPHA/stm32f4xx_it.c | 8 +++ projects/hsm/Makefile | 11 +++- projects/hsm/mgmt-misc.c | 28 ++++++++++ syscalls.c | 6 ++ 8 files changed, 158 insertions(+), 58 deletions(-) diff --git a/Makefile b/Makefile index d8c9593..398e442 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,9 @@ LIBCLI_BLD = $(LIBS_DIR)/libcli LIBTFM_SRC = $(CRYPTECH_ROOT)/sw/thirdparty/libtfm LIBTFM_BLD = $(LIBS_DIR)/libtfm +LIBPROF_SRC = $(LIBS_DIR)/libprof +LIBPROF_BLD = $(LIBS_DIR)/libprof + LIBS = $(MBED_DIR)/libstmf4.a # linker script @@ -105,7 +108,7 @@ CFLAGS += -DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F429xx CFLAGS += -D__CORTEX_M4 -DTARGET_STM -DTARGET_STM32F4 -DTARGET_STM32F429ZI -DTOOLCHAIN_GCC -D__FPU_PRESENT=1 -D$(BOARD) CFLAGS += -DENABLE_WEAK_FUNCTIONS CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections -CFLAGS += -std=c99 +CFLAGS += -std=gnu99 CFLAGS += -I$(TOPLEVEL) CFLAGS += -I$(MBED_DIR)/api CFLAGS += -I$(MBED_DIR)/targets/cmsis @@ -140,11 +143,20 @@ $(LIBHAL_BLD)/libhal.a: $(LIBTFM_BLD)/libtfm.a .FORCE $(LIBCLI_BLD)/libcli.a: .FORCE $(MAKE) -C $(LIBCLI_BLD) +$(LIBPROF_BLD)/libprof.a: .FORCE + $(MAKE) -C $(LIBPROF_BLD) + libhal-test: $(BOARD_OBJS) $(LIBS) $(LIBHAL_BLD)/libhal.a .FORCE $(MAKE) -C projects/libhal-test +ifdef DO_PROFILING +CFLAGS += -pg -DDO_PROFILING +hsm: $(BOARD_OBJS) $(LIBS) $(LIBHAL_BLD)/libhal.a $(LIBCLI_BLD)/libcli.a $(LIBPROF_BLD)/libprof.a .FORCE + $(MAKE) -C projects/hsm +else hsm: $(BOARD_OBJS) $(LIBS) $(LIBHAL_BLD)/libhal.a $(LIBCLI_BLD)/libcli.a .FORCE $(MAKE) -C projects/hsm +endif bootloader: $(BOARD_OBJS) $(LIBS) $(LIBHAL_BLD)/libhal.a .FORCE $(MAKE) -C projects/bootloader @@ -176,3 +188,4 @@ distclean: clean $(MAKE) -C $(MBED_DIR) clean $(MAKE) -C $(LIBTFM_BLD) clean $(MAKE) -C $(LIBCLI_BLD) clean + $(MAKE) -C $(LIBPROF_BLD) clean 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 */ } diff --git a/libraries/mbed/targets/cmsis/TARGET_STM/TARGET_STM32F4/TARGET_CRYPTECH_ALPHA/stm32f4xx_it.c b/libraries/mbed/targets/cmsis/TARGET_STM/TARGET_STM32F4/TARGET_CRYPTECH_ALPHA/stm32f4xx_it.c index 8e5cc73..4629e44 100644 --- a/libraries/mbed/targets/cmsis/TARGET_STM/TARGET_STM32F4/TARGET_CRYPTECH_ALPHA/stm32f4xx_it.c +++ b/libraries/mbed/targets/cmsis/TARGET_STM/TARGET_STM32F4/TARGET_CRYPTECH_ALPHA/stm32f4xx_it.c @@ -64,6 +64,13 @@ void HardFault_Handler(void) while (1) { ; } } +static void default_SysTick_hook(void) { }; +static void (*SysTick_hook)(void) = default_SysTick_hook; +void set_SysTick_hook(void (*hook)(void)) +{ + SysTick_hook = (hook == NULL) ? default_SysTick_hook : hook; +} + /** * @brief This function handles SysTick Handler. * @param None @@ -72,6 +79,7 @@ void HardFault_Handler(void) void SysTick_Handler(void) { HAL_IncTick(); + SysTick_hook(); } /******************************************************************************/ diff --git a/projects/hsm/Makefile b/projects/hsm/Makefile index 927c9f1..6add6a8 100644 --- a/projects/hsm/Makefile +++ b/projects/hsm/Makefile @@ -21,10 +21,19 @@ CFLAGS += -I$(LIBCLI_SRC) LIBS += $(LIBHAL_BLD)/libhal.a $(LIBTFM_BLD)/libtfm.a LIBS += $(LIBCLI_BLD)/libcli.a +LDFLAGS += -mcpu=cortex-m4 -mthumb -mlittle-endian -mthumb-interwork +LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 +LDFLAGS += -Wl,--gc-sections + +ifdef DO_PROFILING +LIBS += $(LIBPROF_BLD)/libprof.a +LDFLAGS += --specs=rdimon.specs -lc -lrdimon +endif + all: $(PROJ:=.elf) %.elf: %.o $(BOARD_OBJS) $(OBJS) $(LIBS) - $(CC) $(CFLAGS) $^ -o $@ -T$(LDSCRIPT) -g -Wl,-Map=$*.map + $(CC) $(LDFLAGS) $^ -o $@ -T$(LDSCRIPT) -g -Wl,-Map=$*.map $(OBJCOPY) -O binary $*.elf $*.bin $(SIZE) $*.elf diff --git a/projects/hsm/mgmt-misc.c b/projects/hsm/mgmt-misc.c index ccd032b..016d7cb 100644 --- a/projects/hsm/mgmt-misc.c +++ b/projects/hsm/mgmt-misc.c @@ -113,6 +113,25 @@ int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_cal return CLI_ERROR; } +#ifdef DO_PROFILING +static int cmd_profile_start(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + extern uint32_t CRYPTECH_FIRMWARE_START; + extern char __etext; /* end of text/code symbol, defined by linker */ + extern void monstartup (size_t lowpc, size_t highpc); + monstartup((size_t)&CRYPTECH_FIRMWARE_START, (size_t)&__etext); + return CLI_OK; +} + +static int cmd_profile_stop(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + extern void _mcleanup(void); + _mcleanup(); + return CLI_OK; +} + +#endif + static int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], int argc) { cli_print(cli, "\n\n\nRebooting\n\n\n"); @@ -124,6 +143,15 @@ static int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], in void configure_cli_misc(struct cli_def *cli) { +#ifdef DO_PROFILING + struct cli_command *c_profile = cli_register_command(cli, NULL, "profile", NULL, 0, 0, NULL); + + /* profile start */ + cli_register_command(cli, c_profile, "start", cmd_profile_start, 0, 0, "Start collecting profiling data"); + + /* profile stop */ + cli_register_command(cli, c_profile, "stop", cmd_profile_stop, 0, 0, "Stop collecting profiling data"); +#endif /* reboot */ cli_register_command(cli, NULL, "reboot", cmd_reboot, 0, 0, "Reboot the STM32"); } diff --git a/syscalls.c b/syscalls.c index d7b7211..1624454 100644 --- a/syscalls.c +++ b/syscalls.c @@ -48,6 +48,7 @@ /***************************************************************************/ +#ifndef DO_PROFILING int _read_r (struct _reent *r, int file, char * ptr, int len) { r = r; @@ -103,6 +104,7 @@ int _close_r (struct _reent *r, int file) { return 0; } +#endif /***************************************************************************/ @@ -143,6 +145,7 @@ caddr_t _sbrk_r (struct _reent *r, int incr) /***************************************************************************/ +#ifndef DO_PROFILING int _fstat_r (struct _reent *r, int file, struct stat * st) { r = r; @@ -181,6 +184,7 @@ int _kill (int a, int b) return 0; } +#endif /***************************************************************************/ @@ -193,6 +197,7 @@ int _getpid(int a) /***************************************************************************/ +#ifndef DO_PROFILING int _open(int a, int b) { a = a; @@ -200,5 +205,6 @@ int _open(int a, int b) return 0; } +#endif /*** EOF ***/ -- cgit v1.2.3