Add LogReplayerStats message

This change adds start and stop times of when replay happens in
log_replayer and the replay_config used as an fbs message that is sent
on the /replay channel when replay is complete.
The start and stop times are needed to properly determine
when exactly log replay was happening to analyze the results of a
resulting diagnostic log only within the context of replay.

Change-Id: Ic86c41f11689597986e02b6154f18bb5c8552b2c
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/events/logging/log_replayer.cc b/aos/events/logging/log_replayer.cc
index b69df4a..22d8b39 100644
--- a/aos/events/logging/log_replayer.cc
+++ b/aos/events/logging/log_replayer.cc
@@ -16,6 +16,8 @@
 #include "aos/events/logging/log_reader.h"
 #include "aos/events/logging/log_reader_utils.h"
 #include "aos/events/logging/log_replayer_config_generated.h"
+#include "aos/events/logging/log_replayer_stats_generated.h"
+#include "aos/events/logging/log_replayer_stats_schema.h"
 #include "aos/events/logging/logfile_sorting.h"
 #include "aos/events/logging/logfile_utils.h"
 #include "aos/events/logging/replay_timing_generated.h"
@@ -48,6 +50,8 @@
 DEFINE_string(merge_with_config, "",
               "A valid json string to be merged with config. This is used to "
               "add extra applications needed to run only for log_replayer");
+DEFINE_bool(print_stats, true,
+            "if set, prints the LogReplayerStats message as JSON to stdout");
 
 namespace aos::logger {
 
@@ -75,6 +79,16 @@
         aos::configuration::GetMyNode(raw_config), channel_overrides);
   }
 
+  // Add the LogReplayerStats channel
+  const aos::Configuration *raw_config = &config.message();
+  aos::ChannelT channel_overrides;
+  channel_overrides.max_size = 10000;
+  channel_overrides.frequency = 1;
+  config = aos::configuration::AddChannelToConfiguration(
+      raw_config, "/replay",
+      aos::FlatbufferSpan<reflection::Schema>(aos::LogReplayerStatsSchema()),
+      aos::configuration::GetMyNode(raw_config), channel_overrides);
+
   if (!FLAGS_merge_with_config.empty()) {
     config = aos::configuration::MergeWithConfig(&config.message(),
                                                  FLAGS_merge_with_config);
@@ -139,7 +153,49 @@
     event_loop.SkipAosLog();
     event_loop.SkipTimingReport();
 
+    aos::Sender<aos::LogReplayerStats> stats_sender =
+        event_loop.MakeSender<aos::LogReplayerStats>("/replay");
+    auto builder = stats_sender.MakeBuilder();
+    auto node_name = builder.fbb()->CreateString(event_loop.node()->name());
+    flatbuffers::Offset<aos::ReplayConfig> replay_config_offset;
+    if (replay_config.has_value()) {
+      replay_config_offset =
+          aos::CopyFlatBuffer(&replay_config.value().message(), builder.fbb());
+    }
+
+    auto stats_builder = builder.MakeBuilder<aos::LogReplayerStats>();
+    if (replay_config.has_value()) {
+      stats_builder.add_replay_config(replay_config_offset);
+    }
+
     reader.Register(&event_loop);
+
+    // Save off the start and end times of replay.
+    reader.OnStart(event_loop.node(), [&event_loop, &stats_builder,
+                                       &node_name]() {
+      stats_builder.add_node(node_name);
+      stats_builder.add_realtime_start_time(
+          std::chrono::nanoseconds(event_loop.realtime_now().time_since_epoch())
+              .count());
+
+      stats_builder.add_monotonic_start_time(
+          std::chrono::nanoseconds(
+              event_loop.monotonic_now().time_since_epoch())
+              .count());
+    });
+
+    reader.OnEnd(event_loop.node(), [&event_loop, &stats_builder, &builder]() {
+      stats_builder.add_realtime_end_time(
+          std::chrono::nanoseconds(event_loop.realtime_now().time_since_epoch())
+              .count());
+
+      stats_builder.add_monotonic_end_time(
+          std::chrono::nanoseconds(
+              event_loop.monotonic_now().time_since_epoch())
+              .count());
+      builder.CheckOk(builder.Send(stats_builder.Finish()));
+    });
+
     reader.OnEnd(event_loop.node(), [&event_loop]() { event_loop.Exit(); });
 
     if (FLAGS_plot_timing) {
@@ -152,6 +208,13 @@
     event_loop.Run();
 
     reader.Deregister();
+
+    if (FLAGS_print_stats) {
+      aos::Fetcher<aos::LogReplayerStats> stats_fetcher =
+          event_loop.MakeFetcher<aos::LogReplayerStats>("/replay");
+      CHECK(stats_fetcher.Fetch()) << "Failed to fetch LogReplayerStats!";
+      std::cout << aos::FlatbufferToJson(stats_fetcher.get());
+    }
   }
 
   return EXIT_SUCCESS;