blob: 1d40de17069e17b8d3116b80f0e5a26322100cc1 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include <stdio.h>
2#include <stdlib.h>
Daniel Pettiaa5bdb12014-12-20 16:33:05 -08003#include <string.h>
brians343bc112013-02-10 01:53:46 +00004#include <getopt.h>
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <fcntl.h>
8#include <inttypes.h>
brians343bc112013-02-10 01:53:46 +00009
Brian Silverman88471dc2014-02-15 22:35:42 -080010#include <algorithm>
Daniel Pettib6c885b2014-09-12 10:04:28 -070011#include <memory>
12#include <string>
Brian Silverman88471dc2014-02-15 22:35:42 -080013
Daniel Pettiaa5bdb12014-12-20 16:33:05 -080014#include "aos/linux_code/configuration.h"
John Park33858a32018-09-28 23:05:48 -070015#include "aos/logging/binary_log_file.h"
16#include "aos/queue_types.h"
17#include "aos/logging/logging.h"
18#include "aos/logging/implementations.h"
19#include "aos/logging/printf_formats.h"
20#include "aos/util/string_to_num.h"
Brian Silverman88471dc2014-02-15 22:35:42 -080021
22using ::aos::logging::linux_code::LogFileMessageHeader;
brians343bc112013-02-10 01:53:46 +000023
24namespace {
25
26const char *kArgsHelp = "[OPTION]... [FILE]\n"
27 "Display log file FILE (created by BinaryLogReader) to stdout.\n"
28 "FILE is \"aos_log-current\" by default.\n"
Brian Silverman65e569d2014-10-24 15:43:20 -040029 "FILE can also be \"-\" to read from standard input.\n"
brians343bc112013-02-10 01:53:46 +000030 "\n"
Brian Silverman8c2b00e2015-02-17 20:30:50 -050031 " -n, --name-prefix NAME "
32 "only display entries from processes which share NAME as a prefix\n"
33 " -N, --name NAME only display entries from processes named NAME\n"
34 " -l, --level LEVEL "
brians343bc112013-02-10 01:53:46 +000035 "only display log entries at least as important as LEVEL\n"
Brian Silverman8c2b00e2015-02-17 20:30:50 -050036 " -p, --pid PID only display log entries from process PID\n"
37 " -f, --follow "
brians343bc112013-02-10 01:53:46 +000038 "wait when the end of the file is reached (implies --end)\n"
Brian Silverman8c2b00e2015-02-17 20:30:50 -050039 " -t, --terminate stop when the end of file is reached (default)\n"
40 " -b, --beginning start at the beginning of the file (default)\n"
41 " -e, --end start at the end of the file\n"
42 " -s, --skip NUMBER skip NUMBER matching logs\n"
43 " -m, --max NUMBER only display up to NUMBER logs\n"
brians343bc112013-02-10 01:53:46 +000044 " // -o, --format FORMAT use FORMAT to display log entries\n"
Brian Silverman8c2b00e2015-02-17 20:30:50 -050045 " -h, --help display this help and exit\n"
brians343bc112013-02-10 01:53:46 +000046 "\n"
47 "LEVEL must be DEBUG, INFO, WARNING, ERROR, or FATAL.\n"
48 " It defaults to INFO.\n"
49 "\n"
Daniel Pettie6f33e22014-08-21 20:35:55 -070050 "TODO(brians) implement the commented out ones.\n";
brians343bc112013-02-10 01:53:46 +000051
Daniel Petti88c81f42014-09-12 10:05:05 -070052const char *kExampleUsages = "To view logs from the shooter:\n"
53 "\t`log_displayer -n shooter`\n"
54 "To view debug logs from the shooter:\n"
55 "\t`log_displayer -n shooter -l DEBUG`\n"
56 "To view what the shooter is logging in realtime:\n"
57 "\t`log_displayer -f -n shooter`\n"
58 "To view shooter logs from an old log file:\n"
59 "\t`log_displayer aos_log-<number> -n shooter`\n"
60 "To view the statuses of the shooter hall effects in realtime:\n"
61 "\t`log_displayer -f -n shooter -l DEBUG | grep .Position`\n";
62
brians343bc112013-02-10 01:53:46 +000063void PrintHelpAndExit() {
64 fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
Daniel Petti88c81f42014-09-12 10:05:05 -070065 fprintf(stderr, "\nExample usages:\n\n%s", kExampleUsages);
66
67 // Get the possible executables from start_list.txt.
68 FILE *start_list = fopen("start_list.txt", "r");
Daniel Pettiaa5bdb12014-12-20 16:33:05 -080069 if (!start_list) {
70 ::std::string path(::aos::configuration::GetRootDirectory());
71 path += "/start_list.txt";
72 start_list = fopen(path.c_str(), "r");
73 if (!start_list) {
74 printf("\nCannot open start_list.txt. This means that the\n"
75 "possible arguments for the -n option cannot be shown. log_displayer\n"
76 "looks for start_list.txt in the current working directory and in\n"
77 "%s.\n\n", ::aos::configuration::GetRootDirectory());
78 PLOG(FATAL, "Unable to open start_list.txt");
79 }
Daniel Petti88c81f42014-09-12 10:05:05 -070080 }
81
82 // Get file size.
83 if (fseek(start_list, 0, SEEK_END)) {
84 PLOG(FATAL, "fseek() failed while reading start_list.txt");
85 }
86 int size = ftell(start_list);
87 if (size < 0) {
88 PLOG(FATAL, "ftell() failed while reading start_list.txt");
89 }
90 rewind(start_list);
91
92 ::std::unique_ptr<char[]> contents(new char[size + 1]);
93 if (contents == NULL) {
94 LOG(FATAL, "malloc() failed while reading start_list.txt.\n");
95 }
96 size_t bytes_read = fread(contents.get(), 1, size, start_list);
97 if (bytes_read < static_cast<size_t>(size)) {
98 LOG(FATAL, "Read %zu bytes from start_list.txt, expected %d.\n",
99 bytes_read, size);
100 }
101
102 // printf doesn't like strings without the \0.
103 contents[size] = '\0';
104 fprintf(stderr, "\nPossible arguments for the -n option:\n%s", contents.get());
105
106 if (fclose(start_list)) {
107 LOG(FATAL, "fclose() failed.\n");
108 }
brians343bc112013-02-10 01:53:46 +0000109
110 exit(EXIT_SUCCESS);
111}
112
113} // namespace
114
115int main(int argc, char **argv) {
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500116 const char *filter_name = nullptr, *filter_exact_name = nullptr;
Brian Silvermanf7780312014-02-16 17:26:15 -0800117 size_t filter_length = 0;
brians343bc112013-02-10 01:53:46 +0000118 log_level filter_level = INFO;
Brian Silvermanf7780312014-02-16 17:26:15 -0800119 bool follow = false;
120 // Whether we need to skip everything until we get to the end of the file.
121 bool skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000122 const char *filename = "aos_log-current";
Daniel Pettib6c885b2014-09-12 10:04:28 -0700123 int display_max = 0;
124 int32_t source_pid = -1;
brians343bc112013-02-10 01:53:46 +0000125
Brian Silvermanff485782014-06-18 19:59:09 -0700126 ::aos::logging::Init();
Brian Silvermanf7780312014-02-16 17:26:15 -0800127 ::aos::logging::AddImplementation(
128 new ::aos::logging::StreamLogImplementation(stdout));
129
brians343bc112013-02-10 01:53:46 +0000130 while (true) {
131 static struct option long_options[] = {
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500132 {"name-prefix", required_argument, NULL, 'n'},
133 {"name", required_argument, NULL, 'N'},
brians343bc112013-02-10 01:53:46 +0000134 {"level", required_argument, NULL, 'l'},
135 {"pid", required_argument, NULL, 'p'},
136
137 {"follow", no_argument, NULL, 'f'},
138 {"terminate", no_argument, NULL, 't'},
139 {"beginning", no_argument, NULL, 'b'},
140 {"end", no_argument, NULL, 'e'},
141 {"skip", required_argument, NULL, 's'},
142 {"max", required_argument, NULL, 'm'},
143
144 {"format", required_argument, NULL, 'o'},
145
146 {"help", no_argument, NULL, 'h'},
147 {0, 0, 0, 0}
148 };
149 int option_index = 0;
150
Brian Silvermandbcc87d2015-03-15 20:41:34 -0700151 const int c = getopt_long(argc, argv, "N:n:l:p:fts:m:o:h",
brians343bc112013-02-10 01:53:46 +0000152 long_options, &option_index);
153 if (c == -1) { // if we're at the end
154 break;
155 }
156 switch (c) {
157 case 0:
Daniel Petti88c81f42014-09-12 10:05:05 -0700158 fputs("LogDisplayer: got a 0 option but didn't set up any\n", stderr);
brians343bc112013-02-10 01:53:46 +0000159 abort();
160 case 'n':
161 filter_name = optarg;
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500162 filter_exact_name = nullptr;
163 filter_length = strlen(filter_name);
164 break;
165 case 'N':
166 filter_exact_name = optarg;
167 filter_name = nullptr;
Brian Silverman88471dc2014-02-15 22:35:42 -0800168 filter_length = strlen(filter_name);
brians343bc112013-02-10 01:53:46 +0000169 break;
170 case 'l':
Brian Silvermanab6615c2013-03-05 20:29:29 -0800171 filter_level = ::aos::logging::str_log(optarg);
brians343bc112013-02-10 01:53:46 +0000172 if (filter_level == LOG_UNKNOWN) {
173 fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
174 exit(EXIT_FAILURE);
175 }
176 break;
177 case 'p':
Daniel Petti5aa29792014-12-27 17:48:07 -0500178 if (!::aos::util::StringToNumber(::std::string(optarg), &source_pid)) {
Daniel Pettib6c885b2014-09-12 10:04:28 -0700179 fprintf(stderr, "ERROR: -p expects a number, not '%s'.\n", optarg);
180 exit(EXIT_FAILURE);
181 }
182 if (source_pid < 0) {
183 fprintf(stderr, "LogDisplayer: invalid pid '%s'\n", optarg);
184 exit(EXIT_FAILURE);
185 }
brians343bc112013-02-10 01:53:46 +0000186 break;
187 case 'f':
188 follow = true;
Brian Silvermanf7780312014-02-16 17:26:15 -0800189 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000190 break;
191 case 't':
192 follow = false;
193 break;
194 case 'b':
Brian Silvermanf7780312014-02-16 17:26:15 -0800195 skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000196 break;
197 case 'e':
Brian Silvermanf7780312014-02-16 17:26:15 -0800198 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000199 break;
200 case 'm':
Daniel Petti5aa29792014-12-27 17:48:07 -0500201 if (!::aos::util::StringToNumber(::std::string(optarg), &display_max)) {
Daniel Pettib6c885b2014-09-12 10:04:28 -0700202 fprintf(stderr, "ERROR: -m expects a number, not '%s'.\n", optarg);
203 exit(EXIT_FAILURE);
204 }
205 if (display_max <= 0) {
206 fprintf(stderr, "LogDisplayer: invalid max log number '%s'\n",
207 optarg);
208 exit(EXIT_FAILURE);
209 }
brians343bc112013-02-10 01:53:46 +0000210 break;
211 case 'o':
212 abort();
213 break;
214 case 'h':
215 PrintHelpAndExit();
216 break;
217 case '?':
218 break;
219 default:
Brian Silvermanab6615c2013-03-05 20:29:29 -0800220 fprintf(stderr, "LogDisplayer: in a bad spot (%s: %d)\n",
221 __FILE__, __LINE__);
brians343bc112013-02-10 01:53:46 +0000222 abort();
223 }
224 }
225
Daniel Pettie6f33e22014-08-21 20:35:55 -0700226 if (optind < argc) {
227 // We got a filename.
228 filename = argv[optind++];
229 }
brians343bc112013-02-10 01:53:46 +0000230 if (optind < argc) {
Daniel Petti88c81f42014-09-12 10:05:05 -0700231 fputs("non-option ARGV-elements: ", stderr);
brians343bc112013-02-10 01:53:46 +0000232 while (optind < argc) {
233 fprintf(stderr, "%s\n", argv[optind++]);
234 }
235 }
236
Brian Silverman65e569d2014-10-24 15:43:20 -0400237 int fd;
238 if (strcmp(filename, "-") == 0) {
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500239 if (skip_to_end) {
240 fputs("Can't skip to end of stdin!\n", stderr);
241 return EXIT_FAILURE;
242 }
Brian Silverman65e569d2014-10-24 15:43:20 -0400243 fd = STDIN_FILENO;
244 } else {
245 fd = open(filename, O_RDONLY);
246 }
Daniel Pettie6f33e22014-08-21 20:35:55 -0700247
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500248 fprintf(stderr, "displaying down to level %s from file '%s'\n",
249 ::aos::logging::log_str(filter_level), filename);
250
brians343bc112013-02-10 01:53:46 +0000251 if (fd == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700252 PLOG(FATAL, "couldn't open file '%s' for reading", filename);
brians343bc112013-02-10 01:53:46 +0000253 }
Brian Silvermanab5ba472014-04-18 15:26:14 -0700254 ::aos::logging::linux_code::LogFileReader reader(fd);
Brian Silvermanf7780312014-02-16 17:26:15 -0800255
256 if (skip_to_end) {
257 fputs("skipping old logs...\n", stderr);
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500258 reader.SkipToLastSeekablePage();
brians343bc112013-02-10 01:53:46 +0000259 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800260
261 const LogFileMessageHeader *msg;
Daniel Pettib6c885b2014-09-12 10:04:28 -0700262 int displayed = 0;
brians343bc112013-02-10 01:53:46 +0000263 do {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700264 msg = reader.ReadNextMessage(follow);
Brian Silverman003ba4b2014-02-10 16:56:18 -0800265 if (msg == NULL) {
266 fputs("reached end of file\n", stderr);
267 return 0;
268 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800269
270 if (msg->type == LogFileMessageHeader::MessageType::kStructType) {
271 size_t bytes = msg->message_size;
272 ::aos::MessageType *type = ::aos::MessageType::Deserialize(
273 reinterpret_cast<const char *>(msg + 1), &bytes);
Brian Silvermanf4452d72014-10-25 17:23:11 -0400274 if (type == nullptr) {
Austin Schuh7e958392014-10-21 22:16:23 -0700275 LOG(INFO, "Trying old version of type decoding.\n");
276 bytes = msg->message_size;
277 type = ::aos::MessageType::Deserialize(
278 reinterpret_cast<const char *>(msg + 1), &bytes, false);
279 }
280
281 if (type == nullptr) {
Brian Silvermanf4452d72014-10-25 17:23:11 -0400282 LOG(WARNING, "Error deserializing MessageType of size %" PRIx32
283 " starting at %zx.\n",
284 msg->message_size, reader.file_offset(msg + 1));
285 } else {
286 ::aos::type_cache::Add(*type);
287 }
brians343bc112013-02-10 01:53:46 +0000288 continue;
289 }
Brian Silvermanf665d692013-02-17 22:11:39 -0800290
Brian Silverman64833d42015-02-08 21:11:36 -0500291 if (source_pid >= 0 && msg->source != source_pid) {
292 // Message is from the wrong process.
293 continue;
294 }
295
Brian Silvermanf7780312014-02-16 17:26:15 -0800296 if (skip_to_end) {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700297 if (reader.IsLastPage()) {
Brian Silvermanf7780312014-02-16 17:26:15 -0800298 fputs("done skipping old logs\n", stderr);
299 skip_to_end = false;
300 } else {
301 continue;
302 }
303 }
304
Brian Silverman88471dc2014-02-15 22:35:42 -0800305 if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500306
Brian Silverman58657aa2015-02-21 20:06:40 -0500307 const char *position =
308 reinterpret_cast<const char *>(msg + 1);
309
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500310 if (filter_name != nullptr) {
311 const size_t compare_length =
312 ::std::min<size_t>(filter_length, msg->name_size);
Brian Silverman58657aa2015-02-21 20:06:40 -0500313 if (memcmp(filter_name, position, compare_length) != 0) {
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500314 continue;
315 }
Brian Silverman58657aa2015-02-21 20:06:40 -0500316 if (compare_length < msg->name_size) {
317 if (position[compare_length] != '.') continue;
318 }
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500319 }
320
321 if (filter_exact_name != nullptr) {
322 if (filter_length != msg->name_size) continue;
Brian Silverman58657aa2015-02-21 20:06:40 -0500323 if (memcmp(filter_exact_name, position, filter_length) != 0) {
Brian Silverman88471dc2014-02-15 22:35:42 -0800324 continue;
325 }
326 }
327
Daniel Pettib6c885b2014-09-12 10:04:28 -0700328 if (display_max && displayed++ >= display_max) {
329 fputs("Not displaying the rest of the messages.\n", stderr);
330 return 0;
331 }
332
Brian Silverman58657aa2015-02-21 20:06:40 -0500333 position += msg->name_size;
334
Brian Silvermana7234c62014-03-24 20:23:25 -0700335#define BASE_ARGS \
336 AOS_LOGGING_BASE_ARGS( \
337 msg->name_size, reinterpret_cast<const char *>(msg + 1), msg->source, \
338 msg->sequence, msg->level, msg->time_sec, msg->time_nsec)
Brian Silverman88471dc2014-02-15 22:35:42 -0800339 switch (msg->type) {
Brian Silvermana7234c62014-03-24 20:23:25 -0700340 case LogFileMessageHeader::MessageType::kString:
341 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
342 static_cast<int>(msg->message_size), position);
Brian Silverman88471dc2014-02-15 22:35:42 -0800343 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700344 case LogFileMessageHeader::MessageType::kStruct: {
345 uint32_t type_id;
346 memcpy(&type_id, position, sizeof(type_id));
347 position += sizeof(type_id);
Brian Silverman664db1a2014-03-20 17:06:29 -0700348
Brian Silvermana7234c62014-03-24 20:23:25 -0700349 uint32_t string_length;
350 memcpy(&string_length, position, sizeof(string_length));
351 position += sizeof(string_length);
352
Austin Schuheee8c962017-04-12 22:32:44 -0700353 char buffer[4096];
Brian Silvermana7234c62014-03-24 20:23:25 -0700354 size_t output_length = sizeof(buffer);
355 size_t input_length =
356 msg->message_size -
357 (sizeof(type_id) + sizeof(uint32_t) + string_length);
358 if (!PrintMessage(buffer, &output_length, position + string_length,
359 &input_length, ::aos::type_cache::Get(type_id))) {
360 LOG(FATAL, "printing message (%.*s) of type %s into %zu-byte buffer "
361 "failed\n",
362 static_cast<int>(string_length), position,
363 ::aos::type_cache::Get(type_id).name.c_str(), sizeof(buffer));
364 }
365 if (input_length > 0) {
366 LOG(WARNING, "%zu extra bytes on message of type %s\n",
367 input_length, ::aos::type_cache::Get(type_id).name.c_str());
368 }
369 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
370 static_cast<int>(string_length), position,
371 static_cast<int>(sizeof(buffer) - output_length), buffer);
372 } break;
373 case LogFileMessageHeader::MessageType::kMatrix: {
374 uint32_t type;
375 memcpy(&type, position, sizeof(type));
376 position += sizeof(type);
377
378 uint32_t string_length;
379 memcpy(&string_length, position, sizeof(string_length));
380 position += sizeof(string_length);
Brian Silverman664db1a2014-03-20 17:06:29 -0700381
382 uint16_t rows;
383 memcpy(&rows, position, sizeof(rows));
Brian Silverman664db1a2014-03-20 17:06:29 -0700384 position += sizeof(rows);
385 uint16_t cols;
386 memcpy(&cols, position, sizeof(cols));
Brian Silverman664db1a2014-03-20 17:06:29 -0700387 position += sizeof(cols);
388
Brian Silvermana7234c62014-03-24 20:23:25 -0700389 const size_t matrix_bytes =
390 msg->message_size -
391 (sizeof(type) + sizeof(uint32_t) + sizeof(uint16_t) +
392 sizeof(uint16_t) + string_length);
393 CHECK_EQ(matrix_bytes, ::aos::MessageType::Sizeof(type) * rows * cols);
Austin Schuheee8c962017-04-12 22:32:44 -0700394 char buffer[4096];
Brian Silvermana7234c62014-03-24 20:23:25 -0700395 size_t output_length = sizeof(buffer);
396 if (!::aos::PrintMatrix(buffer, &output_length,
397 position + string_length, type, rows, cols)) {
398 LOG(FATAL, "printing %dx%d matrix of type %" PRIu32 " failed\n", rows,
399 cols, type);
400 }
401 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
402 static_cast<int>(string_length), position,
403 static_cast<int>(sizeof(buffer) - output_length), buffer);
404 } break;
Brian Silverman88471dc2014-02-15 22:35:42 -0800405 case LogFileMessageHeader::MessageType::kStructType:
406 LOG(FATAL, "shouldn't get here\n");
407 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700408 }
409#undef BASE_ARGS
brians343bc112013-02-10 01:53:46 +0000410 } while (msg != NULL);
411}