summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac20
-rw-r--r--foo.c4
-rw-r--r--src/Makefile.am4
-rw-r--r--src/sandglass.c249
-rw-r--r--src/sandglass.h13
-rw-r--r--src/sandglass_impl.h39
-rw-r--r--src/tsc.c39
-rw-r--r--src/x86/tsc-x86.s17
-rw-r--r--src/x86_64/tsc-x86_64.s17
-rwxr-xr-xtests/simple-test2
-rw-r--r--tests/simple.c27
11 files changed, 416 insertions, 15 deletions
diff --git a/configure.ac b/configure.ac
index 044ea79..deea2b8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,8 +17,8 @@ dnl License along with this program. If not, see
dnl <http://www.gnu.org/licenses/>.
AC_PREREQ(2.63)
-AC_INIT(The Sandglass Library, 0.0.0, tavianator@gmail.com, libsandglass)
-AM_INIT_AUTOMAKE(libsandglass, 0.0.0)
+AC_INIT(The Sandglass Library, 0.0, tavianator@gmail.com, libsandglass)
+AM_INIT_AUTOMAKE(libsandglass, 0.0)
dnl Programs
AC_PROG_CC
@@ -39,8 +39,20 @@ AC_TYPE_SIZE_T
dnl Find out which assembly files to compile
AC_CANONICAL_HOST
-AM_CONDITIONAL([X86], [echo $host_cpu | grep '.*86$' >/dev/null])
-AM_CONDITIONAL([X86_64], [echo $host_cpu | grep 'x86_64$' >/dev/null])
+if echo $host_cpu | grep '86$' 2>&1 >/dev/null
+then
+ arch_x86="true"
+ AC_DEFINE([SANDGLASS_TSC])
+fi
+
+if echo $host_cpu | grep 'x86_64$' 2>&1 >/dev/null
+then
+ arch_x86_64="true"
+ AC_DEFINE([SANDGLASS_TSC])
+fi
+
+AM_CONDITIONAL([X86], [test "$arch_x86"])
+AM_CONDITIONAL([X86_64], [test "$arch_x86_64"])
dnl Generate Makefiles
AC_CONFIG_MACRO_DIR([m4])
diff --git a/foo.c b/foo.c
new file mode 100644
index 0000000..9bdde01
--- /dev/null
+++ b/foo.c
@@ -0,0 +1,4 @@
+
+#ifndef PRED
+#error HI
+#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index 4fc85ab..476c855 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,7 +21,7 @@ lib_LTLIBRARIES = libsandglass.la
nobase_include_HEADERS = sandglass.h
-libsandglass_la_SOURCES = sandglass.h sandglass.c
+libsandglass_la_SOURCES = sandglass.h sandglass_impl.h sandglass.c tsc.c
if X86
libsandglass_la_SOURCES += x86/tsc-x86.s
@@ -30,3 +30,5 @@ endif
if X86_64
libsandglass_la_SOURCES += x86_64/tsc-x86_64.s
endif
+
+libsandglass_la_LIBADD = -lrt
diff --git a/src/sandglass.c b/src/sandglass.c
index 4acf983..57729f9 100644
--- a/src/sandglass.c
+++ b/src/sandglass.c
@@ -18,4 +18,253 @@
* <http://www.gnu.org/licenses/>. *
*************************************************************************/
+#include "sandglass_impl.h"
#include "sandglass.h"
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+static int sandglass_real_create(sandglass_t *sandglass,
+ const sandglass_attributes_t *attr);
+
+/* Create a timer */
+int
+sandglass_create(sandglass_t *sandglass,
+ const sandglass_attributes_t *min,
+ const sandglass_attributes_t *max)
+{
+ sandglass_attributes_t realmin, realmax;
+
+ /* Get our real min and max values */
+
+ if (min)
+ realmin = *min;
+ else {
+ /* min defaults to { SANDGLASS_INTROSPECTIVE, SANDGLASS_SYSTEM } */
+ realmin.incrementation = SANDGLASS_INTROSPECTIVE;
+ realmin.resolution = SANDGLASS_SYSTEM;
+ }
+
+ if (max)
+ realmax = *max;
+ else {
+ /* max defaults to the greater of min and { SANDGLASS_INTROSPECTIVE,
+ SANDGLASS_CPUTIME } */
+ if (realmin.incrementation > SANDGLASS_INTROSPECTIVE
+ || (realmin.incrementation == SANDGLASS_INTROSPECTIVE
+ && realmin.resolution > SANDGLASS_CPUTIME))
+ realmax = realmin;
+ else {
+ realmax.incrementation = SANDGLASS_INTROSPECTIVE;
+ realmax.resolution = SANDGLASS_CPUTIME;
+ }
+ }
+
+ /* Ensure max >= min */
+ if (realmax.incrementation < realmin.incrementation
+ || (realmax.incrementation == realmin.incrementation
+ && realmax.resolution < realmin.resolution))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Now search for available timers, starting from max */
+
+ while (sandglass_real_create(sandglass, &realmax) != 0) {
+ /* Once we reach the minimum attributes, bail out */
+ if (realmax.incrementation == realmin.incrementation
+ && realmax.resolution == realmin.resolution)
+ {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ /* Try the next lowest allowable settings */
+ if (realmax.resolution)
+ --realmax.resolution;
+ else {
+ if (realmax.incrementation) {
+ --realmax.incrementation;
+ realmax.resolution = SANDGLASS_CPUTIME;
+ } else {
+ errno = ENOTSUP;
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+sandglass_real_create(sandglass_t *sandglass,
+ const sandglass_attributes_t *attr)
+{
+ switch (attr->incrementation) {
+ case SANDGLASS_MONOTONIC:
+ switch (attr->resolution) {
+ case SANDGLASS_REALTICKS:
+#ifdef SANDGLASS_TSC
+ sandglass->resolution = sandglass_tsc_resolution();
+ sandglass->loops = sandglass_tsc_loops();
+ break;
+#else
+ return -1;
+#endif
+
+ case SANDGLASS_CPUTIME:
+#ifdef SANDGLASS_TSC
+ sandglass->resolution = sandglass_tsc_resolution();
+ sandglass->loops = 1;
+ break;
+#else
+ return -1;
+#endif
+
+ case SANDGLASS_SYSTEM:
+ sandglass->resolution = 1e9;
+ sandglass->loops = 1;
+ break;
+
+ default:
+ return -1;
+ }
+ break;
+
+ case SANDGLASS_INTROSPECTIVE:
+ switch (attr->resolution) {
+ case SANDGLASS_REALTICKS:
+ /* No such thing as an introspective raw TSC */
+ return -1;
+
+ case SANDGLASS_CPUTIME:
+ if (sysconf(_SC_THREAD_CPUTIME) > 0 || sysconf(_SC_CPUTIME) > 0) {
+ sandglass->resolution = 1e9;
+ sandglass->loops = 1;
+ } else
+ return -1;
+ break;
+
+ case SANDGLASS_SYSTEM:
+ sandglass->resolution = CLOCKS_PER_SEC;
+ sandglass->loops = 1;
+ break;
+
+ default:
+ return -1;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ sandglass->attributes = *attr;
+ return 0;
+}
+
+/* Store a timer value in sandglass->grains */
+static int sandglass_real_gettime(sandglass_t *sandglass);
+
+/* Start timing */
+int
+sandglass_begin(sandglass_t *sandglass)
+{
+ return sandglass_real_gettime(sandglass);
+}
+
+/* Finish timing */
+int
+sandglass_elapse(sandglass_t *sandglass)
+{
+ long oldgrains = sandglass->grains;
+
+ if (sandglass_real_gettime(sandglass) != 0)
+ return -1;
+
+ sandglass->grains -= oldgrains;
+ sandglass->grains /= sandglass->loops;
+
+ return 0;
+}
+
+/* Store a timer value in sandglass->grains */
+static int
+sandglass_real_gettime(sandglass_t *sandglass)
+{
+ struct timespec ts;
+ clock_t clock_ticks;
+
+ switch (sandglass->attributes.incrementation) {
+ case SANDGLASS_MONOTONIC:
+ switch (sandglass->attributes.resolution) {
+ case SANDGLASS_REALTICKS:
+#ifdef SANDGLASS_TSC
+ sandglass->grains = sandglass_get_tsc();
+ break;
+#else
+ return -1;
+#endif
+
+ case SANDGLASS_CPUTIME:
+#ifdef SANDGLASS_TSC
+ sandglass->grains = sandglass_get_tsc();
+ break;
+#else
+ return -1;
+#endif
+
+ case SANDGLASS_SYSTEM:
+ if (sysconf(_SC_MONOTONIC_CLOCK) > 0) {
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ return -1;
+ sandglass->grains = ts.tv_nsec;
+ } else {
+ if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
+ return -1;
+ sandglass->grains = ts.tv_nsec;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+ break;
+
+ case SANDGLASS_INTROSPECTIVE:
+ switch (sandglass->attributes.resolution) {
+ case SANDGLASS_REALTICKS:
+ /* No such thing as an introspective raw TSC */
+ return -1;
+
+ case SANDGLASS_CPUTIME:
+ if (sysconf(_SC_THREAD_CPUTIME) > 0) {
+ if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0)
+ return -1;
+ sandglass->grains = ts.tv_nsec;
+ } else if (sysconf(_SC_CPUTIME) > 0) {
+ if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) != 0)
+ return -1;
+ sandglass->grains = ts.tv_nsec;
+ } else
+ return -1;
+ break;
+
+ case SANDGLASS_SYSTEM:
+ if ((clock_ticks = clock()) == -1)
+ return -1;
+ sandglass->grains = clock();
+ break;
+
+ default:
+ return -1;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/sandglass.h b/src/sandglass.h
index a9e99e5..3cd14ba 100644
--- a/src/sandglass.h
+++ b/src/sandglass.h
@@ -93,7 +93,7 @@ struct sandglass_t
long grains;
/* grains/resolution should give elapsed time in seconds */
- long resolution;
+ double resolution;
/*
* Internal fields
@@ -140,20 +140,15 @@ int sandglass_elapse(sandglass_t *sandglass);
do { \
routine; \
sandglass_begin(sandglass); \
- for ((sandglass)->i = 0; (sandglass)->i < (sandglass)->loops; ++i) { \
+ for ((sandglass)->i = 0; \
+ (sandglass)->i < (sandglass)->loops; \
+ ++(sandglass)->i) { \
SANDGLASS_NO_UNROLL(); \
routine; \
} \
sandglass_elapse(sandglass); \
} while (0)
-/*
- * Low-level API
- */
-
-/* Read the time stamp counter */
-long sandglass_get_tsc();
-
#ifdef __cplusplus
}
#endif
diff --git a/src/sandglass_impl.h b/src/sandglass_impl.h
new file mode 100644
index 0000000..5a3650e
--- /dev/null
+++ b/src/sandglass_impl.h
@@ -0,0 +1,39 @@
+/*************************************************************************
+ * Copyright (C) 2008 Tavian Barnes <tavianator@gmail.com> *
+ * *
+ * This file is part of The Sandglass Library. *
+ * *
+ * The Sandglass 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 *
+ * 3 of the License, or (at your option) any later version. *
+ * *
+ * The Sandglass 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 this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+/*
+ * Internal libsandglass API
+ */
+
+#ifndef SANDGLASS_IMPL_H_INCLUDED
+#define SANDGLASS_IMPL_H_INCLUDED
+
+#include "sandglass.h"
+
+#ifdef SANDGLASS_TSC
+/* Read the time stamp counter */
+long sandglass_get_tsc();
+/* Get the timing resolution of the TSC */
+double sandglass_tsc_resolution();
+/* Get the necessary number of loops for SANDGLASS_REALTICKS */
+unsigned int sandglass_tsc_loops();
+#endif
+
+#endif /* SANDGLASS_IMPL_H_INCLUDED */ \ No newline at end of file
diff --git a/src/tsc.c b/src/tsc.c
new file mode 100644
index 0000000..64fe1b1
--- /dev/null
+++ b/src/tsc.c
@@ -0,0 +1,39 @@
+/*************************************************************************
+ * Copyright (C) 2008 Tavian Barnes <tavianator@gmail.com> *
+ * *
+ * This file is part of The Sandglass Library. *
+ * *
+ * The Sandglass 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 *
+ * 3 of the License, or (at your option) any later version. *
+ * *
+ * The Sandglass 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 this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+#include "sandglass_impl.h"
+#include "sandglass.h"
+#include <time.h>
+
+/* Gets the number of clock ticks per second */
+double
+sandglass_tsc_resolution()
+{
+ static long tsc = 0;
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 };
+
+ if (tsc == 0) {
+ tsc = sandglass_get_tsc();
+ while (nanosleep(&ts, &ts) != 0);
+ tsc = sandglass_get_tsc() - tsc;
+ }
+
+ return tsc*10.0;
+}
diff --git a/src/x86/tsc-x86.s b/src/x86/tsc-x86.s
index 570b8af..46013bb 100644
--- a/src/x86/tsc-x86.s
+++ b/src/x86/tsc-x86.s
@@ -38,3 +38,20 @@ sandglass_get_tsc:
popl %ebx
ret
.size sandglass_get_tsc, .-sandglass_get_tsc
+
+/*
+ * Return the granularity of the TSC
+ */
+
+/* unsigned int sandglass_tsc_loops(); */
+.globl sandglass_tsc_loops
+ .type sandglass_tsc_loops, @function
+sandglass_tsc_loops:
+ rdtsc /* Read time stamp counter */
+ movl %eax, %ecx
+.Lrdtsc:
+ rdtsc /* Read counter again */
+ subl %ecx, %eax
+ jz .Lrdtsc /* If we got the same value, try again */
+ ret
+ .size sandglass_tsc_loops, .-sandglass_tsc_loops
diff --git a/src/x86_64/tsc-x86_64.s b/src/x86_64/tsc-x86_64.s
index c585719..52f02c2 100644
--- a/src/x86_64/tsc-x86_64.s
+++ b/src/x86_64/tsc-x86_64.s
@@ -38,3 +38,20 @@ sandglass_get_tsc:
movq %rdi, %rbx
ret
.size sandglass_get_tsc, .-sandglass_get_tsc
+
+/*
+ * Return the granularity of the TSC
+ */
+
+/* unsigned int sandglass_tsc_loops(); */
+.globl sandglass_tsc_loops
+ .type sandglass_tsc_loops, @function
+sandglass_tsc_loops:
+ rdtsc /* Read time stamp counter */
+ movl %eax, %ecx
+.Lrdtsc:
+ rdtsc /* Read counter again */
+ subl %ecx, %eax
+ jz .Lrdtsc /* If we got the same value, try again */
+ ret
+ .size sandglass_tsc_loops, .-sandglass_tsc_loops
diff --git a/tests/simple-test b/tests/simple-test
index 407febc..8fbcf16 100755
--- a/tests/simple-test
+++ b/tests/simple-test
@@ -32,7 +32,7 @@ DUALCASE=1; export DUALCASE # for MKS sh
# if CDPATH is set.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-relink_command="(cd /home/tavianator/libsandglass/tests; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/java/bin:/opt/java/jre/bin:/home/tavianator/bin:/usr/bin/perlbin/site:/usr/bin/perlbin/vendor:/usr/bin/perlbin/core:/opt/qt/bin; export PATH; gcc -g -Wall -o \$progdir/\$file simple.o ../src/.libs/libsandglass.so -Wl,-rpath -Wl,/home/tavianator/libsandglass/src/.libs)"
+relink_command="(cd /home/tavianator/libsandglass/tests; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/java/bin:/opt/java/jre/bin:/home/tavianator/bin:/usr/bin/perlbin/site:/usr/bin/perlbin/vendor:/usr/bin/perlbin/core:/opt/qt/bin; export PATH; gcc -g -Wall -o \$progdir/\$file simple.o ../src/.libs/libsandglass.so -lrt -Wl,-rpath -Wl,/home/tavianator/libsandglass/src/.libs)"
# This environment variable determines our operation mode.
if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
diff --git a/tests/simple.c b/tests/simple.c
index 921ab6c..85dacb6 100644
--- a/tests/simple.c
+++ b/tests/simple.c
@@ -19,10 +19,37 @@
*************************************************************************/
#include <sandglass.h>
+#include <unistd.h>
+#include <time.h>
#include <stdlib.h>
+#include <stdio.h>
int
main()
{
+ sandglass_t sandglass;
+ sandglass_attributes_t min = { SANDGLASS_MONOTONIC, SANDGLASS_SYSTEM },
+ max = { SANDGLASS_MONOTONIC, SANDGLASS_CPUTIME };
+ struct timespec tosleep = { .tv_sec = 0, .tv_nsec = 100000000 };
+
+ if (sandglass_create(&sandglass, &min, &max) != 0) {
+ perror("sandglass_create()");
+ return EXIT_FAILURE;
+ }
+
+ if (sandglass_begin(&sandglass) != 0) {
+ perror("sandglass_begin()");
+ return EXIT_FAILURE;
+ }
+ while (nanosleep(&tosleep, &tosleep) != 0);
+ if (sandglass_elapse(&sandglass) != 0) {
+ perror("sandglass_elapse()");
+ return EXIT_FAILURE;
+ }
+
+ printf("0.1 seconds timed by sandglass as %ld grains; %g s\n",
+ sandglass.grains,
+ sandglass.grains/sandglass.resolution);
+
return EXIT_SUCCESS;
}