blob: c2378ab8ef13aebb2d152992969bbd2ded812cd5 [file] [log] [blame]
Brian Silverman9891b292020-06-23 16:34:22 -07001#include <algorithm>
James Kuszmaul38735e82019-12-07 16:42:06 -08002#include <iostream>
Brian Silverman9891b292020-06-23 16:34:22 -07003#include <memory>
4#include <optional>
5#include <string>
6#include <string_view>
7#include <vector>
James Kuszmaul38735e82019-12-07 16:42:06 -08008
9#include "aos/configuration.h"
Austin Schuhb06f03b2021-02-17 22:00:37 -080010#include "aos/events/logging/log_reader.h"
James Kuszmaul38735e82019-12-07 16:42:06 -080011#include "aos/events/simulated_event_loop.h"
12#include "aos/init.h"
13#include "aos/json_to_flatbuffer.h"
14#include "gflags/gflags.h"
15
James Kuszmaul38735e82019-12-07 16:42:06 -080016DEFINE_string(
17 name, "",
18 "Name to match for printing out channels. Empty means no name filter.");
19DEFINE_string(type, "",
20 "Channel type to match for printing out channels. Empty means no "
21 "type filter.");
Austin Schuha81454b2020-05-12 19:58:36 -070022DEFINE_bool(fetch, false,
23 "If true, also print out the messages from before the start of the "
24 "log file");
Austin Schuh6f3babe2020-01-26 20:34:50 -080025DEFINE_bool(raw, false,
26 "If true, just print the data out unsorted and unparsed");
Brian Silverman8ff74aa2021-02-05 16:37:15 -080027DEFINE_string(raw_header, "",
28 "If set, the file to read the header from in raw mode");
Austin Schuha81454b2020-05-12 19:58:36 -070029DEFINE_bool(format_raw, true,
30 "If true and --raw is specified, print out raw data, but use the "
31 "schema to format the data.");
Austin Schuhae46f362020-04-11 19:52:56 -070032DEFINE_int32(max_vector_size, 100,
33 "If positive, vectors longer than this will not be printed");
Ravago Jones5cc9df52020-09-02 21:29:58 -070034DEFINE_bool(pretty, false,
35 "If true, pretty print the messages on multiple lines");
Austin Schuh569c7f92020-12-11 20:01:42 -080036DEFINE_bool(print, true,
37 "If true, actually print the messages. If false, discard them, "
38 "confirming they can be parsed.");
Austin Schuh7af06d52021-06-28 15:46:59 -070039DEFINE_bool(print_parts_only, false,
40 "If true, only print out the results of logfile sorting.");
Austin Schuh6f3babe2020-01-26 20:34:50 -080041
Brian Silverman9891b292020-06-23 16:34:22 -070042// Print the flatbuffer out to stdout, both to remove the unnecessary cruft from
43// glog and to allow the user to readily redirect just the logged output
44// independent of any debugging information on stderr.
45void PrintMessage(const std::string_view node_name, const aos::Channel *channel,
Austin Schuh746690f2020-08-01 16:15:57 -070046 const aos::Context &context,
47 aos::FastStringBuilder *builder) {
48 builder->Reset();
Austin Schuha9df9ad2021-06-16 14:49:39 -070049 CHECK(flatbuffers::Verify(*channel->schema(),
50 *channel->schema()->root_table(),
51 static_cast<const uint8_t *>(context.data),
52 static_cast<size_t>(context.size)))
53 << ": Corrupted flatbuffer on " << channel->name()->c_str() << " "
54 << channel->type()->c_str();
55
Ravago Jones5cc9df52020-09-02 21:29:58 -070056 aos::FlatbufferToJson(
57 builder, channel->schema(), static_cast<const uint8_t *>(context.data),
58 {FLAGS_pretty, static_cast<size_t>(FLAGS_max_vector_size)});
Austin Schuh746690f2020-08-01 16:15:57 -070059
Austin Schuha81454b2020-05-12 19:58:36 -070060 if (context.monotonic_remote_time != context.monotonic_event_time) {
61 std::cout << node_name << context.realtime_event_time << " ("
62 << context.monotonic_event_time << ") sent "
63 << context.realtime_remote_time << " ("
64 << context.monotonic_remote_time << ") "
65 << channel->name()->c_str() << ' ' << channel->type()->c_str()
Austin Schuh746690f2020-08-01 16:15:57 -070066 << ": " << *builder << std::endl;
Austin Schuha81454b2020-05-12 19:58:36 -070067 } else {
68 std::cout << node_name << context.realtime_event_time << " ("
69 << context.monotonic_event_time << ") "
70 << channel->name()->c_str() << ' ' << channel->type()->c_str()
Austin Schuh746690f2020-08-01 16:15:57 -070071 << ": " << *builder << std::endl;
Austin Schuha81454b2020-05-12 19:58:36 -070072 }
73}
74
James Kuszmaul38735e82019-12-07 16:42:06 -080075int main(int argc, char **argv) {
76 gflags::SetUsageMessage(
Austin Schuh6f3babe2020-01-26 20:34:50 -080077 "Usage:\n"
78 " log_cat [args] logfile1 logfile2 ...\n"
79 "\n"
James Kuszmaul38735e82019-12-07 16:42:06 -080080 "This program provides a basic interface to dump data from a logfile to "
81 "stdout. Given a logfile, channel name filter, and type filter, it will "
82 "print all the messages in the logfile matching the filters. The message "
83 "filters work by taking the values of --name and --type and printing any "
84 "channel whose name contains --name as a substr and whose type contains "
85 "--type as a substr. Not specifying --name or --type leaves them free. "
86 "Calling this program without --name or --type specified prints out all "
87 "the logged data.");
88 aos::InitGoogle(&argc, &argv);
89
Austin Schuh6f3babe2020-01-26 20:34:50 -080090 if (FLAGS_raw) {
91 if (argc != 2) {
92 LOG(FATAL) << "Expected 1 logfile as an argument.";
James Kuszmaul38735e82019-12-07 16:42:06 -080093 }
Austin Schuh6f3babe2020-01-26 20:34:50 -080094 aos::logger::MessageReader reader(argv[1]);
Brian Silverman8ff74aa2021-02-05 16:37:15 -080095
96 std::optional<aos::logger::MessageReader> raw_header_reader;
97 const aos::logger::LogFileHeader *full_header = reader.log_file_header();
98 if (!FLAGS_raw_header.empty()) {
99 raw_header_reader.emplace(FLAGS_raw_header);
Austin Schuh881530d2021-06-21 17:40:04 -0700100 std::cout << aos::FlatbufferToJson(
101 reader.log_file_header(),
102 {.multi_line = FLAGS_pretty,
103 .max_vector_size =
104 static_cast<size_t>(FLAGS_max_vector_size)})
105 << std::endl;
Brian Silverman8ff74aa2021-02-05 16:37:15 -0800106 CHECK_EQ(
107 reader.log_file_header()->configuration_sha256()->string_view(),
108 aos::logger::Sha256(raw_header_reader->raw_log_file_header().span()));
109 full_header = raw_header_reader->log_file_header();
110 }
111 std::cout << aos::FlatbufferToJson(full_header,
Austin Schuh2f8fd752020-09-01 22:38:28 -0700112 {.multi_line = FLAGS_pretty,
113 .max_vector_size = static_cast<size_t>(
114 FLAGS_max_vector_size)})
115 << std::endl;
Austin Schuh6f3babe2020-01-26 20:34:50 -0800116
117 while (true) {
Austin Schuhadd6eb32020-11-09 21:24:26 -0800118 std::optional<
119 aos::SizePrefixedFlatbufferVector<aos::logger::MessageHeader>>
120 message = reader.ReadMessage();
Austin Schuh6f3babe2020-01-26 20:34:50 -0800121 if (!message) {
122 break;
123 }
Brian Silverman8ff74aa2021-02-05 16:37:15 -0800124 const auto *const channels = full_header->configuration()->channels();
125 const size_t channel_index = message.value().message().channel_index();
126 CHECK_LT(channel_index, channels->size());
127 const aos::Channel *const channel = channels->Get(channel_index);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800128
Austin Schuha9df9ad2021-06-16 14:49:39 -0700129 if (message.value().message().data() != nullptr) {
130 CHECK(channel->has_schema());
131
132 CHECK(flatbuffers::Verify(*channel->schema(),
133 *channel->schema()->root_table(),
134 message.value().message().data()->data(),
135 message.value().message().data()->size()))
136 << ": Corrupted flatbuffer on " << channel->name()->c_str() << " "
137 << channel->type()->c_str();
138 }
139
Austin Schuha81454b2020-05-12 19:58:36 -0700140 if (FLAGS_format_raw && message.value().message().data() != nullptr) {
141 std::cout << aos::configuration::StrippedChannelToString(channel) << " "
Ravago Jonescf453ab2020-05-06 21:14:53 -0700142 << aos::FlatbufferToJson(
143 message.value(),
Ravago Jones5cc9df52020-09-02 21:29:58 -0700144 {.multi_line = FLAGS_pretty, .max_vector_size = 4})
Ravago Jonescf453ab2020-05-06 21:14:53 -0700145 << ": "
Austin Schuha81454b2020-05-12 19:58:36 -0700146 << aos::FlatbufferToJson(
147 channel->schema(),
148 message.value().message().data()->data(),
Ravago Jones5cc9df52020-09-02 21:29:58 -0700149 {FLAGS_pretty,
150 static_cast<size_t>(FLAGS_max_vector_size)})
Austin Schuha81454b2020-05-12 19:58:36 -0700151 << std::endl;
152 } else {
153 std::cout << aos::configuration::StrippedChannelToString(channel) << " "
Ravago Jonescf453ab2020-05-06 21:14:53 -0700154 << aos::FlatbufferToJson(
155 message.value(),
Ravago Jones5cc9df52020-09-02 21:29:58 -0700156 {FLAGS_pretty,
157 static_cast<size_t>(FLAGS_max_vector_size)})
Austin Schuha81454b2020-05-12 19:58:36 -0700158 << std::endl;
159 }
Austin Schuh6f3babe2020-01-26 20:34:50 -0800160 }
161 return 0;
James Kuszmaul38735e82019-12-07 16:42:06 -0800162 }
163
Austin Schuh6f3babe2020-01-26 20:34:50 -0800164 if (argc < 2) {
165 LOG(FATAL) << "Expected at least 1 logfile as an argument.";
166 }
167
Ravago Jones6b23e172020-12-05 17:39:01 -0800168 const std::vector<std::string> unsorted_logfiles =
169 aos::logger::FindLogs(argc, argv);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800170
Austin Schuh11d43732020-09-21 17:28:30 -0700171 const std::vector<aos::logger::LogFile> logfiles =
Austin Schuh5212cad2020-09-09 23:12:09 -0700172 aos::logger::SortParts(unsorted_logfiles);
173
Austin Schuhfe3fb342021-01-16 18:50:37 -0800174 for (auto &it : logfiles) {
175 VLOG(1) << it;
Austin Schuh7af06d52021-06-28 15:46:59 -0700176 if (FLAGS_print_parts_only) {
177 std::cout << it << std::endl;
178 }
179 }
180 if (FLAGS_print_parts_only) {
181 return 0;
Austin Schuhfe3fb342021-01-16 18:50:37 -0800182 }
183
Austin Schuh6f3babe2020-01-26 20:34:50 -0800184 aos::logger::LogReader reader(logfiles);
Austin Schuha81454b2020-05-12 19:58:36 -0700185
Austin Schuh746690f2020-08-01 16:15:57 -0700186 aos::FastStringBuilder builder;
187
Austin Schuha81454b2020-05-12 19:58:36 -0700188 aos::SimulatedEventLoopFactory event_loop_factory(reader.configuration());
189 reader.Register(&event_loop_factory);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800190
191 std::vector<std::unique_ptr<aos::EventLoop>> printer_event_loops;
192
James Kuszmaul912af072020-10-31 16:06:54 -0700193 bool found_channel = false;
194
Austin Schuh07676622021-01-21 18:59:17 -0800195 for (const aos::Node *node :
196 aos::configuration::GetNodes(event_loop_factory.configuration())) {
Austin Schuh6f3babe2020-01-26 20:34:50 -0800197 std::unique_ptr<aos::EventLoop> printer_event_loop =
Austin Schuha81454b2020-05-12 19:58:36 -0700198 event_loop_factory.MakeEventLoop("printer", node);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800199 printer_event_loop->SkipTimingReport();
200 printer_event_loop->SkipAosLog();
201
Brian Silverman9891b292020-06-23 16:34:22 -0700202 struct MessageInfo {
203 std::string node_name;
204 std::unique_ptr<aos::RawFetcher> fetcher;
205 };
206 std::vector<MessageInfo> messages_before_start;
Austin Schuha81454b2020-05-12 19:58:36 -0700207
Austin Schuh6f3babe2020-01-26 20:34:50 -0800208 const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
Brian Silverman9891b292020-06-23 16:34:22 -0700209 printer_event_loop->configuration()->channels();
210
Austin Schuh6f3babe2020-01-26 20:34:50 -0800211 for (flatbuffers::uoffset_t i = 0; i < channels->size(); i++) {
212 const aos::Channel *channel = channels->Get(i);
213 const flatbuffers::string_view name = channel->name()->string_view();
214 const flatbuffers::string_view type = channel->type()->string_view();
215 if (name.find(FLAGS_name) != std::string::npos &&
216 type.find(FLAGS_type) != std::string::npos) {
217 if (!aos::configuration::ChannelIsReadableOnNode(
218 channel, printer_event_loop->node())) {
219 continue;
220 }
221 VLOG(1) << "Listening on " << name << " " << type;
222
223 std::string node_name =
224 node == nullptr ? ""
225 : std::string(node->name()->string_view()) + " ";
226
227 CHECK_NOTNULL(channel->schema());
Austin Schuha81454b2020-05-12 19:58:36 -0700228
Brian Silverman9891b292020-06-23 16:34:22 -0700229 // Fetch the last message on this channel from before the log start
230 // time.
Austin Schuha81454b2020-05-12 19:58:36 -0700231 if (FLAGS_fetch) {
Austin Schuha81454b2020-05-12 19:58:36 -0700232 std::unique_ptr<aos::RawFetcher> fetcher =
233 printer_event_loop->MakeRawFetcher(channel);
234 if (fetcher->Fetch()) {
Brian Silverman9891b292020-06-23 16:34:22 -0700235 MessageInfo message{.node_name = node_name,
236 .fetcher = std::move(fetcher)};
Austin Schuha81454b2020-05-12 19:58:36 -0700237 // Insert it sorted into the vector so we can print in time order
238 // instead of channel order at the start.
239 auto it = std::lower_bound(
Brian Silverman9891b292020-06-23 16:34:22 -0700240 messages_before_start.begin(), messages_before_start.end(),
241 message, [](const MessageInfo &lhs, const MessageInfo &rhs) {
242 if (lhs.fetcher->context().monotonic_event_time <
243 rhs.fetcher->context().monotonic_event_time) {
Austin Schuha81454b2020-05-12 19:58:36 -0700244 return true;
245 }
Brian Silverman9891b292020-06-23 16:34:22 -0700246 if (lhs.fetcher->context().monotonic_event_time >
247 rhs.fetcher->context().monotonic_event_time) {
Austin Schuha81454b2020-05-12 19:58:36 -0700248 return false;
249 }
Brian Silverman9891b292020-06-23 16:34:22 -0700250 return lhs.fetcher->channel() < rhs.fetcher->channel();
Austin Schuha81454b2020-05-12 19:58:36 -0700251 });
Brian Silverman9891b292020-06-23 16:34:22 -0700252 messages_before_start.insert(it, std::move(message));
Austin Schuha81454b2020-05-12 19:58:36 -0700253 }
254 }
255
Austin Schuh6f3babe2020-01-26 20:34:50 -0800256 printer_event_loop->MakeRawWatcher(
Ravago Jones5cc9df52020-09-02 21:29:58 -0700257 channel, [channel, node_name, &builder](const aos::Context &context,
258 const void * /*message*/) {
Austin Schuh569c7f92020-12-11 20:01:42 -0800259 if (FLAGS_print) {
260 PrintMessage(node_name, channel, context, &builder);
261 }
Austin Schuh6f3babe2020-01-26 20:34:50 -0800262 });
263 found_channel = true;
264 }
265 }
266
Brian Silverman9891b292020-06-23 16:34:22 -0700267 // Print the messages from before the log start time.
Austin Schuha81454b2020-05-12 19:58:36 -0700268 // TODO(austin): Sort between nodes too when it becomes annoying enough.
Brian Silverman9891b292020-06-23 16:34:22 -0700269 for (const MessageInfo &message : messages_before_start) {
Austin Schuh569c7f92020-12-11 20:01:42 -0800270 if (FLAGS_print) {
271 PrintMessage(message.node_name, message.fetcher->channel(),
272 message.fetcher->context(), &builder);
273 }
Austin Schuha81454b2020-05-12 19:58:36 -0700274 }
Austin Schuh6f3babe2020-01-26 20:34:50 -0800275 printer_event_loops.emplace_back(std::move(printer_event_loop));
Brian Silverman9891b292020-06-23 16:34:22 -0700276
277 std::cout << std::endl;
Austin Schuhee711052020-08-24 16:06:09 -0700278 std::cout << (node != nullptr ? (node->name()->str() + " ") : "")
279 << "Log starting at " << reader.realtime_start_time(node) << " ("
280 << reader.monotonic_start_time(node) << ")";
Brian Silverman9891b292020-06-23 16:34:22 -0700281 std::cout << std::endl << std::endl;
James Kuszmaul38735e82019-12-07 16:42:06 -0800282 }
283
James Kuszmaul912af072020-10-31 16:06:54 -0700284 if (!found_channel) {
285 LOG(FATAL) << "Could not find any channels";
286 }
287
Austin Schuha81454b2020-05-12 19:58:36 -0700288 if (FLAGS_fetch) {
289 // New line to separate fetched messages from non-fetched messages.
290 std::cout << std::endl;
291 }
292
293 event_loop_factory.Run();
James Kuszmaul38735e82019-12-07 16:42:06 -0800294
Austin Schuh51a92592020-08-09 13:17:00 -0700295 reader.Deregister();
296
James Kuszmaul38735e82019-12-07 16:42:06 -0800297 return 0;
298}