Merge "Revert "logcat: remove the -S option."" into main
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index e949c65..aece1fa 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -1088,59 +1088,64 @@
 }
 
 /*
- * Convert to printable from message to p buffer, return string length. If p is
- * NULL, do not copy, but still return the expected string length.
+ * Convert to printable from src to dst buffer, returning dst bytes used.
+ * If dst is NULL, do not copy, but still return the dst bytes required.
  */
-size_t convertPrintable(char* p, const char* message, size_t messageLen) {
-  char* begin = p;
-  bool print = p != NULL;
-  mbstate_t mb_state = {};
+size_t convertPrintable(char* dst0, const char* src0, size_t n) {
+  char* dst = dst0;
+  const unsigned char* src = reinterpret_cast<const unsigned char*>(src0);
+  mbstate_t mbs = {};
 
-  while (messageLen) {
-    char buf[6];
-    ssize_t len = sizeof(buf) - 1;
-    if ((size_t)len > messageLen) {
-      len = messageLen;
+  bool print = (dst != nullptr);
+
+  while (n > 0) {
+    // ASCII fast path to cover most logging; space and tab aren't escaped,
+    // but backslash is.
+    if ((*src >= ' ' && *src < 0x7f && *src != '\\') || *src == '\t') {
+      if (print) *dst = *src;
+      dst++;
+      src++;
+      n--;
+      continue;
     }
-    len = mbrtowc(nullptr, message, len, &mb_state);
 
-    if (len <= 0) {
-      snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
-      len = 1;
+    // Unprintable fast path #1: single-character C escapes.
+    if ((*src >= '\a' && *src <= '\r') || *src == '\\') {
+      if (print) {
+        dst[0] = '\\';
+        dst[1] = (*src == '\\') ? '\\' : "abtnvfr"[*src - '\a'];
+      }
+      dst += 2;
+      src++;
+      n--;
+      continue;
+    }
+    // Unprintable fast path #2: everything else below space, plus DEL.
+    if (*src < ' ' || *src == 0x7f) {
+      if (print) sprintf(dst, "\\x%02X", *src);
+      dst += 4;
+      src++;
+      n--;
+      continue;
+    }
+
+    // Are we at the start of a valid UTF-8 encoding?
+    ssize_t len = mbrtowc(nullptr, reinterpret_cast<const char*>(src), n, &mbs);
+    if (len > 0) {
+      if (print) memcpy(dst, src, len);
+      dst += len;
+      src += len;
+      n -= len;
     } else {
-      buf[0] = '\0';
-      if (len == 1) {
-        if (*message == '\a') {
-          strcpy(buf, "\\a");
-        } else if (*message == '\b') {
-          strcpy(buf, "\\b");
-        } else if (*message == '\t') {
-          strcpy(buf, "\t"); /* Do not escape tabs */
-        } else if (*message == '\v') {
-          strcpy(buf, "\\v");
-        } else if (*message == '\f') {
-          strcpy(buf, "\\f");
-        } else if (*message == '\r') {
-          strcpy(buf, "\\r");
-        } else if (*message == '\\') {
-          strcpy(buf, "\\\\");
-        } else if ((*message < ' ') || (*message >= 0x7f)) {
-          snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
-        }
-      }
-      if (!buf[0]) {
-        strncpy(buf, message, len);
-        buf[len] = '\0';
-      }
+      // Assume it's just one bad byte, and try again after escaping it.
+      if (print) sprintf(dst, "\\x%02X", *src);
+      dst += 4;
+      src++;
+      n--;
     }
-    if (print) {
-      strcpy(p, buf);
-    }
-    p += strlen(buf);
-    message += len;
-    messageLen -= len;
   }
-  return p - begin;
+  if (print) *dst = '\0';
+  return dst - dst0;
 }
 
 #ifdef __ANDROID__
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index 7ab5cc8..657901a 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -315,4 +315,8 @@
   return 1;
 }
 
+int __android_log_security() {
+  return 0;
+}
+
 #endif
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 06b6126..f5c2680 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -33,15 +33,16 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wno-unused-result",
         "-fno-builtin",
     ],
     shared_libs: [
-        "libm",
         "libbase",
         "libcutils",
     ],
     static_libs: ["liblog"],
     srcs: ["liblog_benchmark.cpp"],
+    host_supported: true,
 }
 
 // -----------------------------------------------------------------------------
@@ -54,6 +55,7 @@
         "liblog_convertPrintable_fuzzer.cpp",
     ],
     static_libs: ["liblog"],
+    host_supported: true,
 }
 
 // -----------------------------------------------------------------------------
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 5ca2c8c..6dc5da6 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -18,6 +18,7 @@
 #include <inttypes.h>
 #include <poll.h>
 #include <sched.h>
+#include <signal.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -34,6 +35,10 @@
 #include <log/log_read.h>
 #include <private/android_logger.h>
 
+#include "test_utils.h"
+
+size_t convertPrintable(char*, const char*, size_t);
+
 BENCHMARK_MAIN();
 
 // enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
@@ -194,7 +199,7 @@
   pmsg_header.pid = getpid();
 
   android_log_header_t header;
-  header.tid = gettid();
+  header.tid = 0;
   header.realtime.tv_sec = ts.tv_sec;
   header.realtime.tv_nsec = ts.tv_nsec;
 
@@ -280,7 +285,7 @@
   buffer->pmsg_header.uid = getuid();
   buffer->pmsg_header.pid = getpid();
 
-  buffer->header.tid = gettid();
+  buffer->header.tid = 0;
   buffer->header.realtime.tv_sec = ts.tv_sec;
   buffer->header.realtime.tv_nsec = ts.tv_nsec;
 
@@ -354,7 +359,7 @@
   buffer->pmsg_header.uid = getuid();
   buffer->pmsg_header.pid = getpid();
 
-  buffer->header.tid = gettid();
+  buffer->header.tid = 0;
   buffer->header.realtime.tv_sec = ts.tv_sec;
   buffer->header.realtime.tv_nsec = ts.tv_nsec;
 
@@ -428,7 +433,7 @@
   buffer->pmsg_header.uid = getuid();
   buffer->pmsg_header.pid = getpid();
 
-  buffer->header.tid = gettid();
+  buffer->header.tid = 0;
   buffer->header.realtime.tv_sec = ts.tv_sec;
   buffer->header.realtime.tv_nsec = ts.tv_nsec;
 
@@ -500,7 +505,7 @@
   buffer->pmsg_header.uid = getuid();
   buffer->pmsg_header.pid = getpid();
 
-  buffer->header.tid = gettid();
+  buffer->header.tid = 0;
   buffer->header.realtime.tv_sec = ts.tv_sec;
   buffer->header.realtime.tv_nsec = ts.tv_nsec;
 
@@ -633,7 +638,7 @@
   return l;
 }
 
-static const int alarm_time = 3;
+static const int alarm_time = getAlarmSeconds(3);
 
 /*
  *	Measure the time it takes for the logd posting call to acquire the
@@ -787,9 +792,6 @@
 }
 BENCHMARK(BM_is_loggable);
 
-/*
- *	Measure the time it takes for __android_log_security.
- */
 static void BM_security(benchmark::State& state) {
   while (state.KeepRunning()) {
     __android_log_security();
@@ -997,3 +999,33 @@
   android::base::SetProperty("log.tag." + test_log_tag, "");
 }
 BENCHMARK(BM_log_verbose_overhead);
+
+static void BM_log_convertPrintable_ascii(benchmark::State& state) {
+  char buf[BUFSIZ];
+  const char* s = "hello, world! this is a plain ASCII string 1234.";
+  size_t n = strlen(s);
+  for (auto _ : state) {
+    convertPrintable(buf, s, n);
+  }
+}
+BENCHMARK(BM_log_convertPrintable_ascii);
+
+static void BM_log_convertPrintable_non_printable(benchmark::State& state) {
+  char buf[BUFSIZ];
+  const char* s = "hello,\x01world!\x02this is a plain ASCII string 1234\x7f";
+  size_t n = strlen(s);
+  for (auto _ : state) {
+    convertPrintable(buf, s, n);
+  }
+}
+BENCHMARK(BM_log_convertPrintable_non_printable);
+
+static void BM_log_convertPrintable_non_ascii(benchmark::State& state) {
+  char buf[BUFSIZ];
+  const char* s = "동해 물과 백두산이 마르고 닳도록, 하느님이 보우하사 우리나라 만세.";
+  size_t n = strlen(s);
+  for (auto _ : state) {
+    convertPrintable(buf, s, n);
+  }
+}
+BENCHMARK(BM_log_convertPrintable_non_ascii);
diff --git a/liblog/tests/liblog_convertPrintable_fuzzer.cpp b/liblog/tests/liblog_convertPrintable_fuzzer.cpp
index f4e376a..a362381 100644
--- a/liblog/tests/liblog_convertPrintable_fuzzer.cpp
+++ b/liblog/tests/liblog_convertPrintable_fuzzer.cpp
@@ -16,7 +16,7 @@
 
 #include <fuzzer/FuzzedDataProvider.h>
 
-size_t convertPrintable(char* p, const char* message, size_t messageLen);
+size_t convertPrintable(char*, const char*, size_t);
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t length) {
   FuzzedDataProvider dataProvider(data, length);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 501f25d..cde5a5b 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -46,6 +46,8 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
+#include "test_utils.h"
+
 using android::base::make_scope_guard;
 
 // #define ENABLE_FLAKY_TESTS
@@ -70,12 +72,6 @@
   void operator()(struct logger_list* list) { android_logger_list_close(list); }
 };
 
-// Devices can set a system property indicating a slower device, giving a
-// multiplier to use for timeouts.  If the device has set this property, we use it.
-static unsigned int getAlarmSeconds(unsigned int seconds) {
-  return seconds * android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
-}
-
 // This function is meant to be used for most log tests, it does the following:
 // 1) Open the log_buffer with a blocking reader
 // 2) Write the messages via write_messages
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
index 891ceb7..1373c2a 100644
--- a/liblog/tests/log_wrap_test.cpp
+++ b/liblog/tests/log_wrap_test.cpp
@@ -28,6 +28,8 @@
 #include <log/log_read.h>
 #include <log/log_time.h>
 
+#include "test_utils.h"
+
 #ifdef __ANDROID__
 static void read_with_wrap() {
   // Read the last line in the log to get a starting timestamp. We're assuming
@@ -68,7 +70,7 @@
   struct sigaction ignore = {.sa_handler = [](int) { _exit(0); }};
   struct sigaction old_sigaction;
   sigaction(SIGALRM, &ignore, &old_sigaction);
-  alarm(10);
+  alarm(getAlarmSeconds(5));
 
   android::base::Timer timer;
   read_with_wrap();
diff --git a/liblog/tests/logprint_test.cpp b/liblog/tests/logprint_test.cpp
index 8475699..dac8c48 100644
--- a/liblog/tests/logprint_test.cpp
+++ b/liblog/tests/logprint_test.cpp
@@ -24,7 +24,7 @@
 
 using namespace std::string_literals;
 
-size_t convertPrintable(char* p, const char* message, size_t messageLen);
+size_t convertPrintable(char*, const char*, size_t);
 
 TEST(liblog, convertPrintable_ascii) {
   auto input = "easy string, output same";
@@ -32,6 +32,7 @@
   EXPECT_EQ(output_size, strlen(input));
 
   char output[output_size + 1];
+  memset(output, 'x', sizeof(output));
 
   output_size = convertPrintable(output, input, strlen(input));
   EXPECT_EQ(output_size, strlen(input));
@@ -39,18 +40,19 @@
 }
 
 TEST(liblog, convertPrintable_escapes) {
-  std::string input = "escape\x00\x7f\a\b\t\v\f\r\\"s;
+  std::string input = "escape\x00\x7f\a\b\t\n\v\f\r\\"s;
   // We want to test escaping of ASCII NUL at the end too.
   auto input_size = input.size() + 1;
 
   // Note that \t is not escaped.
-  std::string expected_output = "escape\\x00\\x7F\\a\\b\t\\v\\f\\r\\\\\\x00"s;
+  std::string expected_output = "escape\\x00\\x7F\\a\\b\t\\n\\v\\f\\r\\\\\\x00"s;
   auto expected_output_size = expected_output.size();
 
   auto output_size = convertPrintable(nullptr, input.c_str(), input_size);
   EXPECT_EQ(output_size, expected_output_size) << input_size;
 
   char output[output_size + 1];
+  memset(output, 'x', sizeof(output));
 
   output_size = convertPrintable(output, input.c_str(), input_size);
   EXPECT_EQ(output_size, expected_output_size);
@@ -65,6 +67,7 @@
   EXPECT_EQ(output_size, strlen(input));
 
   char output[output_size + 1];
+  memset(output, 'x', sizeof(output));
 
   output_size = convertPrintable(output, input, strlen(input));
   EXPECT_EQ(output_size, strlen(input));
@@ -79,6 +82,7 @@
   EXPECT_EQ(output_size, strlen(expected_output));
 
   char output[output_size + 1];
+  memset(output, 'x', sizeof(output));
 
   output_size = convertPrintable(output, input, strlen(input));
   EXPECT_EQ(output_size, strlen(expected_output));
@@ -97,6 +101,7 @@
   EXPECT_EQ(output_size, strlen(expected_output));
 
   char output[output_size + 1];
+  memset(output, 'x', sizeof(output));
 
   output_size = convertPrintable(output, input, strlen(input));
   EXPECT_EQ(output_size, strlen(expected_output));
diff --git a/liblog/tests/test_utils.h b/liblog/tests/test_utils.h
new file mode 100644
index 0000000..ee1668a
--- /dev/null
+++ b/liblog/tests/test_utils.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/properties.h>
+
+// Devices can set a system property indicating a slower device, giving a
+// multiplier to use for timeouts.  If the device has set this property, we use it.
+static unsigned int getAlarmSeconds(unsigned int seconds) {
+  return seconds * android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
+}
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 0793f10..8f27694 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -41,8 +41,6 @@
 static const char suspendStr[] = "PM: suspend entry ";
 static const char resumeStr[] = "PM: suspend exit ";
 static const char suspendedStr[] = "suspended for ";
-static const char healthdStr[] = "healthd";
-static const char batteryStr[] = ": battery ";
 static const char auditStr[] = " audit(";
 static const char klogdStr[] = "logd.klogd: ";
 
@@ -313,15 +311,6 @@
                    (((b += strlen(resumeStr)) - cp) < len)) {
             len -= b - cp;
             calculateCorrection(now, b, len);
-        } else if (((b = android::strnstr(cp, len, healthdStr))) &&
-                   (((b += strlen(healthdStr)) - cp) < len) &&
-                   ((b = android::strnstr(b, len -= b - cp, batteryStr))) &&
-                   (((b += strlen(batteryStr)) - cp) < len)) {
-            // NB: healthd is roughly 150us late, so we use it instead to
-            //     trigger a check for ntp-induced or hardware clock drift.
-            log_time real(CLOCK_REALTIME);
-            log_time mono(CLOCK_MONOTONIC);
-            correction = (real < mono) ? log_time(log_time::EPOCH) : (real - mono);
         } else if (((b = android::strnstr(cp, len, suspendedStr))) &&
                    (((b += strlen(suspendedStr)) - cp) < len)) {
             len -= b - cp;
@@ -345,6 +334,17 @@
                     correction += real;
                 }
             }
+        } else {
+            static time_t last_correction_time_utc = 0;
+            time_t current_time_utc = time(nullptr);
+            if (current_time_utc < last_correction_time_utc ||
+                current_time_utc - last_correction_time_utc > 60) {
+                log_time real(CLOCK_REALTIME);
+                log_time mono(CLOCK_MONOTONIC);
+                correction = (real < mono) ? log_time(log_time::EPOCH) : (real - mono);
+
+                last_correction_time_utc = current_time_utc;
+            }
         }
 
         convertMonotonicToReal(now);
diff --git a/logd/libaudit.cpp b/logd/libaudit.cpp
index ccea0a2..a305fb7 100644
--- a/logd/libaudit.cpp
+++ b/logd/libaudit.cpp
@@ -43,9 +43,7 @@
     if (rep.nlh.nlmsg_type == NLMSG_ERROR) {
         audit_get_reply(fd, &rep, GET_REPLY_BLOCKING, 0);
         rc = reinterpret_cast<struct nlmsgerr*>(rep.data)->error;
-        if (rc) {
-            return -rc;
-        }
+        return rc;
     }
 
     return 0;