aboutsummaryrefslogtreecommitdiff
path: root/libraries/libprof
diff options
context:
space:
mode:
authorPaul Selkirk <paul@psgd.org>2017-05-04 15:23:10 -0400
committerPaul Selkirk <paul@psgd.org>2017-09-07 18:10:33 -0400
commit9ecd51ab1028e8033057df3117aac27f6f2cd406 (patch)
treea34ff235e49bd72919689a9fc1f1553d6e45de1d /libraries/libprof
parent12585a41bbf124ff8204fc1b538b9796812db747 (diff)
Copy profiling code from MCUOnEclipse.
Diffstat (limited to 'libraries/libprof')
-rw-r--r--libraries/libprof/README.txt6
-rw-r--r--libraries/libprof/gmon.c343
-rw-r--r--libraries/libprof/gmon.h177
-rw-r--r--libraries/libprof/profil.c94
-rw-r--r--libraries/libprof/profil.h60
-rw-r--r--libraries/libprof/profiler.S28
6 files changed, 708 insertions, 0 deletions
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 <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "gmon.h"
+#include "profil.h"
+#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))
+
+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 <profile.h>
+#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 <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <math.h>
+#include "profil.h"
+#include <string.h>
+#include <stdint.h>
+
+/* 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
+