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/README.txt | 6 + libraries/libprof/gmon.c | 343 +++++++++++++++++++++++++++++++++++++++++++ libraries/libprof/gmon.h | 177 ++++++++++++++++++++++ libraries/libprof/profil.c | 94 ++++++++++++ libraries/libprof/profil.h | 60 ++++++++ libraries/libprof/profiler.S | 28 ++++ 6 files changed, 708 insertions(+) create mode 100644 libraries/libprof/README.txt create mode 100644 libraries/libprof/gmon.c create mode 100644 libraries/libprof/gmon.h create mode 100644 libraries/libprof/profil.c create mode 100644 libraries/libprof/profil.h create mode 100644 libraries/libprof/profiler.S diff --git a/libraries/libprof/README.txt b/libraries/libprof/README.txt new file mode 100644 index 0000000..2df8b96 --- /dev/null +++ b/libraries/libprof/README.txt @@ -0,0 +1,6 @@ +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.) + 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; +} diff --git a/libraries/libprof/gmon.h b/libraries/libprof/gmon.h new file mode 100644 index 0000000..5eb5180 --- /dev/null +++ b/libraries/libprof/gmon.h @@ -0,0 +1,177 @@ +/* $OpenBSD: gmon.h,v 1.3 1996/04/21 22:31:46 deraadt Exp $ */ +/* $NetBSD: gmon.h,v 1.5 1996/04/09 20:55:30 cgd Exp $ */ + +/*- + * Copyright (c) 1982, 1986, 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. + * + * @(#)gmon.h 8.2 (Berkeley) 1/4/94 + */ + +/* + * This file is taken from Cygwin distribution. Please keep it in sync. + * The differences should be within __MINGW32__ guard. + */ + +#ifndef _SYS_GMON_H_ +#define _SYS_GMON_H_ + +#ifndef __P +#define __P(x) x +#endif + +/* On POSIX systems, profile.h is a KRB5 header. To avoid collisions, just + pull in profile.h's content here. The profile.h header won't be provided + by Mingw-w64 anymore at one point. */ +#if 0 +#include +#else +#ifndef _WIN64 +#define _MCOUNT_CALL __attribute__ ((regparm (2))) +extern void _mcount(void); +#else +#define _MCOUNT_CALL +extern void mcount(void); +#endif +#define _MCOUNT_DECL __attribute__((gnu_inline)) __inline__ \ + void _MCOUNT_CALL _mcount_private +#define MCOUNT +#endif + +/* + * Structure prepended to gmon.out profiling data file. + */ +struct gmonhdr { + size_t lpc; /* base pc address of sample buffer */ + size_t hpc; /* max pc address of sampled buffer */ + int ncnt; /* size of sample buffer (plus this header) */ + int version; /* version number */ + int profrate; /* profiling clock rate */ + int spare[3]; /* reserved */ +}; +#define GMONVERSION 0x00051879 + +/* + * histogram counters are unsigned shorts (according to the kernel). + */ +#define HISTCOUNTER unsigned short + +/* + * fraction of text space to allocate for histogram counters here, 1/2 + */ +#define HISTFRACTION 2 + +/* + * Fraction of text space to allocate for from hash buckets. + * The value of HASHFRACTION is based on the minimum number of bytes + * of separation between two subroutine call points in the object code. + * Given MIN_SUBR_SEPARATION bytes of separation the value of + * HASHFRACTION is calculated as: + * + * HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1); + * + * For example, on the VAX, the shortest two call sequence is: + * + * calls $0,(r0) + * calls $0,(r0) + * + * which is separated by only three bytes, thus HASHFRACTION is + * calculated as: + * + * HASHFRACTION = 3 / (2 * 2 - 1) = 1 + * + * Note that the division above rounds down, thus if MIN_SUBR_FRACTION + * is less than three, this algorithm will not work! + * + * In practice, however, call instructions are rarely at a minimal + * distance. Hence, we will define HASHFRACTION to be 2 across all + * architectures. This saves a reasonable amount of space for + * profiling data structures without (in practice) sacrificing + * any granularity. + */ +#define HASHFRACTION 2 + +/* + * percent of text space to allocate for tostructs with a minimum. + */ +#define ARCDENSITY 2 /* this is in percentage, relative to text size! */ +#define MINARCS 50 +#define MAXARCS ((1 << (8 * sizeof(HISTCOUNTER))) - 2) + +struct tostruct { + size_t selfpc; /* callee address/program counter. The caller address is in froms[] array which points to tos[] array */ + long count; /* how many times it has been called */ + u_short link; /* link to next entry in hash table. For tos[0] this points to the last used entry */ + u_short pad; /* additional padding bytes, to have entries 4byte aligned */ +}; + +/* + * a raw arc, with pointers to the calling site and + * the called site and a count. + */ +struct rawarc { + size_t raw_frompc; + size_t raw_selfpc; + long raw_count; +}; + +/* + * general rounding functions. + */ +#define ROUNDDOWN(x,y) (((x)/(y))*(y)) +#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y)) + +/* + * The profiling data structures are housed in this structure. + */ +struct gmonparam { + int state; + u_short *kcount; /* histogram PC sample array */ + size_t kcountsize; /* size of kcount[] array in bytes */ + u_short *froms; /* array of hashed 'from' addresses. The 16bit value is an index into the tos[] array */ + size_t fromssize; /* size of froms[] array in bytes */ + struct tostruct *tos; /* to struct, contains histogram counter */ + size_t tossize; /* size of tos[] array in bytes */ + long tolimit; + size_t lowpc; /* low program counter of area */ + size_t highpc; /* high program counter */ + size_t textsize; /* code size */ +}; +extern struct gmonparam _gmonparam; + +/* + * Possible states of profiling. + */ +#define GMON_PROF_ON 0 +#define GMON_PROF_BUSY 1 +#define GMON_PROF_ERROR 2 +#define GMON_PROF_OFF 3 + +void _mcleanup(void); /* routine to be called to write gmon.out file */ +void _monInit(void); /* initialization routine */ + +#endif /* !_SYS_GMONH_ */ diff --git a/libraries/libprof/profil.c b/libraries/libprof/profil.c new file mode 100644 index 0000000..24ede21 --- /dev/null +++ b/libraries/libprof/profil.c @@ -0,0 +1,94 @@ +/* profil.c -- win32 profil.c equivalent + + Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +/* + * This file is taken from Cygwin distribution, adopted to be used for bare embeeded targets. + */ +#include +#include +#include +#include +#include "profil.h" +#include +#include + +/* 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; + + 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); + prof.counter[idx]++; + } + } +} + +/* Stop profiling to the profiling buffer pointed to by p. */ +static int profile_off (struct profinfo *p) { + 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) { + p->state = PROFILE_ON; + return 0; /* ok */ +} + +/* + * start or stop profiling + * + * profiling goes into the SAMPLES buffer of size SIZE (which is treated + * as an array of u_shorts of size size/2) + * + * each bin represents a range of pc addresses from OFFSET. The number + * of pc addresses in a bin depends on SCALE. (A scale of 65536 maps + * each bin to two addresses, A scale of 32768 maps each bin to 4 addresses, + * a scale of 1 maps each bin to 128k address). Scale may be 1 - 65536, + * or zero to turn off profiling + */ +int profile_ctl (struct profinfo *p, char *samples, size_t size, size_t offset, u_int scale) { + size_t maxbin; + + if (scale > 65536) { + errno = EINVAL; + return -1; + } + profile_off(p); + if (scale) { + memset(samples, 0, size); + memset(p, 0, sizeof *p); + maxbin = size >> 1; + prof.counter = (u_short*)samples; + prof.lowpc = offset; + prof.highpc = PROFADDR(maxbin, offset, scale); + prof.scale = scale; + return profile_on(p); + } + return 0; +} + +/* Equivalent to unix profil() + Every SLEEPTIME interval, the user's program counter (PC) is examined: + offset is subtracted and the result is multiplied by scale. + The word pointed to by this address is incremented. */ +int profil (char *samples, size_t size, size_t offset, u_int scale) { + return profile_ctl (&prof, samples, size, offset, scale); +} + diff --git a/libraries/libprof/profil.h b/libraries/libprof/profil.h new file mode 100644 index 0000000..af7a3ed --- /dev/null +++ b/libraries/libprof/profil.h @@ -0,0 +1,60 @@ +/* profil.h: gprof profiling header file + + Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +/* + * This file is taken from Cygwin distribution. Please keep it in sync. + * The differences should be within __MINGW32__ guard. + */ + +#ifndef __PROFIL_H__ +#define __PROFIL_H__ + +/* profiling frequency. (No larger than 1000) */ +#define PROF_HZ 1000 + +/* convert an addr to an index */ +#define PROFIDX(pc, base, scale) \ + ({ \ + size_t i = (pc - base) / 2; \ + if (sizeof (unsigned long long int) > sizeof (size_t)) \ + i = (unsigned long long int) i * scale / 65536; \ + else \ + i = i / 65536 * scale + i % 65536 * scale / 65536; \ + i; \ + }) + +/* convert an index into an address */ +#define PROFADDR(idx, base, scale) \ + ((base) \ + + ((((unsigned long long)(idx) << 16) \ + / (unsigned long long)(scale)) << 1)) + +/* convert a bin size into a scale */ +#define PROFSCALE(range, bins) (((bins) << 16) / ((range) >> 1)) + +typedef void *_WINHANDLE; + +typedef enum { + PROFILE_NOT_INIT = 0, + PROFILE_ON, + PROFILE_OFF +} PROFILE_State; + +struct profinfo { + PROFILE_State state; /* profiling state */ + u_short *counter; /* profiling counters */ + size_t lowpc, highpc; /* range to be profiled */ + u_int scale; /* scale value of bins */ +}; + +int profile_ctl(struct profinfo *, char *, size_t, size_t, u_int); +int profil(char *, size_t, size_t, u_int); + +#endif /* __PROFIL_H__ */ diff --git a/libraries/libprof/profiler.S b/libraries/libprof/profiler.S new file mode 100644 index 0000000..1aa5c97 --- /dev/null +++ b/libraries/libprof/profiler.S @@ -0,0 +1,28 @@ +/* + * profiler.S + * Implements the gprof profiler arc counting function. + * Created on: 06.08.2015 + * Author: Erich Styger + */ + + .syntax unified + .arch armv7-m + +.globl __gnu_mcount_nc +.type __gnu_mcount_nc, %function + +__gnu_mcount_nc: +#if 0 /* dummy version, doing nothing */ + mov ip, lr + pop { lr } + bx ip +#else + push {r0, r1, r2, r3, lr} /* save registers */ + bic r1, lr, #1 /* R1 contains callee address, with thumb bit cleared */ + ldr r0, [sp, #20] /* R0 contains caller address */ + bic r0, r0, #1 /* clear thumb bit */ + bl _mcount_internal /* jump to internal _mcount() implementation */ + pop {r0, r1, r2, r3, ip, lr} /* restore saved registers */ + bx ip /* return to caller */ +#endif + -- cgit v1.2.3