blob: 7dabc1855c262817eb3470a311cc262e7453a209 [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>
Ravago Jones8c23dd62020-10-24 16:21:55 -07008#include "dirent.h"
James Kuszmaul38735e82019-12-07 16:42:06 -08009
10#include "aos/configuration.h"
11#include "aos/events/logging/logger.h"
12#include "aos/events/simulated_event_loop.h"
13#include "aos/init.h"
14#include "aos/json_to_flatbuffer.h"
15#include "gflags/gflags.h"
16
James Kuszmaul38735e82019-12-07 16:42:06 -080017DEFINE_string(
18 name, "",
19 "Name to match for printing out channels. Empty means no name filter.");
20DEFINE_string(type, "",
21 "Channel type to match for printing out channels. Empty means no "
22 "type filter.");
Austin Schuha81454b2020-05-12 19:58:36 -070023DEFINE_bool(fetch, false,
24 "If true, also print out the messages from before the start of the "
25 "log file");
Austin Schuh6f3babe2020-01-26 20:34:50 -080026DEFINE_bool(raw, false,
27 "If true, just print the data out unsorted and unparsed");
Austin Schuha81454b2020-05-12 19:58:36 -070028DEFINE_bool(format_raw, true,
29 "If true and --raw is specified, print out raw data, but use the "
30 "schema to format the data.");
Austin Schuhae46f362020-04-11 19:52:56 -070031DEFINE_int32(max_vector_size, 100,
32 "If positive, vectors longer than this will not be printed");
Ravago Jones5cc9df52020-09-02 21:29:58 -070033DEFINE_bool(pretty, false,
34 "If true, pretty print the messages on multiple lines");
Austin Schuh6f3babe2020-01-26 20:34:50 -080035
Austin Schuhc55d1662020-12-04 22:11:27 -080036bool EndsWith(std::string_view str, std::string_view ending) {
37 return str.size() >= ending.size() &&
38 str.substr(str.size() - ending.size()) == ending;
39}
40
Brian Silverman9891b292020-06-23 16:34:22 -070041// Print the flatbuffer out to stdout, both to remove the unnecessary cruft from
42// glog and to allow the user to readily redirect just the logged output
43// independent of any debugging information on stderr.
44void PrintMessage(const std::string_view node_name, const aos::Channel *channel,
Austin Schuh746690f2020-08-01 16:15:57 -070045 const aos::Context &context,
46 aos::FastStringBuilder *builder) {
47 builder->Reset();
Ravago Jones5cc9df52020-09-02 21:29:58 -070048 aos::FlatbufferToJson(
49 builder, channel->schema(), static_cast<const uint8_t *>(context.data),
50 {FLAGS_pretty, static_cast<size_t>(FLAGS_max_vector_size)});
Austin Schuh746690f2020-08-01 16:15:57 -070051
Austin Schuha81454b2020-05-12 19:58:36 -070052 if (context.monotonic_remote_time != context.monotonic_event_time) {
53 std::cout << node_name << context.realtime_event_time << " ("
54 << context.monotonic_event_time << ") sent "
55 << context.realtime_remote_time << " ("
56 << context.monotonic_remote_time << ") "
57 << channel->name()->c_str() << ' ' << channel->type()->c_str()
Austin Schuh746690f2020-08-01 16:15:57 -070058 << ": " << *builder << std::endl;
Austin Schuha81454b2020-05-12 19:58:36 -070059 } else {
60 std::cout << node_name << context.realtime_event_time << " ("
61 << context.monotonic_event_time << ") "
62 << channel->name()->c_str() << ' ' << channel->type()->c_str()
Austin Schuh746690f2020-08-01 16:15:57 -070063 << ": " << *builder << std::endl;
Austin Schuha81454b2020-05-12 19:58:36 -070064 }
65}
66
Ravago Jones8c23dd62020-10-24 16:21:55 -070067void SearchDirectory(std::vector<std::string> *files, std::string filename) {
68 DIR *directory = opendir(filename.c_str());
69
70 if (directory == nullptr) {
71 // its not a directory
72 // it could be a file
73 // or it could not exist
Austin Schuhc55d1662020-12-04 22:11:27 -080074 if (EndsWith(filename, ".bfbs") || EndsWith(filename, ".bfbs.xz")) {
75 files->emplace_back(filename);
76 }
Ravago Jones8c23dd62020-10-24 16:21:55 -070077 return;
78 }
79
80 struct dirent *directory_entry;
81 while ((directory_entry = readdir(directory)) != nullptr) {
82 std::string next_filename = directory_entry->d_name;
83 if (next_filename == "." || next_filename == "..") {
84 continue;
85 }
86
87 std::string path = filename + "/" + next_filename;
88 SearchDirectory(files, path);
89 }
90
91 closedir(directory);
92}
93
James Kuszmaul38735e82019-12-07 16:42:06 -080094int main(int argc, char **argv) {
95 gflags::SetUsageMessage(
Austin Schuh6f3babe2020-01-26 20:34:50 -080096 "Usage:\n"
97 " log_cat [args] logfile1 logfile2 ...\n"
98 "\n"
James Kuszmaul38735e82019-12-07 16:42:06 -080099 "This program provides a basic interface to dump data from a logfile to "
100 "stdout. Given a logfile, channel name filter, and type filter, it will "
101 "print all the messages in the logfile matching the filters. The message "
102 "filters work by taking the values of --name and --type and printing any "
103 "channel whose name contains --name as a substr and whose type contains "
104 "--type as a substr. Not specifying --name or --type leaves them free. "
105 "Calling this program without --name or --type specified prints out all "
106 "the logged data.");
107 aos::InitGoogle(&argc, &argv);
108
Austin Schuh6f3babe2020-01-26 20:34:50 -0800109 if (FLAGS_raw) {
110 if (argc != 2) {
111 LOG(FATAL) << "Expected 1 logfile as an argument.";
James Kuszmaul38735e82019-12-07 16:42:06 -0800112 }
Austin Schuh6f3babe2020-01-26 20:34:50 -0800113 aos::logger::MessageReader reader(argv[1]);
Austin Schuh2f8fd752020-09-01 22:38:28 -0700114 std::cout << aos::FlatbufferToJson(reader.log_file_header(),
115 {.multi_line = FLAGS_pretty,
116 .max_vector_size = static_cast<size_t>(
117 FLAGS_max_vector_size)})
118 << std::endl;
Austin Schuh6f3babe2020-01-26 20:34:50 -0800119
120 while (true) {
Austin Schuhadd6eb32020-11-09 21:24:26 -0800121 std::optional<
122 aos::SizePrefixedFlatbufferVector<aos::logger::MessageHeader>>
123 message = reader.ReadMessage();
Austin Schuh6f3babe2020-01-26 20:34:50 -0800124 if (!message) {
125 break;
126 }
Austin Schuha81454b2020-05-12 19:58:36 -0700127 const aos::Channel *channel =
128 reader.log_file_header()->configuration()->channels()->Get(
129 message.value().message().channel_index());
Austin Schuh6f3babe2020-01-26 20:34:50 -0800130
Austin Schuha81454b2020-05-12 19:58:36 -0700131 if (FLAGS_format_raw && message.value().message().data() != nullptr) {
132 std::cout << aos::configuration::StrippedChannelToString(channel) << " "
Ravago Jonescf453ab2020-05-06 21:14:53 -0700133 << aos::FlatbufferToJson(
134 message.value(),
Ravago Jones5cc9df52020-09-02 21:29:58 -0700135 {.multi_line = FLAGS_pretty, .max_vector_size = 4})
Ravago Jonescf453ab2020-05-06 21:14:53 -0700136 << ": "
Austin Schuha81454b2020-05-12 19:58:36 -0700137 << aos::FlatbufferToJson(
138 channel->schema(),
139 message.value().message().data()->data(),
Ravago Jones5cc9df52020-09-02 21:29:58 -0700140 {FLAGS_pretty,
141 static_cast<size_t>(FLAGS_max_vector_size)})
Austin Schuha81454b2020-05-12 19:58:36 -0700142 << std::endl;
143 } else {
144 std::cout << aos::configuration::StrippedChannelToString(channel) << " "
Ravago Jonescf453ab2020-05-06 21:14:53 -0700145 << aos::FlatbufferToJson(
146 message.value(),
Ravago Jones5cc9df52020-09-02 21:29:58 -0700147 {FLAGS_pretty,
148 static_cast<size_t>(FLAGS_max_vector_size)})
Austin Schuha81454b2020-05-12 19:58:36 -0700149 << std::endl;
150 }
Austin Schuh6f3babe2020-01-26 20:34:50 -0800151 }
152 return 0;
James Kuszmaul38735e82019-12-07 16:42:06 -0800153 }
154
Austin Schuh6f3babe2020-01-26 20:34:50 -0800155 if (argc < 2) {
156 LOG(FATAL) << "Expected at least 1 logfile as an argument.";
157 }
158
Austin Schuh5212cad2020-09-09 23:12:09 -0700159 std::vector<std::string> unsorted_logfiles;
Austin Schuh6f3babe2020-01-26 20:34:50 -0800160 for (int i = 1; i < argc; ++i) {
Ravago Jones8c23dd62020-10-24 16:21:55 -0700161 SearchDirectory(&unsorted_logfiles, argv[i]);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800162 }
163
Austin Schuh11d43732020-09-21 17:28:30 -0700164 const std::vector<aos::logger::LogFile> logfiles =
Austin Schuh5212cad2020-09-09 23:12:09 -0700165 aos::logger::SortParts(unsorted_logfiles);
166
Austin Schuh6f3babe2020-01-26 20:34:50 -0800167 aos::logger::LogReader reader(logfiles);
Austin Schuha81454b2020-05-12 19:58:36 -0700168
Austin Schuh746690f2020-08-01 16:15:57 -0700169 aos::FastStringBuilder builder;
170
Austin Schuha81454b2020-05-12 19:58:36 -0700171 aos::SimulatedEventLoopFactory event_loop_factory(reader.configuration());
172 reader.Register(&event_loop_factory);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800173
174 std::vector<std::unique_ptr<aos::EventLoop>> printer_event_loops;
175
James Kuszmaul912af072020-10-31 16:06:54 -0700176 bool found_channel = false;
177
Austin Schuh6f3babe2020-01-26 20:34:50 -0800178 for (const aos::Node *node : reader.Nodes()) {
179 std::unique_ptr<aos::EventLoop> printer_event_loop =
Austin Schuha81454b2020-05-12 19:58:36 -0700180 event_loop_factory.MakeEventLoop("printer", node);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800181 printer_event_loop->SkipTimingReport();
182 printer_event_loop->SkipAosLog();
183
Brian Silverman9891b292020-06-23 16:34:22 -0700184 struct MessageInfo {
185 std::string node_name;
186 std::unique_ptr<aos::RawFetcher> fetcher;
187 };
188 std::vector<MessageInfo> messages_before_start;
Austin Schuha81454b2020-05-12 19:58:36 -0700189
Austin Schuh6f3babe2020-01-26 20:34:50 -0800190 const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
Brian Silverman9891b292020-06-23 16:34:22 -0700191 printer_event_loop->configuration()->channels();
192
Austin Schuh6f3babe2020-01-26 20:34:50 -0800193 for (flatbuffers::uoffset_t i = 0; i < channels->size(); i++) {
194 const aos::Channel *channel = channels->Get(i);
195 const flatbuffers::string_view name = channel->name()->string_view();
196 const flatbuffers::string_view type = channel->type()->string_view();
197 if (name.find(FLAGS_name) != std::string::npos &&
198 type.find(FLAGS_type) != std::string::npos) {
199 if (!aos::configuration::ChannelIsReadableOnNode(
200 channel, printer_event_loop->node())) {
201 continue;
202 }
203 VLOG(1) << "Listening on " << name << " " << type;
204
205 std::string node_name =
206 node == nullptr ? ""
207 : std::string(node->name()->string_view()) + " ";
208
209 CHECK_NOTNULL(channel->schema());
Austin Schuha81454b2020-05-12 19:58:36 -0700210
Brian Silverman9891b292020-06-23 16:34:22 -0700211 // Fetch the last message on this channel from before the log start
212 // time.
Austin Schuha81454b2020-05-12 19:58:36 -0700213 if (FLAGS_fetch) {
Austin Schuha81454b2020-05-12 19:58:36 -0700214 std::unique_ptr<aos::RawFetcher> fetcher =
215 printer_event_loop->MakeRawFetcher(channel);
216 if (fetcher->Fetch()) {
Brian Silverman9891b292020-06-23 16:34:22 -0700217 MessageInfo message{.node_name = node_name,
218 .fetcher = std::move(fetcher)};
Austin Schuha81454b2020-05-12 19:58:36 -0700219 // Insert it sorted into the vector so we can print in time order
220 // instead of channel order at the start.
221 auto it = std::lower_bound(
Brian Silverman9891b292020-06-23 16:34:22 -0700222 messages_before_start.begin(), messages_before_start.end(),
223 message, [](const MessageInfo &lhs, const MessageInfo &rhs) {
224 if (lhs.fetcher->context().monotonic_event_time <
225 rhs.fetcher->context().monotonic_event_time) {
Austin Schuha81454b2020-05-12 19:58:36 -0700226 return true;
227 }
Brian Silverman9891b292020-06-23 16:34:22 -0700228 if (lhs.fetcher->context().monotonic_event_time >
229 rhs.fetcher->context().monotonic_event_time) {
Austin Schuha81454b2020-05-12 19:58:36 -0700230 return false;
231 }
Brian Silverman9891b292020-06-23 16:34:22 -0700232 return lhs.fetcher->channel() < rhs.fetcher->channel();
Austin Schuha81454b2020-05-12 19:58:36 -0700233 });
Brian Silverman9891b292020-06-23 16:34:22 -0700234 messages_before_start.insert(it, std::move(message));
Austin Schuha81454b2020-05-12 19:58:36 -0700235 }
236 }
237
Austin Schuh6f3babe2020-01-26 20:34:50 -0800238 printer_event_loop->MakeRawWatcher(
Ravago Jones5cc9df52020-09-02 21:29:58 -0700239 channel, [channel, node_name, &builder](const aos::Context &context,
240 const void * /*message*/) {
Austin Schuh746690f2020-08-01 16:15:57 -0700241 PrintMessage(node_name, channel, context, &builder);
Austin Schuh6f3babe2020-01-26 20:34:50 -0800242 });
243 found_channel = true;
244 }
245 }
246
Brian Silverman9891b292020-06-23 16:34:22 -0700247 // Print the messages from before the log start time.
Austin Schuha81454b2020-05-12 19:58:36 -0700248 // TODO(austin): Sort between nodes too when it becomes annoying enough.
Brian Silverman9891b292020-06-23 16:34:22 -0700249 for (const MessageInfo &message : messages_before_start) {
250 PrintMessage(message.node_name, message.fetcher->channel(),
Austin Schuh746690f2020-08-01 16:15:57 -0700251 message.fetcher->context(), &builder);
Austin Schuha81454b2020-05-12 19:58:36 -0700252 }
Austin Schuh6f3babe2020-01-26 20:34:50 -0800253 printer_event_loops.emplace_back(std::move(printer_event_loop));
Brian Silverman9891b292020-06-23 16:34:22 -0700254
255 std::cout << std::endl;
Austin Schuhee711052020-08-24 16:06:09 -0700256 std::cout << (node != nullptr ? (node->name()->str() + " ") : "")
257 << "Log starting at " << reader.realtime_start_time(node) << " ("
258 << reader.monotonic_start_time(node) << ")";
Brian Silverman9891b292020-06-23 16:34:22 -0700259 std::cout << std::endl << std::endl;
James Kuszmaul38735e82019-12-07 16:42:06 -0800260 }
261
James Kuszmaul912af072020-10-31 16:06:54 -0700262 if (!found_channel) {
263 LOG(FATAL) << "Could not find any channels";
264 }
265
Austin Schuha81454b2020-05-12 19:58:36 -0700266 if (FLAGS_fetch) {
267 // New line to separate fetched messages from non-fetched messages.
268 std::cout << std::endl;
269 }
270
271 event_loop_factory.Run();
James Kuszmaul38735e82019-12-07 16:42:06 -0800272
Austin Schuh51a92592020-08-09 13:17:00 -0700273 reader.Deregister();
274
James Kuszmaul38735e82019-12-07 16:42:06 -0800275 return 0;
276}