blob: fa8fe6b0d7631918a67c3f51555b7b857fa85fe6 [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
John Park398c74a2018-10-20 21:17:39 -070014#include "aos/configuration.h"
John Park33858a32018-09-28 23:05:48 -070015#include "aos/logging/binary_log_file.h"
John Park33858a32018-09-28 23:05:48 -070016#include "aos/logging/logging.h"
17#include "aos/logging/implementations.h"
18#include "aos/logging/printf_formats.h"
19#include "aos/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
Austin Schuh03e80a62019-12-28 15:18:54 -080062std::string GetRootDirectory() {
63 ssize_t size = 0;
64 std::unique_ptr<char[]> retu;
65 while (true) {
66 size += 256;
67 retu.reset(new char[size]);
68
69 ssize_t ret = readlink("/proc/self/exe", retu.get(), size);
70 if (ret < 0) {
71 if (ret != -1) {
72 LOG(WARNING) << "it returned " << ret << ", not -1";
73 }
74
75 PLOG(FATAL) << "readlink(\"/proc/self/exe\", " << retu.get() << ", " << size
76 << ") failed";
77 }
78 if (ret < size) {
79 void *last_slash = memrchr(retu.get(), '/', ret);
80 if (last_slash == NULL) {
81 retu.get()[ret] = '\0';
82 LOG(FATAL) << "couldn't find a '/' in \"" << retu.get() << "\"";
83 }
84 *static_cast<char *>(last_slash) = '\0';
85 LOG(INFO) << "got a root dir of \"" << retu.get() << "\"";
86 return std::string(retu.get());
87 }
88 }
89}
90
91
brians343bc112013-02-10 01:53:46 +000092void PrintHelpAndExit() {
93 fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
Daniel Petti88c81f42014-09-12 10:05:05 -070094 fprintf(stderr, "\nExample usages:\n\n%s", kExampleUsages);
95
96 // Get the possible executables from start_list.txt.
97 FILE *start_list = fopen("start_list.txt", "r");
Daniel Pettiaa5bdb12014-12-20 16:33:05 -080098 if (!start_list) {
Austin Schuh03e80a62019-12-28 15:18:54 -080099 ::std::string path(GetRootDirectory());
Daniel Pettiaa5bdb12014-12-20 16:33:05 -0800100 path += "/start_list.txt";
101 start_list = fopen(path.c_str(), "r");
102 if (!start_list) {
Austin Schuh03e80a62019-12-28 15:18:54 -0800103 printf(
104 "\nCannot open start_list.txt. This means that the\npossible "
105 "arguments for the -n option cannot be shown. log_displayer\nlooks "
106 "for start_list.txt in the current working directory and in\n%s.\n\n",
107 path.c_str());
Austin Schuhf257f3c2019-10-27 21:00:43 -0700108 AOS_PLOG(FATAL, "Unable to open start_list.txt");
Daniel Pettiaa5bdb12014-12-20 16:33:05 -0800109 }
Daniel Petti88c81f42014-09-12 10:05:05 -0700110 }
111
112 // Get file size.
113 if (fseek(start_list, 0, SEEK_END)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700114 AOS_PLOG(FATAL, "fseek() failed while reading start_list.txt");
Daniel Petti88c81f42014-09-12 10:05:05 -0700115 }
116 int size = ftell(start_list);
117 if (size < 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700118 AOS_PLOG(FATAL, "ftell() failed while reading start_list.txt");
Daniel Petti88c81f42014-09-12 10:05:05 -0700119 }
120 rewind(start_list);
121
122 ::std::unique_ptr<char[]> contents(new char[size + 1]);
123 if (contents == NULL) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700124 AOS_LOG(FATAL, "malloc() failed while reading start_list.txt.\n");
Daniel Petti88c81f42014-09-12 10:05:05 -0700125 }
126 size_t bytes_read = fread(contents.get(), 1, size, start_list);
127 if (bytes_read < static_cast<size_t>(size)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700128 AOS_LOG(FATAL, "Read %zu bytes from start_list.txt, expected %d.\n",
129 bytes_read, size);
Daniel Petti88c81f42014-09-12 10:05:05 -0700130 }
131
132 // printf doesn't like strings without the \0.
133 contents[size] = '\0';
134 fprintf(stderr, "\nPossible arguments for the -n option:\n%s", contents.get());
135
136 if (fclose(start_list)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700137 AOS_LOG(FATAL, "fclose() failed.\n");
Daniel Petti88c81f42014-09-12 10:05:05 -0700138 }
brians343bc112013-02-10 01:53:46 +0000139
140 exit(EXIT_SUCCESS);
141}
142
143} // namespace
144
145int main(int argc, char **argv) {
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500146 const char *filter_name = nullptr, *filter_exact_name = nullptr;
Brian Silvermanf7780312014-02-16 17:26:15 -0800147 size_t filter_length = 0;
brians343bc112013-02-10 01:53:46 +0000148 log_level filter_level = INFO;
Brian Silvermanf7780312014-02-16 17:26:15 -0800149 bool follow = false;
150 // Whether we need to skip everything until we get to the end of the file.
151 bool skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000152 const char *filename = "aos_log-current";
Daniel Pettib6c885b2014-09-12 10:04:28 -0700153 int display_max = 0;
154 int32_t source_pid = -1;
brians343bc112013-02-10 01:53:46 +0000155
Brian Silvermanff485782014-06-18 19:59:09 -0700156 ::aos::logging::Init();
Tyler Chatow4b471e12020-01-05 20:19:36 -0800157 ::aos::logging::SetImplementation(
Brian Silvermanf7780312014-02-16 17:26:15 -0800158 new ::aos::logging::StreamLogImplementation(stdout));
159
brians343bc112013-02-10 01:53:46 +0000160 while (true) {
161 static struct option long_options[] = {
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500162 {"name-prefix", required_argument, NULL, 'n'},
163 {"name", required_argument, NULL, 'N'},
brians343bc112013-02-10 01:53:46 +0000164 {"level", required_argument, NULL, 'l'},
165 {"pid", required_argument, NULL, 'p'},
166
167 {"follow", no_argument, NULL, 'f'},
168 {"terminate", no_argument, NULL, 't'},
169 {"beginning", no_argument, NULL, 'b'},
170 {"end", no_argument, NULL, 'e'},
171 {"skip", required_argument, NULL, 's'},
172 {"max", required_argument, NULL, 'm'},
173
174 {"format", required_argument, NULL, 'o'},
175
176 {"help", no_argument, NULL, 'h'},
177 {0, 0, 0, 0}
178 };
179 int option_index = 0;
180
Brian Silvermandbcc87d2015-03-15 20:41:34 -0700181 const int c = getopt_long(argc, argv, "N:n:l:p:fts:m:o:h",
brians343bc112013-02-10 01:53:46 +0000182 long_options, &option_index);
183 if (c == -1) { // if we're at the end
184 break;
185 }
186 switch (c) {
187 case 0:
Daniel Petti88c81f42014-09-12 10:05:05 -0700188 fputs("LogDisplayer: got a 0 option but didn't set up any\n", stderr);
brians343bc112013-02-10 01:53:46 +0000189 abort();
190 case 'n':
191 filter_name = optarg;
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500192 filter_exact_name = nullptr;
193 filter_length = strlen(filter_name);
194 break;
195 case 'N':
196 filter_exact_name = optarg;
197 filter_name = nullptr;
James Kuszmaul3ae42262019-11-08 12:33:41 -0800198 filter_length = strlen(filter_exact_name);
brians343bc112013-02-10 01:53:46 +0000199 break;
200 case 'l':
Brian Silvermanab6615c2013-03-05 20:29:29 -0800201 filter_level = ::aos::logging::str_log(optarg);
brians343bc112013-02-10 01:53:46 +0000202 if (filter_level == LOG_UNKNOWN) {
203 fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
204 exit(EXIT_FAILURE);
205 }
206 break;
207 case 'p':
Daniel Petti5aa29792014-12-27 17:48:07 -0500208 if (!::aos::util::StringToNumber(::std::string(optarg), &source_pid)) {
Daniel Pettib6c885b2014-09-12 10:04:28 -0700209 fprintf(stderr, "ERROR: -p expects a number, not '%s'.\n", optarg);
210 exit(EXIT_FAILURE);
211 }
212 if (source_pid < 0) {
213 fprintf(stderr, "LogDisplayer: invalid pid '%s'\n", optarg);
214 exit(EXIT_FAILURE);
215 }
brians343bc112013-02-10 01:53:46 +0000216 break;
217 case 'f':
218 follow = true;
Brian Silvermanf7780312014-02-16 17:26:15 -0800219 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000220 break;
221 case 't':
222 follow = false;
223 break;
224 case 'b':
Brian Silvermanf7780312014-02-16 17:26:15 -0800225 skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000226 break;
227 case 'e':
Brian Silvermanf7780312014-02-16 17:26:15 -0800228 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000229 break;
230 case 'm':
Daniel Petti5aa29792014-12-27 17:48:07 -0500231 if (!::aos::util::StringToNumber(::std::string(optarg), &display_max)) {
Daniel Pettib6c885b2014-09-12 10:04:28 -0700232 fprintf(stderr, "ERROR: -m expects a number, not '%s'.\n", optarg);
233 exit(EXIT_FAILURE);
234 }
235 if (display_max <= 0) {
236 fprintf(stderr, "LogDisplayer: invalid max log number '%s'\n",
237 optarg);
238 exit(EXIT_FAILURE);
239 }
brians343bc112013-02-10 01:53:46 +0000240 break;
241 case 'o':
242 abort();
243 break;
244 case 'h':
245 PrintHelpAndExit();
246 break;
247 case '?':
248 break;
249 default:
Brian Silvermanab6615c2013-03-05 20:29:29 -0800250 fprintf(stderr, "LogDisplayer: in a bad spot (%s: %d)\n",
251 __FILE__, __LINE__);
brians343bc112013-02-10 01:53:46 +0000252 abort();
253 }
254 }
255
Daniel Pettie6f33e22014-08-21 20:35:55 -0700256 if (optind < argc) {
257 // We got a filename.
258 filename = argv[optind++];
259 }
brians343bc112013-02-10 01:53:46 +0000260 if (optind < argc) {
Daniel Petti88c81f42014-09-12 10:05:05 -0700261 fputs("non-option ARGV-elements: ", stderr);
brians343bc112013-02-10 01:53:46 +0000262 while (optind < argc) {
263 fprintf(stderr, "%s\n", argv[optind++]);
264 }
265 }
266
Brian Silverman65e569d2014-10-24 15:43:20 -0400267 int fd;
268 if (strcmp(filename, "-") == 0) {
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500269 if (skip_to_end) {
270 fputs("Can't skip to end of stdin!\n", stderr);
271 return EXIT_FAILURE;
272 }
Brian Silverman65e569d2014-10-24 15:43:20 -0400273 fd = STDIN_FILENO;
274 } else {
275 fd = open(filename, O_RDONLY);
276 }
Daniel Pettie6f33e22014-08-21 20:35:55 -0700277
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500278 fprintf(stderr, "displaying down to level %s from file '%s'\n",
279 ::aos::logging::log_str(filter_level), filename);
280
brians343bc112013-02-10 01:53:46 +0000281 if (fd == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700282 AOS_PLOG(FATAL, "couldn't open file '%s' for reading", filename);
brians343bc112013-02-10 01:53:46 +0000283 }
Brian Silvermanab5ba472014-04-18 15:26:14 -0700284 ::aos::logging::linux_code::LogFileReader reader(fd);
Brian Silvermanf7780312014-02-16 17:26:15 -0800285
286 if (skip_to_end) {
287 fputs("skipping old logs...\n", stderr);
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500288 reader.SkipToLastSeekablePage();
brians343bc112013-02-10 01:53:46 +0000289 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800290
291 const LogFileMessageHeader *msg;
Daniel Pettib6c885b2014-09-12 10:04:28 -0700292 int displayed = 0;
brians343bc112013-02-10 01:53:46 +0000293 do {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700294 msg = reader.ReadNextMessage(follow);
Brian Silverman003ba4b2014-02-10 16:56:18 -0800295 if (msg == NULL) {
296 fputs("reached end of file\n", stderr);
297 return 0;
298 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800299
Brian Silverman64833d42015-02-08 21:11:36 -0500300 if (source_pid >= 0 && msg->source != source_pid) {
301 // Message is from the wrong process.
302 continue;
303 }
304
Brian Silvermanf7780312014-02-16 17:26:15 -0800305 if (skip_to_end) {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700306 if (reader.IsLastPage()) {
Brian Silvermanf7780312014-02-16 17:26:15 -0800307 fputs("done skipping old logs\n", stderr);
308 skip_to_end = false;
309 } else {
310 continue;
311 }
312 }
313
Brian Silverman88471dc2014-02-15 22:35:42 -0800314 if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500315
Brian Silverman58657aa2015-02-21 20:06:40 -0500316 const char *position =
317 reinterpret_cast<const char *>(msg + 1);
318
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500319 if (filter_name != nullptr) {
320 const size_t compare_length =
321 ::std::min<size_t>(filter_length, msg->name_size);
Brian Silverman58657aa2015-02-21 20:06:40 -0500322 if (memcmp(filter_name, position, compare_length) != 0) {
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500323 continue;
324 }
Brian Silverman58657aa2015-02-21 20:06:40 -0500325 if (compare_length < msg->name_size) {
326 if (position[compare_length] != '.') continue;
327 }
Brian Silverman8c2b00e2015-02-17 20:30:50 -0500328 }
329
330 if (filter_exact_name != nullptr) {
331 if (filter_length != msg->name_size) continue;
Brian Silverman58657aa2015-02-21 20:06:40 -0500332 if (memcmp(filter_exact_name, position, filter_length) != 0) {
Brian Silverman88471dc2014-02-15 22:35:42 -0800333 continue;
334 }
335 }
336
Daniel Pettib6c885b2014-09-12 10:04:28 -0700337 if (display_max && displayed++ >= display_max) {
338 fputs("Not displaying the rest of the messages.\n", stderr);
339 return 0;
340 }
341
Brian Silverman58657aa2015-02-21 20:06:40 -0500342 position += msg->name_size;
343
Brian Silvermana7234c62014-03-24 20:23:25 -0700344#define BASE_ARGS \
345 AOS_LOGGING_BASE_ARGS( \
346 msg->name_size, reinterpret_cast<const char *>(msg + 1), msg->source, \
347 msg->sequence, msg->level, msg->time_sec, msg->time_nsec)
Brian Silverman88471dc2014-02-15 22:35:42 -0800348 switch (msg->type) {
Brian Silvermana7234c62014-03-24 20:23:25 -0700349 case LogFileMessageHeader::MessageType::kString:
350 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
351 static_cast<int>(msg->message_size), position);
Brian Silverman88471dc2014-02-15 22:35:42 -0800352 break;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700353 case LogFileMessageHeader::MessageType::kStruct:
354 case LogFileMessageHeader::MessageType::kMatrix:
355 AOS_LOG(FATAL, "Unsupported matrix or struct\n");
356 break;
Brian Silverman88471dc2014-02-15 22:35:42 -0800357 case LogFileMessageHeader::MessageType::kStructType:
Austin Schuhf257f3c2019-10-27 21:00:43 -0700358 AOS_LOG(FATAL, "shouldn't get here\n");
Brian Silverman88471dc2014-02-15 22:35:42 -0800359 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700360 }
361#undef BASE_ARGS
brians343bc112013-02-10 01:53:46 +0000362 } while (msg != NULL);
363}