From 9ecd51ab1028e8033057df3117aac27f6f2cd406 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Thu, 4 May 2017 15:23:10 -0400 Subject: Copy profiling code from MCUOnEclipse. --- libraries/libprof/gmon.c | 343 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 libraries/libprof/gmon.c (limited to 'libraries/libprof/gmon.c') diff --git a/libraries/libprof/gmon.c b/libraries/libprof/gmon.c new file mode 100644 index 0000000..2be8bb2 --- /dev/null +++ b/libraries/libprof/gmon.c @@ -0,0 +1,343 @@ +/*- + * Copyright (c) 1983, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This file is taken from Cygwin distribution. Please keep it in sync. + * The differences should be within __MINGW32__ guard. + */ + +#include +#include +#include +#include +#include "gmon.h" +#include "profil.h" +#include +#include + +#define MINUS_ONE_P (-1) +#define bzero(ptr,size) memset (ptr, 0, size); +#define ERR(s) write(2, s, sizeof(s)) + +struct gmonparam _gmonparam = { GMON_PROF_OFF, NULL, 0, NULL, 0, NULL, 0, 0L, 0, 0, 0}; +static char already_setup = 0; /* flag to indicate if we need to init */ +static int s_scale; +/* see profil(2) where this is described (incorrectly) */ +#define SCALE_1_TO_1 0x10000L + +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; + + /* + * 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->kcountsize = p->textsize / HISTFRACTION; + p->fromssize = p->textsize / HASHFRACTION; + p->tolimit = p->textsize * ARCDENSITY / 100; + if (p->tolimit < MINARCS) { + p->tolimit = MINARCS; + } else if (p->tolimit > MAXARCS) { + p->tolimit = MAXARCS; + } + p->tossize = p->tolimit * sizeof(struct tostruct); + + cp = fake_sbrk(p->kcountsize + p->fromssize + p->tossize); + if (cp == (char *)MINUS_ONE_P) { + ERR("monstartup: out of memory\n"); + return; + } + + /* zero out cp as value will be added there */ + bzero(cp, p->kcountsize + p->fromssize + p->tossize); + + p->tos = (struct tostruct *)cp; + cp += p->tossize; + p->kcount = (u_short *)cp; + cp += p->kcountsize; + p->froms = (u_short *)cp; + + p->tos[0].link = 0; + + o = p->highpc - p->lowpc; + if (p->kcountsize < o) { +#ifndef notdef + s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1; +#else /* avoid floating point */ + int quot = o / p->kcountsize; + + if (quot >= 0x10000) + s_scale = 1; + else if (quot >= 0x100) + s_scale = 0x10000 / quot; + else if (o >= 0x800000) + s_scale = 0x1000000 / (o / (p->kcountsize >> 8)); + else + s_scale = 0x1000000 / ((o << 8) / p->kcountsize); +#endif + } else { + s_scale = SCALE_1_TO_1; + } + moncontrol(1); /* start */ +} + +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; + const char *proffile; +#ifdef DEBUG + int log, len; + char dbuf[200]; +#endif + + 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); + if (fd < 0) { + perror( proffile ); + return; + } +#ifdef DEBUG + log = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664); + if (log < 0) { + perror("mcount: gmon.log"); + return; + } + len = sprintf(dbuf, "[mcleanup1] kcount 0x%x ssiz %d\n", + p->kcount, p->kcountsize); + write(log, dbuf, len); +#endif + hdr = (struct gmonhdr *)&gmonhdr; + hdr->lpc = p->lowpc; + hdr->hpc = p->highpc; + hdr->ncnt = p->kcountsize + sizeof(gmonhdr); + hdr->version = GMONVERSION; + hdr->profrate = hz; + write(fd, (char *)hdr, sizeof *hdr); + write(fd, p->kcount, p->kcountsize); + endfrom = p->fromssize / sizeof(*p->froms); + for (fromindex = 0; fromindex < endfrom; fromindex++) { + if (p->froms[fromindex] == 0) { + continue; + } + frompc = p->lowpc; + frompc += fromindex * HASHFRACTION * sizeof(*p->froms); + 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" , + frompc, p->tos[toindex].selfpc, + p->tos[toindex].count); + write(log, dbuf, len); +#endif + rawarc.raw_frompc = frompc; + rawarc.raw_selfpc = p->tos[toindex].selfpc; + rawarc.raw_count = p->tos[toindex].count; + write(fd, &rawarc, sizeof rawarc); + } + } + close(fd); +} + +/* + * Control profiling + * profiling is what mcount checks to see if + * all the data structures are ready. + */ +static void moncontrol(int mode) { + struct gmonparam *p = &_gmonparam; + + if (mode) { + /* start */ + profil((char *)p->kcount, p->kcountsize, p->lowpc, s_scale); + p->state = GMON_PROF_ON; + } else { + /* stop */ + profil((char *)0, 0, 0, 0); + p->state = GMON_PROF_OFF; + } +} + +void _mcount_internal(uint32_t *frompcindex, uint32_t *selfpc) { + register struct tostruct *top; + register struct tostruct *prevtop; + 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. + */ + if (p->state!=GMON_PROF_ON) { + goto out; + } + p->state++; + /* + * check that frompcindex is a reasonable pc value. + * for example: signal catchers get called from the stack, + * not from text space. too bad. + */ + frompcindex = (uint32_t*)((long)frompcindex - (long)p->lowpc); + if ((unsigned long)frompcindex > p->textsize) { + goto done; + } + frompcindex = (uint32_t*)&p->froms[((long)frompcindex) / (HASHFRACTION * sizeof(*p->froms))]; + toindex = *((u_short*)frompcindex); /* get froms[] value */ + if (toindex == 0) { + /* + * first time traversing this arc + */ + toindex = ++p->tos[0].link; /* the link of tos[0] points to the last used record in the array */ + if (toindex >= p->tolimit) { /* more tos[] entries than we can handle! */ + goto overflow; + } + *((u_short*)frompcindex) = (u_short)toindex; /* store new 'to' value into froms[] */ + top = &p->tos[toindex]; + top->selfpc = (size_t)selfpc; + top->count = 1; + top->link = 0; + goto done; + } + top = &p->tos[toindex]; + if (top->selfpc == (size_t)selfpc) { + /* + * arc at front of chain; usual case. + */ + top->count++; + goto done; + } + /* + * have to go looking down chain for it. + * top points to what we are looking at, + * prevtop points to previous top. + * we know it is not at the head of the chain. + */ + for (; /* goto done */; ) { + if (top->link == 0) { + /* + * top is end of the chain and none of the chain + * had top->selfpc == selfpc. + * so we allocate a new tostruct + * and link it to the head of the chain. + */ + toindex = ++p->tos[0].link; + if (toindex >= p->tolimit) { + goto overflow; + } + top = &p->tos[toindex]; + top->selfpc = (size_t)selfpc; + top->count = 1; + top->link = *((u_short*)frompcindex); + *(u_short*)frompcindex = (u_short)toindex; + goto done; + } + /* + * otherwise, check the next arc on the chain. + */ + prevtop = top; + top = &p->tos[top->link]; + if (top->selfpc == (size_t)selfpc) { + /* + * there it is. + * increment its count + * move it to the head of the chain. + */ + top->count++; + toindex = prevtop->link; + prevtop->link = top->link; + top->link = *((u_short*)frompcindex); + *((u_short*)frompcindex) = (u_short)toindex; + goto done; + } + } + done: + p->state--; + /* and fall through */ + out: + return; /* normal return restores saved registers */ + overflow: + p->state++; /* halt further profiling */ + #define TOLIMIT "mcount: tos overflow\n" + write (2, TOLIMIT, sizeof(TOLIMIT)); + goto out; +} + +void _monInit(void) { + _gmonparam.state = GMON_PROF_OFF; + already_setup = 0; +} -- cgit v1.2.3 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/gmon.c | 61 +++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 40 deletions(-) (limited to 'libraries/libprof/gmon.c') 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. -- cgit v1.2.3 From 03d7fa26a89d44349df86e29ac782d075856c570 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Wed, 10 May 2017 00:00:04 -0400 Subject: Sigh, right offset for the wrong register. Get the PC (the address we interrupted) rather than LR (the return address from the function we interrupted). Also, change u_short and u_int to unsigned short and unsigned int, since gcc recently decided that those aren't part of the C99 standard. Finally, add profilable versions of memcpy, memset, and friends, because they get called a lot in the course of unit testing, and it would be nice to know who's calling them. --- libraries/libprof/gmon.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'libraries/libprof/gmon.c') diff --git a/libraries/libprof/gmon.c b/libraries/libprof/gmon.c index 458028b..92054fc 100644 --- a/libraries/libprof/gmon.c +++ b/libraries/libprof/gmon.c @@ -38,7 +38,6 @@ #include #include "gmon.h" #include "profil.h" -#include #include #define bzero(ptr,size) memset (ptr, 0, size); @@ -98,9 +97,9 @@ void monstartup (size_t lowpc, size_t highpc) { p->tos = (struct tostruct *)cp; cp += p->tossize; - p->kcount = (u_short *)cp; + p->kcount = (unsigned short *)cp; cp += p->kcountsize; - p->froms = (u_short *)cp; + p->froms = (unsigned short *)cp; p->tos[0].link = 0; @@ -238,7 +237,7 @@ void _mcount_internal(uint32_t *frompcindex, uint32_t *selfpc) { goto done; } frompcindex = (uint32_t*)&p->froms[((long)frompcindex) / (HASHFRACTION * sizeof(*p->froms))]; - toindex = *((u_short*)frompcindex); /* get froms[] value */ + toindex = *((unsigned short*)frompcindex); /* get froms[] value */ if (toindex == 0) { /* * first time traversing this arc @@ -247,7 +246,7 @@ void _mcount_internal(uint32_t *frompcindex, uint32_t *selfpc) { if (toindex >= p->tolimit) { /* more tos[] entries than we can handle! */ goto overflow; } - *((u_short*)frompcindex) = (u_short)toindex; /* store new 'to' value into froms[] */ + *((unsigned short*)frompcindex) = (unsigned short)toindex; /* store new 'to' value into froms[] */ top = &p->tos[toindex]; top->selfpc = (size_t)selfpc; top->count = 1; @@ -283,8 +282,8 @@ void _mcount_internal(uint32_t *frompcindex, uint32_t *selfpc) { top = &p->tos[toindex]; top->selfpc = (size_t)selfpc; top->count = 1; - top->link = *((u_short*)frompcindex); - *(u_short*)frompcindex = (u_short)toindex; + top->link = *((unsigned short*)frompcindex); + *(unsigned short*)frompcindex = (unsigned short)toindex; goto done; } /* @@ -301,8 +300,8 @@ void _mcount_internal(uint32_t *frompcindex, uint32_t *selfpc) { top->count++; toindex = prevtop->link; prevtop->link = top->link; - top->link = *((u_short*)frompcindex); - *((u_short*)frompcindex) = (u_short)toindex; + top->link = *((unsigned short*)frompcindex); + *((unsigned short*)frompcindex) = (unsigned short)toindex; goto done; } } -- cgit v1.2.3 From f508e24f5b872a8f7d642eb4fb2217dd1497de96 Mon Sep 17 00:00:00 2001 From: Paul Selkirk Date: Thu, 21 Sep 2017 11:24:02 -0400 Subject: cleanup --- libraries/libprof/gmon.c | 1 + 1 file changed, 1 insertion(+) (limited to 'libraries/libprof/gmon.c') diff --git a/libraries/libprof/gmon.c b/libraries/libprof/gmon.c index 92054fc..317a173 100644 --- a/libraries/libprof/gmon.c +++ b/libraries/libprof/gmon.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "gmon.h" #include "profil.h" #include -- cgit v1.2.3