blob: 22d8b396ed2b97c544bce5d9e033e86bdf263276 [file] [log] [blame]
Naman Guptae7fe3552022-11-23 13:52:03 -08001#include <stdlib.h>
2
3#include <iostream>
4#include <optional>
5#include <ostream>
6#include <sstream>
7#include <string_view>
8#include <vector>
9
Philipp Schrader790cb542023-07-05 21:06:52 -070010#include "flatbuffers/flatbuffers.h"
11#include "gflags/gflags.h"
12#include "glog/logging.h"
13
Naman Guptae7fe3552022-11-23 13:52:03 -080014#include "aos/configuration_generated.h"
15#include "aos/events/event_loop.h"
16#include "aos/events/logging/log_reader.h"
17#include "aos/events/logging/log_reader_utils.h"
18#include "aos/events/logging/log_replayer_config_generated.h"
Eric Schmiedebergbd1c9da2023-07-14 14:46:59 -040019#include "aos/events/logging/log_replayer_stats_generated.h"
20#include "aos/events/logging/log_replayer_stats_schema.h"
Naman Guptae7fe3552022-11-23 13:52:03 -080021#include "aos/events/logging/logfile_sorting.h"
22#include "aos/events/logging/logfile_utils.h"
23#include "aos/events/logging/replay_timing_generated.h"
24#include "aos/events/logging/replay_timing_schema.h"
25#include "aos/events/shm_event_loop.h"
26#include "aos/flatbuffer_merge.h"
27#include "aos/init.h"
28#include "aos/json_to_flatbuffer.h"
29#include "aos/util/file.h"
Naman Guptae7fe3552022-11-23 13:52:03 -080030
31DEFINE_string(config, "", "If specified, overrides logged configuration.");
32DEFINE_bool(
33 plot_timing, true,
34 "If set, generates a plot of the replay timing--namely, the errors between "
35 "when we "
36 "should've sent messages and when we actually sent replayed messages.");
37DEFINE_bool(skip_sender_channels, true,
38 "If set, skips replay of the channels applications replay on");
39DEFINE_bool(skip_replay, false,
40 "If set, skips actually running the replay. Useful for writing a "
41 "config without running replay");
42DEFINE_bool(
43 print_config, false,
44 "If set, prints the config that will be used for replay to stdout as json");
45DEFINE_string(
46 replay_config, "",
47 "Path to the configuration used for log replay which includes items such "
48 "as channels to remap, and applications to target for replay. If not set, "
49 "log_reader will run on shm event loop. ");
50DEFINE_string(merge_with_config, "",
51 "A valid json string to be merged with config. This is used to "
52 "add extra applications needed to run only for log_replayer");
Eric Schmiedebergbd1c9da2023-07-14 14:46:59 -040053DEFINE_bool(print_stats, true,
54 "if set, prints the LogReplayerStats message as JSON to stdout");
Naman Guptae7fe3552022-11-23 13:52:03 -080055
56namespace aos::logger {
57
58int Main(int argc, char *argv[]) {
Naman Guptae7fe3552022-11-23 13:52:03 -080059 const std::vector<aos::logger::LogFile> logfiles =
Austin Schuhc1609732023-06-26 11:51:28 -070060 aos::logger::SortParts(aos::logger::FindLogs(argc, argv));
Naman Guptae7fe3552022-11-23 13:52:03 -080061
Naman Gupta7eabb3a2023-03-01 11:49:44 -080062 aos::logger::LogReader config_reader(logfiles);
Naman Guptae7fe3552022-11-23 13:52:03 -080063 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
64 FLAGS_config.empty()
Naman Gupta7eabb3a2023-03-01 11:49:44 -080065 ? CopyFlatBuffer<aos::Configuration>(config_reader.configuration())
Naman Guptae7fe3552022-11-23 13:52:03 -080066 : aos::configuration::ReadConfig(FLAGS_config);
67
68 if (FLAGS_plot_timing) {
Naman Guptae7fe3552022-11-23 13:52:03 -080069 // Go through the effort to add a ReplayTiming channel to ensure that we
70 // can capture timing information from the replay.
Naman Gupta7eabb3a2023-03-01 11:49:44 -080071 const aos::Configuration *raw_config = &config.message();
James Kuszmaul741a4d02023-01-05 14:59:21 -080072 aos::ChannelT channel_overrides;
73 channel_overrides.max_size = 10000;
74 channel_overrides.frequency = 10000;
75 config = aos::configuration::AddChannelToConfiguration(
76 raw_config, "/timing",
77 aos::FlatbufferSpan<reflection::Schema>(
78 aos::timing::ReplayTimingSchema()),
79 aos::configuration::GetMyNode(raw_config), channel_overrides);
Naman Guptae7fe3552022-11-23 13:52:03 -080080 }
81
Eric Schmiedebergbd1c9da2023-07-14 14:46:59 -040082 // Add the LogReplayerStats channel
83 const aos::Configuration *raw_config = &config.message();
84 aos::ChannelT channel_overrides;
85 channel_overrides.max_size = 10000;
86 channel_overrides.frequency = 1;
87 config = aos::configuration::AddChannelToConfiguration(
88 raw_config, "/replay",
89 aos::FlatbufferSpan<reflection::Schema>(aos::LogReplayerStatsSchema()),
90 aos::configuration::GetMyNode(raw_config), channel_overrides);
91
Naman Guptae7fe3552022-11-23 13:52:03 -080092 if (!FLAGS_merge_with_config.empty()) {
93 config = aos::configuration::MergeWithConfig(&config.message(),
94 FLAGS_merge_with_config);
95 }
96
97 std::optional<aos::FlatbufferDetachedBuffer<ReplayConfig>> replay_config =
98 FLAGS_replay_config.empty()
99 ? std::nullopt
100 : std::make_optional(aos::JsonToFlatbuffer<ReplayConfig>(
101 aos::util::ReadFileToStringOrDie(FLAGS_replay_config.data())));
Naman Gupta3dcab962023-03-01 15:09:53 -0800102 std::vector<std::pair<std::string, std::string>> message_filter;
Naman Guptae7fe3552022-11-23 13:52:03 -0800103 if (FLAGS_skip_sender_channels && replay_config.has_value()) {
104 CHECK(replay_config.value().message().has_active_nodes());
105 std::vector<const Node *> active_nodes;
106 for (const auto &node : *replay_config.value().message().active_nodes()) {
107 active_nodes.emplace_back(configuration::GetNode(
108 &config.message(), node->name()->string_view()));
109 }
110
111 std::vector<std::string> applications;
112 for (const auto &application :
113 *replay_config.value().message().applications()) {
114 if (application->name()->string_view() != "camera_message_interceptor") {
115 applications.emplace_back(application->name()->string_view());
116 }
117 }
118
119 aos::logger::ChannelsInLogResult channels =
120 ChannelsInLog(logfiles, active_nodes, applications);
121 for (auto const &channel :
122 channels.watchers_and_fetchers_without_senders.value()) {
Naman Gupta7eabb3a2023-03-01 11:49:44 -0800123 message_filter.emplace_back(channel.name, channel.type);
Naman Guptae7fe3552022-11-23 13:52:03 -0800124 }
125 }
126
127 aos::logger::LogReader reader(
128 logfiles, &config.message(),
129 message_filter.empty() ? nullptr : &message_filter);
130
Naman Gupta7eabb3a2023-03-01 11:49:44 -0800131 if (replay_config.has_value() &&
132 replay_config.value().message().has_remap_channels()) {
Naman Guptae7fe3552022-11-23 13:52:03 -0800133 for (auto const &remap_channel :
134 *replay_config.value().message().remap_channels()) {
135 auto const &channel = remap_channel->channel();
136 std::string_view new_type = remap_channel->has_new_type()
137 ? remap_channel->new_type()->string_view()
138 : channel->type()->string_view();
139 reader.RemapLoggedChannel(
140 channel->name()->string_view(), channel->type()->string_view(),
141 remap_channel->prefix()->string_view(), new_type);
142 }
143 }
144
145 if (FLAGS_print_config) {
146 // TODO(Naman): Replace with config writer if it will be cleaner
147 std::cout << FlatbufferToJson(reader.configuration()) << std::endl;
148 }
149
150 if (!FLAGS_skip_replay) {
151 aos::ShmEventLoop event_loop(reader.configuration());
152
153 event_loop.SkipAosLog();
154 event_loop.SkipTimingReport();
155
Eric Schmiedebergbd1c9da2023-07-14 14:46:59 -0400156 aos::Sender<aos::LogReplayerStats> stats_sender =
157 event_loop.MakeSender<aos::LogReplayerStats>("/replay");
158 auto builder = stats_sender.MakeBuilder();
159 auto node_name = builder.fbb()->CreateString(event_loop.node()->name());
160 flatbuffers::Offset<aos::ReplayConfig> replay_config_offset;
161 if (replay_config.has_value()) {
162 replay_config_offset =
163 aos::CopyFlatBuffer(&replay_config.value().message(), builder.fbb());
164 }
165
166 auto stats_builder = builder.MakeBuilder<aos::LogReplayerStats>();
167 if (replay_config.has_value()) {
168 stats_builder.add_replay_config(replay_config_offset);
169 }
170
Naman Guptae7fe3552022-11-23 13:52:03 -0800171 reader.Register(&event_loop);
Eric Schmiedebergbd1c9da2023-07-14 14:46:59 -0400172
173 // Save off the start and end times of replay.
174 reader.OnStart(event_loop.node(), [&event_loop, &stats_builder,
175 &node_name]() {
176 stats_builder.add_node(node_name);
177 stats_builder.add_realtime_start_time(
178 std::chrono::nanoseconds(event_loop.realtime_now().time_since_epoch())
179 .count());
180
181 stats_builder.add_monotonic_start_time(
182 std::chrono::nanoseconds(
183 event_loop.monotonic_now().time_since_epoch())
184 .count());
185 });
186
187 reader.OnEnd(event_loop.node(), [&event_loop, &stats_builder, &builder]() {
188 stats_builder.add_realtime_end_time(
189 std::chrono::nanoseconds(event_loop.realtime_now().time_since_epoch())
190 .count());
191
192 stats_builder.add_monotonic_end_time(
193 std::chrono::nanoseconds(
194 event_loop.monotonic_now().time_since_epoch())
195 .count());
196 builder.CheckOk(builder.Send(stats_builder.Finish()));
197 });
198
Naman Guptae7fe3552022-11-23 13:52:03 -0800199 reader.OnEnd(event_loop.node(), [&event_loop]() { event_loop.Exit(); });
200
201 if (FLAGS_plot_timing) {
202 aos::Sender<aos::timing::ReplayTiming> replay_timing_sender =
203 event_loop.MakeSender<aos::timing::ReplayTiming>("/timing");
204 reader.set_timing_accuracy_sender(event_loop.node(),
205 std::move(replay_timing_sender));
206 }
207
208 event_loop.Run();
209
210 reader.Deregister();
Eric Schmiedebergbd1c9da2023-07-14 14:46:59 -0400211
212 if (FLAGS_print_stats) {
213 aos::Fetcher<aos::LogReplayerStats> stats_fetcher =
214 event_loop.MakeFetcher<aos::LogReplayerStats>("/replay");
215 CHECK(stats_fetcher.Fetch()) << "Failed to fetch LogReplayerStats!";
216 std::cout << aos::FlatbufferToJson(stats_fetcher.get());
217 }
Naman Guptae7fe3552022-11-23 13:52:03 -0800218 }
219
220 return EXIT_SUCCESS;
221}
222
223} // namespace aos::logger
224
225int main(int argc, char *argv[]) {
226 gflags::SetUsageMessage(
227 R"message(Binary to replay the full contents of a logfile into shared memory.
228 #replay_config should be set in order to replay a set of nodes, applications and channels
229 #print config and skip replay, if you only want to print the config and not do log replay
230 Use case #1: log_replayer <log_dir> --print_config --replay_config=<path_to_config> --skip_replay
231 Use case #2: log_replayer <log_dir> --nofatal_sent_too_fast --replay_config=<path_to_config>
232 )message");
233
234 aos::InitGoogle(&argc, &argv);
235 return aos::logger::Main(argc, argv);
236}