Support replaying the realtime clock from logs
This adds the concept of the realtime offset to the event scheduler.
Side note: the event scheduler is going to have to get significantly
more complicated when multi-node log files show up.
Change-Id: Ia6f891c77b8c3badcea930cdfa0e236acbff7801
diff --git a/aos/events/event_scheduler.h b/aos/events/event_scheduler.h
index 2fcf90b..432f4ad 100644
--- a/aos/events/event_scheduler.h
+++ b/aos/events/event_scheduler.h
@@ -45,15 +45,23 @@
bool is_running() const { return is_running_; }
monotonic_clock::time_point monotonic_now() const { return now_; }
+
realtime_clock::time_point realtime_now() const {
- // TODO(austin): Make this all configurable...
- return realtime_clock::epoch() + now_.time_since_epoch() +
- std::chrono::seconds(1000000);
+ return realtime_clock::time_point(monotonic_now().time_since_epoch() +
+ realtime_offset_);
+ }
+
+ // Sets realtime clock to realtime_now for a given monotonic clock.
+ void SetRealtimeOffset(monotonic_clock::time_point monotonic_now,
+ realtime_clock::time_point realtime_now) {
+ realtime_offset_ =
+ realtime_now.time_since_epoch() - monotonic_now.time_since_epoch();
}
private:
// Current execution time.
monotonic_clock::time_point now_ = monotonic_clock::epoch();
+ std::chrono::nanoseconds realtime_offset_ = std::chrono::seconds(0);
std::vector<std::function<void()>> on_run_;
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index 3df5592..94e25c2 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -14,10 +14,12 @@
name = "logger",
srcs = ["logger.cc"],
hdrs = ["logger.h"],
+ visibility = ["//visibility:public"],
deps = [
":logger_fbs",
"//aos:flatbuffer_merge",
"//aos/events:event_loop",
+ "//aos/events:simulated_event_loop",
"//aos/network:team_number",
"//aos/time",
"@com_github_google_flatbuffers//:flatbuffers",
diff --git a/aos/events/logging/log_cat.cc b/aos/events/logging/log_cat.cc
index 9c75076..a53d337 100644
--- a/aos/events/logging/log_cat.cc
+++ b/aos/events/logging/log_cat.cc
@@ -29,12 +29,7 @@
aos::logger::LogReader reader(FLAGS_logfile);
aos::SimulatedEventLoopFactory log_reader_factory(reader.configuration());
- std::unique_ptr<aos::EventLoop> reader_event_loop =
- log_reader_factory.MakeEventLoop("log_reader");
- reader.Register(reader_event_loop.get());
- // We don't run timing reports when trying to print out logged data, because
- // otherwise we would end up printing out the timing reports themselves...
- reader_event_loop->SkipTimingReport();
+ reader.Register(&log_reader_factory);
std::unique_ptr<aos::EventLoop> printer_event_loop =
log_reader_factory.MakeEventLoop("printer");
@@ -58,11 +53,10 @@
// unnecessary cruft from glog and to allow the user to readily
// redirect just the logged output independent of any debugging
// information on stderr.
- // TODO(james): Also print out realtime time once we support
- // replaying it.
- std::cout << channel->name()->c_str() << ' '
- << channel->type()->c_str() << " at "
- << context.monotonic_event_time << ": "
+ std::cout << context.realtime_event_time << " ("
+ << context.monotonic_event_time << ") "
+ << channel->name()->c_str() << ' '
+ << channel->type()->c_str() << ": "
<< aos::FlatbufferToJson(
channel->schema(),
static_cast<const uint8_t *>(message))
diff --git a/aos/events/logging/logger.cc b/aos/events/logging/logger.cc
index e38c476..e35fe5b 100644
--- a/aos/events/logging/logger.cc
+++ b/aos/events/logging/logger.cc
@@ -390,6 +390,19 @@
->realtime_start_time()));
}
+void LogReader::Register(SimulatedEventLoopFactory *event_loop_factory) {
+ event_loop_unique_ptr_ = event_loop_factory->MakeEventLoop("log_reader");
+ event_loop_factory_ = event_loop_factory;
+ // We don't run timing reports when trying to print out logged data, because
+ // otherwise we would end up printing out the timing reports themselves...
+ // This is only really relevant when we are replaying into a simulation.
+ event_loop_unique_ptr_->SkipTimingReport();
+
+ Register(event_loop_unique_ptr_.get());
+ event_loop_factory_->RunFor(monotonic_start_time() -
+ event_loop_factory_->monotonic_now());
+}
+
void LogReader::Register(EventLoop *event_loop) {
event_loop_ = event_loop;
@@ -422,7 +435,17 @@
FlatbufferVector<MessageHeader> front = std::move(channel.front());
CHECK(front.message().data() != nullptr);
+
if (oldest_channel_index.first > monotonic_start_time()) {
+ // If we have access to the factory, use it to fix the realtime time.
+ if (event_loop_factory_ != nullptr) {
+ event_loop_factory_->SetRealtimeOffset(
+ monotonic_clock::time_point(
+ chrono::nanoseconds(front.message().monotonic_sent_time())),
+ realtime_clock::time_point(
+ chrono::nanoseconds(front.message().realtime_sent_time())));
+ }
+
channel.raw_sender->Send(front.message().data()->Data(),
front.message().data()->size());
} else {
@@ -463,6 +486,10 @@
for (size_t i = 0; i < channels_.size(); ++i) {
channels_[i].raw_sender.reset();
}
+
+ event_loop_factory_ = nullptr;
+ event_loop_unique_ptr_.reset();
+ event_loop_ = nullptr;
}
void LogReader::EmplaceDataBack(FlatbufferVector<MessageHeader> &&new_data) {
diff --git a/aos/events/logging/logger.h b/aos/events/logging/logger.h
index db23767..497dabb 100644
--- a/aos/events/logging/logger.h
+++ b/aos/events/logging/logger.h
@@ -8,6 +8,7 @@
#include "absl/types/span.h"
#include "aos/events/event_loop.h"
#include "aos/events/logging/logger_generated.h"
+#include "aos/events/simulated_event_loop.h"
#include "aos/time/time.h"
#include "flatbuffers/flatbuffers.h"
@@ -96,6 +97,9 @@
// Registers the timer and senders used to resend the messages from the log
// file.
void Register(EventLoop *event_loop);
+ // Registers everything, but also updates the real time time in sync. Runs
+ // until the log file starts.
+ void Register(SimulatedEventLoopFactory *factory);
// Unregisters the senders.
void Deregister();
@@ -186,7 +190,9 @@
// File descriptor for the log file.
int fd_ = -1;
- EventLoop *event_loop_;
+ SimulatedEventLoopFactory *event_loop_factory_ = nullptr;
+ std::unique_ptr<EventLoop> event_loop_unique_ptr_;
+ EventLoop *event_loop_ = nullptr;
TimerHandler *timer_handler_;
// Vector to read into. This uses an allocator which doesn't zero initialize
diff --git a/aos/events/logging/logger_test.cc b/aos/events/logging/logger_test.cc
index c627101..9aae936 100644
--- a/aos/events/logging/logger_test.cc
+++ b/aos/events/logging/logger_test.cc
@@ -63,22 +63,11 @@
LOG(INFO) << "Config " << FlatbufferToJson(reader.configuration());
- // TODO(austin): Figure out what the API needs to look like. How do we replay
- // the data that was fetched before it all starts? How do we set the starting
- // time from the log file? Probably need to let the reader do more if it
- // knows about the factory.
SimulatedEventLoopFactory log_reader_factory(reader.configuration());
- std::unique_ptr<EventLoop> reader_event_loop =
- log_reader_factory.MakeEventLoop("log_reader");
- reader.Register(reader_event_loop.get());
+ // This sends out the fetched messages and advances time to the start of the log file.
+ reader.Register(&log_reader_factory);
- // Capture monotonic start time in OnRun and offset from there? Let the user
- // configure the factory if they want time to match?
- /*
- log_reader_factory.InitializeTime(log_reader.monotonic_start_time(),
- log_reader.realtime_start_time());
- */
std::unique_ptr<EventLoop> test_event_loop =
log_reader_factory.MakeEventLoop("log_reader");
diff --git a/aos/events/simulated_event_loop.h b/aos/events/simulated_event_loop.h
index 740a4c2..fee3ef0 100644
--- a/aos/events/simulated_event_loop.h
+++ b/aos/events/simulated_event_loop.h
@@ -81,6 +81,12 @@
return scheduler_.realtime_now();
}
+ // Sets realtime clock to realtime_now for a given monotonic clock.
+ void SetRealtimeOffset(monotonic_clock::time_point monotonic_now,
+ realtime_clock::time_point realtime_now) {
+ scheduler_.SetRealtimeOffset(monotonic_now, realtime_now);
+ }
+
private:
const Configuration *const configuration_;
EventScheduler scheduler_;