Add support for editing the logfile headers

This lets us fix bugs in logfiles if the need arises.

Change-Id: I374c4f6b3745835af2279d4b29f6f8098a5d7770
diff --git a/aos/events/logging/BUILD b/aos/events/logging/BUILD
index efbbcc6..2b3df8f 100644
--- a/aos/events/logging/BUILD
+++ b/aos/events/logging/BUILD
@@ -53,6 +53,22 @@
 )
 
 cc_binary(
+    name = "log_edit",
+    srcs = [
+        "log_edit.cc",
+    ],
+    deps = [
+        ":logger",
+        "//aos:configuration",
+        "//aos:init",
+        "//aos:json_to_flatbuffer",
+        "//aos/util:file",
+        "@com_github_gflags_gflags//:gflags",
+        "@com_github_google_glog//:glog",
+    ],
+)
+
+cc_binary(
     name = "log_stats",
     srcs = [
         "log_stats.cc",
diff --git a/aos/events/logging/log_edit.cc b/aos/events/logging/log_edit.cc
new file mode 100644
index 0000000..5db503a
--- /dev/null
+++ b/aos/events/logging/log_edit.cc
@@ -0,0 +1,62 @@
+#include <iostream>
+
+#include "aos/configuration.h"
+#include "aos/events/logging/logger.h"
+#include "aos/events/simulated_event_loop.h"
+#include "aos/init.h"
+#include "aos/json_to_flatbuffer.h"
+#include "aos/util/file.h"
+#include "gflags/gflags.h"
+
+DEFINE_string(logfile, "/tmp/logfile.bfbs",
+              "Name of the logfile to read from.");
+DEFINE_bool(
+    replace, false,
+    "If true, replace the header on the log file with the JSON header.");
+DEFINE_string(
+    header, "",
+    "If provided, this is the path to the JSON with the log file header.");
+
+int main(int argc, char **argv) {
+  gflags::SetUsageMessage(
+      R"(This tool lets us manipulate log files.)");
+  aos::InitGoogle(&argc, &argv);
+
+  if (!FLAGS_header.empty()) {
+    if (FLAGS_replace) {
+      const ::std::string header_json =
+          aos::util::ReadFileToStringOrDie(FLAGS_header);
+      flatbuffers::FlatBufferBuilder fbb;
+      fbb.ForceDefaults(1);
+      flatbuffers::Offset<aos::logger::LogFileHeader> header =
+          aos::JsonToFlatbuffer<aos::logger::LogFileHeader>(header_json, &fbb);
+
+      fbb.FinishSizePrefixed(header);
+
+      const std::string orig_path = FLAGS_logfile + ".orig";
+      PCHECK(rename(FLAGS_logfile.c_str(), orig_path.c_str()) == 0);
+
+      aos::logger::SpanReader span_reader(orig_path);
+      CHECK(span_reader.ReadMessage().empty());
+
+      aos::logger::DetachedBufferWriter buffer_writer(FLAGS_logfile);
+      buffer_writer.QueueSizedFlatbuffer(&fbb);
+
+      while (true) {
+        absl::Span<const uint8_t> msg_data = span_reader.ReadMessage();
+        if (msg_data == absl::Span<const uint8_t>()) {
+          break;
+        }
+
+        buffer_writer.WriteSizedFlatbuffer(msg_data);
+      }
+    } else {
+      aos::logger::MessageReader reader(FLAGS_logfile);
+      aos::util::WriteStringToFileOrDie(
+          FLAGS_header, aos::FlatbufferToJson(reader.log_file_header(), true));
+    }
+  }
+
+  aos::Cleanup();
+  return 0;
+}
diff --git a/aos/events/logging/logfile_utils.cc b/aos/events/logging/logfile_utils.cc
index e55b80d..a5ca084 100644
--- a/aos/events/logging/logfile_utils.cc
+++ b/aos/events/logging/logfile_utils.cc
@@ -39,6 +39,26 @@
   QueueSizedFlatbuffer(fbb->Release());
 }
 
+void DetachedBufferWriter::WriteSizedFlatbuffer(
+    absl::Span<const uint8_t> span) {
+  // Cheat aggressively...  Write out the queued up data, and then write this
+  // data once without buffering.  It is hard to make a DetachedBuffer out of
+  // this data, and we don't want to worry about lifetimes.
+  Flush();
+  iovec_.clear();
+  iovec_.reserve(1);
+
+  struct iovec n;
+  n.iov_base = const_cast<uint8_t *>(span.data());
+  n.iov_len = span.size();
+  iovec_.emplace_back(n);
+
+  const ssize_t written = writev(fd_, iovec_.data(), iovec_.size());
+
+  PCHECK(written == static_cast<ssize_t>(n.iov_len))
+      << ": Wrote " << written << " expected " << n.iov_len;
+}
+
 void DetachedBufferWriter::QueueSizedFlatbuffer(
     flatbuffers::DetachedBuffer &&buffer) {
   queued_size_ += buffer.size();
diff --git a/aos/events/logging/logfile_utils.h b/aos/events/logging/logfile_utils.h
index 6b8e9aa..5b2bfa6 100644
--- a/aos/events/logging/logfile_utils.h
+++ b/aos/events/logging/logfile_utils.h
@@ -44,6 +44,8 @@
   void QueueSizedFlatbuffer(flatbuffers::FlatBufferBuilder *fbb);
   // Queues up a detached buffer directly.
   void QueueSizedFlatbuffer(flatbuffers::DetachedBuffer &&buffer);
+  // Writes a Span.  This is not terribly optimized right now.
+  void WriteSizedFlatbuffer(absl::Span<const uint8_t> span);
 
   // Triggers data to be provided to the kernel and written.
   void Flush();