Commit 31cdabf6 by Dominic Hamon

Merge pull request #216 from efcs/diagnostic-checks

Add checks that <Resume|Pause>Timing functions are not called outside of the KeepRunning() loop. Fixes #204
parents 9341d705 029f3744
...@@ -242,22 +242,25 @@ enum TimeUnit { ...@@ -242,22 +242,25 @@ enum TimeUnit {
// benchmark to use. // benchmark to use.
class State { class State {
public: public:
State(size_t max_iters, bool has_x, int x, bool has_y, int y, int thread_i, int n_threads); State(size_t max_iters, bool has_x, int x, bool has_y, int y,
int thread_i, int n_threads);
// Returns true iff the benchmark should continue through another iteration. // Returns true iff the benchmark should continue through another iteration.
// NOTE: A benchmark may not return from the test until KeepRunning() has // NOTE: A benchmark may not return from the test until KeepRunning() has
// returned false. // returned false.
bool KeepRunning() { bool KeepRunning() {
if (BENCHMARK_BUILTIN_EXPECT(!started_, false)) { if (BENCHMARK_BUILTIN_EXPECT(!started_, false)) {
ResumeTiming(); assert(!finished_);
started_ = true; started_ = true;
ResumeTiming();
} }
bool const res = total_iterations_++ < max_iterations; bool const res = total_iterations_++ < max_iterations;
if (BENCHMARK_BUILTIN_EXPECT(!res, false)) { if (BENCHMARK_BUILTIN_EXPECT(!res, false)) {
assert(started_); assert(started_ && !finished_);
PauseTiming(); PauseTiming();
// Total iterations now is one greater than max iterations. Fix this. // Total iterations now is one greater than max iterations. Fix this.
total_iterations_ = max_iterations; total_iterations_ = max_iterations;
finished_ = true;
} }
return res; return res;
} }
...@@ -378,6 +381,7 @@ public: ...@@ -378,6 +381,7 @@ public:
private: private:
bool started_; bool started_;
bool finished_;
size_t total_iterations_; size_t total_iterations_;
bool has_range_x_; bool has_range_x_;
......
...@@ -815,7 +815,7 @@ void RunBenchmark(const benchmark::internal::Benchmark::Instance& b, ...@@ -815,7 +815,7 @@ void RunBenchmark(const benchmark::internal::Benchmark::Instance& b,
State::State(size_t max_iters, bool has_x, int x, bool has_y, int y, State::State(size_t max_iters, bool has_x, int x, bool has_y, int y,
int thread_i, int n_threads) int thread_i, int n_threads)
: started_(false), total_iterations_(0), : started_(false), finished_(false), total_iterations_(0),
has_range_x_(has_x), range_x_(x), has_range_x_(has_x), range_x_(x),
has_range_y_(has_y), range_y_(y), has_range_y_(has_y), range_y_(y),
bytes_processed_(0), items_processed_(0), bytes_processed_(0), items_processed_(0),
...@@ -830,11 +830,13 @@ State::State(size_t max_iters, bool has_x, int x, bool has_y, int y, ...@@ -830,11 +830,13 @@ State::State(size_t max_iters, bool has_x, int x, bool has_y, int y,
void State::PauseTiming() { void State::PauseTiming() {
// Add in time accumulated so far // Add in time accumulated so far
CHECK(running_benchmark); CHECK(running_benchmark);
CHECK(started_ && !finished_);
timer_manager->StopTimer(); timer_manager->StopTimer();
} }
void State::ResumeTiming() { void State::ResumeTiming() {
CHECK(running_benchmark); CHECK(running_benchmark);
CHECK(started_ && !finished_);
timer_manager->StartTimer(); timer_manager->StartTimer();
} }
......
...@@ -10,6 +10,18 @@ ...@@ -10,6 +10,18 @@
namespace benchmark { namespace benchmark {
namespace internal { namespace internal {
typedef void(AbortHandlerT)();
inline AbortHandlerT*& GetAbortHandler() {
static AbortHandlerT* handler = &std::abort;
return handler;
}
BENCHMARK_NORETURN inline void CallAbortHandler() {
GetAbortHandler()();
std::abort(); // fallback to enforce noreturn
}
// CheckHandler is the class constructed by failing CHECK macros. CheckHandler // CheckHandler is the class constructed by failing CHECK macros. CheckHandler
// will log information about the failures and abort when it is destructed. // will log information about the failures and abort when it is destructed.
class CheckHandler { class CheckHandler {
...@@ -25,13 +37,13 @@ public: ...@@ -25,13 +37,13 @@ public:
return log_; return log_;
} }
BENCHMARK_NORETURN ~CheckHandler() { BENCHMARK_NORETURN ~CheckHandler() noexcept(false) {
log_ << std::endl; log_ << std::endl;
std::abort(); CallAbortHandler();
} }
CheckHandler & operator=(const CheckHandler&) = delete; CheckHandler & operator=(const CheckHandler&) = delete;
CheckHandler(const CheckHandler&) = delete; CheckHandler(const CheckHandler&) = delete;
CheckHandler() = delete; CheckHandler() = delete;
private: private:
std::ostream& log_; std::ostream& log_;
......
...@@ -33,6 +33,9 @@ add_test(options_benchmarks options_test --benchmark_min_time=0.01) ...@@ -33,6 +33,9 @@ add_test(options_benchmarks options_test --benchmark_min_time=0.01)
compile_benchmark_test(basic_test) compile_benchmark_test(basic_test)
add_test(basic_benchmark basic_test --benchmark_min_time=0.01) add_test(basic_benchmark basic_test --benchmark_min_time=0.01)
compile_benchmark_test(diagnostics_test)
add_test(diagnostics_test diagnostics_test --benchmark_min_time=0.01)
compile_benchmark_test(fixture_test) compile_benchmark_test(fixture_test)
add_test(fixture_test fixture_test --benchmark_min_time=0.01) add_test(fixture_test fixture_test --benchmark_min_time=0.01)
......
// Testing:
// State::PauseTiming()
// State::ResumeTiming()
// Test that CHECK's within these function diagnose when they are called
// outside of the KeepRunning() loop.
//
// NOTE: Users should NOT include or use src/check.h. This is only done in
// order to test library internals.
#include "benchmark/benchmark_api.h"
#include "../src/check.h"
#include <stdexcept>
#include <cstdlib>
#if defined(__GNUC__) && !defined(__EXCEPTIONS)
#define TEST_HAS_NO_EXCEPTIONS
#endif
void TestHandler() {
#ifndef TEST_HAS_NO_EXCEPTIONS
throw std::logic_error("");
#else
std::abort();
#endif
}
void try_invalid_pause_resume(benchmark::State& state) {
#if !defined(NDEBUG) && !defined(TEST_HAS_NO_EXCEPTIONS)
try {
state.PauseTiming();
std::abort();
} catch (std::logic_error const&) {}
try {
state.ResumeTiming();
std::abort();
} catch (std::logic_error const&) {}
#else
(void)state; // avoid unused warning
#endif
}
void BM_diagnostic_test(benchmark::State& state) {
static bool called_once = false;
if (called_once == false) try_invalid_pause_resume(state);
while (state.KeepRunning()) {
benchmark::DoNotOptimize(state.iterations());
}
if (called_once == false) try_invalid_pause_resume(state);
called_once = true;
}
BENCHMARK(BM_diagnostic_test);
int main(int argc, char** argv) {
benchmark::internal::GetAbortHandler() = &TestHandler;
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment