Home | History | Annotate | Download | only in logcat
      1 // Copyright 2006-2015 The Android Open Source Project
      2 
      3 #include <arpa/inet.h>
      4 #include <assert.h>
      5 #include <ctype.h>
      6 #include <dirent.h>
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <getopt.h>
     10 #include <math.h>
     11 #include <sched.h>
     12 #include <signal.h>
     13 #include <stdarg.h>
     14 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include <string.h>
     17 #include <sys/cdefs.h>
     18 #include <sys/resource.h>
     19 #include <sys/socket.h>
     20 #include <sys/stat.h>
     21 #include <sys/types.h>
     22 #include <time.h>
     23 #include <unistd.h>
     24 
     25 #include <memory>
     26 #include <string>
     27 
     28 #include <android-base/file.h>
     29 #include <android-base/strings.h>
     30 #include <cutils/sched_policy.h>
     31 #include <cutils/sockets.h>
     32 #include <log/event_tag_map.h>
     33 #include <log/log.h>
     34 #include <log/log_read.h>
     35 #include <log/logd.h>
     36 #include <log/logger.h>
     37 #include <log/logprint.h>
     38 #include <utils/threads.h>
     39 
     40 #include <pcrecpp.h>
     41 
     42 #define DEFAULT_MAX_ROTATED_LOGS 4
     43 
     44 static AndroidLogFormat * g_logformat;
     45 
     46 /* logd prefixes records with a length field */
     47 #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
     48 
     49 struct log_device_t {
     50     const char* device;
     51     bool binary;
     52     struct logger *logger;
     53     struct logger_list *logger_list;
     54     bool printed;
     55 
     56     log_device_t* next;
     57 
     58     log_device_t(const char* d, bool b) {
     59         device = d;
     60         binary = b;
     61         next = NULL;
     62         printed = false;
     63         logger = NULL;
     64         logger_list = NULL;
     65     }
     66 };
     67 
     68 namespace android {
     69 
     70 /* Global Variables */
     71 
     72 static const char * g_outputFileName;
     73 // 0 means "no log rotation"
     74 static size_t g_logRotateSizeKBytes;
     75 // 0 means "unbounded"
     76 static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
     77 static int g_outFD = -1;
     78 static size_t g_outByteCount;
     79 static int g_printBinary;
     80 static int g_devCount;                              // >1 means multiple
     81 static pcrecpp::RE* g_regex;
     82 // 0 means "infinite"
     83 static size_t g_maxCount;
     84 static size_t g_printCount;
     85 static bool g_printItAnyways;
     86 
     87 // if showHelp is set, newline required in fmt statement to transition to usage
     88 __noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
     89 
     90 static int openLogFile (const char *pathname)
     91 {
     92     return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
     93 }
     94 
     95 static void rotateLogs()
     96 {
     97     int err;
     98 
     99     // Can't rotate logs if we're not outputting to a file
    100     if (g_outputFileName == NULL) {
    101         return;
    102     }
    103 
    104     close(g_outFD);
    105 
    106     // Compute the maximum number of digits needed to count up to g_maxRotatedLogs in decimal.
    107     // eg: g_maxRotatedLogs == 30 -> log10(30) == 1.477 -> maxRotationCountDigits == 2
    108     int maxRotationCountDigits =
    109             (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
    110 
    111     for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
    112         char *file0, *file1;
    113 
    114         asprintf(&file1, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
    115 
    116         if (i - 1 == 0) {
    117             asprintf(&file0, "%s", g_outputFileName);
    118         } else {
    119             asprintf(&file0, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
    120         }
    121 
    122         if (!file0 || !file1) {
    123             perror("while rotating log files");
    124             break;
    125         }
    126 
    127         err = rename(file0, file1);
    128 
    129         if (err < 0 && errno != ENOENT) {
    130             perror("while rotating log files");
    131         }
    132 
    133         free(file1);
    134         free(file0);
    135     }
    136 
    137     g_outFD = openLogFile(g_outputFileName);
    138 
    139     if (g_outFD < 0) {
    140         logcat_panic(false, "couldn't open output file");
    141     }
    142 
    143     g_outByteCount = 0;
    144 
    145 }
    146 
    147 void printBinary(struct log_msg *buf)
    148 {
    149     size_t size = buf->len();
    150 
    151     TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
    152 }
    153 
    154 static bool regexOk(const AndroidLogEntry& entry)
    155 {
    156     if (!g_regex) {
    157         return true;
    158     }
    159 
    160     std::string messageString(entry.message, entry.messageLen);
    161 
    162     return g_regex->PartialMatch(messageString);
    163 }
    164 
    165 static void processBuffer(log_device_t* dev, struct log_msg *buf)
    166 {
    167     int bytesWritten = 0;
    168     int err;
    169     AndroidLogEntry entry;
    170     char binaryMsgBuf[1024];
    171 
    172     if (dev->binary) {
    173         static bool hasOpenedEventTagMap = false;
    174         static EventTagMap *eventTagMap = NULL;
    175 
    176         if (!eventTagMap && !hasOpenedEventTagMap) {
    177             eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);
    178             hasOpenedEventTagMap = true;
    179         }
    180         err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
    181                                                  eventTagMap,
    182                                                  binaryMsgBuf,
    183                                                  sizeof(binaryMsgBuf));
    184         //printf(">>> pri=%d len=%d msg='%s'\n",
    185         //    entry.priority, entry.messageLen, entry.message);
    186     } else {
    187         err = android_log_processLogBuffer(&buf->entry_v1, &entry);
    188     }
    189     if (err < 0) {
    190         goto error;
    191     }
    192 
    193     if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {
    194         bool match = regexOk(entry);
    195 
    196         g_printCount += match;
    197         if (match || g_printItAnyways) {
    198             bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
    199 
    200             if (bytesWritten < 0) {
    201                 logcat_panic(false, "output error");
    202             }
    203         }
    204     }
    205 
    206     g_outByteCount += bytesWritten;
    207 
    208     if (g_logRotateSizeKBytes > 0
    209         && (g_outByteCount / 1024) >= g_logRotateSizeKBytes
    210     ) {
    211         rotateLogs();
    212     }
    213 
    214 error:
    215     //fprintf (stderr, "Error processing record\n");
    216     return;
    217 }
    218 
    219 static void maybePrintStart(log_device_t* dev, bool printDividers) {
    220     if (!dev->printed || printDividers) {
    221         if (g_devCount > 1 && !g_printBinary) {
    222             char buf[1024];
    223             snprintf(buf, sizeof(buf), "--------- %s %s\n",
    224                      dev->printed ? "switch to" : "beginning of",
    225                      dev->device);
    226             if (write(g_outFD, buf, strlen(buf)) < 0) {
    227                 logcat_panic(false, "output error");
    228             }
    229         }
    230         dev->printed = true;
    231     }
    232 }
    233 
    234 static void setupOutput()
    235 {
    236 
    237     if (g_outputFileName == NULL) {
    238         g_outFD = STDOUT_FILENO;
    239 
    240     } else {
    241         if (set_sched_policy(0, SP_BACKGROUND) < 0) {
    242             fprintf(stderr, "failed to set background scheduling policy\n");
    243         }
    244 
    245         struct sched_param param;
    246         memset(&param, 0, sizeof(param));
    247         if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) {
    248             fprintf(stderr, "failed to set to batch scheduler\n");
    249         }
    250 
    251         if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
    252             fprintf(stderr, "failed set to priority\n");
    253         }
    254 
    255         g_outFD = openLogFile (g_outputFileName);
    256 
    257         if (g_outFD < 0) {
    258             logcat_panic(false, "couldn't open output file");
    259         }
    260 
    261         struct stat statbuf;
    262         if (fstat(g_outFD, &statbuf) == -1) {
    263             close(g_outFD);
    264             logcat_panic(false, "couldn't get output file stat\n");
    265         }
    266 
    267         if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
    268             close(g_outFD);
    269             logcat_panic(false, "invalid output file stat\n");
    270         }
    271 
    272         g_outByteCount = statbuf.st_size;
    273     }
    274 }
    275 
    276 static void show_help(const char *cmd)
    277 {
    278     fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd);
    279 
    280     fprintf(stderr, "options include:\n"
    281                     "  -s              Set default filter to silent.\n"
    282                     "                  Like specifying filterspec '*:S'\n"
    283                     "  -f <filename>   Log to file. Default is stdout\n"
    284                     "  --file=<filename>\n"
    285                     "  -r <kbytes>     Rotate log every kbytes. Requires -f\n"
    286                     "  --rotate-kbytes=<kbytes>\n"
    287                     "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
    288                     "  --rotate-count=<count>\n"
    289                     "  -v <format>     Sets the log print format, where <format> is:\n"
    290                     "  --format=<format>\n"
    291                     "                      brief color epoch long monotonic printable process raw\n"
    292                     "                      tag thread threadtime time uid usec UTC year zone\n\n"
    293                     "  -D              print dividers between each log buffer\n"
    294                     "  --dividers\n"
    295                     "  -c              clear (flush) the entire log and exit\n"
    296                     "  --clear\n"
    297                     "  -d              dump the log and then exit (don't block)\n"
    298                     "  -e <expr>       only print lines where the log message matches <expr>\n"
    299                     "  --regex <expr>  where <expr> is a regular expression\n"
    300                     "  -m <count>      quit after printing <count> lines. This is meant to be\n"
    301                     "  --max-count=<count> paired with --regex, but will work on its own.\n"
    302                     "  --print         paired with --regex and --max-count to let content bypass\n"
    303                     "                  regex filter but still stop at number of matches.\n"
    304                     "  -t <count>      print only the most recent <count> lines (implies -d)\n"
    305                     "  -t '<time>'     print most recent lines since specified time (implies -d)\n"
    306                     "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
    307                     "  -T '<time>'     print most recent lines since specified time (not imply -d)\n"
    308                     "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
    309                     "                  'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
    310                     "  -g              get the size of the log's ring buffer and exit\n"
    311                     "  --buffer-size\n"
    312                     "  -G <size>       set size of log ring buffer, may suffix with K or M.\n"
    313                     "  --buffer-size=<size>\n"
    314                     "  -L              dump logs from prior to last reboot\n"
    315                     "  --last\n"
    316                     // Leave security (Device Owner only installations) and
    317                     // kernel (userdebug and eng) buffers undocumented.
    318                     "  -b <buffer>     Request alternate ring buffer, 'main', 'system', 'radio',\n"
    319                     "  --buffer=<buffer> 'events', 'crash', 'default' or 'all'. Multiple -b\n"
    320                     "                  parameters are allowed and results are interleaved. The\n"
    321                     "                  default is -b main -b system -b crash.\n"
    322                     "  -B              output the log in binary.\n"
    323                     "  --binary\n"
    324                     "  -S              output statistics.\n"
    325                     "  --statistics\n"
    326                     "  -p              print prune white and ~black list. Service is specified as\n"
    327                     "  --prune         UID, UID/PID or /PID. Weighed for quicker pruning if prefix\n"
    328                     "                  with ~, otherwise weighed for longevity if unadorned. All\n"
    329                     "                  other pruning activity is oldest first. Special case ~!\n"
    330                     "                  represents an automatic quicker pruning for the noisiest\n"
    331                     "                  UID as determined by the current statistics.\n"
    332                     "  -P '<list> ...' set prune white and ~black list, using same format as\n"
    333                     "  --prune='<list> ...'  printed above. Must be quoted.\n"
    334                     "  --pid=<pid>     Only prints logs from the given pid.\n"
    335                     // Check ANDROID_LOG_WRAP_DEFAULT_TIMEOUT value
    336                     "  --wrap          Sleep for 2 hours or when buffer about to wrap whichever\n"
    337                     "                  comes first. Improves efficiency of polling by providing\n"
    338                     "                  an about-to-wrap wakeup.\n");
    339 
    340     fprintf(stderr,"\nfilterspecs are a series of \n"
    341                    "  <tag>[:priority]\n\n"
    342                    "where <tag> is a log component tag (or * for all) and priority is:\n"
    343                    "  V    Verbose (default for <tag>)\n"
    344                    "  D    Debug (default for '*')\n"
    345                    "  I    Info\n"
    346                    "  W    Warn\n"
    347                    "  E    Error\n"
    348                    "  F    Fatal\n"
    349                    "  S    Silent (suppress all output)\n"
    350                    "\n'*' by itself means '*:D' and <tag> by itself means <tag>:V.\n"
    351                    "If no '*' filterspec or -s on command line, all filter defaults to '*:V'.\n"
    352                    "eg: '*:S <tag>' prints only <tag>, '<tag>:S' suppresses all <tag> log messages.\n"
    353                    "\nIf not specified on the command line, filterspec is set from ANDROID_LOG_TAGS.\n"
    354                    "\nIf not specified with -v on command line, format is set from ANDROID_PRINTF_LOG\n"
    355                    "or defaults to \"threadtime\"\n\n");
    356 }
    357 
    358 static int setLogFormat(const char * formatString)
    359 {
    360     static AndroidLogPrintFormat format;
    361 
    362     format = android_log_formatFromString(formatString);
    363 
    364     if (format == FORMAT_OFF) {
    365         // FORMAT_OFF means invalid string
    366         return -1;
    367     }
    368 
    369     return android_log_setPrintFormat(g_logformat, format);
    370 }
    371 
    372 static const char multipliers[][2] = {
    373     { "" },
    374     { "K" },
    375     { "M" },
    376     { "G" }
    377 };
    378 
    379 static unsigned long value_of_size(unsigned long value)
    380 {
    381     for (unsigned i = 0;
    382             (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
    383             value /= 1024, ++i) ;
    384     return value;
    385 }
    386 
    387 static const char *multiplier_of_size(unsigned long value)
    388 {
    389     unsigned i;
    390     for (i = 0;
    391             (i < sizeof(multipliers)/sizeof(multipliers[0])) && (value >= 1024);
    392             value /= 1024, ++i) ;
    393     return multipliers[i];
    394 }
    395 
    396 /*String to unsigned int, returns -1 if it fails*/
    397 static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0,
    398                         size_t max = SIZE_MAX)
    399 {
    400     if (!ptr) {
    401         return false;
    402     }
    403 
    404     char *endp;
    405     errno = 0;
    406     size_t ret = (size_t)strtoll(ptr, &endp, 0);
    407 
    408     if (endp[0] || errno) {
    409         return false;
    410     }
    411 
    412     if ((ret > max) || (ret < min)) {
    413         return false;
    414     }
    415 
    416     *val = ret;
    417     return true;
    418 }
    419 
    420 static void logcat_panic(bool showHelp, const char *fmt, ...)
    421 {
    422     va_list  args;
    423     va_start(args, fmt);
    424     vfprintf(stderr, fmt,  args);
    425     va_end(args);
    426 
    427     if (showHelp) {
    428        show_help(getprogname());
    429     }
    430 
    431     exit(EXIT_FAILURE);
    432 }
    433 
    434 static char *parseTime(log_time &t, const char *cp) {
    435 
    436     char *ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
    437     if (ep) {
    438         return ep;
    439     }
    440     ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
    441     if (ep) {
    442         return ep;
    443     }
    444     return t.strptime(cp, "%s.%q");
    445 }
    446 
    447 // Find last logged line in gestalt of all matching existing output files
    448 static log_time lastLogTime(char *outputFileName) {
    449     log_time retval(log_time::EPOCH);
    450     if (!outputFileName) {
    451         return retval;
    452     }
    453 
    454     std::string directory;
    455     char *file = strrchr(outputFileName, '/');
    456     if (!file) {
    457         directory = ".";
    458         file = outputFileName;
    459     } else {
    460         *file = '\0';
    461         directory = outputFileName;
    462         *file = '/';
    463         ++file;
    464     }
    465 
    466     std::unique_ptr<DIR, int(*)(DIR*)>
    467             dir(opendir(directory.c_str()), closedir);
    468     if (!dir.get()) {
    469         return retval;
    470     }
    471 
    472     clockid_t clock_type = android_log_clockid();
    473     log_time now(clock_type);
    474     bool monotonic = clock_type == CLOCK_MONOTONIC;
    475 
    476     size_t len = strlen(file);
    477     log_time modulo(0, NS_PER_SEC);
    478     struct dirent *dp;
    479 
    480     while ((dp = readdir(dir.get())) != NULL) {
    481         if ((dp->d_type != DT_REG)
    482                 // If we are using realtime, check all files that match the
    483                 // basename for latest time. If we are using monotonic time
    484                 // then only check the main file because time cycles on
    485                 // every reboot.
    486                 || strncmp(dp->d_name, file, len + monotonic)
    487                 || (dp->d_name[len]
    488                     && ((dp->d_name[len] != '.')
    489                         || !isdigit(dp->d_name[len+1])))) {
    490             continue;
    491         }
    492 
    493         std::string file_name = directory;
    494         file_name += "/";
    495         file_name += dp->d_name;
    496         std::string file;
    497         if (!android::base::ReadFileToString(file_name, &file)) {
    498             continue;
    499         }
    500 
    501         bool found = false;
    502         for (const auto& line : android::base::Split(file, "\n")) {
    503             log_time t(log_time::EPOCH);
    504             char *ep = parseTime(t, line.c_str());
    505             if (!ep || (*ep != ' ')) {
    506                 continue;
    507             }
    508             // determine the time precision of the logs (eg: msec or usec)
    509             for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
    510                 if (t.tv_nsec % (mod * 10)) {
    511                     modulo.tv_nsec = mod;
    512                     break;
    513                 }
    514             }
    515             // We filter any times later than current as we may not have the
    516             // year stored with each log entry. Also, since it is possible for
    517             // entries to be recorded out of order (very rare) we select the
    518             // maximum we find just in case.
    519             if ((t < now) && (t > retval)) {
    520                 retval = t;
    521                 found = true;
    522             }
    523         }
    524         // We count on the basename file to be the definitive end, so stop here.
    525         if (!dp->d_name[len] && found) {
    526             break;
    527         }
    528     }
    529     if (retval == log_time::EPOCH) {
    530         return retval;
    531     }
    532     // tail_time prints matching or higher, round up by the modulo to prevent
    533     // a replay of the last entry we have just checked.
    534     retval += modulo;
    535     return retval;
    536 }
    537 
    538 } /* namespace android */
    539 
    540 
    541 int main(int argc, char **argv)
    542 {
    543     using namespace android;
    544     int err;
    545     int hasSetLogFormat = 0;
    546     int clearLog = 0;
    547     int getLogSize = 0;
    548     unsigned long setLogSize = 0;
    549     int getPruneList = 0;
    550     char *setPruneList = NULL;
    551     int printStatistics = 0;
    552     int mode = ANDROID_LOG_RDONLY;
    553     const char *forceFilters = NULL;
    554     log_device_t* devices = NULL;
    555     log_device_t* dev;
    556     bool printDividers = false;
    557     struct logger_list *logger_list;
    558     size_t tail_lines = 0;
    559     log_time tail_time(log_time::EPOCH);
    560     size_t pid = 0;
    561     bool got_t = false;
    562 
    563     signal(SIGPIPE, exit);
    564 
    565     g_logformat = android_log_format_new();
    566 
    567     if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
    568         show_help(argv[0]);
    569         return EXIT_SUCCESS;
    570     }
    571 
    572     for (;;) {
    573         int ret;
    574 
    575         int option_index = 0;
    576         // list of long-argument only strings for later comparison
    577         static const char pid_str[] = "pid";
    578         static const char wrap_str[] = "wrap";
    579         static const char print_str[] = "print";
    580         static const struct option long_options[] = {
    581           { "binary",        no_argument,       NULL,   'B' },
    582           { "buffer",        required_argument, NULL,   'b' },
    583           { "buffer-size",   optional_argument, NULL,   'g' },
    584           { "clear",         no_argument,       NULL,   'c' },
    585           { "dividers",      no_argument,       NULL,   'D' },
    586           { "file",          required_argument, NULL,   'f' },
    587           { "format",        required_argument, NULL,   'v' },
    588           // hidden and undocumented reserved alias for --regex
    589           { "grep",          required_argument, NULL,   'e' },
    590           // hidden and undocumented reserved alias for --max-count
    591           { "head",          required_argument, NULL,   'm' },
    592           { "last",          no_argument,       NULL,   'L' },
    593           { "max-count",     required_argument, NULL,   'm' },
    594           { pid_str,         required_argument, NULL,   0 },
    595           { print_str,       no_argument,       NULL,   0 },
    596           { "prune",         optional_argument, NULL,   'p' },
    597           { "regex",         required_argument, NULL,   'e' },
    598           { "rotate-count",  required_argument, NULL,   'n' },
    599           { "rotate-kbytes", required_argument, NULL,   'r' },
    600           { "statistics",    no_argument,       NULL,   'S' },
    601           // hidden and undocumented reserved alias for -t
    602           { "tail",          required_argument, NULL,   't' },
    603           // support, but ignore and do not document, the optional argument
    604           { wrap_str,        optional_argument, NULL,   0 },
    605           { NULL,            0,                 NULL,   0 }
    606         };
    607 
    608         ret = getopt_long(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
    609                           long_options, &option_index);
    610 
    611         if (ret < 0) {
    612             break;
    613         }
    614 
    615         switch (ret) {
    616             case 0:
    617                 // One of the long options
    618                 if (long_options[option_index].name == pid_str) {
    619                     // ToDo: determine runtime PID_MAX?
    620                     if (!getSizeTArg(optarg, &pid, 1)) {
    621                         logcat_panic(true, "%s %s out of range\n",
    622                                      long_options[option_index].name, optarg);
    623                     }
    624                     break;
    625                 }
    626                 if (long_options[option_index].name == wrap_str) {
    627                     mode |= ANDROID_LOG_WRAP |
    628                             ANDROID_LOG_RDONLY |
    629                             ANDROID_LOG_NONBLOCK;
    630                     // ToDo: implement API that supports setting a wrap timeout
    631                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
    632                     if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
    633                         logcat_panic(true, "%s %s out of range\n",
    634                                      long_options[option_index].name, optarg);
    635                     }
    636                     if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
    637                         fprintf(stderr,
    638                                 "WARNING: %s %u seconds, ignoring %zu\n",
    639                                 long_options[option_index].name,
    640                                 ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
    641                     }
    642                     break;
    643                 }
    644                 if (long_options[option_index].name == print_str) {
    645                     g_printItAnyways = true;
    646                     break;
    647                 }
    648             break;
    649 
    650             case 's':
    651                 // default to all silent
    652                 android_log_addFilterRule(g_logformat, "*:s");
    653             break;
    654 
    655             case 'c':
    656                 clearLog = 1;
    657                 mode |= ANDROID_LOG_WRONLY;
    658             break;
    659 
    660             case 'L':
    661                 mode |= ANDROID_LOG_PSTORE;
    662             break;
    663 
    664             case 'd':
    665                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
    666             break;
    667 
    668             case 't':
    669                 got_t = true;
    670                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
    671                 /* FALLTHRU */
    672             case 'T':
    673                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
    674                     char *cp = parseTime(tail_time, optarg);
    675                     if (!cp) {
    676                         logcat_panic(false, "-%c \"%s\" not in time format\n",
    677                                      ret, optarg);
    678                     }
    679                     if (*cp) {
    680                         char c = *cp;
    681                         *cp = '\0';
    682                         fprintf(stderr,
    683                                 "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
    684                                 ret, optarg, c, cp + 1);
    685                         *cp = c;
    686                     }
    687                 } else {
    688                     if (!getSizeTArg(optarg, &tail_lines, 1)) {
    689                         fprintf(stderr,
    690                                 "WARNING: -%c %s invalid, setting to 1\n",
    691                                 ret, optarg);
    692                         tail_lines = 1;
    693                     }
    694                 }
    695             break;
    696 
    697             case 'D':
    698                 printDividers = true;
    699             break;
    700 
    701             case 'e':
    702                 g_regex = new pcrecpp::RE(optarg);
    703             break;
    704 
    705             case 'm': {
    706                 char *end = NULL;
    707                 if (!getSizeTArg(optarg, &g_maxCount)) {
    708                     logcat_panic(false, "-%c \"%s\" isn't an "
    709                                  "integer greater than zero\n", ret, optarg);
    710                 }
    711             }
    712             break;
    713 
    714             case 'g':
    715                 if (!optarg) {
    716                     getLogSize = 1;
    717                     break;
    718                 }
    719                 // FALLTHRU
    720 
    721             case 'G': {
    722                 char *cp;
    723                 if (strtoll(optarg, &cp, 0) > 0) {
    724                     setLogSize = strtoll(optarg, &cp, 0);
    725                 } else {
    726                     setLogSize = 0;
    727                 }
    728 
    729                 switch(*cp) {
    730                 case 'g':
    731                 case 'G':
    732                     setLogSize *= 1024;
    733                 /* FALLTHRU */
    734                 case 'm':
    735                 case 'M':
    736                     setLogSize *= 1024;
    737                 /* FALLTHRU */
    738                 case 'k':
    739                 case 'K':
    740                     setLogSize *= 1024;
    741                 /* FALLTHRU */
    742                 case '\0':
    743                 break;
    744 
    745                 default:
    746                     setLogSize = 0;
    747                 }
    748 
    749                 if (!setLogSize) {
    750                     fprintf(stderr, "ERROR: -G <num><multiplier>\n");
    751                     return EXIT_FAILURE;
    752                 }
    753             }
    754             break;
    755 
    756             case 'p':
    757                 if (!optarg) {
    758                     getPruneList = 1;
    759                     break;
    760                 }
    761                 // FALLTHRU
    762 
    763             case 'P':
    764                 setPruneList = optarg;
    765             break;
    766 
    767             case 'b': {
    768                 if (strcmp(optarg, "default") == 0) {
    769                     for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
    770                         switch (i) {
    771                         case LOG_ID_SECURITY:
    772                         case LOG_ID_EVENTS:
    773                             continue;
    774                         case LOG_ID_MAIN:
    775                         case LOG_ID_SYSTEM:
    776                         case LOG_ID_CRASH:
    777                             break;
    778                         default:
    779                             continue;
    780                         }
    781 
    782                         const char *name = android_log_id_to_name((log_id_t)i);
    783                         log_id_t log_id = android_name_to_log_id(name);
    784 
    785                         if (log_id != (log_id_t)i) {
    786                             continue;
    787                         }
    788 
    789                         bool found = false;
    790                         for (dev = devices; dev; dev = dev->next) {
    791                             if (!strcmp(optarg, dev->device)) {
    792                                 found = true;
    793                                 break;
    794                             }
    795                             if (!dev->next) {
    796                                 break;
    797                             }
    798                         }
    799                         if (found) {
    800                             break;
    801                         }
    802 
    803                         log_device_t* d = new log_device_t(name, false);
    804 
    805                         if (dev) {
    806                             dev->next = d;
    807                             dev = d;
    808                         } else {
    809                             devices = dev = d;
    810                         }
    811                         g_devCount++;
    812                     }
    813                     break;
    814                 }
    815 
    816                 if (strcmp(optarg, "all") == 0) {
    817                     for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
    818                         const char *name = android_log_id_to_name((log_id_t)i);
    819                         log_id_t log_id = android_name_to_log_id(name);
    820 
    821                         if (log_id != (log_id_t)i) {
    822                             continue;
    823                         }
    824 
    825                         bool found = false;
    826                         for (dev = devices; dev; dev = dev->next) {
    827                             if (!strcmp(optarg, dev->device)) {
    828                                 found = true;
    829                                 break;
    830                             }
    831                             if (!dev->next) {
    832                                 break;
    833                             }
    834                         }
    835                         if (found) {
    836                             break;
    837                         }
    838 
    839                         bool binary = !strcmp(name, "events") ||
    840                                       !strcmp(name, "security");
    841                         log_device_t* d = new log_device_t(name, binary);
    842 
    843                         if (dev) {
    844                             dev->next = d;
    845                             dev = d;
    846                         } else {
    847                             devices = dev = d;
    848                         }
    849                         g_devCount++;
    850                     }
    851                     break;
    852                 }
    853 
    854                 bool binary = !(strcmp(optarg, "events") &&
    855                                 strcmp(optarg, "security"));
    856 
    857                 if (devices) {
    858                     dev = devices;
    859                     while (dev->next) {
    860                         if (!strcmp(optarg, dev->device)) {
    861                             dev = NULL;
    862                             break;
    863                         }
    864                         dev = dev->next;
    865                     }
    866                     if (dev) {
    867                         dev->next = new log_device_t(optarg, binary);
    868                     }
    869                 } else {
    870                     devices = new log_device_t(optarg, binary);
    871                 }
    872                 g_devCount++;
    873             }
    874             break;
    875 
    876             case 'B':
    877                 g_printBinary = 1;
    878             break;
    879 
    880             case 'f':
    881                 if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
    882                     tail_time = lastLogTime(optarg);
    883                 }
    884                 // redirect output to a file
    885                 g_outputFileName = optarg;
    886             break;
    887 
    888             case 'r':
    889                 if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
    890                     logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
    891                 }
    892             break;
    893 
    894             case 'n':
    895                 if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
    896                     logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
    897                 }
    898             break;
    899 
    900             case 'v':
    901                 err = setLogFormat (optarg);
    902                 if (err < 0) {
    903                     logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
    904                 }
    905                 hasSetLogFormat |= err;
    906             break;
    907 
    908             case 'Q':
    909                 /* this is a *hidden* option used to start a version of logcat                 */
    910                 /* in an emulated device only. it basically looks for androidboot.logcat=      */
    911                 /* on the kernel command line. If something is found, it extracts a log filter */
    912                 /* and uses it to run the program. If nothing is found, the program should     */
    913                 /* quit immediately                                                            */
    914 #define  KERNEL_OPTION  "androidboot.logcat="
    915 #define  CONSOLE_OPTION "androidboot.console="
    916                 {
    917                     int          fd;
    918                     char*        logcat;
    919                     char*        console;
    920                     int          force_exit = 1;
    921                     static char  cmdline[1024];
    922 
    923                     fd = open("/proc/cmdline", O_RDONLY);
    924                     if (fd >= 0) {
    925                         int  n = read(fd, cmdline, sizeof(cmdline)-1 );
    926                         if (n < 0) n = 0;
    927                         cmdline[n] = 0;
    928                         close(fd);
    929                     } else {
    930                         cmdline[0] = 0;
    931                     }
    932 
    933                     logcat  = strstr( cmdline, KERNEL_OPTION );
    934                     console = strstr( cmdline, CONSOLE_OPTION );
    935                     if (logcat != NULL) {
    936                         char*  p = logcat + sizeof(KERNEL_OPTION)-1;;
    937                         char*  q = strpbrk( p, " \t\n\r" );;
    938 
    939                         if (q != NULL)
    940                             *q = 0;
    941 
    942                         forceFilters = p;
    943                         force_exit   = 0;
    944                     }
    945                     /* if nothing found or invalid filters, exit quietly */
    946                     if (force_exit) {
    947                         return EXIT_SUCCESS;
    948                     }
    949 
    950                     /* redirect our output to the emulator console */
    951                     if (console) {
    952                         char*  p = console + sizeof(CONSOLE_OPTION)-1;
    953                         char*  q = strpbrk( p, " \t\n\r" );
    954                         char   devname[64];
    955                         int    len;
    956 
    957                         if (q != NULL) {
    958                             len = q - p;
    959                         } else
    960                             len = strlen(p);
    961 
    962                         len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p );
    963                         fprintf(stderr, "logcat using %s (%d)\n", devname, len);
    964                         if (len < (int)sizeof(devname)) {
    965                             fd = open( devname, O_WRONLY );
    966                             if (fd >= 0) {
    967                                 dup2(fd, 1);
    968                                 dup2(fd, 2);
    969                                 close(fd);
    970                             }
    971                         }
    972                     }
    973                 }
    974                 break;
    975 
    976             case 'S':
    977                 printStatistics = 1;
    978                 break;
    979 
    980             case ':':
    981                 logcat_panic(true, "Option -%c needs an argument\n", optopt);
    982                 break;
    983 
    984             default:
    985                 logcat_panic(true, "Unrecognized Option %c\n", optopt);
    986                 break;
    987         }
    988     }
    989 
    990     if (g_maxCount && got_t) {
    991         logcat_panic(true, "Cannot use -m (--max-count) and -t together\n");
    992     }
    993     if (g_printItAnyways && (!g_regex || !g_maxCount)) {
    994         // One day it would be nice if --print -v color and --regex <expr>
    995         // could play with each other and show regex highlighted content.
    996         fprintf(stderr, "WARNING: "
    997                             "--print ignored, to be used in combination with\n"
    998                         "         "
    999                             "--regex <expr> and --max-count <N>\n");
   1000         g_printItAnyways = false;
   1001     }
   1002 
   1003     if (!devices) {
   1004         dev = devices = new log_device_t("main", false);
   1005         g_devCount = 1;
   1006         if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
   1007             dev = dev->next = new log_device_t("system", false);
   1008             g_devCount++;
   1009         }
   1010         if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
   1011             dev = dev->next = new log_device_t("crash", false);
   1012             g_devCount++;
   1013         }
   1014     }
   1015 
   1016     if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
   1017         logcat_panic(true, "-r requires -f as well\n");
   1018     }
   1019 
   1020     setupOutput();
   1021 
   1022     if (hasSetLogFormat == 0) {
   1023         const char* logFormat = getenv("ANDROID_PRINTF_LOG");
   1024 
   1025         if (logFormat != NULL) {
   1026             err = setLogFormat(logFormat);
   1027             if (err < 0) {
   1028                 fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n",
   1029                                     logFormat);
   1030             }
   1031         } else {
   1032             setLogFormat("threadtime");
   1033         }
   1034     }
   1035 
   1036     if (forceFilters) {
   1037         err = android_log_addFilterString(g_logformat, forceFilters);
   1038         if (err < 0) {
   1039             logcat_panic(false, "Invalid filter expression in logcat args\n");
   1040         }
   1041     } else if (argc == optind) {
   1042         // Add from environment variable
   1043         char *env_tags_orig = getenv("ANDROID_LOG_TAGS");
   1044 
   1045         if (env_tags_orig != NULL) {
   1046             err = android_log_addFilterString(g_logformat, env_tags_orig);
   1047 
   1048             if (err < 0) {
   1049                 logcat_panic(true,
   1050                             "Invalid filter expression in ANDROID_LOG_TAGS\n");
   1051             }
   1052         }
   1053     } else {
   1054         // Add from commandline
   1055         for (int i = optind ; i < argc ; i++) {
   1056             err = android_log_addFilterString(g_logformat, argv[i]);
   1057 
   1058             if (err < 0) {
   1059                 logcat_panic(true, "Invalid filter expression '%s'\n", argv[i]);
   1060             }
   1061         }
   1062     }
   1063 
   1064     dev = devices;
   1065     if (tail_time != log_time::EPOCH) {
   1066         logger_list = android_logger_list_alloc_time(mode, tail_time, pid);
   1067     } else {
   1068         logger_list = android_logger_list_alloc(mode, tail_lines, pid);
   1069     }
   1070     const char *openDeviceFail = NULL;
   1071     const char *clearFail = NULL;
   1072     const char *setSizeFail = NULL;
   1073     const char *getSizeFail = NULL;
   1074     // We have three orthogonal actions below to clear, set log size and
   1075     // get log size. All sharing the same iteration loop.
   1076     while (dev) {
   1077         dev->logger_list = logger_list;
   1078         dev->logger = android_logger_open(logger_list,
   1079                                           android_name_to_log_id(dev->device));
   1080         if (!dev->logger) {
   1081             openDeviceFail = openDeviceFail ?: dev->device;
   1082             dev = dev->next;
   1083             continue;
   1084         }
   1085 
   1086         if (clearLog) {
   1087             if (android_logger_clear(dev->logger)) {
   1088                 clearFail = clearFail ?: dev->device;
   1089             }
   1090         }
   1091 
   1092         if (setLogSize) {
   1093             if (android_logger_set_log_size(dev->logger, setLogSize)) {
   1094                 setSizeFail = setSizeFail ?: dev->device;
   1095             }
   1096         }
   1097 
   1098         if (getLogSize) {
   1099             long size = android_logger_get_log_size(dev->logger);
   1100             long readable = android_logger_get_log_readable_size(dev->logger);
   1101 
   1102             if ((size < 0) || (readable < 0)) {
   1103                 getSizeFail = getSizeFail ?: dev->device;
   1104             } else {
   1105                 printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
   1106                        "max entry is %db, max payload is %db\n", dev->device,
   1107                        value_of_size(size), multiplier_of_size(size),
   1108                        value_of_size(readable), multiplier_of_size(readable),
   1109                        (int) LOGGER_ENTRY_MAX_LEN,
   1110                        (int) LOGGER_ENTRY_MAX_PAYLOAD);
   1111             }
   1112         }
   1113 
   1114         dev = dev->next;
   1115     }
   1116     // report any errors in the above loop and exit
   1117     if (openDeviceFail) {
   1118         logcat_panic(false, "Unable to open log device '%s'\n", openDeviceFail);
   1119     }
   1120     if (clearFail) {
   1121         logcat_panic(false, "failed to clear the '%s' log\n", clearFail);
   1122     }
   1123     if (setSizeFail) {
   1124         logcat_panic(false, "failed to set the '%s' log size\n", setSizeFail);
   1125     }
   1126     if (getSizeFail) {
   1127         logcat_panic(false, "failed to get the readable '%s' log size",
   1128                      getSizeFail);
   1129     }
   1130 
   1131     if (setPruneList) {
   1132         size_t len = strlen(setPruneList);
   1133         /*extra 32 bytes are needed by  android_logger_set_prune_list */
   1134         size_t bLen = len + 32;
   1135         char *buf = NULL;
   1136         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
   1137             buf[len] = '\0';
   1138             if (android_logger_set_prune_list(logger_list, buf, bLen)) {
   1139                 logcat_panic(false, "failed to set the prune list");
   1140             }
   1141             free(buf);
   1142         } else {
   1143             logcat_panic(false, "failed to set the prune list (alloc)");
   1144         }
   1145     }
   1146 
   1147     if (printStatistics || getPruneList) {
   1148         size_t len = 8192;
   1149         char *buf;
   1150 
   1151         for (int retry = 32;
   1152                 (retry >= 0) && ((buf = new char [len]));
   1153                 delete [] buf, buf = NULL, --retry) {
   1154             if (getPruneList) {
   1155                 android_logger_get_prune_list(logger_list, buf, len);
   1156             } else {
   1157                 android_logger_get_statistics(logger_list, buf, len);
   1158             }
   1159             buf[len-1] = '\0';
   1160             if (atol(buf) < 3) {
   1161                 delete [] buf;
   1162                 buf = NULL;
   1163                 break;
   1164             }
   1165             size_t ret = atol(buf) + 1;
   1166             if (ret <= len) {
   1167                 len = ret;
   1168                 break;
   1169             }
   1170             len = ret;
   1171         }
   1172 
   1173         if (!buf) {
   1174             logcat_panic(false, "failed to read data");
   1175         }
   1176 
   1177         // remove trailing FF
   1178         char *cp = buf + len - 1;
   1179         *cp = '\0';
   1180         bool truncated = *--cp != '\f';
   1181         if (!truncated) {
   1182             *cp = '\0';
   1183         }
   1184 
   1185         // squash out the byte count
   1186         cp = buf;
   1187         if (!truncated) {
   1188             while (isdigit(*cp)) {
   1189                 ++cp;
   1190             }
   1191             if (*cp == '\n') {
   1192                 ++cp;
   1193             }
   1194         }
   1195 
   1196         printf("%s", cp);
   1197         delete [] buf;
   1198         return EXIT_SUCCESS;
   1199     }
   1200 
   1201 
   1202     if (getLogSize) {
   1203         return EXIT_SUCCESS;
   1204     }
   1205     if (setLogSize || setPruneList) {
   1206         return EXIT_SUCCESS;
   1207     }
   1208     if (clearLog) {
   1209         return EXIT_SUCCESS;
   1210     }
   1211 
   1212     //LOG_EVENT_INT(10, 12345);
   1213     //LOG_EVENT_LONG(11, 0x1122334455667788LL);
   1214     //LOG_EVENT_STRING(0, "whassup, doc?");
   1215 
   1216     dev = NULL;
   1217     log_device_t unexpected("unexpected", false);
   1218 
   1219     while (!g_maxCount || (g_printCount < g_maxCount)) {
   1220         struct log_msg log_msg;
   1221         log_device_t* d;
   1222         int ret = android_logger_list_read(logger_list, &log_msg);
   1223 
   1224         if (ret == 0) {
   1225             logcat_panic(false, "read: unexpected EOF!\n");
   1226         }
   1227 
   1228         if (ret < 0) {
   1229             if (ret == -EAGAIN) {
   1230                 break;
   1231             }
   1232 
   1233             if (ret == -EIO) {
   1234                 logcat_panic(false, "read: unexpected EOF!\n");
   1235             }
   1236             if (ret == -EINVAL) {
   1237                 logcat_panic(false, "read: unexpected length.\n");
   1238             }
   1239             logcat_panic(false, "logcat read failure");
   1240         }
   1241 
   1242         for (d = devices; d; d = d->next) {
   1243             if (android_name_to_log_id(d->device) == log_msg.id()) {
   1244                 break;
   1245             }
   1246         }
   1247         if (!d) {
   1248             g_devCount = 2; // set to Multiple
   1249             d = &unexpected;
   1250             d->binary = log_msg.id() == LOG_ID_EVENTS;
   1251         }
   1252 
   1253         if (dev != d) {
   1254             dev = d;
   1255             maybePrintStart(dev, printDividers);
   1256         }
   1257         if (g_printBinary) {
   1258             printBinary(&log_msg);
   1259         } else {
   1260             processBuffer(dev, &log_msg);
   1261         }
   1262     }
   1263 
   1264     android_logger_list_free(logger_list);
   1265 
   1266     return EXIT_SUCCESS;
   1267 }
   1268