Merge "Handle zero snapshot size appropriately." into rvc-dev
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 190c235..e686973 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -617,7 +617,7 @@
}
std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode);
- if (!SendRequest(ID_SEND_V1, path_and_mode.c_str())) {
+ if (!SendRequest(ID_SEND_V1, path_and_mode)) {
Error("failed to send ID_SEND_V1 message '%s': %s", path_and_mode.c_str(),
strerror(errno));
return false;
diff --git a/base/Android.bp b/base/Android.bp
index 5b91078..4556f9b 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -51,6 +51,7 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ min_sdk_version: "29",
}
cc_defaults {
@@ -132,6 +133,7 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ min_sdk_version: "29",
}
cc_library_static {
@@ -157,6 +159,7 @@
"errors_test.cpp",
"expected_test.cpp",
"file_test.cpp",
+ "logging_splitters_test.cpp",
"logging_test.cpp",
"macros_test.cpp",
"mapped_file_test.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index 6321fc6..97cc2b2 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -225,7 +225,7 @@
content->reserve(sb.st_size);
}
- char buf[BUFSIZ];
+ char buf[BUFSIZ] __attribute__((__uninitialized__));
ssize_t n;
while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
content->append(buf, n);
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index 9603bb1..9470344 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -182,7 +182,7 @@
!std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
std::is_convertible_v<U&&, T> /* non-explicit */
)>
- // NOLINTNEXTLINE(google-explicit-constructor)
+ // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
template <class U = T _ENABLE_IF(
@@ -192,6 +192,7 @@
!std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
!std::is_convertible_v<U&&, T> /* explicit */
)>
+ // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
template<class G = E _ENABLE_IF(
@@ -387,13 +388,9 @@
template<class T1, class E1, class T2, class E2>
constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return *x == *y;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return *x == *y;
}
template<class T1, class E1, class T2, class E2>
@@ -581,35 +578,23 @@
template<class E1, class E2>
constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return true;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return true;
}
template<class T1, class E1, class E2>
constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return false;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return false;
}
template<class E1, class T2, class E2>
constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return false;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return false;
}
template<class E>
@@ -623,7 +608,7 @@
std::is_constructible_v<E, Err> &&
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
- // NOLINTNEXTLINE(google-explicit-constructor)
+ // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
template<class U, class... Args _ENABLE_IF(
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index accc225..26827fb 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -118,8 +118,10 @@
void SetDefaultTag(const std::string& tag);
-// We expose this even though it is the default because a user that wants to
-// override the default log buffer will have to construct this themselves.
+// The LogdLogger sends chunks of up to ~4000 bytes at a time to logd. It does not prevent other
+// threads from writing to logd between sending each chunk, so other threads may interleave their
+// messages. If preventing interleaving is required, then a custom logger that takes a lock before
+// calling this logger should be provided.
class LogdLogger {
public:
explicit LogdLogger(LogId default_log_id = android::base::MAIN);
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
index 5e65876..56a4f3e 100644
--- a/base/include/android-base/result.h
+++ b/base/include/android-base/result.h
@@ -130,6 +130,7 @@
template <typename T>
Error& operator<<(T&& t) {
+ // NOLINTNEXTLINE(bugprone-suspicious-semicolon)
if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
errno_ = t.code();
return (*this) << t.message();
diff --git a/base/logging.cpp b/base/logging.cpp
index cd460eb..5bd21da 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -61,6 +61,7 @@
#include <android-base/threads.h>
#include "liblog_symbols.h"
+#include "logging_splitters.h"
namespace android {
namespace base {
@@ -190,12 +191,6 @@
}
}
-static std::mutex& LoggingLock() {
- static auto& logging_lock = *new std::mutex();
- return logging_lock;
-}
-
-// Only used for Q fallback.
static LogFunction& Logger() {
#ifdef __ANDROID__
static auto& logger = *new LogFunction(LogdLogger());
@@ -205,7 +200,6 @@
return logger;
}
-// Only used for Q fallback.
static AbortFunction& Aborter() {
static auto& aborter = *new AbortFunction(DefaultAborter);
return aborter;
@@ -241,8 +235,8 @@
static LogSeverity gMinimumLogSeverity = INFO;
#if defined(__linux__)
-void KernelLogger(android::base::LogId, android::base::LogSeverity severity,
- const char* tag, const char*, unsigned int, const char* msg) {
+static void KernelLogLine(const char* msg, int length, android::base::LogSeverity severity,
+ const char* tag) {
// clang-format off
static constexpr int kLogSeverityToKernelLogLevel[] = {
[android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
@@ -266,8 +260,8 @@
// The kernel's printk buffer is only 1024 bytes.
// TODO: should we automatically break up long lines into multiple lines?
// Or we could log but with something like "..." at the end?
- char buf[1024];
- size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg);
+ char buf[1024] __attribute__((__uninitialized__));
+ size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg);
if (size > sizeof(buf)) {
size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
level, tag, size);
@@ -278,6 +272,11 @@
iov[0].iov_len = size;
TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
}
+
+void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
+ const char*, unsigned int, const char* full_message) {
+ SplitByLines(full_message, KernelLogLine, severity, tag);
+}
#endif
void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
@@ -290,21 +289,10 @@
#else
localtime_r(&t, &now);
#endif
+ auto output_string =
+ StderrOutputGenerator(now, getpid(), GetThreadId(), severity, tag, file, line, message);
- char timestamp[32];
- strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
-
- static const char log_characters[] = "VDIWEFF";
- static_assert(arraysize(log_characters) - 1 == FATAL + 1,
- "Mismatch in size of log_characters and values in LogSeverity");
- char severity_char = log_characters[severity];
- if (file != nullptr) {
- fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
- timestamp, getpid(), GetThreadId(), file, line, message);
- } else {
- fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n", tag ? tag : "nullptr", severity_char,
- timestamp, getpid(), GetThreadId(), message);
- }
+ fputs(output_string.c_str(), stderr);
}
void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
@@ -326,26 +314,9 @@
abort();
}
-
-LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
-}
-
-void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
- const char* file, unsigned int line,
- const char* message) {
- int32_t priority = LogSeverityToPriority(severity);
- if (id == DEFAULT) {
- id = default_log_id_;
- }
-
+static void LogdLogChunk(LogId id, LogSeverity severity, const char* tag, const char* message) {
int32_t lg_id = LogIdTolog_id_t(id);
-
- char log_message_with_file[4068]; // LOGGER_ENTRY_MAX_PAYLOAD, not available in the NDK.
- if (priority == ANDROID_LOG_FATAL && file != nullptr) {
- snprintf(log_message_with_file, sizeof(log_message_with_file), "%s:%u] %s", file, line,
- message);
- message = log_message_with_file;
- }
+ int32_t priority = LogSeverityToPriority(severity);
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
@@ -357,6 +328,17 @@
}
}
+LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {}
+
+void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ if (id == DEFAULT) {
+ id = default_log_id_;
+ }
+
+ SplitByLogdChunks(id, severity, tag, file, line, message, LogdLogChunk);
+}
+
void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
SetLogger(std::forward<LogFunction>(logger));
SetAborter(std::forward<AbortFunction>(aborter));
@@ -416,45 +398,27 @@
}
void SetLogger(LogFunction&& logger) {
+ Logger() = std::move(logger);
+
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
- // We need to atomically swap the old and new pointers since other threads may be logging.
- // We know all threads will be using the new logger after __android_log_set_logger() returns,
- // so we can delete it then.
- // This leaks one std::function<> per instance of libbase if multiple copies of libbase within a
- // single process call SetLogger(). That is the same cost as having a static
- // std::function<>, which is the not-thread-safe alternative.
- static std::atomic<LogFunction*> logger_function(nullptr);
- auto* old_logger_function = logger_function.exchange(new LogFunction(logger));
liblog_functions->__android_log_set_logger([](const struct __android_log_message* log_message) {
auto log_id = log_id_tToLogId(log_message->buffer_id);
auto severity = PriorityToLogSeverity(log_message->priority);
- auto& function = *logger_function.load(std::memory_order_acquire);
- function(log_id, severity, log_message->tag, log_message->file, log_message->line,
+ Logger()(log_id, severity, log_message->tag, log_message->file, log_message->line,
log_message->message);
});
- delete old_logger_function;
- } else {
- std::lock_guard<std::mutex> lock(LoggingLock());
- Logger() = std::move(logger);
}
}
void SetAborter(AbortFunction&& aborter) {
+ Aborter() = std::move(aborter);
+
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
- // See the comment in SetLogger().
- static std::atomic<AbortFunction*> abort_function(nullptr);
- auto* old_abort_function = abort_function.exchange(new AbortFunction(aborter));
- liblog_functions->__android_log_set_aborter([](const char* abort_message) {
- auto& function = *abort_function.load(std::memory_order_acquire);
- function(abort_message);
- });
- delete old_abort_function;
- } else {
- std::lock_guard<std::mutex> lock(LoggingLock());
- Aborter() = std::move(aborter);
+ liblog_functions->__android_log_set_aborter(
+ [](const char* abort_message) { Aborter()(abort_message); });
}
}
@@ -535,26 +499,8 @@
#endif
}
- {
- // Do the actual logging with the lock held.
- std::lock_guard<std::mutex> lock(LoggingLock());
- if (msg.find('\n') == std::string::npos) {
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
- msg.c_str());
- } else {
- msg += '\n';
- size_t i = 0;
- while (i < msg.size()) {
- size_t nl = msg.find('\n', i);
- msg[nl] = '\0';
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
- &msg[i]);
- // Undo the zero-termination so we can give the complete message to the aborter.
- msg[nl] = '\n';
- i = nl + 1;
- }
- }
- }
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+ msg.c_str());
// Abort if necessary.
if (data_->GetSeverity() == FATAL) {
diff --git a/base/logging_splitters.h b/base/logging_splitters.h
new file mode 100644
index 0000000..2ec2b20
--- /dev/null
+++ b/base/logging_splitters.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068 // This constant is not in the NDK.
+
+namespace android {
+namespace base {
+
+// This splits the message up line by line, by calling log_function with a pointer to the start of
+// each line and the size up to the newline character. It sends size = -1 for the final line.
+template <typename F, typename... Args>
+static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
+ const char* newline = strchr(msg, '\n');
+ while (newline != nullptr) {
+ log_function(msg, newline - msg, args...);
+ msg = newline + 1;
+ newline = strchr(msg, '\n');
+ }
+
+ log_function(msg, -1, args...);
+}
+
+// This splits the message up into chunks that logs can process delimited by new lines. It calls
+// log_function with the exact null terminated message that should be sent to logd.
+// Note, despite the loops and snprintf's, if severity is not fatal and there are no new lines,
+// this function simply calls log_function with msg without any extra overhead.
+template <typename F>
+static void SplitByLogdChunks(LogId log_id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* msg, const F& log_function) {
+ // The maximum size of a payload, after the log header that logd will accept is
+ // LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload to find the size of
+ // the string that we can log in each pass.
+ // The protocol is documented in liblog/README.protocol.md.
+ // Specifically we subtract a byte for the priority, the length of the tag + its null terminator,
+ // and an additional byte for the null terminator on the payload. We subtract an additional 32
+ // bytes for slack, similar to java/android/util/Log.java.
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+ if (max_size <= 0) {
+ abort();
+ }
+ // If we're logging a fatal message, we'll append the file and line numbers.
+ bool add_file = file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT);
+
+ std::string file_header;
+ if (add_file) {
+ file_header = StringPrintf("%s:%u] ", file, line);
+ }
+ int file_header_size = file_header.size();
+
+ __attribute__((uninitialized)) char logd_chunk[max_size + 1];
+ ptrdiff_t chunk_position = 0;
+
+ auto call_log_function = [&]() {
+ log_function(log_id, severity, tag, logd_chunk);
+ chunk_position = 0;
+ };
+
+ auto write_to_logd_chunk = [&](const char* message, int length) {
+ int size_written = 0;
+ const char* new_line = chunk_position > 0 ? "\n" : "";
+ if (add_file) {
+ size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+ "%s%s%.*s", new_line, file_header.c_str(), length, message);
+ } else {
+ size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+ "%s%.*s", new_line, length, message);
+ }
+
+ // This should never fail, if it does and we set size_written to 0, which will skip this line
+ // and move to the next one.
+ if (size_written < 0) {
+ size_written = 0;
+ }
+ chunk_position += size_written;
+ };
+
+ const char* newline = strchr(msg, '\n');
+ while (newline != nullptr) {
+ // If we have data in the buffer and this next line doesn't fit, write the buffer.
+ if (chunk_position != 0 && chunk_position + (newline - msg) + 1 + file_header_size > max_size) {
+ call_log_function();
+ }
+
+ // Otherwise, either the next line fits or we have any empty buffer and too large of a line to
+ // ever fit, in both cases, we add it to the buffer and continue.
+ write_to_logd_chunk(msg, newline - msg);
+
+ msg = newline + 1;
+ newline = strchr(msg, '\n');
+ }
+
+ // If we have left over data in the buffer and we can fit the rest of msg, add it to the buffer
+ // then write the buffer.
+ if (chunk_position != 0 &&
+ chunk_position + static_cast<int>(strlen(msg)) + 1 + file_header_size <= max_size) {
+ write_to_logd_chunk(msg, -1);
+ call_log_function();
+ } else {
+ // If the buffer is not empty and we can't fit the rest of msg into it, write its contents.
+ if (chunk_position != 0) {
+ call_log_function();
+ }
+ // Then write the rest of the msg.
+ if (add_file) {
+ snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(), msg);
+ log_function(log_id, severity, tag, logd_chunk);
+ } else {
+ log_function(log_id, severity, tag, msg);
+ }
+ }
+}
+
+static std::pair<int, int> CountSizeAndNewLines(const char* message) {
+ int size = 0;
+ int new_lines = 0;
+ while (*message != '\0') {
+ size++;
+ if (*message == '\n') {
+ ++new_lines;
+ }
+ ++message;
+ }
+ return {size, new_lines};
+}
+
+// This adds the log header to each line of message and returns it as a string intended to be
+// written to stderr.
+static std::string StderrOutputGenerator(const struct tm& now, int pid, uint64_t tid,
+ LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ char timestamp[32];
+ strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
+ static const char log_characters[] = "VDIWEFF";
+ static_assert(arraysize(log_characters) - 1 == FATAL + 1,
+ "Mismatch in size of log_characters and values in LogSeverity");
+ char severity_char = log_characters[severity];
+ std::string line_prefix;
+ if (file != nullptr) {
+ line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " %s:%u] ", tag ? tag : "nullptr",
+ severity_char, timestamp, pid, tid, file, line);
+ } else {
+ line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " ", tag ? tag : "nullptr", severity_char,
+ timestamp, pid, tid);
+ }
+
+ auto [size, new_lines] = CountSizeAndNewLines(message);
+ std::string output_string;
+ output_string.reserve(size + new_lines * line_prefix.size() + 1);
+
+ auto concat_lines = [&](const char* message, int size) {
+ output_string.append(line_prefix);
+ if (size == -1) {
+ output_string.append(message);
+ } else {
+ output_string.append(message, size);
+ }
+ output_string.append("\n");
+ };
+ SplitByLines(message, concat_lines);
+ return output_string;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_splitters_test.cpp b/base/logging_splitters_test.cpp
new file mode 100644
index 0000000..679d19e
--- /dev/null
+++ b/base/logging_splitters_test.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2020 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 "logging_splitters.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+void TestNewlineSplitter(const std::string& input,
+ const std::vector<std::string>& expected_output) {
+ std::vector<std::string> output;
+ auto logger_function = [&](const char* msg, int length) {
+ if (length == -1) {
+ output.push_back(msg);
+ } else {
+ output.push_back(std::string(msg, length));
+ }
+ };
+ SplitByLines(input.c_str(), logger_function);
+
+ EXPECT_EQ(expected_output, output);
+}
+
+TEST(logging_splitters, NewlineSplitter_EmptyString) {
+ TestNewlineSplitter("", std::vector<std::string>{""});
+}
+
+TEST(logging_splitters, NewlineSplitter_BasicString) {
+ TestNewlineSplitter("normal string", std::vector<std::string>{"normal string"});
+}
+
+TEST(logging_splitters, NewlineSplitter_ormalBasicStringTrailingNewline) {
+ TestNewlineSplitter("normal string\n", std::vector<std::string>{"normal string", ""});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineTrailing) {
+ TestNewlineSplitter("normal string\nsecond string\nthirdstring",
+ std::vector<std::string>{"normal string", "second string", "thirdstring"});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineTrailingNewline) {
+ TestNewlineSplitter(
+ "normal string\nsecond string\nthirdstring\n",
+ std::vector<std::string>{"normal string", "second string", "thirdstring", ""});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineEmbeddedNewlines) {
+ TestNewlineSplitter(
+ "normal string\n\n\nsecond string\n\nthirdstring\n",
+ std::vector<std::string>{"normal string", "", "", "second string", "", "thirdstring", ""});
+}
+
+void TestLogdChunkSplitter(const std::string& tag, const std::string& file,
+ const std::string& input,
+ const std::vector<std::string>& expected_output) {
+ std::vector<std::string> output;
+ auto logger_function = [&](LogId, LogSeverity, const char*, const char* msg) {
+ output.push_back(msg);
+ };
+
+ SplitByLogdChunks(MAIN, FATAL, tag.c_str(), file.empty() ? nullptr : file.c_str(), 1000,
+ input.c_str(), logger_function);
+
+ auto return_lengths = [&] {
+ std::string sizes;
+ sizes += "expected_output sizes:";
+ for (const auto& string : expected_output) {
+ sizes += " " + std::to_string(string.size());
+ }
+ sizes += "\noutput sizes:";
+ for (const auto& string : output) {
+ sizes += " " + std::to_string(string.size());
+ }
+ return sizes;
+ };
+
+ EXPECT_EQ(expected_output, output) << return_lengths();
+}
+
+TEST(logging_splitters, LogdChunkSplitter_EmptyString) {
+ TestLogdChunkSplitter("tag", "", "", std::vector<std::string>{""});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_BasicString) {
+ TestLogdChunkSplitter("tag", "", "normal string", std::vector<std::string>{"normal string"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_NormalBasicStringTrailingNewline) {
+ TestLogdChunkSplitter("tag", "", "normal string\n", std::vector<std::string>{"normal string\n"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineTrailing) {
+ TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring",
+ std::vector<std::string>{"normal string\nsecond string\nthirdstring"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineTrailingNewline) {
+ TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring\n",
+ std::vector<std::string>{"normal string\nsecond string\nthirdstring\n"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineEmbeddedNewlines) {
+ TestLogdChunkSplitter(
+ "tag", "", "normal string\n\n\nsecond string\n\nthirdstring\n",
+ std::vector<std::string>{"normal string\n\n\nsecond string\n\nthirdstring\n"});
+}
+
+// This test should return the same string, the logd logger itself will truncate down to size.
+// This has historically been the behavior both in libbase and liblog.
+TEST(logging_splitters, LogdChunkSplitter_HugeLineNoNewline) {
+ auto long_string = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
+ ASSERT_EQ(LOGGER_ENTRY_MAX_PAYLOAD, static_cast<int>(long_string.size()));
+
+ TestLogdChunkSplitter("tag", "", long_string, std::vector{long_string});
+}
+
+std::string ReduceToMaxSize(const std::string& tag, const std::string& string) {
+ return string.substr(0, LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultipleHugeLineNoNewline) {
+ auto long_string_x = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
+ auto long_string_y = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'y');
+ auto long_string_z = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'z');
+
+ auto long_strings = long_string_x + '\n' + long_string_y + '\n' + long_string_z;
+
+ std::string tag = "tag";
+ std::vector expected = {ReduceToMaxSize(tag, long_string_x), ReduceToMaxSize(tag, long_string_y),
+ long_string_z};
+
+ TestLogdChunkSplitter(tag, "", long_strings, expected);
+}
+
+// With a ~4k buffer, we should print 2 long strings per logger call.
+TEST(logging_splitters, LogdChunkSplitter_Multiple2kLines) {
+ std::vector expected = {
+ std::string(2000, 'a') + '\n' + std::string(2000, 'b'),
+ std::string(2000, 'c') + '\n' + std::string(2000, 'd'),
+ std::string(2000, 'e') + '\n' + std::string(2000, 'f'),
+ };
+
+ auto long_strings = Join(expected, '\n');
+
+ TestLogdChunkSplitter("tag", "", long_strings, expected);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_ExactSizedLines) {
+ const char* tag = "tag";
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+ auto long_string_a = std::string(max_size, 'a');
+ auto long_string_b = std::string(max_size, 'b');
+ auto long_string_c = std::string(max_size, 'c');
+
+ auto long_strings = long_string_a + '\n' + long_string_b + '\n' + long_string_c;
+
+ TestLogdChunkSplitter(tag, "", long_strings,
+ std::vector{long_string_a, long_string_b, long_string_c});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_UnderEqualOver) {
+ std::string tag = "tag";
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
+
+ auto first_string_size = 1000;
+ auto first_string = std::string(first_string_size, 'a');
+ auto second_string_size = max_size - first_string_size - 1;
+ auto second_string = std::string(second_string_size, 'b');
+
+ auto exact_string = std::string(max_size, 'c');
+
+ auto large_string = std::string(max_size + 50, 'd');
+
+ auto final_string = std::string("final string!\n\nfinal \n \n final \n");
+
+ std::vector expected = {first_string + '\n' + second_string, exact_string,
+ ReduceToMaxSize(tag, large_string), final_string};
+
+ std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
+ final_string};
+ auto long_strings = Join(input_strings, '\n');
+
+ TestLogdChunkSplitter(tag, "", long_strings, expected);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_WithFile) {
+ std::string tag = "tag";
+ std::string file = "/path/to/myfile.cpp";
+ int line = 1000;
+ auto file_header = StringPrintf("%s:%d] ", file.c_str(), line);
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
+
+ auto first_string_size = 1000;
+ auto first_string = std::string(first_string_size, 'a');
+ auto second_string_size = max_size - first_string_size - 1 - 2 * file_header.size();
+ auto second_string = std::string(second_string_size, 'b');
+
+ auto exact_string = std::string(max_size - file_header.size(), 'c');
+
+ auto large_string = std::string(max_size + 50, 'd');
+
+ auto final_string = std::string("final string!");
+
+ std::vector expected = {
+ file_header + first_string + '\n' + file_header + second_string, file_header + exact_string,
+ file_header + ReduceToMaxSize(file_header + tag, large_string), file_header + final_string};
+
+ std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
+ final_string};
+ auto long_strings = Join(input_strings, '\n');
+
+ TestLogdChunkSplitter(tag, file, long_strings, expected);
+}
+
+// We set max_size based off of tag, so if it's too large, the buffer will be sized wrong.
+// We could recover from this, but it's certainly an error for someone to attempt to use a tag this
+// large, so we abort instead.
+TEST(logging_splitters, LogdChunkSplitter_TooLongTag) {
+ auto long_tag = std::string(5000, 'x');
+ auto logger_function = [](LogId, LogSeverity, const char*, const char*) {};
+ ASSERT_DEATH(
+ SplitByLogdChunks(MAIN, ERROR, long_tag.c_str(), nullptr, 0, "message", logger_function), "");
+}
+
+// We do handle excessively large file names correctly however.
+TEST(logging_splitters, LogdChunkSplitter_TooLongFile) {
+ auto long_file = std::string(5000, 'x');
+ std::string tag = "tag";
+
+ std::vector expected = {ReduceToMaxSize(tag, long_file), ReduceToMaxSize(tag, long_file)};
+
+ TestLogdChunkSplitter(tag, long_file, "can't see me\nor me", expected);
+}
+
+void TestStderrOutputGenerator(const char* tag, const char* file, int line, const char* message,
+ const std::string& expected) {
+ // All log messages will show "01-01 00:00:00"
+ struct tm now = {
+ .tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 1,
+ .tm_mon = 0,
+ .tm_year = 1970,
+ };
+
+ int pid = 1234; // All log messages will have 1234 for their PID.
+ uint64_t tid = 4321; // All log messages will have 4321 for their TID.
+
+ auto result = StderrOutputGenerator(now, pid, tid, ERROR, tag, file, line, message);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(logging_splitters, StderrOutputGenerator_Basic) {
+ TestStderrOutputGenerator(nullptr, nullptr, 0, "simple message",
+ "nullptr E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator("tag", nullptr, 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator(
+ "tag", "/path/to/some/file", 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 /path/to/some/file:0] simple message\n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_NewlineTagAndFile) {
+ TestStderrOutputGenerator("tag\n\n", nullptr, 0, "simple message",
+ "tag\n\n E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator(
+ "tag", "/path/to/some/file\n\n", 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 /path/to/some/file\n\n:0] simple message\n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_TrailingNewLine) {
+ TestStderrOutputGenerator(
+ "tag", nullptr, 0, "simple message\n",
+ "tag E 01-01 00:00:00 1234 4321 simple message\ntag E 01-01 00:00:00 1234 4321 \n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_MultiLine) {
+ const char* expected_result =
+ "tag E 01-01 00:00:00 1234 4321 simple message\n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 another message \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 final message \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n";
+
+ TestStderrOutputGenerator("tag", nullptr, 0,
+ "simple message\n\n\nanother message \n\n final message \n\n\n",
+ expected_result);
+}
+
+TEST(logging_splitters, StderrOutputGenerator_MultiLineLong) {
+ auto long_string_a = std::string(4000, 'a');
+ auto long_string_b = std::string(4000, 'b');
+
+ auto message = long_string_a + '\n' + long_string_b;
+ auto expected_result = "tag E 01-01 00:00:00 1234 4321 " + long_string_a + '\n' +
+ "tag E 01-01 00:00:00 1234 4321 " + long_string_b + '\n';
+ TestStderrOutputGenerator("tag", nullptr, 0, message.c_str(), expected_result);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 3a453e6..593e2c1 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -24,8 +24,10 @@
#include <regex>
#include <string>
+#include <thread>
#include "android-base/file.h"
+#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/test_utils.h"
@@ -596,7 +598,7 @@
CapturedStderr cap;
LOG(FATAL) << "foo\nbar";
- EXPECT_EQ(CountLineAborter::newline_count, 1U + 1U); // +1 for final '\n'.
+ EXPECT_EQ(CountLineAborter::newline_count, 1U);
}
__attribute__((constructor)) void TestLoggingInConstructor() {
@@ -617,3 +619,55 @@
// Whereas ERROR logging includes the program name.
ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str());
}
+
+TEST(logging, ForkSafe) {
+#if !defined(_WIN32)
+ using namespace android::base;
+ SetLogger(
+ [&](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) { sleep(3); });
+
+ auto guard = make_scope_guard([&] {
+#ifdef __ANDROID__
+ SetLogger(LogdLogger());
+#else
+ SetLogger(StderrLogger);
+#endif
+ });
+
+ auto thread = std::thread([] {
+ LOG(ERROR) << "This should sleep for 3 seconds, long enough to fork another process, if there "
+ "is no intervention";
+ });
+ thread.detach();
+
+ auto pid = fork();
+ ASSERT_NE(-1, pid);
+
+ if (pid == 0) {
+ // Reset the logger, so the next message doesn't sleep().
+ SetLogger([](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) {});
+ LOG(ERROR) << "This should succeed in the child, only if libbase is forksafe.";
+ _exit(EXIT_SUCCESS);
+ }
+
+ // Wait for up to 3 seconds for the child to exit.
+ int tries = 3;
+ bool found_child = false;
+ while (tries-- > 0) {
+ auto result = waitpid(pid, nullptr, WNOHANG);
+ EXPECT_NE(-1, result);
+ if (result == pid) {
+ found_child = true;
+ break;
+ }
+ sleep(1);
+ }
+
+ EXPECT_TRUE(found_child);
+
+ // Kill the child if it did not exit.
+ if (!found_child) {
+ kill(pid, SIGKILL);
+ }
+#endif
+}
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
index 78e1e8d..e83ab13 100644
--- a/base/stringprintf.cpp
+++ b/base/stringprintf.cpp
@@ -25,7 +25,7 @@
void StringAppendV(std::string* dst, const char* format, va_list ap) {
// First try with a small fixed size buffer
- char space[1024];
+ char space[1024] __attribute__((__uninitialized__));
// It's possible for methods that use a va_list to invalidate
// the data in it upon use. The fix is to make a copy
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index d9167e7..e7f785b 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -261,6 +261,10 @@
GTEST_LOG_(INFO) << "Flashing a logical partition..";
EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), SUCCESS)
<< "flash logical -partition failed";
+
+ GTEST_LOG_(INFO) << "Testing 'fastboot delete-logical-partition' command";
+ EXPECT_EQ(fb->DeletePartition(test_partition_name), SUCCESS)
+ << "delete logical-partition failed";
}
// Conformance tests
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index d5b59cc..58241b3 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -84,17 +84,21 @@
}
cc_fuzz {
- name: "dm_linear_table_fuzzer",
- defaults: ["fs_mgr_defaults"],
- srcs: [
- "dm_linear_fuzzer.cpp",
- "test_util.cpp",
- ],
- static_libs: [
+ name: "dm_linear_table_fuzzer",
+ defaults: ["fs_mgr_defaults"],
+ srcs: [
+ "dm_linear_fuzzer.cpp",
+ "test_util.cpp",
+ ],
+ static_libs: [
"libdm",
"libbase",
"libext2_uuid",
"libfs_mgr",
"liblog",
- ],
+ ],
+}
+
+vts_config {
+ name: "VtsKernelLibdmTest",
}
diff --git a/fs_mgr/libdm/Android.mk b/fs_mgr/libdm/Android.mk
deleted file mode 100644
index 6aedc25..0000000
--- a/fs_mgr/libdm/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2018 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsKernelLibdmTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index f6c2b5a..976cd90 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -68,6 +68,7 @@
"libdm",
"libfs_mgr",
"liblog",
+ "libgsi",
],
data: [
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 22a3722..3c8ab42 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -35,6 +35,7 @@
#include <libdm/loop_control.h>
#include <libfiemap/fiemap_writer.h>
#include <libfiemap/split_fiemap_writer.h>
+#include <libgsi/libgsi.h>
#include "utility.h"
@@ -148,7 +149,10 @@
FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
EXPECT_EQ(fptr->size(), gBlockSize);
EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
- EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+
+ if (!android::gsi::IsGsiRunning()) {
+ EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+ }
}
TEST_F(FiemapWriterTest, CheckFileCreated) {
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 20349dc..a779a78 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -108,3 +108,6 @@
defaults: ["liblp_test_defaults"],
}
+vts_config {
+ name: "VtsKernelLiblpTest",
+}
diff --git a/fs_mgr/liblp/Android.mk b/fs_mgr/liblp/Android.mk
deleted file mode 100644
index 7f7f891..0000000
--- a/fs_mgr/liblp/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2018 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsKernelLiblpTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index d496466..2f516fa 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -40,6 +40,20 @@
return true;
}
+bool LinearExtent::OverlapsWith(const LinearExtent& other) const {
+ if (device_index_ != other.device_index()) {
+ return false;
+ }
+ return physical_sector() < other.end_sector() && other.physical_sector() < end_sector();
+}
+
+bool LinearExtent::OverlapsWith(const Interval& interval) const {
+ if (device_index_ != interval.device_index) {
+ return false;
+ }
+ return physical_sector() < interval.end && interval.start < end_sector();
+}
+
Interval LinearExtent::AsInterval() const {
return Interval(device_index(), physical_sector(), end_sector());
}
@@ -774,8 +788,7 @@
bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
const LinearExtent& candidate) const {
for (const auto& region : regions) {
- if (region.device_index == candidate.device_index() &&
- (candidate.OwnsSector(region.start) || candidate.OwnsSector(region.end))) {
+ if (candidate.OverlapsWith(region)) {
return true;
}
}
@@ -786,11 +799,10 @@
for (const auto& partition : partitions_) {
for (const auto& extent : partition->extents()) {
LinearExtent* linear = extent->AsLinearExtent();
- if (!linear || linear->device_index() != candidate.device_index()) {
+ if (!linear) {
continue;
}
- if (linear->OwnsSector(candidate.physical_sector()) ||
- linear->OwnsSector(candidate.end_sector() - 1)) {
+ if (linear->OverlapsWith(candidate)) {
return true;
}
}
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index ca8df61..977ebe3 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -937,3 +937,85 @@
EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2));
EXPECT_EQ(exported->header.flags, 0x5e5e5e5e);
}
+
+static Interval ToInterval(const std::unique_ptr<Extent>& extent) {
+ if (LinearExtent* le = extent->AsLinearExtent()) {
+ return le->AsInterval();
+ }
+ return {0, 0, 0};
+}
+
+static void AddPartition(const std::unique_ptr<MetadataBuilder>& builder,
+ const std::string& partition_name, uint64_t num_sectors,
+ uint64_t start_sector, std::vector<Interval>* intervals) {
+ Partition* p = builder->AddPartition(partition_name, "group", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(p, "super", num_sectors, start_sector));
+ ASSERT_EQ(p->extents().size(), 1);
+
+ if (!intervals) {
+ return;
+ }
+
+ auto new_interval = ToInterval(p->extents().back());
+ std::vector<Interval> new_intervals = {new_interval};
+
+ auto overlap = Interval::Intersect(*intervals, new_intervals);
+ ASSERT_TRUE(overlap.empty());
+
+ intervals->push_back(new_interval);
+}
+
+TEST_F(BuilderTest, CollidedExtents) {
+ BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
+ std::vector<BlockDeviceInfo> block_devices = {super};
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("group", 0));
+
+ std::vector<Interval> old_intervals;
+ AddPartition(builder, "system", 10229008, 2048, &old_intervals);
+ AddPartition(builder, "test_a", 648, 12709888, &old_intervals);
+ AddPartition(builder, "test_b", 625184, 12711936, &old_intervals);
+ AddPartition(builder, "test_c", 130912, 13338624, &old_intervals);
+ AddPartition(builder, "test_d", 888, 13469696, &old_intervals);
+ AddPartition(builder, "test_e", 888, 13471744, &old_intervals);
+ AddPartition(builder, "test_f", 888, 13475840, &old_intervals);
+ AddPartition(builder, "test_g", 888, 13477888, &old_intervals);
+
+ // Don't track the first vendor interval, since it will get extended.
+ AddPartition(builder, "vendor", 2477920, 10231808, nullptr);
+
+ std::vector<Interval> new_intervals;
+
+ Partition* p = builder->FindPartition("vendor");
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 1282031616));
+ ASSERT_GE(p->extents().size(), 1);
+ for (const auto& extent : p->extents()) {
+ new_intervals.push_back(ToInterval(extent));
+ }
+
+ std::vector<Interval> overlap = Interval::Intersect(old_intervals, new_intervals);
+ ASSERT_TRUE(overlap.empty());
+}
+
+TEST_F(BuilderTest, LinearExtentOverlap) {
+ LinearExtent extent(20, 0, 10);
+
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 10}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{50, 0, 10}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 0, 30}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{10, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{40, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 15}));
+
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 0}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{50, 1, 10}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{40, 1, 0}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 15}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 10}));
+}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 382d53d..99bff6e 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -56,6 +56,10 @@
// Having an alignment offset > alignment doesn't really make sense.
EXPECT_LT(device_info.alignment_offset, device_info.alignment);
+
+ if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+ EXPECT_EQ(device_info.alignment_offset, 0);
+ }
}
TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f7738fb..bd39150 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -71,9 +71,8 @@
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
uint32_t device_index() const { return device_index_; }
- bool OwnsSector(uint64_t sector) const {
- return sector >= physical_sector_ && sector < end_sector();
- }
+ bool OverlapsWith(const LinearExtent& other) const;
+ bool OverlapsWith(const Interval& interval) const;
Interval AsInterval() const;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index e290cdc..c118788 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -159,23 +159,23 @@
"snapshot_test.cpp",
],
shared_libs: [
- "android.hardware.boot@1.0",
- "android.hardware.boot@1.1",
"libbinder",
"libcrypto",
"libhidlbase",
"libprotobuf-cpp-lite",
- "libsparse",
"libutils",
"libz",
],
static_libs: [
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
"libfs_mgr",
"libgsi",
"libgmock",
"liblp",
"libsnapshot",
"libsnapshot_test_helpers",
+ "libsparse",
],
header_libs: [
"libstorage_literals_headers",
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index b036606..f82a602 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -212,8 +212,8 @@
return AssertionFailure() << strerror(errno);
}
bsize_ = buf.f_bsize;
- free_space_ = buf.f_bsize * buf.f_bfree;
- available_space_ = buf.f_bsize * buf.f_bavail;
+ free_space_ = bsize_ * buf.f_bfree;
+ available_space_ = bsize_ * buf.f_bavail;
return AssertionSuccess();
}
diff --git a/init/README.md b/init/README.md
index 13f1bac..726c0cc 100644
--- a/init/README.md
+++ b/init/README.md
@@ -322,6 +322,10 @@
This is mutually exclusive with the console option, which additionally connects stdin to the
given console.
+`task_profiles <profile> [ <profile>\* ]`
+> Set task profiles for the process when it forks. This is designed to replace the use of
+ writepid option for moving a process into a cgroup.
+
`timeout_period <seconds>`
> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
here, so oneshot services do not automatically restart, however all other services will.
@@ -356,6 +360,8 @@
cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
'/foreground'), then the pid is written to file /dev/cpuset/_cpuset\_name_/tasks.
+ The use of this option for moving a process into a cgroup is obsolete. Please
+ use task_profiles option instead.
Triggers
diff --git a/init/init.cpp b/init/init.cpp
index cadd5c5..6465df1 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -75,6 +75,7 @@
#include "service.h"
#include "service_parser.h"
#include "sigchld_handler.h"
+#include "subcontext.h"
#include "system/core/init/property_service.pb.h"
#include "util.h"
@@ -99,8 +100,6 @@
static int signal_fd = -1;
static int property_fd = -1;
-static std::unique_ptr<Subcontext> subcontext;
-
struct PendingControlMessage {
std::string message;
std::string name;
@@ -253,9 +252,8 @@
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(
- &service_list, subcontext.get(), std::nullopt));
- parser.AddSectionParser("on",
- std::make_unique<ActionParser>(&action_manager, subcontext.get()));
+ &service_list, GetSubcontext(), std::nullopt));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
@@ -265,9 +263,9 @@
Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, subcontext.get(),
- std::nullopt, from_apex));
+ parser.AddSectionParser(
+ "service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
+ from_apex));
return parser;
}
@@ -783,7 +781,7 @@
PLOG(FATAL) << "SetupMountNamespaces failed";
}
- subcontext = InitializeSubcontext();
+ InitializeSubcontext();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
diff --git a/init/init_test.cpp b/init/init_test.cpp
index caf3e03..07b4724 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -239,6 +239,28 @@
EXPECT_EQ(6, num_executed);
}
+TEST(init, RejectsCriticalAndOneshotService) {
+ std::string init_script =
+ R"init(
+service A something
+ class first
+ critical
+ oneshot
+)init";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+
+ ServiceList service_list;
+ Parser parser;
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+ ASSERT_TRUE(parser.ParseConfig(tf.path));
+ ASSERT_EQ(1u, parser.parse_error_count());
+}
+
} // namespace init
} // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 72f0450..e89f74a 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -758,20 +758,24 @@
static Result<void> DoUserspaceReboot() {
LOG(INFO) << "Userspace reboot initiated";
- auto guard = android::base::make_scope_guard([] {
+ // An ugly way to pass a more precise reason on why fallback to hard reboot was triggered.
+ std::string sub_reason = "";
+ auto guard = android::base::make_scope_guard([&sub_reason] {
// Leave shutdown so that we can handle a full reboot.
LeaveShutdown();
- trigger_shutdown("reboot,userspace_failed,shutdown_aborted");
+ trigger_shutdown("reboot,userspace_failed,shutdown_aborted," + sub_reason);
});
// Triggering userspace-reboot-requested will result in a bunch of setprop
// actions. We should make sure, that all of them are propagated before
// proceeding with userspace reboot. Synchronously setting sys.init.userspace_reboot.in_progress
// property is not perfect, but it should do the trick.
if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
+ sub_reason = "setprop";
return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
}
EnterShutdown();
if (!SetProperty("sys.powerctl", "")) {
+ sub_reason = "resetprop";
return Error() << "Failed to reset sys.powerctl property";
}
std::vector<Service*> stop_first;
@@ -800,18 +804,22 @@
StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ sub_reason = "sigkill";
// TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " post-data services are still running";
}
if (auto result = KillZramBackingDevice(); !result.ok()) {
+ sub_reason = "zram";
return result;
}
if (auto result = CallVdc("volume", "reset"); !result.ok()) {
+ sub_reason = "vold_reset";
return result;
}
if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */),
sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ sub_reason = "sigkill_debug";
// TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " debugging services are still running";
}
@@ -822,9 +830,11 @@
LOG(INFO) << "sync() took " << sync_timer;
}
if (auto result = UnmountAllApexes(); !result.ok()) {
+ sub_reason = "apex";
return result;
}
if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+ sub_reason = "ns_switch";
return Error() << "Failed to switch to bootstrap namespace";
}
// Remove services that were defined in an APEX.
diff --git a/init/service.cpp b/init/service.cpp
index b12d11a..69f944e 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -511,6 +511,10 @@
LOG(ERROR) << "failed to write pid to files: " << result.error();
}
+ if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
+ LOG(ERROR) << "failed to set task profiles";
+ }
+
// As requested, set our gid, supplemental gids, uid, context, and
// priority. Aborts on failure.
SetProcessAttributesAndCaps();
diff --git a/init/service.h b/init/service.h
index 9f1d697..34ed5ef 100644
--- a/init/service.h
+++ b/init/service.h
@@ -170,6 +170,8 @@
std::vector<std::string> writepid_files_;
+ std::vector<std::string> task_profiles_;
+
std::set<std::string> interfaces_; // e.g. some.package.foo@1.0::IBaz/instance-name
// keycodes for triggering this service via /dev/input/input*
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 560f693..bdac077 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -360,6 +360,12 @@
return Error() << "Invalid shutdown option";
}
+Result<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {
+ args.erase(args.begin());
+ service_->task_profiles_ = std::move(args);
+ return {};
+}
+
Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args) {
int period;
if (!ParseInt(args[1], &period, 1)) {
@@ -529,6 +535,7 @@
{"sigstop", {0, 0, &ServiceParser::ParseSigstop}},
{"socket", {3, 6, &ServiceParser::ParseSocket}},
{"stdio_to_kmsg", {0, 0, &ServiceParser::ParseStdioToKmsg}},
+ {"task_profiles", {1, kMax, &ServiceParser::ParseTaskProfiles}},
{"timeout_period", {1, 1, &ServiceParser::ParseTimeoutPeriod}},
{"updatable", {0, 0, &ServiceParser::ParseUpdatable}},
{"user", {1, 1, &ServiceParser::ParseUser}},
@@ -598,6 +605,13 @@
}
}
+ if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+ if ((service_->flags() & SVC_CRITICAL) != 0 && (service_->flags() & SVC_ONESHOT) != 0) {
+ return Error() << "service '" << service_->name()
+ << "' can't be both critical and oneshot";
+ }
+ }
+
Service* old_service = service_list_->FindService(service_->name());
if (old_service) {
if (!service_->is_override()) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 7bb0cc0..0fd2da5 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -78,6 +78,7 @@
Result<void> ParseSigstop(std::vector<std::string>&& args);
Result<void> ParseSocket(std::vector<std::string>&& args);
Result<void> ParseStdioToKmsg(std::vector<std::string>&& args);
+ Result<void> ParseTaskProfiles(std::vector<std::string>&& args);
Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
Result<void> ParseFile(std::vector<std::string>&& args);
Result<void> ParseUser(std::vector<std::string>&& args);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 5263c14..f3dd538 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -52,6 +52,8 @@
namespace {
std::string shutdown_command;
+static bool subcontext_terminated_by_shutdown;
+static std::unique_ptr<Subcontext> subcontext;
class SubcontextProcess {
public:
@@ -323,34 +325,30 @@
return expanded_args;
}
-static std::vector<Subcontext> subcontexts;
-static bool shutting_down;
-
-std::unique_ptr<Subcontext> InitializeSubcontext() {
+void InitializeSubcontext() {
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
- return std::make_unique<Subcontext>(std::vector<std::string>{"/vendor", "/odm"},
- kVendorContext);
+ subcontext.reset(
+ new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
}
- return nullptr;
+}
+
+Subcontext* GetSubcontext() {
+ return subcontext.get();
}
bool SubcontextChildReap(pid_t pid) {
- for (auto& subcontext : subcontexts) {
- if (subcontext.pid() == pid) {
- if (!shutting_down) {
- subcontext.Restart();
- }
- return true;
+ if (subcontext->pid() == pid) {
+ if (!subcontext_terminated_by_shutdown) {
+ subcontext->Restart();
}
+ return true;
}
return false;
}
void SubcontextTerminate() {
- shutting_down = true;
- for (auto& subcontext : subcontexts) {
- kill(subcontext.pid(), SIGTERM);
- }
+ subcontext_terminated_by_shutdown = true;
+ kill(subcontext->pid(), SIGTERM);
}
} // namespace init
diff --git a/init/subcontext.h b/init/subcontext.h
index 5e1d8a8..788d3be 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -60,7 +60,8 @@
};
int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
-std::unique_ptr<Subcontext> InitializeSubcontext();
+void InitializeSubcontext();
+Subcontext* GetSubcontext();
bool SubcontextChildReap(pid_t pid);
void SubcontextTerminate();
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 0b98e1a..6051ac7 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
native_bridge_supported: true,
export_include_dirs: ["include"],
system_shared_libs: [],
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 90d1e76..c116add 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -143,8 +143,11 @@
/*
* Release any logger resources (a new log write will immediately re-acquire)
*
- * May be used to clean up File descriptors after a Fork, the resources are
- * all O_CLOEXEC so wil self clean on exec().
+ * This is specifically meant to be used by Zygote to close open file descriptors after fork()
+ * and before specialization. O_CLOEXEC is used on file descriptors, so they will be closed upon
+ * exec() in normal use cases.
+ *
+ * Note that this is not safe to call from a multi-threaded program.
*/
void __android_log_close(void);
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 67376f4..a230749 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -32,58 +32,53 @@
#include <time.h>
#include <unistd.h>
-#include <shared_mutex>
-
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
-static int logd_socket;
-static RwLock logd_socket_lock;
+static atomic_int logd_socket;
-static void OpenSocketLocked() {
- logd_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
- if (logd_socket <= 0) {
- return;
- }
-
+// Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
+// function is used to reconnect to logd without requiring a new socket.
+static void LogdConnect() {
sockaddr_un un = {};
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "/dev/socket/logdw");
-
- if (TEMP_FAILURE_RETRY(
- connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un))) < 0) {
- close(logd_socket);
- logd_socket = 0;
- }
+ TEMP_FAILURE_RETRY(connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
}
-static void OpenSocket() {
- auto lock = std::unique_lock{logd_socket_lock};
- if (logd_socket > 0) {
- // Someone raced us and opened the socket already.
+// logd_socket should only be opened once. If we see that logd_socket is uninitialized, we create a
+// new socket and attempt to exchange it into the atomic logd_socket. If the compare/exchange was
+// successful, then that will be the socket used for the duration of the program, otherwise a
+// different thread has already opened and written the socket to the atomic, so close the new socket
+// and return.
+static void GetSocket() {
+ if (logd_socket != 0) {
return;
}
- OpenSocketLocked();
-}
-
-static void ResetSocket(int old_socket) {
- auto lock = std::unique_lock{logd_socket_lock};
- if (old_socket != logd_socket) {
- // Someone raced us and reset the socket already.
+ int new_socket =
+ TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ if (new_socket <= 0) {
return;
}
- close(logd_socket);
- logd_socket = 0;
- OpenSocketLocked();
+
+ int uninitialized_value = 0;
+ if (!logd_socket.compare_exchange_strong(uninitialized_value, new_socket)) {
+ close(new_socket);
+ return;
+ }
+
+ LogdConnect();
}
+// This is the one exception to the above. Zygote uses this to clean up open FD's after fork() and
+// before specialization. It is single threaded at this point and therefore this function is
+// explicitly not thread safe. It sets logd_socket to 0, so future logs will be safely initialized
+// whenever they happen.
void LogdClose() {
- auto lock = std::unique_lock{logd_socket_lock};
if (logd_socket > 0) {
close(logd_socket);
}
@@ -99,12 +94,7 @@
static atomic_int dropped;
static atomic_int droppedSecurity;
- auto lock = std::shared_lock{logd_socket_lock};
- if (logd_socket <= 0) {
- lock.unlock();
- OpenSocket();
- lock.lock();
- }
+ GetSocket();
if (logd_socket <= 0) {
return -EBADF;
@@ -183,10 +173,7 @@
// the connection, so we reset it and try again.
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
if (ret < 0 && errno != EAGAIN) {
- int old_socket = logd_socket;
- lock.unlock();
- ResetSocket(old_socket);
- lock.lock();
+ LogdConnect();
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
}
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 74b0ab9..b1bed80 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -28,7 +28,6 @@
#endif
#include <atomic>
-#include <shared_mutex>
#include <android-base/errno_restorer.h>
#include <android-base/macros.h>
@@ -38,7 +37,6 @@
#include "android/log.h"
#include "log/log_read.h"
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
#ifdef __ANDROID__
@@ -142,10 +140,8 @@
static std::string default_tag = getprogname();
return default_tag;
}
-RwLock default_tag_lock;
void __android_log_set_default_tag(const char* tag) {
- auto lock = std::unique_lock{default_tag_lock};
GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD);
}
@@ -163,10 +159,8 @@
#else
static __android_logger_function logger_function = __android_log_stderr_logger;
#endif
-static RwLock logger_function_lock;
void __android_log_set_logger(__android_logger_function logger) {
- auto lock = std::unique_lock{logger_function_lock};
logger_function = logger;
}
@@ -180,15 +174,12 @@
}
static __android_aborter_function aborter_function = __android_log_default_aborter;
-static RwLock aborter_function_lock;
void __android_log_set_aborter(__android_aborter_function aborter) {
- auto lock = std::unique_lock{aborter_function_lock};
aborter_function = aborter;
}
void __android_log_call_aborter(const char* abort_message) {
- auto lock = std::shared_lock{aborter_function_lock};
aborter_function(abort_message);
}
@@ -310,9 +301,7 @@
return;
}
- auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
if (log_message->tag == nullptr) {
- tag_lock.lock();
log_message->tag = GetDefaultTag().c_str();
}
@@ -322,7 +311,6 @@
}
#endif
- auto lock = std::shared_lock{logger_function_lock};
logger_function(log_message);
}
diff --git a/liblog/logger_write.h b/liblog/logger_write.h
index 065fd55..eee2778 100644
--- a/liblog/logger_write.h
+++ b/liblog/logger_write.h
@@ -18,7 +18,4 @@
#include <string>
-#include "rwlock.h"
-
-std::string& GetDefaultTag(); // Must read lock default_tag_lock
-extern RwLock default_tag_lock;
\ No newline at end of file
+std::string& GetDefaultTag();
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 06e5e04..0751e2c 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -23,30 +23,36 @@
#include <sys/types.h>
#include <time.h>
-#include <shared_mutex>
-
#include <log/log_properties.h>
#include <private/android_logger.h>
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
-static int pmsg_fd;
-static RwLock pmsg_fd_lock;
+static atomic_int pmsg_fd;
-static void PmsgOpen() {
- auto lock = std::unique_lock{pmsg_fd_lock};
- if (pmsg_fd > 0) {
- // Someone raced us and opened the socket already.
+// pmsg_fd should only beopened once. If we see that pmsg_fd is uninitialized, we open "/dev/pmsg0"
+// then attempt to compare/exchange it into pmsg_fd. If the compare/exchange was successful, then
+// that will be the fd used for the duration of the program, otherwise a different thread has
+// already opened and written the fd to the atomic, so close the new fd and return.
+static void GetPmsgFd() {
+ if (pmsg_fd != 0) {
return;
}
- pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (new_fd <= 0) {
+ return;
+ }
+
+ int uninitialized_value = 0;
+ if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
+ close(new_fd);
+ return;
+ }
}
void PmsgClose() {
- auto lock = std::unique_lock{pmsg_fd_lock};
if (pmsg_fd > 0) {
close(pmsg_fd);
}
@@ -77,13 +83,7 @@
}
}
- auto lock = std::shared_lock{pmsg_fd_lock};
-
- if (pmsg_fd <= 0) {
- lock.unlock();
- PmsgOpen();
- lock.lock();
- }
+ GetPmsgFd();
if (pmsg_fd <= 0) {
return -EBADF;
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index abd48fc..37670ec 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -23,7 +23,6 @@
#include <unistd.h>
#include <algorithm>
-#include <shared_mutex>
#include <private/android_logger.h>
@@ -99,9 +98,7 @@
static const char log_namespace[] = "persist.log.tag.";
static const size_t base_offset = 8; /* skip "persist." */
- auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
if (tag == nullptr || len == 0) {
- tag_lock.lock();
auto& tag_string = GetDefaultTag();
tag = tag_string.c_str();
len = tag_string.size();
diff --git a/liblog/rwlock.h b/liblog/rwlock.h
deleted file mode 100644
index 00f1806..0000000
--- a/liblog/rwlock.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#pragma once
-
-#include <pthread.h>
-
-// As of the end of Dec 2019, std::shared_mutex is *not* simply a pthread_rwlock, but rather a
-// combination of std::mutex and std::condition variable, which is obviously less efficient. This
-// immitates what std::shared_mutex should be doing and is compatible with std::shared_lock and
-// std::unique_lock.
-
-class RwLock {
- public:
- RwLock() {}
- ~RwLock() {}
-
- void lock() { pthread_rwlock_wrlock(&rwlock_); }
- void unlock() { pthread_rwlock_unlock(&rwlock_); }
-
- void lock_shared() { pthread_rwlock_rdlock(&rwlock_); }
- void unlock_shared() { pthread_rwlock_unlock(&rwlock_); }
-
- private:
- pthread_rwlock_t rwlock_ = PTHREAD_RWLOCK_INITIALIZER;
-};
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 385b079..50800c5 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -63,8 +63,8 @@
"log_system_test.cpp",
"log_time_test.cpp",
"log_wrap_test.cpp",
+ "logd_writer_test.cpp",
"logprint_test.cpp",
- "rwlock_test.cpp",
],
shared_libs: [
"libcutils",
@@ -108,7 +108,6 @@
"liblog_host_test.cpp",
"liblog_default_tag.cpp",
"liblog_global_state.cpp",
- "rwlock_test.cpp",
],
isolated: true,
}
diff --git a/liblog/tests/logd_writer_test.cpp b/liblog/tests/logd_writer_test.cpp
new file mode 100644
index 0000000..b8e4726
--- /dev/null
+++ b/liblog/tests/logd_writer_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 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 <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+// logd_writer takes advantage of the fact that connect() can be called multiple times for a DGRAM
+// socket. This tests for that behavior.
+TEST(liblog, multi_connect_dgram_socket) {
+#ifdef __ANDROID__
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+ auto temp_dir = TemporaryDir();
+ auto socket_path = StringPrintf("%s/test_socket", temp_dir.path);
+
+ unique_fd server_socket;
+
+ auto open_server_socket = [&] {
+ server_socket.reset(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
+ ASSERT_TRUE(server_socket.ok());
+
+ sockaddr_un server_sockaddr = {};
+ server_sockaddr.sun_family = AF_UNIX;
+ strlcpy(server_sockaddr.sun_path, socket_path.c_str(), sizeof(server_sockaddr.sun_path));
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(bind(server_socket, reinterpret_cast<sockaddr*>(&server_sockaddr),
+ sizeof(server_sockaddr))));
+ };
+
+ // Open the server socket.
+ open_server_socket();
+
+ // Open the client socket.
+ auto client_socket =
+ unique_fd{TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0))};
+ ASSERT_TRUE(client_socket.ok());
+ sockaddr_un client_sockaddr = {};
+ client_sockaddr.sun_family = AF_UNIX;
+ strlcpy(client_sockaddr.sun_path, socket_path.c_str(), sizeof(client_sockaddr.sun_path));
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+ sizeof(client_sockaddr))));
+
+ // Ensure that communication works.
+ constexpr static char kSmoke[] = "smoke test";
+ ssize_t smoke_len = sizeof(kSmoke);
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ char read_buf[512];
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+ ASSERT_STREQ(kSmoke, read_buf);
+
+ // Close the server socket.
+ server_socket.reset();
+ ASSERT_EQ(0, unlink(socket_path.c_str())) << strerror(errno);
+
+ // Ensure that write() from the client returns an error since the server is closed.
+ ASSERT_EQ(-1, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ ASSERT_EQ(errno, ECONNREFUSED) << strerror(errno);
+
+ // Open the server socket again.
+ open_server_socket();
+
+ // Reconnect the same client socket.
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+ sizeof(client_sockaddr))))
+ << strerror(errno);
+
+ // Ensure that communication works.
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+ ASSERT_STREQ(kSmoke, read_buf);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
\ No newline at end of file
diff --git a/liblog/tests/rwlock_test.cpp b/liblog/tests/rwlock_test.cpp
deleted file mode 100644
index 617d5c4..0000000
--- a/liblog/tests/rwlock_test.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2019 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 "../rwlock.h"
-
-#include <chrono>
-#include <shared_mutex>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-using namespace std::literals;
-
-TEST(rwlock, reader_then_reader_lock) {
- RwLock lock;
-
- bool thread_ran = false;
- auto read_guard = std::shared_lock{lock};
-
- auto reader_thread = std::thread([&] {
- auto read_guard = std::shared_lock{lock};
- thread_ran = true;
- });
-
- auto end_time = std::chrono::steady_clock::now() + 1s;
-
- while (std::chrono::steady_clock::now() < end_time) {
- if (thread_ran) {
- break;
- }
- }
-
- EXPECT_EQ(true, thread_ran);
-
- // Unlock the lock in case something went wrong, to ensure that we can still join() the thread.
- read_guard.unlock();
- reader_thread.join();
-}
-
-template <template <typename> typename L1, template <typename> typename L2>
-void TestBlockingLocks() {
- RwLock lock;
-
- bool thread_ran = false;
- auto read_guard = L1{lock};
-
- auto reader_thread = std::thread([&] {
- auto read_guard = L2{lock};
- thread_ran = true;
- });
-
- auto end_time = std::chrono::steady_clock::now() + 1s;
-
- while (std::chrono::steady_clock::now() < end_time) {
- if (thread_ran) {
- break;
- }
- }
-
- EXPECT_EQ(false, thread_ran);
-
- read_guard.unlock();
- reader_thread.join();
-
- EXPECT_EQ(true, thread_ran);
-}
-
-TEST(rwlock, reader_then_writer_lock) {
- TestBlockingLocks<std::shared_lock, std::unique_lock>();
-}
-
-TEST(rwlock, writer_then_reader_lock) {
- TestBlockingLocks<std::unique_lock, std::shared_lock>();
-}
-
-TEST(rwlock, writer_then_writer_lock) {
- TestBlockingLocks<std::unique_lock, std::unique_lock>();
-}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index e05a690..766ea0f 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -100,9 +100,12 @@
"libjsoncpp",
"libprotobuf-cpp-full",
],
- target: {
- android: {
- test_config: "vts_processgroup_validate_test.xml",
- },
- },
+ test_suites: [
+ "vts",
+ ],
+}
+
+vts_config {
+ name: "VtsProcessgroupValidateTest",
+ test_config: "vts_processgroup_validate_test.xml",
}
diff --git a/libprocessgroup/profiles/Android.mk b/libprocessgroup/profiles/Android.mk
deleted file mode 100644
index eab96d4..0000000
--- a/libprocessgroup/profiles/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# Copyright (C) 2019 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := VtsProcessgroupValidateTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 0cee6bb..bc6bc7c 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -149,6 +149,19 @@
}
]
},
+ {
+ "Name": "CameraServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
{
"Name": "CpuPolicySpread",
diff --git a/libstats/pull/Android.bp b/libstats/pull/Android.bp
index ef1c5c5..2658639 100644
--- a/libstats/pull/Android.bp
+++ b/libstats/pull/Android.bp
@@ -84,7 +84,15 @@
"libstatspull",
"libstatssocket",
],
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "mts"],
+ //TODO(b/153588990): Remove when the build system properly separates
+ //32bit and 64bit architectures.
+ compile_multilib: "both",
+ multilib: {
+ lib64: {
+ suffix: "64",
+ }
+ },
cflags: [
"-Wall",
"-Werror",
@@ -93,4 +101,5 @@
"-Wno-unused-function",
"-Wno-unused-parameter",
],
+ require_root: true,
}
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 8c12fe0..e40a432 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -109,7 +109,10 @@
cc_test {
name: "libstatssocket_test",
- srcs: ["tests/stats_event_test.cpp"],
+ srcs: [
+ "tests/stats_event_test.cpp",
+ "tests/stats_writer_test.cpp",
+ ],
cflags: [
"-Wall",
"-Werror",
@@ -122,5 +125,14 @@
"libcutils",
"libutils",
],
- test_suites: ["device-tests"],
+ test_suites: ["device-tests", "mts"],
+ //TODO(b/153588990): Remove when the build system properly separates
+ //32bit and 64bit architectures.
+ compile_multilib: "both",
+ multilib: {
+ lib64: {
+ suffix: "64",
+ }
+ },
+ require_root: true,
}
diff --git a/libstats/socket/include/stats_buffer_writer.h b/libstats/socket/include/stats_buffer_writer.h
index de4a5e2..5b41f0e 100644
--- a/libstats/socket/include/stats_buffer_writer.h
+++ b/libstats/socket/include/stats_buffer_writer.h
@@ -23,6 +23,7 @@
extern "C" {
#endif // __CPLUSPLUS
void stats_log_close();
+int stats_log_is_closed();
int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId);
#ifdef __cplusplus
}
diff --git a/libstats/socket/stats_buffer_writer.c b/libstats/socket/stats_buffer_writer.c
index c5c591d..74acb20 100644
--- a/libstats/socket/stats_buffer_writer.c
+++ b/libstats/socket/stats_buffer_writer.c
@@ -43,6 +43,10 @@
statsd_writer_init_unlock();
}
+int stats_log_is_closed() {
+ return statsdLoggerWrite.isClosed && (*statsdLoggerWrite.isClosed)();
+}
+
int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId) {
int ret = 1;
diff --git a/libstats/socket/statsd_writer.c b/libstats/socket/statsd_writer.c
index 04d3b46..73b7a7e 100644
--- a/libstats/socket/statsd_writer.c
+++ b/libstats/socket/statsd_writer.c
@@ -62,6 +62,7 @@
static void statsdClose();
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
static void statsdNoteDrop();
+static int statsdIsClosed();
struct android_log_transport_write statsdLoggerWrite = {
.name = "statsd",
@@ -71,6 +72,7 @@
.close = statsdClose,
.write = statsdWrite,
.noteDrop = statsdNoteDrop,
+ .isClosed = statsdIsClosed,
};
/* log_init_lock assumed */
@@ -153,6 +155,13 @@
atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
}
+static int statsdIsClosed() {
+ if (atomic_load(&statsdLoggerWrite.sock) < 0) {
+ return 1;
+ }
+ return 0;
+}
+
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
ssize_t ret;
int sock;
diff --git a/libstats/socket/statsd_writer.h b/libstats/socket/statsd_writer.h
index fe2d37c..562bda5 100644
--- a/libstats/socket/statsd_writer.h
+++ b/libstats/socket/statsd_writer.h
@@ -40,6 +40,8 @@
int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
/* note one log drop */
void (*noteDrop)(int error, int tag);
+ /* checks if the socket is closed */
+ int (*isClosed)();
};
#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libstats/socket/tests/stats_event_test.cpp b/libstats/socket/tests/stats_event_test.cpp
index 6e47e3d..80ef145 100644
--- a/libstats/socket/tests/stats_event_test.cpp
+++ b/libstats/socket/tests/stats_event_test.cpp
@@ -53,7 +53,12 @@
// Side-effect: this function moves the start of the buffer past the read value
template <class T>
T readNext(uint8_t** buffer) {
- T value = *(T*)(*buffer);
+ T value;
+ if ((reinterpret_cast<uintptr_t>(*buffer) % alignof(T)) == 0) {
+ value = *(T*)(*buffer);
+ } else {
+ memcpy(&value, *buffer, sizeof(T));
+ }
*buffer += sizeof(T);
return value;
}
diff --git a/libstats/socket/tests/stats_writer_test.cpp b/libstats/socket/tests/stats_writer_test.cpp
new file mode 100644
index 0000000..47f3517
--- /dev/null
+++ b/libstats/socket/tests/stats_writer_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 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 <gtest/gtest.h>
+#include "stats_buffer_writer.h"
+#include "stats_event.h"
+#include "stats_socket.h"
+
+TEST(StatsWriterTest, TestSocketClose) {
+ EXPECT_TRUE(stats_log_is_closed());
+
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ AStatsEvent_writeInt32(event, 5);
+ AStatsEvent_build(event);
+ int successResult = AStatsEvent_write(event);
+ AStatsEvent_release(event);
+
+ // In the case of a successful write, we return the number of bytes written.
+ EXPECT_GT(successResult, 0);
+ EXPECT_FALSE(stats_log_is_closed());
+
+ AStatsSocket_close();
+
+ EXPECT_TRUE(stats_log_is_closed());
+}
\ No newline at end of file
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 1c3acb8..8ad9900 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -41,6 +41,7 @@
#include <string>
#include <unordered_map>
#include <unordered_set>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -1204,9 +1205,19 @@
}
}
// We are here because we have confirmed kernel live-lock
+ std::vector<std::string> threads;
+ auto taskdir = procdir + std::to_string(tid) + "/task/";
+ dir taskDirectory(taskdir);
+ for (auto tp = taskDirectory.read(); tp != nullptr; tp = taskDirectory.read()) {
+ std::string piddir;
+ if (getValidTidDir(tp, &piddir))
+ threads.push_back(android::base::Basename(piddir));
+ }
const auto message = state + " "s + llkFormat(procp->count) + " " +
std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
- std::to_string(tid) + " " + process_comm + " [panic]";
+ std::to_string(tid) + " " + process_comm + " [panic]\n" +
+ " thread group: {" + android::base::Join(threads, ",") +
+ "}";
llkPanicKernel(dump, tid,
(state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
message);
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 76a970f..b8c143d 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -122,6 +122,18 @@
return fd;
}
+static void closeLogFile(const char* pathname) {
+ int fd = open(pathname, O_WRONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return;
+ }
+
+ // no need to check errors
+ __u32 set = 0;
+ ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
+ close(fd);
+}
+
void Logcat::RotateLogs() {
// Can't rotate logs if we're not outputting to a file
if (!output_file_name_) return;
@@ -152,6 +164,8 @@
break;
}
+ closeLogFile(file0.c_str());
+
int err = rename(file0.c_str(), file1.c_str());
if (err < 0 && errno != ENOENT) {
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index d0945f3..10bac62 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -582,6 +582,7 @@
"dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=0.000000000");
}
+#ifdef ENABLE_FLAKY_TESTS
// b/26447386 refined behavior
TEST(logd, timeout) {
#ifdef __ANDROID__
@@ -716,6 +717,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
// b/27242723 confirmed fixed
TEST(logd, SNDTIMEO) {
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c571c26..38ba137 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -78,6 +78,9 @@
mkdir /dev/boringssl 0755 root root
mkdir /dev/boringssl/selftest 0755 root root
+ # Mount tracefs
+ mount tracefs tracefs /sys/kernel/tracing
+
# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
on early-init && property:ro.product.cpu.abilist32=*
exec_start boringssl_self_test32
@@ -544,8 +547,8 @@
enter_default_mount_ns
# /data/apex is now available. Start apexd to scan and activate APEXes.
- mkdir /data/apex 0750 root system encryption=None
- mkdir /data/apex/active 0750 root system
+ mkdir /data/apex 0755 root system encryption=None
+ mkdir /data/apex/active 0755 root system
mkdir /data/apex/backup 0700 root system
mkdir /data/apex/hashtree 0700 root system
mkdir /data/apex/sessions 0700 root system
@@ -743,7 +746,7 @@
mkdir /data/misc/train-info/ 0770 statsd system
# Wait for apexd to finish activating APEXes before starting more processes.
- wait_for_prop apexd.status ready
+ wait_for_prop apexd.status activated
perform_apex_config
# Special-case /data/media/obb per b/64566063
@@ -758,7 +761,7 @@
# Allow apexd to snapshot and restore device encrypted apex data in the case
# of a rollback. This should be done immediately after DE_user data keys
# are loaded. APEXes should not access this data until this has been
- # completed.
+ # completed and apexd.status becomes "ready".
exec_start apexd-snapshotde
# Set SELinux security contexts on upgrade or policy update.
diff --git a/toolbox/start.cpp b/toolbox/start.cpp
index 4b1a54d..cffb89c 100644
--- a/toolbox/start.cpp
+++ b/toolbox/start.cpp
@@ -36,7 +36,13 @@
}
static void ControlDefaultServices(bool start) {
- std::vector<std::string> services = {"iorapd", "netd", "surfaceflinger", "zygote"};
+ std::vector<std::string> services = {
+ "iorapd",
+ "netd",
+ "surfaceflinger",
+ "audioserver",
+ "zygote",
+ };
// Only start zygote_secondary if not single arch.
std::string zygote_configuration = GetProperty("ro.zygote", "");