blob: cdc8f4602a86fa21b2bb37229592c0aa5f42053c [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"
Brian Silvermanfe9b7a22014-02-10 15:03:42 -080015#include "aos/linux_code/logging/binary_log_file.h"
Brian Silverman88471dc2014-02-15 22:35:42 -080016#include "aos/common/queue_types.h"
Brian Silvermana7234c62014-03-24 20:23:25 -070017#include "aos/common/logging/logging_impl.h"
18#include "aos/common/logging/logging_printf_formats.h"
Daniel Pettib6c885b2014-09-12 10:04:28 -070019#include "aos/common/util/string_to_num.h"
Brian Silverman88471dc2014-02-15 22:35:42 -080020
21using ::aos::logging::linux_code::LogFileMessageHeader;
brians343bc112013-02-10 01:53:46 +000022
23namespace {
24
25const char *kArgsHelp = "[OPTION]... [FILE]\n"
26 "Display log file FILE (created by BinaryLogReader) to stdout.\n"
27 "FILE is \"aos_log-current\" by default.\n"
Brian Silverman65e569d2014-10-24 15:43:20 -040028 "FILE can also be \"-\" to read from standard input.\n"
brians343bc112013-02-10 01:53:46 +000029 "\n"
Brian Silverman8c2b00e2015-02-17 20:30:50 -050030 " -n, --name-prefix NAME "
31 "only display entries from processes which share NAME as a prefix\n"
32 " -N, --name NAME only display entries from processes named NAME\n"
33 " -l, --level LEVEL "
brians343bc112013-02-10 01:53:46 +000034 "only display log entries at least as important as LEVEL\n"
Brian Silverman8c2b00e2015-02-17 20:30:50 -050035 " -p, --pid PID only display log entries from process PID\n"
36 " -f, --follow "
brians343bc112013-02-10 01:53:46 +000037 "wait when the end of the file is reached (implies --end)\n"
Brian Silverman8c2b00e2015-02-17 20:30:50 -050038 " -t, --terminate stop when the end of file is reached (default)\n"
39 " -b, --beginning start at the beginning of the file (default)\n"
40 " -e, --end start at the end of the file\n"
41 " -s, --skip NUMBER skip NUMBER matching logs\n"
42 " -m, --max NUMBER only display up to NUMBER logs\n"
brians343bc112013-02-10 01:53:46 +000043 " // -o, --format FORMAT use FORMAT to display log entries\n"
Brian Silverman8c2b00e2015-02-17 20:30:50 -050044 " -h, --help display this help and exit\n"
brians343bc112013-02-10 01:53:46 +000045 "\n"
46 "LEVEL must be DEBUG, INFO, WARNING, ERROR, or FATAL.\n"
47 " It defaults to INFO.\n"
48 "\n"
Daniel Pettie6f33e22014-08-21 20:35:55 -070049 "TODO(brians) implement the commented out ones.\n";
brians343bc112013-02-10 01:53:46 +000050
Daniel Petti88c81f42014-09-12 10:05:05 -070051const char *kExampleUsages = "To view logs from the shooter:\n"
52 "\t`log_displayer -n shooter`\n"
53 "To view debug logs from the shooter:\n"
54 "\t`log_displayer -n shooter -l DEBUG`\n"
55 "To view what the shooter is logging in realtime:\n"
56 "\t`log_displayer -f -n shooter`\n"
57 "To view shooter logs from an old log file:\n"
58 "\t`log_displayer aos_log-<number> -n shooter`\n"
59 "To view the statuses of the shooter hall effects in realtime:\n"
60 "\t`log_displayer -f -n shooter -l DEBUG | grep .Position`\n";
61
brians343bc112013-02-10 01:53:46 +000062void PrintHelpAndExit() {
63 fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
Daniel Petti88c81f42014-09-12 10:05:05 -070064 fprintf(stderr, "\nExample usages:\n\n%s", kExampleUsages);
65
66 // Get the possible executables from start_list.txt.
67 FILE *start_list = fopen("start_list.txt", "r");
Daniel Pettiaa5bdb12014-12-20 16:33:05 -080068 if (!start_list) {
69 ::std::string path(::aos::configuration::GetRootDirectory());
70 path += "/start_list.txt";
71 start_list = fopen(path.c_str(), "r");
72 if (!start_list) {
73 printf("\nCannot open start_list.txt. This means that the\n"
74 "possible arguments for the -n option cannot be shown. log_displayer\n"
75 "looks for start_list.txt in the current working directory and in\n"
76 "%s.\n\n", ::aos::configuration::GetRootDirectory());
77 PLOG(FATAL, "Unable to open start_list.txt");
78 }
Daniel Petti88c81f42014-09-12 10:05:05 -070079 }
80
81 // Get file size.
82 if (fseek(start_list, 0, SEEK_END)) {
83 PLOG(FATAL, "fseek() failed while reading start_list.txt");
84 }
85 int size = ftell(start_list);
86 if (size < 0) {
87 PLOG(FATAL, "ftell() failed while reading start_list.txt");
88 }
89 rewind(start_list);
90
91 ::std::unique_ptr<char[]> contents(new char[size + 1]);
92 if (contents == NULL) {
93 LOG(FATAL, "malloc() failed while reading start_list.txt.\n");
94 }
95 size_t bytes_read = fread(contents.get(), 1, size, start_list);
96 if (bytes_read < static_cast<size_t>(size)) {
97 LOG(FATAL, "Read %zu bytes from start_list.txt, expected %d.\n",
98 bytes_read, size);
99 }
100
101 // printf doesn't like strings without the \0.
102 contents[size] = '\0';
103 fprintf(stderr, "\nPossible arguments for the -n option:\n%s", contents.get());
104
105 if (fclose(start_list)) {
106 LOG(FATAL, "fclose() failed.\n");
107 }
brians343bc112013-02-10 01:53:46 +0000108
109 exit(EXIT_SUCCESS);
110}
111
112} // namespace
113
114int main(int argc, char **argv) {
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500115 const char *filter_name = nullptr, *filter_exact_name = nullptr;
Brian Silvermanf7780312014-02-16 17:26:15 -0800116 size_t filter_length = 0;
brians343bc112013-02-10 01:53:46 +0000117 log_level filter_level = INFO;
Brian Silvermanf7780312014-02-16 17:26:15 -0800118 bool follow = false;
119 // Whether we need to skip everything until we get to the end of the file.
120 bool skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000121 const char *filename = "aos_log-current";
Daniel Pettib6c885b2014-09-12 10:04:28 -0700122 int display_max = 0;
123 int32_t source_pid = -1;
brians343bc112013-02-10 01:53:46 +0000124
Brian Silvermanff485782014-06-18 19:59:09 -0700125 ::aos::logging::Init();
Brian Silvermanf7780312014-02-16 17:26:15 -0800126 ::aos::logging::AddImplementation(
127 new ::aos::logging::StreamLogImplementation(stdout));
128
brians343bc112013-02-10 01:53:46 +0000129 while (true) {
130 static struct option long_options[] = {
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500131 {"name-prefix", required_argument, NULL, 'n'},
132 {"name", required_argument, NULL, 'N'},
brians343bc112013-02-10 01:53:46 +0000133 {"level", required_argument, NULL, 'l'},
134 {"pid", required_argument, NULL, 'p'},
135
136 {"follow", no_argument, NULL, 'f'},
137 {"terminate", no_argument, NULL, 't'},
138 {"beginning", no_argument, NULL, 'b'},
139 {"end", no_argument, NULL, 'e'},
140 {"skip", required_argument, NULL, 's'},
141 {"max", required_argument, NULL, 'm'},
142
143 {"format", required_argument, NULL, 'o'},
144
145 {"help", no_argument, NULL, 'h'},
146 {0, 0, 0, 0}
147 };
148 int option_index = 0;
149
150 const int c = getopt_long(argc, argv, "n:l:p:fts:m:o:h",
151 long_options, &option_index);
152 if (c == -1) { // if we're at the end
153 break;
154 }
155 switch (c) {
156 case 0:
Daniel Petti88c81f42014-09-12 10:05:05 -0700157 fputs("LogDisplayer: got a 0 option but didn't set up any\n", stderr);
brians343bc112013-02-10 01:53:46 +0000158 abort();
159 case 'n':
160 filter_name = optarg;
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500161 filter_exact_name = nullptr;
162 filter_length = strlen(filter_name);
163 break;
164 case 'N':
165 filter_exact_name = optarg;
166 filter_name = nullptr;
Brian Silverman88471dc2014-02-15 22:35:42 -0800167 filter_length = strlen(filter_name);
brians343bc112013-02-10 01:53:46 +0000168 break;
169 case 'l':
Brian Silvermanab6615c2013-03-05 20:29:29 -0800170 filter_level = ::aos::logging::str_log(optarg);
brians343bc112013-02-10 01:53:46 +0000171 if (filter_level == LOG_UNKNOWN) {
172 fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
173 exit(EXIT_FAILURE);
174 }
175 break;
176 case 'p':
Daniel Petti5aa29792014-12-27 17:48:07 -0500177 if (!::aos::util::StringToNumber(::std::string(optarg), &source_pid)) {
Daniel Pettib6c885b2014-09-12 10:04:28 -0700178 fprintf(stderr, "ERROR: -p expects a number, not '%s'.\n", optarg);
179 exit(EXIT_FAILURE);
180 }
181 if (source_pid < 0) {
182 fprintf(stderr, "LogDisplayer: invalid pid '%s'\n", optarg);
183 exit(EXIT_FAILURE);
184 }
brians343bc112013-02-10 01:53:46 +0000185 break;
186 case 'f':
187 follow = true;
Brian Silvermanf7780312014-02-16 17:26:15 -0800188 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000189 break;
190 case 't':
191 follow = false;
192 break;
193 case 'b':
Brian Silvermanf7780312014-02-16 17:26:15 -0800194 skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000195 break;
196 case 'e':
Brian Silvermanf7780312014-02-16 17:26:15 -0800197 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000198 break;
199 case 'm':
Daniel Petti5aa29792014-12-27 17:48:07 -0500200 if (!::aos::util::StringToNumber(::std::string(optarg), &display_max)) {
Daniel Pettib6c885b2014-09-12 10:04:28 -0700201 fprintf(stderr, "ERROR: -m expects a number, not '%s'.\n", optarg);
202 exit(EXIT_FAILURE);
203 }
204 if (display_max <= 0) {
205 fprintf(stderr, "LogDisplayer: invalid max log number '%s'\n",
206 optarg);
207 exit(EXIT_FAILURE);
208 }
brians343bc112013-02-10 01:53:46 +0000209 break;
210 case 'o':
211 abort();
212 break;
213 case 'h':
214 PrintHelpAndExit();
215 break;
216 case '?':
217 break;
218 default:
Brian Silvermanab6615c2013-03-05 20:29:29 -0800219 fprintf(stderr, "LogDisplayer: in a bad spot (%s: %d)\n",
220 __FILE__, __LINE__);
brians343bc112013-02-10 01:53:46 +0000221 abort();
222 }
223 }
224
Daniel Pettie6f33e22014-08-21 20:35:55 -0700225 if (optind < argc) {
226 // We got a filename.
227 filename = argv[optind++];
228 }
brians343bc112013-02-10 01:53:46 +0000229 if (optind < argc) {
Daniel Petti88c81f42014-09-12 10:05:05 -0700230 fputs("non-option ARGV-elements: ", stderr);
brians343bc112013-02-10 01:53:46 +0000231 while (optind < argc) {
232 fprintf(stderr, "%s\n", argv[optind++]);
233 }
234 }
235
Brian Silverman65e569d2014-10-24 15:43:20 -0400236 int fd;
237 if (strcmp(filename, "-") == 0) {
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500238 if (skip_to_end) {
239 fputs("Can't skip to end of stdin!\n", stderr);
240 return EXIT_FAILURE;
241 }
Brian Silverman65e569d2014-10-24 15:43:20 -0400242 fd = STDIN_FILENO;
243 } else {
244 fd = open(filename, O_RDONLY);
245 }
Daniel Pettie6f33e22014-08-21 20:35:55 -0700246
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500247 fprintf(stderr, "displaying down to level %s from file '%s'\n",
248 ::aos::logging::log_str(filter_level), filename);
249
brians343bc112013-02-10 01:53:46 +0000250 if (fd == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700251 PLOG(FATAL, "couldn't open file '%s' for reading", filename);
brians343bc112013-02-10 01:53:46 +0000252 }
Brian Silvermanab5ba472014-04-18 15:26:14 -0700253 ::aos::logging::linux_code::LogFileReader reader(fd);
Brian Silvermanf7780312014-02-16 17:26:15 -0800254
255 if (skip_to_end) {
256 fputs("skipping old logs...\n", stderr);
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500257 reader.SkipToLastSeekablePage();
brians343bc112013-02-10 01:53:46 +0000258 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800259
260 const LogFileMessageHeader *msg;
Daniel Pettib6c885b2014-09-12 10:04:28 -0700261 int displayed = 0;
brians343bc112013-02-10 01:53:46 +0000262 do {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700263 msg = reader.ReadNextMessage(follow);
Brian Silverman003ba4b2014-02-10 16:56:18 -0800264 if (msg == NULL) {
265 fputs("reached end of file\n", stderr);
266 return 0;
267 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800268
269 if (msg->type == LogFileMessageHeader::MessageType::kStructType) {
270 size_t bytes = msg->message_size;
271 ::aos::MessageType *type = ::aos::MessageType::Deserialize(
272 reinterpret_cast<const char *>(msg + 1), &bytes);
Brian Silvermanf4452d72014-10-25 17:23:11 -0400273 if (type == nullptr) {
Austin Schuh7e958392014-10-21 22:16:23 -0700274 LOG(INFO, "Trying old version of type decoding.\n");
275 bytes = msg->message_size;
276 type = ::aos::MessageType::Deserialize(
277 reinterpret_cast<const char *>(msg + 1), &bytes, false);
278 }
279
280 if (type == nullptr) {
Brian Silvermanf4452d72014-10-25 17:23:11 -0400281 LOG(WARNING, "Error deserializing MessageType of size %" PRIx32
282 " starting at %zx.\n",
283 msg->message_size, reader.file_offset(msg + 1));
284 } else {
285 ::aos::type_cache::Add(*type);
286 }
brians343bc112013-02-10 01:53:46 +0000287 continue;
288 }
Brian Silvermanf665d692013-02-17 22:11:39 -0800289
Brian Silverman64833d42015-02-08 21:11:36 -0500290 if (source_pid >= 0 && msg->source != source_pid) {
291 // Message is from the wrong process.
292 continue;
293 }
294
Brian Silvermanf7780312014-02-16 17:26:15 -0800295 if (skip_to_end) {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700296 if (reader.IsLastPage()) {
Brian Silvermanf7780312014-02-16 17:26:15 -0800297 fputs("done skipping old logs\n", stderr);
298 skip_to_end = false;
299 } else {
300 continue;
301 }
302 }
303
Brian Silverman88471dc2014-02-15 22:35:42 -0800304 if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500305
Brian Silverman58657aa2015-02-21 20:06:40 -0500306 const char *position =
307 reinterpret_cast<const char *>(msg + 1);
308
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500309 if (filter_name != nullptr) {
310 const size_t compare_length =
311 ::std::min<size_t>(filter_length, msg->name_size);
Brian Silverman58657aa2015-02-21 20:06:40 -0500312 if (memcmp(filter_name, position, compare_length) != 0) {
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500313 continue;
314 }
Brian Silverman58657aa2015-02-21 20:06:40 -0500315 if (compare_length < msg->name_size) {
316 if (position[compare_length] != '.') continue;
317 }
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500318 }
319
320 if (filter_exact_name != nullptr) {
321 if (filter_length != msg->name_size) continue;
Brian Silverman58657aa2015-02-21 20:06:40 -0500322 if (memcmp(filter_exact_name, position, filter_length) != 0) {
Brian Silverman88471dc2014-02-15 22:35:42 -0800323 continue;
324 }
325 }
326
Daniel Pettib6c885b2014-09-12 10:04:28 -0700327 if (display_max && displayed++ >= display_max) {
328 fputs("Not displaying the rest of the messages.\n", stderr);
329 return 0;
330 }
331
Brian Silverman58657aa2015-02-21 20:06:40 -0500332 position += msg->name_size;
333
Brian Silvermana7234c62014-03-24 20:23:25 -0700334#define BASE_ARGS \
335 AOS_LOGGING_BASE_ARGS( \
336 msg->name_size, reinterpret_cast<const char *>(msg + 1), msg->source, \
337 msg->sequence, msg->level, msg->time_sec, msg->time_nsec)
Brian Silverman88471dc2014-02-15 22:35:42 -0800338 switch (msg->type) {
Brian Silvermana7234c62014-03-24 20:23:25 -0700339 case LogFileMessageHeader::MessageType::kString:
340 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
341 static_cast<int>(msg->message_size), position);
Brian Silverman88471dc2014-02-15 22:35:42 -0800342 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700343 case LogFileMessageHeader::MessageType::kStruct: {
344 uint32_t type_id;
345 memcpy(&type_id, position, sizeof(type_id));
346 position += sizeof(type_id);
Brian Silverman664db1a2014-03-20 17:06:29 -0700347
Brian Silvermana7234c62014-03-24 20:23:25 -0700348 uint32_t string_length;
349 memcpy(&string_length, position, sizeof(string_length));
350 position += sizeof(string_length);
351
352 char buffer[2048];
353 size_t output_length = sizeof(buffer);
354 size_t input_length =
355 msg->message_size -
356 (sizeof(type_id) + sizeof(uint32_t) + string_length);
357 if (!PrintMessage(buffer, &output_length, position + string_length,
358 &input_length, ::aos::type_cache::Get(type_id))) {
359 LOG(FATAL, "printing message (%.*s) of type %s into %zu-byte buffer "
360 "failed\n",
361 static_cast<int>(string_length), position,
362 ::aos::type_cache::Get(type_id).name.c_str(), sizeof(buffer));
363 }
364 if (input_length > 0) {
365 LOG(WARNING, "%zu extra bytes on message of type %s\n",
366 input_length, ::aos::type_cache::Get(type_id).name.c_str());
367 }
368 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
369 static_cast<int>(string_length), position,
370 static_cast<int>(sizeof(buffer) - output_length), buffer);
371 } break;
372 case LogFileMessageHeader::MessageType::kMatrix: {
373 uint32_t type;
374 memcpy(&type, position, sizeof(type));
375 position += sizeof(type);
376
377 uint32_t string_length;
378 memcpy(&string_length, position, sizeof(string_length));
379 position += sizeof(string_length);
Brian Silverman664db1a2014-03-20 17:06:29 -0700380
381 uint16_t rows;
382 memcpy(&rows, position, sizeof(rows));
Brian Silverman664db1a2014-03-20 17:06:29 -0700383 position += sizeof(rows);
384 uint16_t cols;
385 memcpy(&cols, position, sizeof(cols));
Brian Silverman664db1a2014-03-20 17:06:29 -0700386 position += sizeof(cols);
387
Brian Silvermana7234c62014-03-24 20:23:25 -0700388 const size_t matrix_bytes =
389 msg->message_size -
390 (sizeof(type) + sizeof(uint32_t) + sizeof(uint16_t) +
391 sizeof(uint16_t) + string_length);
392 CHECK_EQ(matrix_bytes, ::aos::MessageType::Sizeof(type) * rows * cols);
393 char buffer[2048];
394 size_t output_length = sizeof(buffer);
395 if (!::aos::PrintMatrix(buffer, &output_length,
396 position + string_length, type, rows, cols)) {
397 LOG(FATAL, "printing %dx%d matrix of type %" PRIu32 " failed\n", rows,
398 cols, type);
399 }
400 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
401 static_cast<int>(string_length), position,
402 static_cast<int>(sizeof(buffer) - output_length), buffer);
403 } break;
Brian Silverman88471dc2014-02-15 22:35:42 -0800404 case LogFileMessageHeader::MessageType::kStructType:
405 LOG(FATAL, "shouldn't get here\n");
406 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700407 }
408#undef BASE_ARGS
brians343bc112013-02-10 01:53:46 +0000409 } while (msg != NULL);
410}