summaryrefslogtreecommitdiff
path: root/libc/sysdeps/linux/common/profil.c
diff options
context:
space:
mode:
authorEric Andersen <andersen@codepoet.org>2003-09-06 13:00:11 +0000
committerEric Andersen <andersen@codepoet.org>2003-09-06 13:00:11 +0000
commit12d9322b4f7b32f6b4c56664c96929767652f66c (patch)
tree444cb1a5b45a6f3cdb3d7b4476a6d5f0768735a0 /libc/sysdeps/linux/common/profil.c
parentcc76cf5876b8adb8b82fd2b3e91efa9cabea2efe (diff)
Add an implementation of profil(), based on the version from
glibc, with several changes for use in uClibc.
Diffstat (limited to 'libc/sysdeps/linux/common/profil.c')
-rw-r--r--libc/sysdeps/linux/common/profil.c113
1 files changed, 113 insertions, 0 deletions
diff --git a/libc/sysdeps/linux/common/profil.c b/libc/sysdeps/linux/common/profil.c
new file mode 100644
index 000000000..3df0836c1
--- /dev/null
+++ b/libc/sysdeps/linux/common/profil.c
@@ -0,0 +1,113 @@
+/* Low-level statistical profiling support function. Mostly POSIX.1 version.
+ Copyright (C) 1996,97,98,2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#ifndef SIGPROF
+
+int profil (u_short *sample_buffer, size_t size, size_t offset, u_int scale)
+{
+ if (scale == 0)
+ /* Disable profiling. */
+ return 0;
+ __set_errno (ENOSYS);
+ return -1;
+}
+
+#else
+
+static u_short *samples;
+static size_t nsamples;
+static size_t pc_offset;
+static u_int pc_scale;
+
+static inline void
+profil_count (void *pc)
+{
+ size_t i = (pc - pc_offset - (void *) 0) / 2;
+ if (sizeof (unsigned long long int) > sizeof (size_t))
+ i = (unsigned long long int) i * pc_scale / 65536;
+ else
+ i = i / 65536 * pc_scale + i % 65536 * pc_scale / 65536;
+ if (i < nsamples)
+ ++samples[i];
+}
+
+/* Get the machine-dependent definition of `profil_counter', the signal
+ handler for SIGPROF. It calls `profil_count' (above) with the PC of the
+ interrupted code. */
+#include <bits/profil-counter.h>
+
+
+/* Enable statistical profiling, writing samples of the PC into at most
+ SIZE bytes of SAMPLE_BUFFER; every processor clock tick while profiling
+ is enabled, the system examines the user PC and increments
+ SAMPLE_BUFFER[((PC - OFFSET) / 2) * SCALE / 65536]. If SCALE is zero,
+ disable profiling. Returns zero on success, -1 on error. */
+int profil (u_short *sample_buffer, size_t size, size_t offset, u_int scale)
+{
+ static struct sigaction oact;
+ static struct itimerval otimer;
+ struct sigaction act;
+ struct itimerval timer;
+
+ if (sample_buffer == NULL)
+ {
+ /* Disable profiling. */
+ if (samples == NULL)
+ /* Wasn't turned on. */
+ return 0;
+
+ if (setitimer (ITIMER_PROF, &otimer, NULL) < 0)
+ return -1;
+ samples = NULL;
+ return sigaction (SIGPROF, &oact, NULL);
+ }
+
+ if (samples)
+ {
+ /* Was already turned on. Restore old timer and signal handler
+ first. */
+ if (setitimer (ITIMER_PROF, &otimer, NULL) < 0
+ || sigaction (SIGPROF, &oact, NULL) < 0)
+ return -1;
+ }
+
+ samples = sample_buffer;
+ nsamples = size / sizeof *samples;
+ pc_offset = offset;
+ pc_scale = scale;
+
+ act.sa_handler = (__sighandler_t) &profil_counter;
+ act.sa_flags = SA_RESTART;
+ __sigfillset (&act.sa_mask);
+ if (sigaction (SIGPROF, &act, &oact) < 0)
+ return -1;
+
+ timer.it_value.tv_sec = 0;
+ timer.it_value.tv_usec = 1;
+ timer.it_interval = timer.it_value;
+ return setitimer (ITIMER_PROF, &timer, &otimer);
+}
+
+#endif