Home | History | Annotate | Download | only in logcat
      1 // Copyright 2006-2014 The Android Open Source Project
      2 
      3 #include <assert.h>
      4 #include <ctype.h>
      5 #include <errno.h>
      6 #include <fcntl.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <stdarg.h>
     10 #include <string.h>
     11 #include <signal.h>
     12 #include <time.h>
     13 #include <unistd.h>
     14 #include <sys/socket.h>
     15 #include <sys/stat.h>
     16 #include <arpa/inet.h>
     17 
     18 #include <cutils/sockets.h>
     19 #include <log/log.h>
     20 #include <log/log_read.h>
     21 #include <log/logger.h>
     22 #include <log/logd.h>
     23 #include <log/logprint.h>
     24 #include <log/event_tag_map.h>
     25 
     26 #define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16
     27 #define DEFAULT_MAX_ROTATED_LOGS 4
     28 
     29 static AndroidLogFormat * g_logformat;
     30 
     31 /* logd prefixes records with a length field */
     32 #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
     33 
     34 struct log_device_t {
     35     const char* device;
     36     bool binary;
     37     struct logger *logger;
     38     struct logger_list *logger_list;
     39     bool printed;
     40     char label;
     41 
     42     log_device_t* next;
     43 
     44     log_device_t(const char* d, bool b, char l) {
     45         device = d;
     46         binary = b;
     47         label = l;
     48         next = NULL;
     49         printed = false;
     50     }
     51 };
     52 
     53 namespace android {
     54 
     55 /* Global Variables */
     56 
     57 static const char * g_outputFileName = NULL;
     58 static int g_logRotateSizeKBytes = 0;                   // 0 means "no log rotation"
     59 static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded"
     60 static int g_outFD = -1;
     61 static off_t g_outByteCount = 0;
     62 static int g_printBinary = 0;
     63 static int g_devCount = 0;
     64 
     65 static EventTagMap* g_eventTagMap = NULL;
     66 
     67 static int openLogFile (const char *pathname)
     68 {
     69     return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
     70 }
     71 
     72 static void rotateLogs()
     73 {
     74     int err;
     75 
     76     // Can't rotate logs if we're not outputting to a file
     77     if (g_outputFileName == NULL) {
     78         return;
     79     }
     80 
     81     close(g_outFD);
     82 
     83     for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
     84         char *file0, *file1;
     85 
     86         asprintf(&file1, "%s.%d", g_outputFileName, i);
     87 
     88         if (i - 1 == 0) {
     89             asprintf(&file0, "%s", g_outputFileName);
     90         } else {
     91             asprintf(&file0, "%s.%d", g_outputFileName, i - 1);
     92         }
     93 
     94         err = rename (file0, file1);
     95 
     96         if (err < 0 && errno != ENOENT) {
     97             perror("while rotating log files");
     98         }
     99 
    100         free(file1);
    101         free(file0);
    102     }
    103 
    104     g_outFD = openLogFile (g_outputFileName);
    105 
    106     if (g_outFD < 0) {
    107         perror ("couldn't open output file");
    108         exit(-1);
    109     }
    110 
    111     g_outByteCount = 0;
    112 
    113 }
    114 
    115 void printBinary(struct log_msg *buf)
    116 {
    117     size_t size = buf->len();
    118 
    119     TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
    120 }
    121 
    122 static void processBuffer(log_device_t* dev, struct log_msg *buf)
    123 {
    124     int bytesWritten = 0;
    125     int err;
    126     AndroidLogEntry entry;
    127     char binaryMsgBuf[1024];
    128 
    129     if (dev->binary) {
    130         err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
    131                                                  g_eventTagMap,
    132                                                  binaryMsgBuf,
    133                                                  sizeof(binaryMsgBuf));
    134         //printf(">>> pri=%d len=%d msg='%s'\n",
    135         //    entry.priority, entry.messageLen, entry.message);
    136     } else {
    137         err = android_log_processLogBuffer(&buf->entry_v1, &entry);
    138     }
    139     if (err < 0) {
    140         goto error;
    141     }
    142 
    143     if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
    144         if (false && g_devCount > 1) {
    145             binaryMsgBuf[0] = dev->label;
    146             binaryMsgBuf[1] = ' ';
    147             bytesWritten = write(g_outFD, binaryMsgBuf, 2);
    148             if (bytesWritten < 0) {
    149                 perror("output error");
    150                 exit(-1);
    151             }
    152         }
    153 
    154         bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
    155 
    156         if (bytesWritten < 0) {
    157             perror("output error");
    158             exit(-1);
    159         }
    160     }
    161 
    162     g_outByteCount += bytesWritten;
    163 
    164     if (g_logRotateSizeKBytes > 0
    165         && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
    166     ) {
    167         rotateLogs();
    168     }
    169 
    170 error:
    171     //fprintf (stderr, "Error processing record\n");
    172     return;
    173 }
    174 
    175 static void maybePrintStart(log_device_t* dev) {
    176     if (!dev->printed) {
    177         dev->printed = true;
    178         if (g_devCount > 1 && !g_printBinary) {
    179             char buf[1024];
    180             snprintf(buf, sizeof(buf), "--------- beginning of %s\n",
    181                      dev->device);
    182             if (write(g_outFD, buf, strlen(buf)) < 0) {
    183                 perror("output error");
    184                 exit(-1);
    185             }
    186         }
    187     }
    188 }
    189 
    190 static void setupOutput()
    191 {
    192 
    193     if (g_outputFileName == NULL) {
    194         g_outFD = STDOUT_FILENO;
    195 
    196     } else {
    197         struct stat statbuf;
    198 
    199         g_outFD = openLogFile (g_outputFileName);
    200 
    201         if (g_outFD < 0) {
    202             perror ("couldn't open output file");
    203             exit(-1);
    204         }
    205 
    206         fstat(g_outFD, &statbuf);
    207 
    208         g_outByteCount = statbuf.st_size;
    209     }
    210 }
    211 
    212 static void show_help(const char *cmd)
    213 {
    214     fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
    215 
    216     fprintf(stderr, "options include:\n"
    217                     "  -s              Set default filter to silent.\n"
    218                     "                  Like specifying filterspec '*:s'\n"
    219                     "  -f <filename>   Log to file. Default to stdout\n"
    220                     "  -r [<kbytes>]   Rotate log every kbytes. (16 if unspecified). Requires -f\n"
    221                     "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
    222                     "  -v <format>     Sets the log print format, where <format> is one of:\n\n"
    223                     "                  brief process tag thread raw time threadtime long\n\n"
    224                     "  -c              clear (flush) the entire log and exit\n"
    225                     "  -d              dump the log and then exit (don't block)\n"
    226                     "  -t <count>      print only the most recent <count> lines (implies -d)\n"
    227                     "  -t '<time>'     print most recent lines since specified time (implies -d)\n"
    228                     "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
    229                     "  -T '<time>'     print most recent lines since specified time (not imply -d)\n"
    230                     "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm'\n"
    231                     "  -g              get the size of the log's ring buffer and exit\n"
    232                     "  -b <buffer>     Request alternate ring buffer, 'main', 'system', 'radio',\n"
    233                     "                  'events', 'crash' or 'all'. Multiple -b parameters are\n"
    234                     "                  allowed and results are interleaved. The default is\n"
    235                     "                  -b main -b system -b crash.\n"
    236                     "  -B              output the log in binary.\n"
    237                     "  -S              output statistics.\n"
    238                     "  -G <size>       set size of log ring buffer, may suffix with K or M.\n"
    239                     "  -p              print prune white and ~black list. Service is specified as\n"
    240                     "                  UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
    241                     "                  with ~, otherwise weighed for longevity if unadorned. All\n"
    242                     "                  other pruning activity is oldest first. Special case ~!\n"
    243                     "                  represents an automatic quicker pruning for the noisiest\n"
    244                     "                  UID as determined by the current statistics.\n"
    245                     "  -P '<list> ...' set prune white and ~black list, using same format as\n"
    246                     "                  printed above. Must be quoted.\n");
    247 
    248     fprintf(stderr,"\nfilterspecs are a series of \n"
    249                    "  <tag>[:priority]\n\n"
    250                    "where <tag> is a log component tag (or * for all) and priority is:\n"
    251                    "  V    Verbose\n"
    252                    "  D    Debug\n"
    253                    "  I    Info\n"
    254                    "  W    Warn\n"
    255                    "  E    Error\n"
    256                    "  F    Fatal\n"
    257                    "  S    Silent (supress all output)\n"
    258                    "\n'*' means '*:d' and <tag> by itself means <tag>:v\n"
    259                    "\nIf not specified on the commandline, filterspec is set from ANDROID_LOG_TAGS.\n"
    260                    "If no filterspec is found, filter defaults to '*:I'\n"
    261                    "\nIf not specified with -v, format is set from ANDROID_PRINTF_LOG\n"
    262                    "or defaults to \"brief\"\n\n");
    263 
    264 
    265 
    266 }
    267 
    268 
    269 } /* namespace android */
    270 
    271 static int setLogFormat(const char * formatString)
    272 {
    273     static AndroidLogPrintFormat format;
    274 
    275     format = android_log_formatFromString(formatString);
    276 
    277     if (format == FORMAT_OFF) {
    278         // FORMAT_OFF means invalid string
    279         return -1;
    280     }
    281 
    282     android_log_setPrintFormat(g_logformat, format);
    283 
    284     return 0;
    285 }
    286 
    287 static const char multipliers[][2] = {
    288     { "" },
    289     { "K" },
    290     { "M" },
    291     { "G" }
    292 };
    293 
    294 static unsigned long value_of_size(unsigned long value)
    295 {
    296     for (unsigned i = 0;
    297             (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
    298             value /= 1024, ++i) ;
    299     return value;
    300 }
    301 
    302 static const char *multiplier_of_size(unsigned long value)
    303 {
    304     unsigned i;
    305     for (i = 0;
    306             (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
    307             value /= 1024, ++i) ;
    308     return multipliers[i];
    309 }
    310 
    311 int main(int argc, char **argv)
    312 {
    313     int err;
    314     int hasSetLogFormat = 0;
    315     int clearLog = 0;
    316     int getLogSize = 0;
    317     unsigned long setLogSize = 0;
    318     int getPruneList = 0;
    319     char *setPruneList = NULL;
    320     int printStatistics = 0;
    321     int mode = O_RDONLY;
    322     const char *forceFilters = NULL;
    323     log_device_t* devices = NULL;
    324     log_device_t* dev;
    325     bool needBinary = false;
    326     struct logger_list *logger_list;
    327     unsigned int tail_lines = 0;
    328     log_time tail_time(log_time::EPOCH);
    329 
    330     signal(SIGPIPE, exit);
    331 
    332     g_logformat = android_log_format_new();
    333 
    334     if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
    335         android::show_help(argv[0]);
    336         exit(0);
    337     }
    338 
    339     for (;;) {
    340         int ret;
    341 
    342         ret = getopt(argc, argv, "cdt:T:gG:sQf:r:n:v:b:BSpP:");
    343 
    344         if (ret < 0) {
    345             break;
    346         }
    347 
    348         switch(ret) {
    349             case 's':
    350                 // default to all silent
    351                 android_log_addFilterRule(g_logformat, "*:s");
    352             break;
    353 
    354             case 'c':
    355                 clearLog = 1;
    356                 mode = O_WRONLY;
    357             break;
    358 
    359             case 'd':
    360                 mode = O_RDONLY | O_NDELAY;
    361             break;
    362 
    363             case 't':
    364                 mode = O_RDONLY | O_NDELAY;
    365                 /* FALLTHRU */
    366             case 'T':
    367                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
    368                     char *cp = tail_time.strptime(optarg,
    369                                                   log_time::default_format);
    370                     if (!cp) {
    371                         fprintf(stderr,
    372                                 "ERROR: -%c \"%s\" not in \"%s\" time format\n",
    373                                 ret, optarg, log_time::default_format);
    374                         exit(1);
    375                     }
    376                     if (*cp) {
    377                         char c = *cp;
    378                         *cp = '\0';
    379                         fprintf(stderr,
    380                                 "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
    381                                 ret, optarg, c, cp + 1);
    382                         *cp = c;
    383                     }
    384                 } else {
    385                     tail_lines = atoi(optarg);
    386                     if (!tail_lines) {
    387                         fprintf(stderr,
    388                                 "WARNING: -%c %s invalid, setting to 1\n",
    389                                 ret, optarg);
    390                         tail_lines = 1;
    391                     }
    392                 }
    393             break;
    394 
    395             case 'g':
    396                 getLogSize = 1;
    397             break;
    398 
    399             case 'G': {
    400                 // would use atol if not for the multiplier
    401                 char *cp = optarg;
    402                 setLogSize = 0;
    403                 while (('0' <= *cp) && (*cp <= '9')) {
    404                     setLogSize *= 10;
    405                     setLogSize += *cp - '0';
    406                     ++cp;
    407                 }
    408 
    409                 switch(*cp) {
    410                 case 'g':
    411                 case 'G':
    412                     setLogSize *= 1024;
    413                 /* FALLTHRU */
    414                 case 'm':
    415                 case 'M':
    416                     setLogSize *= 1024;
    417                 /* FALLTHRU */
    418                 case 'k':
    419                 case 'K':
    420                     setLogSize *= 1024;
    421                 /* FALLTHRU */
    422                 case '\0':
    423                 break;
    424 
    425                 default:
    426                     setLogSize = 0;
    427                 }
    428 
    429                 if (!setLogSize) {
    430                     fprintf(stderr, "ERROR: -G <num><multiplier>\n");
    431                     exit(1);
    432                 }
    433             }
    434             break;
    435 
    436             case 'p':
    437                 getPruneList = 1;
    438             break;
    439 
    440             case 'P':
    441                 setPruneList = optarg;
    442             break;
    443 
    444             case 'b': {
    445                 if (strcmp(optarg, "all") == 0) {
    446                     while (devices) {
    447                         dev = devices;
    448                         devices = dev->next;
    449                         delete dev;
    450                     }
    451 
    452                     dev = devices = new log_device_t("main", false, 'm');
    453                     android::g_devCount = 1;
    454                     if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
    455                         dev->next = new log_device_t("system", false, 's');
    456                         if (dev->next) {
    457                             dev = dev->next;
    458                             android::g_devCount++;
    459                         }
    460                     }
    461                     if (android_name_to_log_id("radio") == LOG_ID_RADIO) {
    462                         dev->next = new log_device_t("radio", false, 'r');
    463                         if (dev->next) {
    464                             dev = dev->next;
    465                             android::g_devCount++;
    466                         }
    467                     }
    468                     if (android_name_to_log_id("events") == LOG_ID_EVENTS) {
    469                         dev->next = new log_device_t("events", true, 'e');
    470                         if (dev->next) {
    471                             dev = dev->next;
    472                             android::g_devCount++;
    473                             needBinary = true;
    474                         }
    475                     }
    476                     if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
    477                         dev->next = new log_device_t("crash", false, 'c');
    478                         if (dev->next) {
    479                             android::g_devCount++;
    480                         }
    481                     }
    482                     break;
    483                 }
    484 
    485                 bool binary = strcmp(optarg, "events") == 0;
    486                 if (binary) {
    487                     needBinary = true;
    488                 }
    489 
    490                 if (devices) {
    491                     dev = devices;
    492                     while (dev->next) {
    493                         dev = dev->next;
    494                     }
    495                     dev->next = new log_device_t(optarg, binary, optarg[0]);
    496                 } else {
    497                     devices = new log_device_t(optarg, binary, optarg[0]);
    498                 }
    499                 android::g_devCount++;
    500             }
    501             break;
    502 
    503             case 'B':
    504                 android::g_printBinary = 1;
    505             break;
    506 
    507             case 'f':
    508                 // redirect output to a file
    509 
    510                 android::g_outputFileName = optarg;
    511 
    512             break;
    513 
    514             case 'r':
    515                 if (optarg == NULL) {
    516                     android::g_logRotateSizeKBytes
    517                                 = DEFAULT_LOG_ROTATE_SIZE_KBYTES;
    518                 } else {
    519                     if (!isdigit(optarg[0])) {
    520                         fprintf(stderr,"Invalid parameter to -r\n");
    521                         android::show_help(argv[0]);
    522                         exit(-1);
    523                     }
    524                     android::g_logRotateSizeKBytes = atoi(optarg);
    525                 }
    526             break;
    527 
    528             case 'n':
    529                 if (!isdigit(optarg[0])) {
    530                     fprintf(stderr,"Invalid parameter to -r\n");
    531                     android::show_help(argv[0]);
    532                     exit(-1);
    533                 }
    534 
    535                 android::g_maxRotatedLogs = atoi(optarg);
    536             break;
    537 
    538             case 'v':
    539                 err = setLogFormat (optarg);
    540                 if (err < 0) {
    541                     fprintf(stderr,"Invalid parameter to -v\n");
    542                     android::show_help(argv[0]);
    543                     exit(-1);
    544                 }
    545 
    546                 hasSetLogFormat = 1;
    547             break;
    548 
    549             case 'Q':
    550                 /* this is a *hidden* option used to start a version of logcat                 */
    551                 /* in an emulated device only. it basically looks for androidboot.logcat=      */
    552                 /* on the kernel command line. If something is found, it extracts a log filter */
    553                 /* and uses it to run the program. If nothing is found, the program should     */
    554                 /* quit immediately                                                            */
    555 #define  KERNEL_OPTION  "androidboot.logcat="
    556 #define  CONSOLE_OPTION "androidboot.console="
    557                 {
    558                     int          fd;
    559                     char*        logcat;
    560                     char*        console;
    561                     int          force_exit = 1;
    562                     static char  cmdline[1024];
    563 
    564                     fd = open("/proc/cmdline", O_RDONLY);
    565                     if (fd >= 0) {
    566                         int  n = read(fd, cmdline, sizeof(cmdline)-1 );
    567                         if (n < 0) n = 0;
    568                         cmdline[n] = 0;
    569                         close(fd);
    570                     } else {
    571                         cmdline[0] = 0;
    572                     }
    573 
    574                     logcat  = strstr( cmdline, KERNEL_OPTION );
    575                     console = strstr( cmdline, CONSOLE_OPTION );
    576                     if (logcat != NULL) {
    577                         char*  p = logcat + sizeof(KERNEL_OPTION)-1;;
    578                         char*  q = strpbrk( p, " \t\n\r" );;
    579 
    580                         if (q != NULL)
    581                             *q = 0;
    582 
    583                         forceFilters = p;
    584                         force_exit   = 0;
    585                     }
    586                     /* if nothing found or invalid filters, exit quietly */
    587                     if (force_exit)
    588                         exit(0);
    589 
    590                     /* redirect our output to the emulator console */
    591                     if (console) {
    592                         char*  p = console + sizeof(CONSOLE_OPTION)-1;
    593                         char*  q = strpbrk( p, " \t\n\r" );
    594                         char   devname[64];
    595                         int    len;
    596 
    597                         if (q != NULL) {
    598                             len = q - p;
    599                         } else
    600                             len = strlen(p);
    601 
    602                         len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
    603                         fprintf(stderr, "logcat using %s (%d)\n", devname, len);
    604                         if (len < (int)sizeof(devname)) {
    605                             fd = open( devname, O_WRONLY );
    606                             if (fd >= 0) {
    607                                 dup2(fd, 1);
    608                                 dup2(fd, 2);
    609                                 close(fd);
    610                             }
    611                         }
    612                     }
    613                 }
    614                 break;
    615 
    616             case 'S':
    617                 printStatistics = 1;
    618                 break;
    619 
    620             default:
    621                 fprintf(stderr,"Unrecognized Option\n");
    622                 android::show_help(argv[0]);
    623                 exit(-1);
    624             break;
    625         }
    626     }
    627 
    628     if (!devices) {
    629         dev = devices = new log_device_t("main", false, 'm');
    630         android::g_devCount = 1;
    631         if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
    632             dev = dev->next = new log_device_t("system", false, 's');
    633             android::g_devCount++;
    634         }
    635         if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
    636             dev = dev->next = new log_device_t("crash", false, 'c');
    637             android::g_devCount++;
    638         }
    639     }
    640 
    641     if (android::g_logRotateSizeKBytes != 0
    642         && android::g_outputFileName == NULL
    643     ) {
    644         fprintf(stderr,"-r requires -f as well\n");
    645         android::show_help(argv[0]);
    646         exit(-1);
    647     }
    648 
    649     android::setupOutput();
    650 
    651     if (hasSetLogFormat == 0) {
    652         const char* logFormat = getenv("ANDROID_PRINTF_LOG");
    653 
    654         if (logFormat != NULL) {
    655             err = setLogFormat(logFormat);
    656 
    657             if (err < 0) {
    658                 fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
    659                                     logFormat);
    660             }
    661         }
    662     }
    663 
    664     if (forceFilters) {
    665         err = android_log_addFilterString(g_logformat, forceFilters);
    666         if (err < 0) {
    667             fprintf (stderr, "Invalid filter expression in -logcat option\n");
    668             exit(0);
    669         }
    670     } else if (argc == optind) {
    671         // Add from environment variable
    672         char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
    673 
    674         if (env_tags_orig != NULL) {
    675             err = android_log_addFilterString(g_logformat, env_tags_orig);
    676 
    677             if (err < 0) {
    678                 fprintf(stderr, "Invalid filter expression in"
    679                                     " ANDROID_LOG_TAGS\n");
    680                 android::show_help(argv[0]);
    681                 exit(-1);
    682             }
    683         }
    684     } else {
    685         // Add from commandline
    686         for (int i = optind ; i < argc ; i++) {
    687             err = android_log_addFilterString(g_logformat, argv[i]);
    688 
    689             if (err < 0) {
    690                 fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]);
    691                 android::show_help(argv[0]);
    692                 exit(-1);
    693             }
    694         }
    695     }
    696 
    697     dev = devices;
    698     if (tail_time != log_time::EPOCH) {
    699         logger_list = android_logger_list_alloc_time(mode, tail_time, 0);
    700     } else {
    701         logger_list = android_logger_list_alloc(mode, tail_lines, 0);
    702     }
    703     while (dev) {
    704         dev->logger_list = logger_list;
    705         dev->logger = android_logger_open(logger_list,
    706                                           android_name_to_log_id(dev->device));
    707         if (!dev->logger) {
    708             fprintf(stderr, "Unable to open log device '%s'\n", dev->device);
    709             exit(EXIT_FAILURE);
    710         }
    711 
    712         if (clearLog) {
    713             int ret;
    714             ret = android_logger_clear(dev->logger);
    715             if (ret) {
    716                 perror("failed to clear the log");
    717                 exit(EXIT_FAILURE);
    718             }
    719         }
    720 
    721         if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
    722             perror("failed to set the log size");
    723             exit(EXIT_FAILURE);
    724         }
    725 
    726         if (getLogSize) {
    727             long size, readable;
    728 
    729             size = android_logger_get_log_size(dev->logger);
    730             if (size < 0) {
    731                 perror("failed to get the log size");
    732                 exit(EXIT_FAILURE);
    733             }
    734 
    735             readable = android_logger_get_log_readable_size(dev->logger);
    736             if (readable < 0) {
    737                 perror("failed to get the readable log size");
    738                 exit(EXIT_FAILURE);
    739             }
    740 
    741             printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
    742                    "max entry is %db, max payload is %db\n", dev->device,
    743                    value_of_size(size), multiplier_of_size(size),
    744                    value_of_size(readable), multiplier_of_size(readable),
    745                    (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
    746         }
    747 
    748         dev = dev->next;
    749     }
    750 
    751     if (setPruneList) {
    752         size_t len = strlen(setPruneList) + 32; // margin to allow rc
    753         char *buf = (char *) malloc(len);
    754 
    755         strcpy(buf, setPruneList);
    756         int ret = android_logger_set_prune_list(logger_list, buf, len);
    757         free(buf);
    758 
    759         if (ret) {
    760             perror("failed to set the prune list");
    761             exit(EXIT_FAILURE);
    762         }
    763     }
    764 
    765     if (printStatistics || getPruneList) {
    766         size_t len = 8192;
    767         char *buf;
    768 
    769         for(int retry = 32;
    770                 (retry >= 0) && ((buf = new char [len]));
    771                 delete [] buf, --retry) {
    772             if (getPruneList) {
    773                 android_logger_get_prune_list(logger_list, buf, len);
    774             } else {
    775                 android_logger_get_statistics(logger_list, buf, len);
    776             }
    777             buf[len-1] = '\0';
    778             size_t ret = atol(buf) + 1;
    779             if (ret < 4) {
    780                 delete [] buf;
    781                 buf = NULL;
    782                 break;
    783             }
    784             bool check = ret <= len;
    785             len = ret;
    786             if (check) {
    787                 break;
    788             }
    789         }
    790 
    791         if (!buf) {
    792             perror("failed to read data");
    793             exit(EXIT_FAILURE);
    794         }
    795 
    796         // remove trailing FF
    797         char *cp = buf + len - 1;
    798         *cp = '\0';
    799         bool truncated = *--cp != '\f';
    800         if (!truncated) {
    801             *cp = '\0';
    802         }
    803 
    804         // squash out the byte count
    805         cp = buf;
    806         if (!truncated) {
    807             while (isdigit(*cp)) {
    808                 ++cp;
    809             }
    810             if (*cp == '\n') {
    811                 ++cp;
    812             }
    813         }
    814 
    815         printf("%s", cp);
    816         delete [] buf;
    817         exit(0);
    818     }
    819 
    820 
    821     if (getLogSize) {
    822         exit(0);
    823     }
    824     if (setLogSize || setPruneList) {
    825         exit(0);
    826     }
    827     if (clearLog) {
    828         exit(0);
    829     }
    830 
    831     //LOG_EVENT_INT(10, 12345);
    832     //LOG_EVENT_LONG(11, 0x1122334455667788LL);
    833     //LOG_EVENT_STRING(0, "whassup, doc?");
    834 
    835     if (needBinary)
    836         android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
    837 
    838     while (1) {
    839         struct log_msg log_msg;
    840         int ret = android_logger_list_read(logger_list, &log_msg);
    841 
    842         if (ret == 0) {
    843             fprintf(stderr, "read: Unexpected EOF!\n");
    844             exit(EXIT_FAILURE);
    845         }
    846 
    847         if (ret < 0) {
    848             if (ret == -EAGAIN) {
    849                 break;
    850             }
    851 
    852             if (ret == -EIO) {
    853                 fprintf(stderr, "read: Unexpected EOF!\n");
    854                 exit(EXIT_FAILURE);
    855             }
    856             if (ret == -EINVAL) {
    857                 fprintf(stderr, "read: unexpected length.\n");
    858                 exit(EXIT_FAILURE);
    859             }
    860             perror("logcat read failure");
    861             exit(EXIT_FAILURE);
    862         }
    863 
    864         for(dev = devices; dev; dev = dev->next) {
    865             if (android_name_to_log_id(dev->device) == log_msg.id()) {
    866                 break;
    867             }
    868         }
    869         if (!dev) {
    870             fprintf(stderr, "read: Unexpected log ID!\n");
    871             exit(EXIT_FAILURE);
    872         }
    873 
    874         android::maybePrintStart(dev);
    875         if (android::g_printBinary) {
    876             android::printBinary(&log_msg);
    877         } else {
    878             android::processBuffer(dev, &log_msg);
    879         }
    880     }
    881 
    882     android_logger_list_free(logger_list);
    883 
    884     return 0;
    885 }
    886