Merge "logcat: test: inject() tuning"
am: 26a714a0ab
Change-Id: Ie7f63c15466e8d82cc61da100439466984b89472
diff --git a/liblog/include/log/logprint.h b/liblog/include/log/logprint.h
index 3509e7f..5b99c3c 100644
--- a/liblog/include/log/logprint.h
+++ b/liblog/include/log/logprint.h
@@ -42,11 +42,13 @@
FORMAT_MODIFIER_TIME_USEC, /* switches from msec to usec time precision */
FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
FORMAT_MODIFIER_YEAR, /* Adds year to date */
- FORMAT_MODIFIER_ZONE, /* Adds zone to date */
+ FORMAT_MODIFIER_ZONE, /* Adds zone to date, + UTC */
FORMAT_MODIFIER_EPOCH, /* Print time as seconds since Jan 1 1970 */
FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
FORMAT_MODIFIER_UID, /* Adds uid */
FORMAT_MODIFIER_DESCRIPT, /* Adds descriptive */
+ /* private, undocumented */
+ FORMAT_MODIFIER_TIME_NSEC, /* switches from msec to nsec time precision */
} AndroidLogPrintFormat;
typedef struct AndroidLogFormat_t AndroidLogFormat;
diff --git a/liblog/logprint.c b/liblog/logprint.c
index da80e36..18af9de 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -52,6 +52,7 @@
AndroidLogPrintFormat format;
bool colored_output;
bool usec_time_output;
+ bool nsec_time_output;
bool printable_output;
bool year_output;
bool zone_output;
@@ -210,6 +211,7 @@
p_ret->format = FORMAT_BRIEF;
p_ret->colored_output = false;
p_ret->usec_time_output = false;
+ p_ret->nsec_time_output = false;
p_ret->printable_output = false;
p_ret->year_output = false;
p_ret->zone_output = false;
@@ -258,6 +260,9 @@
case FORMAT_MODIFIER_TIME_USEC:
p_format->usec_time_output = true;
return 0;
+ case FORMAT_MODIFIER_TIME_NSEC:
+ p_format->nsec_time_output = true;
+ return 0;
case FORMAT_MODIFIER_PRINTABLE:
p_format->printable_output = true;
return 0;
@@ -309,6 +314,7 @@
else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR;
else if (strcmp(formatString, "colour") == 0) format = FORMAT_MODIFIER_COLOR;
else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC;
+ else if (strcmp(formatString, "nsec") == 0) format = FORMAT_MODIFIER_TIME_NSEC;
else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE;
else if (strcmp(formatString, "year") == 0) format = FORMAT_MODIFIER_YEAR;
else if (strcmp(formatString, "zone") == 0) format = FORMAT_MODIFIER_ZONE;
@@ -1496,7 +1502,8 @@
struct tm tmBuf;
#endif
struct tm* ptm;
- char timeBuf[64]; /* good margin, 23+nul for msec, 26+nul for usec */
+ /* good margin, 23+nul for msec, 26+nul for usec, 29+nul to nsec */
+ char timeBuf[64];
char prefixBuf[128], suffixBuf[128];
char priChar;
int prefixSuffixIsHeaderFooter = 0;
@@ -1550,7 +1557,10 @@
ptm);
}
len = strlen(timeBuf);
- if (p_format->usec_time_output) {
+ if (p_format->nsec_time_output) {
+ len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
+ ".%09ld", nsec);
+ } else if (p_format->usec_time_output) {
len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
".%06ld", nsec / US_PER_NSEC);
} else {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index d9935c3..4a171fd 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -298,6 +298,8 @@
" and individually flagged modifying adverbs can be added:\n"
" color descriptive epoch monotonic printable uid\n"
" usec UTC year zone\n"
+ // private and undocumented nsec, no signal, too much noise
+ // useful for -T or -t <timestamp> accurate testing though.
" -D, --dividers Print dividers between each log buffer\n"
" -c, --clear Clear (flush) the entire log and exit\n"
" if Log to File specified, clear fileset instead\n"
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 10d9e39..081bf92 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -107,6 +107,32 @@
EXPECT_LT(4, count);
}
+// If there is not enough background noise in the logs, then spam the logs to
+// permit tail checking so that the tests can progress.
+static size_t inject(ssize_t count) {
+ if (count <= 0) return 0;
+
+ static const size_t retry = 4;
+ size_t errors = retry;
+ size_t num = 0;
+ for(;;) {
+ log_time ts(CLOCK_MONOTONIC);
+ if (__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) >= 0) {
+ if (++num >= (size_t)count) {
+ // let data settle end-to-end
+ sleep(3);
+ return num;
+ }
+ errors = retry;
+ usleep(100); // ~32 per timer tick, we are a spammer regardless
+ } else if (--errors <= 0) {
+ return num;
+ }
+ }
+ // NOTREACH
+ return num;
+}
+
TEST(logcat, year) {
if (android_log_clockid() == CLOCK_MONOTONIC) {
@@ -114,35 +140,40 @@
return;
}
- FILE *fp;
+ int count;
+ int tries = 3; // in case run too soon after system start or buffer clear
- char needle[32];
- time_t now;
- time(&now);
- struct tm *ptm;
+ do {
+ FILE *fp;
+
+ char needle[32];
+ time_t now;
+ time(&now);
+ struct tm *ptm;
#if !defined(_WIN32)
- struct tm tmBuf;
- ptm = localtime_r(&now, &tmBuf);
+ struct tm tmBuf;
+ ptm = localtime_r(&now, &tmBuf);
#else
- ptm = localtime(&&now);
+ ptm = localtime(&&now);
#endif
- strftime(needle, sizeof(needle), "[ %Y-", ptm);
+ strftime(needle, sizeof(needle), "[ %Y-", ptm);
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v long -v year -b all -t 3 2>/dev/null",
- "r")));
+ ASSERT_TRUE(NULL != (fp = popen(
+ "logcat -v long -v year -b all -t 3 2>/dev/null",
+ "r")));
- char buffer[BIG_BUFFER];
+ char buffer[BIG_BUFFER];
- int count = 0;
+ count = 0;
- while (fgets(buffer, sizeof(buffer), fp)) {
- if (!strncmp(buffer, needle, strlen(needle))) {
- ++count;
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (!strncmp(buffer, needle, strlen(needle))) {
+ ++count;
+ }
}
- }
+ pclose(fp);
- pclose(fp);
+ } while ((count < 3) && --tries && inject(3 - count));
ASSERT_EQ(3, count);
}
@@ -179,32 +210,6 @@
return NULL;
}
-// If there is not enough background noise in the logs, then spam the logs to
-// permit tail checking so that the tests can progress.
-static size_t inject(ssize_t count) {
- if (count <= 0) return 0;
-
- static const size_t retry = 4;
- size_t errors = retry;
- size_t num = 0;
- for(;;) {
- log_time ts(CLOCK_MONOTONIC);
- if (__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) >= 0) {
- if (++num >= (size_t)count) {
- // let data settle end-to-end
- sleep(3);
- return num;
- }
- errors = retry;
- usleep(50);
- } else if (--errors <= 0) {
- return num;
- }
- }
- // NOTREACH
- return num;
-}
-
TEST(logcat, tz) {
if (android_log_clockid() == CLOCK_MONOTONIC) {
@@ -267,6 +272,8 @@
int tries = 4; // in case run too soon after system start or buffer clear
int count;
+ if (num > 10) ++tries;
+ if (num > 100) ++tries;
do {
char buffer[BIG_BUFFER];
@@ -310,25 +317,32 @@
int count;
char buffer[BIG_BUFFER];
char *last_timestamp = NULL;
+ // Hard to predict 100% if first (overlap) or second line will match.
+ // -v nsec will in a substantial majority be the second line.
char *first_timestamp = NULL;
- char *cp;
+ char *second_timestamp = NULL;
+ char *input;
int tries = 4; // in case run too soon after system start or buffer clear
- // Do not be tempted to use -v usec because that increases the
- // chances of an occasional test failure by 1000 (see below).
do {
- ASSERT_TRUE(NULL != (fp = popen("logcat -v long -b all -t 10 2>&1", "r")));
-
+ ASSERT_TRUE(NULL != (fp = popen("logcat"
+ " -v long"
+ " -v nsec"
+ " -b all"
+ " -t 10"
+ " 2>&1", "r")));
count = 0;
- while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+ while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
++count;
if (!first_timestamp) {
- first_timestamp = strdup(cp);
+ first_timestamp = strdup(input);
+ } else if (!second_timestamp) {
+ second_timestamp = strdup(input);
}
free(last_timestamp);
- last_timestamp = strdup(cp);
+ last_timestamp = strdup(input);
}
pclose(fp);
@@ -337,34 +351,80 @@
EXPECT_EQ(10, count); // We want _some_ history, too small, falses below
EXPECT_TRUE(last_timestamp != NULL);
EXPECT_TRUE(first_timestamp != NULL);
+ EXPECT_TRUE(second_timestamp != NULL);
- snprintf(buffer, sizeof(buffer), "logcat -v long -b all -t '%s' 2>&1",
- first_timestamp);
+ snprintf(buffer, sizeof(buffer), "logcat"
+ " -v long"
+ " -v nsec"
+ " -b all"
+ " -t '%s'"
+ " 2>&1",
+ first_timestamp);
ASSERT_TRUE(NULL != (fp = popen(buffer, "r")));
int second_count = 0;
int last_timestamp_count = -1;
- while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+ --count; // One less unless we match the first_timestamp
+ bool found = false;
+ while ((input = fgetLongTime(buffer, sizeof(buffer), fp))) {
++second_count;
+ // We want to highlight if we skip to the next entry.
+ // WAI, if the time in logd is *exactly*
+ // XX-XX XX:XX:XX.XXXXXX000 (usec) or XX-XX XX:XX:XX.XXX000000
+ // this can happen, but it should not happen with nsec.
+ // We can make this WAI behavior happen 1000 times less
+ // frequently if the caller does not use the -v usec flag,
+ // but always the second (always skip) if they use the
+ // (undocumented) -v nsec flag.
if (first_timestamp) {
- // we can get a transitory *extremely* rare failure if hidden
- // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
- EXPECT_STREQ(cp, first_timestamp);
+ found = !strcmp(input, first_timestamp);
+ if (found) {
+ ++count;
+ GTEST_LOG_(INFO) << "input = first("
+ << first_timestamp
+ << ")\n";
+ }
free(first_timestamp);
first_timestamp = NULL;
}
- if (!strcmp(cp, last_timestamp)) {
+ if (second_timestamp) {
+ found = found || !strcmp(input, second_timestamp);
+ if (!found) {
+ GTEST_LOG_(INFO) << "input("
+ << input
+ << ") != second("
+ << second_timestamp
+ << ")\n";
+ }
+ free(second_timestamp);
+ second_timestamp = NULL;
+ }
+ if (!strcmp(input, last_timestamp)) {
last_timestamp_count = second_count;
}
}
pclose(fp);
+ EXPECT_TRUE(found);
+ if (!found) {
+ if (first_timestamp) {
+ GTEST_LOG_(INFO) << "first = " << first_timestamp << "\n";
+ }
+ if (second_timestamp) {
+ GTEST_LOG_(INFO) << "second = " << second_timestamp << "\n";
+ }
+ if (last_timestamp) {
+ GTEST_LOG_(INFO) << "last = " << last_timestamp << "\n";
+ }
+ }
free(last_timestamp);
last_timestamp = NULL;
free(first_timestamp);
+ free(second_timestamp);
EXPECT_TRUE(first_timestamp == NULL);
+ EXPECT_TRUE(second_timestamp == NULL);
EXPECT_LE(count, second_count);
EXPECT_LE(count, last_timestamp_count);
}
@@ -747,7 +807,7 @@
ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
static const char log_filename[] = "log.txt";
- static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 256 -r 1024";
+ static const char logcat_cmd[] = "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
static const char cleanup_cmd[] = "rm -rf %s";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
@@ -769,9 +829,15 @@
}
char *line = NULL;
char *last_line = NULL; // this line is allowed to stutter, one-line overlap
- char *second_last_line = NULL;
+ char *second_last_line = NULL; // should never stutter
+ char *first_line = NULL; // help diagnose failure?
size_t len = 0;
while (getline(&line, &len, fp) != -1) {
+ if (!first_line) {
+ first_line = line;
+ line = NULL;
+ continue;
+ }
free(second_last_line);
second_last_line = last_line;
last_line = line;
@@ -789,6 +855,7 @@
if (!second_last_line) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
+ free(first_line);
return;
}
// re-run the command, it should only add a few lines more content if it
@@ -798,6 +865,8 @@
if (ret) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
+ free(second_last_line);
+ free(first_line);
return;
}
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
@@ -805,6 +874,8 @@
if (!dir) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));
+ free(second_last_line);
+ free(first_line);
return;
}
struct dirent *entry;
@@ -834,13 +905,18 @@
}
if (count > 1) {
char *brk = strpbrk(second_last_line, "\r\n");
- if (!brk) {
- brk = second_last_line + strlen(second_last_line);
- }
- fprintf(stderr, "\"%.*s\" occured %u times\n",
+ if (!brk) brk = second_last_line + strlen(second_last_line);
+ fprintf(stderr, "\"%.*s\" occurred %u times\n",
(int)(brk - second_last_line), second_last_line, count);
+ if (first_line) {
+ brk = strpbrk(first_line, "\r\n");
+ if (!brk) brk = first_line + strlen(first_line);
+ fprintf(stderr, "\"%.*s\" was first line, fault?\n",
+ (int)(brk - first_line), first_line);
+ }
}
free(second_last_line);
+ free(first_line);
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(IsFalse(system(command), command));