about summary refs log tree commit diff homepage
path: root/lib/Support
diff options
context:
space:
mode:
authorFrank Busse <bb0xfb@gmail.com>2017-11-24 16:58:27 +0000
committerCristian Cadar <c.cadar@imperial.ac.uk>2018-10-30 22:22:26 +0200
commit3caf3e985e4f35ac6ac04f61b92f11d2569550c6 (patch)
tree4c8cb1ce7e8d7bdf4f890e76b98ea2ef77370f66 /lib/Support
parent652c2bdc171a448a2d6082040eebec366946ad33 (diff)
downloadklee-3caf3e985e4f35ac6ac04f61b92f11d2569550c6.tar.gz
Base time API upon std::chrono
This should not change the behaviour of KLEE and mimics the old API.

- functions moved from util into time namespace
- uses time points and time spans instead of double
- CLI arguments now have the form "3h5min8us"

Changed command line parameters:
- batch-time (double to string)
- istats-write-interval (double to string)
- max-instruction-time (double to string)
- max-solver-time (double to string)
- max-time (double to string)
- min-query-time-to-log (double to string)
- seed-time (double to string)
- stats-write-interval (double to string)
- uncovered-update-interval (double to string)
- added: log-timed-out-queries (replaces negative max-solver-time)
Diffstat (limited to 'lib/Support')
-rw-r--r--lib/Support/Time.cpp201
-rw-r--r--lib/Support/Timer.cpp24
2 files changed, 171 insertions, 54 deletions
diff --git a/lib/Support/Time.cpp b/lib/Support/Time.cpp
index f508e6fa..7cd9659b 100644
--- a/lib/Support/Time.cpp
+++ b/lib/Support/Time.cpp
@@ -7,58 +7,193 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "klee/Config/Version.h"
+#include "klee/Internal/Support/ErrorHandling.h"
 #include "klee/Internal/System/Time.h"
 
-#if LLVM_VERSION_CODE >= LLVM_VERSION(4, 0)
-#include <chrono>
 
-#include <llvm/Support/Chrono.h>
-#else
-#include "llvm/Support/TimeValue.h"
-#endif
+#include <cstdint>
+#include <regex>
+#include <sstream>
+#include <tuple>
+#include <sys/resource.h>
 
-#include "llvm/Support/Process.h"
 
-using namespace llvm;
 using namespace klee;
 
