Move win/time to misc/time and add more conversion functions

This CL pulls together similar time conversion functions and adds
conversions between `FILETIME`s and `timespec`s.

Bug: crashpad:206
Change-Id: I1d9b1560884ffde2364af0092114f82e1534ad1c
Reviewed-on: https://chromium-review.googlesource.com/752574
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2017-11-06 14:01:04 -08:00 committed by Commit Bot
parent b851b2590b
commit 18726100ed
12 changed files with 289 additions and 111 deletions

View File

@ -21,12 +21,12 @@
#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
#include "util/misc/time.h"
#include "util/win/capture_context.h"
#include "util/win/nt_internals.h"
#include "util/win/ntstatus_logging.h"
#include "util/win/process_structs.h"
#include "util/win/scoped_handle.h"
#include "util/win/time.h"
namespace crashpad {

View File

@ -24,9 +24,9 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "util/misc/from_pointer_cast.h"
#include "util/misc/time.h"
#include "util/win/nt_internals.h"
#include "util/win/registration_protocol_win.h"
#include "util/win/time.h"
namespace crashpad {

View File

@ -22,27 +22,12 @@
#include "base/logging.h"
#include "util/file/file_io.h"
#include "util/misc/lexing.h"
#include "util/misc/time.h"
namespace crashpad {
namespace {
void SubtractTimespec(const timespec& t1,
const timespec& t2,
timespec* result) {
result->tv_sec = t1.tv_sec - t2.tv_sec;
result->tv_nsec = t1.tv_nsec - t2.tv_nsec;
if (result->tv_nsec < 0) {
result->tv_sec -= 1;
result->tv_nsec += static_cast<long>(1E9);
}
}
void TimespecToTimeval(const timespec& ts, timeval* tv) {
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec / 1000;
}
long GetClockTicksPerSecond() {
long clock_ticks_per_s = sysconf(_SC_CLK_TCK);
if (clock_ticks_per_s <= 0) {

49
util/misc/time.cc Normal file
View File

@ -0,0 +1,49 @@
// Copyright 2017 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/misc/time.h"
#include "util/numeric/safe_assignment.h"
namespace crashpad {
void AddTimespec(const timespec& ts1, const timespec& ts2, timespec* result) {
result->tv_sec = ts1.tv_sec + ts2.tv_sec;
result->tv_nsec = ts1.tv_nsec + ts2.tv_nsec;
if (result->tv_nsec >= long{kNanosecondsPerSecond}) {
++result->tv_sec;
result->tv_nsec -= kNanosecondsPerSecond;
}
}
void SubtractTimespec(const timespec& t1,
const timespec& t2,
timespec* result) {
result->tv_sec = t1.tv_sec - t2.tv_sec;
result->tv_nsec = t1.tv_nsec - t2.tv_nsec;
if (result->tv_nsec < 0) {
result->tv_sec -= 1;
result->tv_nsec += kNanosecondsPerSecond;
}
}
bool TimespecToTimeval(const timespec& ts, timeval* tv) {
tv->tv_usec = ts.tv_nsec / 1000;
// timespec::tv_sec and timeval::tv_sec should generally both be of type
// time_t, however, on Windows, timeval::tv_sec is declared as a long, which
// may be smaller than a time_t.
return AssignIfInRange(&tv->tv_sec, ts.tv_sec);
}
} // namespace crashpad

71
util/misc/time.h Normal file
View File

@ -0,0 +1,71 @@
// Copyright 2015 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_UTIL_MISC_TIME_H_
#define CRASHPAD_UTIL_MISC_TIME_H_
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
namespace crashpad {
constexpr uint64_t kNanosecondsPerSecond = static_cast<uint64_t>(1E9);
//! \brief Add `timespec` \a ts1 and \a ts2 and return the result in \a result.
void AddTimespec(const timespec& ts1, const timespec& ts2, timespec* result);
//! \brief Subtract `timespec` \a ts2 from \a ts1 and return the result in \a
//! result.
void SubtractTimespec(const timespec& ts1,
const timespec& ts2,
timespec* result);
//! \brief Convert the timespec \a ts to a timeval \a tv.
//! \return `true` if the assignment is possible without truncation.
bool TimespecToTimeval(const timespec& ts, timeval* tv);
#if defined(OS_WIN) || DOXYGEN
//! \brief Convert a `timespec` to a Windows `FILETIME`, converting from POSIX
//! epoch to Windows epoch.
FILETIME TimespecToFiletimeEpoch(const timespec& ts);
//! \brief Convert a Windows `FILETIME` to `timespec`, converting from Windows
//! epoch to POSIX epoch.
timespec FiletimeToTimespecEpoch(const FILETIME& filetime);
//! \brief Convert Windows `FILETIME` to `timeval`, converting from Windows
//! epoch to POSIX epoch.
timeval FiletimeToTimevalEpoch(const FILETIME& filetime);
//! \brief Convert Windows `FILETIME` to `timeval`, treating the values as
//! an interval of elapsed time.
timeval FiletimeToTimevalInterval(const FILETIME& filetime);
//! \brief Similar to POSIX `gettimeofday()`, gets the current system time in
//! UTC.
void GetTimeOfDay(timeval* tv);
#endif // OS_WIN
} // namespace crashpad
#endif // CRASHPAD_UTIL_MISC_TIME_H_

126
util/misc/time_test.cc Normal file
View File

@ -0,0 +1,126 @@
// Copyright 2015 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/misc/time.h"
#include <limits>
#include "gtest/gtest.h"
namespace crashpad {
namespace test {
namespace {
TEST(Time, TimespecArithmetic) {
timespec ts1, ts2, result;
ts1.tv_sec = ts2.tv_sec = 1;
ts1.tv_nsec = ts2.tv_nsec = kNanosecondsPerSecond / 2;
AddTimespec(ts1, ts2, &result);
EXPECT_EQ(result.tv_sec, 3);
EXPECT_EQ(result.tv_nsec, 0);
ts1.tv_sec = 2;
ts1.tv_nsec = 0;
ts2.tv_sec = 1;
ts2.tv_nsec = 1;
SubtractTimespec(ts1, ts2, &result);
EXPECT_EQ(result.tv_sec, 0);
EXPECT_EQ(result.tv_nsec, long{kNanosecondsPerSecond - 1});
}
TEST(Time, TimeConversions) {
// On July 30th, 2014 at 9:15 PM GMT+0, the Crashpad git repository was born.
// (nanoseconds are approximate)
constexpr timespec kCrashpadBirthdate = {
/* .tv_sec= */ 1406754914,
/* .tv_nsec= */ 32487
};
timeval timeval_birthdate;
ASSERT_TRUE(TimespecToTimeval(kCrashpadBirthdate, &timeval_birthdate));
EXPECT_EQ(timeval_birthdate.tv_sec, kCrashpadBirthdate.tv_sec);
EXPECT_EQ(timeval_birthdate.tv_usec, kCrashpadBirthdate.tv_nsec / 1000);
constexpr timespec kEndOfTime = {
/* .tv_sec= */ std::numeric_limits<decltype(timespec::tv_sec)>::max(),
/* .tv_nsec= */ 0
};
timeval end_of_timeval;
if (std::numeric_limits<decltype(timespec::tv_sec)>::max() >
std::numeric_limits<decltype(timeval::tv_sec)>::max()) {
EXPECT_FALSE(TimespecToTimeval(kEndOfTime, &end_of_timeval));
} else {
EXPECT_TRUE(TimespecToTimeval(kEndOfTime, &end_of_timeval));
}
#if defined(OS_WIN)
constexpr uint64_t kBirthdateFiletimeIntervals = 130512285140000324;
FILETIME filetime_birthdate;
filetime_birthdate.dwLowDateTime = 0xffffffff & kBirthdateFiletimeIntervals;
filetime_birthdate.dwHighDateTime = kBirthdateFiletimeIntervals >> 32;
FILETIME filetime = TimespecToFiletimeEpoch(kCrashpadBirthdate);
EXPECT_EQ(filetime.dwLowDateTime, filetime_birthdate.dwLowDateTime);
EXPECT_EQ(filetime.dwHighDateTime, filetime_birthdate.dwHighDateTime);
timespec timespec_birthdate = FiletimeToTimespecEpoch(filetime_birthdate);
EXPECT_EQ(timespec_birthdate.tv_sec, kCrashpadBirthdate.tv_sec);
EXPECT_EQ(timespec_birthdate.tv_nsec,
kCrashpadBirthdate.tv_nsec - kCrashpadBirthdate.tv_nsec % 100);
timeval_birthdate = FiletimeToTimevalEpoch(filetime_birthdate);
EXPECT_EQ(timeval_birthdate.tv_sec, kCrashpadBirthdate.tv_sec);
EXPECT_EQ(timeval_birthdate.tv_usec, kCrashpadBirthdate.tv_nsec / 1000);
FILETIME elapsed_filetime;
elapsed_filetime.dwLowDateTime = 0;
elapsed_filetime.dwHighDateTime = 0;
timeval elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime);
EXPECT_EQ(elapsed_timeval.tv_sec, 0);
EXPECT_EQ(elapsed_timeval.tv_usec, 0);
elapsed_filetime.dwLowDateTime = 9;
elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime);
EXPECT_EQ(elapsed_timeval.tv_sec, 0);
EXPECT_EQ(elapsed_timeval.tv_usec, 0);
elapsed_filetime.dwLowDateTime = 10;
elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime);
EXPECT_EQ(elapsed_timeval.tv_sec, 0);
EXPECT_EQ(elapsed_timeval.tv_usec, 1);
elapsed_filetime.dwHighDateTime = 1;
elapsed_filetime.dwLowDateTime = 0;
elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime);
EXPECT_EQ(elapsed_timeval.tv_sec, 429);
EXPECT_EQ(elapsed_timeval.tv_usec, 496729);
#endif // OS_WIN
}
#if defined(OS_WIN)
TEST(Time, GetTimeOfDay) {
timeval t;
GetTimeOfDay(&t);
time_t approx_now = time(nullptr);
EXPECT_GE(approx_now, t.tv_sec);
EXPECT_LT(approx_now - 100, t.tv_sec);
}
#endif // OS_WIN
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/win/time.h"
#include "util/misc/time.h"
#include <stdint.h>
@ -23,11 +23,22 @@ namespace crashpad {
namespace {
constexpr uint64_t kMicrosecondsPerSecond = static_cast<uint64_t>(1E6);
constexpr uint64_t kNanosecondsPerFiletimeInterval = static_cast<uint64_t>(100);
constexpr uint64_t kFiletimeIntervalsPerSecond =
kNanosecondsPerSecond / kNanosecondsPerFiletimeInterval;
constexpr uint64_t kFiletimeIntervalsPerMicrosecond =
kFiletimeIntervalsPerSecond / kMicrosecondsPerSecond;
// Windows epoch is 1601-01-01, and FILETIME ticks are 100 nanoseconds.
// 1601 to 1970 is 369 years + 89 leap days = 134774 days * 86400 seconds per
// day. It's not entirely clear, but it appears that these are solar seconds,
// not SI seconds, so there are no leap seconds to be considered.
constexpr uint64_t kNumSecondsFrom1601To1970 = (369 * 365 + 89) * 86400ULL;
uint64_t FiletimeToMicroseconds(const FILETIME& filetime) {
uint64_t t = (static_cast<uint64_t>(filetime.dwHighDateTime) << 32) |
filetime.dwLowDateTime;
return t / 10; // 100 nanosecond intervals to microseconds.
return t / kFiletimeIntervalsPerMicrosecond;
}
timeval MicrosecondsToTimeval(uint64_t microseconds) {
@ -39,14 +50,31 @@ timeval MicrosecondsToTimeval(uint64_t microseconds) {
} // namespace
FILETIME TimespecToFiletimeEpoch(const timespec& ts) {
uint64_t intervals =
(kNumSecondsFrom1601To1970 + ts.tv_sec) * kFiletimeIntervalsPerSecond +
ts.tv_nsec / kNanosecondsPerFiletimeInterval;
FILETIME filetime;
filetime.dwLowDateTime = intervals & 0xffffffff;
filetime.dwHighDateTime = intervals >> 32;
return filetime;
}
timespec FiletimeToTimespecEpoch(const FILETIME& filetime) {
uint64_t intervals =
(uint64_t{filetime.dwHighDateTime} << 32) | filetime.dwLowDateTime;
timespec result;
result.tv_sec =
(intervals / kFiletimeIntervalsPerSecond) - kNumSecondsFrom1601To1970;
result.tv_nsec =
static_cast<long>(intervals % kFiletimeIntervalsPerSecond) *
kNanosecondsPerFiletimeInterval;
return result;
}
timeval FiletimeToTimevalEpoch(const FILETIME& filetime) {
uint64_t microseconds = FiletimeToMicroseconds(filetime);
// Windows epoch is 1601-01-01, and FILETIME ticks are 100 nanoseconds.
// 1601 to 1970 is 369 years + 89 leap days = 134774 days * 86400 seconds per
// day. It's not entirely clear, but it appears that these are solar seconds,
// not SI seconds, so there are no leap seconds to be considered.
constexpr uint64_t kNumSecondsFrom1601To1970 = (369 * 365 + 89) * 86400ULL;
DCHECK_GE(microseconds, kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond);
microseconds -= kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond;
return MicrosecondsToTimeval(microseconds);

View File

@ -20,24 +20,12 @@
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "util/misc/time.h"
namespace crashpad {
#if !defined(OS_MACOSX)
namespace {
void AddTimespec(const timespec& ts1, const timespec& ts2, timespec* result) {
result->tv_sec = ts1.tv_sec + ts2.tv_sec;
result->tv_nsec = ts1.tv_nsec + ts2.tv_nsec;
if (result->tv_nsec > static_cast<long>(1E9)) {
++result->tv_sec;
result->tv_nsec -= static_cast<long>(1E9);
}
}
} // namespace
Semaphore::Semaphore(int value) {
PCHECK(sem_init(&semaphore_, 0, value) == 0) << "sem_init";
}

View File

@ -144,6 +144,9 @@
'misc/scoped_forbid_return.cc',
'misc/scoped_forbid_return.h',
'misc/symbolic_constants_common.h',
'misc/time.cc',
'misc/time.h',
'misc/time_win.cc',
'misc/tri_state.h',
'misc/uuid.cc',
'misc/uuid.h',
@ -257,8 +260,6 @@
'win/session_end_watcher.cc',
'win/session_end_watcher.h',
'win/termination_codes.h',
'win/time.cc',
'win/time.h',
'win/xp_compat.h',
],
'conditions': [

View File

@ -74,6 +74,7 @@
'misc/scoped_forbid_return_test.cc',
'misc/random_string_test.cc',
'misc/reinterpret_bytes_test.cc',
'misc/time_test.cc',
'misc/uuid_test.cc',
'net/http_body_gzip_test.cc',
'net/http_body_test.cc',
@ -115,7 +116,6 @@
'win/safe_terminate_process_test.cc',
'win/scoped_process_suspend_test.cc',
'win/session_end_watcher_test.cc',
'win/time_test.cc',
],
'conditions': [
['OS=="mac"', {

View File

@ -1,36 +0,0 @@
// Copyright 2015 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_UTIL_WIN_TIME_H_
#define CRASHPAD_UTIL_WIN_TIME_H_
#include <sys/time.h>
#include <windows.h>
namespace crashpad {
//! \brief Convert Windows `FILETIME` to `timeval`, converting from Windows
//! epoch to POSIX epoch.
timeval FiletimeToTimevalEpoch(const FILETIME& filetime);
//! \brief Convert Windows `FILETIME` to `timeval`, treating the values as
//! an interval of elapsed time.
timeval FiletimeToTimevalInterval(const FILETIME& filetime);
//! \brief Similar to POSIX gettimeofday(), gets the current system time in UTC.
void GetTimeOfDay(timeval* tv);
} // namespace crashpad
#endif // CRASHPAD_UTIL_WIN_TIME_H_

View File

@ -1,34 +0,0 @@
// Copyright 2015 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/win/time.h"
#include "gtest/gtest.h"
namespace crashpad {
namespace test {
namespace {
TEST(Time, Reasonable) {
timeval t;
GetTimeOfDay(&t);
// Assume that time's time_t return is seconds from 1970.
time_t approx_now = time(nullptr);
EXPECT_GE(approx_now, t.tv_sec);
EXPECT_LT(approx_now - 100, t.tv_sec);
}
} // namespace
} // namespace test
} // namespace crashpad