blob: 8141cd47420b25d54b3ef20ba7043657428a77de [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>
10
Brian Silvermanfe9b7a22014-02-10 15:03:42 -080011#include "aos/linux_code/logging/binary_log_file.h"
Brian Silverman88471dc2014-02-15 22:35:42 -080012#include "aos/common/queue_types.h"
Brian Silvermana7234c62014-03-24 20:23:25 -070013#include "aos/common/logging/logging_impl.h"
14#include "aos/common/logging/logging_printf_formats.h"
Brian Silverman88471dc2014-02-15 22:35:42 -080015
16using ::aos::logging::linux_code::LogFileMessageHeader;
brians343bc112013-02-10 01:53:46 +000017
18namespace {
19
20const char *kArgsHelp = "[OPTION]... [FILE]\n"
21 "Display log file FILE (created by BinaryLogReader) to stdout.\n"
22 "FILE is \"aos_log-current\" by default.\n"
23 "\n"
24 " -n, --name NAME only display entries from processes named NAME\n"
25 " -l, --level LEVEL "
26 "only display log entries at least as important as LEVEL\n"
27 " // -p, --pid PID only display log entries from process PID\n"
28 " -f, --follow "
29 "wait when the end of the file is reached (implies --end)\n"
30 " -t, --terminate stop when the end of file is reached (default)\n"
31 " -b, --beginning start at the beginning of the file (default)\n"
32 " -e, --end start at the end of the file\n"
33 " -s, --skip NUMBER skip NUMBER matching logs\n"
34 " // -m, --max NUMBER only display up to NUMBER logs\n"
35 " // -o, --format FORMAT use FORMAT to display log entries\n"
Brian Silvermana52ba162013-02-28 15:01:12 -080036 " -h, --help display this help and exit\n"
brians343bc112013-02-10 01:53:46 +000037 "\n"
38 "LEVEL must be DEBUG, INFO, WARNING, ERROR, or FATAL.\n"
39 " It defaults to INFO.\n"
40 "\n"
Daniel Pettie6f33e22014-08-21 20:35:55 -070041 "TODO(brians) implement the commented out ones.\n";
brians343bc112013-02-10 01:53:46 +000042
43void PrintHelpAndExit() {
44 fprintf(stderr, "Usage: %s %s", program_invocation_name, kArgsHelp);
45
46 exit(EXIT_SUCCESS);
47}
48
49} // namespace
50
51int main(int argc, char **argv) {
52 const char *filter_name = NULL;
Brian Silvermanf7780312014-02-16 17:26:15 -080053 size_t filter_length = 0;
brians343bc112013-02-10 01:53:46 +000054 log_level filter_level = INFO;
Brian Silvermanf7780312014-02-16 17:26:15 -080055 bool follow = false;
56 // Whether we need to skip everything until we get to the end of the file.
57 bool skip_to_end = false;
brians343bc112013-02-10 01:53:46 +000058 const char *filename = "aos_log-current";
59
Brian Silvermanff485782014-06-18 19:59:09 -070060 ::aos::logging::Init();
Brian Silvermanf7780312014-02-16 17:26:15 -080061 ::aos::logging::AddImplementation(
62 new ::aos::logging::StreamLogImplementation(stdout));
63
brians343bc112013-02-10 01:53:46 +000064 while (true) {
65 static struct option long_options[] = {
66 {"name", required_argument, NULL, 'n'},
67 {"level", required_argument, NULL, 'l'},
68 {"pid", required_argument, NULL, 'p'},
69
70 {"follow", no_argument, NULL, 'f'},
71 {"terminate", no_argument, NULL, 't'},
72 {"beginning", no_argument, NULL, 'b'},
73 {"end", no_argument, NULL, 'e'},
74 {"skip", required_argument, NULL, 's'},
75 {"max", required_argument, NULL, 'm'},
76
77 {"format", required_argument, NULL, 'o'},
78
79 {"help", no_argument, NULL, 'h'},
80 {0, 0, 0, 0}
81 };
82 int option_index = 0;
83
84 const int c = getopt_long(argc, argv, "n:l:p:fts:m:o:h",
85 long_options, &option_index);
86 if (c == -1) { // if we're at the end
87 break;
88 }
89 switch (c) {
90 case 0:
91 fprintf(stderr, "LogDisplayer: got a 0 option but didn't set up any\n");
92 abort();
93 case 'n':
94 filter_name = optarg;
Brian Silverman88471dc2014-02-15 22:35:42 -080095 filter_length = strlen(filter_name);
brians343bc112013-02-10 01:53:46 +000096 break;
97 case 'l':
Brian Silvermanab6615c2013-03-05 20:29:29 -080098 filter_level = ::aos::logging::str_log(optarg);
brians343bc112013-02-10 01:53:46 +000099 if (filter_level == LOG_UNKNOWN) {
100 fprintf(stderr, "LogDisplayer: unknown log level '%s'\n", optarg);
101 exit(EXIT_FAILURE);
102 }
103 break;
104 case 'p':
105 abort();
106 break;
107 case 'f':
108 follow = true;
Brian Silvermanf7780312014-02-16 17:26:15 -0800109 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000110 break;
111 case 't':
112 follow = false;
113 break;
114 case 'b':
Brian Silvermanf7780312014-02-16 17:26:15 -0800115 skip_to_end = false;
brians343bc112013-02-10 01:53:46 +0000116 break;
117 case 'e':
Brian Silvermanf7780312014-02-16 17:26:15 -0800118 skip_to_end = true;
brians343bc112013-02-10 01:53:46 +0000119 break;
120 case 'm':
121 abort();
122 break;
123 case 'o':
124 abort();
125 break;
126 case 'h':
127 PrintHelpAndExit();
128 break;
129 case '?':
130 break;
131 default:
Brian Silvermanab6615c2013-03-05 20:29:29 -0800132 fprintf(stderr, "LogDisplayer: in a bad spot (%s: %d)\n",
133 __FILE__, __LINE__);
brians343bc112013-02-10 01:53:46 +0000134 abort();
135 }
136 }
137
Daniel Pettie6f33e22014-08-21 20:35:55 -0700138 if (optind < argc) {
139 // We got a filename.
140 filename = argv[optind++];
141 }
brians343bc112013-02-10 01:53:46 +0000142 if (optind < argc) {
143 fprintf(stderr, "non-option ARGV-elements: ");
144 while (optind < argc) {
145 fprintf(stderr, "%s\n", argv[optind++]);
146 }
147 }
148
Daniel Pettie6f33e22014-08-21 20:35:55 -0700149 fprintf(stderr, "displaying down to level %s from file '%s'\n",
150 ::aos::logging::log_str(filter_level), filename);
151
brians343bc112013-02-10 01:53:46 +0000152 int fd = open(filename, O_RDONLY);
Daniel Pettie6f33e22014-08-21 20:35:55 -0700153
brians343bc112013-02-10 01:53:46 +0000154 if (fd == -1) {
Brian Silverman01be0002014-05-10 15:44:38 -0700155 PLOG(FATAL, "couldn't open file '%s' for reading", filename);
brians343bc112013-02-10 01:53:46 +0000156 }
Brian Silvermanab5ba472014-04-18 15:26:14 -0700157 ::aos::logging::linux_code::LogFileReader reader(fd);
Brian Silvermanf7780312014-02-16 17:26:15 -0800158
159 if (skip_to_end) {
160 fputs("skipping old logs...\n", stderr);
brians343bc112013-02-10 01:53:46 +0000161 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800162
163 const LogFileMessageHeader *msg;
brians343bc112013-02-10 01:53:46 +0000164 do {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700165 msg = reader.ReadNextMessage(follow);
Brian Silverman003ba4b2014-02-10 16:56:18 -0800166 if (msg == NULL) {
167 fputs("reached end of file\n", stderr);
168 return 0;
169 }
Brian Silverman88471dc2014-02-15 22:35:42 -0800170
171 if (msg->type == LogFileMessageHeader::MessageType::kStructType) {
172 size_t bytes = msg->message_size;
173 ::aos::MessageType *type = ::aos::MessageType::Deserialize(
174 reinterpret_cast<const char *>(msg + 1), &bytes);
175 ::aos::type_cache::Add(*type);
brians343bc112013-02-10 01:53:46 +0000176 continue;
177 }
Brian Silvermanf665d692013-02-17 22:11:39 -0800178
Brian Silvermanf7780312014-02-16 17:26:15 -0800179 if (skip_to_end) {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700180 if (reader.IsLastPage()) {
Brian Silvermanf7780312014-02-16 17:26:15 -0800181 fputs("done skipping old logs\n", stderr);
182 skip_to_end = false;
183 } else {
184 continue;
185 }
186 }
187
Brian Silverman88471dc2014-02-15 22:35:42 -0800188 if (::aos::logging::log_gt_important(filter_level, msg->level)) continue;
189 if (filter_name != NULL) {
190 if (filter_length != msg->name_size) continue;
191 if (memcmp(filter_name,
192 reinterpret_cast<const char *>(msg) + sizeof(*msg),
193 filter_length) !=
194 0) {
195 continue;
196 }
197 }
198
Brian Silvermana7234c62014-03-24 20:23:25 -0700199 const char *position =
200 reinterpret_cast<const char *>(msg + 1) + msg->name_size;
201#define BASE_ARGS \
202 AOS_LOGGING_BASE_ARGS( \
203 msg->name_size, reinterpret_cast<const char *>(msg + 1), msg->source, \
204 msg->sequence, msg->level, msg->time_sec, msg->time_nsec)
Brian Silverman88471dc2014-02-15 22:35:42 -0800205 switch (msg->type) {
Brian Silvermana7234c62014-03-24 20:23:25 -0700206 case LogFileMessageHeader::MessageType::kString:
207 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
208 static_cast<int>(msg->message_size), position);
Brian Silverman88471dc2014-02-15 22:35:42 -0800209 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700210 case LogFileMessageHeader::MessageType::kStruct: {
211 uint32_t type_id;
212 memcpy(&type_id, position, sizeof(type_id));
213 position += sizeof(type_id);
Brian Silverman664db1a2014-03-20 17:06:29 -0700214
Brian Silvermana7234c62014-03-24 20:23:25 -0700215 uint32_t string_length;
216 memcpy(&string_length, position, sizeof(string_length));
217 position += sizeof(string_length);
218
219 char buffer[2048];
220 size_t output_length = sizeof(buffer);
221 size_t input_length =
222 msg->message_size -
223 (sizeof(type_id) + sizeof(uint32_t) + string_length);
224 if (!PrintMessage(buffer, &output_length, position + string_length,
225 &input_length, ::aos::type_cache::Get(type_id))) {
226 LOG(FATAL, "printing message (%.*s) of type %s into %zu-byte buffer "
227 "failed\n",
228 static_cast<int>(string_length), position,
229 ::aos::type_cache::Get(type_id).name.c_str(), sizeof(buffer));
230 }
231 if (input_length > 0) {
232 LOG(WARNING, "%zu extra bytes on message of type %s\n",
233 input_length, ::aos::type_cache::Get(type_id).name.c_str());
234 }
235 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
236 static_cast<int>(string_length), position,
237 static_cast<int>(sizeof(buffer) - output_length), buffer);
238 } break;
239 case LogFileMessageHeader::MessageType::kMatrix: {
240 uint32_t type;
241 memcpy(&type, position, sizeof(type));
242 position += sizeof(type);
243
244 uint32_t string_length;
245 memcpy(&string_length, position, sizeof(string_length));
246 position += sizeof(string_length);
Brian Silverman664db1a2014-03-20 17:06:29 -0700247
248 uint16_t rows;
249 memcpy(&rows, position, sizeof(rows));
Brian Silverman664db1a2014-03-20 17:06:29 -0700250 position += sizeof(rows);
251 uint16_t cols;
252 memcpy(&cols, position, sizeof(cols));
Brian Silverman664db1a2014-03-20 17:06:29 -0700253 position += sizeof(cols);
254
Brian Silvermana7234c62014-03-24 20:23:25 -0700255 const size_t matrix_bytes =
256 msg->message_size -
257 (sizeof(type) + sizeof(uint32_t) + sizeof(uint16_t) +
258 sizeof(uint16_t) + string_length);
259 CHECK_EQ(matrix_bytes, ::aos::MessageType::Sizeof(type) * rows * cols);
260 char buffer[2048];
261 size_t output_length = sizeof(buffer);
262 if (!::aos::PrintMatrix(buffer, &output_length,
263 position + string_length, type, rows, cols)) {
264 LOG(FATAL, "printing %dx%d matrix of type %" PRIu32 " failed\n", rows,
265 cols, type);
266 }
267 fprintf(stdout, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
268 static_cast<int>(string_length), position,
269 static_cast<int>(sizeof(buffer) - output_length), buffer);
270 } break;
Brian Silverman88471dc2014-02-15 22:35:42 -0800271 case LogFileMessageHeader::MessageType::kStructType:
272 LOG(FATAL, "shouldn't get here\n");
273 break;
Brian Silvermana7234c62014-03-24 20:23:25 -0700274 }
275#undef BASE_ARGS
brians343bc112013-02-10 01:53:46 +0000276 } while (msg != NULL);
277}