-#if LLVM_VERSION_CODE >= LLVM_VERSION(4, 0)
-double util::durationToDouble(std::chrono::nanoseconds dur)
-{
-  return dur.count() / 1e9;
+
+/* Why std::chrono:
+ * - C++11
+ * - separation between time points and durations
+ * - good performance on Linux and macOS (similar to gettimeofday)
+ * and not:
+ * - clock_gettime(CLOCK_MONOTONIC_COARSE): software clock, Linux-specific
+ * - clock_gettime(CLOCK_MONOTONIC): slowest on macOS, C-like API
+ * - gettimeofday: C-like API, non-monotonic
+ *
+ * TODO: add time literals with C++14
+ */
+
+// === Point ===
+
+// operators
+time::Point& time::Point::operator+=(const time::Span &span) { point += span.duration; return *this; }
+time::Point& time::Point::operator-=(const time::Span &span) { point -= span.duration; return *this; }
+
+time::Point time::operator+(const time::Point &point, const time::Span &span) { return time::Point(point.point + span.duration); }
+time::Point time::operator+(const time::Span &span, const time::Point &point) { return time::Point(point.point + span.duration); }
+time::Point time::operator-(const time::Point &point, const time::Span &span) { return time::Point(point.point - span.duration); }
+time::Span time::operator-(const time::Point &lhs, const time::Point &rhs) { return time::Span(lhs.point - rhs.point); }
+bool time::operator==(const time::Point &lhs, const time::Point &rhs) { return lhs.point == rhs.point; }
+bool time::operator!=(const time::Point &lhs, const time::Point &rhs) { return lhs.point != rhs.point; }
+bool time::operator<(const time::Point &lhs, const time::Point &rhs) { return lhs.point < rhs.point; }
+bool time::operator<=(const time::Point &lhs, const time::Point &rhs) { return lhs.point <= rhs.point; }
+bool time::operator>(const time::Point &lhs, const time::Point &rhs) { return lhs.point > rhs.point; }
+bool time::operator>=(const time::Point &lhs, const time::Point &rhs) { return lhs.point >= rhs.point; }
+
+
+// === Span ===
+
+// ctors
+/// returns span from string in old (X.Y) and new (3h4min) format
+time::Span::Span(const std::string &s) {
+  if (s.empty()) return;
+
+  std::regex re("^([0-9]*\\.?[0-9]+)|((([0-9]+)(h|min|s|ms|us|ns))+)$", std::regex::extended);
+  std::regex nre("([0-9]+)(h|min|s|ms|us|ns)", std::regex::extended);
+  std::smatch match;
+  std::string submatch;
+
+  // error
+  if (!std::regex_match(s, match, re)) goto error;
+
+  // old (double) format
+  submatch = match[1].str();
+  if (match[1].matched) {
+    errno = 0;
+    auto value = std::stod(submatch);
+    if (errno) goto error;
+
+    std::chrono::duration<double> d(value);
+    duration = std::chrono::duration_cast<std::chrono::microseconds>(d);
+  }
+
+  // new (string) format
+  submatch = match[2].str();
+  for (std::smatch m; std::regex_search(submatch, m, nre); submatch = m.suffix()) {
+    errno = 0;
+    const auto value = std::stoull(m[1]);
+    if (errno) goto error;
+
+    Duration d;
+    if (m[2] == "h") d = std::chrono::hours(value);
+    else if (m[2] == "min") d = std::chrono::minutes(value);
+    else if (m[2] ==   "s") d = std::chrono::seconds(value);
+    else if (m[2] ==  "ms") d = std::chrono::milliseconds(value);
+    else if (m[2] ==  "us") d = std::chrono::microseconds(value);
+    else if (m[2] ==  "ns") d = std::chrono::nanoseconds(value);
+    else goto error;
+
+    duration += d;
+  }
+
+  return;
+
+error:
+  klee_error("Illegal number format: %s", s.c_str());
 }
 
-double util::getUserTime() {
-  sys::TimePoint<> now;
-  std::chrono::nanoseconds user, sys;
+// operators
+time::Span& time::Span::operator=(const time::Duration &d) { duration = d; return *this; };
+time::Span& time::Span::operator+=(const time::Span &other) { duration += other.duration; return *this; }
+time::Span& time::Span::operator-=(const time::Span &other) { duration -= other.duration; return *this; }
+time::Span& time::Span::operator*=(const time::Duration::rep &rep) { duration *= rep; return *this; }
+
+time::Span time::operator+(const time::Span &lhs, const time::Span &rhs) { return time::Span(lhs.duration + rhs.duration); }
+time::Span time::operator-(const time::Span &lhs, const time::Span &rhs) { return time::Span(lhs.duration - rhs.duration); }
+time::Span time::operator*(const time::Span &span, const time::Duration::rep &rep) { return time::Span(span.duration * rep); }
+time::Span time::operator*(const time::Duration::rep &rep, const time::Span &span) { return time::Span(span.duration * rep); }
+time::Span time::operator/(const time::Span &span, const time::Duration::rep &rep) { return time::Span(span.duration / rep); }
+bool time::operator==(const time::Span &lhs, const time::Span &rhs) { return lhs.duration == rhs.duration; }
+bool time::operator<=(const time::Span &lhs, const time::Span &rhs) { return lhs.duration <= rhs.duration; }
+bool time::operator>=(const time::Span &lhs, const time::Span &rhs) { return lhs.duration >= rhs.duration; }
+bool time::operator<(const time::Span &lhs, const time::Span &rhs) { return lhs.duration < rhs.duration; }
+bool time::operator>(const time::Span &lhs, const time::Span &rhs) { return lhs.duration > rhs.duration; }
+
+std::ostream& time::operator<<(std::ostream &stream, time::Span span) { return stream << span.toSeconds() << 's'; }
+llvm::raw_ostream& time::operator<<(llvm::raw_ostream &stream, time::Span span) { return stream << span.toSeconds() << 's'; }
+
 
-  sys::Process::GetTimeUsage(now, user, sys);
+// units
+time::Span time::hours(std::uint16_t ticks) { return time::Span(std::chrono::hours(ticks)); }
+time::Span time::minutes(std::uint16_t ticks) { return time::Span(std::chrono::minutes(ticks)); }
+time::Span time::seconds(std::uint64_t ticks) { return time::Span(std::chrono::seconds(ticks)); }
+time::Span time::milliseconds(std::uint64_t ticks) { return time::Span(std::chrono::milliseconds(ticks)); }
+time::Span time::microseconds(std::uint64_t ticks) { return time::Span(std::chrono::microseconds(ticks)); }
+time::Span time::nanoseconds(std::uint64_t ticks) { return time::Span(std::chrono::nanoseconds(ticks)); }
 
-  return durationToDouble(user);
+
+// conversions
+time::Span::operator time::Duration() const { return duration; }
+
+time::Span::operator bool() const { return duration.count() != 0; }
+
+time::Span::operator timeval() const {
+  timeval tv{};
+  const auto secs = std::chrono::duration_cast<std::chrono::seconds>(duration);
+  const auto usecs = std::chrono::duration_cast<std::chrono::microseconds>(duration - secs);
+  tv.tv_sec = secs.count();
+  tv.tv_usec = usecs.count();
+  return tv;
 }
 
-double util::getWallTime() {
-  return durationToDouble(getWallTimeVal().time_since_epoch());
+std::uint64_t time::Span::toMicroseconds() const {
+  return (std::uint64_t)std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
 }
 
-sys::TimePoint<> util::getWallTimeVal() {
-  return std::chrono::system_clock::now();
+double time::Span::toSeconds() const {
+  return std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() / (double)1000000000;
 }
 
-#else
-double util::getUserTime() {
-  sys::TimeValue now(0,0),user(0,0),sys(0,0);
-  sys::Process::GetTimeUsage(now,user,sys);
-  return (user.seconds() + (double) user.nanoseconds() * 1e-9);
+std::tuple<std::uint32_t, std::uint8_t, std::uint8_t> time::Span::toHMS() const {
+  auto d = duration;
+  const auto h = std::chrono::duration_cast<std::chrono::hours>(d);
+  const auto m = std::chrono::duration_cast<std::chrono::minutes>(d -= h);
+  const auto s = std::chrono::duration_cast<std::chrono::seconds>(d -= m);
+
+  return std::make_tuple((std::uint32_t) h.count(), (std::uint8_t) m.count(), (std::uint8_t) s.count());
 }
 
-double util::getWallTime() {
-  sys::TimeValue now = getWallTimeVal();
-  return (now.seconds() + ((double) now.nanoseconds() * 1e-9));
+
+// methods
+/// Returns information about clock
+std::string time::getClockInfo() {
+  std::stringstream buffer;
+  buffer << "Using monotonic steady clock with "
+         << std::chrono::steady_clock::period::num
+         << '/'
+         << std::chrono::steady_clock::period::den
+         << "s resolution\n";
+  return buffer.str();
 }
 
-sys::TimeValue util::getWallTimeVal() {
-  return sys::TimeValue::now();
+
+/// Returns time spent by this process in user mode
+time::Span time::getUserTime() {
+  rusage usage{};
+  auto ret = ::getrusage(RUSAGE_SELF, &usage);
+
+  if (ret) {
+    klee_warning("getrusage returned with error, return (0,0)");
+    return {};
+  } else {
+    return time::seconds(static_cast<std::uint64_t>(usage.ru_utime.tv_sec)) +
+           time::microseconds(static_cast<std::uint64_t>(usage.ru_utime.tv_usec));
+  }
 }
-#endif
+
+
+/// Returns point in time using a monotonic steady clock
+time::Point time::getWallTime() {
+  return time::Point(std::chrono::steady_clock::now());
+}
+
diff --git a/lib/Support/Timer.cpp b/lib/Support/Timer.cpp
index 0e727bb4..4e52feb5 100644
--- a/lib/Support/Timer.cpp
+++ b/lib/Support/Timer.cpp
@@ -9,32 +9,14 @@
 
 #include "klee/Config/Version.h"
 #include "klee/Internal/Support/Timer.h"
-
 #include "klee/Internal/System/Time.h"
 
 using namespace klee;
-using namespace llvm;
-
-#if LLVM_VERSION_CODE >= LLVM_VERSION(4, 0)
 
 WallTimer::WallTimer() {
-  start = util::getWallTimeVal();
+  start = time::getWallTime();
 }
 
-uint64_t WallTimer::check() {
-  auto now = util::getWallTimeVal();
-  return std::chrono::duration_cast<std::chrono::microseconds>(now -
-    start).count();
+time::Span WallTimer::check() {
+  return time::Span(time::getWallTime() - start);
 }
-
-#else
-
-WallTimer::WallTimer() {
-  startMicroseconds = util::getWallTimeVal().usec();
-}
-
-uint64_t WallTimer::check() {
-  return util::getWallTimeVal().usec() - startMicroseconds;
-}
-
-#endif