Naman Gupta | e7fe355 | 2022-11-23 13:52:03 -0800 | [diff] [blame] | 1 | #include <stdlib.h> |
| 2 | |
| 3 | #include <iostream> |
| 4 | #include <optional> |
| 5 | #include <ostream> |
| 6 | #include <sstream> |
| 7 | #include <string_view> |
| 8 | #include <vector> |
| 9 | |
| 10 | #include "aos/configuration_generated.h" |
| 11 | #include "aos/events/event_loop.h" |
| 12 | #include "aos/events/logging/log_reader.h" |
| 13 | #include "aos/events/logging/log_reader_utils.h" |
| 14 | #include "aos/events/logging/log_replayer_config_generated.h" |
| 15 | #include "aos/events/logging/logfile_sorting.h" |
| 16 | #include "aos/events/logging/logfile_utils.h" |
| 17 | #include "aos/events/logging/replay_timing_generated.h" |
| 18 | #include "aos/events/logging/replay_timing_schema.h" |
| 19 | #include "aos/events/shm_event_loop.h" |
| 20 | #include "aos/flatbuffer_merge.h" |
| 21 | #include "aos/init.h" |
| 22 | #include "aos/json_to_flatbuffer.h" |
| 23 | #include "aos/util/file.h" |
| 24 | #include "flatbuffers/flatbuffers.h" |
| 25 | #include "gflags/gflags.h" |
| 26 | #include "glog/logging.h" |
| 27 | |
| 28 | DEFINE_string(config, "", "If specified, overrides logged configuration."); |
| 29 | DEFINE_bool( |
| 30 | plot_timing, true, |
| 31 | "If set, generates a plot of the replay timing--namely, the errors between " |
| 32 | "when we " |
| 33 | "should've sent messages and when we actually sent replayed messages."); |
| 34 | DEFINE_bool(skip_sender_channels, true, |
| 35 | "If set, skips replay of the channels applications replay on"); |
| 36 | DEFINE_bool(skip_replay, false, |
| 37 | "If set, skips actually running the replay. Useful for writing a " |
| 38 | "config without running replay"); |
| 39 | DEFINE_bool( |
| 40 | print_config, false, |
| 41 | "If set, prints the config that will be used for replay to stdout as json"); |
| 42 | DEFINE_string( |
| 43 | replay_config, "", |
| 44 | "Path to the configuration used for log replay which includes items such " |
| 45 | "as channels to remap, and applications to target for replay. If not set, " |
| 46 | "log_reader will run on shm event loop. "); |
| 47 | DEFINE_string(merge_with_config, "", |
| 48 | "A valid json string to be merged with config. This is used to " |
| 49 | "add extra applications needed to run only for log_replayer"); |
| 50 | |
| 51 | namespace aos::logger { |
| 52 | |
| 53 | int Main(int argc, char *argv[]) { |
| 54 | const std::vector<std::string> unsorted_logfiles = |
| 55 | aos::logger::FindLogs(argc, argv); |
| 56 | |
| 57 | const std::vector<aos::logger::LogFile> logfiles = |
| 58 | aos::logger::SortParts(unsorted_logfiles); |
| 59 | |
Naman Gupta | 7eabb3a | 2023-03-01 11:49:44 -0800 | [diff] [blame^] | 60 | aos::logger::LogReader config_reader(logfiles); |
Naman Gupta | e7fe355 | 2022-11-23 13:52:03 -0800 | [diff] [blame] | 61 | aos::FlatbufferDetachedBuffer<aos::Configuration> config = |
| 62 | FLAGS_config.empty() |
Naman Gupta | 7eabb3a | 2023-03-01 11:49:44 -0800 | [diff] [blame^] | 63 | ? CopyFlatBuffer<aos::Configuration>(config_reader.configuration()) |
Naman Gupta | e7fe355 | 2022-11-23 13:52:03 -0800 | [diff] [blame] | 64 | : aos::configuration::ReadConfig(FLAGS_config); |
| 65 | |
| 66 | if (FLAGS_plot_timing) { |
Naman Gupta | e7fe355 | 2022-11-23 13:52:03 -0800 | [diff] [blame] | 67 | // Go through the effort to add a ReplayTiming channel to ensure that we |
| 68 | // can capture timing information from the replay. |
Naman Gupta | 7eabb3a | 2023-03-01 11:49:44 -0800 | [diff] [blame^] | 69 | const aos::Configuration *raw_config = &config.message(); |
James Kuszmaul | 741a4d0 | 2023-01-05 14:59:21 -0800 | [diff] [blame] | 70 | aos::ChannelT channel_overrides; |
| 71 | channel_overrides.max_size = 10000; |
| 72 | channel_overrides.frequency = 10000; |
| 73 | config = aos::configuration::AddChannelToConfiguration( |
| 74 | raw_config, "/timing", |
| 75 | aos::FlatbufferSpan<reflection::Schema>( |
| 76 | aos::timing::ReplayTimingSchema()), |
| 77 | aos::configuration::GetMyNode(raw_config), channel_overrides); |
Naman Gupta | e7fe355 | 2022-11-23 13:52:03 -0800 | [diff] [blame] | 78 | } |
| 79 | |
| 80 | if (!FLAGS_merge_with_config.empty()) { |
| 81 | config = aos::configuration::MergeWithConfig(&config.message(), |
| 82 | FLAGS_merge_with_config); |
| 83 | } |
| 84 | |
| 85 | std::optional<aos::FlatbufferDetachedBuffer<ReplayConfig>> replay_config = |
| 86 | FLAGS_replay_config.empty() |
| 87 | ? std::nullopt |
| 88 | : std::make_optional(aos::JsonToFlatbuffer<ReplayConfig>( |
| 89 | aos::util::ReadFileToStringOrDie(FLAGS_replay_config.data()))); |
Naman Gupta | e7fe355 | 2022-11-23 13:52:03 -0800 | [diff] [blame] | 90 | std::vector<std::pair<std::string_view, std::string_view>> message_filter; |
| 91 | if (FLAGS_skip_sender_channels && replay_config.has_value()) { |
| 92 | CHECK(replay_config.value().message().has_active_nodes()); |
| 93 | std::vector<const Node *> active_nodes; |
| 94 | for (const auto &node : *replay_config.value().message().active_nodes()) { |
| 95 | active_nodes.emplace_back(configuration::GetNode( |
| 96 | &config.message(), node->name()->string_view())); |
| 97 | } |
| 98 | |
| 99 | std::vector<std::string> applications; |
| 100 | for (const auto &application : |
| 101 | *replay_config.value().message().applications()) { |
| 102 | if (application->name()->string_view() != "camera_message_interceptor") { |
| 103 | applications.emplace_back(application->name()->string_view()); |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | aos::logger::ChannelsInLogResult channels = |
| 108 | ChannelsInLog(logfiles, active_nodes, applications); |
| 109 | for (auto const &channel : |
| 110 | channels.watchers_and_fetchers_without_senders.value()) { |
Naman Gupta | 7eabb3a | 2023-03-01 11:49:44 -0800 | [diff] [blame^] | 111 | message_filter.emplace_back(channel.name, channel.type); |
Naman Gupta | e7fe355 | 2022-11-23 13:52:03 -0800 | [diff] [blame] | 112 | } |
| 113 | } |
| 114 | |
| 115 | aos::logger::LogReader reader( |
| 116 | logfiles, &config.message(), |
| 117 | message_filter.empty() ? nullptr : &message_filter); |
| 118 | |
Naman Gupta | 7eabb3a | 2023-03-01 11:49:44 -0800 | [diff] [blame^] | 119 | if (replay_config.has_value() && |
| 120 | replay_config.value().message().has_remap_channels()) { |
Naman Gupta | e7fe355 | 2022-11-23 13:52:03 -0800 | [diff] [blame] | 121 | for (auto const &remap_channel : |
| 122 | *replay_config.value().message().remap_channels()) { |
| 123 | auto const &channel = remap_channel->channel(); |
| 124 | std::string_view new_type = remap_channel->has_new_type() |
| 125 | ? remap_channel->new_type()->string_view() |
| 126 | : channel->type()->string_view(); |
| 127 | reader.RemapLoggedChannel( |
| 128 | channel->name()->string_view(), channel->type()->string_view(), |
| 129 | remap_channel->prefix()->string_view(), new_type); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | if (FLAGS_print_config) { |
| 134 | // TODO(Naman): Replace with config writer if it will be cleaner |
| 135 | std::cout << FlatbufferToJson(reader.configuration()) << std::endl; |
| 136 | } |
| 137 | |
| 138 | if (!FLAGS_skip_replay) { |
| 139 | aos::ShmEventLoop event_loop(reader.configuration()); |
| 140 | |
| 141 | event_loop.SkipAosLog(); |
| 142 | event_loop.SkipTimingReport(); |
| 143 | |
| 144 | reader.Register(&event_loop); |
| 145 | reader.OnEnd(event_loop.node(), [&event_loop]() { event_loop.Exit(); }); |
| 146 | |
| 147 | if (FLAGS_plot_timing) { |
| 148 | aos::Sender<aos::timing::ReplayTiming> replay_timing_sender = |
| 149 | event_loop.MakeSender<aos::timing::ReplayTiming>("/timing"); |
| 150 | reader.set_timing_accuracy_sender(event_loop.node(), |
| 151 | std::move(replay_timing_sender)); |
| 152 | } |
| 153 | |
| 154 | event_loop.Run(); |
| 155 | |
| 156 | reader.Deregister(); |
| 157 | } |
| 158 | |
| 159 | return EXIT_SUCCESS; |
| 160 | } |
| 161 | |
| 162 | } // namespace aos::logger |
| 163 | |
| 164 | int main(int argc, char *argv[]) { |
| 165 | gflags::SetUsageMessage( |
| 166 | R"message(Binary to replay the full contents of a logfile into shared memory. |
| 167 | #replay_config should be set in order to replay a set of nodes, applications and channels |
| 168 | #print config and skip replay, if you only want to print the config and not do log replay |
| 169 | Use case #1: log_replayer <log_dir> --print_config --replay_config=<path_to_config> --skip_replay |
| 170 | Use case #2: log_replayer <log_dir> --nofatal_sent_too_fast --replay_config=<path_to_config> |
| 171 | )message"); |
| 172 | |
| 173 | aos::InitGoogle(&argc, &argv); |
| 174 | return aos::logger::Main(argc, argv); |
| 175 | } |