blob: 98e9914cf0d5a84ebbcf626c50128ac50e74c010 [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"
30 " -n, --name NAME only display entries from processes named NAME\n"
31 " -l, --level LEVEL "
32 "only display log entries at least as important as LEVEL\n"
Daniel Pettib6c885b2014-09-12 10:04:28 -070033 " -p, --pid PID only display log entries from process PID\n"
brians343bc112013-02-10 01:53:46 +000034 " -f, --follow "
35 "wait when the end of the file is reached (implies --end)\n"
36 " -t, --terminate stop when the end of file is reached (default)\n"
37 " -b, --beginning start at the beginning of the file (default)\n"
38 " -e, --end start at the end of the file\n"
39 " -s, --skip NUMBER skip NUMBER matching logs\n"
Daniel Pettib6c885b2014-09-12 10:04:28 -070040 " -m, --max NUMBER only display up to NUMBER logs\n"
brians343bc112013-02-10 01:53:46 +000041 " // -o, --format FORMAT use FORMAT to display log entries\n"
Brian Silvermana52ba162013-02-28 15:01:12 -080042 " -h, --help display this help and exit\n"
brians343bc112013-02-10 01:53:46 +000043 "\n"
44 "LEVEL must be DEBUG, INFO, WARNING, ERROR, or FATAL.\n"
45 " It defaults to INFO.\n"
46 "\n"
Daniel Pettie6f33e22014-08-21 20:35:55 -070047 "TODO(brians) implement the commented out ones.\n";
brians343bc112013-02-10 01:53:46 +000048
Daniel Petti88c81f42014-09-12 10:05:05 -070049const char *kExampleUsages = "To view logs from the shooter:\n"
50 "\t`log_displayer -n shooter`\n"
51 "To view debug logs from the shooter:\n"
52 "\t`log_displayer -n shooter -l DEBUG`\n"
53 "To view what the shooter is logging in realtime:\n"
54 "\t`log_displayer -f -n shooter`\n"
55 "To view shooter logs from an old log file:\n"
56 "\t`log_displayer aos_log-<number> -n shooter`\n"
57 "To view the statuses of the shooter hall effects in realtime:\n"
58 "\t`log_displayer -f -n shooter -l DEBUG | grep .Position`\n";
59
brians343bc112013-02-10 01:53:46 +000060void PrintHelpAndExit() {
61 fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
Daniel Petti88c81f42014-09-12 10:05:05 -070062 fprintf(stderr, "\nExample usages:\n\n%s", kExampleUsages);
63
64 // Get the possible executables from start_list.txt.
65 FILE *start_list = fopen("start_list.txt", "r");
Daniel Pettiaa5bdb12014-12-20 16:33:05 -080066 if (!start_list) {
67 ::std::string path(::aos::configuration::GetRootDirectory());
68 path += "/start_list.txt";
69 start_list = fopen(path.c_str(), "r");
70 if (!start_list) {
71 printf("\nCannot open start_list.txt. This means that the\n"
72 "possible arguments for the -n option cannot be shown. log_displayer\n"
73 "looks for start_list.txt in the current working directory and in\n"
74 "%s.\n\n", ::aos::configuration::GetRootDirectory());
75 PLOG(FATAL, "Unable to open start_list.txt");
76 }
Daniel Petti88c81f42014-09-12 10:05:05 -070077 }
78
79 // Get file size.
80 if (fseek(start_list, 0, SEEK_END)) {
81 PLOG(FATAL, "fseek() failed while reading start_list.txt");
82 }
83 int size = ftell(start_list);
84 if (size < 0) {
85 PLOG(FATAL, "ftell() failed while reading start_list.txt");
86 }
87 rewind(start_list);
88
89 ::std::unique_ptr<char[]> contents(new char[size + 1]);
90 if (contents == NULL) {
91 LOG(FATAL, "malloc() failed while reading start_list.txt.\n");
92 }
93 size_t bytes_read = fread(contents.get(), 1, size, start_list);
94 if (bytes_read < static_cast<size_t>(size)) {
95 LOG(FATAL, "Read %zu bytes from start_list.txt, expected %d.\n",
96 bytes_read, size);
97 }
98
99 // printf doesn't like strings without the \0.
100 contents[size] = '\0';
101 fprintf(stderr, "\nPossible arguments for the -n option:\n%s", contents.get());
102
103 if (fclose(start_list)) {
104 LOG(FATAL, "fclose() failed.\n");
105 }
brians343bc112013-02-10 01:53:46 +0000106
107 exit(EXIT_SUCCESS);
108}
109
110} // namespace
111
112int main(int argc, char **argv) {
113 const char *filter_name = NULL;
Brian Silvermanf7780312014-02-16 17:26:15 -0800114 size_t filter_length = 0;
brians343bc112013-02-10 01:53:46 +0000115 log_level filter_level = INFO;
Brian Silvermanf7780312014-02-16 17:26:15 -0800116 bool follow = false;
117 // Whether we need to skip everything until we get to the end of the file.
118 bool skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000119 const char *filename = "aos_log-current";
Daniel Pettib6c885b2014-09-12 10:04:28 -0700120 int display_max = 0;
121 int32_t source_pid = -1;
brians343bc112013-02-10 01:53:46 +0000122
Brian Silvermanff485782014-06-18 19:59:09 -0700123 ::aos::logging::Init();
Brian Silvermanf7780312014-02-16 17:26:15 -0800124 ::aos::logging::AddImplementation(
125 new ::aos::logging::StreamLogImplementation(stdout));
126
brians343bc112013-02-10 01:53:46 +0000127 while (true) {
128 static struct option long_options[] = {
129 {"name", required_argument, NULL, 'n'},
130 {"level", required_argument, NULL, 'l'},
131 {"pid", required_argument, NULL, 'p'},
132
133 {"follow", no_argument, NULL, 'f'},
134 {"terminate", no_argument, NULL, 't'},
135 {"beginning", no_argument, NULL, 'b'},
136 {"end", no_argument, NULL, 'e'},
137 {"skip", required_argument, NULL, 's'},
138 {"max", required_argument, NULL, 'm'},
139
140 {"format", required_argument, NULL, 'o'},
141
142 {"help", no_argument, NULL, 'h'},
143 {0, 0, 0, 0}
144 };
145 int option_index = 0;
146
147 const int c = getopt_long(argc, argv, "n:l:p:fts:m:o:h",
148 long_options, &option_index);
149 if (c == -1) { // if we're at the end
150 break;
151 }
152 switch (c) {
153 case 0:
Daniel Petti88c81f42014-09-12 10:05:05 -0700154 fputs("LogDisplayer: got a 0 option but didn't set up any\n", stderr);
brians343bc112013-02-10 01:53:46 +0000155 abort();
156 case 'n':
157 filter_name = optarg;
Brian Silverman88471dc2014-02-15 22:35:42 -0800158 filter_length = strlen(filter_name);
brians343bc112013-02-10 01:53:46 +0000159 break;
160 case 'l':
Brian Silvermanab6615c2013-03-05 20:29:29 -0800161 filter_level = ::aos::logging::str_log(optarg);
brians343bc112013-02-10 01:53:46 +0000162 if (filter_level == LOG_UNKNOWN) {
163 fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
164 exit(EXIT_FAILURE);
165 }
166 break;
167 case 'p':
Daniel Pettib6c885b2014-09-12 10:04:28 -0700168 if (!::aos::util::StringToInteger(::std::string(optarg), &source_pid)) {
169 fprintf(stderr, "ERROR: -p expects a number, not '%s'.\n", optarg);
170 exit(EXIT_FAILURE);
171 }
172 if (source_pid < 0) {
173 fprintf(stderr, "LogDisplayer: invalid pid '%s'\n", optarg);
174 exit(EXIT_FAILURE);
175 }
brians343bc112013-02-10 01:53:46 +0000176 break;
177 case 'f':
178 follow = true;
Brian Silvermanf7780312014-02-16 17:26:15 -0800179 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000180 break;
181 case 't':
182 follow = false;
183 break;
184 case 'b':
Brian Silvermanf7780312014-02-16 17:26:15 -0800185 skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000186 break;
187 case 'e':
Brian Silvermanf7780312014-02-16 17:26:15 -0800188 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000189 break;
190 case 'm':
Daniel Pettib6c885b2014-09-12 10:04:28 -0700191 if (!::aos::util::StringToInteger(::std::string(optarg), &display_max)) {
192 fprintf(stderr, "ERROR: -m expects a number, not '%s'.\n", optarg);
193 exit(EXIT_FAILURE);
194 }
195 if (display_max <= 0) {
196 fprintf(stderr, "LogDisplayer: invalid max log number '%s'\n",
197 optarg);
198 exit(EXIT_FAILURE);
199 }
brians343bc112013-02-10 01:53:46 +0000200 break;
201 case 'o':
202 abort();
203 break;
204 case 'h':
205 PrintHelpAndExit();
206 break;
207 case '?':
208 break;
209 default:
Brian Silvermanab6615c2013-03-05 20:29:29 -0800210 fprintf(stderr, "LogDisplayer: in a bad spot (%s: %d)\n",
211 __FILE__, __LINE__);
brians343bc112013-02-10 01:53:46 +0000212 abort();
213 }
214 }
215
Daniel Pettie6f33e22014-08-21 20:35:55 -0700216 if (optind < argc) {
217 // We got a filename.
218 filename = argv[optind++];
219 }
brians343bc112013-02-10 01:53:46 +0000220 if (optind < argc) {
Daniel Petti88c81f42014-09-12 10:05:05 -0700221 fputs("non-option ARGV-elements: ", stderr);
brians343bc112013-02-10 01:53:46 +0000222 while (optind < argc) {
223 fprintf(stderr, "%s\n", argv[optind++]);
224 }
225 }
226
Daniel Pettie6f33e22014-08-21 20:35:55 -0700227 fprintf(stderr, "displaying down to level %s from file '%s'\n",
228 ::aos::logging::log_str(filter_level), filename);
229
Brian Silverman65e569d2014-10-24 15:43:20 -0400230 int fd;
231 if (strcmp(filename, "-") == 0) {
232 fd = STDIN_FILENO;
233 } else {
234 fd = open(filename, O_RDONLY);
235 }
Daniel Pettie6f33e22014-08-21 20:35:55 -0700236
brians343bc112013-02-10 01:53:46 +0000237 if (fd == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700238 PLOG(FATAL, "couldn't open file '%s' for reading", filename);
brians343bc112013-02-10 01:53:46 +0000239 }
Brian Silvermanab5ba472014-04-18 15:26:14 -0700240 ::aos::logging::linux_code::LogFileReader reader(fd);
Brian Silvermanf7780312014-02-16 17:26:15 -0800241
242 if (skip_to_end) {
243 fputs("skipping old logs...\n", stderr);
brians343bc112013-02-10 01:53:46 +0000244 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800245
246 const LogFileMessageHeader *msg;
Daniel Pettib6c885b2014-09-12 10:04:28 -0700247 int displayed = 0;
brians343bc112013-02-10 01:53:46 +0000248 do {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700249 msg = reader.ReadNextMessage(follow);
Brian Silverman003ba4b2014-02-10 16:56:18 -0800250 if (msg == NULL) {
251 fputs("reached end of file\n", stderr);
252 return 0;
253 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800254
Daniel Pettib6c885b2014-09-12 10:04:28 -0700255 if (source_pid >= 0 && msg->source != source_pid) {
256 // Message is from the wrong process.
257 continue;
258 }
259
Brian Silverman88471dc2014-02-15 22:35:42 -0800260 if (msg->type == LogFileMessageHeader::MessageType::kStructType) {
261 size_t bytes = msg->message_size;
262 ::aos::MessageType *type = ::aos::MessageType::Deserialize(
263 reinterpret_cast<const char *>(msg + 1), &bytes);
Brian Silvermanf4452d72014-10-25 17:23:11 -0400264 if (type == nullptr) {
Austin Schuh7e958392014-10-21 22:16:23 -0700265 LOG(INFO, "Trying old version of type decoding.\n");
266 bytes = msg->message_size;
267 type = ::aos::MessageType::Deserialize(
268 reinterpret_cast<const char *>(msg + 1), &bytes, false);
269 }
270
271 if (type == nullptr) {
Brian Silvermanf4452d72014-10-25 17:23:11 -0400272 LOG(WARNING, "Error deserializing MessageType of size %" PRIx32
273 " starting at %zx.\n",
274 msg->message_size, reader.file_offset(msg + 1));
275 } else {
276 ::aos::type_cache::Add(*type);
277 }
brians343bc112013-02-10 01:53:46 +0000278 continue;
279 }
Brian Silvermanf665d692013-02-17 22:11:39 -0800280
Brian Silvermanf7780312014-02-16 17:26:15 -0800281 if (skip_to_end) {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700282 if (reader.IsLastPage()) {
Brian Silvermanf7780312014-02-16 17:26:15 -0800283 fputs("done skipping old logs\n", stderr);
284 skip_to_end = false;
285 } else {
286 continue;
287 }
288 }
289
Brian Silverman88471dc2014-02-15 22:35:42 -0800290 if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
291 if (filter_name != NULL) {
292 if (filter_length != msg->name_size) continue;
293 if (memcmp(filter_name,
294 reinterpret_cast<const char *>(msg) + sizeof(*msg),
295 filter_length) !=
296 0) {
297 continue;
298 }
299 }
300
Daniel Pettib6c885b2014-09-12 10:04:28 -0700301 if (display_max && displayed++ >= display_max) {
302 fputs("Not displaying the rest of the messages.\n", stderr);
303 return 0;
304 }
305
Brian Silvermana7234c62014-03-24 20:23:25 -0700306 const char *position =
307 reinterpret_cast<const char *>(msg + 1) + msg->name_size;
308#define BASE_ARGS \
309 AOS_LOGGING_BASE_ARGS( \
310 msg->name_size, reinterpret_cast<const char *>(msg + 1), msg->source, \
311 msg->sequence, msg->level, msg->time_sec, msg->time_nsec)
Brian Silverman88471dc2014-02-15 22:35:42 -0800312 switch (msg->type) {
Brian Silvermana7234c62014-03-24 20:23:25 -0700313 case LogFileMessageHeader::MessageType::kString:
314 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
315 static_cast<int>(msg->message_size), position);
Brian Silverman88471dc2014-02-15 22:35:42 -0800316 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700317 case LogFileMessageHeader::MessageType::kStruct: {
318 uint32_t type_id;
319 memcpy(&type_id, position, sizeof(type_id));
320 position += sizeof(type_id);
Brian Silverman664db1a2014-03-20 17:06:29 -0700321
Brian Silvermana7234c62014-03-24 20:23:25 -0700322 uint32_t string_length;
323 memcpy(&string_length, position, sizeof(string_length));
324 position += sizeof(string_length);
325
326 char buffer[2048];
327 size_t output_length = sizeof(buffer);
328 size_t input_length =
329 msg->message_size -
330 (sizeof(type_id) + sizeof(uint32_t) + string_length);
331 if (!PrintMessage(buffer, &output_length, position + string_length,
332 &input_length, ::aos::type_cache::Get(type_id))) {
333 LOG(FATAL, "printing message (%.*s) of type %s into %zu-byte buffer "
334 "failed\n",
335 static_cast<int>(string_length), position,
336 ::aos::type_cache::Get(type_id).name.c_str(), sizeof(buffer));
337 }
338 if (input_length > 0) {
339 LOG(WARNING, "%zu extra bytes on message of type %s\n",
340 input_length, ::aos::type_cache::Get(type_id).name.c_str());
341 }
342 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
343 static_cast<int>(string_length), position,
344 static_cast<int>(sizeof(buffer) - output_length), buffer);
345 } break;
346 case LogFileMessageHeader::MessageType::kMatrix: {
347 uint32_t type;
348 memcpy(&type, position, sizeof(type));
349 position += sizeof(type);
350
351 uint32_t string_length;
352 memcpy(&string_length, position, sizeof(string_length));
353 position += sizeof(string_length);
Brian Silverman664db1a2014-03-20 17:06:29 -0700354
355 uint16_t rows;
356 memcpy(&rows, position, sizeof(rows));
Brian Silverman664db1a2014-03-20 17:06:29 -0700357 position += sizeof(rows);
358 uint16_t cols;
359 memcpy(&cols, position, sizeof(cols));
Brian Silverman664db1a2014-03-20 17:06:29 -0700360 position += sizeof(cols);
361
Brian Silvermana7234c62014-03-24 20:23:25 -0700362 const size_t matrix_bytes =
363 msg->message_size -
364 (sizeof(type) + sizeof(uint32_t) + sizeof(uint16_t) +
365 sizeof(uint16_t) + string_length);
366 CHECK_EQ(matrix_bytes, ::aos::MessageType::Sizeof(type) * rows * cols);
367 char buffer[2048];
368 size_t output_length = sizeof(buffer);
369 if (!::aos::PrintMatrix(buffer, &output_length,
370 position + string_length, type, rows, cols)) {
371 LOG(FATAL, "printing %dx%d matrix of type %" PRIu32 " failed\n", rows,
372 cols, type);
373 }
374 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
375 static_cast<int>(string_length), position,
376 static_cast<int>(sizeof(buffer) - output_length), buffer);
377 } break;
Brian Silverman88471dc2014-02-15 22:35:42 -0800378 case LogFileMessageHeader::MessageType::kStructType:
379 LOG(FATAL, "shouldn't get here\n");
380 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700381 }
382#undef BASE_ARGS
brians343bc112013-02-10 01:53:46 +0000383 } while (msg != NULL);
384}