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