Enable renaming logged locations
This adds two main features:
-Renaming logged locations so that they don't interfere with attempted
replay.
-Using a different (than logged) configuration for the replay so that we
can take advantage of updated schemas or the such.
Change-Id: I81a0da10fd60a32be2e7ea87ffe98d9b3043198c
diff --git a/aos/events/logging/logger.cc b/aos/events/logging/logger.cc
index 3e790b6..486cda9 100644
--- a/aos/events/logging/logger.cc
+++ b/aos/events/logging/logger.cc
@@ -219,20 +219,40 @@
writer_->Flush();
}
-LogReader::LogReader(std::string_view filename)
- : sorted_message_reader_(filename) {
- channels_.resize(configuration()->channels()->size());
+LogReader::LogReader(std::string_view filename,
+ const Configuration *replay_configuration)
+ : sorted_message_reader_(filename),
+ replay_configuration_(replay_configuration) {
+ channels_.resize(logged_configuration()->channels()->size());
}
LogReader::~LogReader() {
Deregister();
}
-const Configuration *LogReader::configuration() const {
+const Configuration *LogReader::logged_configuration() const {
return sorted_message_reader_.configuration();
}
-const Node *LogReader::node() const { return sorted_message_reader_.node(); }
+const Configuration *LogReader::configuration() const {
+ CHECK(remapped_configuration_ != nullptr)
+ << ": Need to call Register() before the remapped config will be "
+ "generated.";
+ return remapped_configuration_;
+}
+
+const Node *LogReader::node() const {
+ // Because the Node pointer will only be valid if it actually points to memory
+ // owned by remapped_configuration_, we need to wait for the
+ // remapped_configuration_ to be populated before accessing it.
+ CHECK(remapped_configuration_ != nullptr)
+ << ": Need to call Register before the node() pointer will be valid.";
+ if (sorted_message_reader_.node() == nullptr) {
+ return nullptr;
+ }
+ return configuration::GetNode(
+ configuration(), sorted_message_reader_.node()->name()->string_view());
+}
monotonic_clock::time_point LogReader::monotonic_start_time() {
return sorted_message_reader_.monotonic_start_time();
@@ -243,6 +263,7 @@
}
void LogReader::Register() {
+ MakeRemappedConfig();
event_loop_factory_unique_ptr_ =
std::make_unique<SimulatedEventLoopFactory>(configuration(), node());
Register(event_loop_factory_unique_ptr_.get());
@@ -268,13 +289,21 @@
event_loop_->SkipTimingReport();
for (size_t i = 0; i < channels_.size(); ++i) {
- CHECK_EQ(configuration()->channels()->Get(i)->name(),
- event_loop_->configuration()->channels()->Get(i)->name());
- CHECK_EQ(configuration()->channels()->Get(i)->type(),
- event_loop_->configuration()->channels()->Get(i)->type());
+ const Channel *const original_channel =
+ logged_configuration()->channels()->Get(i);
- channels_[i] = event_loop_->MakeRawSender(
- event_loop_->configuration()->channels()->Get(i));
+ std::string_view channel_name = original_channel->name()->string_view();
+ std::string_view channel_type = original_channel->type()->string_view();
+ // If the channel is remapped, find the correct channel name to use.
+ if (remapped_channels_.count(i) > 0) {
+ VLOG(2) << "Got remapped channel on "
+ << configuration::CleanedChannelToString(original_channel);
+ channel_name = remapped_channels_[i];
+ }
+ VLOG(1) << "Going to remap channel " << channel_name << " " << channel_type;
+ channels_[i] = event_loop_->MakeRawSender(CHECK_NOTNULL(
+ configuration::GetChannel(event_loop_->configuration(), channel_name,
+ channel_type, "", nullptr)));
}
timer_handler_ = event_loop_->AddTimer([this]() {
@@ -352,5 +381,109 @@
event_loop_factory_ = nullptr;
}
+void LogReader::RemapLoggedChannel(std::string_view name, std::string_view type,
+ std::string_view add_prefix) {
+ CHECK(remapped_configuration_ == nullptr)
+ << "Must call RemapLoggedChannel before calling Register().";
+ for (size_t ii = 0; ii < logged_configuration()->channels()->size(); ++ii) {
+ const Channel *const channel = logged_configuration()->channels()->Get(ii);
+ if (channel->name()->str() == name &&
+ channel->type()->string_view() == type) {
+ CHECK_EQ(0u, remapped_channels_.count(ii))
+ << "Already remapped channel "
+ << configuration::CleanedChannelToString(channel);
+ remapped_channels_[ii] = std::string(add_prefix) + std::string(name);
+ VLOG(1) << "Remapping channel "
+ << configuration::CleanedChannelToString(channel)
+ << " to have name " << remapped_channels_[ii];
+ return;
+ }
+ }
+ LOG(FATAL) << "Unabled to locate channel with name " << name << " and type "
+ << type;
+}
+
+void LogReader::MakeRemappedConfig() {
+ // If no remapping occurred and we are using the original config, then there
+ // is nothing interesting to do here.
+ if (remapped_channels_.empty() && replay_configuration_ == nullptr) {
+ remapped_configuration_ = sorted_message_reader_.configuration();
+ return;
+ }
+ // Config to copy Channel definitions from. Use the specified
+ // replay_configuration_ if it has been provided.
+ const Configuration *const base_config = replay_configuration_ == nullptr
+ ? logged_configuration()
+ : replay_configuration_;
+ // The remapped config will be identical to the base_config, except that it
+ // will have a bunch of extra channels in the channel list, which are exact
+ // copies of the remapped channels, but with different names.
+ // Because the flatbuffers API is a pain to work with, this requires a bit of
+ // a song-and-dance to get copied over.
+ // The order of operations is to:
+ // 1) Make a flatbuffer builder for a config that will just contain a list of
+ // the new channels that we want to add.
+ // 2) For each channel that we are remapping:
+ // a) Make a buffer/builder and construct into it a Channel table that only
+ // contains the new name for the channel.
+ // b) Merge the new channel with just the name into the channel that we are
+ // trying to copy, built in the flatbuffer builder made in 1. This gives
+ // us the new channel definition that we need.
+ // 3) Using this list of offsets, build the Configuration of just new
+ // Channels.
+ // 4) Merge the Configuration with the new Channels into the base_config.
+ // 5) Call MergeConfiguration() on that result to give MergeConfiguration a
+ // chance to sanitize the config.
+
+ // This is the builder that we use for the config containing all the new
+ // channels.
+ flatbuffers::FlatBufferBuilder new_config_fbb;
+ new_config_fbb.ForceDefaults(1);
+ std::vector<flatbuffers::Offset<Channel>> channel_offsets;
+ for (auto &pair : remapped_channels_) {
+ // This is the builder that we use for creating the Channel with just the
+ // new name.
+ flatbuffers::FlatBufferBuilder new_name_fbb;
+ new_name_fbb.ForceDefaults(1);
+ const flatbuffers::Offset<flatbuffers::String> name_offset =
+ new_name_fbb.CreateString(pair.second);
+ ChannelBuilder new_name_builder(new_name_fbb);
+ new_name_builder.add_name(name_offset);
+ new_name_fbb.Finish(new_name_builder.Finish());
+ const FlatbufferDetachedBuffer<Channel> new_name = new_name_fbb.Release();
+ // Retrieve the channel that we want to copy, confirming that it is actually
+ // present in base_config.
+ const Channel *const base_channel = CHECK_NOTNULL(configuration::GetChannel(
+ base_config, logged_configuration()->channels()->Get(pair.first), "",
+ nullptr));
+ // Actually create the new channel and put it into the vector of Offsets
+ // that we will use to create the new Configuration.
+ channel_offsets.emplace_back(MergeFlatBuffers<Channel>(
+ reinterpret_cast<const flatbuffers::Table *>(base_channel),
+ reinterpret_cast<const flatbuffers::Table *>(&new_name.message()),
+ &new_config_fbb));
+ }
+ // Create the Configuration containing the new channels that we want to add.
+ const auto
+ new_name_vector_offsets = new_config_fbb.CreateVector(channel_offsets);
+ ConfigurationBuilder new_config_builder(new_config_fbb);
+ new_config_builder.add_channels(new_name_vector_offsets);
+ new_config_fbb.Finish(new_config_builder.Finish());
+ const FlatbufferDetachedBuffer<Configuration> new_name_config =
+ new_config_fbb.Release();
+ // Merge the new channels configuration into the base_config, giving us the
+ // remapped configuration.
+ remapped_configuration_buffer_ =
+ std::make_unique<FlatbufferDetachedBuffer<Configuration>>(
+ MergeFlatBuffers<Configuration>(base_config,
+ &new_name_config.message()));
+ // Call MergeConfiguration to deal with sanitizing the config.
+ remapped_configuration_buffer_ =
+ std::make_unique<FlatbufferDetachedBuffer<Configuration>>(
+ configuration::MergeConfiguration(*remapped_configuration_buffer_));
+
+ remapped_configuration_ = &remapped_configuration_buffer_->message();
+}
+
} // namespace logger
} // namespace aos