blob: 5eb6f8a5ccd1072258abeaf0815b364e43820c23 [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"
26 "\n"
27 " -n, --name NAME only display entries from processes named NAME\n"
28 " -l, --level LEVEL "
29 "only display log entries at least as important as LEVEL\n"
Daniel Pettib6c885b2014-09-12 10:04:28 -070030 " -p, --pid PID only display log entries from process PID\n"
brians343bc112013-02-10 01:53:46 +000031 " -f, --follow "
32 "wait when the end of the file is reached (implies --end)\n"
33 " -t, --terminate stop when the end of file is reached (default)\n"
34 " -b, --beginning start at the beginning of the file (default)\n"
35 " -e, --end start at the end of the file\n"
36 " -s, --skip NUMBER skip NUMBER matching logs\n"
Daniel Pettib6c885b2014-09-12 10:04:28 -070037 " -m, --max NUMBER only display up to NUMBER logs\n"
brians343bc112013-02-10 01:53:46 +000038 " // -o, --format FORMAT use FORMAT to display log entries\n"
Brian Silvermana52ba162013-02-28 15:01:12 -080039 " -h, --help display this help and exit\n"
brians343bc112013-02-10 01:53:46 +000040 "\n"
41 "LEVEL must be DEBUG, INFO, WARNING, ERROR, or FATAL.\n"
42 " It defaults to INFO.\n"
43 "\n"
Daniel Pettie6f33e22014-08-21 20:35:55 -070044 "TODO(brians) implement the commented out ones.\n";
brians343bc112013-02-10 01:53:46 +000045
46void PrintHelpAndExit() {
47 fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
48
49 exit(EXIT_SUCCESS);
50}
51
52} // namespace
53
54int main(int argc, char **argv) {
55 const char *filter_name = NULL;
Brian Silvermanf7780312014-02-16 17:26:15 -080056 size_t filter_length = 0;
brians343bc112013-02-10 01:53:46 +000057 log_level filter_level = INFO;
Brian Silvermanf7780312014-02-16 17:26:15 -080058 bool follow = false;
59 // Whether we need to skip everything until we get to the end of the file.
60 bool skip_to_end = false;
brians343bc112013-02-10 01:53:46 +000061 const char *filename = "aos_log-current";
Daniel Pettib6c885b2014-09-12 10:04:28 -070062 int display_max = 0;
63 int32_t source_pid = -1;
brians343bc112013-02-10 01:53:46 +000064
Brian Silvermanff485782014-06-18 19:59:09 -070065 ::aos::logging::Init();
Brian Silvermanf7780312014-02-16 17:26:15 -080066 ::aos::logging::AddImplementation(
67 new ::aos::logging::StreamLogImplementation(stdout));
68
brians343bc112013-02-10 01:53:46 +000069 while (true) {
70 static struct option long_options[] = {
71 {"name", required_argument, NULL, 'n'},
72 {"level", required_argument, NULL, 'l'},
73 {"pid", required_argument, NULL, 'p'},
74
75 {"follow", no_argument, NULL, 'f'},
76 {"terminate", no_argument, NULL, 't'},
77 {"beginning", no_argument, NULL, 'b'},
78 {"end", no_argument, NULL, 'e'},
79 {"skip", required_argument, NULL, 's'},
80 {"max", required_argument, NULL, 'm'},
81
82 {"format", required_argument, NULL, 'o'},
83
84 {"help", no_argument, NULL, 'h'},
85 {0, 0, 0, 0}
86 };
87 int option_index = 0;
88
89 const int c = getopt_long(argc, argv, "n:l:p:fts:m:o:h",
90 long_options, &option_index);
91 if (c == -1) { // if we're at the end
92 break;
93 }
94 switch (c) {
95 case 0:
96 fprintf(stderr, "LogDisplayer: got a 0 option but didn't set up any\n");
97 abort();
98 case 'n':
99 filter_name = optarg;
Brian Silverman88471dc2014-02-15 22:35:42 -0800100 filter_length = strlen(filter_name);
brians343bc112013-02-10 01:53:46 +0000101 break;
102 case 'l':
Brian Silvermanab6615c2013-03-05 20:29:29 -0800103 filter_level = ::aos::logging::str_log(optarg);
brians343bc112013-02-10 01:53:46 +0000104 if (filter_level == LOG_UNKNOWN) {
105 fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
106 exit(EXIT_FAILURE);
107 }
108 break;
109 case 'p':
Daniel Pettib6c885b2014-09-12 10:04:28 -0700110 if (!::aos::util::StringToInteger(::std::string(optarg), &source_pid)) {
111 fprintf(stderr, "ERROR: -p expects a number, not '%s'.\n", optarg);
112 exit(EXIT_FAILURE);
113 }
114 if (source_pid < 0) {
115 fprintf(stderr, "LogDisplayer: invalid pid '%s'\n", optarg);
116 exit(EXIT_FAILURE);
117 }
brians343bc112013-02-10 01:53:46 +0000118 break;
119 case 'f':
120 follow = true;
Brian Silvermanf7780312014-02-16 17:26:15 -0800121 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000122 break;
123 case 't':
124 follow = false;
125 break;
126 case 'b':
Brian Silvermanf7780312014-02-16 17:26:15 -0800127 skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000128 break;
129 case 'e':
Brian Silvermanf7780312014-02-16 17:26:15 -0800130 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000131 break;
132 case 'm':
Daniel Pettib6c885b2014-09-12 10:04:28 -0700133 if (!::aos::util::StringToInteger(::std::string(optarg), &display_max)) {
134 fprintf(stderr, "ERROR: -m expects a number, not '%s'.\n", optarg);
135 exit(EXIT_FAILURE);
136 }
137 if (display_max <= 0) {
138 fprintf(stderr, "LogDisplayer: invalid max log number '%s'\n",
139 optarg);
140 exit(EXIT_FAILURE);
141 }
brians343bc112013-02-10 01:53:46 +0000142 break;
143 case 'o':
144 abort();
145 break;
146 case 'h':
147 PrintHelpAndExit();
148 break;
149 case '?':
150 break;
151 default:
Brian Silvermanab6615c2013-03-05 20:29:29 -0800152 fprintf(stderr, "LogDisplayer: in a bad spot (%s: %d)\n",
153 __FILE__, __LINE__);
brians343bc112013-02-10 01:53:46 +0000154 abort();
155 }
156 }
157
Daniel Pettie6f33e22014-08-21 20:35:55 -0700158 if (optind < argc) {
159 // We got a filename.
160 filename = argv[optind++];
161 }
brians343bc112013-02-10 01:53:46 +0000162 if (optind < argc) {
163 fprintf(stderr, "non-option ARGV-elements: ");
164 while (optind < argc) {
165 fprintf(stderr, "%s\n", argv[optind++]);
166 }
167 }
168
Daniel Pettie6f33e22014-08-21 20:35:55 -0700169 fprintf(stderr, "displaying down to level %s from file '%s'\n",
170 ::aos::logging::log_str(filter_level), filename);
171
brians343bc112013-02-10 01:53:46 +0000172 int fd = open(filename, O_RDONLY);
Daniel Pettie6f33e22014-08-21 20:35:55 -0700173
brians343bc112013-02-10 01:53:46 +0000174 if (fd == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700175 PLOG(FATAL, "couldn't open file '%s' for reading", filename);
brians343bc112013-02-10 01:53:46 +0000176 }
Brian Silvermanab5ba472014-04-18 15:26:14 -0700177 ::aos::logging::linux_code::LogFileReader reader(fd);
Brian Silvermanf7780312014-02-16 17:26:15 -0800178
179 if (skip_to_end) {
180 fputs("skipping old logs...\n", stderr);
brians343bc112013-02-10 01:53:46 +0000181 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800182
183 const LogFileMessageHeader *msg;
Daniel Pettib6c885b2014-09-12 10:04:28 -0700184 int displayed = 0;
brians343bc112013-02-10 01:53:46 +0000185 do {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700186 msg = reader.ReadNextMessage(follow);
Brian Silverman003ba4b2014-02-10 16:56:18 -0800187 if (msg == NULL) {
188 fputs("reached end of file\n", stderr);
189 return 0;
190 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800191
Daniel Pettib6c885b2014-09-12 10:04:28 -0700192 if (source_pid >= 0 && msg->source != source_pid) {
193 // Message is from the wrong process.
194 continue;
195 }
196
Brian Silverman88471dc2014-02-15 22:35:42 -0800197 if (msg->type == LogFileMessageHeader::MessageType::kStructType) {
198 size_t bytes = msg->message_size;
199 ::aos::MessageType *type = ::aos::MessageType::Deserialize(
200 reinterpret_cast<const char *>(msg + 1), &bytes);
201 ::aos::type_cache::Add(*type);
brians343bc112013-02-10 01:53:46 +0000202 continue;
203 }
Brian Silvermanf665d692013-02-17 22:11:39 -0800204
Brian Silvermanf7780312014-02-16 17:26:15 -0800205 if (skip_to_end) {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700206 if (reader.IsLastPage()) {
Brian Silvermanf7780312014-02-16 17:26:15 -0800207 fputs("done skipping old logs\n", stderr);
208 skip_to_end = false;
209 } else {
210 continue;
211 }
212 }
213
Brian Silverman88471dc2014-02-15 22:35:42 -0800214 if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
215 if (filter_name != NULL) {
216 if (filter_length != msg->name_size) continue;
217 if (memcmp(filter_name,
218 reinterpret_cast<const char *>(msg) + sizeof(*msg),
219 filter_length) !=
220 0) {
221 continue;
222 }
223 }
224
Daniel Pettib6c885b2014-09-12 10:04:28 -0700225 if (display_max && displayed++ >= display_max) {
226 fputs("Not displaying the rest of the messages.\n", stderr);
227 return 0;
228 }
229
Brian Silvermana7234c62014-03-24 20:23:25 -0700230 const char *position =
231 reinterpret_cast<const char *>(msg + 1) + msg->name_size;
232#define BASE_ARGS \
233 AOS_LOGGING_BASE_ARGS( \
234 msg->name_size, reinterpret_cast<const char *>(msg + 1), msg->source, \
235 msg->sequence, msg->level, msg->time_sec, msg->time_nsec)
Brian Silverman88471dc2014-02-15 22:35:42 -0800236 switch (msg->type) {
Brian Silvermana7234c62014-03-24 20:23:25 -0700237 case LogFileMessageHeader::MessageType::kString:
238 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
239 static_cast<int>(msg->message_size), position);
Brian Silverman88471dc2014-02-15 22:35:42 -0800240 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700241 case LogFileMessageHeader::MessageType::kStruct: {
242 uint32_t type_id;
243 memcpy(&type_id, position, sizeof(type_id));
244 position += sizeof(type_id);
Brian Silverman664db1a2014-03-20 17:06:29 -0700245
Brian Silvermana7234c62014-03-24 20:23:25 -0700246 uint32_t string_length;
247 memcpy(&string_length, position, sizeof(string_length));
248 position += sizeof(string_length);
249
250 char buffer[2048];
251 size_t output_length = sizeof(buffer);
252 size_t input_length =
253 msg->message_size -
254 (sizeof(type_id) + sizeof(uint32_t) + string_length);
255 if (!PrintMessage(buffer, &output_length, position + string_length,
256 &input_length, ::aos::type_cache::Get(type_id))) {
257 LOG(FATAL, "printing message (%.*s) of type %s into %zu-byte buffer "
258 "failed\n",
259 static_cast<int>(string_length), position,
260 ::aos::type_cache::Get(type_id).name.c_str(), sizeof(buffer));
261 }
262 if (input_length > 0) {
263 LOG(WARNING, "%zu extra bytes on message of type %s\n",
264 input_length, ::aos::type_cache::Get(type_id).name.c_str());
265 }
266 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
267 static_cast<int>(string_length), position,
268 static_cast<int>(sizeof(buffer) - output_length), buffer);
269 } break;
270 case LogFileMessageHeader::MessageType::kMatrix: {
271 uint32_t type;
272 memcpy(&type, position, sizeof(type));
273 position += sizeof(type);
274
275 uint32_t string_length;
276 memcpy(&string_length, position, sizeof(string_length));
277 position += sizeof(string_length);
Brian Silverman664db1a2014-03-20 17:06:29 -0700278
279 uint16_t rows;
280 memcpy(&rows, position, sizeof(rows));
Brian Silverman664db1a2014-03-20 17:06:29 -0700281 position += sizeof(rows);
282 uint16_t cols;
283 memcpy(&cols, position, sizeof(cols));
Brian Silverman664db1a2014-03-20 17:06:29 -0700284 position += sizeof(cols);
285
Brian Silvermana7234c62014-03-24 20:23:25 -0700286 const size_t matrix_bytes =
287 msg->message_size -
288 (sizeof(type) + sizeof(uint32_t) + sizeof(uint16_t) +
289 sizeof(uint16_t) + string_length);
290 CHECK_EQ(matrix_bytes, ::aos::MessageType::Sizeof(type) * rows * cols);
291 char buffer[2048];
292 size_t output_length = sizeof(buffer);
293 if (!::aos::PrintMatrix(buffer, &output_length,
294 position + string_length, type, rows, cols)) {
295 LOG(FATAL, "printing %dx%d matrix of type %" PRIu32 " failed\n", rows,
296 cols, type);
297 }
298 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
299 static_cast<int>(string_length), position,
300 static_cast<int>(sizeof(buffer) - output_length), buffer);
301 } break;
Brian Silverman88471dc2014-02-15 22:35:42 -0800302 case LogFileMessageHeader::MessageType::kStructType:
303 LOG(FATAL, "shouldn't get here\n");
304 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700305 }
306#undef BASE_ARGS
brians343bc112013-02-10 01:53:46 +0000307 } while (msg != NULL);
308}