blob: 70f8fe951cea601175f00dbbb6612716dd8c7c79 [file] [log] [blame]
Mark Salyzynd3ba6652014-02-28 13:44:12 -08001/*
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 <signal.h>
19#include <stdio.h>
20#include <string.h>
21
22#include <gtest/gtest.h>
23
24#include "cutils/sockets.h"
25#include "log/logger.h"
26
27#define __unused __attribute__((__unused__))
28
29/*
30 * returns statistics
31 */
32static void my_android_logger_get_statistics(char *buf, size_t len)
33{
34 snprintf(buf, len, "getStatistics 0 1 2 3");
35 int sock = socket_local_client("logd",
36 ANDROID_SOCKET_NAMESPACE_RESERVED,
37 SOCK_STREAM);
38 if (sock >= 0) {
39 if (write(sock, buf, strlen(buf) + 1) > 0) {
40 read(sock, buf, len);
41 }
42 close(sock);
43 }
44}
45
46static void alloc_statistics(char **buffer, size_t *length)
47{
48 size_t len = 8192;
49 char *buf;
50
51 for(int retry = 32; (retry >= 0); delete [] buf, --retry) {
52 buf = new char [len];
53 my_android_logger_get_statistics(buf, len);
54
55 buf[len-1] = '\0';
56 size_t ret = atol(buf) + 1;
57 if (ret < 4) {
58 delete [] buf;
59 buf = NULL;
60 break;
61 }
62 bool check = ret <= len;
63 len = ret;
64 if (check) {
65 break;
66 }
67 len += len / 8; // allow for some slop
68 }
69 *buffer = buf;
70 *length = len;
71}
72
73static char *find_benchmark_spam(char *cp)
74{
75 // liblog_benchmarks has been run designed to SPAM. The signature of
76 // a noisiest UID statistics is one of the following:
77 //
78 // main: UID/PID Total size/num Now UID/PID[?] Total
79 // 0 7500306/304207 71608/3183 0/4225? 7454388/303656
80 // -or-
81 // 0/gone 7454388/303656
82 //
83 // basically if we see a *large* number of 0/????? entries
84 unsigned long value;
85 do {
86 char *benchmark = strstr(cp, " 0/");
87 char *benchmark_newline = strstr(cp, "\n0/");
88 if (!benchmark) {
89 benchmark = benchmark_newline;
90 }
91 if (benchmark_newline && (benchmark > benchmark_newline)) {
92 benchmark = benchmark_newline;
93 }
94 cp = benchmark;
95 if (!cp) {
96 break;
97 }
98 cp += 3;
99 while (isdigit(*cp) || (*cp == 'g') || (*cp == 'o') || (*cp == 'n')) {
100 ++cp;
101 }
102 value = 0;
103 // ###? or gone
104 if ((*cp == '?') || (*cp == 'e')) {
105 while (*++cp == ' ');
106 while (isdigit(*cp)) {
107 value = value * 10ULL + *cp - '0';
108 ++cp;
109 }
110 }
111 } while ((value < 900000ULL) && *cp);
112 return cp;
113}
114
115TEST(logd, statistics) {
116 size_t len;
117 char *buf;
118
119 alloc_statistics(&buf, &len);
120
121#ifdef TARGET_USES_LOGD
122 ASSERT_TRUE(NULL != buf);
123#else
124 if (!buf) {
125 return;
126 }
127#endif
128
129 // remove trailing FF
130 char *cp = buf + len - 1;
131 *cp = '\0';
132 bool truncated = *--cp != '\f';
133 if (!truncated) {
134 *cp = '\0';
135 }
136
137 // squash out the byte count
138 cp = buf;
139 if (!truncated) {
140 while (isdigit(*cp) || (*cp == '\n')) {
141 ++cp;
142 }
143 }
144
145 fprintf(stderr, "%s", cp);
146
147 EXPECT_LT((size_t)64, strlen(cp));
148
149 EXPECT_EQ(0, truncated);
150
151#ifdef TARGET_USES_LOGD
152 char *main_logs = strstr(cp, "\nmain:");
153 EXPECT_TRUE(NULL != main_logs);
154
155 char *radio_logs = strstr(cp, "\nradio:");
156 EXPECT_TRUE(NULL != radio_logs);
157
158 char *system_logs = strstr(cp, "\nsystem:");
159 EXPECT_TRUE(NULL != system_logs);
160
161 char *events_logs = strstr(cp, "\nevents:");
162 EXPECT_TRUE(NULL != events_logs);
163#endif
164
165 // Parse timing stats
166
167 cp = strstr(cp, "Minimum time between log events per dgram_qlen:");
168
169 char *log_events_per_span = cp;
170
171 if (cp) {
172 while (*cp && (*cp != '\n')) {
173 ++cp;
174 }
175 if (*cp == '\n') {
176 ++cp;
177 }
178
179 char *list_of_spans = cp;
180 EXPECT_NE('\0', *list_of_spans);
181
182 unsigned short number_of_buckets = 0;
183 unsigned short *dgram_qlen = NULL;
184 unsigned short bucket = 0;
185 while (*cp && (*cp != '\n')) {
186 bucket = 0;
187 while (isdigit(*cp)) {
188 bucket = bucket * 10 + *cp - '0';
189 ++cp;
190 }
191 while (*cp == ' ') {
192 ++cp;
193 }
194 if (!bucket) {
195 break;
196 }
197 unsigned short *new_dgram_qlen = new unsigned short[number_of_buckets + 1];
198 EXPECT_TRUE(new_dgram_qlen != NULL);
199 if (dgram_qlen) {
200 memcpy(new_dgram_qlen, dgram_qlen, sizeof(*dgram_qlen) * number_of_buckets);
201 delete [] dgram_qlen;
202 }
203
204 dgram_qlen = new_dgram_qlen;
205 dgram_qlen[number_of_buckets++] = bucket;
206 }
207
208 char *end_of_spans = cp;
209 EXPECT_NE('\0', *end_of_spans);
210
211 EXPECT_LT(5, number_of_buckets);
212
213 unsigned long long *times = new unsigned long long [number_of_buckets];
214 ASSERT_TRUE(times != NULL);
215
216 memset(times, 0, sizeof(*times) * number_of_buckets);
217
218 while (*cp == '\n') {
219 ++cp;
220 }
221
222 unsigned short number_of_values = 0;
223 unsigned long long value;
224 while (*cp && (*cp != '\n')) {
225 EXPECT_GE(number_of_buckets, number_of_values);
226
227 value = 0;
228 while (isdigit(*cp)) {
229 value = value * 10ULL + *cp - '0';
230 ++cp;
231 }
232
233 switch(*cp) {
234 case ' ':
235 case '\n':
236 value *= 1000ULL;
237 /* FALLTHRU */
238 case 'm':
239 value *= 1000ULL;
240 /* FALLTHRU */
241 case 'u':
242 value *= 1000ULL;
243 /* FALLTHRU */
244 case 'n':
245 default:
246 break;
247 }
248 while (*++cp == ' ');
249
250 if (!value) {
251 break;
252 }
253
254 times[number_of_values] = value;
255 ++number_of_values;
256 }
257
258#ifdef TARGET_USES_LOGD
259 EXPECT_EQ(number_of_values, number_of_buckets);
260#endif
261
262 FILE *fp;
263 ASSERT_TRUE(NULL != (fp = fopen("/proc/sys/net/unix/max_dgram_qlen", "r")));
264
265 unsigned max_dgram_qlen = 0;
266 fscanf(fp, "%u", &max_dgram_qlen);
267
268 fclose(fp);
269
270 // Find launch point
271 unsigned short launch = 0;
272 unsigned long long total = 0;
273 do {
274 total += times[launch];
275 } while (((++launch < number_of_buckets)
276 && ((total / launch) >= (times[launch] / 8ULL)))
277 || (launch == 1)); // too soon
278
279 bool failure = number_of_buckets <= launch;
280 if (!failure) {
281 unsigned short l = launch;
282 if (l >= number_of_buckets) {
283 l = number_of_buckets - 1;
284 }
285 failure = max_dgram_qlen < dgram_qlen[l];
286 }
287
288 // We can get failure if at any time liblog_benchmarks has been run
289 // because designed to overload /proc/sys/net/unix/max_dgram_qlen even
290 // at excessive values like 20000. It does so to measure the raw processing
291 // performance of logd.
292 if (failure) {
293 cp = find_benchmark_spam(cp);
294 }
295
296 if (cp) {
297 // Fake a failure, but without the failure code
298 if (number_of_buckets <= launch) {
299 printf ("Expected: number_of_buckets > launch, actual: %u vs %u\n",
300 number_of_buckets, launch);
301 }
302 if (launch >= number_of_buckets) {
303 launch = number_of_buckets - 1;
304 }
305 if (max_dgram_qlen < dgram_qlen[launch]) {
306 printf ("Expected: max_dgram_qlen >= dgram_qlen[%d],"
307 " actual: %u vs %u\n",
308 launch, max_dgram_qlen, dgram_qlen[launch]);
309 }
310 } else
311#ifndef TARGET_USES_LOGD
312 if (total)
313#endif
314 {
315 EXPECT_GT(number_of_buckets, launch);
316 if (launch >= number_of_buckets) {
317 launch = number_of_buckets - 1;
318 }
319 EXPECT_GE(max_dgram_qlen, dgram_qlen[launch]);
320 }
321
322 delete [] dgram_qlen;
323 delete [] times;
324 }
325 delete [] buf;
326}
327
328static void caught_signal(int signum __unused) { }
329
330static void dump_log_msg(const char *prefix,
331 log_msg *msg, unsigned int version, int lid) {
332 switch(msg->entry.hdr_size) {
333 case 0:
334 version = 1;
335 break;
336
337 case sizeof(msg->entry_v2):
338 if (version == 0) {
339 version = 2;
340 }
341 break;
342 }
343
344 fprintf(stderr, "%s: v%u[%u] ", prefix, version, msg->len());
345 if (version != 1) {
346 fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
347 }
348 fprintf(stderr, "pid=%u tid=%u %u.%09u ",
349 msg->entry.pid, msg->entry.tid, msg->entry.sec, msg->entry.nsec);
350 switch(version) {
351 case 1:
352 break;
353 case 2:
354 fprintf(stderr, "euid=%u ", msg->entry_v2.euid);
355 break;
356 case 3:
357 default:
358 lid = msg->entry.lid;
359 break;
360 }
361
362 switch(lid) {
363 case 0:
364 fprintf(stderr, "lid=main ");
365 break;
366 case 1:
367 fprintf(stderr, "lid=radio ");
368 break;
369 case 2:
370 fprintf(stderr, "lid=events ");
371 break;
372 case 3:
373 fprintf(stderr, "lid=system ");
374 break;
375 default:
376 if (lid >= 0) {
377 fprintf(stderr, "lid=%d ", lid);
378 }
379 }
380
381 unsigned int len = msg->entry.len;
382 fprintf(stderr, "msg[%u]={", len);
383 unsigned char *cp = reinterpret_cast<unsigned char *>(msg->msg());
384 while(len) {
385 unsigned char *p = cp;
386 while (*p && (((' ' <= *p) && (*p < 0x7F)) || (*p == '\n'))) {
387 ++p;
388 }
389 if (((p - cp) > 3) && !*p && ((unsigned int)(p - cp) < len)) {
390 fprintf(stderr, "\"");
391 while (*cp) {
392 fprintf(stderr, (*cp != '\n') ? "%c" : "\\n", *cp);
393 ++cp;
394 --len;
395 }
396 fprintf(stderr, "\"");
397 } else {
398 fprintf(stderr, "%02x", *cp);
399 }
400 ++cp;
401 if (--len) {
402 fprintf(stderr, ", ");
403 }
404 }
405 fprintf(stderr, "}\n");
406}
407
408TEST(logd, both) {
409 log_msg msg;
410
411 // check if we can read any logs from logd
412 bool user_logger_available = false;
413 bool user_logger_content = false;
414
415 int fd = socket_local_client("logdr",
416 ANDROID_SOCKET_NAMESPACE_RESERVED,
417 SOCK_SEQPACKET);
418 if (fd >= 0) {
419 struct sigaction ignore, old_sigaction;
420 memset(&ignore, 0, sizeof(ignore));
421 ignore.sa_handler = caught_signal;
422 sigemptyset(&ignore.sa_mask);
423 sigaction(SIGALRM, &ignore, &old_sigaction);
424 unsigned int old_alarm = alarm(10);
425
426 static const char ask[] = "dumpAndClose lids=0,1,2,3";
427 user_logger_available = write(fd, ask, sizeof(ask)) == sizeof(ask);
428
429 user_logger_content = recv(fd, msg.buf, sizeof(msg), 0) > 0;
430
431 if (user_logger_content) {
432 dump_log_msg("user", &msg, 3, -1);
433 }
434
435 alarm(0);
436 sigaction(SIGALRM, &old_sigaction, NULL);
437
438 close(fd);
439 }
440
441 // check if we can read any logs from kernel logger
442 bool kernel_logger_available = false;
443 bool kernel_logger_content = false;
444
445 static const char *loggers[] = {
446 "/dev/log/main", "/dev/log_main",
447 "/dev/log/radio", "/dev/log_radio",
448 "/dev/log/events", "/dev/log_events",
449 "/dev/log/system", "/dev/log_system",
450 };
451
452 for (unsigned int i = 0; i < (sizeof(loggers) / sizeof(loggers[0])); ++i) {
453 fd = open(loggers[i], O_RDONLY);
454 if (fd < 0) {
455 continue;
456 }
457 kernel_logger_available = true;
458 fcntl(fd, F_SETFL, O_RDONLY | O_NONBLOCK);
459 int result = TEMP_FAILURE_RETRY(read(fd, msg.buf, sizeof(msg)));
460 if (result > 0) {
461 kernel_logger_content = true;
462 dump_log_msg("kernel", &msg, 0, i / 2);
463 }
464 close(fd);
465 }
466
467 static const char yes[] = "\xE2\x9C\x93";
468 static const char no[] = "\xE2\x9c\x98";
469 fprintf(stderr,
470 "LOGGER Available Content\n"
471 "user %-13s%s\n"
472 "kernel %-13s%s\n"
473 " status %-11s%s\n",
474 (user_logger_available) ? yes : no,
475 (user_logger_content) ? yes : no,
476 (kernel_logger_available) ? yes : no,
477 (kernel_logger_content) ? yes : no,
478 (user_logger_available && kernel_logger_available) ? "WARNING" : "ok",
479 (user_logger_content && kernel_logger_content) ? "ERROR" : "ok");
480
481 if (user_logger_available && kernel_logger_available) {
482 printf("WARNING: kernel & user logger; both consuming resources!!!\n");
483 }
484
485 EXPECT_EQ(0, user_logger_content && kernel_logger_content);
486 EXPECT_EQ(0, !user_logger_content && !kernel_logger_content);
487}
488
489// BAD ROBOT
490// Benchmark threshold are generally considered bad form unless there is
491// is some human love applied to the continued maintenance and whether the
492// thresholds are tuned on a per-target basis. Here we check if the values
493// are more than double what is expected. Doubling will not prevent failure
494// on busy or low-end systems that could have a tendency to stretch values.
495//
496// The primary goal of this test is to simulate a spammy app (benchmark
497// being the worst) and check to make sure the logger can deal with it
498// appropriately by checking all the statistics are in an expected range.
499//
500TEST(logd, benchmark) {
501 size_t len;
502 char *buf;
503
504 alloc_statistics(&buf, &len);
505 bool benchmark_already_run = buf && find_benchmark_spam(buf);
506 delete [] buf;
507
508 if (benchmark_already_run) {
509 fprintf(stderr, "WARNING: spam already present and too much history\n"
510 " false OK for prune by worst UID check\n");
511 }
512
513 FILE *fp;
514
515 // Introduce some extreme spam for the worst UID filter
516 ASSERT_TRUE(NULL != (fp = popen(
517 "/data/nativetest/liblog-benchmarks/liblog-benchmarks",
518 "r")));
519
520 char buffer[5120];
521
522 static const char *benchmarks[] = {
523 "BM_log_maximum_retry ",
524 "BM_log_maximum ",
525 "BM_clock_overhead ",
526 "BM_log_overhead ",
527 "BM_log_latency ",
528 "BM_log_delay "
529 };
530 static const unsigned int log_maximum_retry = 0;
531 static const unsigned int log_maximum = 1;
532 static const unsigned int clock_overhead = 2;
533 static const unsigned int log_overhead = 3;
534 static const unsigned int log_latency = 4;
535 static const unsigned int log_delay = 5;
536
537 unsigned long ns[sizeof(benchmarks) / sizeof(benchmarks[0])];
538
539 memset(ns, 0, sizeof(ns));
540
541 while (fgets(buffer, sizeof(buffer), fp)) {
542 for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
543 if (strncmp(benchmarks[i], buffer, strlen(benchmarks[i]))) {
544 continue;
545 }
546 sscanf(buffer, "%*s %lu %lu", &ns[i], &ns[i]);
547 fprintf(stderr, "%-22s%8lu\n", benchmarks[i], ns[i]);
548 }
549 }
550 int ret = pclose(fp);
551
552 if (!WIFEXITED(ret) || (WEXITSTATUS(ret) == 127)) {
553 fprintf(stderr,
554 "WARNING: "
555 "/data/nativetest/liblog-benchmarks/liblog-benchmarks missing\n"
556 " can not perform test\n");
557 return;
558 }
559
560#ifdef TARGET_USES_LOGD
561 EXPECT_GE(100000UL, ns[log_maximum_retry]); // 42777 user
562#else
563 EXPECT_GE(10000UL, ns[log_maximum_retry]); // 5636 kernel
564#endif
565
566#ifdef TARGET_USES_LOGD
567 EXPECT_GE(25000UL, ns[log_maximum]); // 14055 user
568#else
569 EXPECT_GE(10000UL, ns[log_maximum]); // 5637 kernel
570#endif
571
572 EXPECT_GE(4000UL, ns[clock_overhead]); // 2008
573
574#ifdef TARGET_USES_LOGD
575 EXPECT_GE(250000UL, ns[log_overhead]); // 113219 user
576#else
577 EXPECT_GE(100000UL, ns[log_overhead]); // 50945 kernel
578#endif
579
580#ifdef TARGET_USES_LOGD
581 EXPECT_GE(7500UL, ns[log_latency]); // 3718 user space
582#else
583 EXPECT_GE(500000UL, ns[log_latency]); // 254200 kernel
584#endif
585
586#ifdef TARGET_USES_LOGD
587 EXPECT_GE(20000000UL, ns[log_delay]); // 9542541 user
588#else
589 EXPECT_GE(55000UL, ns[log_delay]); // 27341 kernel
590#endif
591
592 for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
593 EXPECT_NE(0UL, ns[i]);
594 }
595
596 alloc_statistics(&buf, &len);
597
598#ifdef TARGET_USES_LOGD
599 bool collected_statistics = !!buf;
600 EXPECT_EQ(true, collected_statistics);
601#else
602 if (!buf) {
603 return;
604 }
605#endif
606
607 ASSERT_TRUE(NULL != buf);
608 EXPECT_TRUE(find_benchmark_spam(buf) != NULL);
609
610 delete [] buf;
611}