mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
Add, test, and use clock utilities.
This includes ClockMonotonicNanoseconds() and SleepNanoseconds(). SleepNanoseconds() is like base::PlatformThread::Sleep(), but PlatformThread is not in mini_chromium and I’m not keen on adding it because I’m not sold on the interface. I’m not convinced Sleep() belongs there, and I don’t want to have to bring all of base::Time* along for the ride. TEST=util_test Clock.*:MachMessageServer.*:ServiceManagement.* R=rsesek@chromium.org Review URL: https://codereview.chromium.org/597533002
This commit is contained in:
parent
cb2288d174
commit
8decf86db8
@ -16,10 +16,10 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <launch.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "base/mac/scoped_launch_data.h"
|
||||
#include "util/mac/launchd.h"
|
||||
#include "util/misc/clock.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -96,10 +96,7 @@ bool ServiceManagementRemoveJob(const std::string& label, bool wait) {
|
||||
// remove the job. Even so, the job’s PID may change between the time it’s
|
||||
// obtained and the time the kqueue is set up, so this is nontrivial.
|
||||
do {
|
||||
timespec sleep_time;
|
||||
sleep_time.tv_sec = 0;
|
||||
sleep_time.tv_nsec = 1E5; // 100 microseconds
|
||||
nanosleep(&sleep_time, NULL);
|
||||
SleepNanoseconds(1E5); // 100 microseconds
|
||||
} while (ServiceManagementIsJobLoaded(label));
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <launch.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -27,6 +26,7 @@
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "util/misc/clock.h"
|
||||
#include "util/posix/process_util.h"
|
||||
#include "util/stdlib/objc.h"
|
||||
|
||||
@ -53,10 +53,7 @@ void ExpectProcessIsRunning(pid_t pid, std::string& last_arg) {
|
||||
break;
|
||||
}
|
||||
if (inner_tries > 0) {
|
||||
timespec sleep_time;
|
||||
sleep_time.tv_sec = 0;
|
||||
sleep_time.tv_nsec = 1E6; // 1 millisecond
|
||||
nanosleep(&sleep_time, NULL);
|
||||
SleepNanoseconds(1E6); // 1 millisecond
|
||||
}
|
||||
} while (inner_tries--);
|
||||
ASSERT_TRUE(success);
|
||||
@ -68,10 +65,7 @@ void ExpectProcessIsRunning(pid_t pid, std::string& last_arg) {
|
||||
}
|
||||
|
||||
if (outer_tries > 0) {
|
||||
timespec sleep_time;
|
||||
sleep_time.tv_sec = 0;
|
||||
sleep_time.tv_nsec = 1E6; // 1 millisecond
|
||||
nanosleep(&sleep_time, NULL);
|
||||
SleepNanoseconds(1E6); // 1 millisecond
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,10 +95,7 @@ void ExpectProcessIsNotRunning(pid_t pid, std::string& last_arg) {
|
||||
}
|
||||
|
||||
if (tries > 0) {
|
||||
timespec sleep_time;
|
||||
sleep_time.tv_sec = 0;
|
||||
sleep_time.tv_nsec = 1E6; // 1 millisecond
|
||||
nanosleep(&sleep_time, NULL);
|
||||
SleepNanoseconds(1E6); // 1 millisecond
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,35 +14,15 @@
|
||||
|
||||
#include "util/mach/mach_message_server.h"
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "base/mac/mach_logging.h"
|
||||
#include "base/mac/scoped_mach_vm.h"
|
||||
#include "util/misc/clock.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
mach_timebase_info_data_t* TimebaseInternal() {
|
||||
mach_timebase_info_data_t* timebase_info = new mach_timebase_info_data_t;
|
||||
kern_return_t kr = mach_timebase_info(timebase_info);
|
||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info";
|
||||
return timebase_info;
|
||||
}
|
||||
|
||||
mach_timebase_info_data_t* Timebase() {
|
||||
static mach_timebase_info_data_t* timebase_info = TimebaseInternal();
|
||||
return timebase_info;
|
||||
}
|
||||
|
||||
uint64_t NanosecondTicks() {
|
||||
uint64_t absolute_time = mach_absolute_time();
|
||||
mach_timebase_info_data_t* timebase_info = Timebase();
|
||||
return absolute_time * timebase_info->numer / timebase_info->denom;
|
||||
}
|
||||
|
||||
const int kNanosecondsPerMillisecond = 1E6;
|
||||
|
||||
// TimerRunning determines whether |deadline| has passed. If |deadline| is in
|
||||
@ -51,14 +31,14 @@ const int kNanosecondsPerMillisecond = 1E6;
|
||||
// |deadline| is zero (indicating that no timer is in effect), |*remaining_ms|
|
||||
// is set to zero and this function returns true. Otherwise, this function
|
||||
// returns false. |deadline| is specified on the same time base as is returned
|
||||
// by NanosecondTicks().
|
||||
// by ClockMonotonicNanoseconds().
|
||||
bool TimerRunning(uint64_t deadline, mach_msg_timeout_t* remaining_ms) {
|
||||
if (!deadline) {
|
||||
*remaining_ms = MACH_MSG_TIMEOUT_NONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t now = NanosecondTicks();
|
||||
uint64_t now = ClockMonotonicNanoseconds();
|
||||
|
||||
if (now >= deadline) {
|
||||
return false;
|
||||
@ -114,7 +94,7 @@ mach_msg_return_t MachMessageServer::Run(Interface* interface,
|
||||
deadline = 0;
|
||||
} else if (timeout_ms != MACH_MSG_TIMEOUT_NONE) {
|
||||
options |= timeout_options;
|
||||
deadline = NanosecondTicks() +
|
||||
deadline = ClockMonotonicNanoseconds() +
|
||||
static_cast<uint64_t>(timeout_ms) * kNanosecondsPerMillisecond;
|
||||
} else {
|
||||
options &= ~timeout_options;
|
||||
|
51
util/misc/clock.cc
Normal file
51
util/misc/clock.cc
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2014 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/clock.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const uint64_t kNanosecondsPerSecond = 1E9;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
#if !defined(OS_MACOSX)
|
||||
|
||||
uint64_t ClockMonotonicNanoseconds() {
|
||||
timespec now;
|
||||
int rv = clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
DPCHECK(rv == 0) << "clock_gettime";
|
||||
|
||||
return now.tv_sec * kNanosecondsPerSecond + now.tv_nsec;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void SleepNanoseconds(uint64_t nanoseconds) {
|
||||
timespec sleep_time;
|
||||
sleep_time.tv_sec = nanoseconds / kNanosecondsPerSecond;
|
||||
sleep_time.tv_nsec = nanoseconds % kNanosecondsPerSecond;
|
||||
int rv = HANDLE_EINTR(nanosleep(&sleep_time, &sleep_time));
|
||||
DPCHECK(rv == 0) << "nanosleep";
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
46
util/misc/clock.h
Normal file
46
util/misc/clock.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2014 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_CLOCK_H_
|
||||
#define CRASHPAD_UTIL_MISC_CLOCK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief Returns the value of the system’s monotonic clock.
|
||||
//!
|
||||
//! The monotonic clock is a tick counter whose epoch is unspecified. It is a
|
||||
//! monotonically-increasing clock that cannot be set, and never jumps backwards
|
||||
//! on a running system. The monotonic clock may stop while the system is
|
||||
//! sleeping, and it may be reset when the system starts up. This clock is
|
||||
//! suitable for computing durations of events. Subject to the underlying
|
||||
//! clock’s resolution, successive calls to this function will result in a
|
||||
//! series of increasing values.
|
||||
//!
|
||||
//! \return The value of the system’s monotonic clock, in nanoseconds.
|
||||
uint64_t ClockMonotonicNanoseconds();
|
||||
|
||||
//! \brief Sleeps for the specified duration.
|
||||
//!
|
||||
//! \param[in] nanoseconds The number of nanoseconds to sleep. The actual sleep
|
||||
//! may be slightly longer due to latencies and timer resolution.
|
||||
//!
|
||||
//! This function is resilient against the underlying `nanosleep()` system call
|
||||
//! being interrupted by a signal.
|
||||
void SleepNanoseconds(uint64_t nanoseconds);
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_MISC_CLOCK_H_
|
45
util/misc/clock_mac.cc
Normal file
45
util/misc/clock_mac.cc
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2014 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/clock.h"
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
#include "base/mac/mach_logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
mach_timebase_info_data_t* TimebaseInternal() {
|
||||
mach_timebase_info_data_t* timebase_info = new mach_timebase_info_data_t;
|
||||
kern_return_t kr = mach_timebase_info(timebase_info);
|
||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info";
|
||||
return timebase_info;
|
||||
}
|
||||
|
||||
mach_timebase_info_data_t* Timebase() {
|
||||
static mach_timebase_info_data_t* timebase_info = TimebaseInternal();
|
||||
return timebase_info;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
uint64_t ClockMonotonicNanoseconds() {
|
||||
uint64_t absolute_time = mach_absolute_time();
|
||||
mach_timebase_info_data_t* timebase_info = Timebase();
|
||||
return absolute_time * timebase_info->numer / timebase_info->denom;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
94
util/misc/clock_test.cc
Normal file
94
util/misc/clock_test.cc
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright 2014 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/clock.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace crashpad;
|
||||
|
||||
TEST(Clock, ClockMonotonicNanoseconds) {
|
||||
uint64_t start = ClockMonotonicNanoseconds();
|
||||
EXPECT_GT(start, 0u);
|
||||
|
||||
uint64_t now = start;
|
||||
for (size_t iteration = 0; iteration < 10; ++iteration) {
|
||||
uint64_t last = now;
|
||||
now = ClockMonotonicNanoseconds();
|
||||
|
||||
// Use EXPECT_GE instead of EXPECT_GT, because there are no guarantees about
|
||||
// the clock’s resolution.
|
||||
EXPECT_GE(now, last);
|
||||
}
|
||||
|
||||
// SleepNanoseconds() should sleep for at least the value of the clock’s
|
||||
// resolution, so the clock’s value should definitely increase after a sleep.
|
||||
// EXPECT_GT can be used instead of EXPECT_GE after the sleep.
|
||||
SleepNanoseconds(1);
|
||||
now = ClockMonotonicNanoseconds();
|
||||
EXPECT_GT(now, start);
|
||||
}
|
||||
|
||||
void TestSleepNanoseconds(uint64_t nanoseconds) {
|
||||
uint64_t start = ClockMonotonicNanoseconds();
|
||||
|
||||
SleepNanoseconds(nanoseconds);
|
||||
|
||||
uint64_t end = ClockMonotonicNanoseconds();
|
||||
uint64_t diff = end - start;
|
||||
|
||||
// |nanoseconds| is the lower bound for the actual amount of time spent
|
||||
// sleeping.
|
||||
EXPECT_GE(diff, nanoseconds);
|
||||
|
||||
// It’s difficult to set an upper bound for the time spent sleeping. Allow
|
||||
// sleeps twice as long as requested, or sleeps a millisecond longer than
|
||||
// requested, whichever is larger. This is quite a lot of slop, but the
|
||||
// alternative would be test flakiness.
|
||||
uint64_t slop = std::max(static_cast<uint64_t>(1E6), nanoseconds);
|
||||
EXPECT_LE(diff, nanoseconds + slop);
|
||||
}
|
||||
|
||||
TEST(Clock, SleepNanoseconds) {
|
||||
const uint64_t kTestData[] = {
|
||||
0,
|
||||
1,
|
||||
static_cast<uint64_t>(1E3), // 1 microsecond
|
||||
static_cast<uint64_t>(1E4), // 10 microseconds
|
||||
static_cast<uint64_t>(1E5), // 100 microseconds
|
||||
static_cast<uint64_t>(1E6), // 1 millisecond
|
||||
static_cast<uint64_t>(1E7), // 10 milliseconds
|
||||
static_cast<uint64_t>(2E7), // 20 milliseconds
|
||||
static_cast<uint64_t>(5E7), // 50 milliseconds
|
||||
};
|
||||
|
||||
for (size_t index = 0; index < arraysize(kTestData); ++index) {
|
||||
const uint64_t nanoseconds = kTestData[index];
|
||||
SCOPED_TRACE(
|
||||
base::StringPrintf("index %zu, nanoseconds %llu", index, nanoseconds));
|
||||
|
||||
TestSleepNanoseconds(nanoseconds);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
@ -82,6 +82,9 @@
|
||||
'mach/symbolic_constants_mach.h',
|
||||
'mach/task_memory.cc',
|
||||
'mach/task_memory.h',
|
||||
'misc/clock.cc',
|
||||
'misc/clock.h',
|
||||
'misc/clock_mac.cc',
|
||||
'misc/initialization_state.h',
|
||||
'misc/initialization_state_dcheck.cc',
|
||||
'misc/initialization_state_dcheck.h',
|
||||
@ -216,6 +219,7 @@
|
||||
'mach/mach_message_server_test.cc',
|
||||
'mach/symbolic_constants_mach_test.cc',
|
||||
'mach/task_memory_test.cc',
|
||||
'misc/clock_test.cc',
|
||||
'misc/initialization_state_dcheck_test.cc',
|
||||
'misc/initialization_state_test.cc',
|
||||
'misc/scoped_forbid_return_test.cc',
|
||||
|
Loading…
x
Reference in New Issue
Block a user