• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
26 static 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 
34 namespace android {
35 
36 /* Global Variables */
37 
38 static const char * g_outputFileName = NULL;
39 static int g_logRotateSizeKBytes = 0;                   // 0 means "no log rotation"
40 static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded"
41 static int g_outFD = -1;
42 static off_t g_outByteCount = 0;
43 static int g_isBinary = 0;
44 static int g_printBinary = 0;
45 
46 static EventTagMap* g_eventTagMap = NULL;
47 
openLogFile(const char * pathname)48 static int openLogFile (const char *pathname)
49 {
50     return open(g_outputFileName, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
51 }
52 
rotateLogs()53 static 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 
printBinary(struct logger_entry * buf)96 void 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 
processBuffer(struct logger_entry * buf)106 static 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 
140 error:
141     //fprintf (stderr, "Error processing record\n");
142     return;
143 }
144 
readLogLines(int logfd)145 static 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 
clearLog(int logfd)178 static 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 */
getLogSize(int logfd)184 static 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) */
getLogReadableSize(int logfd)190 static int getLogReadableSize(int logfd)
191 {
192     return ioctl(logfd, LOGGER_GET_LOG_LEN);
193 }
194 
setupOutput()195 static 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 
show_help(const char * cmd)217 static 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 
setLogFormat(const char * formatString)260 static 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 
276 extern "C" void logprint_run_tests(void);
277 
main(int argc,char ** argv)278 int 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