1 /* 2 * Copyright (C) 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 <fcntl.h> 18 #include <poll.h> 19 #include <signal.h> 20 #include <stdio.h> 21 #include <string.h> 22 23 #include <gtest/gtest.h> 24 25 #include "cutils/sockets.h" 26 #include "log/log.h" 27 #include "log/logger.h" 28 29 #define __unused __attribute__((__unused__)) 30 31 /* 32 * returns statistics 33 */ 34 static void my_android_logger_get_statistics(char *buf, size_t len) 35 { 36 snprintf(buf, len, "getStatistics 0 1 2 3 4"); 37 int sock = socket_local_client("logd", 38 ANDROID_SOCKET_NAMESPACE_RESERVED, 39 SOCK_STREAM); 40 if (sock >= 0) { 41 if (write(sock, buf, strlen(buf) + 1) > 0) { 42 ssize_t ret; 43 while ((ret = read(sock, buf, len)) > 0) { 44 if ((size_t)ret == len) { 45 break; 46 } 47 len -= ret; 48 buf += ret; 49 50 struct pollfd p = { 51 .fd = sock, 52 .events = POLLIN, 53 .revents = 0 54 }; 55 56 ret = poll(&p, 1, 20); 57 if ((ret <= 0) || !(p.revents & POLLIN)) { 58 break; 59 } 60 } 61 } 62 close(sock); 63 } 64 } 65 66 static void alloc_statistics(char **buffer, size_t *length) 67 { 68 size_t len = 8192; 69 char *buf; 70 71 for(int retry = 32; (retry >= 0); delete [] buf, --retry) { 72 buf = new char [len]; 73 my_android_logger_get_statistics(buf, len); 74 75 buf[len-1] = '\0'; 76 size_t ret = atol(buf) + 1; 77 if (ret < 4) { 78 delete [] buf; 79 buf = NULL; 80 break; 81 } 82 bool check = ret <= len; 83 len = ret; 84 if (check) { 85 break; 86 } 87 len += len / 8; // allow for some slop 88 } 89 *buffer = buf; 90 *length = len; 91 } 92 93 static char *find_benchmark_spam(char *cp) 94 { 95 // liblog_benchmarks has been run designed to SPAM. The signature of 96 // a noisiest UID statistics is: 97 // 98 // Chattiest UIDs in main log buffer: Size Pruned 99 // UID PACKAGE BYTES LINES 100 // 0 root 54164 147569 101 // 102 char *benchmark = NULL; 103 do { 104 static const char signature[] = "\n0 root "; 105 106 benchmark = strstr(cp, signature); 107 if (!benchmark) { 108 break; 109 } 110 cp = benchmark + sizeof(signature); 111 while (isspace(*cp)) { 112 ++cp; 113 } 114 benchmark = cp; 115 while (isdigit(*cp)) { 116 ++cp; 117 } 118 while (isspace(*cp)) { 119 ++cp; 120 } 121 unsigned long value = 0; 122 while (isdigit(*cp)) { 123 value = value * 10ULL + *cp - '0'; 124 ++cp; 125 } 126 if (value > 100000UL) { 127 break; 128 } 129 benchmark = NULL; 130 } while (*cp); 131 return benchmark; 132 } 133 134 TEST(logd, statistics) { 135 size_t len; 136 char *buf; 137 138 alloc_statistics(&buf, &len); 139 140 #ifdef TARGET_USES_LOGD 141 ASSERT_TRUE(NULL != buf); 142 #else 143 if (!buf) { 144 return; 145 } 146 #endif 147 148 // remove trailing FF 149 char *cp = buf + len - 1; 150 *cp = '\0'; 151 bool truncated = *--cp != '\f'; 152 if (!truncated) { 153 *cp = '\0'; 154 } 155 156 // squash out the byte count 157 cp = buf; 158 if (!truncated) { 159 while (isdigit(*cp) || (*cp == '\n')) { 160 ++cp; 161 } 162 } 163 164 fprintf(stderr, "%s", cp); 165 166 EXPECT_LT((size_t)64, strlen(cp)); 167 168 EXPECT_EQ(0, truncated); 169 170 #ifdef TARGET_USES_LOGD 171 char *main_logs = strstr(cp, "\nChattiest UIDs in main "); 172 EXPECT_TRUE(NULL != main_logs); 173 174 char *radio_logs = strstr(cp, "\nChattiest UIDs in radio "); 175 EXPECT_TRUE(NULL != radio_logs); 176 177 char *system_logs = strstr(cp, "\nChattiest UIDs in system "); 178 EXPECT_TRUE(NULL != system_logs); 179 180 char *events_logs = strstr(cp, "\nChattiest UIDs in events "); 181 EXPECT_TRUE(NULL != events_logs); 182 #endif 183 184 delete [] buf; 185 } 186 187 static void caught_signal(int signum __unused) { } 188 189 static void dump_log_msg(const char *prefix, 190 log_msg *msg, unsigned int version, int lid) { 191 switch(msg->entry.hdr_size) { 192 case 0: 193 version = 1; 194 break; 195 196 case sizeof(msg->entry_v2): 197 if (version == 0) { 198 version = 2; 199 } 200 break; 201 } 202 203 fprintf(stderr, "%s: v%u[%u] ", prefix, version, msg->len()); 204 if (version != 1) { 205 fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size); 206 } 207 fprintf(stderr, "pid=%u tid=%u %u.%09u ", 208 msg->entry.pid, msg->entry.tid, msg->entry.sec, msg->entry.nsec); 209 switch(version) { 210 case 1: 211 break; 212 case 2: 213 fprintf(stderr, "euid=%u ", msg->entry_v2.euid); 214 break; 215 case 3: 216 default: 217 lid = msg->entry.lid; 218 break; 219 } 220 221 switch(lid) { 222 case 0: 223 fprintf(stderr, "lid=main "); 224 break; 225 case 1: 226 fprintf(stderr, "lid=radio "); 227 break; 228 case 2: 229 fprintf(stderr, "lid=events "); 230 break; 231 case 3: 232 fprintf(stderr, "lid=system "); 233 break; 234 default: 235 if (lid >= 0) { 236 fprintf(stderr, "lid=%d ", lid); 237 } 238 } 239 240 unsigned int len = msg->entry.len; 241 fprintf(stderr, "msg[%u]={", len); 242 unsigned char *cp = reinterpret_cast<unsigned char *>(msg->msg()); 243 while(len) { 244 unsigned char *p = cp; 245 while (*p && (((' ' <= *p) && (*p < 0x7F)) || (*p == '\n'))) { 246 ++p; 247 } 248 if (((p - cp) > 3) && !*p && ((unsigned int)(p - cp) < len)) { 249 fprintf(stderr, "\""); 250 while (*cp) { 251 if (*cp != '\n') { 252 fprintf(stderr, "%c", *cp); 253 } else { 254 fprintf(stderr, "\\n"); 255 } 256 ++cp; 257 --len; 258 } 259 fprintf(stderr, "\""); 260 } else { 261 fprintf(stderr, "%02x", *cp); 262 } 263 ++cp; 264 if (--len) { 265 fprintf(stderr, ", "); 266 } 267 } 268 fprintf(stderr, "}\n"); 269 } 270 271 TEST(logd, both) { 272 log_msg msg; 273 274 // check if we can read any logs from logd 275 bool user_logger_available = false; 276 bool user_logger_content = false; 277 278 int fd = socket_local_client("logdr", 279 ANDROID_SOCKET_NAMESPACE_RESERVED, 280 SOCK_SEQPACKET); 281 if (fd >= 0) { 282 struct sigaction ignore, old_sigaction; 283 memset(&ignore, 0, sizeof(ignore)); 284 ignore.sa_handler = caught_signal; 285 sigemptyset(&ignore.sa_mask); 286 sigaction(SIGALRM, &ignore, &old_sigaction); 287 unsigned int old_alarm = alarm(10); 288 289 static const char ask[] = "dumpAndClose lids=0,1,2,3"; 290 user_logger_available = write(fd, ask, sizeof(ask)) == sizeof(ask); 291 292 user_logger_content = recv(fd, msg.buf, sizeof(msg), 0) > 0; 293 294 if (user_logger_content) { 295 dump_log_msg("user", &msg, 3, -1); 296 } 297 298 alarm(old_alarm); 299 sigaction(SIGALRM, &old_sigaction, NULL); 300 301 close(fd); 302 } 303 304 // check if we can read any logs from kernel logger 305 bool kernel_logger_available = false; 306 bool kernel_logger_content = false; 307 308 static const char *loggers[] = { 309 "/dev/log/main", "/dev/log_main", 310 "/dev/log/radio", "/dev/log_radio", 311 "/dev/log/events", "/dev/log_events", 312 "/dev/log/system", "/dev/log_system", 313 }; 314 315 for (unsigned int i = 0; i < (sizeof(loggers) / sizeof(loggers[0])); ++i) { 316 fd = open(loggers[i], O_RDONLY); 317 if (fd < 0) { 318 continue; 319 } 320 kernel_logger_available = true; 321 fcntl(fd, F_SETFL, O_RDONLY | O_NONBLOCK); 322 int result = TEMP_FAILURE_RETRY(read(fd, msg.buf, sizeof(msg))); 323 if (result > 0) { 324 kernel_logger_content = true; 325 dump_log_msg("kernel", &msg, 0, i / 2); 326 } 327 close(fd); 328 } 329 330 static const char yes[] = "\xE2\x9C\x93"; 331 static const char no[] = "\xE2\x9c\x98"; 332 fprintf(stderr, 333 "LOGGER Available Content\n" 334 "user %-13s%s\n" 335 "kernel %-13s%s\n" 336 " status %-11s%s\n", 337 (user_logger_available) ? yes : no, 338 (user_logger_content) ? yes : no, 339 (kernel_logger_available) ? yes : no, 340 (kernel_logger_content) ? yes : no, 341 (user_logger_available && kernel_logger_available) ? "ERROR" : "ok", 342 (user_logger_content && kernel_logger_content) ? "ERROR" : "ok"); 343 344 EXPECT_EQ(0, user_logger_available && kernel_logger_available); 345 EXPECT_EQ(0, !user_logger_available && !kernel_logger_available); 346 EXPECT_EQ(0, user_logger_content && kernel_logger_content); 347 EXPECT_EQ(0, !user_logger_content && !kernel_logger_content); 348 } 349 350 // BAD ROBOT 351 // Benchmark threshold are generally considered bad form unless there is 352 // is some human love applied to the continued maintenance and whether the 353 // thresholds are tuned on a per-target basis. Here we check if the values 354 // are more than double what is expected. Doubling will not prevent failure 355 // on busy or low-end systems that could have a tendency to stretch values. 356 // 357 // The primary goal of this test is to simulate a spammy app (benchmark 358 // being the worst) and check to make sure the logger can deal with it 359 // appropriately by checking all the statistics are in an expected range. 360 // 361 TEST(logd, benchmark) { 362 size_t len; 363 char *buf; 364 365 alloc_statistics(&buf, &len); 366 bool benchmark_already_run = buf && find_benchmark_spam(buf); 367 delete [] buf; 368 369 if (benchmark_already_run) { 370 fprintf(stderr, "WARNING: spam already present and too much history\n" 371 " false OK for prune by worst UID check\n"); 372 } 373 374 FILE *fp; 375 376 // Introduce some extreme spam for the worst UID filter 377 ASSERT_TRUE(NULL != (fp = popen( 378 "/data/nativetest/liblog-benchmarks/liblog-benchmarks", 379 "r"))); 380 381 char buffer[5120]; 382 383 static const char *benchmarks[] = { 384 "BM_log_maximum_retry ", 385 "BM_log_maximum ", 386 "BM_clock_overhead ", 387 "BM_log_overhead ", 388 "BM_log_latency ", 389 "BM_log_delay " 390 }; 391 static const unsigned int log_maximum_retry = 0; 392 static const unsigned int log_maximum = 1; 393 static const unsigned int clock_overhead = 2; 394 static const unsigned int log_overhead = 3; 395 static const unsigned int log_latency = 4; 396 static const unsigned int log_delay = 5; 397 398 unsigned long ns[sizeof(benchmarks) / sizeof(benchmarks[0])]; 399 400 memset(ns, 0, sizeof(ns)); 401 402 while (fgets(buffer, sizeof(buffer), fp)) { 403 for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) { 404 char *cp = strstr(buffer, benchmarks[i]); 405 if (!cp) { 406 continue; 407 } 408 sscanf(cp, "%*s %lu %lu", &ns[i], &ns[i]); 409 fprintf(stderr, "%-22s%8lu\n", benchmarks[i], ns[i]); 410 } 411 } 412 int ret = pclose(fp); 413 414 if (!WIFEXITED(ret) || (WEXITSTATUS(ret) == 127)) { 415 fprintf(stderr, 416 "WARNING: " 417 "/data/nativetest/liblog-benchmarks/liblog-benchmarks missing\n" 418 " can not perform test\n"); 419 return; 420 } 421 422 #ifdef TARGET_USES_LOGD 423 EXPECT_GE(200000UL, ns[log_maximum_retry]); // 104734 user 424 #else 425 EXPECT_GE(10000UL, ns[log_maximum_retry]); // 5636 kernel 426 #endif 427 428 #ifdef TARGET_USES_LOGD 429 EXPECT_GE(90000UL, ns[log_maximum]); // 46913 user 430 #else 431 EXPECT_GE(10000UL, ns[log_maximum]); // 5637 kernel 432 #endif 433 434 EXPECT_GE(4096UL, ns[clock_overhead]); // 4095 435 436 #ifdef TARGET_USES_LOGD 437 EXPECT_GE(250000UL, ns[log_overhead]); // 126886 user 438 #else 439 EXPECT_GE(100000UL, ns[log_overhead]); // 50945 kernel 440 #endif 441 442 #ifdef TARGET_USES_LOGD 443 EXPECT_GE(10000UL, ns[log_latency]); // 5669 user space 444 #else 445 EXPECT_GE(500000UL, ns[log_latency]); // 254200 kernel 446 #endif 447 448 #ifdef TARGET_USES_LOGD 449 EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user 450 #else 451 EXPECT_GE(55000UL, ns[log_delay]); // 27341 kernel 452 #endif 453 454 for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) { 455 EXPECT_NE(0UL, ns[i]); 456 } 457 458 alloc_statistics(&buf, &len); 459 460 #ifdef TARGET_USES_LOGD 461 bool collected_statistics = !!buf; 462 EXPECT_EQ(true, collected_statistics); 463 #else 464 if (!buf) { 465 return; 466 } 467 #endif 468 469 ASSERT_TRUE(NULL != buf); 470 471 char *benchmark_statistics_found = find_benchmark_spam(buf); 472 ASSERT_TRUE(benchmark_statistics_found != NULL); 473 474 // Check how effective the SPAM filter is, parse out Now size. 475 // 0 root 54164 147569 476 // ^-- benchmark_statistics_found 477 478 unsigned long nowSpamSize = atol(benchmark_statistics_found); 479 480 delete [] buf; 481 482 ASSERT_NE(0UL, nowSpamSize); 483 484 // Determine if we have the spam filter enabled 485 int sock = socket_local_client("logd", 486 ANDROID_SOCKET_NAMESPACE_RESERVED, 487 SOCK_STREAM); 488 489 ASSERT_TRUE(sock >= 0); 490 491 static const char getPruneList[] = "getPruneList"; 492 if (write(sock, getPruneList, sizeof(getPruneList)) > 0) { 493 char buffer[80]; 494 memset(buffer, 0, sizeof(buffer)); 495 read(sock, buffer, sizeof(buffer)); 496 char *cp = strchr(buffer, '\n'); 497 if (!cp || (cp[1] != '~') || (cp[2] != '!')) { 498 close(sock); 499 fprintf(stderr, 500 "WARNING: " 501 "Logger has SPAM filtration turned off \"%s\"\n", buffer); 502 return; 503 } 504 } else { 505 int save_errno = errno; 506 close(sock); 507 FAIL() << "Can not send " << getPruneList << " to logger -- " << strerror(save_errno); 508 } 509 510 static const unsigned long expected_absolute_minimum_log_size = 65536UL; 511 unsigned long totalSize = expected_absolute_minimum_log_size; 512 static const char getSize[] = { 513 'g', 'e', 't', 'L', 'o', 'g', 'S', 'i', 'z', 'e', ' ', 514 LOG_ID_MAIN + '0', '\0' 515 }; 516 if (write(sock, getSize, sizeof(getSize)) > 0) { 517 char buffer[80]; 518 memset(buffer, 0, sizeof(buffer)); 519 read(sock, buffer, sizeof(buffer)); 520 totalSize = atol(buffer); 521 if (totalSize < expected_absolute_minimum_log_size) { 522 fprintf(stderr, 523 "WARNING: " 524 "Logger had unexpected referenced size \"%s\"\n", buffer); 525 totalSize = expected_absolute_minimum_log_size; 526 } 527 } 528 close(sock); 529 530 // logd allows excursions to 110% of total size 531 totalSize = (totalSize * 11 ) / 10; 532 533 // 50% threshold for SPAM filter (<20% typical, lots of engineering margin) 534 ASSERT_GT(totalSize, nowSpamSize * 2); 535 } 536