blob: 2de17d748c2d827df05fbaa0e28ed38d1ebaf386 [file] [log] [blame]
James Kuszmaul38735e82019-12-07 16:42:06 -08001#include "aos/events/logging/logger.h"
Austin Schuhe309d2a2019-11-29 13:25:21 -08002
3#include <fcntl.h>
Austin Schuh4c4e0092019-12-22 16:18:03 -08004#include <limits.h>
Austin Schuhe309d2a2019-11-29 13:25:21 -08005#include <sys/stat.h>
6#include <sys/types.h>
7#include <sys/uio.h>
8#include <vector>
9
Austin Schuhe309d2a2019-11-29 13:25:21 -080010#include "absl/types/span.h"
11#include "aos/events/event_loop.h"
James Kuszmaul38735e82019-12-07 16:42:06 -080012#include "aos/events/logging/logger_generated.h"
Austin Schuhe309d2a2019-11-29 13:25:21 -080013#include "aos/flatbuffer_merge.h"
Austin Schuh288479d2019-12-18 19:47:52 -080014#include "aos/network/team_number.h"
Austin Schuhe309d2a2019-11-29 13:25:21 -080015#include "aos/time/time.h"
16#include "flatbuffers/flatbuffers.h"
17
Austin Schuh15649d62019-12-28 16:36:38 -080018DEFINE_bool(skip_missing_forwarding_entries, false,
19 "If true, drop any forwarding entries with missing data. If "
20 "false, CHECK.");
Austin Schuhe309d2a2019-11-29 13:25:21 -080021
22namespace aos {
23namespace logger {
24
25namespace chrono = std::chrono;
26
Austin Schuhe309d2a2019-11-29 13:25:21 -080027Logger::Logger(DetachedBufferWriter *writer, EventLoop *event_loop,
28 std::chrono::milliseconds polling_period)
29 : event_loop_(event_loop),
30 writer_(writer),
31 timer_handler_(event_loop_->AddTimer([this]() { DoLogData(); })),
32 polling_period_(polling_period) {
33 for (const Channel *channel : *event_loop_->configuration()->channels()) {
34 FetcherStruct fs;
Austin Schuh15649d62019-12-28 16:36:38 -080035 const bool is_readable =
36 configuration::ChannelIsReadableOnNode(channel, event_loop_->node());
37 const bool log_message = configuration::ChannelMessageIsLoggedOnNode(
38 channel, event_loop_->node()) &&
39 is_readable;
40
41 const bool log_delivery_times =
42 (event_loop_->node() == nullptr)
43 ? false
44 : configuration::ConnectionDeliveryTimeIsLoggedOnNode(
45 channel, event_loop_->node(), event_loop_->node());
46
47 if (log_message || log_delivery_times) {
48 fs.fetcher = event_loop->MakeRawFetcher(channel);
49 VLOG(1) << "Logging channel "
50 << configuration::CleanedChannelToString(channel);
51
52 if (log_delivery_times) {
53 if (log_message) {
54 VLOG(1) << " Logging message and delivery times";
55 fs.log_type = LogType::kLogMessageAndDeliveryTime;
56 } else {
57 VLOG(1) << " Logging delivery times only";
58 fs.log_type = LogType::kLogDeliveryTimeOnly;
59 }
60 } else {
61 // We don't have a particularly great use case right now for logging a
62 // forwarded message, but either not logging the delivery times, or
63 // logging them on another node. Fail rather than produce bad results.
64 CHECK(configuration::ChannelIsSendableOnNode(channel,
65 event_loop_->node()))
66 << ": Logger only knows how to log remote messages with "
67 "forwarding timestamps.";
68 VLOG(1) << " Logging message only";
69 fs.log_type = LogType::kLogMessage;
70 }
71 }
72
Austin Schuhe309d2a2019-11-29 13:25:21 -080073 fs.written = false;
74 fetchers_.emplace_back(std::move(fs));
75 }
76
77 // When things start, we want to log the header, then the most recent messages
78 // available on each fetcher to capture the previous state, then start
79 // polling.
80 event_loop_->OnRun([this, polling_period]() {
81 // Grab data from each channel right before we declare the log file started
82 // so we can capture the latest message on each channel. This lets us have
83 // non periodic messages with configuration that now get logged.
84 for (FetcherStruct &f : fetchers_) {
Austin Schuh15649d62019-12-28 16:36:38 -080085 if (f.fetcher.get() != nullptr) {
86 f.written = !f.fetcher->Fetch();
87 }
Austin Schuhe309d2a2019-11-29 13:25:21 -080088 }
89
90 // We need to pick a point in time to declare the log file "started". This
91 // starts here. It needs to be after everything is fetched so that the
92 // fetchers are all pointed at the most recent message before the start
93 // time.
Austin Schuhfa895892020-01-07 20:07:41 -080094 monotonic_start_time_ = event_loop_->monotonic_now();
95 realtime_start_time_ = event_loop_->realtime_now();
96 last_synchronized_time_ = monotonic_start_time_;
Austin Schuhe309d2a2019-11-29 13:25:21 -080097
Austin Schuhfa895892020-01-07 20:07:41 -080098 LOG(INFO) << "Logging node as " << FlatbufferToJson(event_loop_->node());
Austin Schuhe309d2a2019-11-29 13:25:21 -080099
Austin Schuhfa895892020-01-07 20:07:41 -0800100 WriteHeader();
Austin Schuhe309d2a2019-11-29 13:25:21 -0800101
102 timer_handler_->Setup(event_loop_->monotonic_now() + polling_period,
103 polling_period);
104 });
105}
106
Austin Schuhfa895892020-01-07 20:07:41 -0800107void Logger::WriteHeader() {
108 // Now write the header with this timestamp in it.
109 flatbuffers::FlatBufferBuilder fbb;
110 fbb.ForceDefaults(1);
111
112 flatbuffers::Offset<aos::Configuration> configuration_offset =
113 CopyFlatBuffer(event_loop_->configuration(), &fbb);
114
115 flatbuffers::Offset<flatbuffers::String> string_offset =
116 fbb.CreateString(network::GetHostname());
117
118 flatbuffers::Offset<Node> node_offset;
119 if (event_loop_->node() != nullptr) {
120 node_offset = CopyFlatBuffer(event_loop_->node(), &fbb);
121 }
122
123 aos::logger::LogFileHeader::Builder log_file_header_builder(fbb);
124
125 log_file_header_builder.add_name(string_offset);
126
127 // Only add the node if we are running in a multinode configuration.
128 if (event_loop_->node() != nullptr) {
129 log_file_header_builder.add_node(node_offset);
130 }
131
132 log_file_header_builder.add_configuration(configuration_offset);
133 // The worst case theoretical out of order is the polling period times 2.
134 // One message could get logged right after the boundary, but be for right
135 // before the next boundary. And the reverse could happen for another
136 // message. Report back 3x to be extra safe, and because the cost isn't
137 // huge on the read side.
138 log_file_header_builder.add_max_out_of_order_duration(
139 std::chrono::duration_cast<std::chrono::nanoseconds>(3 * polling_period_)
140 .count());
141
142 log_file_header_builder.add_monotonic_start_time(
143 std::chrono::duration_cast<std::chrono::nanoseconds>(
144 monotonic_start_time_.time_since_epoch())
145 .count());
146 log_file_header_builder.add_realtime_start_time(
147 std::chrono::duration_cast<std::chrono::nanoseconds>(
148 realtime_start_time_.time_since_epoch())
149 .count());
150
151 fbb.FinishSizePrefixed(log_file_header_builder.Finish());
152 writer_->QueueSizedFlatbuffer(&fbb);
153}
154
155void Logger::Rotate(DetachedBufferWriter *writer) {
156 // Force data up until now to be written.
157 DoLogData();
158
159 // Swap the writer out, and re-write the header.
160 writer_ = writer;
161 WriteHeader();
162}
163
Austin Schuhe309d2a2019-11-29 13:25:21 -0800164void Logger::DoLogData() {
165 // We want to guarentee that messages aren't out of order by more than
166 // max_out_of_order_duration. To do this, we need sync points. Every write
167 // cycle should be a sync point.
Austin Schuhfa895892020-01-07 20:07:41 -0800168 const monotonic_clock::time_point monotonic_now =
169 event_loop_->monotonic_now();
Austin Schuhe309d2a2019-11-29 13:25:21 -0800170
171 do {
172 // Move the sync point up by at most polling_period. This forces one sync
173 // per iteration, even if it is small.
174 last_synchronized_time_ =
175 std::min(last_synchronized_time_ + polling_period_, monotonic_now);
176 size_t channel_index = 0;
177 // Write each channel to disk, one at a time.
178 for (FetcherStruct &f : fetchers_) {
Austin Schuh15649d62019-12-28 16:36:38 -0800179 // Skip any channels which we aren't supposed to log.
180 if (f.fetcher.get() != nullptr) {
181 while (true) {
182 if (f.written) {
183 if (!f.fetcher->FetchNext()) {
184 VLOG(2) << "No new data on "
185 << configuration::CleanedChannelToString(
186 f.fetcher->channel());
187 break;
188 } else {
189 f.written = false;
190 }
Austin Schuhe309d2a2019-11-29 13:25:21 -0800191 }
Austin Schuhe309d2a2019-11-29 13:25:21 -0800192
Austin Schuh15649d62019-12-28 16:36:38 -0800193 CHECK(!f.written);
194
195 // TODO(james): Write tests to exercise this logic.
196 if (f.fetcher->context().monotonic_event_time <
197 last_synchronized_time_) {
198 // Write!
199 flatbuffers::FlatBufferBuilder fbb(f.fetcher->context().size +
200 max_header_size_);
201 fbb.ForceDefaults(1);
202
203 fbb.FinishSizePrefixed(PackMessage(&fbb, f.fetcher->context(),
204 channel_index, f.log_type));
205
206 VLOG(2) << "Writing data for channel "
207 << configuration::CleanedChannelToString(
208 f.fetcher->channel());
209
210 max_header_size_ = std::max(
211 max_header_size_, fbb.GetSize() - f.fetcher->context().size);
212 writer_->QueueSizedFlatbuffer(&fbb);
213
214 f.written = true;
Austin Schuhe309d2a2019-11-29 13:25:21 -0800215 } else {
Austin Schuh15649d62019-12-28 16:36:38 -0800216 break;
Austin Schuhe309d2a2019-11-29 13:25:21 -0800217 }
218 }
Austin Schuhe309d2a2019-11-29 13:25:21 -0800219 }
220
221 ++channel_index;
222 }
223
224 CHECK_EQ(channel_index, fetchers_.size());
225
226 // If we missed cycles, we could be pretty far behind. Spin until we are
227 // caught up.
228 } while (last_synchronized_time_ + polling_period_ < monotonic_now);
229
230 writer_->Flush();
231}
232
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800233LogReader::LogReader(std::string_view filename,
234 const Configuration *replay_configuration)
Austin Schuhfa895892020-01-07 20:07:41 -0800235 : LogReader(std::vector<std::string>{std::string(filename)},
236 replay_configuration) {}
237
238LogReader::LogReader(const std::vector<std::string> &filenames,
239 const Configuration *replay_configuration)
240 : sorted_message_reader_(filenames),
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800241 replay_configuration_(replay_configuration) {
242 channels_.resize(logged_configuration()->channels()->size());
Austin Schuh6331ef92020-01-07 18:28:09 -0800243 MakeRemappedConfig();
Austin Schuhe309d2a2019-11-29 13:25:21 -0800244}
245
Austin Schuhfa895892020-01-07 20:07:41 -0800246LogReader::~LogReader() { Deregister(); }
Austin Schuhe309d2a2019-11-29 13:25:21 -0800247
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800248const Configuration *LogReader::logged_configuration() const {
Austin Schuh05b70472020-01-01 17:11:17 -0800249 return sorted_message_reader_.configuration();
Austin Schuhe309d2a2019-11-29 13:25:21 -0800250}
251
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800252const Configuration *LogReader::configuration() const {
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800253 return remapped_configuration_;
254}
255
256const Node *LogReader::node() const {
257 // Because the Node pointer will only be valid if it actually points to memory
258 // owned by remapped_configuration_, we need to wait for the
259 // remapped_configuration_ to be populated before accessing it.
260 CHECK(remapped_configuration_ != nullptr)
261 << ": Need to call Register before the node() pointer will be valid.";
262 if (sorted_message_reader_.node() == nullptr) {
263 return nullptr;
264 }
265 return configuration::GetNode(
266 configuration(), sorted_message_reader_.node()->name()->string_view());
267}
Austin Schuh15649d62019-12-28 16:36:38 -0800268
Austin Schuhe309d2a2019-11-29 13:25:21 -0800269monotonic_clock::time_point LogReader::monotonic_start_time() {
Austin Schuh05b70472020-01-01 17:11:17 -0800270 return sorted_message_reader_.monotonic_start_time();
Austin Schuhe309d2a2019-11-29 13:25:21 -0800271}
272
273realtime_clock::time_point LogReader::realtime_start_time() {
Austin Schuh05b70472020-01-01 17:11:17 -0800274 return sorted_message_reader_.realtime_start_time();
Austin Schuhe309d2a2019-11-29 13:25:21 -0800275}
276
James Kuszmaul84ff3e52020-01-03 19:48:53 -0800277void LogReader::Register() {
278 event_loop_factory_unique_ptr_ =
Austin Schuhac0771c2020-01-07 18:36:30 -0800279 std::make_unique<SimulatedEventLoopFactory>(configuration());
James Kuszmaul84ff3e52020-01-03 19:48:53 -0800280 Register(event_loop_factory_unique_ptr_.get());
281}
282
Austin Schuh92547522019-12-28 14:33:43 -0800283void LogReader::Register(SimulatedEventLoopFactory *event_loop_factory) {
Austin Schuh92547522019-12-28 14:33:43 -0800284 event_loop_factory_ = event_loop_factory;
Austin Schuhac0771c2020-01-07 18:36:30 -0800285 node_event_loop_factory_ =
286 event_loop_factory_->GetNodeEventLoopFactory(node());
287 event_loop_unique_ptr_ =
288 event_loop_factory->MakeEventLoop("log_reader", node());
Austin Schuh92547522019-12-28 14:33:43 -0800289 // We don't run timing reports when trying to print out logged data, because
290 // otherwise we would end up printing out the timing reports themselves...
291 // This is only really relevant when we are replaying into a simulation.
292 event_loop_unique_ptr_->SkipTimingReport();
293
294 Register(event_loop_unique_ptr_.get());
295 event_loop_factory_->RunFor(monotonic_start_time() -
Austin Schuha5e14192020-01-06 18:02:41 -0800296 event_loop_->monotonic_now());
Austin Schuh92547522019-12-28 14:33:43 -0800297}
298
Austin Schuhe309d2a2019-11-29 13:25:21 -0800299void LogReader::Register(EventLoop *event_loop) {
300 event_loop_ = event_loop;
301
Austin Schuh39788ff2019-12-01 18:22:57 -0800302 // Otherwise we replay the timing report and try to resend it...
303 event_loop_->SkipTimingReport();
304
Austin Schuhe309d2a2019-11-29 13:25:21 -0800305 for (size_t i = 0; i < channels_.size(); ++i) {
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800306 const Channel *const original_channel =
307 logged_configuration()->channels()->Get(i);
Austin Schuhe309d2a2019-11-29 13:25:21 -0800308
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800309 std::string_view channel_name = original_channel->name()->string_view();
310 std::string_view channel_type = original_channel->type()->string_view();
311 // If the channel is remapped, find the correct channel name to use.
312 if (remapped_channels_.count(i) > 0) {
313 VLOG(2) << "Got remapped channel on "
314 << configuration::CleanedChannelToString(original_channel);
315 channel_name = remapped_channels_[i];
316 }
Austin Schuh6331ef92020-01-07 18:28:09 -0800317
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800318 VLOG(1) << "Going to remap channel " << channel_name << " " << channel_type;
Austin Schuh6331ef92020-01-07 18:28:09 -0800319 const Channel *channel = configuration::GetChannel(
320 event_loop_->configuration(), channel_name, channel_type,
321 event_loop_->name(), event_loop_->node());
322
323 CHECK(channel != nullptr)
324 << ": Unable to send {\"name\": \"" << channel_name
325 << "\", \"type\": \"" << channel_type
326 << "\"} because it is not in the provided configuration.";
327
328 channels_[i] = event_loop_->MakeRawSender(channel);
Austin Schuhe309d2a2019-11-29 13:25:21 -0800329 }
330
331 timer_handler_ = event_loop_->AddTimer([this]() {
James Kuszmaul314f1672020-01-03 20:02:08 -0800332 if (sorted_message_reader_.active_channel_count() == 0u) {
333 event_loop_factory_->Exit();
334 return;
335 }
Austin Schuh05b70472020-01-01 17:11:17 -0800336 monotonic_clock::time_point channel_timestamp;
337 int channel_index;
338 FlatbufferVector<MessageHeader> channel_data =
339 FlatbufferVector<MessageHeader>::Empty();
340
341 std::tie(channel_timestamp, channel_index, channel_data) =
342 sorted_message_reader_.PopOldestChannel();
343
Austin Schuhe309d2a2019-11-29 13:25:21 -0800344 const monotonic_clock::time_point monotonic_now =
Austin Schuhad154822019-12-27 15:45:13 -0800345 event_loop_->context().monotonic_event_time;
Austin Schuh05b70472020-01-01 17:11:17 -0800346 CHECK(monotonic_now == channel_timestamp)
Austin Schuhe309d2a2019-11-29 13:25:21 -0800347 << ": Now " << monotonic_now.time_since_epoch().count()
Austin Schuh05b70472020-01-01 17:11:17 -0800348 << " trying to send " << channel_timestamp.time_since_epoch().count();
Austin Schuhe309d2a2019-11-29 13:25:21 -0800349
Austin Schuh05b70472020-01-01 17:11:17 -0800350 if (channel_timestamp > monotonic_start_time() ||
Austin Schuh15649d62019-12-28 16:36:38 -0800351 event_loop_factory_ != nullptr) {
352 if (!FLAGS_skip_missing_forwarding_entries ||
Austin Schuh05b70472020-01-01 17:11:17 -0800353 channel_data.message().data() != nullptr) {
354 CHECK(channel_data.message().data() != nullptr)
355 << ": Got a message without data. Forwarding entry which was "
356 "not "
Austin Schuh15649d62019-12-28 16:36:38 -0800357 "matched? Use --skip_missing_forwarding_entries to ignore "
358 "this.";
Austin Schuh92547522019-12-28 14:33:43 -0800359
Austin Schuh15649d62019-12-28 16:36:38 -0800360 // If we have access to the factory, use it to fix the realtime time.
Austin Schuhac0771c2020-01-07 18:36:30 -0800361 if (node_event_loop_factory_ != nullptr) {
362 node_event_loop_factory_->SetRealtimeOffset(
Austin Schuh05b70472020-01-01 17:11:17 -0800363 monotonic_clock::time_point(chrono::nanoseconds(
364 channel_data.message().monotonic_sent_time())),
365 realtime_clock::time_point(chrono::nanoseconds(
366 channel_data.message().realtime_sent_time())));
Austin Schuh15649d62019-12-28 16:36:38 -0800367 }
368
Austin Schuh05b70472020-01-01 17:11:17 -0800369 channels_[channel_index]->Send(
370 channel_data.message().data()->Data(),
371 channel_data.message().data()->size(),
372 monotonic_clock::time_point(chrono::nanoseconds(
373 channel_data.message().monotonic_remote_time())),
374 realtime_clock::time_point(chrono::nanoseconds(
375 channel_data.message().realtime_remote_time())),
376 channel_data.message().remote_queue_index());
Austin Schuh92547522019-12-28 14:33:43 -0800377 }
Austin Schuhe309d2a2019-11-29 13:25:21 -0800378 } else {
379 LOG(WARNING) << "Not sending data from before the start of the log file. "
Austin Schuh05b70472020-01-01 17:11:17 -0800380 << channel_timestamp.time_since_epoch().count() << " start "
Austin Schuhe309d2a2019-11-29 13:25:21 -0800381 << monotonic_start_time().time_since_epoch().count() << " "
Austin Schuh05b70472020-01-01 17:11:17 -0800382 << FlatbufferToJson(channel_data);
Austin Schuhe309d2a2019-11-29 13:25:21 -0800383 }
384
Austin Schuh05b70472020-01-01 17:11:17 -0800385 if (sorted_message_reader_.active_channel_count() > 0u) {
386 timer_handler_->Setup(sorted_message_reader_.oldest_message().first);
James Kuszmaul314f1672020-01-03 20:02:08 -0800387 } else {
388 // Set a timer up immediately after now to die. If we don't do this, then
389 // the senders waiting on the message we just read will never get called.
Austin Schuheecb9282020-01-08 17:43:30 -0800390 if (event_loop_factory_ != nullptr) {
391 timer_handler_->Setup(monotonic_now +
392 event_loop_factory_->send_delay() +
393 std::chrono::nanoseconds(1));
394 }
Austin Schuhe309d2a2019-11-29 13:25:21 -0800395 }
396 });
397
Austin Schuh05b70472020-01-01 17:11:17 -0800398 if (sorted_message_reader_.active_channel_count() > 0u) {
399 event_loop_->OnRun([this]() {
400 timer_handler_->Setup(sorted_message_reader_.oldest_message().first);
401 });
Austin Schuhe309d2a2019-11-29 13:25:21 -0800402 }
403}
404
405void LogReader::Deregister() {
James Kuszmaul84ff3e52020-01-03 19:48:53 -0800406 // Make sure that things get destroyed in the correct order, rather than
407 // relying on getting the order correct in the class definition.
Austin Schuhe309d2a2019-11-29 13:25:21 -0800408 for (size_t i = 0; i < channels_.size(); ++i) {
Austin Schuh05b70472020-01-01 17:11:17 -0800409 channels_[i].reset();
Austin Schuhe309d2a2019-11-29 13:25:21 -0800410 }
Austin Schuh92547522019-12-28 14:33:43 -0800411
Austin Schuh92547522019-12-28 14:33:43 -0800412 event_loop_unique_ptr_.reset();
413 event_loop_ = nullptr;
James Kuszmaul84ff3e52020-01-03 19:48:53 -0800414 event_loop_factory_unique_ptr_.reset();
415 event_loop_factory_ = nullptr;
Austin Schuhac0771c2020-01-07 18:36:30 -0800416 node_event_loop_factory_ = nullptr;
Austin Schuhe309d2a2019-11-29 13:25:21 -0800417}
418
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800419void LogReader::RemapLoggedChannel(std::string_view name, std::string_view type,
420 std::string_view add_prefix) {
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800421 for (size_t ii = 0; ii < logged_configuration()->channels()->size(); ++ii) {
422 const Channel *const channel = logged_configuration()->channels()->Get(ii);
423 if (channel->name()->str() == name &&
424 channel->type()->string_view() == type) {
425 CHECK_EQ(0u, remapped_channels_.count(ii))
426 << "Already remapped channel "
427 << configuration::CleanedChannelToString(channel);
428 remapped_channels_[ii] = std::string(add_prefix) + std::string(name);
429 VLOG(1) << "Remapping channel "
430 << configuration::CleanedChannelToString(channel)
431 << " to have name " << remapped_channels_[ii];
Austin Schuh6331ef92020-01-07 18:28:09 -0800432 MakeRemappedConfig();
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800433 return;
434 }
435 }
436 LOG(FATAL) << "Unabled to locate channel with name " << name << " and type "
437 << type;
438}
439
440void LogReader::MakeRemappedConfig() {
Austin Schuhac0771c2020-01-07 18:36:30 -0800441 CHECK(!event_loop_)
442 << ": Can't change the mapping after the events are scheduled.";
443
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800444 // If no remapping occurred and we are using the original config, then there
445 // is nothing interesting to do here.
446 if (remapped_channels_.empty() && replay_configuration_ == nullptr) {
447 remapped_configuration_ = sorted_message_reader_.configuration();
448 return;
449 }
450 // Config to copy Channel definitions from. Use the specified
451 // replay_configuration_ if it has been provided.
452 const Configuration *const base_config = replay_configuration_ == nullptr
453 ? logged_configuration()
454 : replay_configuration_;
455 // The remapped config will be identical to the base_config, except that it
456 // will have a bunch of extra channels in the channel list, which are exact
457 // copies of the remapped channels, but with different names.
458 // Because the flatbuffers API is a pain to work with, this requires a bit of
459 // a song-and-dance to get copied over.
460 // The order of operations is to:
461 // 1) Make a flatbuffer builder for a config that will just contain a list of
462 // the new channels that we want to add.
463 // 2) For each channel that we are remapping:
464 // a) Make a buffer/builder and construct into it a Channel table that only
465 // contains the new name for the channel.
466 // b) Merge the new channel with just the name into the channel that we are
467 // trying to copy, built in the flatbuffer builder made in 1. This gives
468 // us the new channel definition that we need.
469 // 3) Using this list of offsets, build the Configuration of just new
470 // Channels.
471 // 4) Merge the Configuration with the new Channels into the base_config.
472 // 5) Call MergeConfiguration() on that result to give MergeConfiguration a
473 // chance to sanitize the config.
474
475 // This is the builder that we use for the config containing all the new
476 // channels.
477 flatbuffers::FlatBufferBuilder new_config_fbb;
478 new_config_fbb.ForceDefaults(1);
479 std::vector<flatbuffers::Offset<Channel>> channel_offsets;
480 for (auto &pair : remapped_channels_) {
481 // This is the builder that we use for creating the Channel with just the
482 // new name.
483 flatbuffers::FlatBufferBuilder new_name_fbb;
484 new_name_fbb.ForceDefaults(1);
485 const flatbuffers::Offset<flatbuffers::String> name_offset =
486 new_name_fbb.CreateString(pair.second);
487 ChannelBuilder new_name_builder(new_name_fbb);
488 new_name_builder.add_name(name_offset);
489 new_name_fbb.Finish(new_name_builder.Finish());
490 const FlatbufferDetachedBuffer<Channel> new_name = new_name_fbb.Release();
491 // Retrieve the channel that we want to copy, confirming that it is actually
492 // present in base_config.
493 const Channel *const base_channel = CHECK_NOTNULL(configuration::GetChannel(
494 base_config, logged_configuration()->channels()->Get(pair.first), "",
495 nullptr));
496 // Actually create the new channel and put it into the vector of Offsets
497 // that we will use to create the new Configuration.
498 channel_offsets.emplace_back(MergeFlatBuffers<Channel>(
499 reinterpret_cast<const flatbuffers::Table *>(base_channel),
500 reinterpret_cast<const flatbuffers::Table *>(&new_name.message()),
501 &new_config_fbb));
502 }
503 // Create the Configuration containing the new channels that we want to add.
Austin Schuhfa895892020-01-07 20:07:41 -0800504 const auto new_name_vector_offsets =
505 new_config_fbb.CreateVector(channel_offsets);
James Kuszmaulc7bbb3e2020-01-03 20:01:00 -0800506 ConfigurationBuilder new_config_builder(new_config_fbb);
507 new_config_builder.add_channels(new_name_vector_offsets);
508 new_config_fbb.Finish(new_config_builder.Finish());
509 const FlatbufferDetachedBuffer<Configuration> new_name_config =
510 new_config_fbb.Release();
511 // Merge the new channels configuration into the base_config, giving us the
512 // remapped configuration.
513 remapped_configuration_buffer_ =
514 std::make_unique<FlatbufferDetachedBuffer<Configuration>>(
515 MergeFlatBuffers<Configuration>(base_config,
516 &new_name_config.message()));
517 // Call MergeConfiguration to deal with sanitizing the config.
518 remapped_configuration_buffer_ =
519 std::make_unique<FlatbufferDetachedBuffer<Configuration>>(
520 configuration::MergeConfiguration(*remapped_configuration_buffer_));
521
522 remapped_configuration_ = &remapped_configuration_buffer_->message();
523}
524
Austin Schuhe309d2a2019-11-29 13:25:21 -0800525} // namespace logger
526} // namespace aos