blob: b2f4d45c501094ee6ce77bd7ba8eb17d5407b480 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include <stdio.h>
2#include <stdlib.h>
3#include <getopt.h>
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <fcntl.h>
7#include <inttypes.h>
brians343bc112013-02-10 01:53:46 +00008
Brian Silverman88471dc2014-02-15 22:35:42 -08009#include <algorithm>
Daniel Pettib6c885b2014-09-12 10:04:28 -070010#include <memory>
11#include <string>
Brian Silverman88471dc2014-02-15 22:35:42 -080012
Brian Silvermanfe9b7a22014-02-10 15:03:42 -080013#include "aos/linux_code/logging/binary_log_file.h"
Brian Silverman88471dc2014-02-15 22:35:42 -080014#include "aos/common/queue_types.h"
Brian Silvermana7234c62014-03-24 20:23:25 -070015#include "aos/common/logging/logging_impl.h"
16#include "aos/common/logging/logging_printf_formats.h"
Daniel Pettib6c885b2014-09-12 10:04:28 -070017#include "aos/common/util/string_to_num.h"
Brian Silverman88471dc2014-02-15 22:35:42 -080018
19using ::aos::logging::linux_code::LogFileMessageHeader;
brians343bc112013-02-10 01:53:46 +000020
21namespace {
22
23const char *kArgsHelp = "[OPTION]... [FILE]\n"
24 "Display log file FILE (created by BinaryLogReader) to stdout.\n"
25 "FILE is \"aos_log-current\" by default.\n"
Brian Silverman65e569d2014-10-24 15:43:20 -040026 "FILE can also be \"-\" to read from standard input.\n"
brians343bc112013-02-10 01:53:46 +000027 "\n"
28 " -n, --name NAME only display entries from processes named NAME\n"
29 " -l, --level LEVEL "
30 "only display log entries at least as important as LEVEL\n"
Daniel Pettib6c885b2014-09-12 10:04:28 -070031 " -p, --pid PID only display log entries from process PID\n"
brians343bc112013-02-10 01:53:46 +000032 " -f, --follow "
33 "wait when the end of the file is reached (implies --end)\n"
34 " -t, --terminate stop when the end of file is reached (default)\n"
35 " -b, --beginning start at the beginning of the file (default)\n"
36 " -e, --end start at the end of the file\n"
37 " -s, --skip NUMBER skip NUMBER matching logs\n"
Daniel Pettib6c885b2014-09-12 10:04:28 -070038 " -m, --max NUMBER only display up to NUMBER logs\n"
brians343bc112013-02-10 01:53:46 +000039 " // -o, --format FORMAT use FORMAT to display log entries\n"
Brian Silvermana52ba162013-02-28 15:01:12 -080040 " -h, --help display this help and exit\n"
brians343bc112013-02-10 01:53:46 +000041 "\n"
42 "LEVEL must be DEBUG, INFO, WARNING, ERROR, or FATAL.\n"
43 " It defaults to INFO.\n"
44 "\n"
Daniel Pettie6f33e22014-08-21 20:35:55 -070045 "TODO(brians) implement the commented out ones.\n";
brians343bc112013-02-10 01:53:46 +000046
Daniel Petti88c81f42014-09-12 10:05:05 -070047const char *kExampleUsages = "To view logs from the shooter:\n"
48 "\t`log_displayer -n shooter`\n"
49 "To view debug logs from the shooter:\n"
50 "\t`log_displayer -n shooter -l DEBUG`\n"
51 "To view what the shooter is logging in realtime:\n"
52 "\t`log_displayer -f -n shooter`\n"
53 "To view shooter logs from an old log file:\n"
54 "\t`log_displayer aos_log-<number> -n shooter`\n"
55 "To view the statuses of the shooter hall effects in realtime:\n"
56 "\t`log_displayer -f -n shooter -l DEBUG | grep .Position`\n";
57
brians343bc112013-02-10 01:53:46 +000058void PrintHelpAndExit() {
59 fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
Daniel Petti88c81f42014-09-12 10:05:05 -070060 fprintf(stderr, "\nExample usages:\n\n%s", kExampleUsages);
61
62 // Get the possible executables from start_list.txt.
63 FILE *start_list = fopen("start_list.txt", "r");
64 if (start_list == NULL) {
65 PLOG(FATAL, "Unable to open start_list.txt");
66 }
67
68 // Get file size.
69 if (fseek(start_list, 0, SEEK_END)) {
70 PLOG(FATAL, "fseek() failed while reading start_list.txt");
71 }
72 int size = ftell(start_list);
73 if (size < 0) {
74 PLOG(FATAL, "ftell() failed while reading start_list.txt");
75 }
76 rewind(start_list);
77
78 ::std::unique_ptr<char[]> contents(new char[size + 1]);
79 if (contents == NULL) {
80 LOG(FATAL, "malloc() failed while reading start_list.txt.\n");
81 }
82 size_t bytes_read = fread(contents.get(), 1, size, start_list);
83 if (bytes_read < static_cast<size_t>(size)) {
84 LOG(FATAL, "Read %zu bytes from start_list.txt, expected %d.\n",
85 bytes_read, size);
86 }
87
88 // printf doesn't like strings without the \0.
89 contents[size] = '\0';
90 fprintf(stderr, "\nPossible arguments for the -n option:\n%s", contents.get());
91
92 if (fclose(start_list)) {
93 LOG(FATAL, "fclose() failed.\n");
94 }
brians343bc112013-02-10 01:53:46 +000095
96 exit(EXIT_SUCCESS);
97}
98
99} // namespace
100
101int main(int argc, char **argv) {
102 const char *filter_name = NULL;
Brian Silvermanf7780312014-02-16 17:26:15 -0800103 size_t filter_length = 0;
brians343bc112013-02-10 01:53:46 +0000104 log_level filter_level = INFO;
Brian Silvermanf7780312014-02-16 17:26:15 -0800105 bool follow = false;
106 // Whether we need to skip everything until we get to the end of the file.
107 bool skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000108 const char *filename = "aos_log-current";
Daniel Pettib6c885b2014-09-12 10:04:28 -0700109 int display_max = 0;
110 int32_t source_pid = -1;
brians343bc112013-02-10 01:53:46 +0000111
Brian Silvermanff485782014-06-18 19:59:09 -0700112 ::aos::logging::Init();
Brian Silvermanf7780312014-02-16 17:26:15 -0800113 ::aos::logging::AddImplementation(
114 new ::aos::logging::StreamLogImplementation(stdout));
115
brians343bc112013-02-10 01:53:46 +0000116 while (true) {
117 static struct option long_options[] = {
118 {"name", required_argument, NULL, 'n'},
119 {"level", required_argument, NULL, 'l'},
120 {"pid", required_argument, NULL, 'p'},
121
122 {"follow", no_argument, NULL, 'f'},
123 {"terminate", no_argument, NULL, 't'},
124 {"beginning", no_argument, NULL, 'b'},
125 {"end", no_argument, NULL, 'e'},
126 {"skip", required_argument, NULL, 's'},
127 {"max", required_argument, NULL, 'm'},
128
129 {"format", required_argument, NULL, 'o'},
130
131 {"help", no_argument, NULL, 'h'},
132 {0, 0, 0, 0}
133 };
134 int option_index = 0;
135
136 const int c = getopt_long(argc, argv, "n:l:p:fts:m:o:h",
137 long_options, &option_index);
138 if (c == -1) { // if we're at the end
139 break;
140 }
141 switch (c) {
142 case 0:
Daniel Petti88c81f42014-09-12 10:05:05 -0700143 fputs("LogDisplayer: got a 0 option but didn't set up any\n", stderr);
brians343bc112013-02-10 01:53:46 +0000144 abort();
145 case 'n':
146 filter_name = optarg;
Brian Silverman88471dc2014-02-15 22:35:42 -0800147 filter_length = strlen(filter_name);
brians343bc112013-02-10 01:53:46 +0000148 break;
149 case 'l':
Brian Silvermanab6615c2013-03-05 20:29:29 -0800150 filter_level = ::aos::logging::str_log(optarg);
brians343bc112013-02-10 01:53:46 +0000151 if (filter_level == LOG_UNKNOWN) {
152 fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
153 exit(EXIT_FAILURE);
154 }
155 break;
156 case 'p':
Daniel Petti5aa29792014-12-27 17:48:07 -0500157 if (!::aos::util::StringToNumber(::std::string(optarg), &source_pid)) {
Daniel Pettib6c885b2014-09-12 10:04:28 -0700158 fprintf(stderr, "ERROR: -p expects a number, not '%s'.\n", optarg);
159 exit(EXIT_FAILURE);
160 }
161 if (source_pid < 0) {
162 fprintf(stderr, "LogDisplayer: invalid pid '%s'\n", optarg);
163 exit(EXIT_FAILURE);
164 }
brians343bc112013-02-10 01:53:46 +0000165 break;
166 case 'f':
167 follow = true;
Brian Silvermanf7780312014-02-16 17:26:15 -0800168 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000169 break;
170 case 't':
171 follow = false;
172 break;
173 case 'b':
Brian Silvermanf7780312014-02-16 17:26:15 -0800174 skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000175 break;
176 case 'e':
Brian Silvermanf7780312014-02-16 17:26:15 -0800177 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000178 break;
179 case 'm':
Daniel Petti5aa29792014-12-27 17:48:07 -0500180 if (!::aos::util::StringToNumber(::std::string(optarg), &display_max)) {
Daniel Pettib6c885b2014-09-12 10:04:28 -0700181 fprintf(stderr, "ERROR: -m expects a number, not '%s'.\n", optarg);
182 exit(EXIT_FAILURE);
183 }
184 if (display_max <= 0) {
185 fprintf(stderr, "LogDisplayer: invalid max log number '%s'\n",
186 optarg);
187 exit(EXIT_FAILURE);
188 }
brians343bc112013-02-10 01:53:46 +0000189 break;
190 case 'o':
191 abort();
192 break;
193 case 'h':
194 PrintHelpAndExit();
195 break;
196 case '?':
197 break;
198 default:
Brian Silvermanab6615c2013-03-05 20:29:29 -0800199 fprintf(stderr, "LogDisplayer: in a bad spot (%s: %d)\n",
200 __FILE__, __LINE__);
brians343bc112013-02-10 01:53:46 +0000201 abort();
202 }
203 }
204
Daniel Pettie6f33e22014-08-21 20:35:55 -0700205 if (optind < argc) {
206 // We got a filename.
207 filename = argv[optind++];
208 }
brians343bc112013-02-10 01:53:46 +0000209 if (optind < argc) {
Daniel Petti88c81f42014-09-12 10:05:05 -0700210 fputs("non-option ARGV-elements: ", stderr);
brians343bc112013-02-10 01:53:46 +0000211 while (optind < argc) {
212 fprintf(stderr, "%s\n", argv[optind++]);
213 }
214 }
215
Daniel Pettie6f33e22014-08-21 20:35:55 -0700216 fprintf(stderr, "displaying down to level %s from file '%s'\n",
217 ::aos::logging::log_str(filter_level), filename);
218
Brian Silverman65e569d2014-10-24 15:43:20 -0400219 int fd;
220 if (strcmp(filename, "-") == 0) {
221 fd = STDIN_FILENO;
222 } else {
223 fd = open(filename, O_RDONLY);
224 }
Daniel Pettie6f33e22014-08-21 20:35:55 -0700225
brians343bc112013-02-10 01:53:46 +0000226 if (fd == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700227 PLOG(FATAL, "couldn't open file '%s' for reading", filename);
brians343bc112013-02-10 01:53:46 +0000228 }
Brian Silvermanab5ba472014-04-18 15:26:14 -0700229 ::aos::logging::linux_code::LogFileReader reader(fd);
Brian Silvermanf7780312014-02-16 17:26:15 -0800230
231 if (skip_to_end) {
232 fputs("skipping old logs...\n", stderr);
brians343bc112013-02-10 01:53:46 +0000233 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800234
235 const LogFileMessageHeader *msg;
Daniel Pettib6c885b2014-09-12 10:04:28 -0700236 int displayed = 0;
brians343bc112013-02-10 01:53:46 +0000237 do {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700238 msg = reader.ReadNextMessage(follow);
Brian Silverman003ba4b2014-02-10 16:56:18 -0800239 if (msg == NULL) {
240 fputs("reached end of file\n", stderr);
241 return 0;
242 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800243
Daniel Pettib6c885b2014-09-12 10:04:28 -0700244 if (source_pid >= 0 && msg->source != source_pid) {
245 // Message is from the wrong process.
246 continue;
247 }
248
Brian Silverman88471dc2014-02-15 22:35:42 -0800249 if (msg->type == LogFileMessageHeader::MessageType::kStructType) {
250 size_t bytes = msg->message_size;
251 ::aos::MessageType *type = ::aos::MessageType::Deserialize(
252 reinterpret_cast<const char *>(msg + 1), &bytes);
Brian Silvermanf4452d72014-10-25 17:23:11 -0400253 if (type == nullptr) {
Austin Schuh7e958392014-10-21 22:16:23 -0700254 LOG(INFO, "Trying old version of type decoding.\n");
255 bytes = msg->message_size;
256 type = ::aos::MessageType::Deserialize(
257 reinterpret_cast<const char *>(msg + 1), &bytes, false);
258 }
259
260 if (type == nullptr) {
Brian Silvermanf4452d72014-10-25 17:23:11 -0400261 LOG(WARNING, "Error deserializing MessageType of size %" PRIx32
262 " starting at %zx.\n",
263 msg->message_size, reader.file_offset(msg + 1));
264 } else {
265 ::aos::type_cache::Add(*type);
266 }
brians343bc112013-02-10 01:53:46 +0000267 continue;
268 }
Brian Silvermanf665d692013-02-17 22:11:39 -0800269
Brian Silvermanf7780312014-02-16 17:26:15 -0800270 if (skip_to_end) {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700271 if (reader.IsLastPage()) {
Brian Silvermanf7780312014-02-16 17:26:15 -0800272 fputs("done skipping old logs\n", stderr);
273 skip_to_end = false;
274 } else {
275 continue;
276 }
277 }
278
Brian Silverman88471dc2014-02-15 22:35:42 -0800279 if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
280 if (filter_name != NULL) {
281 if (filter_length != msg->name_size) continue;
282 if (memcmp(filter_name,
283 reinterpret_cast<const char *>(msg) + sizeof(*msg),
284 filter_length) !=
285 0) {
286 continue;
287 }
288 }
289
Daniel Pettib6c885b2014-09-12 10:04:28 -0700290 if (display_max && displayed++ >= display_max) {
291 fputs("Not displaying the rest of the messages.\n", stderr);
292 return 0;
293 }
294
Brian Silvermana7234c62014-03-24 20:23:25 -0700295 const char *position =
296 reinterpret_cast<const char *>(msg + 1) + msg->name_size;
297#define BASE_ARGS \
298 AOS_LOGGING_BASE_ARGS( \
299 msg->name_size, reinterpret_cast<const char *>(msg + 1), msg->source, \
300 msg->sequence, msg->level, msg->time_sec, msg->time_nsec)
Brian Silverman88471dc2014-02-15 22:35:42 -0800301 switch (msg->type) {
Brian Silvermana7234c62014-03-24 20:23:25 -0700302 case LogFileMessageHeader::MessageType::kString:
303 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
304 static_cast<int>(msg->message_size), position);
Brian Silverman88471dc2014-02-15 22:35:42 -0800305 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700306 case LogFileMessageHeader::MessageType::kStruct: {
307 uint32_t type_id;
308 memcpy(&type_id, position, sizeof(type_id));
309 position += sizeof(type_id);
Brian Silverman664db1a2014-03-20 17:06:29 -0700310
Brian Silvermana7234c62014-03-24 20:23:25 -0700311 uint32_t string_length;
312 memcpy(&string_length, position, sizeof(string_length));
313 position += sizeof(string_length);
314
315 char buffer[2048];
316 size_t output_length = sizeof(buffer);
317 size_t input_length =
318 msg->message_size -
319 (sizeof(type_id) + sizeof(uint32_t) + string_length);
320 if (!PrintMessage(buffer, &output_length, position + string_length,
321 &input_length, ::aos::type_cache::Get(type_id))) {
322 LOG(FATAL, "printing message (%.*s) of type %s into %zu-byte buffer "
323 "failed\n",
324 static_cast<int>(string_length), position,
325 ::aos::type_cache::Get(type_id).name.c_str(), sizeof(buffer));
326 }
327 if (input_length > 0) {
328 LOG(WARNING, "%zu extra bytes on message of type %s\n",
329 input_length, ::aos::type_cache::Get(type_id).name.c_str());
330 }
331 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
332 static_cast<int>(string_length), position,
333 static_cast<int>(sizeof(buffer) - output_length), buffer);
334 } break;
335 case LogFileMessageHeader::MessageType::kMatrix: {
336 uint32_t type;
337 memcpy(&type, position, sizeof(type));
338 position += sizeof(type);
339
340 uint32_t string_length;
341 memcpy(&string_length, position, sizeof(string_length));
342 position += sizeof(string_length);
Brian Silverman664db1a2014-03-20 17:06:29 -0700343
344 uint16_t rows;
345 memcpy(&rows, position, sizeof(rows));
Brian Silverman664db1a2014-03-20 17:06:29 -0700346 position += sizeof(rows);
347 uint16_t cols;
348 memcpy(&cols, position, sizeof(cols));
Brian Silverman664db1a2014-03-20 17:06:29 -0700349 position += sizeof(cols);
350
Brian Silvermana7234c62014-03-24 20:23:25 -0700351 const size_t matrix_bytes =
352 msg->message_size -
353 (sizeof(type) + sizeof(uint32_t) + sizeof(uint16_t) +
354 sizeof(uint16_t) + string_length);
355 CHECK_EQ(matrix_bytes, ::aos::MessageType::Sizeof(type) * rows * cols);
356 char buffer[2048];
357 size_t output_length = sizeof(buffer);
358 if (!::aos::PrintMatrix(buffer, &output_length,
359 position + string_length, type, rows, cols)) {
360 LOG(FATAL, "printing %dx%d matrix of type %" PRIu32 " failed\n", rows,
361 cols, type);
362 }
363 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
364 static_cast<int>(string_length), position,
365 static_cast<int>(sizeof(buffer) - output_length), buffer);
366 } break;
Brian Silverman88471dc2014-02-15 22:35:42 -0800367 case LogFileMessageHeader::MessageType::kStructType:
368 LOG(FATAL, "shouldn't get here\n");
369 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700370 }
371#undef BASE_ARGS
brians343bc112013-02-10 01:53:46 +0000372 } while (msg != NULL);
373}