Added monotonic cross-platform timer
This commit is contained in:
parent
c28cff39b8
commit
4a5c19a08c
7 changed files with 117 additions and 12 deletions
|
@ -45,4 +45,5 @@ libcriterion_la_SOURCES = \
|
||||||
src/stats.h \
|
src/stats.h \
|
||||||
src/logging.c \
|
src/logging.c \
|
||||||
src/options.c \
|
src/options.c \
|
||||||
|
src/timer.c \
|
||||||
src/main.c
|
src/main.c
|
||||||
|
|
|
@ -50,8 +50,8 @@ struct event *read_event(int fd) {
|
||||||
return unique_ptr(struct event, ({ .kind = kind, .data = buf }), destroy_event);
|
return unique_ptr(struct event, ({ .kind = kind, .data = buf }), destroy_event);
|
||||||
}
|
}
|
||||||
case POST_TEST: {
|
case POST_TEST: {
|
||||||
float *elapsed_time = malloc(sizeof (float));
|
double *elapsed_time = malloc(sizeof (double));
|
||||||
if (read(fd, elapsed_time, sizeof (float)) < (ssize_t) sizeof (float))
|
if (read(fd, elapsed_time, sizeof (double)) < (ssize_t) sizeof (double))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return unique_ptr(struct event, ({ .kind = kind, .data = elapsed_time }), destroy_event);
|
return unique_ptr(struct event, ({ .kind = kind, .data = elapsed_time }), destroy_event);
|
||||||
|
|
14
src/report.c
14
src/report.c
|
@ -27,6 +27,7 @@
|
||||||
#include "criterion/logging.h"
|
#include "criterion/logging.h"
|
||||||
#include "criterion/options.h"
|
#include "criterion/options.h"
|
||||||
#include "report.h"
|
#include "report.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
#define IMPL_CALL_REPORT_HOOKS(Kind) \
|
#define IMPL_CALL_REPORT_HOOKS(Kind) \
|
||||||
IMPL_SECTION_LIMITS(f_report_hook, crit_ ## Kind); \
|
IMPL_SECTION_LIMITS(f_report_hook, crit_ ## Kind); \
|
||||||
|
@ -57,11 +58,14 @@ ReportHook(PRE_INIT)(struct criterion_test *test) {
|
||||||
|
|
||||||
ReportHook(POST_TEST)(struct criterion_test_stats *stats) {
|
ReportHook(POST_TEST)(struct criterion_test_stats *stats) {
|
||||||
if (criterion_options.enable_tap_format) {
|
if (criterion_options.enable_tap_format) {
|
||||||
criterion_important("%s " SIZE_T_FORMAT " - %s::%s\n",
|
const char *format = can_measure_time() ? "%s " SIZE_T_FORMAT " - %s::%s (%3.2fs)\n"
|
||||||
|
: "%s " SIZE_T_FORMAT " - %s::%s\n";
|
||||||
|
criterion_important(format,
|
||||||
stats->failed ? "not ok" : "ok",
|
stats->failed ? "not ok" : "ok",
|
||||||
tap_test_index++,
|
tap_test_index++,
|
||||||
stats->test->category,
|
stats->test->category,
|
||||||
stats->test->name);
|
stats->test->name,
|
||||||
|
stats->elapsed_time);
|
||||||
for (struct criterion_assert_stats *asrt = stats->asserts; asrt; asrt = asrt->next) {
|
for (struct criterion_assert_stats *asrt = stats->asserts; asrt; asrt = asrt->next) {
|
||||||
if (!asrt->passed) {
|
if (!asrt->passed) {
|
||||||
char *dup = strdup(*asrt->message ? asrt->message : asrt->condition), *saveptr = NULL;
|
char *dup = strdup(*asrt->message ? asrt->message : asrt->condition), *saveptr = NULL;
|
||||||
|
@ -76,11 +80,13 @@ ReportHook(POST_TEST)(struct criterion_test_stats *stats) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const char *format = can_measure_time() ? "%s::%s: %s (%3.2fs)\n" : "%s::%s: %s\n";
|
||||||
criterion_log(stats->failed ? CRITERION_IMPORTANT : CRITERION_INFO,
|
criterion_log(stats->failed ? CRITERION_IMPORTANT : CRITERION_INFO,
|
||||||
"%s::%s: %s\n",
|
format,
|
||||||
stats->test->category,
|
stats->test->category,
|
||||||
stats->test->name,
|
stats->test->name,
|
||||||
stats->failed ? "FAILURE" : "SUCCESS");
|
stats->failed ? "FAILURE" : "SUCCESS",
|
||||||
|
stats->elapsed_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
12
src/runner.c
12
src/runner.c
|
@ -24,7 +24,6 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <time.h>
|
|
||||||
#include <csptr/smart_ptr.h>
|
#include <csptr/smart_ptr.h>
|
||||||
#include "criterion/options.h"
|
#include "criterion/options.h"
|
||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
|
@ -32,6 +31,7 @@
|
||||||
#include "report.h"
|
#include "report.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
IMPL_SECTION_LIMITS(struct criterion_test, criterion_tests);
|
IMPL_SECTION_LIMITS(struct criterion_test, criterion_tests);
|
||||||
|
|
||||||
|
@ -89,11 +89,13 @@ static void run_test_child(struct criterion_test *test) {
|
||||||
(test->data->init ?: nothing)();
|
(test->data->init ?: nothing)();
|
||||||
send_event(PRE_TEST, NULL, 0);
|
send_event(PRE_TEST, NULL, 0);
|
||||||
|
|
||||||
clock_t before = clock();
|
struct timespec_compat ts;
|
||||||
|
timer_start(&ts);
|
||||||
(test->test ?: nothing)();
|
(test->test ?: nothing)();
|
||||||
clock_t after = clock();
|
double elapsed_time;
|
||||||
|
if (!timer_end(&elapsed_time, &ts))
|
||||||
|
elapsed_time = -1;
|
||||||
|
|
||||||
double elapsed_time = (double) (after - before) / CLOCKS_PER_SEC;
|
|
||||||
send_event(POST_TEST, &elapsed_time, sizeof (double));
|
send_event(POST_TEST, &elapsed_time, sizeof (double));
|
||||||
(test->data->fini ?: nothing)();
|
(test->data->fini ?: nothing)();
|
||||||
send_event(POST_FINI, NULL, 0);
|
send_event(POST_FINI, NULL, 0);
|
||||||
|
@ -135,7 +137,7 @@ static void run_test(struct criterion_global_stats *stats, struct criterion_test
|
||||||
stat_push_event(stats, test_stats, &ev);
|
stat_push_event(stats, test_stats, &ev);
|
||||||
report(POST_TEST, test_stats);
|
report(POST_TEST, test_stats);
|
||||||
|
|
||||||
ev.kind = POST_FINI;
|
ev = (struct event) { .kind = POST_FINI, .data = NULL };
|
||||||
stat_push_event(stats, test_stats, &ev);
|
stat_push_event(stats, test_stats, &ev);
|
||||||
report(POST_FINI, test_stats);
|
report(POST_FINI, test_stats);
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ static void push_assert(s_glob_stats *stats,
|
||||||
|
|
||||||
static void push_post_test(s_glob_stats *stats,
|
static void push_post_test(s_glob_stats *stats,
|
||||||
s_test_stats *test,
|
s_test_stats *test,
|
||||||
float *ptr) {
|
double *ptr) {
|
||||||
test->elapsed_time = *ptr;
|
test->elapsed_time = *ptr;
|
||||||
if (test->failed_asserts > 0 || test->signal != test->test->data->signal) {
|
if (test->failed_asserts > 0 || test->signal != test->test->data->signal) {
|
||||||
test->failed = 1;
|
test->failed = 1;
|
||||||
|
|
79
src/timer.c
Normal file
79
src/timer.c
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
#define GIGA 1e9
|
||||||
|
|
||||||
|
#if defined(__unix__)
|
||||||
|
|
||||||
|
# ifdef CLOCK_MONOTONIC_RAW
|
||||||
|
# define CLOCK CLOCK_MONOTONIC_RAW
|
||||||
|
# else
|
||||||
|
# define CLOCK CLOCK_MONOTONIC
|
||||||
|
# endif
|
||||||
|
|
||||||
|
extern __attribute__ ((weak)) int clock_gettime(clockid_t, struct timespec *);
|
||||||
|
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
# include <mach/clock.h>
|
||||||
|
# include <mach/mach.h>
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
# define VC_EXTRALEAN
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool can_measure_time(void) {
|
||||||
|
#ifdef __unix__
|
||||||
|
return clock_gettime != NULL;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int gettime_compat(struct timespec_compat *ts) {
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
clock_serv_t cclock;
|
||||||
|
mach_timespec_t mts;
|
||||||
|
|
||||||
|
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
|
||||||
|
int res = clock_get_time(cclock, &mts);
|
||||||
|
mach_port_deallocate(mach_task_self(), cclock);
|
||||||
|
|
||||||
|
*ts = (struct timespec_compat) { mts->tv_sec, mts->tv_nsec };
|
||||||
|
return res > 0 ? -1 : 0;
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
LARGE_INTEGER freq, count;
|
||||||
|
if (!QueryPerformanceFrequency(&freq)
|
||||||
|
|| !QueryPerformanceCounter(&count))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*ts = (struct timespec_compat) { count / freq, count * (GIGA - 1) / freq };
|
||||||
|
return 0;
|
||||||
|
#elif defined(__unix__)
|
||||||
|
if (!can_measure_time()) {
|
||||||
|
errno = ENOTSUP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timespec ts_;
|
||||||
|
int res = clock_gettime(CLOCK, &ts_);
|
||||||
|
|
||||||
|
*ts = (struct timespec_compat) { ts_.tv_sec, ts_.tv_nsec };
|
||||||
|
return res;
|
||||||
|
#else
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int timer_start(struct timespec_compat *state) {
|
||||||
|
return gettime_compat(state) == -1 ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int timer_end(double *time, struct timespec_compat *state) {
|
||||||
|
struct timespec_compat last;
|
||||||
|
if (gettime_compat(&last) == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*time = (last.tv_sec - state->tv_sec) + (last.tv_nsec - state->tv_nsec) / GIGA;
|
||||||
|
return 1;
|
||||||
|
}
|
17
src/timer.h
Normal file
17
src/timer.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef TIMER_H_
|
||||||
|
# define TIMER_H_
|
||||||
|
|
||||||
|
# include <time.h>
|
||||||
|
# include <inttypes.h>
|
||||||
|
# include <stdbool.h>
|
||||||
|
|
||||||
|
struct timespec_compat {
|
||||||
|
int64_t tv_sec;
|
||||||
|
int64_t tv_nsec;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool can_measure_time(void);
|
||||||
|
int timer_start(struct timespec_compat *state);
|
||||||
|
int timer_end(double *time, struct timespec_compat *state);
|
||||||
|
|
||||||
|
#endif /* !TIMER_H_ */
|
Loading…
Add table
Reference in a new issue