logcat_test.cpp revision a5e2429f09295dd94b0ae2125e380cfb3b02ab04
1/* 2 * Copyright (C) 2013-2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <ctype.h> 18#include <signal.h> 19#include <stdio.h> 20#include <stdlib.h> 21#include <string.h> 22 23#include <gtest/gtest.h> 24#include <log/log.h> 25#include <log/logger.h> 26#include <log/log_read.h> 27 28// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and 29// non-syscall libs. Since we are only using this in the emergency of 30// a signal to stuff a terminating code into the logs, we will spin rather 31// than try a usleep. 32#define LOG_FAILURE_RETRY(exp) ({ \ 33 typeof (exp) _rc; \ 34 do { \ 35 _rc = (exp); \ 36 } while (((_rc == -1) \ 37 && ((errno == EINTR) \ 38 || (errno == EAGAIN))) \ 39 || (_rc == -EINTR) \ 40 || (_rc == -EAGAIN)); \ 41 _rc; }) 42 43static const char begin[] = "--------- beginning of "; 44 45TEST(logcat, buckets) { 46 FILE *fp; 47 48 ASSERT_TRUE(NULL != (fp = popen( 49 "logcat -b radio -b events -b system -b main -d 2>/dev/null", 50 "r"))); 51 52 char buffer[5120]; 53 54 int ids = 0; 55 int count = 0; 56 57 while (fgets(buffer, sizeof(buffer), fp)) { 58 if (!strncmp(begin, buffer, sizeof(begin) - 1)) { 59 while (char *cp = strrchr(buffer, '\n')) { 60 *cp = '\0'; 61 } 62 log_id_t id = android_name_to_log_id(buffer + sizeof(begin) - 1); 63 ids |= 1 << id; 64 ++count; 65 } 66 } 67 68 pclose(fp); 69 70 EXPECT_EQ(15, ids); 71 72 EXPECT_EQ(4, count); 73} 74 75TEST(logcat, tail_3) { 76 FILE *fp; 77 78 ASSERT_TRUE(NULL != (fp = popen( 79 "logcat -v long -b radio -b events -b system -b main -t 3 2>/dev/null", 80 "r"))); 81 82 char buffer[5120]; 83 84 int count = 0; 85 86 while (fgets(buffer, sizeof(buffer), fp)) { 87 if ((buffer[0] == '[') && (buffer[1] == ' ') 88 && isdigit(buffer[2]) && isdigit(buffer[3]) 89 && (buffer[4] == '-')) { 90 ++count; 91 } 92 } 93 94 pclose(fp); 95 96 ASSERT_EQ(3, count); 97} 98 99TEST(logcat, tail_10) { 100 FILE *fp; 101 102 ASSERT_TRUE(NULL != (fp = popen( 103 "logcat -v long -b radio -b events -b system -b main -t 10 2>/dev/null", 104 "r"))); 105 106 char buffer[5120]; 107 108 int count = 0; 109 110 while (fgets(buffer, sizeof(buffer), fp)) { 111 if ((buffer[0] == '[') && (buffer[1] == ' ') 112 && isdigit(buffer[2]) && isdigit(buffer[3]) 113 && (buffer[4] == '-')) { 114 ++count; 115 } 116 } 117 118 pclose(fp); 119 120 ASSERT_EQ(10, count); 121} 122 123TEST(logcat, tail_100) { 124 FILE *fp; 125 126 ASSERT_TRUE(NULL != (fp = popen( 127 "logcat -v long -b radio -b events -b system -b main -t 100 2>/dev/null", 128 "r"))); 129 130 char buffer[5120]; 131 132 int count = 0; 133 134 while (fgets(buffer, sizeof(buffer), fp)) { 135 if ((buffer[0] == '[') && (buffer[1] == ' ') 136 && isdigit(buffer[2]) && isdigit(buffer[3]) 137 && (buffer[4] == '-')) { 138 ++count; 139 } 140 } 141 142 pclose(fp); 143 144 ASSERT_EQ(100, count); 145} 146 147TEST(logcat, tail_1000) { 148 FILE *fp; 149 150 ASSERT_TRUE(NULL != (fp = popen( 151 "logcat -v long -b radio -b events -b system -b main -t 1000 2>/dev/null", 152 "r"))); 153 154 char buffer[5120]; 155 156 int count = 0; 157 158 while (fgets(buffer, sizeof(buffer), fp)) { 159 if ((buffer[0] == '[') && (buffer[1] == ' ') 160 && isdigit(buffer[2]) && isdigit(buffer[3]) 161 && (buffer[4] == '-')) { 162 ++count; 163 } 164 } 165 166 pclose(fp); 167 168 ASSERT_EQ(1000, count); 169} 170 171TEST(logcat, tail_time) { 172 FILE *fp; 173 174 ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r"))); 175 176 char buffer[5120]; 177 char *last_timestamp = NULL; 178 char *first_timestamp = NULL; 179 int count = 0; 180 const unsigned int time_length = 18; 181 const unsigned int time_offset = 2; 182 183 while (fgets(buffer, sizeof(buffer), fp)) { 184 if ((buffer[0] == '[') && (buffer[1] == ' ') 185 && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1]) 186 && (buffer[time_offset + 2] == '-')) { 187 ++count; 188 buffer[time_length + time_offset] = '\0'; 189 if (!first_timestamp) { 190 first_timestamp = strdup(buffer + time_offset); 191 } 192 free(last_timestamp); 193 last_timestamp = strdup(buffer + time_offset); 194 } 195 } 196 pclose(fp); 197 198 EXPECT_EQ(10, count); 199 EXPECT_TRUE(last_timestamp != NULL); 200 EXPECT_TRUE(first_timestamp != NULL); 201 202 snprintf(buffer, sizeof(buffer), "logcat -v long -b all -t '%s' 2>&1", 203 first_timestamp); 204 ASSERT_TRUE(NULL != (fp = popen(buffer, "r"))); 205 206 int second_count = 0; 207 int last_timestamp_count = -1; 208 209 while (fgets(buffer, sizeof(buffer), fp)) { 210 if ((buffer[0] == '[') && (buffer[1] == ' ') 211 && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1]) 212 && (buffer[time_offset + 2] == '-')) { 213 ++second_count; 214 buffer[time_length + time_offset] = '\0'; 215 if (first_timestamp) { 216 // we can get a transitory *extremely* rare failure if hidden 217 // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000 218 EXPECT_STREQ(buffer + time_offset, first_timestamp); 219 free(first_timestamp); 220 first_timestamp = NULL; 221 } 222 if (!strcmp(buffer + time_offset, last_timestamp)) { 223 last_timestamp_count = second_count; 224 } 225 } 226 } 227 pclose(fp); 228 229 free(last_timestamp); 230 last_timestamp = NULL; 231 232 EXPECT_TRUE(first_timestamp == NULL); 233 EXPECT_LE(count, second_count); 234 EXPECT_LE(count, last_timestamp_count); 235} 236 237TEST(logcat, End_to_End) { 238 pid_t pid = getpid(); 239 240 log_time ts(CLOCK_MONOTONIC); 241 242 ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts))); 243 244 FILE *fp; 245 ASSERT_TRUE(NULL != (fp = popen( 246 "logcat -v brief -b events -t 100 2>/dev/null", 247 "r"))); 248 249 char buffer[5120]; 250 251 int count = 0; 252 253 while (fgets(buffer, sizeof(buffer), fp)) { 254 int p; 255 unsigned long long t; 256 257 if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) 258 || (p != pid)) { 259 continue; 260 } 261 262 log_time tx((const char *) &t); 263 if (ts == tx) { 264 ++count; 265 } 266 } 267 268 pclose(fp); 269 270 ASSERT_EQ(1, count); 271} 272 273TEST(logcat, get_size) { 274 FILE *fp; 275 276 // NB: crash log only available in user space 277 ASSERT_TRUE(NULL != (fp = popen( 278 "logcat -v brief -b radio -b events -b system -b main -g 2>/dev/null", 279 "r"))); 280 281 char buffer[5120]; 282 283 int count = 0; 284 285 while (fgets(buffer, sizeof(buffer), fp)) { 286 int size, consumed, max, payload; 287 char size_mult[2], consumed_mult[2]; 288 long full_size, full_consumed; 289 290 size = consumed = max = payload = 0; 291 // NB: crash log can be very small, not hit a Kb of consumed space 292 // doubly lucky we are not including it. 293 if (6 != sscanf(buffer, "%*s ring buffer is %d%2s (%d%2s consumed)," 294 " max entry is %db, max payload is %db", 295 &size, size_mult, &consumed, consumed_mult, 296 &max, &payload)) { 297 fprintf(stderr, "WARNING: Parse error: %s", buffer); 298 continue; 299 } 300 full_size = size; 301 switch(size_mult[0]) { 302 case 'G': 303 full_size *= 1024; 304 /* FALLTHRU */ 305 case 'M': 306 full_size *= 1024; 307 /* FALLTHRU */ 308 case 'K': 309 full_size *= 1024; 310 /* FALLTHRU */ 311 case 'b': 312 break; 313 } 314 full_consumed = consumed; 315 switch(consumed_mult[0]) { 316 case 'G': 317 full_consumed *= 1024; 318 /* FALLTHRU */ 319 case 'M': 320 full_consumed *= 1024; 321 /* FALLTHRU */ 322 case 'K': 323 full_consumed *= 1024; 324 /* FALLTHRU */ 325 case 'b': 326 break; 327 } 328 EXPECT_GT((full_size * 9) / 4, full_consumed); 329 EXPECT_GT(full_size, max); 330 EXPECT_GT(max, payload); 331 332 if ((((full_size * 9) / 4) >= full_consumed) 333 && (full_size > max) 334 && (max > payload)) { 335 ++count; 336 } 337 } 338 339 pclose(fp); 340 341 ASSERT_EQ(4, count); 342} 343 344static void caught_blocking(int /*signum*/) 345{ 346 unsigned long long v = 0xDEADBEEFA55A0000ULL; 347 348 v += getpid() & 0xFFFF; 349 350 LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); 351} 352 353TEST(logcat, blocking) { 354 FILE *fp; 355 unsigned long long v = 0xDEADBEEFA55F0000ULL; 356 357 pid_t pid = getpid(); 358 359 v += pid & 0xFFFF; 360 361 LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); 362 363 v &= 0xFFFFFFFFFFFAFFFFULL; 364 365 ASSERT_TRUE(NULL != (fp = popen( 366 "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" 367 " logcat -v brief -b events 2>&1", 368 "r"))); 369 370 char buffer[5120]; 371 372 int count = 0; 373 374 int signals = 0; 375 376 signal(SIGALRM, caught_blocking); 377 alarm(2); 378 while (fgets(buffer, sizeof(buffer), fp)) { 379 380 if (!strncmp(buffer, "DONE", 4)) { 381 break; 382 } 383 384 ++count; 385 386 int p; 387 unsigned long long l; 388 389 if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) 390 || (p != pid)) { 391 continue; 392 } 393 394 if (l == v) { 395 ++signals; 396 break; 397 } 398 } 399 alarm(0); 400 signal(SIGALRM, SIG_DFL); 401 402 // Generate SIGPIPE 403 fclose(fp); 404 caught_blocking(0); 405 406 pclose(fp); 407 408 EXPECT_LE(2, count); 409 410 EXPECT_EQ(1, signals); 411} 412 413static void caught_blocking_tail(int /*signum*/) 414{ 415 unsigned long long v = 0xA55ADEADBEEF0000ULL; 416 417 v += getpid() & 0xFFFF; 418 419 LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); 420} 421 422TEST(logcat, blocking_tail) { 423 FILE *fp; 424 unsigned long long v = 0xA55FDEADBEEF0000ULL; 425 426 pid_t pid = getpid(); 427 428 v += pid & 0xFFFF; 429 430 LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); 431 432 v &= 0xFFFAFFFFFFFFFFFFULL; 433 434 ASSERT_TRUE(NULL != (fp = popen( 435 "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" 436 " logcat -v brief -b events -T 5 2>&1", 437 "r"))); 438 439 char buffer[5120]; 440 441 int count = 0; 442 443 int signals = 0; 444 445 signal(SIGALRM, caught_blocking_tail); 446 alarm(2); 447 while (fgets(buffer, sizeof(buffer), fp)) { 448 449 if (!strncmp(buffer, "DONE", 4)) { 450 break; 451 } 452 453 ++count; 454 455 int p; 456 unsigned long long l; 457 458 if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) 459 || (p != pid)) { 460 continue; 461 } 462 463 if (l == v) { 464 if (count >= 5) { 465 ++signals; 466 } 467 break; 468 } 469 } 470 alarm(0); 471 signal(SIGALRM, SIG_DFL); 472 473 // Generate SIGPIPE 474 fclose(fp); 475 caught_blocking_tail(0); 476 477 pclose(fp); 478 479 EXPECT_LE(2, count); 480 481 EXPECT_EQ(1, signals); 482} 483 484TEST(logcat, logrotate) { 485 static const char form[] = "/data/local/tmp/logcat.logrotate.XXXXXX"; 486 char buf[sizeof(form)]; 487 ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form))); 488 489 static const char comm[] = "logcat -b radio -b events -b system -b main" 490 " -d -f %s/log.txt -n 7 -r 1"; 491 char command[sizeof(buf) + sizeof(comm)]; 492 sprintf(command, comm, buf); 493 494 int ret; 495 EXPECT_FALSE((ret = system(command))); 496 if (!ret) { 497 sprintf(command, "ls -s %s 2>/dev/null", buf); 498 499 FILE *fp; 500 EXPECT_TRUE(NULL != (fp = popen(command, "r"))); 501 if (fp) { 502 char buffer[5120]; 503 int count = 0; 504 505 while (fgets(buffer, sizeof(buffer), fp)) { 506 static const char match_1[] = "4 log.txt"; 507 static const char match_2[] = "8 log.txt"; 508 static const char match_3[] = "16 log.txt"; 509 static const char total[] = "total "; 510 511 if (!strncmp(buffer, match_1, sizeof(match_1) - 1) 512 || !strncmp(buffer, match_2, sizeof(match_2) - 1) 513 || !strncmp(buffer, match_3, sizeof(match_3) - 1)) { 514 ++count; 515 } else if (strncmp(buffer, total, sizeof(total) - 1)) { 516 fprintf(stderr, "WARNING: Parse error: %s", buffer); 517 } 518 } 519 pclose(fp); 520 EXPECT_TRUE(count == 7 || count == 8); 521 } 522 } 523 sprintf(command, "rm -rf %s", buf); 524 EXPECT_FALSE(system(command)); 525} 526 527static void caught_blocking_clear(int /*signum*/) 528{ 529 unsigned long long v = 0xDEADBEEFA55C0000ULL; 530 531 v += getpid() & 0xFFFF; 532 533 LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v))); 534} 535 536TEST(logcat, blocking_clear) { 537 FILE *fp; 538 unsigned long long v = 0xDEADBEEFA55C0000ULL; 539 540 pid_t pid = getpid(); 541 542 v += pid & 0xFFFF; 543 544 // This test is racey; an event occurs between clear and dump. 545 // We accept that we will get a false positive, but never a false negative. 546 ASSERT_TRUE(NULL != (fp = popen( 547 "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&" 548 " logcat -b events -c 2>&1 ;" 549 " logcat -v brief -b events 2>&1", 550 "r"))); 551 552 char buffer[5120]; 553 554 int count = 0; 555 556 int signals = 0; 557 558 signal(SIGALRM, caught_blocking_clear); 559 alarm(2); 560 while (fgets(buffer, sizeof(buffer), fp)) { 561 562 if (!strncmp(buffer, "clearLog: ", 10)) { 563 fprintf(stderr, "WARNING: Test lacks permission to run :-(\n"); 564 count = signals = 1; 565 break; 566 } 567 568 if (!strncmp(buffer, "DONE", 4)) { 569 break; 570 } 571 572 ++count; 573 574 int p; 575 unsigned long long l; 576 577 if ((2 != sscanf(buffer, "I/[0] ( %u): %lld", &p, &l)) 578 || (p != pid)) { 579 continue; 580 } 581 582 if (l == v) { 583 if (count > 1) { 584 fprintf(stderr, "WARNING: Possible false positive\n"); 585 } 586 ++signals; 587 break; 588 } 589 } 590 alarm(0); 591 signal(SIGALRM, SIG_DFL); 592 593 // Generate SIGPIPE 594 fclose(fp); 595 caught_blocking_clear(0); 596 597 pclose(fp); 598 599 EXPECT_LE(1, count); 600 601 EXPECT_EQ(1, signals); 602} 603 604static bool get_white_black(char **list) { 605 FILE *fp; 606 607 fp = popen("logcat -p 2>/dev/null", "r"); 608 if (fp == NULL) { 609 fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n"); 610 return false; 611 } 612 613 char buffer[5120]; 614 615 while (fgets(buffer, sizeof(buffer), fp)) { 616 char *hold = *list; 617 char *buf = buffer; 618 while (isspace(*buf)) { 619 ++buf; 620 } 621 char *end = buf + strlen(buf); 622 while (isspace(*--end) && (end >= buf)) { 623 *end = '\0'; 624 } 625 if (end < buf) { 626 continue; 627 } 628 if (hold) { 629 asprintf(list, "%s %s", hold, buf); 630 free(hold); 631 } else { 632 asprintf(list, "%s", buf); 633 } 634 } 635 pclose(fp); 636 return *list != NULL; 637} 638 639static bool set_white_black(const char *list) { 640 FILE *fp; 641 642 char buffer[5120]; 643 644 snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : ""); 645 fp = popen(buffer, "r"); 646 if (fp == NULL) { 647 fprintf(stderr, "ERROR: %s\n", buffer); 648 return false; 649 } 650 651 while (fgets(buffer, sizeof(buffer), fp)) { 652 char *buf = buffer; 653 while (isspace(*buf)) { 654 ++buf; 655 } 656 char *end = buf + strlen(buf); 657 while ((end > buf) && isspace(*--end)) { 658 *end = '\0'; 659 } 660 if (end <= buf) { 661 continue; 662 } 663 fprintf(stderr, "%s\n", buf); 664 pclose(fp); 665 return false; 666 } 667 return pclose(fp) == 0; 668} 669 670TEST(logcat, white_black_adjust) { 671 char *list = NULL; 672 char *adjust = NULL; 673 674 get_white_black(&list); 675 676 static const char adjustment[] = "~! 300/20 300/25 2000 ~1000/5 ~1000/30"; 677 ASSERT_EQ(true, set_white_black(adjustment)); 678 ASSERT_EQ(true, get_white_black(&adjust)); 679 EXPECT_STREQ(adjustment, adjust); 680 free(adjust); 681 adjust = NULL; 682 683 static const char adjustment2[] = "300/20 300/21 2000 ~1000"; 684 ASSERT_EQ(true, set_white_black(adjustment2)); 685 ASSERT_EQ(true, get_white_black(&adjust)); 686 EXPECT_STREQ(adjustment2, adjust); 687 free(adjust); 688 adjust = NULL; 689 690 ASSERT_EQ(true, set_white_black(list)); 691 get_white_black(&adjust); 692 EXPECT_STREQ(list ? list : "", adjust ? adjust : ""); 693 free(adjust); 694 adjust = NULL; 695 696 free(list); 697 list = NULL; 698} 699