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/BUILD b/aos/events/logging/BUILD
index fef834b..a687748 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -28,14 +28,31 @@
 )
 
 flatbuffer_cc_library(
+    name = "log_replayer_stats_fbs",
+    srcs = ["log_replayer_stats.fbs"],
+    gen_reflections = True,
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = [
+        ":replay_config_fbs",
+    ],
+)
+
+cc_static_flatbuffer(
+    name = "log_replayer_stats_schema",
+    function = "aos::LogReplayerStatsSchema",
+    target = ":log_replayer_stats_fbs_reflection_out",
+    visibility = ["//visibility:public"],
+)
+
+flatbuffer_cc_library(
     name = "replay_config_fbs",
     srcs = ["log_replayer_config.fbs"],
     gen_reflections = True,
-    includes = [
-        "//aos:configuration_fbs_includes",
-    ],
     target_compatible_with = ["@platforms//os:linux"],
     visibility = ["//visibility:public"],
+    deps = [
+        "//aos:configuration_fbs",
+    ],
 )
 
 cc_library(
@@ -59,6 +76,8 @@
     deps = [
         ":log_reader",
         ":log_reader_utils",
+        ":log_replayer_stats_fbs",
+        ":log_replayer_stats_schema",
         ":replay_config_fbs",
         ":replay_timing_fbs",
         ":replay_timing_schema",
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;
diff --git a/aos/events/logging/log_replayer_stats.fbs b/aos/events/logging/log_replayer_stats.fbs
new file mode 100644
index 0000000..8e54fdb
--- /dev/null
+++ b/aos/events/logging/log_replayer_stats.fbs
@@ -0,0 +1,19 @@
+include "log_replayer_config.fbs";
+
+namespace aos;
+
+table LogReplayerStats {
+	// The ReplayConfig passed to log_replayer.
+	replay_config:ReplayConfig (id: 0);
+	// Realtime start and end times of log replay, in nanoseconds.
+	realtime_start_time:int64 (id: 1);
+	realtime_end_time:int64 (id: 2);
+	// Monotonic start and end times of log replay, in nanoseconds.
+	monotonic_start_time:int64 (id: 3);
+	monotonic_end_time:int64 (id: 4);
+    // Name of the node where the log originated from.
+    // Note: Currently, only single node replay is supported.
+	node:string (id: 5);
+}
+
+root_type LogReplayerStats;