blob: d9ca131b64fcc3d1774068dcf6a5e34d05782a3f [file] [log] [blame]
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001// Copyright 2006 The Android Open Source Project
2
3#include <utils/misc.h>
4#include <utils/logger.h>
5#include <cutils/logd.h>
6#include <cutils/sockets.h>
7#include <cutils/logprint.h>
8#include <cutils/event_tag_map.h>
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <stdarg.h>
13#include <string.h>
14#include <unistd.h>
15#include <fcntl.h>
16#include <time.h>
17#include <errno.h>
18#include <assert.h>
19#include <ctype.h>
20#include <sys/socket.h>
21#include <sys/stat.h>
22#include <arpa/inet.h>
23
24#define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16
25#define DEFAULT_MAX_ROTATED_LOGS 4
26
27static AndroidLogFormat * g_logformat;
28
29/* logd prefixes records with a length field */
30#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
31
32#define LOG_FILE_DIR "/dev/log/"
33
34
35namespace android {
36
37/* Global Variables */
38
39static const char * g_outputFileName = NULL;
40static int g_logRotateSizeKBytes = 0; // 0 means "no log rotation"
41static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded"
42static int g_outFD = -1;
43static off_t g_outByteCount = 0;
44static int g_isBinary = 0;
45static int g_printBinary = 0;
46
47static EventTagMap* g_eventTagMap = NULL;
48
49static int openLogFile (const char *pathname)
50{
51 return open(g_outputFileName, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
52}
53
54static void rotateLogs()
55{
56 int err;
57
58 // Can't rotate logs if we're not outputting to a file
59 if (g_outputFileName == NULL) {
60 return;
61 }
62
63 close(g_outFD);
64
65 for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
66 char *file0, *file1;
67
68 asprintf(&file1, "%s.%d", g_outputFileName, i);
69
70 if (i - 1 == 0) {
71 asprintf(&file0, "%s", g_outputFileName);
72 } else {
73 asprintf(&file0, "%s.%d", g_outputFileName, i - 1);
74 }
75
76 err = rename (file0, file1);
77
78 if (err < 0 && errno != ENOENT) {
79 perror("while rotating log files");
80 }
81
82 free(file1);
83 free(file0);
84 }
85
86 g_outFD = openLogFile (g_outputFileName);
87
88 if (g_outFD < 0) {
89 perror ("couldn't open output file");
90 exit(-1);
91 }
92
93 g_outByteCount = 0;
94
95}
96
97void printBinary(struct logger_entry *buf)
98{
99 size_t size = sizeof(logger_entry) + buf->len;
100 int ret;
101
102 do {
103 ret = write(g_outFD, buf, size);
104 } while (ret < 0 && errno == EINTR);
105}
106
107static void processBuffer(struct logger_entry *buf)
108{
109 int bytesWritten;
110 int err;
111 AndroidLogEntry entry;
112 char binaryMsgBuf[1024];
113
114 if (g_isBinary) {
115 err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap,
116 binaryMsgBuf, sizeof(binaryMsgBuf));
117 //printf(">>> pri=%d len=%d msg='%s'\n",
118 // entry.priority, entry.messageLen, entry.message);
119 } else {
120 err = android_log_processLogBuffer(buf, &entry);
121 }
122 if (err < 0)
123 goto error;
124
125 bytesWritten = android_log_filterAndPrintLogLine(
126 g_logformat, g_outFD, &entry);
127
128 if (bytesWritten < 0) {
129 perror("output error");
130 exit(-1);
131 }
132
133 g_outByteCount += bytesWritten;
134
135 if (g_logRotateSizeKBytes > 0
136 && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
137 ) {
138 rotateLogs();
139 }
140
141error:
142 //fprintf (stderr, "Error processing record\n");
143 return;
144}
145
146static void readLogLines(int logfd)
147{
148 while (1) {
149 unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
150 struct logger_entry *entry = (struct logger_entry *) buf;
151 int ret;
152
153 ret = read(logfd, entry, LOGGER_ENTRY_MAX_LEN);
154 if (ret < 0) {
155 if (errno == EINTR)
156 continue;
157 if (errno == EAGAIN)
158 break;
159 perror("logcat read");
160 exit(EXIT_FAILURE);
161 }
162 else if (!ret) {
163 fprintf(stderr, "read: Unexpected EOF!\n");
164 exit(EXIT_FAILURE);
165 }
166
167 /* NOTE: driver guarantees we read exactly one full entry */
168
169 entry->msg[entry->len] = '\0';
170
171 if (g_printBinary) {
172 printBinary(entry);
173 } else {
174 (void) processBuffer(entry);
175 }
176 }
177}
178
179static int clearLog(int logfd)
180{
181 return ioctl(logfd, LOGGER_FLUSH_LOG);
182}
183
184/* returns the total size of the log's ring buffer */
185static int getLogSize(int logfd)
186{
187 return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE);
188}
189
190/* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */
191static int getLogReadableSize(int logfd)
192{
193 return ioctl(logfd, LOGGER_GET_LOG_LEN);
194}
195
196static void setupOutput()
197{
198
199 if (g_outputFileName == NULL) {
200 g_outFD = STDOUT_FILENO;
201
202 } else {
203 struct stat statbuf;
204
205 g_outFD = openLogFile (g_outputFileName);
206
207 if (g_outFD < 0) {
208 perror ("couldn't open output file");
209 exit(-1);
210 }
211
212 fstat(g_outFD, &statbuf);
213
214 g_outByteCount = statbuf.st_size;
215 }
216}
217
218static void show_help(const char *cmd)
219{
220 fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
221
222 fprintf(stderr, "options include:\n"
223 " -s Set default filter to silent.\n"
224 " Like specifying filterspec '*:s'\n"
225 " -f <filename> Log to file. Default to stdout\n"
226 " -r [<kbytes>] Rotate log every kbytes. (16 if unspecified). Requires -f\n"
227 " -n <count> Sets max number of rotated logs to <count>, default 4\n"
228 " -v <format> Sets the log print format, where <format> is one of:\n\n"
229 " brief process tag thread raw time threadtime long\n\n"
230 " -c clear (flush) the entire log and exit\n"
231 " -d dump the log and then exit (don't block)\n"
232 " -g get the size of the log's ring buffer and exit\n"
233 " -b <buffer> request alternate ring buffer\n"
234 " ('main' (default), 'radio', 'events')\n"
235 " -B output the log in binary");
236
237
238 fprintf(stderr,"\nfilterspecs are a series of \n"
239 " <tag>[:priority]\n\n"
240 "where <tag> is a log component tag (or * for all) and priority is:\n"
241 " V Verbose\n"
242 " D Debug\n"
243 " I Info\n"
244 " W Warn\n"
245 " E Error\n"
246 " F Fatal\n"
247 " S Silent (supress all output)\n"
248 "\n'*' means '*:d' and <tag> by itself means <tag>:v\n"
249 "\nIf not specified on the commandline, filterspec is set from ANDROID_LOG_TAGS.\n"
250 "If no filterspec is found, filter defaults to '*:I'\n"
251 "\nIf not specified with -v, format is set from ANDROID_PRINTF_LOG\n"
252 "or defaults to \"brief\"\n\n");
253
254
255
256}
257
258
259} /* namespace android */
260
261static int setLogFormat(const char * formatString)
262{
263 static AndroidLogPrintFormat format;
264
265 format = android_log_formatFromString(formatString);
266
267 if (format == FORMAT_OFF) {
268 // FORMAT_OFF means invalid string
269 return -1;
270 }
271
272 android_log_setPrintFormat(g_logformat, format);
273
274 return 0;
275}
276
277extern "C" void logprint_run_tests(void);
278
279int main (int argc, char **argv)
280{
281 int logfd;
282 int err;
283 int hasSetLogFormat = 0;
284 int clearLog = 0;
285 int getLogSize = 0;
286 int mode = O_RDONLY;
287 char *log_device = strdup("/dev/"LOGGER_LOG_MAIN);
288 const char *forceFilters = NULL;
289
290 g_logformat = android_log_format_new();
291
292 if (argc == 2 && 0 == strcmp(argv[1], "--test")) {
293 logprint_run_tests();
294 exit(0);
295 }
296
297 if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
298 android::show_help(argv[0]);
299 exit(0);
300 }
301
302 for (;;) {
303 int ret;
304
305 ret = getopt(argc, argv, "cdgsQf:r::n:v:b:B");
306
307 if (ret < 0) {
308 break;
309 }
310
311 switch(ret) {
312 case 's':
313 // default to all silent
314 android_log_addFilterRule(g_logformat, "*:s");
315 break;
316
317 case 'c':
318 clearLog = 1;
319 mode = O_WRONLY;
320 break;
321
322 case 'd':
323 mode |= O_NONBLOCK;
324 break;
325
326 case 'g':
327 getLogSize = 1;
328 break;
329
330 case 'b':
331 free(log_device);
332 log_device =
333 (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1);
334 strcpy(log_device, LOG_FILE_DIR);
335 strcat(log_device, optarg);
336
337 android::g_isBinary = (strcmp(optarg, "events") == 0);
338 break;
339
340 case 'B':
341 android::g_printBinary = 1;
342 break;
343
344 case 'f':
345 // redirect output to a file
346
347 android::g_outputFileName = optarg;
348
349 break;
350
351 case 'r':
352 if (optarg == NULL) {
353 android::g_logRotateSizeKBytes
354 = DEFAULT_LOG_ROTATE_SIZE_KBYTES;
355 } else {
356 long logRotateSize;
357 char *lastDigit;
358
359 if (!isdigit(optarg[0])) {
360 fprintf(stderr,"Invalid parameter to -r\n");
361 android::show_help(argv[0]);
362 exit(-1);
363 }
364 android::g_logRotateSizeKBytes = atoi(optarg);
365 }
366 break;
367
368 case 'n':
369 if (!isdigit(optarg[0])) {
370 fprintf(stderr,"Invalid parameter to -r\n");
371 android::show_help(argv[0]);
372 exit(-1);
373 }
374
375 android::g_maxRotatedLogs = atoi(optarg);
376 break;
377
378 case 'v':
379 err = setLogFormat (optarg);
380 if (err < 0) {
381 fprintf(stderr,"Invalid parameter to -v\n");
382 android::show_help(argv[0]);
383 exit(-1);
384 }
385
386 hasSetLogFormat = 1;
387 break;
388
389 case 'Q':
390 /* this is a *hidden* option used to start a version of logcat */
391 /* in an emulated device only. it basically looks for androidboot.logcat= */
392 /* on the kernel command line. If something is found, it extracts a log filter */
393 /* and uses it to run the program. If nothing is found, the program should */
394 /* quit immediately */
395#define KERNEL_OPTION "androidboot.logcat="
396#define CONSOLE_OPTION "androidboot.console="
397 {
398 int fd;
399 char* logcat;
400 char* console;
401 int force_exit = 1;
402 static char cmdline[1024];
403
404 fd = open("/proc/cmdline", O_RDONLY);
405 if (fd >= 0) {
406 int n = read(fd, cmdline, sizeof(cmdline)-1 );
407 if (n < 0) n = 0;
408 cmdline[n] = 0;
409 close(fd);
410 } else {
411 cmdline[0] = 0;
412 }
413
414 logcat = strstr( cmdline, KERNEL_OPTION );
415 console = strstr( cmdline, CONSOLE_OPTION );
416 if (logcat != NULL) {
417 char* p = logcat + sizeof(KERNEL_OPTION)-1;;
418 char* q = strpbrk( p, " \t\n\r" );;
419
420 if (q != NULL)
421 *q = 0;
422
423 forceFilters = p;
424 force_exit = 0;
425 }
426 /* if nothing found or invalid filters, exit quietly */
427 if (force_exit)
428 exit(0);
429
430 /* redirect our output to the emulator console */
431 if (console) {
432 char* p = console + sizeof(CONSOLE_OPTION)-1;
433 char* q = strpbrk( p, " \t\n\r" );
434 char devname[64];
435 int len;
436
437 if (q != NULL) {
438 len = q - p;
439 } else
440 len = strlen(p);
441
442 len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
443 fprintf(stderr, "logcat using %s (%d)\n", devname, len);
444 if (len < (int)sizeof(devname)) {
445 fd = open( devname, O_WRONLY );
446 if (fd >= 0) {
447 dup2(fd, 1);
448 dup2(fd, 2);
449 close(fd);
450 }
451 }
452 }
453 }
454 break;
455
456 default:
457 fprintf(stderr,"Unrecognized Option\n");
458 android::show_help(argv[0]);
459 exit(-1);
460 break;
461 }
462 }
463
464 if (android::g_logRotateSizeKBytes != 0
465 && android::g_outputFileName == NULL
466 ) {
467 fprintf(stderr,"-r requires -f as well\n");
468 android::show_help(argv[0]);
469 exit(-1);
470 }
471
472 android::setupOutput();
473
474 if (hasSetLogFormat == 0) {
475 const char* logFormat = getenv("ANDROID_PRINTF_LOG");
476
477 if (logFormat != NULL) {
478 err = setLogFormat(logFormat);
479
480 if (err < 0) {
481 fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
482 logFormat);
483 }
484 }
485 }
486
487 if (forceFilters) {
488 err = android_log_addFilterString(g_logformat, forceFilters);
489 if (err < 0) {
490 fprintf (stderr, "Invalid filter expression in -logcat option\n");
491 exit(0);
492 }
493 } else if (argc == optind) {
494 // Add from environment variable
495 char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
496
497 if (env_tags_orig != NULL) {
498 err = android_log_addFilterString(g_logformat, env_tags_orig);
499
500 if (err < 0) {
501 fprintf(stderr, "Invalid filter expression in"
502 " ANDROID_LOG_TAGS\n");
503 android::show_help(argv[0]);
504 exit(-1);
505 }
506 }
507 } else {
508 // Add from commandline
509 for (int i = optind ; i < argc ; i++) {
510 err = android_log_addFilterString(g_logformat, argv[i]);
511
512 if (err < 0) {
513 fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]);
514 android::show_help(argv[0]);
515 exit(-1);
516 }
517 }
518 }
519
520 logfd = open(log_device, mode);
521 if (logfd < 0) {
522 fprintf(stderr, "Unable to open log device '%s': %s\n",
523 log_device, strerror(errno));
524 exit(EXIT_FAILURE);
525 }
526
527 if (clearLog) {
528 int ret;
529 ret = android::clearLog(logfd);
530 if (ret) {
531 perror("ioctl");
532 exit(EXIT_FAILURE);
533 }
534 return 0;
535 }
536
537 if (getLogSize) {
538 int size, readable;
539
540 size = android::getLogSize(logfd);
541 if (size < 0) {
542 perror("ioctl");
543 exit(EXIT_FAILURE);
544 }
545
546 readable = android::getLogReadableSize(logfd);
547 if (readable < 0) {
548 perror("ioctl");
549 exit(EXIT_FAILURE);
550 }
551
552 printf("ring buffer is %dKb (%dKb consumed), "
553 "max entry is %db, max payload is %db\n",
554 size / 1024, readable / 1024,
555 (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
556 return 0;
557 }
558
559 //LOG_EVENT_INT(10, 12345);
560 //LOG_EVENT_LONG(11, 0x1122334455667788LL);
561 //LOG_EVENT_STRING(0, "whassup, doc?");
562
563 if (android::g_isBinary)
564 android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
565
566 android::readLogLines(logfd);
567
568 return 0;
569}