logcat.cpp revision de02546e3d7c9c307e3d87e0a7fe8bb39b363a72
1// Copyright 2006-2014 The Android Open Source Project 2 3#include <assert.h> 4#include <ctype.h> 5#include <errno.h> 6#include <fcntl.h> 7#include <stdio.h> 8#include <stdlib.h> 9#include <stdarg.h> 10#include <string.h> 11#include <signal.h> 12#include <time.h> 13#include <unistd.h> 14#include <sys/socket.h> 15#include <sys/stat.h> 16#include <arpa/inet.h> 17 18#include <cutils/sockets.h> 19#include <log/log.h> 20#include <log/logger.h> 21#include <log/logd.h> 22#include <log/logprint.h> 23#include <log/event_tag_map.h> 24 25#define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16 26#define DEFAULT_MAX_ROTATED_LOGS 4 27 28static AndroidLogFormat * g_logformat; 29 30/* logd prefixes records with a length field */ 31#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t) 32 33struct log_device_t { 34 const char* device; 35 bool binary; 36 struct logger *logger; 37 struct logger_list *logger_list; 38 bool printed; 39 char label; 40 41 log_device_t* next; 42 43 log_device_t(const char* d, bool b, char l) { 44 device = d; 45 binary = b; 46 label = l; 47 next = NULL; 48 printed = false; 49 } 50}; 51 52namespace android { 53 54/* Global Variables */ 55 56static const char * g_outputFileName = NULL; 57static int g_logRotateSizeKBytes = 0; // 0 means "no log rotation" 58static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded" 59static int g_outFD = -1; 60static off_t g_outByteCount = 0; 61static int g_printBinary = 0; 62static int g_devCount = 0; 63 64static EventTagMap* g_eventTagMap = NULL; 65 66static int openLogFile (const char *pathname) 67{ 68 return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); 69} 70 71static void rotateLogs() 72{ 73 int err; 74 75 // Can't rotate logs if we're not outputting to a file 76 if (g_outputFileName == NULL) { 77 return; 78 } 79 80 close(g_outFD); 81 82 for (int i = g_maxRotatedLogs ; i > 0 ; i--) { 83 char *file0, *file1; 84 85 asprintf(&file1, "%s.%d", g_outputFileName, i); 86 87 if (i - 1 == 0) { 88 asprintf(&file0, "%s", g_outputFileName); 89 } else { 90 asprintf(&file0, "%s.%d", g_outputFileName, i - 1); 91 } 92 93 err = rename (file0, file1); 94 95 if (err < 0 && errno != ENOENT) { 96 perror("while rotating log files"); 97 } 98 99 free(file1); 100 free(file0); 101 } 102 103 g_outFD = openLogFile (g_outputFileName); 104 105 if (g_outFD < 0) { 106 perror ("couldn't open output file"); 107 exit(-1); 108 } 109 110 g_outByteCount = 0; 111 112} 113 114void printBinary(struct log_msg *buf) 115{ 116 size_t size = buf->len(); 117 118 TEMP_FAILURE_RETRY(write(g_outFD, buf, size)); 119} 120 121static void processBuffer(log_device_t* dev, struct log_msg *buf) 122{ 123 int bytesWritten = 0; 124 int err; 125 AndroidLogEntry entry; 126 char binaryMsgBuf[1024]; 127 128 if (dev->binary) { 129 err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry, 130 g_eventTagMap, 131 binaryMsgBuf, 132 sizeof(binaryMsgBuf)); 133 //printf(">>> pri=%d len=%d msg='%s'\n", 134 // entry.priority, entry.messageLen, entry.message); 135 } else { 136 err = android_log_processLogBuffer(&buf->entry_v1, &entry); 137 } 138 if (err < 0) { 139 goto error; 140 } 141 142 if (android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) { 143 if (false && g_devCount > 1) { 144 binaryMsgBuf[0] = dev->label; 145 binaryMsgBuf[1] = ' '; 146 bytesWritten = write(g_outFD, binaryMsgBuf, 2); 147 if (bytesWritten < 0) { 148 perror("output error"); 149 exit(-1); 150 } 151 } 152 153 bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry); 154 155 if (bytesWritten < 0) { 156 perror("output error"); 157 exit(-1); 158 } 159 } 160 161 g_outByteCount += bytesWritten; 162 163 if (g_logRotateSizeKBytes > 0 164 && (g_outByteCount / 1024) >= g_logRotateSizeKBytes 165 ) { 166 rotateLogs(); 167 } 168 169error: 170 //fprintf (stderr, "Error processing record\n"); 171 return; 172} 173 174static void maybePrintStart(log_device_t* dev) { 175 if (!dev->printed) { 176 dev->printed = true; 177 if (g_devCount > 1 && !g_printBinary) { 178 char buf[1024]; 179 snprintf(buf, sizeof(buf), "--------- beginning of %s\n", 180 dev->device); 181 if (write(g_outFD, buf, strlen(buf)) < 0) { 182 perror("output error"); 183 exit(-1); 184 } 185 } 186 } 187} 188 189static void setupOutput() 190{ 191 192 if (g_outputFileName == NULL) { 193 g_outFD = STDOUT_FILENO; 194 195 } else { 196 struct stat statbuf; 197 198 g_outFD = openLogFile (g_outputFileName); 199 200 if (g_outFD < 0) { 201 perror ("couldn't open output file"); 202 exit(-1); 203 } 204 205 fstat(g_outFD, &statbuf); 206 207 g_outByteCount = statbuf.st_size; 208 } 209} 210 211static void show_help(const char *cmd) 212{ 213 fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd); 214 215 fprintf(stderr, "options include:\n" 216 " -s Set default filter to silent.\n" 217 " Like specifying filterspec '*:s'\n" 218 " -f <filename> Log to file. Default to stdout\n" 219 " -r [<kbytes>] Rotate log every kbytes. (16 if unspecified). Requires -f\n" 220 " -n <count> Sets max number of rotated logs to <count>, default 4\n" 221 " -v <format> Sets the log print format, where <format> is one of:\n\n" 222 " brief process tag thread raw time threadtime long\n\n" 223 " -c clear (flush) the entire log and exit\n" 224 " -d dump the log and then exit (don't block)\n" 225 " -t <count> print only the most recent <count> lines (implies -d)\n" 226 " -T <count> print only the most recent <count> lines (does not imply -d)\n" 227 " -g get the size of the log's ring buffer and exit\n" 228 " -b <buffer> Request alternate ring buffer, 'main', 'system', 'radio'\n" 229 " or 'events'. Multiple -b parameters are allowed and the\n" 230 " results are interleaved. The default is -b main -b system.\n" 231 " -B output the log in binary"); 232 233 234 fprintf(stderr,"\nfilterspecs are a series of \n" 235 " <tag>[:priority]\n\n" 236 "where <tag> is a log component tag (or * for all) and priority is:\n" 237 " V Verbose\n" 238 " D Debug\n" 239 " I Info\n" 240 " W Warn\n" 241 " E Error\n" 242 " F Fatal\n" 243 " S Silent (supress all output)\n" 244 "\n'*' means '*:d' and <tag> by itself means <tag>:v\n" 245 "\nIf not specified on the commandline, filterspec is set from ANDROID_LOG_TAGS.\n" 246 "If no filterspec is found, filter defaults to '*:I'\n" 247 "\nIf not specified with -v, format is set from ANDROID_PRINTF_LOG\n" 248 "or defaults to \"brief\"\n\n"); 249 250 251 252} 253 254 255} /* namespace android */ 256 257static int setLogFormat(const char * formatString) 258{ 259 static AndroidLogPrintFormat format; 260 261 format = android_log_formatFromString(formatString); 262 263 if (format == FORMAT_OFF) { 264 // FORMAT_OFF means invalid string 265 return -1; 266 } 267 268 android_log_setPrintFormat(g_logformat, format); 269 270 return 0; 271} 272 273extern "C" void logprint_run_tests(void); 274 275int main(int argc, char **argv) 276{ 277 int err; 278 int hasSetLogFormat = 0; 279 int clearLog = 0; 280 int getLogSize = 0; 281 int mode = O_RDONLY; 282 const char *forceFilters = NULL; 283 log_device_t* devices = NULL; 284 log_device_t* dev; 285 bool needBinary = false; 286 struct logger_list *logger_list; 287 int tail_lines = 0; 288 289 signal(SIGPIPE, exit); 290 291 g_logformat = android_log_format_new(); 292 293 if (argc == 2 && 0 == strcmp(argv[1], "--test")) { 294 logprint_run_tests(); 295 exit(0); 296 } 297 298 if (argc == 2 && 0 == strcmp(argv[1], "--help")) { 299 android::show_help(argv[0]); 300 exit(0); 301 } 302 303 for (;;) { 304 int ret; 305 306 ret = getopt(argc, argv, "cdt:T:gsQf:r::n:v:b:B"); 307 308 if (ret < 0) { 309 break; 310 } 311 312 switch(ret) { 313 case 's': 314 // default to all silent 315 android_log_addFilterRule(g_logformat, "*:s"); 316 break; 317 318 case 'c': 319 clearLog = 1; 320 mode = O_WRONLY; 321 break; 322 323 case 'd': 324 mode = O_RDONLY | O_NDELAY; 325 break; 326 327 case 't': 328 mode = O_RDONLY | O_NDELAY; 329 /* FALLTHRU */ 330 case 'T': 331 tail_lines = atoi(optarg); 332 break; 333 334 case 'g': 335 getLogSize = 1; 336 break; 337 338 case 'b': { 339 bool binary = strcmp(optarg, "events") == 0; 340 if (binary) { 341 needBinary = true; 342 } 343 344 if (devices) { 345 dev = devices; 346 while (dev->next) { 347 dev = dev->next; 348 } 349 dev->next = new log_device_t(optarg, binary, optarg[0]); 350 } else { 351 devices = new log_device_t(optarg, binary, optarg[0]); 352 } 353 android::g_devCount++; 354 } 355 break; 356 357 case 'B': 358 android::g_printBinary = 1; 359 break; 360 361 case 'f': 362 // redirect output to a file 363 364 android::g_outputFileName = optarg; 365 366 break; 367 368 case 'r': 369 if (optarg == NULL) { 370 android::g_logRotateSizeKBytes 371 = DEFAULT_LOG_ROTATE_SIZE_KBYTES; 372 } else { 373 long logRotateSize; 374 char *lastDigit; 375 376 if (!isdigit(optarg[0])) { 377 fprintf(stderr,"Invalid parameter to -r\n"); 378 android::show_help(argv[0]); 379 exit(-1); 380 } 381 android::g_logRotateSizeKBytes = atoi(optarg); 382 } 383 break; 384 385 case 'n': 386 if (!isdigit(optarg[0])) { 387 fprintf(stderr,"Invalid parameter to -r\n"); 388 android::show_help(argv[0]); 389 exit(-1); 390 } 391 392 android::g_maxRotatedLogs = atoi(optarg); 393 break; 394 395 case 'v': 396 err = setLogFormat (optarg); 397 if (err < 0) { 398 fprintf(stderr,"Invalid parameter to -v\n"); 399 android::show_help(argv[0]); 400 exit(-1); 401 } 402 403 hasSetLogFormat = 1; 404 break; 405 406 case 'Q': 407 /* this is a *hidden* option used to start a version of logcat */ 408 /* in an emulated device only. it basically looks for androidboot.logcat= */ 409 /* on the kernel command line. If something is found, it extracts a log filter */ 410 /* and uses it to run the program. If nothing is found, the program should */ 411 /* quit immediately */ 412#define KERNEL_OPTION "androidboot.logcat=" 413#define CONSOLE_OPTION "androidboot.console=" 414 { 415 int fd; 416 char* logcat; 417 char* console; 418 int force_exit = 1; 419 static char cmdline[1024]; 420 421 fd = open("/proc/cmdline", O_RDONLY); 422 if (fd >= 0) { 423 int n = read(fd, cmdline, sizeof(cmdline)-1 ); 424 if (n < 0) n = 0; 425 cmdline[n] = 0; 426 close(fd); 427 } else { 428 cmdline[0] = 0; 429 } 430 431 logcat = strstr( cmdline, KERNEL_OPTION ); 432 console = strstr( cmdline, CONSOLE_OPTION ); 433 if (logcat != NULL) { 434 char* p = logcat + sizeof(KERNEL_OPTION)-1;; 435 char* q = strpbrk( p, " \t\n\r" );; 436 437 if (q != NULL) 438 *q = 0; 439 440 forceFilters = p; 441 force_exit = 0; 442 } 443 /* if nothing found or invalid filters, exit quietly */ 444 if (force_exit) 445 exit(0); 446 447 /* redirect our output to the emulator console */ 448 if (console) { 449 char* p = console + sizeof(CONSOLE_OPTION)-1; 450 char* q = strpbrk( p, " \t\n\r" ); 451 char devname[64]; 452 int len; 453 454 if (q != NULL) { 455 len = q - p; 456 } else 457 len = strlen(p); 458 459 len = snprintf( devname, sizeof(devname), "/dev/%.*s", len, p ); 460 fprintf(stderr, "logcat using %s (%d)\n", devname, len); 461 if (len < (int)sizeof(devname)) { 462 fd = open( devname, O_WRONLY ); 463 if (fd >= 0) { 464 dup2(fd, 1); 465 dup2(fd, 2); 466 close(fd); 467 } 468 } 469 } 470 } 471 break; 472 473 default: 474 fprintf(stderr,"Unrecognized Option\n"); 475 android::show_help(argv[0]); 476 exit(-1); 477 break; 478 } 479 } 480 481 if (!devices) { 482 devices = new log_device_t("main", false, 'm'); 483 android::g_devCount = 1; 484 if (android_name_to_log_id("system") == LOG_ID_SYSTEM) { 485 devices->next = new log_device_t("system", false, 's'); 486 android::g_devCount++; 487 } 488 } 489 490 if (android::g_logRotateSizeKBytes != 0 491 && android::g_outputFileName == NULL 492 ) { 493 fprintf(stderr,"-r requires -f as well\n"); 494 android::show_help(argv[0]); 495 exit(-1); 496 } 497 498 android::setupOutput(); 499 500 if (hasSetLogFormat == 0) { 501 const char* logFormat = getenv("ANDROID_PRINTF_LOG"); 502 503 if (logFormat != NULL) { 504 err = setLogFormat(logFormat); 505 506 if (err < 0) { 507 fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", 508 logFormat); 509 } 510 } 511 } 512 513 if (forceFilters) { 514 err = android_log_addFilterString(g_logformat, forceFilters); 515 if (err < 0) { 516 fprintf (stderr, "Invalid filter expression in -logcat option\n"); 517 exit(0); 518 } 519 } else if (argc == optind) { 520 // Add from environment variable 521 char *env_tags_orig = getenv("ANDROID_LOG_TAGS"); 522 523 if (env_tags_orig != NULL) { 524 err = android_log_addFilterString(g_logformat, env_tags_orig); 525 526 if (err < 0) { 527 fprintf(stderr, "Invalid filter expression in" 528 " ANDROID_LOG_TAGS\n"); 529 android::show_help(argv[0]); 530 exit(-1); 531 } 532 } 533 } else { 534 // Add from commandline 535 for (int i = optind ; i < argc ; i++) { 536 err = android_log_addFilterString(g_logformat, argv[i]); 537 538 if (err < 0) { 539 fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]); 540 android::show_help(argv[0]); 541 exit(-1); 542 } 543 } 544 } 545 546 dev = devices; 547 logger_list = android_logger_list_alloc(mode, tail_lines, 0); 548 while (dev) { 549 dev->logger_list = logger_list; 550 dev->logger = android_logger_open(logger_list, 551 android_name_to_log_id(dev->device)); 552 if (!dev->logger) { 553 fprintf(stderr, "Unable to open log device '%s'\n", dev->device); 554 exit(EXIT_FAILURE); 555 } 556 557 if (clearLog) { 558 int ret; 559 ret = android_logger_clear(dev->logger); 560 if (ret) { 561 perror("clearLog"); 562 exit(EXIT_FAILURE); 563 } 564 } 565 566 if (getLogSize) { 567 int size, readable; 568 569 size = android_logger_get_log_size(dev->logger); 570 if (size < 0) { 571 perror("getLogSize"); 572 exit(EXIT_FAILURE); 573 } 574 575 readable = android_logger_get_log_readable_size(dev->logger); 576 if (readable < 0) { 577 perror("getLogReadableSize"); 578 exit(EXIT_FAILURE); 579 } 580 581 printf("%s: ring buffer is %dKb (%dKb consumed), " 582 "max entry is %db, max payload is %db\n", dev->device, 583 size / 1024, readable / 1024, 584 (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD); 585 } 586 587 dev = dev->next; 588 } 589 590 if (getLogSize) { 591 exit(0); 592 } 593 if (clearLog) { 594 exit(0); 595 } 596 597 //LOG_EVENT_INT(10, 12345); 598 //LOG_EVENT_LONG(11, 0x1122334455667788LL); 599 //LOG_EVENT_STRING(0, "whassup, doc?"); 600 601 if (needBinary) 602 android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE); 603 604 while (1) { 605 struct log_msg log_msg; 606 int ret = android_logger_list_read(logger_list, &log_msg); 607 608 if (ret == 0) { 609 fprintf(stderr, "read: Unexpected EOF!\n"); 610 exit(EXIT_FAILURE); 611 } 612 613 if (ret < 0) { 614 if (ret == -EAGAIN) { 615 break; 616 } 617 618 if (ret == -EIO) { 619 fprintf(stderr, "read: Unexpected EOF!\n"); 620 exit(EXIT_FAILURE); 621 } 622 if (ret == -EINVAL) { 623 fprintf(stderr, "read: unexpected length.\n"); 624 exit(EXIT_FAILURE); 625 } 626 perror("logcat read"); 627 exit(EXIT_FAILURE); 628 } 629 630 for(dev = devices; dev; dev = dev->next) { 631 if (android_name_to_log_id(dev->device) == log_msg.id()) { 632 break; 633 } 634 } 635 if (!dev) { 636 fprintf(stderr, "read: Unexpected log ID!\n"); 637 exit(EXIT_FAILURE); 638 } 639 640 android::maybePrintStart(dev); 641 if (android::g_printBinary) { 642 android::printBinary(&log_msg); 643 } else { 644 android::processBuffer(dev, &log_msg); 645 } 646 } 647 648 android_logger_list_free(logger_list); 649 650 return 0; 651} 652