Add base/time.h
Introduces GetWallTime() and GetThreadTime().
The latter is required for benchmarks.
Bug: 73612642
Test: perfetto_unittests --gtest_filter=TimeTest*
Change-Id: I3eea8bcc3b3a98c10c1fad7d99875b5f4fd2ab2f
diff --git a/Android.bp b/Android.bp
index a820f17..9911868 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3084,6 +3084,7 @@
"src/base/test/vm_test_utils.cc",
"src/base/thread_checker.cc",
"src/base/thread_checker_unittest.cc",
+ "src/base/time_unittest.cc",
"src/base/unix_task_runner.cc",
"src/base/utils_unittest.cc",
"src/base/watchdog.cc",
diff --git a/include/perfetto/base/BUILD.gn b/include/perfetto/base/BUILD.gn
index 1a4c10a..c9efc2d 100644
--- a/include/perfetto/base/BUILD.gn
+++ b/include/perfetto/base/BUILD.gn
@@ -22,6 +22,7 @@
"string_splitter.h",
"task_runner.h",
"thread_checker.h",
+ "time.h",
"unix_task_runner.h",
"utils.h",
"watchdog.h",
diff --git a/include/perfetto/base/android_task_runner.h b/include/perfetto/base/android_task_runner.h
index 37abed6..6d3fa4e 100644
--- a/include/perfetto/base/android_task_runner.h
+++ b/include/perfetto/base/android_task_runner.h
@@ -20,6 +20,7 @@
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/task_runner.h"
#include "perfetto/base/thread_checker.h"
+#include "perfetto/base/time.h"
#include <poll.h>
#include <chrono>
@@ -58,35 +59,6 @@
void RemoveFileDescriptorWatch(int fd) override;
private:
- class TimePoint : public timespec {
- public:
- TimePoint() { tv_sec = tv_nsec = 0; }
-
- bool operator<(const TimePoint& other) const {
- if (tv_sec == other.tv_sec)
- return tv_nsec < other.tv_nsec;
- return tv_sec < other.tv_sec;
- }
-
- explicit operator bool() const { return tv_sec || tv_nsec; }
-
- TimePoint AdvanceByMs(time_t ms) {
- const time_t kSecondsPerNs = 1000000000;
- PERFETTO_DCHECK(tv_nsec < kSecondsPerNs);
- TimePoint result(*this);
- time_t seconds = ms / 1000;
- result.tv_sec += seconds;
- ms -= seconds * 1000;
- result.tv_nsec += ms * 1000 * 1000;
- if (result.tv_nsec >= kSecondsPerNs) {
- result.tv_sec++;
- result.tv_nsec -= kSecondsPerNs;
- }
- return result;
- }
- };
- TimePoint GetTime() const;
-
bool OnFileDescriptorEvent(int signalled_fd, int events);
void RunImmediateTask();
void RunDelayedTask();
@@ -94,7 +66,7 @@
void GetNextDelayedTaskRunTimeLocked(struct itimerspec* runtime);
void ScheduleImmediateWakeUp();
- void ScheduleDelayedWakeUp(const TimePoint& time);
+ void ScheduleDelayedWakeUp(TimeMillis time);
ALooper* const looper_;
ScopedFile immediate_event_;
@@ -107,7 +79,7 @@
// Note: std::deque allocates blocks of 4k in some implementations. Consider
// another data structure if we end up having many task runner instances.
std::deque<std::function<void()>> immediate_tasks_;
- std::multimap<TimePoint, std::function<void()>> delayed_tasks_;
+ std::multimap<TimeMillis, std::function<void()>> delayed_tasks_;
std::map<int, std::function<void()>> watch_tasks_;
bool quit_ = false;
// --- End lock-protected members.
diff --git a/include/perfetto/base/time.h b/include/perfetto/base/time.h
new file mode 100644
index 0000000..d0c2744
--- /dev/null
+++ b/include/perfetto/base/time.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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 INCLUDE_PERFETTO_BASE_TIME_H_
+#define INCLUDE_PERFETTO_BASE_TIME_H_
+
+#include <time.h>
+
+#include <chrono>
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace base {
+
+using TimeMillis = std::chrono::milliseconds;
+using TimeNanos = std::chrono::nanoseconds;
+constexpr clockid_t kWallTimeClockSource = CLOCK_MONOTONIC;
+
+inline TimeNanos GetTimeInternalNs(clockid_t clk_id) {
+ struct timespec ts = {};
+ PERFETTO_CHECK(clock_gettime(clk_id, &ts) == 0);
+ return TimeNanos(ts.tv_sec * 1000000000LL + ts.tv_nsec);
+}
+
+inline TimeNanos GetWallTimeNs() {
+ return GetTimeInternalNs(kWallTimeClockSource);
+}
+
+inline TimeMillis GetWallTimeMs() {
+ return std::chrono::duration_cast<TimeMillis>(GetWallTimeNs());
+}
+
+inline TimeNanos GetThreadCPUTimeNs() {
+ return GetTimeInternalNs(CLOCK_THREAD_CPUTIME_ID);
+}
+
+inline struct timespec ToPosixTimespec(TimeMillis time) {
+ struct timespec ts {};
+ const long time_s = static_cast<long>(time.count() / 1000);
+ ts.tv_sec = time_s;
+ ts.tv_nsec = (static_cast<long>(time.count()) - time_s * 1000L) * 1000000L;
+ return ts;
+}
+
+} // namespace base
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_BASE_TIME_H_
diff --git a/include/perfetto/base/unix_task_runner.h b/include/perfetto/base/unix_task_runner.h
index 23da0ff..0bc2f64 100644
--- a/include/perfetto/base/unix_task_runner.h
+++ b/include/perfetto/base/unix_task_runner.h
@@ -20,6 +20,7 @@
#include "perfetto/base/scoped_file.h"
#include "perfetto/base/task_runner.h"
#include "perfetto/base/thread_checker.h"
+#include "perfetto/base/time.h"
#include <poll.h>
#include <chrono>
@@ -53,15 +54,11 @@
void RemoveFileDescriptorWatch(int fd) override;
private:
- using TimePoint = std::chrono::time_point<std::chrono::steady_clock>;
- using TimeDurationMs = std::chrono::milliseconds;
- TimePoint GetTime() const;
-
void WakeUp();
void UpdateWatchTasksLocked();
- TimeDurationMs GetDelayToNextTaskLocked() const;
+ int GetDelayMsToNextTaskLocked() const;
void RunImmediateAndDelayedTask();
void PostFileDescriptorWatches();
void RunFileDescriptorWatch(int fd);
@@ -78,7 +75,7 @@
std::mutex lock_;
std::deque<std::function<void()>> immediate_tasks_;
- std::multimap<TimePoint, std::function<void()>> delayed_tasks_;
+ std::multimap<TimeMillis, std::function<void()>> delayed_tasks_;
bool quit_ = false;
struct WatchTask {
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 1c3890e..41c47d0 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -105,6 +105,7 @@
"task_runner_unittest.cc",
"temp_file_unittest.cc",
"thread_checker_unittest.cc",
+ "time_unittest.cc",
"utils_unittest.cc",
"weak_ptr_unittest.cc",
]
diff --git a/src/base/android_task_runner.cc b/src/base/android_task_runner.cc
index 850c30e..dfd8ead 100644
--- a/src/base/android_task_runner.cc
+++ b/src/base/android_task_runner.cc
@@ -27,7 +27,7 @@
: looper_(ALooper_prepare(0 /* require callbacks */)),
immediate_event_(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)),
delayed_timer_(
- timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC)) {
+ timerfd_create(kWallTimeClockSource, TFD_NONBLOCK | TFD_CLOEXEC)) {
ALooper_acquire(looper_);
PERFETTO_CHECK(immediate_event_);
PERFETTO_CHECK(delayed_timer_);
@@ -80,15 +80,6 @@
return immediate_tasks_.empty();
}
-AndroidTaskRunner::TimePoint AndroidTaskRunner::GetTime() const {
- static_assert(sizeof(TimePoint) == sizeof(struct timespec),
- "TimePoint layout must match struct timespec");
- TimePoint now;
- if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
- PERFETTO_DPLOG("clock_gettime");
- return now;
-}
-
void AndroidTaskRunner::RunImmediateTask() {
uint64_t unused = 0;
if (read(immediate_event_.get(), &unused, sizeof(unused)) != sizeof(unused) &&
@@ -124,19 +115,19 @@
}
std::function<void()> delayed_task;
- TimePoint next_wake_up;
+ TimeMillis next_wake_up{};
{
std::lock_guard<std::mutex> lock(lock_);
if (delayed_tasks_.empty())
return;
auto it = delayed_tasks_.begin();
- PERFETTO_DCHECK(!(GetTime() < it->first));
+ PERFETTO_DCHECK(!(GetWallTimeMs() < it->first));
delayed_task = std::move(it->second);
delayed_tasks_.erase(it);
if (!delayed_tasks_.empty())
next_wake_up = delayed_tasks_.begin()->first;
}
- if (next_wake_up)
+ if (next_wake_up.count())
ScheduleDelayedWakeUp(next_wake_up);
errno = 0;
RunTask(delayed_task);
@@ -150,10 +141,10 @@
}
}
-void AndroidTaskRunner::ScheduleDelayedWakeUp(const TimePoint& time) {
- PERFETTO_DCHECK(time);
+void AndroidTaskRunner::ScheduleDelayedWakeUp(TimeMillis time) {
+ PERFETTO_DCHECK(time.count());
struct itimerspec wake_up = {};
- wake_up.it_value = time;
+ wake_up.it_value = ToPosixTimespec(time);
if (timerfd_settime(delayed_timer_.get(), TFD_TIMER_ABSTIME, &wake_up,
nullptr) == -1) {
PERFETTO_DPLOG("timerfd_settime");
@@ -174,7 +165,7 @@
void AndroidTaskRunner::PostDelayedTask(std::function<void()> task,
int delay_ms) {
PERFETTO_DCHECK(delay_ms >= 0);
- auto runtime = GetTime().AdvanceByMs(delay_ms);
+ TimeMillis runtime = GetWallTimeMs() + TimeMillis(delay_ms);
bool is_next = false;
{
std::lock_guard<std::mutex> lock(lock_);
diff --git a/src/base/time_unittest.cc b/src/base/time_unittest.cc
new file mode 100644
index 0000000..0a89632
--- /dev/null
+++ b/src/base/time_unittest.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 "perfetto/base/time.h"
+
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace base {
+namespace {
+
+TEST(TimeTest, Conversions) {
+ TimeMillis ms = GetWallTimeMs();
+ TimeNanos ns = GetWallTimeNs();
+ EXPECT_NEAR(ms.count(), ns.count() / 1000000, 1000);
+
+ {
+ struct timespec ts = ToPosixTimespec(TimeMillis(0));
+ EXPECT_EQ(0, ts.tv_sec);
+ EXPECT_EQ(0, ts.tv_nsec);
+ }
+ {
+ struct timespec ts = ToPosixTimespec(TimeMillis(1));
+ EXPECT_EQ(0, ts.tv_sec);
+ EXPECT_EQ(1000000, ts.tv_nsec);
+ }
+ {
+ struct timespec ts = ToPosixTimespec(TimeMillis(12345));
+ EXPECT_EQ(12, ts.tv_sec);
+ EXPECT_EQ(345000000, ts.tv_nsec);
+ }
+ {
+ struct timespec ts = ToPosixTimespec(TimeMillis(1000000000001LL));
+ EXPECT_EQ(1000000000, ts.tv_sec);
+ EXPECT_EQ(1000000, ts.tv_nsec);
+ }
+}
+
+} // namespace
+} // namespace base
+} // namespace perfetto
diff --git a/src/base/unix_task_runner.cc b/src/base/unix_task_runner.cc
index d182e63..76b344b 100644
--- a/src/base/unix_task_runner.cc
+++ b/src/base/unix_task_runner.cc
@@ -23,6 +23,8 @@
#include <stdlib.h>
#include <unistd.h>
+#include <limits>
+
namespace perfetto {
namespace base {
@@ -58,10 +60,6 @@
UnixTaskRunner::~UnixTaskRunner() = default;
-UnixTaskRunner::TimePoint UnixTaskRunner::GetTime() const {
- return std::chrono::steady_clock::now();
-}
-
void UnixTaskRunner::WakeUp() {
const char dummy = 'P';
if (write(control_write_.get(), &dummy, 1) <= 0 && errno != EAGAIN)
@@ -77,7 +75,7 @@
std::lock_guard<std::mutex> lock(lock_);
if (quit_)
return;
- poll_timeout_ms = static_cast<int>(GetDelayToNextTaskLocked().count());
+ poll_timeout_ms = GetDelayMsToNextTaskLocked();
UpdateWatchTasksLocked();
}
int ret = PERFETTO_EINTR(poll(
@@ -121,7 +119,7 @@
// becomes an issue.
std::function<void()> immediate_task;
std::function<void()> delayed_task;
- auto now = GetTime();
+ TimeMillis now = GetWallTimeMs();
{
std::lock_guard<std::mutex> lock(lock_);
if (!immediate_tasks_.empty()) {
@@ -197,17 +195,15 @@
RunTask(task);
}
-UnixTaskRunner::TimeDurationMs UnixTaskRunner::GetDelayToNextTaskLocked()
- const {
+int UnixTaskRunner::GetDelayMsToNextTaskLocked() const {
PERFETTO_DCHECK_THREAD(thread_checker_);
if (!immediate_tasks_.empty())
- return TimeDurationMs(0);
+ return 0;
if (!delayed_tasks_.empty()) {
- return std::max(TimeDurationMs(0),
- std::chrono::duration_cast<TimeDurationMs>(
- delayed_tasks_.begin()->first - GetTime()));
+ TimeMillis diff = delayed_tasks_.begin()->first - GetWallTimeMs();
+ return std::max(0, static_cast<int>(diff.count()));
}
- return TimeDurationMs(-1);
+ return -1;
}
void UnixTaskRunner::PostTask(std::function<void()> task) {
@@ -223,7 +219,7 @@
void UnixTaskRunner::PostDelayedTask(std::function<void()> task, int delay_ms) {
PERFETTO_DCHECK(delay_ms >= 0);
- auto runtime = GetTime() + std::chrono::milliseconds(delay_ms);
+ TimeMillis runtime = GetWallTimeMs() + TimeMillis(delay_ms);
{
std::lock_guard<std::mutex> lock(lock_);
delayed_tasks_.insert(std::make_pair(runtime, std::move(task)));
diff --git a/src/ftrace_reader/ftrace_controller.cc b/src/ftrace_reader/ftrace_controller.cc
index da49221..cf4c80b 100644
--- a/src/ftrace_reader/ftrace_controller.cc
+++ b/src/ftrace_reader/ftrace_controller.cc
@@ -30,6 +30,7 @@
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
+#include "perfetto/base/time.h"
#include "perfetto/base/utils.h"
#include "src/ftrace_reader/cpu_reader.h"
#include "src/ftrace_reader/event_info.h"
@@ -142,9 +143,7 @@
}
uint64_t FtraceController::NowMs() const {
- timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- return (now.tv_sec * 1000000000L + now.tv_nsec) / 1000000L;
+ return base::GetWallTimeMs().count();
}
// static