From 13b6a10e7b01f57089e38b081de7de60e8eb22d2 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Wed, 19 Oct 2016 13:58:55 +0000 Subject: [PATCH] Add Chrono.h - std::chrono support header Summary: std::chrono mostly covers the functionality of llvm::sys::TimeValue and lldb_private::TimeValue. This header adds a bit of utility functions and typedefs, which make the usage of the library and porting code from TimeValues easier. Rationale: - TimePoint typedef - precision of system_clock is implementation defined - using a well-defined precision helps maintain consistency between platforms, makes it interact better with existing TimeValue classes, and avoids cases there a time point is implicitly convertible to a specific precision on some platforms but not on others. - system_clock::to_time_t only accepts time_points with the default system precision (even though time_t has only second precision on all platforms we support). To avoid the need for explicit casts, I have added a toTimeT() wrapper function. toTimePoint(time_t) was not strictly necessary, but I have added it for symmetry. Reviewers: zturner, mehdi_amini Subscribers: beanz, mgorny, llvm-commits, modocache Differential Revision: https://reviews.llvm.org/D25416 llvm-svn: 284590 --- llvm/include/llvm/Support/Chrono.h | 55 ++++++++++++++++++++++ llvm/include/llvm/Support/TimeValue.h | 11 +++++ llvm/lib/Support/CMakeLists.txt | 1 + llvm/lib/Support/Chrono.cpp | 47 +++++++++++++++++++ llvm/lib/Support/TimeValue.cpp | 17 ++++--- llvm/lib/Support/Unix/TimeValue.inc | 54 ---------------------- llvm/lib/Support/Windows/TimeValue.inc | 61 ------------------------ llvm/unittests/Support/CMakeLists.txt | 1 + llvm/unittests/Support/Chrono.cpp | 79 ++++++++++++++++++++++++++++++++ llvm/unittests/Support/TimeValueTest.cpp | 10 ++++ 10 files changed, 212 insertions(+), 124 deletions(-) create mode 100644 llvm/include/llvm/Support/Chrono.h create mode 100644 llvm/lib/Support/Chrono.cpp delete mode 100644 llvm/lib/Support/Unix/TimeValue.inc delete mode 100644 llvm/lib/Support/Windows/TimeValue.inc create mode 100644 llvm/unittests/Support/Chrono.cpp diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h new file mode 100644 index 0000000..203439c --- /dev/null +++ b/llvm/include/llvm/Support/Chrono.h @@ -0,0 +1,55 @@ +//===- llvm/Support/Chrono.h - Utilities for Timing Manipulation-*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CHRONO_H +#define LLVM_SUPPORT_CHRONO_H + +#include "llvm/Support/Compiler.h" + +#include +#include + +namespace llvm { + +class raw_ostream; + +namespace sys { + +/// A time point on the system clock. This is provided for two reasons: +/// - to insulate us agains subtle differences in behavoir to differences in +/// system clock precision (which is implementation-defined and differs between +/// platforms). +/// - to shorten the type name +/// The default precision is nanoseconds. If need a specific precision specify +/// it explicitly. If unsure, use the default. If you need a time point on a +/// clock other than the system_clock, use std::chrono directly. +template +using TimePoint = std::chrono::time_point; + +/// Convert a TimePoint to std::time_t +LLVM_ATTRIBUTE_ALWAYS_INLINE inline std::time_t toTimeT(TimePoint<> TP) { + using namespace std::chrono; + return system_clock::to_time_t( + time_point_cast(TP)); +} + +/// Convert a std::time_t to a TimePoint +LLVM_ATTRIBUTE_ALWAYS_INLINE inline TimePoint +toTimePoint(std::time_t T) { + using namespace std::chrono; + return time_point_cast(system_clock::from_time_t(T)); +} + +} // namespace sys + +raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP); + +} // namespace llvm + +#endif // LLVM_SUPPORT_CHRONO_H diff --git a/llvm/include/llvm/Support/TimeValue.h b/llvm/include/llvm/Support/TimeValue.h index 6bca58b..4b6dd15 100644 --- a/llvm/include/llvm/Support/TimeValue.h +++ b/llvm/include/llvm/Support/TimeValue.h @@ -14,6 +14,7 @@ #ifndef LLVM_SUPPORT_TIMEVALUE_H #define LLVM_SUPPORT_TIMEVALUE_H +#include "llvm/Support/Chrono.h" #include "llvm/Support/DataTypes.h" #include @@ -112,6 +113,11 @@ namespace sys { this->normalize(); } + template + TimeValue(TimePoint TP) + : seconds_(sys::toTimeT(TP) + PosixZeroTimeSeconds), + nanos_((TimePoint<>(TP).time_since_epoch() % std::chrono::seconds(1)).count()) {} + /// This is a static constructor that returns a TimeValue that represents /// the current time. /// @brief Creates a TimeValue with the current time (UTC). @@ -121,6 +127,11 @@ namespace sys { /// @name Operators /// @{ public: + operator TimePoint<>() const { + return toTimePoint(seconds_ - PosixZeroTimeSeconds) + + std::chrono::nanoseconds(nanos_); + } + /// Add \p that to \p this. /// @returns this /// @brief Incrementing assignment operator. diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt index cf04016..d8ecfa9 100644 --- a/llvm/lib/Support/CMakeLists.txt +++ b/llvm/lib/Support/CMakeLists.txt @@ -37,6 +37,7 @@ add_llvm_library(LLVMSupport BranchProbability.cpp CachePruning.cpp circular_raw_ostream.cpp + Chrono.cpp COM.cpp CommandLine.cpp Compression.cpp diff --git a/llvm/lib/Support/Chrono.cpp b/llvm/lib/Support/Chrono.cpp new file mode 100644 index 0000000..f0673be9 --- /dev/null +++ b/llvm/lib/Support/Chrono.cpp @@ -0,0 +1,47 @@ +//===- Support/Chrono.cpp - Utilities for Timing Manipulation ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Chrono.h" +#include "llvm/Config/config.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +using namespace sys; + +static inline struct tm getStructTM(TimePoint<> TP) { + struct tm Storage; + std::time_t OurTime = toTimeT(TP); + +#if defined(LLVM_ON_UNIX) + struct tm *LT = ::localtime_r(&OurTime, &Storage); + assert(LT); + (void)LT; +#endif +#if defined(LLVM_ON_WIN32) + int Error = ::_localtime64_s(&Storage, &OurTime); + assert(!Error); + (void)Error; +#endif + + return Storage; +} + +raw_ostream &operator<<(raw_ostream &OS, TimePoint<> TP) { + struct tm LT = getStructTM(TP); + char Buffer[sizeof("YYYY-MM-DD HH:MM:SS")]; + strftime(Buffer, sizeof(Buffer), "%Y-%m-%d %H:%M:%S", <); + return OS << Buffer << '.' + << format("%.9lu", + long((TP.time_since_epoch() % std::chrono::seconds(1)) + .count())); +} + +} // namespace llvm diff --git a/llvm/lib/Support/TimeValue.cpp b/llvm/lib/Support/TimeValue.cpp index 94a4c01..18c84ca 100644 --- a/llvm/lib/Support/TimeValue.cpp +++ b/llvm/lib/Support/TimeValue.cpp @@ -12,7 +12,8 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/TimeValue.h" -#include "llvm/Config/config.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/ScopedPrinter.h" namespace llvm { @@ -45,12 +46,10 @@ void TimeValue::normalize() { } } -} // namespace llvm +std::string TimeValue::str() const { return to_string(TimePoint<>(*this)); } + +TimeValue TimeValue::now() { + return TimePoint<>(std::chrono::system_clock::now()); +} -/// Include the platform-specific portion of TimeValue class -#ifdef LLVM_ON_UNIX -#include "Unix/TimeValue.inc" -#endif -#ifdef LLVM_ON_WIN32 -#include "Windows/TimeValue.inc" -#endif +} // namespace llvm diff --git a/llvm/lib/Support/Unix/TimeValue.inc b/llvm/lib/Support/Unix/TimeValue.inc deleted file mode 100644 index 042e0da..0000000 --- a/llvm/lib/Support/Unix/TimeValue.inc +++ /dev/null @@ -1,54 +0,0 @@ -//===- Unix/TimeValue.cpp - Unix TimeValue Implementation -------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the Unix specific portion of the TimeValue class. -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -//=== WARNING: Implementation here must contain only generic UNIX code that -//=== is guaranteed to work on *all* UNIX variants. -//===----------------------------------------------------------------------===// - -#include "Unix.h" - -namespace llvm { - using namespace sys; - -std::string TimeValue::str() const { - time_t OurTime = time_t(this->toEpochTime()); - struct tm Storage; - struct tm *LT = ::localtime_r(&OurTime, &Storage); - assert(LT); - char Buffer1[sizeof("YYYY-MM-DD HH:MM:SS")]; - strftime(Buffer1, sizeof(Buffer1), "%Y-%m-%d %H:%M:%S", LT); - char Buffer2[sizeof("YYYY-MM-DD HH:MM:SS.MMMUUUNNN")]; - snprintf(Buffer2, sizeof(Buffer2), "%s.%.9u", Buffer1, this->nanoseconds()); - return std::string(Buffer2); -} - -TimeValue TimeValue::now() { - struct timeval the_time; - timerclear(&the_time); - if (0 != ::gettimeofday(&the_time,nullptr)) { - // This is *really* unlikely to occur because the only gettimeofday - // errors concern the timezone parameter which we're passing in as 0. - // In the unlikely case it does happen, just return MinTime, no error - // message needed. - return MinTime(); - } - - return TimeValue( - static_cast( the_time.tv_sec + - PosixZeroTimeSeconds ), - static_cast( the_time.tv_usec * - NANOSECONDS_PER_MICROSECOND ) ); -} - -} diff --git a/llvm/lib/Support/Windows/TimeValue.inc b/llvm/lib/Support/Windows/TimeValue.inc deleted file mode 100644 index b90b4f1..0000000 --- a/llvm/lib/Support/Windows/TimeValue.inc +++ /dev/null @@ -1,61 +0,0 @@ -//===- Win32/TimeValue.cpp - Win32 TimeValue Implementation -----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file provides the Win32 implementation of the TimeValue class. -// -//===----------------------------------------------------------------------===// - -#include "WindowsSupport.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/raw_ostream.h" -#include -#include - -using namespace llvm; -using namespace llvm::sys; - -//===----------------------------------------------------------------------===// -//=== WARNING: Implementation here must contain only Win32 specific code. -//===----------------------------------------------------------------------===// - -TimeValue TimeValue::now() { - uint64_t ft; - GetSystemTimeAsFileTime(reinterpret_cast(&ft)); - - TimeValue t(0, 0); - t.fromWin32Time(ft); - return t; -} - -std::string TimeValue::str() const { - std::string S; - struct tm *LT; -#ifdef __MINGW32__ - // Old versions of mingw don't have _localtime64_s. Remove this once we drop support - // for them. - time_t OurTime = time_t(this->toEpochTime()); - LT = ::localtime(&OurTime); - assert(LT); -#else - struct tm Storage; - __time64_t OurTime = this->toEpochTime(); - int Error = ::_localtime64_s(&Storage, &OurTime); - assert(!Error); - (void)Error; - LT = &Storage; -#endif - - char Buffer[sizeof("YYYY-MM-DD HH:MM:SS")]; - strftime(Buffer, sizeof(Buffer), "%Y-%m-%d %H:%M:%S", LT); - raw_string_ostream OS(S); - OS << format("%s.%.9u", static_cast(Buffer), - this->nanoseconds()); - OS.flush(); - return S; -} diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index 6ae2aed..5ff5bdf 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -9,6 +9,7 @@ add_llvm_unittest(SupportTests BlockFrequencyTest.cpp BranchProbabilityTest.cpp Casting.cpp + Chrono.cpp CommandLineTest.cpp CompressionTest.cpp ConvertUTFTest.cpp diff --git a/llvm/unittests/Support/Chrono.cpp b/llvm/unittests/Support/Chrono.cpp new file mode 100644 index 0000000..3d57878 --- /dev/null +++ b/llvm/unittests/Support/Chrono.cpp @@ -0,0 +1,79 @@ +//===- llvm/unittest/Support/Chrono.cpp - Time utilities tests ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Chrono.h" +#include "llvm/ADT/SmallVector.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::sys; +using namespace std::chrono; + +namespace { + +TEST(Chrono, TimeTConversion) { + EXPECT_EQ(time_t(0), toTimeT(toTimePoint(time_t(0)))); + EXPECT_EQ(time_t(1), toTimeT(toTimePoint(time_t(1)))); + EXPECT_EQ(time_t(47), toTimeT(toTimePoint(time_t(47)))); + + TimePoint<> TP; + EXPECT_EQ(TP, toTimePoint(toTimeT(TP))); + TP += seconds(1); + EXPECT_EQ(TP, toTimePoint(toTimeT(TP))); + TP += hours(47); + EXPECT_EQ(TP, toTimePoint(toTimeT(TP))); +} + +TEST(Chrono, StringConversion) { + std::string S; + raw_string_ostream OS(S); + OS << system_clock::now(); + + // Do a basic sanity check on the output. + // The format we expect is YYYY-MM-DD HH:MM:SS.MMMUUUNNN + StringRef Date, Time; + std::tie(Date, Time) = StringRef(OS.str()).split(' '); + + SmallVector Components; + Date.split(Components, '-'); + ASSERT_EQ(3u, Components.size()); + EXPECT_EQ(4u, Components[0].size()); + EXPECT_EQ(2u, Components[1].size()); + EXPECT_EQ(2u, Components[2].size()); + + StringRef Sec, Nano; + std::tie(Sec, Nano) = Time.split('.'); + + Components.clear(); + Sec.split(Components, ':'); + ASSERT_EQ(3u, Components.size()); + EXPECT_EQ(2u, Components[0].size()); + EXPECT_EQ(2u, Components[1].size()); + EXPECT_EQ(2u, Components[2].size()); + EXPECT_EQ(9u, Nano.size()); +} + +// Test that toTimePoint and toTimeT can be called with a arguments with varying +// precisions. +TEST(Chrono, ImplicitConversions) { + std::time_t TimeT = 47; + TimePoint Sec = toTimePoint(TimeT); + TimePoint Milli = toTimePoint(TimeT); + TimePoint Micro = toTimePoint(TimeT); + TimePoint Nano = toTimePoint(TimeT); + EXPECT_EQ(Sec, Milli); + EXPECT_EQ(Sec, Micro); + EXPECT_EQ(Sec, Nano); + EXPECT_EQ(TimeT, toTimeT(Sec)); + EXPECT_EQ(TimeT, toTimeT(Milli)); + EXPECT_EQ(TimeT, toTimeT(Micro)); + EXPECT_EQ(TimeT, toTimeT(Nano)); +} + +} // anonymous namespace diff --git a/llvm/unittests/Support/TimeValueTest.cpp b/llvm/unittests/Support/TimeValueTest.cpp index 3d2b978..9e8690f 100644 --- a/llvm/unittests/Support/TimeValueTest.cpp +++ b/llvm/unittests/Support/TimeValueTest.cpp @@ -37,4 +37,14 @@ TEST(TimeValue, Win32FILETIME) { EXPECT_EQ(ft1970, epoch.toWin32Time()); } +TEST(TimeValue, Chrono) { + sys::TimeValue TV; + TV.fromEpochTime(0); + sys::TimePoint<> TP = TV; + EXPECT_EQ(0u, sys::toTimeT(TP)); + + TP += std::chrono::seconds(47); + TV = TP; + EXPECT_EQ(47u, TV.toEpochTime()); +} } -- 2.7.4