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