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(¶m, 0, sizeof(param)); 247 if (sched_setscheduler((pid_t) 0, SCHED_BATCH, ¶m) < 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