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