Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507
Merged-In: Id18cb0e2d2f3e776a42b566c4a1af2e250890896
Change-Id: Iba7cab32ab3aa6f47952c840ff6dc8492e8d0704
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 1abeb2e..89bd66a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -16,13 +16,19 @@
"name": "adb_tls_connection_test"
},
{
+ "name": "CtsFsMgrTestCases"
+ },
+ {
"name": "CtsInitTestCases"
},
{
- "name": "debuggerd_test"
+ "name": "CtsLiblogTestCases"
},
{
- "name": "CtsFsMgrTestCases"
+ "name": "CtsLogdTestCases"
+ },
+ {
+ "name": "debuggerd_test"
},
{
"name": "fs_mgr_vendor_overlay_test"
@@ -61,5 +67,10 @@
{
"name": "propertyinfoserializer_tests"
}
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/tests/StagedInstallTest"
+ }
]
}
diff --git a/adb/Android.bp b/adb/Android.bp
index 8a3c758..8710e9d 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -117,6 +117,7 @@
static_libs: [
"libadb_crypto",
"libadb_pairing_connection",
+ "libadb_sysdeps",
"libadb_tls_connection",
"libadbd",
"libadbd_core",
@@ -167,6 +168,7 @@
"services.cpp",
"sockets.cpp",
"socket_spec.cpp",
+ "sysdeps/env.cpp",
"sysdeps/errno.cpp",
"transport.cpp",
"transport_fd.cpp",
@@ -261,6 +263,43 @@
],
}
+cc_library {
+ name: "libadb_sysdeps",
+ defaults: ["adb_defaults"],
+ recovery_available: true,
+ host_supported: true,
+ compile_multilib: "both",
+ min_sdk_version: "apex_inherit",
+
+ srcs: [
+ "sysdeps/env.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ target: {
+ windows: {
+ enabled: true,
+ ldflags: ["-municode"],
+ },
+ },
+
+ export_include_dirs: ["."],
+
+ visibility: [
+ "//system/core/adb:__subpackages__",
+ "//bootable/recovery/minadbd:__subpackages__",
+ ],
+
+ apex_available: [
+ "com.android.adbd",
+ "test_com.android.adbd",
+ ],
+}
+
cc_test_host {
name: "adb_test",
defaults: ["adb_defaults"],
@@ -274,6 +313,7 @@
"libadb_pairing_auth_static",
"libadb_pairing_connection_static",
"libadb_protos_static",
+ "libadb_sysdeps",
"libadb_tls_connection_static",
"libbase",
"libcutils",
@@ -330,6 +370,7 @@
"libadb_pairing_auth",
"libadb_pairing_connection",
"libadb_protos",
+ "libadb_sysdeps",
"libadb_tls_connection",
"libandroidfw",
"libapp_processes_protos_full",
@@ -833,6 +874,7 @@
"libadb_pairing_auth_static",
"libadb_pairing_connection_static",
"libadb_protos_static",
+ "libadb_sysdeps",
"libadb_tls_connection_static",
"libandroidfw",
"libbase",
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 66cba12..e72d8b6 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -55,7 +55,7 @@
bool set_file_block_mode(borrowed_fd fd, bool block);
-// Given forward/reverse targets, returns true if they look sane. If an error is found, fills
+// Given forward/reverse targets, returns true if they look valid. If an error is found, fills
// |error| and returns false.
// Currently this only checks "tcp:" targets. Additional checking could be added for other targets
// if needed.
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
index 3033059..60735f8 100644
--- a/adb/client/incremental.cpp
+++ b/adb/client/incremental.cpp
@@ -55,9 +55,10 @@
return {};
}
- std::vector<char> invalid_signature;
+ auto [signature, tree_size] = read_id_sig_headers(fd);
- if (st.st_size > kMaxSignatureSize) {
+ std::vector<char> invalid_signature;
+ if (signature.size() > kMaxSignatureSize) {
if (!silent) {
fprintf(stderr, "Signature is too long. Max allowed is %d. Abort.\n",
kMaxSignatureSize);
@@ -65,7 +66,6 @@
return {std::move(fd), std::move(invalid_signature)};
}
- auto [signature, tree_size] = read_id_sig_headers(fd);
if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
if (!silent) {
fprintf(stderr,
diff --git a/adb/crypto/Android.bp b/adb/crypto/Android.bp
index 9d14b03..e2c27f1 100644
--- a/adb/crypto/Android.bp
+++ b/adb/crypto/Android.bp
@@ -48,6 +48,7 @@
shared_libs: [
"libadb_protos",
+ "libadb_sysdeps",
"libbase",
"liblog",
"libcrypto",
@@ -76,5 +77,6 @@
static_libs: [
"libadb_protos_static",
+ "libadb_sysdeps",
],
}
diff --git a/adb/crypto/rsa_2048_key.cpp b/adb/crypto/rsa_2048_key.cpp
index 7911af9..6d9ee30 100644
--- a/adb/crypto/rsa_2048_key.cpp
+++ b/adb/crypto/rsa_2048_key.cpp
@@ -20,32 +20,11 @@
#include <crypto_utils/android_pubkey.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
+#include <sysdeps/env.h>
namespace adb {
namespace crypto {
-namespace {
-std::string get_user_info() {
- std::string hostname;
- if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
-#if !defined(_WIN32)
- char buf[64];
- if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
-#endif
- if (hostname.empty()) hostname = "unknown";
-
- std::string username;
- if (getenv("LOGNAME")) username = getenv("LOGNAME");
-#if !defined(_WIN32)
- if (username.empty() && getlogin()) username = getlogin();
-#endif
- if (username.empty()) hostname = "unknown";
-
- return " " + username + "@" + hostname;
-}
-
-} // namespace
-
bool CalculatePublicKey(std::string* out, RSA* private_key) {
uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
@@ -63,7 +42,10 @@
size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
sizeof(binary_key_data));
out->resize(actual_length);
- out->append(get_user_info());
+ out->append(" ");
+ out->append(sysdeps::GetLoginNameUTF8());
+ out->append("@");
+ out->append(sysdeps::GetHostNameUTF8());
return true;
}
diff --git a/adb/crypto/tests/Android.bp b/adb/crypto/tests/Android.bp
index b32dcf7..b041055 100644
--- a/adb/crypto/tests/Android.bp
+++ b/adb/crypto/tests/Android.bp
@@ -35,6 +35,7 @@
static_libs: [
"libadb_crypto_static",
"libadb_protos_static",
+ "libadb_sysdeps",
],
test_suites: ["device-tests"],
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index eb28668..8c41c5e 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -108,9 +108,12 @@
// AID_NET_BW_STATS to read out qtaguid statistics
// AID_READPROC for reading /proc entries across UID boundaries
// AID_UHID for using 'hid' command to read/write to /dev/uhid
+ // AID_EXT_DATA_RW for writing to /sdcard/Android/data (devices without sdcardfs)
+ // AID_EXT_OBB_RW for writing to /sdcard/Android/obb (devices without sdcardfs)
gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT, AID_INET,
AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
- AID_NET_BW_STATS, AID_READPROC, AID_UHID};
+ AID_NET_BW_STATS, AID_READPROC, AID_UHID, AID_EXT_DATA_RW,
+ AID_EXT_OBB_RW};
minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
// Don't listen on a port (default 5037) if running in secure mode.
diff --git a/adb/fdevent/fdevent_test.h b/adb/fdevent/fdevent_test.h
index ecda4da..fcbf181 100644
--- a/adb/fdevent/fdevent_test.h
+++ b/adb/fdevent/fdevent_test.h
@@ -65,7 +65,7 @@
ASSERT_EQ(0u, fdevent_installed_count());
}
- // Register a dummy socket used to wake up the fdevent loop to tell it to die.
+ // Register a placeholder socket used to wake up the fdevent loop to tell it to die.
void PrepareThread() {
int dummy_fds[2];
if (adb_socketpair(dummy_fds) != 0) {
@@ -84,7 +84,7 @@
}
size_t GetAdditionalLocalSocketCount() {
- // dummy socket installed in PrepareThread()
+ // placeholder socket installed in PrepareThread()
return 1;
}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 13a4737..33b9524 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -856,7 +856,7 @@
s->peer->shutdown = nullptr;
s->peer->close = local_socket_close_notify;
s->peer->peer = nullptr;
- /* give him our transport and upref it */
+ /* give them our transport and upref it */
s->peer->transport = s->transport;
connect_to_remote(s->peer, std::string_view(s->smart_socket_data).substr(4));
diff --git a/adb/sysdeps/env.cpp b/adb/sysdeps/env.cpp
new file mode 100644
index 0000000..4058728
--- /dev/null
+++ b/adb/sysdeps/env.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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 "sysdeps/env.h"
+
+#ifdef _WIN32
+#include <lmcons.h>
+#include <windows.h>
+#endif // _WIN32
+
+#include <android-base/utf8.h>
+
+namespace adb {
+namespace sysdeps {
+
+std::optional<std::string> GetEnvironmentVariable(std::string_view var) {
+ if (var.empty()) {
+ return std::nullopt;
+ }
+
+#ifdef _WIN32
+ constexpr size_t kMaxEnvVarSize = 32767;
+ wchar_t wbuf[kMaxEnvVarSize];
+ std::wstring wvar;
+ if (!android::base::UTF8ToWide(var.data(), &wvar)) {
+ return std::nullopt;
+ }
+
+ auto sz = ::GetEnvironmentVariableW(wvar.data(), wbuf, sizeof(wbuf));
+ if (sz == 0) {
+ return std::nullopt;
+ }
+
+ std::string val;
+ if (!android::base::WideToUTF8(wbuf, &val)) {
+ return std::nullopt;
+ }
+
+ return std::make_optional(val);
+#else // !_WIN32
+ const char* val = getenv(var.data());
+ if (val == nullptr) {
+ return std::nullopt;
+ }
+
+ return std::make_optional(std::string(val));
+#endif
+}
+
+#ifdef _WIN32
+constexpr char kHostNameEnvVar[] = "COMPUTERNAME";
+constexpr char kUserNameEnvVar[] = "USERNAME";
+#else
+constexpr char kHostNameEnvVar[] = "HOSTNAME";
+constexpr char kUserNameEnvVar[] = "LOGNAME";
+#endif
+
+std::string GetHostNameUTF8() {
+ const auto hostName = GetEnvironmentVariable(kHostNameEnvVar);
+ if (hostName && !hostName->empty()) {
+ return *hostName;
+ }
+
+#ifdef _WIN32
+ wchar_t wbuf[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD size = sizeof(wbuf);
+ if (!GetComputerNameW(wbuf, &size) || size == 0) {
+ return "";
+ }
+
+ std::string name;
+ if (!android::base::WideToUTF8(wbuf, &name)) {
+ return "";
+ }
+
+ return name;
+#else // !_WIN32
+ char buf[256];
+ return (gethostname(buf, sizeof(buf)) == -1) ? "" : buf;
+#endif // _WIN32
+}
+
+std::string GetLoginNameUTF8() {
+ const auto userName = GetEnvironmentVariable(kUserNameEnvVar);
+ if (userName && !userName->empty()) {
+ return *userName;
+ }
+
+#ifdef _WIN32
+ wchar_t wbuf[UNLEN + 1];
+ DWORD size = sizeof(wbuf);
+ if (!GetUserNameW(wbuf, &size) || size == 0) {
+ return "";
+ }
+
+ std::string login;
+ if (!android::base::WideToUTF8(wbuf, &login)) {
+ return "";
+ }
+
+ return login;
+#else // !_WIN32
+ const char* login = getlogin();
+ return login ? login : "";
+#endif // _WIN32
+}
+
+} // namespace sysdeps
+} // namespace adb
diff --git a/adb/sysdeps/env.h b/adb/sysdeps/env.h
new file mode 100644
index 0000000..b39b675
--- /dev/null
+++ b/adb/sysdeps/env.h
@@ -0,0 +1,35 @@
+/*
+ * 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 <optional>
+#include <string>
+
+namespace adb {
+namespace sysdeps {
+
+// Attempts to retrieve the environment variable value for |var|. Returns std::nullopt
+// if unset.
+std::optional<std::string> GetEnvironmentVariableUTF8(std::string_view var);
+
+// Gets the host name of the system. Returns empty string on failure.
+std::string GetHostNameUTF8();
+// Gets the current login user. Returns empty string on failure.
+std::string GetLoginNameUTF8();
+
+} // namespace sysdeps
+} // namespace adb
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index be82bc0..217a6b7 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -1010,55 +1010,6 @@
return _fh_to_int(f);
}
-static bool isBlankStr(const char* str) {
- for (; *str != '\0'; ++str) {
- if (!isblank(*str)) {
- return false;
- }
- }
- return true;
-}
-
-int adb_gethostname(char* name, size_t len) {
- const char* computerName = adb_getenv("COMPUTERNAME");
- if (computerName && !isBlankStr(computerName)) {
- strncpy(name, computerName, len);
- name[len - 1] = '\0';
- return 0;
- }
-
- wchar_t buffer[MAX_COMPUTERNAME_LENGTH + 1];
- DWORD size = sizeof(buffer);
- if (!GetComputerNameW(buffer, &size)) {
- return -1;
- }
- std::string name_utf8;
- if (!android::base::WideToUTF8(buffer, &name_utf8)) {
- return -1;
- }
-
- strncpy(name, name_utf8.c_str(), len);
- name[len - 1] = '\0';
- return 0;
-}
-
-int adb_getlogin_r(char* buf, size_t bufsize) {
- wchar_t buffer[UNLEN + 1];
- DWORD len = sizeof(buffer);
- if (!GetUserNameW(buffer, &len)) {
- return -1;
- }
-
- std::string login;
- if (!android::base::WideToUTF8(buffer, &login)) {
- return -1;
- }
-
- strncpy(buf, login.c_str(), bufsize);
- buf[bufsize - 1] = '\0';
- return 0;
-}
-
#undef accept
int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen) {
FH serverfh = _fh_from_int(serverfd, __func__);
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 2f2919f..7cff7dc 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -1331,7 +1331,7 @@
shift
fi
- # Check if all conditions for the script are sane
+ # Check if all conditions for the script are valid
if [ -z "${ANDROID_SERIAL}" ]; then
ndev=`(
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 31c2d5d..ad10a1f 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -253,7 +253,6 @@
"libcutils",
"libdebuggerd_client",
"liblog",
- "libminijail",
"libnativehelper",
"libunwindstack",
],
@@ -261,6 +260,7 @@
static_libs: [
"libdebuggerd",
"libgmock",
+ "libminijail",
],
header_libs: [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 5c02738..6bfb5f2 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -70,36 +70,6 @@
tv->tv_usec = static_cast<long>(microseconds.count());
}
-static void get_wchan_header(pid_t pid, std::stringstream& buffer) {
- struct tm now;
- time_t t = time(nullptr);
- localtime_r(&t, &now);
- char timestamp[32];
- strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &now);
- std::string time_now(timestamp);
-
- std::string path = "/proc/" + std::to_string(pid) + "/cmdline";
-
- char proc_name_buf[1024];
- const char* proc_name = nullptr;
- std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path.c_str(), "r"), &fclose);
-
- if (fp) {
- proc_name = fgets(proc_name_buf, sizeof(proc_name_buf), fp.get());
- }
-
- if (!proc_name) {
- proc_name = "<unknown>";
- }
-
- buffer << "\n----- Waiting Channels: pid " << pid << " at " << time_now << " -----\n"
- << "Cmd line: " << proc_name << "\n";
-}
-
-static void get_wchan_footer(pid_t pid, std::stringstream& buffer) {
- buffer << "----- end " << std::to_string(pid) << " -----\n";
-}
-
/**
* Returns the wchan data for each thread in the process,
* or empty string if unable to obtain any data.
@@ -125,9 +95,10 @@
}
if (std::string str = data.str(); !str.empty()) {
- get_wchan_header(pid, buffer);
+ buffer << "\n----- Waiting Channels: pid " << pid << " at " << get_timestamp() << " -----\n"
+ << "Cmd line: " << get_process_name(pid) << "\n";
buffer << "\n" << str << "\n";
- get_wchan_footer(pid, buffer);
+ buffer << "----- end " << std::to_string(pid) << " -----\n";
buffer << "\n";
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f1119cc..108787e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <stdlib.h>
#include <sys/capability.h>
+#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
@@ -556,6 +557,55 @@
#endif
}
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+static uintptr_t CreateTagMapping() {
+ uintptr_t mapping =
+ reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+ if (reinterpret_cast<void*>(mapping) == MAP_FAILED) {
+ return 0;
+ }
+ __asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
+ :
+ : "r"(mapping + (1ULL << 56))
+ : "memory");
+ return mapping;
+}
+#endif
+
+TEST_F(CrasherTest, mte_tag_dump) {
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([&]() {
+ SetTagCheckingLevelSync();
+ Trap(reinterpret_cast<void *>(CreateTagMapping()));
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGTRAP);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ ASSERT_MATCH(result, R"(memory near x0:
+.*
+.*
+ 01.............0 0000000000000000 0000000000000000 ................
+ 00.............0)");
+#else
+ GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+#endif
+}
+
TEST_F(CrasherTest, LD_PRELOAD) {
int intercept_result;
unique_fd output_fd;
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index c606970..f5a873c 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -27,7 +27,6 @@
#include <string.h>
#include <sys/ptrace.h>
#include <sys/types.h>
-#include <time.h>
#include <unistd.h>
#include <map>
@@ -40,14 +39,10 @@
#include "libdebuggerd/types.h"
#include "libdebuggerd/utility.h"
+#include "util.h"
static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {
- time_t t = time(NULL);
- struct tm tm;
- localtime_r(&t, &tm);
- char timestr[64];
- strftime(timestr, sizeof(timestr), "%F %T", &tm);
- _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, timestr);
+ _LOG(log, logtype::BACKTRACE, "\n\n----- pid %d at %s -----\n", pid, get_timestamp().c_str());
if (process_name) {
_LOG(log, logtype::BACKTRACE, "Cmd line: %s\n", process_name);
@@ -106,9 +101,8 @@
log.tfd = output_fd;
log.amfd_data = nullptr;
- char process_name[128];
- read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
- dump_process_header(&log, getpid(), process_name);
+ pid_t pid = getpid();
+ dump_process_header(&log, pid, get_process_name(pid).c_str());
}
void dump_backtrace_footer(int output_fd) {
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 7bfcf5d..76155b1 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -83,8 +83,6 @@
void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&);
-void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
-
void drop_capabilities();
bool signal_has_sender(const siginfo_t*, pid_t caller_pid);
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index be39582..f16f578 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -30,39 +30,39 @@
const char g_expected_full_dump[] =
"\nmemory near r1:\n"
#if defined(__LP64__)
-" 0000000012345658 0706050403020100 0f0e0d0c0b0a0908 ................\n"
-" 0000000012345668 1716151413121110 1f1e1d1c1b1a1918 ................\n"
-" 0000000012345678 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
-" 0000000012345688 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
-" 0000000012345698 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" 00000000123456a8 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
-" 00000000123456b8 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
-" 00000000123456c8 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
-" 00000000123456d8 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 00000000123456e8 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 00000000123456f8 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 0000000012345708 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 0000000012345718 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 0000000012345728 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 0000000012345738 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 0000000012345748 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+" 0000000012345650 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 0000000012345660 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 0000000012345670 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 0000000012345680 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 0000000012345690 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 00000000123456a0 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 00000000123456b0 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" 00000000123456c0 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" 00000000123456d0 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 00000000123456e0 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 00000000123456f0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 0000000012345700 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 0000000012345710 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000012345720 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000012345730 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 0000000012345740 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
#else
-" 12345658 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
-" 12345668 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
-" 12345678 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
-" 12345688 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
-" 12345698 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
-" 123456a8 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
-" 123456b8 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
-" 123456c8 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
-" 123456d8 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" 123456e8 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" 123456f8 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" 12345708 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" 12345718 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" 12345728 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" 12345738 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" 12345748 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+" 12345650 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" 12345660 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" 12345670 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" 12345680 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" 12345690 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" 123456a0 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" 123456b0 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
+" 123456c0 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
+" 123456d0 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" 123456e0 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" 123456f0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" 12345700 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" 12345710 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 12345720 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 12345730 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" 12345740 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
#endif
const char g_expected_partial_dump[] = \
@@ -112,7 +112,10 @@
if (last_read_addr_ > 0) {
offset = addr - last_read_addr_;
}
- size_t bytes_available = buffer_.size() - offset;
+ size_t bytes_available = 0;
+ if (offset < buffer_.size()) {
+ bytes_available = buffer_.size() - offset;
+ }
if (partial_read_) {
bytes = std::min(bytes, bytes_partial_read_);
@@ -258,44 +261,7 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near pc:\n"
-#if defined(__LP64__)
-" 00000000a2345658 ---------------- ---------------- ................\n"
-" 00000000a2345668 ---------------- ---------------- ................\n"
-" 00000000a2345678 ---------------- ---------------- ................\n"
-" 00000000a2345688 ---------------- ---------------- ................\n"
-" 00000000a2345698 ---------------- ---------------- ................\n"
-" 00000000a23456a8 ---------------- ---------------- ................\n"
-" 00000000a23456b8 ---------------- ---------------- ................\n"
-" 00000000a23456c8 ---------------- ---------------- ................\n"
-" 00000000a23456d8 ---------------- ---------------- ................\n"
-" 00000000a23456e8 ---------------- ---------------- ................\n"
-" 00000000a23456f8 ---------------- ---------------- ................\n"
-" 00000000a2345708 ---------------- ---------------- ................\n"
-" 00000000a2345718 ---------------- ---------------- ................\n"
-" 00000000a2345728 ---------------- ---------------- ................\n"
-" 00000000a2345738 ---------------- ---------------- ................\n"
-" 00000000a2345748 ---------------- ---------------- ................\n";
-#else
-" a2345658 -------- -------- -------- -------- ................\n"
-" a2345668 -------- -------- -------- -------- ................\n"
-" a2345678 -------- -------- -------- -------- ................\n"
-" a2345688 -------- -------- -------- -------- ................\n"
-" a2345698 -------- -------- -------- -------- ................\n"
-" a23456a8 -------- -------- -------- -------- ................\n"
-" a23456b8 -------- -------- -------- -------- ................\n"
-" a23456c8 -------- -------- -------- -------- ................\n"
-" a23456d8 -------- -------- -------- -------- ................\n"
-" a23456e8 -------- -------- -------- -------- ................\n"
-" a23456f8 -------- -------- -------- -------- ................\n"
-" a2345708 -------- -------- -------- -------- ................\n"
-" a2345718 -------- -------- -------- -------- ................\n"
-" a2345728 -------- -------- -------- -------- ................\n"
-" a2345738 -------- -------- -------- -------- ................\n"
-" a2345748 -------- -------- -------- -------- ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+ ASSERT_STREQ("", tombstone_contents.c_str());
// Verify that the log buf is empty, and no error messages.
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -429,57 +395,17 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-TEST_F(DumpMemoryTest, memory_address_too_low) {
- uint8_t buffer[256];
- memset(buffer, 0, sizeof(buffer));
- memory_mock_->SetReadData(buffer, sizeof(buffer));
-
- dump_memory(&log_, memory_mock_.get(), 0, "memory near r1");
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
TEST_F(DumpMemoryTest, memory_address_too_high) {
uint8_t buffer[256];
memset(buffer, 0, sizeof(buffer));
memory_mock_->SetReadData(buffer, sizeof(buffer));
#if defined(__LP64__)
- dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near r1");
- dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near r1");
- dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), -32, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), -208, "memory near r1");
#else
- dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near r1");
- dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near r1");
- dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near r1");
-#endif
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_STREQ("", tombstone_contents.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
-TEST_F(DumpMemoryTest, memory_address_would_overflow) {
- uint8_t buffer[256];
- memset(buffer, 0, sizeof(buffer));
- memory_mock_->SetReadData(buffer, sizeof(buffer));
-
-#if defined(__LP64__)
- dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near r1");
-#else
- dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), 0x100000000 - 32, "memory near r1");
+ dump_memory(&log_, memory_mock_.get(), 0x100000000 - 208, "memory near r1");
#endif
std::string tombstone_contents;
@@ -500,9 +426,9 @@
memory_mock_->SetReadData(buffer, sizeof(buffer));
#if defined(__LP64__)
- dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near r4");
+ dump_memory(&log_, memory_mock_.get(), -224, "memory near r4");
#else
- dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near r4");
+ dump_memory(&log_, memory_mock_.get(), 0x100000000 - 224, "memory near r4");
#endif
std::string tombstone_contents;
@@ -510,40 +436,57 @@
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump = \
"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 3fffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
-" 3fffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
-" 3fffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
-" 3fffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
-" 3fffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
-" 3fffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
-" 3fffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
-" 3fffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
-" 3fffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 3fffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 3fffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 3fffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 3fffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 3fffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 3fffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 3ffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#if defined(__aarch64__)
+" 00ffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" 00ffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" 00ffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" 00ffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" 00ffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" 00ffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" 00ffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" 00ffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" 00ffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 00ffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 00ffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 00ffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 00ffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 00ffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 00ffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 00fffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#elif defined(__LP64__)
+" ffffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n"
+" ffffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n"
+" ffffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n"
+" ffffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n"
+" ffffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n"
+" ffffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"
+" ffffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n"
+" ffffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n"
+" ffffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" ffffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" ffffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" ffffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" ffffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" ffffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" ffffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" fffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
#else
-" fffeff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
-" fffeff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
-" fffeff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
-" fffeff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
-" fffeff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
-" fffeff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
-" fffeff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
-" fffeff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
-" fffeff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" fffeff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" fffeffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" fffeffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" fffeffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" fffeffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" fffeffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" fffefff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+" ffffff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n"
+" ffffff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n"
+" ffffff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n"
+" ffffff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n"
+" ffffff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n"
+" ffffff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"
+" ffffff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n"
+" ffffff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n"
+" ffffff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" ffffff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" ffffffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" ffffffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" ffffffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" ffffffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" ffffffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" fffffff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -570,39 +513,41 @@
const char* expected_dump = \
"\nmemory near r4:\n"
#if defined(__LP64__)
-" 0000000010000f88 ---------------- ---------------- ................\n"
-" 0000000010000f98 ---------------- ---------------- ................\n"
-" 0000000010000fa8 ---------------- ---------------- ................\n"
-" 0000000010000fb8 ---------------- ---------------- ................\n"
-" 0000000010000fc8 ---------------- ---------------- ................\n"
-" 0000000010000fd8 ---------------- ---------------- ................\n"
-" 0000000010000fe8 ---------------- ---------------- ................\n"
-" 0000000010000ff8 ---------------- 7f7e7d7c7b7a7978 ........xyz{|}~.\n"
-" 0000000010001008 8786858483828180 8f8e8d8c8b8a8988 ................\n"
-" 0000000010001018 9796959493929190 9f9e9d9c9b9a9998 ................\n"
-" 0000000010001028 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
-" 0000000010001038 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
-" 0000000010001048 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
-" 0000000010001058 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
-" 0000000010001068 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
-" 0000000010001078 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+R"( 0000000010000f80 ---------------- ---------------- ................
+ 0000000010000f90 ---------------- ---------------- ................
+ 0000000010000fa0 ---------------- ---------------- ................
+ 0000000010000fb0 ---------------- ---------------- ................
+ 0000000010000fc0 ---------------- ---------------- ................
+ 0000000010000fd0 ---------------- ---------------- ................
+ 0000000010000fe0 ---------------- ---------------- ................
+ 0000000010000ff0 ---------------- ---------------- ................
+ 0000000010001000 8786858483828180 8f8e8d8c8b8a8988 ................
+ 0000000010001010 9796959493929190 9f9e9d9c9b9a9998 ................
+ 0000000010001020 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................
+ 0000000010001030 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................
+ 0000000010001040 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................
+ 0000000010001050 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................
+ 0000000010001060 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................
+ 0000000010001070 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................
+)";
#else
-" 10000f88 -------- -------- -------- -------- ................\n"
-" 10000f98 -------- -------- -------- -------- ................\n"
-" 10000fa8 -------- -------- -------- -------- ................\n"
-" 10000fb8 -------- -------- -------- -------- ................\n"
-" 10000fc8 -------- -------- -------- -------- ................\n"
-" 10000fd8 -------- -------- -------- -------- ................\n"
-" 10000fe8 -------- -------- -------- -------- ................\n"
-" 10000ff8 -------- -------- 7b7a7978 7f7e7d7c ........xyz{|}~.\n"
-" 10001008 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
-" 10001018 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
-" 10001028 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
-" 10001038 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
-" 10001048 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
-" 10001058 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
-" 10001068 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
-" 10001078 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+R"( 10000f80 -------- -------- -------- -------- ................
+ 10000f90 -------- -------- -------- -------- ................
+ 10000fa0 -------- -------- -------- -------- ................
+ 10000fb0 -------- -------- -------- -------- ................
+ 10000fc0 -------- -------- -------- -------- ................
+ 10000fd0 -------- -------- -------- -------- ................
+ 10000fe0 -------- -------- -------- -------- ................
+ 10000ff0 -------- -------- -------- -------- ................
+ 10001000 83828180 87868584 8b8a8988 8f8e8d8c ................
+ 10001010 93929190 97969594 9b9a9998 9f9e9d9c ................
+ 10001020 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................
+ 10001030 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................
+ 10001040 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................
+ 10001050 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................
+ 10001060 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................
+ 10001070 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................
+)";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
@@ -684,44 +629,7 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 0000000010000000 ---------------- ---------------- ................\n"
-" 0000000010000010 ---------------- ---------------- ................\n"
-" 0000000010000020 ---------------- ---------------- ................\n"
-" 0000000010000030 ---------------- ---------------- ................\n"
-" 0000000010000040 ---------------- ---------------- ................\n"
-" 0000000010000050 ---------------- ---------------- ................\n"
-" 0000000010000060 ---------------- ---------------- ................\n"
-" 0000000010000070 ---------------- ---------------- ................\n"
-" 0000000010000080 ---------------- ---------------- ................\n"
-" 0000000010000090 ---------------- ---------------- ................\n"
-" 00000000100000a0 ---------------- ---------------- ................\n"
-" 00000000100000b0 ---------------- ---------------- ................\n"
-" 00000000100000c0 ---------------- ---------------- ................\n"
-" 00000000100000d0 ---------------- ---------------- ................\n"
-" 00000000100000e0 ---------------- ---------------- ................\n"
-" 00000000100000f0 ---------------- ---------------- ................\n";
-#else
-" 10000000 -------- -------- -------- -------- ................\n"
-" 10000010 -------- -------- -------- -------- ................\n"
-" 10000020 -------- -------- -------- -------- ................\n"
-" 10000030 -------- -------- -------- -------- ................\n"
-" 10000040 -------- -------- -------- -------- ................\n"
-" 10000050 -------- -------- -------- -------- ................\n"
-" 10000060 -------- -------- -------- -------- ................\n"
-" 10000070 -------- -------- -------- -------- ................\n"
-" 10000080 -------- -------- -------- -------- ................\n"
-" 10000090 -------- -------- -------- -------- ................\n"
-" 100000a0 -------- -------- -------- -------- ................\n"
-" 100000b0 -------- -------- -------- -------- ................\n"
-" 100000c0 -------- -------- -------- -------- ................\n"
-" 100000d0 -------- -------- -------- -------- ................\n"
-" 100000e0 -------- -------- -------- -------- ................\n"
-" 100000f0 -------- -------- -------- -------- ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+ ASSERT_STREQ("", tombstone_contents.c_str());
// Verify that the log buf is empty, and no error messages.
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -744,44 +652,7 @@
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-" 0000000010000f00 ---------------- ---------------- ................\n"
-" 0000000010000f10 ---------------- ---------------- ................\n"
-" 0000000010000f20 ---------------- ---------------- ................\n"
-" 0000000010000f30 ---------------- ---------------- ................\n"
-" 0000000010000f40 ---------------- ---------------- ................\n"
-" 0000000010000f50 ---------------- ---------------- ................\n"
-" 0000000010000f60 ---------------- ---------------- ................\n"
-" 0000000010000f70 ---------------- ---------------- ................\n"
-" 0000000010000f80 ---------------- ---------------- ................\n"
-" 0000000010000f90 ---------------- ---------------- ................\n"
-" 0000000010000fa0 ---------------- ---------------- ................\n"
-" 0000000010000fb0 ---------------- ---------------- ................\n"
-" 0000000010000fc0 ---------------- ---------------- ................\n"
-" 0000000010000fd0 ---------------- ---------------- ................\n"
-" 0000000010000fe0 ---------------- ---------------- ................\n"
-" 0000000010000ff0 ---------------- ---------------- ................\n";
-#else
-" 10000f00 -------- -------- -------- -------- ................\n"
-" 10000f10 -------- -------- -------- -------- ................\n"
-" 10000f20 -------- -------- -------- -------- ................\n"
-" 10000f30 -------- -------- -------- -------- ................\n"
-" 10000f40 -------- -------- -------- -------- ................\n"
-" 10000f50 -------- -------- -------- -------- ................\n"
-" 10000f60 -------- -------- -------- -------- ................\n"
-" 10000f70 -------- -------- -------- -------- ................\n"
-" 10000f80 -------- -------- -------- -------- ................\n"
-" 10000f90 -------- -------- -------- -------- ................\n"
-" 10000fa0 -------- -------- -------- -------- ................\n"
-" 10000fb0 -------- -------- -------- -------- ................\n"
-" 10000fc0 -------- -------- -------- -------- ................\n"
-" 10000fd0 -------- -------- -------- -------- ................\n"
-" 10000fe0 -------- -------- -------- -------- ................\n"
-" 10000ff0 -------- -------- -------- -------- ................\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+ ASSERT_STREQ("", tombstone_contents.c_str());
// Verify that the log buf is empty, and no error messages.
ASSERT_STREQ("", getFakeLogBuf().c_str());
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index aec8c60..b42d70c 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -359,13 +359,6 @@
ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
}
-TEST_F(TombstoneTest, dump_timestamp) {
- setenv("TZ", "UTC", 1);
- tzset();
- dump_timestamp(&log_, 0);
- ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
-}
-
class GwpAsanCrashDataTest : public GwpAsanCrashData {
public:
GwpAsanCrashDataTest(
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index ab65dd1..7af99c9 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -58,6 +58,7 @@
#include "libdebuggerd/open_files_list.h"
#include "libdebuggerd/scudo.h"
#include "libdebuggerd/utility.h"
+#include "util.h"
#include "gwp_asan/common.h"
#include "gwp_asan/crash_handler.h"
@@ -80,15 +81,6 @@
_LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
}
-static void dump_timestamp(log_t* log, time_t time) {
- struct tm tm;
- localtime_r(&time, &tm);
-
- char buf[strlen("1970-01-01 00:00:00+0830") + 1];
- strftime(buf, sizeof(buf), "%F %T%z", &tm);
- _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf);
-}
-
static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,
unwindstack::Maps* maps) {
static constexpr uint64_t kMaxDifferenceBytes = 256;
@@ -182,12 +174,8 @@
}
static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
- // Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
- // TODO: Why is this controlled by thread name?
- if (thread_info.thread_name == "logd" ||
- android::base::StartsWith(thread_info.thread_name, "logd.")) {
- log->should_retrieve_logcat = false;
- }
+ // Don't try to collect logs from the threads that implement the logging system itself.
+ if (thread_info.uid == AID_LOGD) log->should_retrieve_logcat = false;
_LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", thread_info.pid,
thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
@@ -507,10 +495,9 @@
// (although in this case the pid is redundant).
char timeBuf[32];
time_t sec = static_cast<time_t>(log_entry.entry.sec);
- struct tm tmBuf;
- struct tm* ptm;
- ptm = localtime_r(&sec, &tmBuf);
- strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
+ tm tm;
+ localtime_r(&sec, &tm);
+ strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", &tm);
char* msg = log_entry.msg();
if (msg == nullptr) {
@@ -571,23 +558,20 @@
log.tfd = tombstone_fd;
log.amfd_data = nullptr;
- char thread_name[16];
- char process_name[128];
-
- read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
- read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
+ std::string thread_name = get_thread_name(tid);
+ std::string process_name = get_process_name(pid);
std::unique_ptr<unwindstack::Regs> regs(
unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
std::map<pid_t, ThreadInfo> threads;
- threads[gettid()] = ThreadInfo{
+ threads[tid] = ThreadInfo{
.registers = std::move(regs),
.uid = uid,
.tid = tid,
- .thread_name = thread_name,
+ .thread_name = thread_name.c_str(),
.pid = pid,
- .process_name = process_name,
+ .process_name = process_name.c_str(),
.siginfo = siginfo,
};
@@ -606,8 +590,8 @@
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, OpenFilesList* open_files,
std::string* amfd_data) {
- // don't copy log messages to tombstone unless this is a dev device
- bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
+ // Don't copy log messages to tombstone unless this is a development device.
+ bool want_logs = GetBoolProperty("ro.debuggable", false);
log_t log;
log.current_tid = target_thread;
@@ -617,7 +601,7 @@
_LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
dump_header_info(&log);
- dump_timestamp(&log, time(nullptr));
+ _LOG(&log, logtype::HEADER, "Timestamp: %s\n", get_timestamp().c_str());
auto it = threads.find(target_thread);
if (it == threads.end()) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 0a491bb..4e6df09 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -44,7 +44,6 @@
using android::base::unique_fd;
-// Whitelist output desired in the logcat output.
bool is_allowed_in_logcat(enum logtype ltype) {
if ((ltype == HEADER)
|| (ltype == REGISTERS)
@@ -129,28 +128,23 @@
#define MEMORY_BYTES_PER_LINE 16
void dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const std::string& label) {
- // Align the address to sizeof(long) and start 32 bytes before the address.
- addr &= ~(sizeof(long) - 1);
+ // Align the address to the number of bytes per line to avoid confusing memory tag output if
+ // memory is tagged and we start from a misaligned address. Start 32 bytes before the address.
+ addr &= ~(MEMORY_BYTES_PER_LINE - 1);
if (addr >= 4128) {
addr -= 32;
}
- // We don't want the address tag to interfere with the bounds check below or appear in the
- // addresses in the memory dump.
+ // We don't want the address tag to appear in the addresses in the memory dump.
addr = untag_address(addr);
- // Don't bother if the address looks too low, or looks too high.
- if (addr < 4096 ||
-#if defined(__LP64__)
- addr > 0x4000000000000000UL - MEMORY_BYTES_TO_DUMP) {
-#else
- addr > 0xffff0000 - MEMORY_BYTES_TO_DUMP) {
-#endif
+ // Don't bother if the address would overflow, taking tag bits into account. Note that
+ // untag_address truncates to 32 bits on 32-bit platforms as a side effect of returning a
+ // uintptr_t, so this also checks for 32-bit overflow.
+ if (untag_address(addr + MEMORY_BYTES_TO_DUMP - 1) < addr) {
return;
}
- _LOG(log, logtype::MEMORY, "\n%s:\n", label.c_str());
-
// Dump 256 bytes
uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)];
memset(data, 0, MEMORY_BYTES_TO_DUMP);
@@ -191,6 +185,15 @@
}
}
+ // If we were unable to read anything, it probably means that the register doesn't contain a
+ // valid pointer. In that case, skip the output for this register entirely rather than emitting 16
+ // lines of dashes.
+ if (bytes == 0) {
+ return;
+ }
+
+ _LOG(log, logtype::MEMORY, "\n%s:\n", label.c_str());
+
// Dump the code around memory as:
// addr contents ascii
// 0000000000008d34 ef000000e8bd0090 e1b00000512fff1e ............../Q
@@ -201,8 +204,13 @@
size_t current = 0;
size_t total_bytes = start + bytes;
for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
+ uint64_t tagged_addr = addr;
+ long tag = memory->ReadTag(addr);
+ if (tag >= 0) {
+ tagged_addr |= static_cast<uint64_t>(tag) << 56;
+ }
std::string logline;
- android::base::StringAppendF(&logline, " %" PRIPTR, addr);
+ android::base::StringAppendF(&logline, " %" PRIPTR, tagged_addr);
addr += MEMORY_BYTES_PER_LINE;
std::string ascii;
@@ -230,23 +238,6 @@
}
}
-void read_with_default(const char* path, char* buf, size_t len, const char* default_value) {
- unique_fd fd(open(path, O_RDONLY | O_CLOEXEC));
- if (fd != -1) {
- int rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, len - 1));
- if (rc != -1) {
- buf[rc] = '\0';
-
- // Trim trailing newlines.
- if (rc > 0 && buf[rc - 1] == '\n') {
- buf[rc - 1] = '\0';
- }
- return;
- }
- }
- strcpy(buf, default_value);
-}
-
void drop_capabilities() {
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index a37b3b9..9d09210 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -17,6 +17,7 @@
#include "util.h"
#include <sys/socket.h>
+#include <time.h>
#include <string>
#include <utility>
@@ -38,3 +39,19 @@
android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/comm", tid), &result);
return android::base::Trim(result);
}
+
+std::string get_timestamp() {
+ timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+
+ tm tm;
+ localtime_r(&ts.tv_sec, &tm);
+
+ char buf[strlen("1970-01-01 00:00:00.123456789+0830") + 1];
+ char* s = buf;
+ size_t sz = sizeof(buf), n;
+ n = strftime(s, sz, "%F %H:%M", &tm), s += n, sz -= n;
+ n = snprintf(s, sz, ":%02d.%09ld", tm.tm_sec, ts.tv_nsec), s += n, sz -= n;
+ n = strftime(s, sz, "%z", &tm), s += n, sz -= n;
+ return buf;
+}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index e964423..07e7e99 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -23,3 +23,5 @@
std::string get_process_name(pid_t pid);
std::string get_thread_name(pid_t tid);
+
+std::string get_timestamp();
diff --git a/diagnose_usb/diagnose_usb.cpp b/diagnose_usb/diagnose_usb.cpp
index 5695ece..35edb5e 100644
--- a/diagnose_usb/diagnose_usb.cpp
+++ b/diagnose_usb/diagnose_usb.cpp
@@ -49,7 +49,7 @@
// additionally just to be sure.
if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
// The user is in plugdev so the problem is likely with the udev rules.
- return "user in plugdev group; are your udev rules wrong?";
+ return "missing udev rules? user is in the plugdev group";
}
passwd* pwd = getpwuid(getuid());
return android::base::StringPrintf("user %s is not in the plugdev group",
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index bdb786c..6673543 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -271,6 +271,7 @@
required: [
"mke2fs",
"make_f2fs",
+ "make_f2fs_casefold",
],
dist: {
targets: [
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index fd009e7..0e918a3 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -21,6 +21,7 @@
my_dist_files := $(SOONG_HOST_OUT_EXECUTABLES)/mke2fs
my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/e2fsdroid
my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs_casefold
my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/sload_f2fs
$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
my_dist_files :=
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index e7f785b..34ab32c 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -1286,7 +1286,7 @@
ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_buf, &retcode, &err_msg)) << err_msg;
ASSERT_EQ(retcode, 0) << err_msg;
- // Sanity check of hash
+ // Validity check of hash
EXPECT_NE(hash_before, hash_buf)
<< "Writing a random buffer to 'userdata' had the same hash as after erasing it";
SetLockState(true); // Lock the device
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 6cd0430..a349408 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -7,7 +7,7 @@
"name": "liblp_test"
},
{
- "name": "fiemap_image_test_presubmit"
+ "name": "fiemap_image_test"
},
{
"name": "fiemap_writer_test"
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 10c1eba..1b2cdc1 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -301,13 +301,10 @@
return true;
}
-static bool needs_block_encryption(const FstabEntry& entry);
-static bool should_use_metadata_encryption(const FstabEntry& entry);
-
// Read the primary superblock from an ext4 filesystem. On failure return
// false. If it's not an ext4 filesystem, also set FS_STAT_INVALID_MAGIC.
-static bool read_ext4_superblock(const std::string& blk_device, const FstabEntry& entry,
- struct ext4_super_block* sb, int* fs_stat) {
+static bool read_ext4_superblock(const std::string& blk_device, struct ext4_super_block* sb,
+ int* fs_stat) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd < 0) {
@@ -324,29 +321,7 @@
LINFO << "Invalid ext4 superblock on '" << blk_device << "'";
// not a valid fs, tune2fs, fsck, and mount will all fail.
*fs_stat |= FS_STAT_INVALID_MAGIC;
-
- bool encrypted = should_use_metadata_encryption(entry) || needs_block_encryption(entry);
- if (entry.mount_point == "/data" &&
- (!encrypted || android::base::StartsWith(blk_device, "/dev/block/dm-"))) {
- // try backup superblock, if main superblock is corrupted
- for (unsigned int blocksize = EXT4_MIN_BLOCK_SIZE; blocksize <= EXT4_MAX_BLOCK_SIZE;
- blocksize *= 2) {
- uint64_t superblock = blocksize * 8;
- if (blocksize == EXT4_MIN_BLOCK_SIZE) superblock++;
-
- if (TEMP_FAILURE_RETRY(pread(fd, sb, sizeof(*sb), superblock * blocksize)) !=
- sizeof(*sb)) {
- PERROR << "Can't read '" << blk_device << "' superblock";
- return false;
- }
- if (is_ext4_superblock_valid(sb) &&
- (1 << (10 + sb->s_log_block_size) == blocksize)) {
- *fs_stat &= ~FS_STAT_INVALID_MAGIC;
- break;
- }
- }
- }
- if (*fs_stat & FS_STAT_INVALID_MAGIC) return false;
+ return false;
}
*fs_stat |= FS_STAT_IS_EXT4;
LINFO << "superblock s_max_mnt_count:" << sb->s_max_mnt_count << "," << blk_device;
@@ -546,13 +521,13 @@
}
// Enable casefold if needed.
-static void tune_casefold(const std::string& blk_device, const struct ext4_super_block* sb,
- int* fs_stat) {
+static void tune_casefold(const std::string& blk_device, const FstabEntry& entry,
+ const struct ext4_super_block* sb, int* fs_stat) {
bool has_casefold = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_CASEFOLD)) != 0;
bool wants_casefold =
android::base::GetBoolProperty("external_storage.casefold.enabled", false);
- if (!wants_casefold || has_casefold) return;
+ if (entry.mount_point != "data" || !wants_casefold || has_casefold ) return;
std::string casefold_support;
if (!android::base::ReadFileToString(SYSFS_EXT4_CASEFOLD, &casefold_support)) {
@@ -607,7 +582,7 @@
LINFO << "Enabling ext4 metadata_csum on " << blk_device;
- // requires to give last_fsck_time to current to avoid insane time.
+ // Must give `-T now` to prevent last_fsck_time from growing too large,
// otherwise, tune2fs won't enable metadata_csum.
const char* tune2fs_args[] = {TUNE2FS_BIN, "-O", "metadata_csum,64bit,extent",
"-T", "now", blk_device.c_str()};
@@ -687,7 +662,7 @@
if (is_extfs(entry.fs_type)) {
struct ext4_super_block sb;
- if (read_ext4_superblock(blk_device, entry, &sb, &fs_stat)) {
+ if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
(sb.s_state & EXT4_VALID_FS) == 0) {
LINFO << "Filesystem on " << blk_device << " was not cleanly shutdown; "
@@ -717,11 +692,11 @@
entry.fs_mgr_flags.fs_verity || entry.fs_mgr_flags.ext_meta_csum)) {
struct ext4_super_block sb;
- if (read_ext4_superblock(blk_device, entry, &sb, &fs_stat)) {
+ if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
tune_reserved_size(blk_device, entry, &sb, &fs_stat);
tune_encrypt(blk_device, entry, &sb, &fs_stat);
tune_verity(blk_device, entry, &sb, &fs_stat);
- tune_casefold(blk_device, &sb, &fs_stat);
+ tune_casefold(blk_device, entry, &sb, &fs_stat);
tune_metadata_csum(blk_device, entry, &sb, &fs_stat);
}
}
@@ -1021,6 +996,19 @@
}
}
+static void set_type_property(int status) {
+ switch (status) {
+ case FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED:
+ SetProperty("ro.crypto.type", "block");
+ break;
+ case FS_MGR_MNTALL_DEV_FILE_ENCRYPTED:
+ case FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED:
+ case FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION:
+ SetProperty("ro.crypto.type", "file");
+ break;
+ }
+}
+
static bool call_vdc(const std::vector<std::string>& args, int* ret) {
std::vector<char const*> argv;
argv.emplace_back("/system/bin/vdc");
@@ -1158,10 +1146,9 @@
// metadata-encrypted device with smaller blocks, we must not change this for
// devices shipped with Q or earlier unless they explicitly selected dm-default-key
// v2
- constexpr unsigned int pre_gki_level = __ANDROID_API_Q__;
unsigned int options_format_version = android::base::GetUintProperty<unsigned int>(
"ro.crypto.dm_default_key.options_format.version",
- (android::fscrypt::GetFirstApiLevel() <= pre_gki_level ? 1 : 2));
+ (android::fscrypt::GetFirstApiLevel() <= __ANDROID_API_Q__ ? 1 : 2));
if (options_format_version > 1) {
bowTarget->SetBlockSize(4096);
}
@@ -1412,6 +1399,7 @@
avb_handle = AvbHandle::Open();
if (!avb_handle) {
LERROR << "Failed to open AvbHandle";
+ set_type_property(encryptable);
return FS_MGR_MNTALL_FAIL;
}
}
@@ -1468,6 +1456,7 @@
attempted_entry.mount_point},
nullptr)) {
LERROR << "Encryption failed";
+ set_type_property(encryptable);
return FS_MGR_MNTALL_FAIL;
}
}
@@ -1565,6 +1554,8 @@
}
}
+ set_type_property(encryptable);
+
#if ALLOW_ADBD_DISABLE_VERITY == 1 // "userdebug" build
fs_mgr_overlayfs_mount_all(fstab);
#endif
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 2f20c91..63b7ad2 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -635,13 +635,14 @@
entry.fs_mgr_flags.wait = true;
entry.fs_mgr_flags.logical = true;
entry.fs_mgr_flags.first_stage_mount = true;
+ fstab->emplace_back(entry);
} else {
// If the corresponding partition exists, transform all its Fstab
// by pointing .blk_device to the DSU partition.
for (auto&& entry : entries) {
entry->blk_device = partition;
// AVB keys for DSU should always be under kDsuKeysDir.
- entry->avb_keys += kDsuKeysDir;
+ entry->avb_keys = kDsuKeysDir;
}
// Make sure the ext4 is included to support GSI.
auto partition_ext4 =
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 58241b3..e425284 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -42,6 +42,7 @@
enabled: false,
},
},
+ ramdisk_available: true,
}
filegroup {
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index cae43e6..a622110 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -110,30 +110,3 @@
auto_gen_config: true,
require_root: true,
}
-
-/* BUG(148874852) temporary test */
-cc_test {
- name: "fiemap_image_test_presubmit",
- cppflags: [
- "-DSKIP_TEST_IN_PRESUBMIT",
- ],
- static_libs: [
- "libcrypto_utils",
- "libdm",
- "libext4_utils",
- "libfs_mgr",
- "liblp",
- ],
- shared_libs: [
- "libbase",
- "libcrypto",
- "libcutils",
- "liblog",
- ],
- srcs: [
- "image_test.cpp",
- ],
- test_suites: ["device-tests"],
- auto_gen_config: true,
- require_root: true,
-}
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 4dd4bcc..621031a 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -45,7 +45,7 @@
using namespace android::dm;
-// We cap the maximum number of extents as a sanity measure.
+// We cap the maximum number of extents as a robustness measure.
static constexpr uint32_t kMaxExtents = 50000;
// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 3ee742f..93fc131 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -136,13 +136,13 @@
return !!FindPartition(*metadata.get(), name);
}
-static bool IsTestDir(const std::string& path) {
- return android::base::StartsWith(path, kTestImageMetadataDir) ||
- android::base::StartsWith(path, kOtaTestImageMetadataDir);
+bool ImageManager::MetadataDirIsTest() const {
+ return IsSubdir(metadata_dir_, kTestImageMetadataDir) ||
+ IsSubdir(metadata_dir_, kOtaTestImageMetadataDir);
}
-static bool IsUnreliablePinningAllowed(const std::string& path) {
- return android::base::StartsWith(path, "/data/gsi/dsu/") || IsTestDir(path);
+bool ImageManager::IsUnreliablePinningAllowed() const {
+ return IsSubdir(data_dir_, "/data/gsi/dsu/") || MetadataDirIsTest();
}
FiemapStatus ImageManager::CreateBackingImage(
@@ -159,7 +159,7 @@
if (!FilesystemHasReliablePinning(data_path, &reliable_pinning)) {
return FiemapStatus::Error();
}
- if (!reliable_pinning && !IsUnreliablePinningAllowed(data_path)) {
+ if (!reliable_pinning && !IsUnreliablePinningAllowed()) {
// For historical reasons, we allow unreliable pinning for certain use
// cases (DSUs, testing) because the ultimate use case is either
// developer-oriented or ephemeral (the intent is to boot immediately
@@ -178,7 +178,7 @@
// if device-mapper is stacked in some complex way not supported by
// FiemapWriter.
auto device_path = GetDevicePathForFile(fw.get());
- if (android::base::StartsWith(device_path, "/dev/block/dm-") && !IsTestDir(metadata_dir_)) {
+ if (android::base::StartsWith(device_path, "/dev/block/dm-") && !MetadataDirIsTest()) {
LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
fw = {};
@@ -640,16 +640,22 @@
return false;
}
+ bool ok = true;
for (const auto& partition : metadata->partitions) {
auto name = GetPartitionName(partition);
auto image_path = GetImageHeaderPath(name);
auto fiemap = SplitFiemap::Open(image_path);
- if (!fiemap || !fiemap->HasPinnedExtents()) {
- LOG(ERROR) << "Image is missing or was moved: " << image_path;
- return false;
+ if (fiemap == nullptr) {
+ LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
+ ok = false;
+ continue;
+ }
+ if (!fiemap->HasPinnedExtents()) {
+ LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
+ ok = false;
}
}
- return true;
+ return ok;
}
bool ImageManager::DisableImage(const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 6663391..6d09751 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -34,10 +34,13 @@
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
+#include "utility.h"
+
using namespace android::dm;
using namespace std::literals;
using android::base::unique_fd;
using android::fiemap::ImageManager;
+using android::fiemap::IsSubdir;
using android::fs_mgr::BlockDeviceInfo;
using android::fs_mgr::PartitionOpener;
using android::fs_mgr::WaitForFile;
@@ -131,6 +134,51 @@
ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
}
+namespace {
+
+struct IsSubdirTestParam {
+ std::string child;
+ std::string parent;
+ bool result;
+};
+
+class IsSubdirTest : public ::testing::TestWithParam<IsSubdirTestParam> {};
+
+TEST_P(IsSubdirTest, Test) {
+ const auto& param = GetParam();
+ EXPECT_EQ(param.result, IsSubdir(param.child, param.parent))
+ << "IsSubdir(child=\"" << param.child << "\", parent=\"" << param.parent
+ << "\") != " << (param.result ? "true" : "false");
+}
+
+std::vector<IsSubdirTestParam> IsSubdirTestValues() {
+ // clang-format off
+ std::vector<IsSubdirTestParam> base_cases{
+ {"/foo/bar", "/foo", true},
+ {"/foo/bar/baz", "/foo", true},
+ {"/foo", "/foo", true},
+ {"/foo", "/", true},
+ {"/", "/", true},
+ {"/foo", "/foo/bar", false},
+ {"/foo", "/bar", false},
+ {"/foo-bar", "/foo", false},
+ {"/", "/foo", false},
+ };
+ // clang-format on
+ std::vector<IsSubdirTestParam> ret;
+ for (const auto& e : base_cases) {
+ ret.push_back(e);
+ ret.push_back({e.child + "/", e.parent, e.result});
+ ret.push_back({e.child, e.parent + "/", e.result});
+ ret.push_back({e.child + "/", e.parent + "/", e.result});
+ }
+ return ret;
+}
+
+INSTANTIATE_TEST_SUITE_P(IsSubdirTest, IsSubdirTest, ::testing::ValuesIn(IsSubdirTestValues()));
+
+} // namespace
+
bool Mkdir(const std::string& path) {
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 60b98dc..50f4f33 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -176,6 +176,8 @@
bool MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,
const std::chrono::milliseconds& timeout_ms, std::string* path);
bool UnmapImageDevice(const std::string& name, bool force);
+ bool IsUnreliablePinningAllowed() const;
+ bool MetadataDirIsTest() const;
ImageManager(const ImageManager&) = delete;
ImageManager& operator=(const ImageManager&) = delete;
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index 12c7397..36bb3df 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -266,7 +266,7 @@
cursor_file_pos_ += bytes_to_write;
}
- // If we've reached the end of the current file, close it for sanity.
+ // If we've reached the end of the current file, close it.
if (cursor_file_pos_ == file->size()) {
cursor_fd_ = {};
}
diff --git a/fs_mgr/libfiemap/utility.cpp b/fs_mgr/libfiemap/utility.cpp
index bbb0510..54cf183 100644
--- a/fs_mgr/libfiemap/utility.cpp
+++ b/fs_mgr/libfiemap/utility.cpp
@@ -139,8 +139,7 @@
}
*bdev_name = ::android::base::Basename(sysfs_bdev);
- // Paranoid sanity check to make sure we just didn't get the
- // input in return as-is.
+ // Check that the symlink doesn't point to itself.
if (sysfs_bdev == *bdev_name) {
LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
return false;
@@ -168,5 +167,30 @@
return F2fsPinBeforeAllocate(fd, supported);
}
+bool IsSubdir(const std::string& child, const std::string& parent) {
+ // Precondition: both are absolute paths.
+ CHECK(android::base::StartsWith(child, "/")) << "Not an absolute path: " << child;
+ CHECK(android::base::StartsWith(parent, "/")) << "Not an absolute path: " << parent;
+
+ // Remove extraneous "/" at the end.
+ std::string_view child_sv = child;
+ while (child_sv != "/" && android::base::ConsumeSuffix(&child_sv, "/"))
+ ;
+
+ std::string_view parent_sv = parent;
+ while (parent_sv != "/" && android::base::ConsumeSuffix(&parent_sv, "/"))
+ ;
+
+ // IsSubdir(anything, "/") => true
+ if (parent_sv == "/") return true;
+
+ // IsSubdir("/foo", "/foo") => true
+ if (parent_sv == child_sv) return true;
+
+ // IsSubdir("/foo/bar", "/foo") => true
+ // IsSubdir("/foo-bar", "/foo") => false
+ return android::base::StartsWith(child_sv, std::string(parent_sv) + "/");
+}
+
} // namespace fiemap
} // namespace android
diff --git a/fs_mgr/libfiemap/utility.h b/fs_mgr/libfiemap/utility.h
index 4c0bc2b..aa40f79 100644
--- a/fs_mgr/libfiemap/utility.h
+++ b/fs_mgr/libfiemap/utility.h
@@ -51,5 +51,9 @@
// cases (such as snapshots or adb remount).
bool FilesystemHasReliablePinning(const std::string& file, bool* supported);
+// Crude implementation to check if |child| is a subdir of |parent|.
+// Assume both are absolute paths.
+bool IsSubdir(const std::string& child, const std::string& parent);
+
} // namespace fiemap
} // namespace android
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index c192bf5..46072bb 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -52,16 +52,16 @@
partition, offset, num_bytes, buffer, out_num_read);
}
-static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
- size_t rollback_index_location ATTRIBUTE_UNUSED,
- uint64_t* out_rollback_index) {
+static AvbIOResult no_op_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
+ size_t rollback_index_location ATTRIBUTE_UNUSED,
+ uint64_t* out_rollback_index) {
// rollback_index has been checked in bootloader phase.
// In user-space, returns the smallest value 0 to pass the check.
*out_rollback_index = 0;
return AVB_IO_RESULT_OK;
}
-static AvbIOResult dummy_validate_vbmeta_public_key(
+static AvbIOResult no_op_validate_vbmeta_public_key(
AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
size_t public_key_length ATTRIBUTE_UNUSED,
const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
@@ -76,8 +76,8 @@
return AVB_IO_RESULT_OK;
}
-static AvbIOResult dummy_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
- bool* out_is_unlocked) {
+static AvbIOResult no_op_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,
+ bool* out_is_unlocked) {
// The function is for bootloader to update the value into
// androidboot.vbmeta.device_state in kernel cmdline.
// In user-space, returns true as we don't need to update it anymore.
@@ -85,9 +85,9 @@
return AVB_IO_RESULT_OK;
}
-static AvbIOResult dummy_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,
- const char* partition ATTRIBUTE_UNUSED,
- char* guid_buf, size_t guid_buf_size) {
+static AvbIOResult no_op_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+ const char* partition ATTRIBUTE_UNUSED,
+ char* guid_buf, size_t guid_buf_size) {
// The function is for bootloader to set the correct UUID
// for a given partition in kernel cmdline.
// In user-space, returns a faking one as we don't need to update
@@ -96,9 +96,9 @@
return AVB_IO_RESULT_OK;
}
-static AvbIOResult dummy_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
- const char* partition ATTRIBUTE_UNUSED,
- uint64_t* out_size_num_byte) {
+static AvbIOResult no_op_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+ const char* partition ATTRIBUTE_UNUSED,
+ uint64_t* out_size_num_byte) {
// The function is for bootloader to load entire content of AVB HASH partitions.
// In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
*out_size_num_byte = 0;
@@ -123,15 +123,15 @@
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
// Other I/O operations are only required in bootloader but not in
- // user-space so we set them as dummy operations. Also zero the entire
+ // user-space so we set them as no-op operations. Also zero the entire
// struct so operations added in the future will be set to NULL.
memset(&avb_ops_, 0, sizeof(AvbOps));
avb_ops_.read_from_partition = read_from_partition;
- avb_ops_.read_rollback_index = dummy_read_rollback_index;
- avb_ops_.validate_vbmeta_public_key = dummy_validate_vbmeta_public_key;
- avb_ops_.read_is_device_unlocked = dummy_read_is_device_unlocked;
- avb_ops_.get_unique_guid_for_partition = dummy_get_unique_guid_for_partition;
- avb_ops_.get_size_of_partition = dummy_get_size_of_partition;
+ avb_ops_.read_rollback_index = no_op_read_rollback_index;
+ avb_ops_.validate_vbmeta_public_key = no_op_validate_vbmeta_public_key;
+ avb_ops_.read_is_device_unlocked = no_op_read_is_device_unlocked;
+ avb_ops_.get_unique_guid_for_partition = no_op_get_unique_guid_for_partition;
+ avb_ops_.get_size_of_partition = no_op_get_size_of_partition;
// Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
avb_ops_.user_data = this;
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 5d504ab..49333a1 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -226,7 +226,7 @@
return nullptr;
}
- // Sanity check here because we have to use vbmeta_images_[0] below.
+ // Validity check here because we have to use vbmeta_images_[0] below.
if (avb_handle->vbmeta_images_.size() < 1) {
LERROR << "LoadAndVerifyVbmetaByPartition failed, no vbmeta loaded";
return nullptr;
@@ -405,11 +405,11 @@
// - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (UNLOCKED only).
// Might occur in either the top-level vbmeta or a chained vbmeta.
// - AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED (UNLOCKED only).
- // Could only occur in a chained vbmeta. Because we have *dummy* operations in
+ // Could only occur in a chained vbmeta. Because we have *no-op* operations in
// FsManagerAvbOps such that avb_ops->validate_vbmeta_public_key() used to validate
// the public key of the top-level vbmeta always pass in userspace here.
//
- // The following verify result won't happen, because the *dummy* operation
+ // The following verify result won't happen, because the *no-op* operation
// avb_ops->read_rollback_index() always returns the minimum value zero. So rollbacked
// vbmeta images, which should be caught in the bootloader stage, won't be detected here.
// - AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
index 5c388aa..a52a00d 100644
--- a/fs_mgr/libfs_avb/tests/util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -222,7 +222,7 @@
base::FilePath test_dir;
ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
- // Generates dummy files to list.
+ // Generates test files to list.
base::FilePath file_path_1 = test_dir.Append("1.txt");
ASSERT_TRUE(base::WriteFile(file_path_1, "1", 1));
base::FilePath file_path_2 = test_dir.Append("2.txt");
@@ -253,7 +253,7 @@
base::FilePath test_dir;
ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, "list-file-tests.", &test_dir));
- // Generates dummy files to list.
+ // Generates test files to list.
base::FilePath file_path_1 = test_dir.Append("1.txt");
ASSERT_TRUE(base::WriteFile(file_path_1, "1", 1));
base::FilePath file_path_2 = test_dir.Append("2.txt");
@@ -281,7 +281,7 @@
base::FilePath tmp_dir;
ASSERT_TRUE(GetTempDir(&tmp_dir));
- // Generates dummy files to list.
+ // Generates test files to list.
base::FilePath no_such_dir = tmp_dir.Append("not_such_dir");
auto fail = ListFiles(no_such_dir.value());
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index a21e09e..e4b617a 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -234,7 +234,7 @@
EXPECT_EQ(lba, aligned_lba);
}
- // Sanity check one extent.
+ // Check one extent.
EXPECT_EQ(exported->extents.back().target_data, 3072);
}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 6af9d94..236fd8d 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -47,7 +47,7 @@
BlockDeviceInfo device_info;
ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
- // Sanity check that the device doesn't give us some weird inefficient
+ // Check that the device doesn't give us some weird inefficient
// alignment.
EXPECT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index 1d4db85..3d3dde6 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -49,7 +49,7 @@
// Dynamic System Update is installed to an sdcard, which won't be in
// the boot device list.
//
- // We whitelist because most devices in /dev/block are not valid for
+ // mmcblk* is allowed because most devices in /dev/block are not valid for
// storing fiemaps.
if (android::base::StartsWith(path, "mmcblk")) {
return "/dev/block/" + path;
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index e6fd9f7..24ccc0f 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -174,7 +174,7 @@
return false;
}
- // Do basic sanity checks before computing the checksum.
+ // Do basic validity checks before computing the checksum.
if (header.magic != LP_METADATA_HEADER_MAGIC) {
LERROR << "Logical partition metadata has invalid magic value.";
return false;
@@ -255,7 +255,7 @@
LpMetadataHeader& header = metadata->header;
- // Sanity check the table size.
+ // Check the table size.
if (header.tables_size > geometry.metadata_max_size) {
LERROR << "Invalid partition metadata header table size.";
return nullptr;
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 8bf1ee9..2708efa 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -81,8 +81,8 @@
return header_blob + tables;
}
-// Perform sanity checks so we don't accidentally overwrite valid metadata
-// with potentially invalid metadata, or random partition data with metadata.
+// Perform checks so we don't accidentally overwrite valid metadata with
+// potentially invalid metadata, or random partition data with metadata.
static bool ValidateAndSerializeMetadata([[maybe_unused]] const IPartitionOpener& opener,
const LpMetadata& metadata, const std::string& slot_suffix,
std::string* blob) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 95301ff..77d21b2 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -122,6 +122,39 @@
],
}
+cc_defaults {
+ name: "libsnapshot_cow_defaults",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: ["include"],
+ srcs: [
+ "cow_reader.cpp",
+ "cow_writer.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "libsnapshot_cow",
+ defaults: [
+ "libsnapshot_cow_defaults",
+ ],
+ host_supported: true,
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "liblog",
+ ],
+ static_libs: [
+ "libz",
+ ],
+}
+
cc_library_static {
name: "libsnapshot_test_helpers",
defaults: ["libsnapshot_defaults"],
@@ -310,3 +343,95 @@
auto_gen_config: true,
require_root: true,
}
+
+cc_defaults {
+ name: "snapuserd_defaults",
+ srcs: [
+ "snapuserd.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror"
+ ],
+
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libdm",
+ ],
+}
+
+cc_binary {
+ name: "snapuserd",
+ defaults: ["snapuserd_defaults"],
+}
+
+cc_binary {
+ name: "snapuserd_ramdisk",
+ stem: "snapuserd",
+ defaults: ["snapuserd_defaults"],
+
+ ramdisk: true,
+ static_executable: true,
+ system_shared_libs: [],
+}
+
+cc_test {
+ name: "cow_api_test",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ srcs: [
+ "cow_api_test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "liblog",
+ "libz",
+ ],
+ static_libs: [
+ "libgtest",
+ "libsnapshot_cow",
+ ],
+ test_min_api_level: 30,
+ auto_gen_config: true,
+ require_root: false,
+}
+
+cc_binary {
+ name: "make_cow_from_ab_ota",
+ host_supported: true,
+ device_supported: false,
+ static_libs: [
+ "libbase",
+ "libbspatch",
+ "libbrotli",
+ "libbz",
+ "libchrome",
+ "libcrypto",
+ "libgflags",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libpuffpatch",
+ "libsnapshot_cow",
+ "libsparse",
+ "libxz",
+ "libz",
+ "libziparchive",
+ "update_metadata-protos",
+ ],
+ srcs: [
+ "make_cow_from_ab_ota.cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
new file mode 100644
index 0000000..3b3fc47
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -0,0 +1,244 @@
+// 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.
+
+#include <iostream>
+#include <memory>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+
+namespace android {
+namespace snapshot {
+
+class CowTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ cow_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_->fd, 0) << strerror(errno);
+ }
+
+ void TearDown() override { cow_ = nullptr; }
+
+ std::unique_ptr<TemporaryFile> cow_;
+};
+
+// Sink that always appends to the end of a string.
+class StringSink : public IByteSink {
+ public:
+ void* GetBuffer(size_t requested, size_t* actual) override {
+ size_t old_size = stream_.size();
+ stream_.resize(old_size + requested, '\0');
+ *actual = requested;
+ return stream_.data() + old_size;
+ }
+ bool ReturnData(void*, size_t) override { return true; }
+ void Reset() { stream_.clear(); }
+
+ std::string& stream() { return stream_; }
+
+ private:
+ std::string stream_;
+};
+
+TEST_F(CowTest, ReadWrite) {
+ CowOptions options;
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddCopy(10, 20));
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ CowHeader header;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_TRUE(reader.GetHeader(&header));
+ ASSERT_EQ(header.magic, kCowMagicNumber);
+ ASSERT_EQ(header.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.num_ops, 4);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 10);
+ ASSERT_EQ(op->source, 20);
+
+ StringSink sink;
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(op->source, 104);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ // Note: the zero operation gets split into two blocks.
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 51);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 52);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, CompressGz) {
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+
+ StringSink sink;
+
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_EQ(op->data_length, 56); // compressed!
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(op->source, 104);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, CompressTwoBlocks) {
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size * 2, '\0');
+
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+
+ StringSink sink;
+
+ auto op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_EQ(op->new_block, 51);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+}
+
+// Only return 1-byte buffers, to stress test the partial read logic in
+// CowReader.
+class HorribleStringSink : public StringSink {
+ public:
+ void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
+};
+
+TEST_F(CowTest, HorribleSink) {
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+
+ HorribleStringSink sink;
+ auto op = &iter->Get();
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
new file mode 100644
index 0000000..86565c4
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -0,0 +1,264 @@
+//
+// 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/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_reader.h>
+#include <openssl/sha.h>
+#include <zlib.h>
+
+namespace android {
+namespace snapshot {
+
+CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
+
+static void SHA256(const void* data, size_t length, uint8_t out[32]) {
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+}
+
+bool CowReader::Parse(android::base::unique_fd&& fd) {
+ owned_fd_ = std::move(fd);
+ return Parse(android::base::borrowed_fd{owned_fd_});
+}
+
+bool CowReader::Parse(android::base::borrowed_fd fd) {
+ fd_ = fd;
+
+ auto pos = lseek(fd_.get(), 0, SEEK_END);
+ if (pos < 0) {
+ PLOG(ERROR) << "lseek end failed";
+ return false;
+ }
+ fd_size_ = pos;
+
+ if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+ if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "read header failed";
+ return false;
+ }
+
+ // Validity check the ops range.
+ if (header_.ops_offset >= fd_size_) {
+ LOG(ERROR) << "ops offset " << header_.ops_offset << " larger than fd size " << fd_size_;
+ return false;
+ }
+ if (fd_size_ - header_.ops_offset < header_.ops_size) {
+ LOG(ERROR) << "ops size " << header_.ops_size << " is too large";
+ return false;
+ }
+
+ uint8_t header_csum[32];
+ {
+ CowHeader tmp = header_;
+ memset(&tmp.header_checksum, 0, sizeof(tmp.header_checksum));
+ SHA256(&tmp, sizeof(tmp), header_csum);
+ }
+ if (memcmp(header_csum, header_.header_checksum, sizeof(header_csum)) != 0) {
+ LOG(ERROR) << "header checksum is invalid";
+ return false;
+ }
+ return true;
+}
+
+bool CowReader::GetHeader(CowHeader* header) {
+ *header = header_;
+ return true;
+}
+
+class CowOpIter final : public ICowOpIter {
+ public:
+ CowOpIter(std::unique_ptr<uint8_t[]>&& ops, size_t len);
+
+ bool Done() override;
+ const CowOperation& Get() override;
+ void Next() override;
+
+ private:
+ bool HasNext();
+
+ std::unique_ptr<uint8_t[]> ops_;
+ const uint8_t* pos_;
+ const uint8_t* end_;
+ bool done_;
+};
+
+CowOpIter::CowOpIter(std::unique_ptr<uint8_t[]>&& ops, size_t len)
+ : ops_(std::move(ops)), pos_(ops_.get()), end_(pos_ + len), done_(!HasNext()) {}
+
+bool CowOpIter::Done() {
+ return done_;
+}
+
+bool CowOpIter::HasNext() {
+ return pos_ < end_ && size_t(end_ - pos_) >= sizeof(CowOperation);
+}
+
+void CowOpIter::Next() {
+ CHECK(!Done());
+
+ pos_ += sizeof(CowOperation);
+ if (!HasNext()) done_ = true;
+}
+
+const CowOperation& CowOpIter::Get() {
+ CHECK(!Done());
+ CHECK(HasNext());
+ return *reinterpret_cast<const CowOperation*>(pos_);
+}
+
+std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
+ if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek ops failed";
+ return nullptr;
+ }
+ auto ops_buffer = std::make_unique<uint8_t[]>(header_.ops_size);
+ if (!android::base::ReadFully(fd_, ops_buffer.get(), header_.ops_size)) {
+ PLOG(ERROR) << "read ops failed";
+ return nullptr;
+ }
+
+ uint8_t csum[32];
+ SHA256(ops_buffer.get(), header_.ops_size, csum);
+ if (memcmp(csum, header_.ops_checksum, sizeof(csum)) != 0) {
+ LOG(ERROR) << "ops checksum does not match";
+ return nullptr;
+ }
+
+ return std::make_unique<CowOpIter>(std::move(ops_buffer), header_.ops_size);
+}
+
+bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len) {
+ // Validate the offset, taking care to acknowledge possible overflow of offset+len.
+ if (offset < sizeof(header_) || offset >= header_.ops_offset || len >= fd_size_ ||
+ offset + len > header_.ops_offset) {
+ LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
+ return false;
+ }
+ if (lseek(fd_.get(), offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek to read raw bytes failed";
+ return false;
+ }
+ if (!android::base::ReadFully(fd_, buffer, len)) {
+ PLOG(ERROR) << "read raw bytes failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowReader::ReadData(const CowOperation& op, IByteSink* sink) {
+ uint64_t offset = op.source;
+
+ switch (op.compression) {
+ case kCowCompressNone: {
+ size_t remaining = op.data_length;
+ while (remaining) {
+ size_t amount = remaining;
+ void* buffer = sink->GetBuffer(amount, &amount);
+ if (!buffer) {
+ LOG(ERROR) << "Could not acquire buffer from sink";
+ return false;
+ }
+ if (!GetRawBytes(offset, buffer, amount)) {
+ return false;
+ }
+ if (!sink->ReturnData(buffer, amount)) {
+ LOG(ERROR) << "Could not return buffer to sink";
+ return false;
+ }
+ remaining -= amount;
+ offset += amount;
+ }
+ return true;
+ }
+ case kCowCompressGz: {
+ auto input = std::make_unique<Bytef[]>(op.data_length);
+ if (!GetRawBytes(offset, input.get(), op.data_length)) {
+ return false;
+ }
+
+ z_stream z = {};
+ z.next_in = input.get();
+ z.avail_in = op.data_length;
+ if (int rv = inflateInit(&z); rv != Z_OK) {
+ LOG(ERROR) << "inflateInit returned error code " << rv;
+ return false;
+ }
+
+ while (z.total_out < header_.block_size) {
+ // If no more output buffer, grab a new buffer.
+ if (z.avail_out == 0) {
+ size_t amount = header_.block_size - z.total_out;
+ z.next_out = reinterpret_cast<Bytef*>(sink->GetBuffer(amount, &amount));
+ if (!z.next_out) {
+ LOG(ERROR) << "Could not acquire buffer from sink";
+ return false;
+ }
+ z.avail_out = amount;
+ }
+
+ // Remember the position of the output buffer so we can call ReturnData.
+ auto buffer = z.next_out;
+ auto avail_out = z.avail_out;
+
+ // Decompress.
+ int rv = inflate(&z, Z_NO_FLUSH);
+ if (rv != Z_OK && rv != Z_STREAM_END) {
+ LOG(ERROR) << "inflate returned error code " << rv;
+ return false;
+ }
+
+ // Return the section of the buffer that was updated.
+ if (z.avail_out < avail_out && !sink->ReturnData(buffer, avail_out - z.avail_out)) {
+ LOG(ERROR) << "Could not return buffer to sink";
+ return false;
+ }
+
+ if (rv == Z_STREAM_END) {
+ // Error if the stream has ended, but we didn't fill the entire block.
+ if (z.total_out != header_.block_size) {
+ LOG(ERROR) << "Reached gz stream end but did not read a full block of data";
+ return false;
+ }
+ break;
+ }
+
+ CHECK(rv == Z_OK);
+
+ // Error if the stream is expecting more data, but we don't have any to read.
+ if (z.avail_in == 0) {
+ LOG(ERROR) << "Gz stream ended prematurely";
+ return false;
+ }
+ }
+ return true;
+ }
+ default:
+ LOG(ERROR) << "Unknown compression type: " << op.compression;
+ return false;
+ }
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
new file mode 100644
index 0000000..ea8e534
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -0,0 +1,230 @@
+//
+// 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/types.h>
+#include <unistd.h>
+
+#include <limits>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_writer.h>
+#include <openssl/sha.h>
+#include <zlib.h>
+
+namespace android {
+namespace snapshot {
+
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
+CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
+ SetupHeaders();
+}
+
+void CowWriter::SetupHeaders() {
+ header_ = {};
+ header_.magic = kCowMagicNumber;
+ header_.major_version = kCowVersionMajor;
+ header_.minor_version = kCowVersionMinor;
+ header_.block_size = options_.block_size;
+}
+
+bool CowWriter::Initialize(android::base::unique_fd&& fd) {
+ owned_fd_ = std::move(fd);
+ return Initialize(android::base::borrowed_fd{owned_fd_});
+}
+
+bool CowWriter::Initialize(android::base::borrowed_fd fd) {
+ fd_ = fd;
+
+ // This limitation is tied to the data field size in CowOperation.
+ if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
+ LOG(ERROR) << "Block size is too large";
+ return false;
+ }
+
+ if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+
+ if (options_.compression == "gz") {
+ compression_ = kCowCompressGz;
+ } else if (!options_.compression.empty()) {
+ LOG(ERROR) << "unrecognized compression: " << options_.compression;
+ return false;
+ }
+
+ // Headers are not complete, but this ensures the file is at the right
+ // position.
+ if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "write failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
+ header_.num_ops++;
+
+ CowOperation op = {};
+ op.type = kCowCopyOp;
+ op.new_block = new_block;
+ op.source = old_block;
+ ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+
+ return true;
+}
+
+bool CowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+ if (size % header_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << header_.block_size;
+ return false;
+ }
+
+ uint64_t pos;
+ if (!GetDataPos(&pos)) {
+ return false;
+ }
+
+ const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
+ for (size_t i = 0; i < size / header_.block_size; i++) {
+ header_.num_ops++;
+
+ CowOperation op = {};
+ op.type = kCowReplaceOp;
+ op.new_block = new_block_start + i;
+ op.source = pos;
+
+ if (compression_) {
+ auto data = Compress(iter, header_.block_size);
+ if (data.empty()) {
+ PLOG(ERROR) << "AddRawBlocks: compression failed";
+ return false;
+ }
+ if (data.size() > std::numeric_limits<uint16_t>::max()) {
+ LOG(ERROR) << "Compressed block is too large: " << data.size() << " bytes";
+ return false;
+ }
+ if (!android::base::WriteFully(fd_, data.data(), data.size())) {
+ PLOG(ERROR) << "AddRawBlocks: write failed";
+ return false;
+ }
+ op.compression = compression_;
+ op.data_length = static_cast<uint16_t>(data.size());
+ pos += data.size();
+ } else {
+ op.data_length = static_cast<uint16_t>(header_.block_size);
+ pos += header_.block_size;
+ }
+
+ ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+ iter += header_.block_size;
+ }
+
+ if (!compression_ && !android::base::WriteFully(fd_, data, size)) {
+ PLOG(ERROR) << "AddRawBlocks: write failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+ for (uint64_t i = 0; i < num_blocks; i++) {
+ header_.num_ops++;
+
+ CowOperation op = {};
+ op.type = kCowZeroOp;
+ op.new_block = new_block_start + i;
+ op.source = 0;
+ ops_ += std::basic_string<uint8_t>(reinterpret_cast<uint8_t*>(&op), sizeof(op));
+ }
+ return true;
+}
+
+std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
+ switch (compression_) {
+ case kCowCompressGz: {
+ auto bound = compressBound(length);
+ auto buffer = std::make_unique<uint8_t[]>(bound);
+
+ uLongf dest_len = bound;
+ auto rv = compress2(buffer.get(), &dest_len, reinterpret_cast<const Bytef*>(data),
+ length, Z_BEST_COMPRESSION);
+ if (rv != Z_OK) {
+ LOG(ERROR) << "compress2 returned: " << rv;
+ return {};
+ }
+ return std::basic_string<uint8_t>(buffer.get(), dest_len);
+ }
+ default:
+ LOG(ERROR) << "unhandled compression type: " << compression_;
+ break;
+ }
+ return {};
+}
+
+static void SHA256(const void* data, size_t length, uint8_t out[32]) {
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+}
+
+bool CowWriter::Finalize() {
+ auto offs = lseek(fd_.get(), 0, SEEK_CUR);
+ if (offs < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+ header_.ops_offset = offs;
+ header_.ops_size = ops_.size();
+
+ SHA256(ops_.data(), ops_.size(), header_.ops_checksum);
+ SHA256(&header_, sizeof(header_), header_.header_checksum);
+
+ if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek start failed";
+ return false;
+ }
+ if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "write header failed";
+ return false;
+ }
+ if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek ops failed";
+ return false;
+ }
+ if (!android::base::WriteFully(fd_, ops_.data(), ops_.size())) {
+ PLOG(ERROR) << "write ops failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowWriter::GetDataPos(uint64_t* pos) {
+ off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
+ if (offs < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+ *pos = offs;
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
new file mode 100644
index 0000000..6d500e7
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -0,0 +1,103 @@
+// 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 <stdint.h>
+
+namespace android {
+namespace snapshot {
+
+static constexpr uint64_t kCowMagicNumber = 0x436f77634f572121ULL;
+static constexpr uint32_t kCowVersionMajor = 1;
+static constexpr uint32_t kCowVersionMinor = 0;
+
+// This header appears as the first sequence of bytes in the COW. All fields
+// in the layout are little-endian encoded. The on-disk layout is:
+//
+// +-----------------------+
+// | Header (fixed) |
+// +-----------------------+
+// | Raw Data (variable) |
+// +-----------------------+
+// | Operations (variable) |
+// +-----------------------+
+//
+// The "raw data" occurs immediately after the header, and the operation
+// sequence occurs after the raw data. This ordering is intentional. While
+// streaming an OTA, we can immediately write compressed data, but store the
+// metadata in memory. At the end, we can simply append the metadata and flush
+// the file. There is no need to create separate files to store the metadata
+// and block data.
+struct CowHeader {
+ uint64_t magic;
+ uint16_t major_version;
+ uint16_t minor_version;
+
+ // Offset to the location of the operation sequence, and size of the
+ // operation sequence buffer. |ops_offset| is also the end of the
+ // raw data region.
+ uint64_t ops_offset;
+ uint64_t ops_size;
+ uint64_t num_ops;
+
+ // The size of block operations, in bytes.
+ uint32_t block_size;
+
+ // SHA256 checksums of this header, with this field set to 0.
+ uint8_t header_checksum[32];
+
+ // SHA256 of the operation sequence.
+ uint8_t ops_checksum[32];
+} __attribute__((packed));
+
+// Cow operations are currently fixed-size entries, but this may change if
+// needed.
+struct CowOperation {
+ // The operation code (see the constants and structures below).
+ uint8_t type;
+
+ // If this operation reads from the data section of the COW, this contains
+ // the compression type of that data (see constants below).
+ uint8_t compression;
+
+ // If this operation reads from the data section of the COW, this contains
+ // the length.
+ uint16_t data_length;
+
+ // The block of data in the new image that this operation modifies.
+ uint64_t new_block;
+
+ // The value of |source| depends on the operation code.
+ //
+ // For copy operations, this is a block location in the source image.
+ //
+ // For replace operations, this is a byte offset within the COW's data
+ // section (eg, not landing within the header or metadata). It is an
+ // absolute position within the image.
+ //
+ // For zero operations (replace with all zeroes), this is unused and must
+ // be zero.
+ uint64_t source;
+} __attribute__((packed));
+
+static constexpr uint8_t kCowCopyOp = 1;
+static constexpr uint8_t kCowReplaceOp = 2;
+static constexpr uint8_t kCowZeroOp = 3;
+
+static constexpr uint8_t kCowCompressNone = 0;
+static constexpr uint8_t kCowCompressGz = 1;
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
new file mode 100644
index 0000000..a3b1291
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -0,0 +1,107 @@
+// 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 <stdint.h>
+
+#include <functional>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+class ICowOpIter;
+
+// A ByteSink object handles requests for a buffer of a specific size. It
+// always owns the underlying buffer. It's designed to minimize potential
+// copying as we parse or decompress the COW.
+class IByteSink {
+ public:
+ virtual ~IByteSink() {}
+
+ // Called when the reader has data. The size of the request is given. The
+ // sink must return a valid pointer (or null on failure), and return the
+ // maximum number of bytes that can be written to the returned buffer.
+ //
+ // The returned buffer is owned by IByteSink, but must remain valid until
+ // the ready operation has completed (or the entire buffer has been
+ // covered by calls to ReturnData).
+ //
+ // After calling GetBuffer(), all previous buffers returned are no longer
+ // valid.
+ virtual void* GetBuffer(size_t requested, size_t* actual) = 0;
+
+ // Called when a section returned by |GetBuffer| has been filled with data.
+ virtual bool ReturnData(void* buffer, size_t length) = 0;
+};
+
+// Interface for reading from a snapuserd COW.
+class ICowReader {
+ public:
+ virtual ~ICowReader() {}
+
+ // Return the file header.
+ virtual bool GetHeader(CowHeader* header) = 0;
+
+ // Return an iterator for retrieving CowOperation entries.
+ virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
+
+ // Get raw bytes from the data section.
+ virtual bool GetRawBytes(uint64_t offset, void* buffer, size_t len) = 0;
+
+ // Get decoded bytes from the data section, handling any decompression.
+ // All retrieved data is passed to the sink.
+ virtual bool ReadData(const CowOperation& op, IByteSink* sink) = 0;
+};
+
+// Iterate over a sequence of COW operations.
+class ICowOpIter {
+ public:
+ virtual ~ICowOpIter() {}
+
+ // True if there are more items to read, false otherwise.
+ virtual bool Done() = 0;
+
+ // Read the current operation.
+ virtual const CowOperation& Get() = 0;
+
+ // Advance to the next item.
+ virtual void Next() = 0;
+};
+
+class CowReader : public ICowReader {
+ public:
+ CowReader();
+
+ bool Parse(android::base::unique_fd&& fd);
+ bool Parse(android::base::borrowed_fd fd);
+
+ bool GetHeader(CowHeader* header) override;
+ std::unique_ptr<ICowOpIter> GetOpIter() override;
+ bool GetRawBytes(uint64_t offset, void* buffer, size_t len) override;
+ bool ReadData(const CowOperation& op, IByteSink* sink) override;
+
+ private:
+ android::base::unique_fd owned_fd_;
+ android::base::borrowed_fd fd_;
+ CowHeader header_;
+ uint64_t fd_size_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
new file mode 100644
index 0000000..5a2cbd6
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -0,0 +1,86 @@
+// 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 <stdint.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+struct CowOptions {
+ uint32_t block_size = 4096;
+ std::string compression;
+};
+
+// Interface for writing to a snapuserd COW. All operations are ordered; merges
+// will occur in the sequence they were added to the COW.
+class ICowWriter {
+ public:
+ explicit ICowWriter(const CowOptions& options) : options_(options) {}
+
+ virtual ~ICowWriter() {}
+
+ // Encode an operation that copies the contents of |old_block| to the
+ // location of |new_block|.
+ virtual bool AddCopy(uint64_t new_block, uint64_t old_block) = 0;
+
+ // Encode a sequence of raw blocks. |size| must be a multiple of the block size.
+ virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+
+ // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
+ virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+
+ protected:
+ CowOptions options_;
+};
+
+class CowWriter : public ICowWriter {
+ public:
+ explicit CowWriter(const CowOptions& options);
+
+ // Set up the writer.
+ bool Initialize(android::base::unique_fd&& fd);
+ bool Initialize(android::base::borrowed_fd fd);
+
+ bool AddCopy(uint64_t new_block, uint64_t old_block) override;
+ bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+
+ // Finalize all COW operations and flush pending writes.
+ bool Finalize();
+
+ private:
+ void SetupHeaders();
+ bool GetDataPos(uint64_t* pos);
+ std::basic_string<uint8_t> Compress(const void* data, size_t length);
+
+ private:
+ android::base::unique_fd owned_fd_;
+ android::base::borrowed_fd fd_;
+ CowHeader header_;
+ int compression_ = 0;
+
+ // :TODO: this is not efficient, but stringstream ubsan aborts because some
+ // bytes overflow a signed char.
+ std::basic_string<uint8_t> ops_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 3c2c776..a4a3150 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -553,9 +553,8 @@
// This should only be called in recovery.
bool UnmapAllPartitions();
- // Sanity check no snapshot overflows. Note that this returns false negatives if the snapshot
- // overflows, then is remapped and not written afterwards. Hence, the function may only serve
- // as a sanity check.
+ // Check no snapshot overflows. Note that this returns false negatives if the snapshot
+ // overflows, then is remapped and not written afterwards.
bool EnsureNoOverflowSnapshot(LockedFile* lock);
enum class Slot { Unknown, Source, Target };
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 98bf56a..8e369b0 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -180,5 +180,22 @@
uint64_t bsize_ = 0;
};
+bool IsVirtualAbEnabled();
+
+#define SKIP_IF_NON_VIRTUAL_AB() \
+ do { \
+ if (!IsVirtualAbEnabled()) GTEST_SKIP() << "Test for Virtual A/B devices only"; \
+ } while (0)
+
+#define RETURN_IF_NON_VIRTUAL_AB_MSG(msg) \
+ do { \
+ if (!IsVirtualAbEnabled()) { \
+ std::cerr << (msg); \
+ return; \
+ } \
+ } while (0)
+
+#define RETURN_IF_NON_VIRTUAL_AB() RETURN_IF_NON_VIRTUAL_AB_MSG("")
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
new file mode 100644
index 0000000..d0b8f52
--- /dev/null
+++ b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
@@ -0,0 +1,690 @@
+//
+// 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 <arpa/inet.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <limits>
+#include <string>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <bsdiff/bspatch.h>
+#include <bzlib.h>
+#include <gflags/gflags.h>
+#include <libsnapshot/cow_writer.h>
+#include <puffin/puffpatch.h>
+#include <sparse/sparse.h>
+#include <update_engine/update_metadata.pb.h>
+#include <xz.h>
+#include <ziparchive/zip_archive.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::Extent;
+using chromeos_update_engine::InstallOperation;
+using chromeos_update_engine::PartitionUpdate;
+
+static constexpr uint64_t kBlockSize = 4096;
+
+DEFINE_string(source_tf, "", "Source target files (dir or zip file) for incremental payloads");
+DEFINE_string(compression, "gz", "Compression type to use (none or gz)");
+
+void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
+ unsigned int, const char* message) {
+ if (severity == android::base::ERROR) {
+ fprintf(stderr, "%s\n", message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
+uint64_t ToLittleEndian(uint64_t value) {
+ union {
+ uint64_t u64;
+ char bytes[8];
+ } packed;
+ packed.u64 = value;
+ std::swap(packed.bytes[0], packed.bytes[7]);
+ std::swap(packed.bytes[1], packed.bytes[6]);
+ std::swap(packed.bytes[2], packed.bytes[5]);
+ std::swap(packed.bytes[3], packed.bytes[4]);
+ return packed.u64;
+}
+
+class PayloadConverter final {
+ public:
+ PayloadConverter(const std::string& in_file, const std::string& out_dir)
+ : in_file_(in_file), out_dir_(out_dir), source_tf_zip_(nullptr, &CloseArchive) {}
+
+ bool Run();
+
+ private:
+ bool OpenPayload();
+ bool OpenSourceTargetFiles();
+ bool ProcessPartition(const PartitionUpdate& update);
+ bool ProcessOperation(const InstallOperation& op);
+ bool ProcessZero(const InstallOperation& op);
+ bool ProcessCopy(const InstallOperation& op);
+ bool ProcessReplace(const InstallOperation& op);
+ bool ProcessDiff(const InstallOperation& op);
+ borrowed_fd OpenSourceImage();
+
+ std::string in_file_;
+ std::string out_dir_;
+ unique_fd in_fd_;
+ uint64_t payload_offset_ = 0;
+ DeltaArchiveManifest manifest_;
+ std::unordered_set<std::string> dap_;
+ unique_fd source_tf_fd_;
+ std::unique_ptr<ZipArchive, decltype(&CloseArchive)> source_tf_zip_;
+
+ // Updated during ProcessPartition().
+ std::string partition_name_;
+ std::unique_ptr<CowWriter> writer_;
+ unique_fd source_image_;
+};
+
+bool PayloadConverter::Run() {
+ if (!OpenPayload()) {
+ return false;
+ }
+
+ if (manifest_.has_dynamic_partition_metadata()) {
+ const auto& dpm = manifest_.dynamic_partition_metadata();
+ for (const auto& group : dpm.groups()) {
+ for (const auto& partition : group.partition_names()) {
+ dap_.emplace(partition);
+ }
+ }
+ }
+
+ if (dap_.empty()) {
+ LOG(ERROR) << "No dynamic partitions found.";
+ return false;
+ }
+
+ if (!OpenSourceTargetFiles()) {
+ return false;
+ }
+
+ for (const auto& update : manifest_.partitions()) {
+ if (!ProcessPartition(update)) {
+ return false;
+ }
+ writer_ = nullptr;
+ source_image_.reset();
+ }
+ return true;
+}
+
+bool PayloadConverter::OpenSourceTargetFiles() {
+ if (FLAGS_source_tf.empty()) {
+ return true;
+ }
+
+ source_tf_fd_.reset(open(FLAGS_source_tf.c_str(), O_RDONLY));
+ if (source_tf_fd_ < 0) {
+ LOG(ERROR) << "open failed: " << FLAGS_source_tf;
+ return false;
+ }
+
+ struct stat s;
+ if (fstat(source_tf_fd_.get(), &s) < 0) {
+ LOG(ERROR) << "fstat failed: " << FLAGS_source_tf;
+ return false;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ return true;
+ }
+
+ // Otherwise, assume it's a zip file.
+ ZipArchiveHandle handle;
+ if (OpenArchiveFd(source_tf_fd_.get(), FLAGS_source_tf.c_str(), &handle, false)) {
+ LOG(ERROR) << "Could not open " << FLAGS_source_tf << " as a zip archive.";
+ return false;
+ }
+ source_tf_zip_.reset(handle);
+ return true;
+}
+
+bool PayloadConverter::ProcessPartition(const PartitionUpdate& update) {
+ auto partition_name = update.partition_name();
+ if (dap_.find(partition_name) == dap_.end()) {
+ // Skip non-DAP partitions.
+ return true;
+ }
+
+ auto path = out_dir_ + "/" + partition_name + ".cow";
+ unique_fd fd(open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ if (fd < 0) {
+ PLOG(ERROR) << "open failed: " << path;
+ return false;
+ }
+
+ CowOptions options;
+ options.block_size = kBlockSize;
+ options.compression = FLAGS_compression;
+
+ writer_ = std::make_unique<CowWriter>(options);
+ if (!writer_->Initialize(std::move(fd))) {
+ LOG(ERROR) << "Unable to initialize COW writer";
+ return false;
+ }
+
+ partition_name_ = partition_name;
+
+ for (const auto& op : update.operations()) {
+ if (!ProcessOperation(op)) {
+ return false;
+ }
+ }
+
+ if (!writer_->Finalize()) {
+ LOG(ERROR) << "Unable to finalize COW for " << partition_name;
+ return false;
+ }
+ return true;
+}
+
+bool PayloadConverter::ProcessOperation(const InstallOperation& op) {
+ switch (op.type()) {
+ case InstallOperation::SOURCE_COPY:
+ return ProcessCopy(op);
+ case InstallOperation::BROTLI_BSDIFF:
+ case InstallOperation::PUFFDIFF:
+ return ProcessDiff(op);
+ case InstallOperation::REPLACE:
+ case InstallOperation::REPLACE_XZ:
+ case InstallOperation::REPLACE_BZ:
+ return ProcessReplace(op);
+ case InstallOperation::ZERO:
+ return ProcessZero(op);
+ default:
+ LOG(ERROR) << "Unsupported op: " << (int)op.type();
+ return false;
+ }
+ return true;
+}
+
+bool PayloadConverter::ProcessZero(const InstallOperation& op) {
+ for (const auto& extent : op.dst_extents()) {
+ if (!writer_->AddZeroBlocks(extent.start_block(), extent.num_blocks())) {
+ LOG(ERROR) << "Could not add zero operation";
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename T>
+static uint64_t SizeOfAllExtents(const T& extents) {
+ uint64_t total = 0;
+ for (const auto& extent : extents) {
+ total += extent.num_blocks() * kBlockSize;
+ }
+ return total;
+}
+
+class PuffInputStream final : public puffin::StreamInterface {
+ public:
+ PuffInputStream(uint8_t* buffer, size_t length) : buffer_(buffer), length_(length), pos_(0) {}
+
+ bool GetSize(uint64_t* size) const override {
+ *size = length_;
+ return true;
+ }
+ bool GetOffset(uint64_t* offset) const override {
+ *offset = pos_;
+ return true;
+ }
+ bool Seek(uint64_t offset) override {
+ if (offset > length_) return false;
+ pos_ = offset;
+ return true;
+ }
+ bool Read(void* buffer, size_t length) override {
+ if (length_ - pos_ < length) return false;
+ memcpy(buffer, buffer_ + pos_, length);
+ pos_ += length;
+ return true;
+ }
+ bool Write(const void*, size_t) override { return false; }
+ bool Close() override { return true; }
+
+ private:
+ uint8_t* buffer_;
+ size_t length_;
+ size_t pos_;
+};
+
+class PuffOutputStream final : public puffin::StreamInterface {
+ public:
+ PuffOutputStream(std::vector<uint8_t>& stream) : stream_(stream), pos_(0) {}
+
+ bool GetSize(uint64_t* size) const override {
+ *size = stream_.size();
+ return true;
+ }
+ bool GetOffset(uint64_t* offset) const override {
+ *offset = pos_;
+ return true;
+ }
+ bool Seek(uint64_t offset) override {
+ if (offset > stream_.size()) {
+ return false;
+ }
+ pos_ = offset;
+ return true;
+ }
+ bool Read(void* buffer, size_t length) override {
+ if (stream_.size() - pos_ < length) {
+ return false;
+ }
+ memcpy(buffer, &stream_[0] + pos_, length);
+ pos_ += length;
+ return true;
+ }
+ bool Write(const void* buffer, size_t length) override {
+ auto remaining = stream_.size() - pos_;
+ if (remaining < length) {
+ stream_.resize(stream_.size() + (length - remaining));
+ }
+ memcpy(&stream_[0] + pos_, buffer, length);
+ pos_ += length;
+ return true;
+ }
+ bool Close() override { return true; }
+
+ private:
+ std::vector<uint8_t>& stream_;
+ size_t pos_;
+};
+
+bool PayloadConverter::ProcessDiff(const InstallOperation& op) {
+ auto source_image = OpenSourceImage();
+ if (source_image < 0) {
+ return false;
+ }
+
+ uint64_t src_length = SizeOfAllExtents(op.src_extents());
+ auto src = std::make_unique<uint8_t[]>(src_length);
+ size_t src_pos = 0;
+
+ // Read source bytes.
+ for (const auto& extent : op.src_extents()) {
+ uint64_t offset = extent.start_block() * kBlockSize;
+ if (lseek(source_image.get(), offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek source image failed";
+ return false;
+ }
+
+ uint64_t size = extent.num_blocks() * kBlockSize;
+ CHECK(src_length - src_pos >= size);
+ if (!android::base::ReadFully(source_image, src.get() + src_pos, size)) {
+ PLOG(ERROR) << "read source image failed";
+ return false;
+ }
+ src_pos += size;
+ }
+ CHECK(src_pos == src_length);
+
+ // Read patch bytes.
+ auto patch = std::make_unique<uint8_t[]>(op.data_length());
+ if (lseek(in_fd_.get(), payload_offset_ + op.data_offset(), SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek payload failed";
+ return false;
+ }
+ if (!android::base::ReadFully(in_fd_, patch.get(), op.data_length())) {
+ PLOG(ERROR) << "read payload failed";
+ return false;
+ }
+
+ std::vector<uint8_t> dest(SizeOfAllExtents(op.dst_extents()));
+
+ // Apply the diff.
+ if (op.type() == InstallOperation::BROTLI_BSDIFF) {
+ size_t dest_pos = 0;
+ auto sink = [&](const uint8_t* data, size_t length) -> size_t {
+ CHECK(dest.size() - dest_pos >= length);
+ memcpy(&dest[dest_pos], data, length);
+ dest_pos += length;
+ return length;
+ };
+ if (int rv = bsdiff::bspatch(src.get(), src_pos, patch.get(), op.data_length(), sink)) {
+ LOG(ERROR) << "bspatch failed, error code " << rv;
+ return false;
+ }
+ } else if (op.type() == InstallOperation::PUFFDIFF) {
+ auto src_stream = std::make_unique<PuffInputStream>(src.get(), src_length);
+ auto dest_stream = std::make_unique<PuffOutputStream>(dest);
+ bool ok = PuffPatch(std::move(src_stream), std::move(dest_stream), patch.get(),
+ op.data_length());
+ if (!ok) {
+ LOG(ERROR) << "puffdiff operation failed to apply";
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "unsupported diff operation: " << op.type();
+ return false;
+ }
+
+ // Write the final blocks to the COW.
+ size_t dest_pos = 0;
+ for (const auto& extent : op.dst_extents()) {
+ uint64_t size = extent.num_blocks() * kBlockSize;
+ CHECK(dest.size() - dest_pos >= size);
+
+ if (!writer_->AddRawBlocks(extent.start_block(), &dest[dest_pos], size)) {
+ return false;
+ }
+ dest_pos += size;
+ }
+ return true;
+}
+
+borrowed_fd PayloadConverter::OpenSourceImage() {
+ if (source_image_ >= 0) {
+ return source_image_;
+ }
+
+ unique_fd unzip_fd;
+
+ auto local_path = "IMAGES/" + partition_name_ + ".img";
+ if (source_tf_zip_) {
+ {
+ TemporaryFile tmp;
+ if (tmp.fd < 0) {
+ PLOG(ERROR) << "mkstemp failed";
+ return -1;
+ }
+ unzip_fd.reset(tmp.release());
+ }
+
+ ZipEntry64 entry;
+ if (FindEntry(source_tf_zip_.get(), local_path, &entry)) {
+ LOG(ERROR) << "not found in archive: " << local_path;
+ return -1;
+ }
+ if (ExtractEntryToFile(source_tf_zip_.get(), &entry, unzip_fd.get())) {
+ LOG(ERROR) << "could not extract " << local_path;
+ return -1;
+ }
+ if (lseek(unzip_fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return -1;
+ }
+ } else if (source_tf_fd_ >= 0) {
+ unzip_fd.reset(openat(source_tf_fd_.get(), local_path.c_str(), O_RDONLY));
+ if (unzip_fd < 0) {
+ PLOG(ERROR) << "open failed: " << FLAGS_source_tf << "/" << local_path;
+ return -1;
+ }
+ } else {
+ LOG(ERROR) << "No source target files package was specified; need -source_tf";
+ return -1;
+ }
+
+ std::unique_ptr<struct sparse_file, decltype(&sparse_file_destroy)> s(
+ sparse_file_import(unzip_fd.get(), false, false), &sparse_file_destroy);
+ if (s) {
+ TemporaryFile tmp;
+ if (tmp.fd < 0) {
+ PLOG(ERROR) << "mkstemp failed";
+ return -1;
+ }
+ if (sparse_file_write(s.get(), tmp.fd, false, false, false) < 0) {
+ LOG(ERROR) << "sparse_file_write failed";
+ return -1;
+ }
+ source_image_.reset(tmp.release());
+ } else {
+ source_image_ = std::move(unzip_fd);
+ }
+ return source_image_;
+}
+
+template <typename ContainerType>
+class ExtentIter final {
+ public:
+ ExtentIter(const ContainerType& container)
+ : iter_(container.cbegin()), end_(container.cend()), dst_index_(0) {}
+
+ bool GetNext(uint64_t* block) {
+ while (iter_ != end_) {
+ if (dst_index_ < iter_->num_blocks()) {
+ break;
+ }
+ iter_++;
+ dst_index_ = 0;
+ }
+ if (iter_ == end_) {
+ return false;
+ }
+ *block = iter_->start_block() + dst_index_;
+ dst_index_++;
+ return true;
+ }
+
+ private:
+ typename ContainerType::const_iterator iter_;
+ typename ContainerType::const_iterator end_;
+ uint64_t dst_index_;
+};
+
+bool PayloadConverter::ProcessCopy(const InstallOperation& op) {
+ ExtentIter dst_blocks(op.dst_extents());
+
+ for (const auto& extent : op.src_extents()) {
+ for (uint64_t i = 0; i < extent.num_blocks(); i++) {
+ uint64_t src_block = extent.start_block() + i;
+ uint64_t dst_block;
+ if (!dst_blocks.GetNext(&dst_block)) {
+ LOG(ERROR) << "SOURCE_COPY contained mismatching extents";
+ return false;
+ }
+ if (src_block == dst_block) continue;
+ if (!writer_->AddCopy(dst_block, src_block)) {
+ LOG(ERROR) << "Could not add copy operation";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool PayloadConverter::ProcessReplace(const InstallOperation& op) {
+ auto buffer_size = op.data_length();
+ auto buffer = std::make_unique<char[]>(buffer_size);
+ uint64_t offs = payload_offset_ + op.data_offset();
+ if (lseek(in_fd_.get(), offs, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek " << offs << " failed";
+ return false;
+ }
+ if (!android::base::ReadFully(in_fd_, buffer.get(), buffer_size)) {
+ PLOG(ERROR) << "read " << buffer_size << " bytes from offset " << offs << "failed";
+ return false;
+ }
+
+ uint64_t dst_size = 0;
+ for (const auto& extent : op.dst_extents()) {
+ dst_size += extent.num_blocks() * kBlockSize;
+ }
+
+ if (op.type() == InstallOperation::REPLACE_BZ) {
+ auto tmp = std::make_unique<char[]>(dst_size);
+
+ uint32_t actual_size;
+ if (dst_size > std::numeric_limits<typeof(actual_size)>::max()) {
+ LOG(ERROR) << "too many bytes to decompress: " << dst_size;
+ return false;
+ }
+ actual_size = static_cast<uint32_t>(dst_size);
+
+ auto rv = BZ2_bzBuffToBuffDecompress(tmp.get(), &actual_size, buffer.get(), buffer_size, 0,
+ 0);
+ if (rv) {
+ LOG(ERROR) << "bz2 decompress failed: " << rv;
+ return false;
+ }
+ if (actual_size != dst_size) {
+ LOG(ERROR) << "bz2 returned " << actual_size << " bytes, expected " << dst_size;
+ return false;
+ }
+ buffer = std::move(tmp);
+ buffer_size = dst_size;
+ } else if (op.type() == InstallOperation::REPLACE_XZ) {
+ constexpr uint32_t kXzMaxDictSize = 64 * 1024 * 1024;
+
+ if (dst_size > std::numeric_limits<size_t>::max()) {
+ LOG(ERROR) << "too many bytes to decompress: " << dst_size;
+ return false;
+ }
+
+ std::unique_ptr<struct xz_dec, decltype(&xz_dec_end)> s(
+ xz_dec_init(XZ_DYNALLOC, kXzMaxDictSize), xz_dec_end);
+ if (!s) {
+ LOG(ERROR) << "xz_dec_init failed";
+ return false;
+ }
+
+ auto tmp = std::make_unique<char[]>(dst_size);
+
+ struct xz_buf args;
+ args.in = reinterpret_cast<const uint8_t*>(buffer.get());
+ args.in_pos = 0;
+ args.in_size = buffer_size;
+ args.out = reinterpret_cast<uint8_t*>(tmp.get());
+ args.out_pos = 0;
+ args.out_size = dst_size;
+
+ auto rv = xz_dec_run(s.get(), &args);
+ if (rv != XZ_STREAM_END) {
+ LOG(ERROR) << "xz decompress failed: " << (int)rv;
+ return false;
+ }
+ buffer = std::move(tmp);
+ buffer_size = dst_size;
+ }
+
+ uint64_t buffer_pos = 0;
+ for (const auto& extent : op.dst_extents()) {
+ uint64_t extent_size = extent.num_blocks() * kBlockSize;
+ if (buffer_size - buffer_pos < extent_size) {
+ LOG(ERROR) << "replace op ran out of input buffer";
+ return false;
+ }
+ if (!writer_->AddRawBlocks(extent.start_block(), buffer.get() + buffer_pos, extent_size)) {
+ LOG(ERROR) << "failed to add raw blocks from replace op";
+ return false;
+ }
+ buffer_pos += extent_size;
+ }
+ return true;
+}
+
+bool PayloadConverter::OpenPayload() {
+ in_fd_.reset(open(in_file_.c_str(), O_RDONLY));
+ if (in_fd_ < 0) {
+ PLOG(ERROR) << "open " << in_file_;
+ return false;
+ }
+
+ char magic[4];
+ if (!android::base::ReadFully(in_fd_, magic, sizeof(magic))) {
+ PLOG(ERROR) << "read magic";
+ return false;
+ }
+ if (std::string(magic, sizeof(magic)) != "CrAU") {
+ LOG(ERROR) << "Invalid magic in " << in_file_;
+ return false;
+ }
+
+ uint64_t version;
+ uint64_t manifest_size;
+ uint32_t manifest_signature_size = 0;
+ if (!android::base::ReadFully(in_fd_, &version, sizeof(version))) {
+ PLOG(ERROR) << "read version";
+ return false;
+ }
+ version = ToLittleEndian(version);
+ if (version < 2) {
+ LOG(ERROR) << "Only payload version 2 or higher is supported.";
+ return false;
+ }
+
+ if (!android::base::ReadFully(in_fd_, &manifest_size, sizeof(manifest_size))) {
+ PLOG(ERROR) << "read manifest_size";
+ return false;
+ }
+ manifest_size = ToLittleEndian(manifest_size);
+ if (!android::base::ReadFully(in_fd_, &manifest_signature_size,
+ sizeof(manifest_signature_size))) {
+ PLOG(ERROR) << "read manifest_signature_size";
+ return false;
+ }
+ manifest_signature_size = ntohl(manifest_signature_size);
+
+ auto manifest = std::make_unique<uint8_t[]>(manifest_size);
+ if (!android::base::ReadFully(in_fd_, manifest.get(), manifest_size)) {
+ PLOG(ERROR) << "read manifest";
+ return false;
+ }
+
+ // Skip past manifest signature.
+ auto offs = lseek(in_fd_, manifest_signature_size, SEEK_CUR);
+ if (offs < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+ payload_offset_ = offs;
+
+ if (!manifest_.ParseFromArray(manifest.get(), manifest_size)) {
+ LOG(ERROR) << "could not parse manifest";
+ return false;
+ }
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, android::snapshot::MyLogger);
+ gflags::SetUsageMessage("Convert OTA payload to a Virtual A/B COW");
+ int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, false);
+
+ xz_crc32_init();
+
+ if (argc - arg_start != 2) {
+ std::cerr << "Usage: [options] <payload.bin> <out-dir>\n";
+ return 1;
+ }
+
+ android::snapshot::PayloadConverter pc(argv[arg_start], argv[arg_start + 1]);
+ return pc.Run() ? 0 : 1;
+}
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index adfb975..2970222 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -41,8 +41,14 @@
class PartitionCowCreatorTest : public ::testing::Test {
public:
- void SetUp() override { SnapshotTestPropertyFetcher::SetUp(); }
- void TearDown() override { SnapshotTestPropertyFetcher::TearDown(); }
+ void SetUp() override {
+ SKIP_IF_NON_VIRTUAL_AB();
+ SnapshotTestPropertyFetcher::SetUp();
+ }
+ void TearDown() override {
+ RETURN_IF_NON_VIRTUAL_AB();
+ SnapshotTestPropertyFetcher::TearDown();
+ }
};
TEST_F(PartitionCowCreatorTest, IntersectSelf) {
@@ -223,6 +229,8 @@
}
TEST(DmSnapshotInternals, CowSizeCalculator) {
+ SKIP_IF_NON_VIRTUAL_AB();
+
DmSnapCowSizeCalculator cc(512, 8);
unsigned long int b;
@@ -286,7 +294,9 @@
std::optional<InstallOperation> expected_output;
};
-class OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {};
+class OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {
+ void SetUp() override { SKIP_IF_NON_VIRTUAL_AB(); }
+};
TEST_P(OptimizeOperationTest, Test) {
InstallOperation actual_output;
EXPECT_EQ(GetParam().expected_output.has_value(),
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 7488bda..b49f99e 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -300,9 +300,9 @@
LOG(ERROR) << "SnapshotStatus has no name.";
return false;
}
- // Sanity check these sizes. Like liblp, we guarantee the partition size
- // is respected, which means it has to be sector-aligned. (This guarantee
- // is useful for locating avb footers correctly). The COW file size, however,
+ // Check these sizes. Like liblp, we guarantee the partition size is
+ // respected, which means it has to be sector-aligned. (This guarantee is
+ // useful for locating avb footers correctly). The COW file size, however,
// can be arbitrarily larger than specified, so we can safely round it up.
if (status->device_size() % kSectorSize != 0) {
LOG(ERROR) << "Snapshot " << status->name()
@@ -351,7 +351,6 @@
}
// The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
- // Sanity check this.
if (status.cow_file_size() % kSectorSize != 0) {
LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
<< status.cow_file_size();
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
index 5b145c3..aced3ed 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -141,7 +141,7 @@
const RecoveryCreateSnapshotDevicesArgs& args) {
std::unique_ptr<AutoDevice> device;
if (args.has_metadata_device_object()) {
- device = std::make_unique<DummyAutoDevice>(args.metadata_mounted());
+ device = std::make_unique<NoOpAutoDevice>(args.metadata_mounted());
}
return snapshot->RecoveryCreateSnapshotDevices(device);
}
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index fa327b8..5319e69 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -35,9 +35,9 @@
class AutoMemBasedDir;
class SnapshotFuzzDeviceInfo;
-class DummyAutoDevice : public AutoDevice {
+class NoOpAutoDevice : public AutoDevice {
public:
- DummyAutoDevice(bool mounted) : AutoDevice(mounted ? "dummy" : "") {}
+ NoOpAutoDevice(bool mounted) : AutoDevice(mounted ? "no_op" : "") {}
};
struct SnapshotTestModule {
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
index 051584c..17a0c96 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
@@ -39,6 +39,8 @@
SnapshotMetadataUpdater::SnapshotMetadataUpdater(MetadataBuilder* builder, uint32_t target_slot,
const DeltaArchiveManifest& manifest)
: builder_(builder), target_suffix_(SlotSuffixForSlotNumber(target_slot)) {
+ partial_update_ = manifest.partial_update();
+
if (!manifest.has_dynamic_partition_metadata()) {
return;
}
@@ -63,7 +65,6 @@
}
}
- partial_update_ = manifest.partial_update();
}
bool SnapshotMetadataUpdater::ShrinkPartitions() const {
@@ -173,9 +174,9 @@
if (iter != groups_.end()) {
continue;
}
- // Update package metadata doesn't have this group. Before deleting it, sanity check that it
- // doesn't have any partitions left. Update metadata shouldn't assign any partitions to this
- // group, so all partitions that originally belong to this group should be moved by
+ // Update package metadata doesn't have this group. Before deleting it, check that it
+ // doesn't have any partitions left. Update metadata shouldn't assign any partitions to
+ // this group, so all partitions that originally belong to this group should be moved by
// MovePartitionsToDefault at this point.
auto existing_partitions_in_group = builder_->ListPartitionsInGroup(existing_group_name);
if (!existing_partitions_in_group.empty()) {
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
index 5530e59..0a16c03 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
@@ -43,11 +43,11 @@
class SnapshotMetadataUpdaterTest : public ::testing::TestWithParam<uint32_t> {
public:
- SnapshotMetadataUpdaterTest() {
- is_virtual_ab_ = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
- }
+ SnapshotMetadataUpdaterTest() = default;
void SetUp() override {
+ SKIP_IF_NON_VIRTUAL_AB();
+
target_slot_ = GetParam();
target_suffix_ = SlotSuffixForSlotNumber(target_slot_);
SnapshotTestPropertyFetcher::SetUp(SlotSuffixForSlotNumber(1 - target_slot_));
@@ -68,7 +68,11 @@
ASSERT_TRUE(FillFakeMetadata(builder_.get(), manifest_, target_suffix_));
}
- void TearDown() override { SnapshotTestPropertyFetcher::TearDown(); }
+ void TearDown() override {
+ RETURN_IF_NON_VIRTUAL_AB();
+
+ SnapshotTestPropertyFetcher::TearDown();
+ }
// Append suffix to name.
std::string T(std::string_view name) { return std::string(name) + target_suffix_; }
@@ -127,7 +131,6 @@
<< ".";
}
- bool is_virtual_ab_;
std::unique_ptr<MetadataBuilder> builder_;
uint32_t target_slot_;
std::string target_suffix_;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 2bd0135..6ff935b 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -83,9 +83,7 @@
class SnapshotTest : public ::testing::Test {
public:
- SnapshotTest() : dm_(DeviceMapper::Instance()) {
- is_virtual_ab_ = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
- }
+ SnapshotTest() : dm_(DeviceMapper::Instance()) {}
// This is exposed for main.
void Cleanup() {
@@ -95,7 +93,7 @@
protected:
void SetUp() override {
- if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+ SKIP_IF_NON_VIRTUAL_AB();
SnapshotTestPropertyFetcher::SetUp();
InitializeState();
@@ -106,7 +104,7 @@
}
void TearDown() override {
- if (!is_virtual_ab_) return;
+ RETURN_IF_NON_VIRTUAL_AB();
lock_ = nullptr;
@@ -341,7 +339,6 @@
}
static constexpr std::chrono::milliseconds snapshot_timeout_ = 5s;
- bool is_virtual_ab_;
DeviceMapper& dm_;
std::unique_ptr<SnapshotManager::LockedFile> lock_;
android::fiemap::IImageManager* image_manager_ = nullptr;
@@ -722,11 +719,13 @@
class LockTest : public ::testing::Test {
public:
void SetUp() {
+ SKIP_IF_NON_VIRTUAL_AB();
first_consumer.StartHandleRequestsInBackground();
second_consumer.StartHandleRequestsInBackground();
}
void TearDown() {
+ RETURN_IF_NON_VIRTUAL_AB();
EXPECT_TRUE(first_consumer.MakeRequest(Request::EXIT));
EXPECT_TRUE(second_consumer.MakeRequest(Request::EXIT));
}
@@ -770,7 +769,7 @@
class SnapshotUpdateTest : public SnapshotTest {
public:
void SetUp() override {
- if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+ SKIP_IF_NON_VIRTUAL_AB();
SnapshotTest::SetUp();
Cleanup();
@@ -832,7 +831,7 @@
}
}
void TearDown() override {
- if (!is_virtual_ab_) return;
+ RETURN_IF_NON_VIRTUAL_AB();
Cleanup();
SnapshotTest::TearDown();
@@ -1365,13 +1364,17 @@
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
}
-class MetadataMountedTest : public SnapshotUpdateTest {
+class MetadataMountedTest : public ::testing::Test {
public:
+ // This is so main() can instantiate this to invoke Cleanup.
+ virtual void TestBody() override {}
void SetUp() override {
+ SKIP_IF_NON_VIRTUAL_AB();
metadata_dir_ = test_device->GetMetadataDir();
ASSERT_TRUE(ReadDefaultFstab(&fstab_));
}
void TearDown() override {
+ RETURN_IF_NON_VIRTUAL_AB();
SetUp();
// Remount /metadata
test_device->set_recovery(false);
@@ -1702,8 +1705,6 @@
};
TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
- if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
-
// OTA client blindly unmaps all partitions that are possibly mapped.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
@@ -1803,14 +1804,13 @@
class ImageManagerTest : public SnapshotTest, public WithParamInterface<uint64_t> {
protected:
void SetUp() override {
- if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
-
+ SKIP_IF_NON_VIRTUAL_AB();
SnapshotTest::SetUp();
userdata_ = std::make_unique<LowSpaceUserdata>();
ASSERT_TRUE(userdata_->Init(GetParam()));
}
void TearDown() override {
- if (!is_virtual_ab_) return;
+ RETURN_IF_NON_VIRTUAL_AB();
return; // BUG(149738928)
EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
@@ -1852,11 +1852,6 @@
INSTANTIATE_TEST_SUITE_P(ImageManagerTest, ImageManagerTest, ValuesIn(ImageManagerTestParams()));
-} // namespace snapshot
-} // namespace android
-
-using namespace android::snapshot;
-
bool Mkdir(const std::string& path) {
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
@@ -1865,8 +1860,21 @@
return true;
}
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
+class SnapshotTestEnvironment : public ::testing::Environment {
+ public:
+ ~SnapshotTestEnvironment() override {}
+ void SetUp() override;
+ void TearDown() override;
+
+ private:
+ std::unique_ptr<IImageManager> super_images_;
+};
+
+void SnapshotTestEnvironment::SetUp() {
+ // b/163082876: GTEST_SKIP in Environment will make atest report incorrect results. Until
+ // that is fixed, don't call GTEST_SKIP here, but instead call GTEST_SKIP in individual test
+ // suites.
+ RETURN_IF_NON_VIRTUAL_AB_MSG("Virtual A/B is not enabled, skipping global setup.\n");
std::vector<std::string> paths = {
// clang-format off
@@ -1879,18 +1887,13 @@
// clang-format on
};
for (const auto& path : paths) {
- if (!Mkdir(path)) {
- return 1;
- }
+ ASSERT_TRUE(Mkdir(path));
}
// Create this once, otherwise, gsid will start/stop between each test.
test_device = new TestDeviceInfo();
sm = SnapshotManager::New(test_device);
- if (!sm) {
- std::cerr << "Could not create snapshot manager\n";
- return 1;
- }
+ ASSERT_NE(nullptr, sm) << "Could not create snapshot manager";
// Clean up previous run.
MetadataMountedTest().TearDown();
@@ -1898,31 +1901,35 @@
SnapshotTest().Cleanup();
// Use a separate image manager for our fake super partition.
- auto super_images = IImageManager::Open("ota/test/super", 10s);
- if (!super_images) {
- std::cerr << "Could not create image manager\n";
- return 1;
- }
+ super_images_ = IImageManager::Open("ota/test/super", 10s);
+ ASSERT_NE(nullptr, super_images_) << "Could not create image manager";
// Clean up any old copy.
- DeleteBackingImage(super_images.get(), "fake-super");
+ DeleteBackingImage(super_images_.get(), "fake-super");
// Create and map the fake super partition.
static constexpr int kImageFlags =
IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
- if (!super_images->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) {
- std::cerr << "Could not create fake super partition\n";
- return 1;
- }
- if (!super_images->MapImageDevice("fake-super", 10s, &fake_super)) {
- std::cerr << "Could not map fake super partition\n";
- return 1;
- }
+ ASSERT_TRUE(super_images_->CreateBackingImage("fake-super", kSuperSize, kImageFlags))
+ << "Could not create fake super partition";
+
+ ASSERT_TRUE(super_images_->MapImageDevice("fake-super", 10s, &fake_super))
+ << "Could not map fake super partition";
test_device->set_fake_super(fake_super);
+}
- auto result = RUN_ALL_TESTS();
+void SnapshotTestEnvironment::TearDown() {
+ RETURN_IF_NON_VIRTUAL_AB();
+ if (super_images_ != nullptr) {
+ DeleteBackingImage(super_images_.get(), "fake-super");
+ }
+}
- DeleteBackingImage(super_images.get(), "fake-super");
+} // namespace snapshot
+} // namespace android
- return result;
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(new ::android::snapshot::SnapshotTestEnvironment());
+ return RUN_ALL_TESTS();
}
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
new file mode 100644
index 0000000..a6ff4fd
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 <linux/types.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <libdm/dm.h>
+
+using android::base::unique_fd;
+
+#define DM_USER_MAP_READ 0
+#define DM_USER_MAP_WRITE 1
+
+struct dm_user_message {
+ __u64 seq;
+ __u64 type;
+ __u64 flags;
+ __u64 sector;
+ __u64 len;
+ __u8 buf[];
+};
+
+using namespace android::dm;
+
+static int daemon_main(const std::string& device) {
+ unique_fd block_fd(open(device.c_str(), O_RDWR));
+ if (block_fd < 0) {
+ PLOG(ERROR) << "Unable to open " << device;
+ return 1;
+ }
+
+ unique_fd ctrl_fd(open("/dev/dm-user", O_RDWR));
+ if (ctrl_fd < 0) {
+ PLOG(ERROR) << "Unable to open /dev/dm-user";
+ return 1;
+ }
+
+ size_t buf_size = 1UL << 16;
+ auto buf = std::make_unique<char>(buf_size);
+
+ /* Just keeps pumping messages between userspace and the kernel. We won't
+ * actually be doing anything, but the sequence numbers line up so it'll at
+ * least make forward progress. */
+ while (true) {
+ struct dm_user_message* msg = (struct dm_user_message*)buf.get();
+
+ memset(buf.get(), 0, buf_size);
+
+ ssize_t readed = read(ctrl_fd.get(), buf.get(), buf_size);
+ if (readed < 0) {
+ PLOG(ERROR) << "Control read failed, trying with more space";
+ buf_size *= 2;
+ buf = std::make_unique<char>(buf_size);
+ continue;
+ }
+
+ LOG(DEBUG) << android::base::StringPrintf("read() from dm-user returned %d bytes:",
+ (int)readed);
+ LOG(DEBUG) << android::base::StringPrintf(" msg->seq: 0x%016llx", msg->seq);
+ LOG(DEBUG) << android::base::StringPrintf(" msg->type: 0x%016llx", msg->type);
+ LOG(DEBUG) << android::base::StringPrintf(" msg->flags: 0x%016llx", msg->flags);
+ LOG(DEBUG) << android::base::StringPrintf(" msg->sector: 0x%016llx", msg->sector);
+ LOG(DEBUG) << android::base::StringPrintf(" msg->len: 0x%016llx", msg->len);
+
+ switch (msg->type) {
+ case DM_USER_MAP_READ: {
+ LOG(DEBUG) << android::base::StringPrintf(
+ "Responding to read of sector %lld with %lld bytes data", msg->sector,
+ msg->len);
+
+ if ((sizeof(*msg) + msg->len) > buf_size) {
+ auto old_buf = std::move(buf);
+ buf_size = sizeof(*msg) + msg->len;
+ buf = std::make_unique<char>(buf_size);
+ memcpy(buf.get(), old_buf.get(), sizeof(*msg));
+ msg = (struct dm_user_message*)buf.get();
+ }
+
+ if (lseek(block_fd.get(), msg->sector * 512, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed: " << device;
+ return 7;
+ }
+ if (!android::base::ReadFully(block_fd.get(), msg->buf, msg->len)) {
+ PLOG(ERROR) << "read failed: " << device;
+ return 7;
+ }
+
+ if (!android::base::WriteFully(ctrl_fd.get(), buf.get(), sizeof(*msg) + msg->len)) {
+ PLOG(ERROR) << "write control failed";
+ return 3;
+ }
+ break;
+ }
+
+ case DM_USER_MAP_WRITE:
+ abort();
+ break;
+ }
+
+ LOG(DEBUG) << "read() finished, next message";
+ }
+
+ return 0;
+}
+
+int main([[maybe_unused]] int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ daemon_main(argv[1]);
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index de3d912..b07bf91 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -18,6 +18,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
@@ -241,5 +242,9 @@
return bsize_;
}
+bool IsVirtualAbEnabled() {
+ return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 14d46b3..b3de9c4 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -240,3 +240,47 @@
defaults: ["charger_defaults"],
srcs: ["charger_test.cpp"],
}
+
+cc_test {
+ name: "libhealthd_charger_test",
+ defaults: ["charger_defaults"],
+ srcs: [
+ "AnimationParser_test.cpp",
+ "healthd_mode_charger_test.cpp"
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+ test_suites: [
+ "general-tests",
+ "device-tests",
+ ],
+ data: [
+ ":libhealthd_charger_test_data",
+ ],
+ require_root: true,
+}
+
+// /system/etc/res/images/charger/battery_fail.png
+prebuilt_etc {
+ name: "system_core_charger_res_images_battery_fail.png",
+ src: "images/battery_fail.png",
+ relative_install_path: "res/images/charger",
+ filename: "battery_fail.png",
+}
+
+// /system/etc/res/images/charger/battery_scale.png
+prebuilt_etc {
+ name: "system_core_charger_res_images_battery_scale.png",
+ src: "images/battery_scale.png",
+ relative_install_path: "res/images/charger",
+ filename: "battery_scale.png",
+}
+
+phony {
+ name: "charger_res_images",
+ required: [
+ "system_core_charger_res_images_battery_fail.png",
+ "system_core_charger_res_images_battery_scale.png",
+ ],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
deleted file mode 100644
index 4b09cf8..0000000
--- a/healthd/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
-LOCAL_CHARGER_NO_UI := true
-endif
-
-### charger_res_images ###
-ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-define _add-charger-image
-include $$(CLEAR_VARS)
-LOCAL_MODULE := system_core_charger_res_images_$(notdir $(1))
-LOCAL_MODULE_STEM := $(notdir $(1))
-_img_modules += $$(LOCAL_MODULE)
-LOCAL_SRC_FILES := $1
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger
-include $$(BUILD_PREBUILT)
-endef
-
-_img_modules :=
-_images :=
-$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
- $(eval $(call _add-charger-image,$(_img))))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := charger_res_images
-LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := $(_img_modules)
-include $(BUILD_PHONY_PACKAGE)
-
-_add-charger-image :=
-_img_modules :=
-endif # LOCAL_CHARGER_NO_UI
diff --git a/healthd/AnimationParser.cpp b/healthd/AnimationParser.cpp
index fde3b95..6b08570 100644
--- a/healthd/AnimationParser.cpp
+++ b/healthd/AnimationParser.cpp
@@ -37,8 +37,8 @@
return true;
}
-bool remove_prefix(const std::string& line, const char* prefix, const char** rest) {
- const char* str = line.c_str();
+bool remove_prefix(std::string_view line, const char* prefix, const char** rest) {
+ const char* str = line.data();
int start;
char c;
diff --git a/healthd/AnimationParser.h b/healthd/AnimationParser.h
index bc00845..f55b563 100644
--- a/healthd/AnimationParser.h
+++ b/healthd/AnimationParser.h
@@ -17,6 +17,8 @@
#ifndef HEALTHD_ANIMATION_PARSER_H
#define HEALTHD_ANIMATION_PARSER_H
+#include <string_view>
+
#include "animation.h"
namespace android {
@@ -24,7 +26,7 @@
bool parse_animation_desc(const std::string& content, animation* anim);
bool can_ignore_line(const char* str);
-bool remove_prefix(const std::string& str, const char* prefix, const char** rest);
+bool remove_prefix(std::string_view str, const char* prefix, const char** rest);
bool parse_text_field(const char* in, animation::text_field* field);
} // namespace android
diff --git a/healthd/tests/AnimationParser_test.cpp b/healthd/AnimationParser_test.cpp
similarity index 100%
rename from healthd/tests/AnimationParser_test.cpp
rename to healthd/AnimationParser_test.cpp
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 599f500..fd810cb 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -79,7 +79,7 @@
// HIDL enum values are zero initialized, so they need to be initialized
// properly.
- health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
+ health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED;
health_info_2_1->batteryChargeTimeToFullNowSeconds =
(int64_t)Constants::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
auto* props = &health_info_2_1->legacy.legacy;
diff --git a/healthd/TEST_MAPPING b/healthd/TEST_MAPPING
new file mode 100644
index 0000000..5893d10
--- /dev/null
+++ b/healthd/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libhealthd_charger_test"
+ }
+ ]
+}
diff --git a/healthd/animation.h b/healthd/animation.h
index d02d7a7..c2d5f1c 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -18,6 +18,7 @@
#define HEALTHD_ANIMATION_H
#include <inttypes.h>
+
#include <string>
class GRSurface;
@@ -52,20 +53,11 @@
// - When treating paths as relative paths, it adds ".png" suffix.
// - When treating paths as absolute paths, it doesn't add the suffix. Hence, the suffix
// is added here.
- void set_resource_root(const std::string& root) {
- if (!animation_file.empty()) {
- animation_file = root + animation_file + ".png";
- }
- if (!fail_file.empty()) {
- fail_file = root + fail_file + ".png";
- }
- if (!text_clock.font_file.empty()) {
- text_clock.font_file = root + text_clock.font_file + ".png";
- }
- if (!text_percent.font_file.empty()) {
- text_percent.font_file = root + text_percent.font_file + ".png";
- }
- }
+ // If |backup_root| is provided, additionally check if file under |root| is accessbile or not.
+ // If not accessbile, use |backup_root| instead.
+ // Require that |root| starts and ends with "/". If |backup_root| is provided, require that
+ // |backup_root| starts and ends with "/".
+ void set_resource_root(const std::string& root, const std::string& backup_root = "");
std::string animation_file;
std::string fail_file;
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 386ba1a..e95efc0 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -33,7 +33,9 @@
#include <optional>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/strings.h>
#include <linux/netlink.h>
#include <sys/socket.h>
@@ -58,6 +60,7 @@
#include <health2impl/Health.h>
#include <healthd/healthd.h>
+using std::string_literals::operator""s;
using namespace android;
using android::hardware::Return;
using android::hardware::health::GetHealthServiceOrDefault;
@@ -103,7 +106,13 @@
namespace android {
-// Resources in /product/etc/res overrides resources in /res.
+// Legacy animation resources are loaded from this directory.
+static constexpr const char* legacy_animation_root = "/res/images/";
+
+// Built-in animation resources are loaded from this directory.
+static constexpr const char* system_animation_root = "/system/etc/res/images/";
+
+// Resources in /product/etc/res overrides resources in /res and /system/etc/res.
// If the device is using the Generic System Image (GSI), resources may exist in
// both paths.
static constexpr const char* product_animation_desc_path =
@@ -625,6 +634,12 @@
batt_anim_.set_resource_root(product_animation_root);
} else if (base::ReadFileToString(animation_desc_path, &content)) {
parse_success = parse_animation_desc(content, &batt_anim_);
+ // Fallback resources always exist in system_animation_root. On legacy devices with an old
+ // ramdisk image, resources may be overridden under root. For example,
+ // /res/images/charger/battery_fail.png may not be the same as
+ // system/core/healthd/images/battery_fail.png in the source tree, but is a device-specific
+ // image. Hence, load from /res, and fall back to /system/etc/res.
+ batt_anim_.set_resource_root(legacy_animation_root, system_animation_root);
} else {
LOGW("Could not open animation description at %s\n", animation_desc_path);
parse_success = false;
@@ -633,13 +648,13 @@
if (!parse_success) {
LOGW("Could not parse animation description. Using default animation.\n");
batt_anim_ = BASE_ANIMATION;
- batt_anim_.animation_file.assign("charger/battery_scale");
+ batt_anim_.animation_file.assign(system_animation_root + "charger/battery_scale.png"s);
InitDefaultAnimationFrames();
batt_anim_.frames = owned_frames_.data();
batt_anim_.num_frames = owned_frames_.size();
}
if (batt_anim_.fail_file.empty()) {
- batt_anim_.fail_file.assign("charger/battery_fail");
+ batt_anim_.fail_file.assign(system_animation_root + "charger/battery_fail.png"s);
}
LOGV("Animation Description:\n");
@@ -678,10 +693,11 @@
InitAnimation();
- ret = res_create_display_surface(batt_anim_.fail_file.c_str(), &surf_unknown_);
+ ret = CreateDisplaySurface(batt_anim_.fail_file, &surf_unknown_);
if (ret < 0) {
LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
- ret = res_create_display_surface("charger/battery_fail", &surf_unknown_);
+ ret = CreateDisplaySurface((system_animation_root + "charger/battery_fail.png"s).c_str(),
+ &surf_unknown_);
if (ret < 0) {
LOGE("Cannot load built in battery_fail image\n");
surf_unknown_ = NULL;
@@ -692,8 +708,8 @@
int scale_count;
int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text
// chunk). We are using hard-coded frame.disp_time instead.
- ret = res_create_multi_display_surface(batt_anim_.animation_file.c_str(), &scale_count,
- &scale_fps, &scale_frames);
+ ret = CreateMultiDisplaySurface(batt_anim_.animation_file, &scale_count, &scale_fps,
+ &scale_frames);
if (ret < 0) {
LOGE("Cannot load battery_scale image\n");
batt_anim_.num_frames = 0;
@@ -722,6 +738,43 @@
boot_min_cap_ = config->boot_min_cap;
}
+int Charger::CreateDisplaySurface(const std::string& name, GRSurface** surface) {
+ return res_create_display_surface(name.c_str(), surface);
+}
+
+int Charger::CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,
+ GRSurface*** surface) {
+ return res_create_multi_display_surface(name.c_str(), frames, fps, surface);
+}
+
+void set_resource_root_for(const std::string& root, const std::string& backup_root,
+ std::string* value) {
+ if (value->empty()) {
+ return;
+ }
+
+ std::string new_value = root + *value + ".png";
+ // If |backup_root| is provided, additionally check whether the file under |root| is
+ // accessible or not. If not accessible, fallback to file under |backup_root|.
+ if (!backup_root.empty() && access(new_value.data(), F_OK) == -1) {
+ new_value = backup_root + *value + ".png";
+ }
+
+ *value = new_value;
+}
+
+void animation::set_resource_root(const std::string& root, const std::string& backup_root) {
+ CHECK(android::base::StartsWith(root, "/") && android::base::EndsWith(root, "/"))
+ << "animation root " << root << " must start and end with /";
+ CHECK(backup_root.empty() || (android::base::StartsWith(backup_root, "/") &&
+ android::base::EndsWith(backup_root, "/")))
+ << "animation backup root " << backup_root << " must start and end with /";
+ set_resource_root_for(root, backup_root, &animation_file);
+ set_resource_root_for(root, backup_root, &fail_file);
+ set_resource_root_for(root, backup_root, &text_clock.font_file);
+ set_resource_root_for(root, backup_root, &text_percent.font_file);
+}
+
} // namespace android
int healthd_charger_main(int argc, char** argv) {
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
index 6e569ee..6f9ae8c 100644
--- a/healthd/healthd_mode_charger.h
+++ b/healthd/healthd_mode_charger.h
@@ -53,6 +53,11 @@
// HalHealthLoop overrides
void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;
+ // Allowed to be mocked for testing.
+ virtual int CreateDisplaySurface(const std::string& name, GRSurface** surface);
+ virtual int CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,
+ GRSurface*** surface);
+
private:
void InitDefaultAnimationFrames();
void UpdateScreenState(int64_t now);
diff --git a/healthd/healthd_mode_charger_test.cpp b/healthd/healthd_mode_charger_test.cpp
new file mode 100644
index 0000000..f444f66
--- /dev/null
+++ b/healthd/healthd_mode_charger_test.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 <sysexits.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <health/utils.h>
+
+#include "healthd_mode_charger.h"
+
+using android::hardware::Return;
+using android::hardware::health::InitHealthdConfig;
+using std::string_literals::operator""s;
+using testing::_;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::StrEq;
+using testing::Test;
+
+namespace android {
+
+// A replacement to ASSERT_* to be used in a forked process. When the condition is not met,
+// print a gtest message, then exit abnormally.
+class ChildAssertHelper : public std::stringstream {
+ public:
+ ChildAssertHelper(bool res, const char* expr, const char* file, int line) : res_(res) {
+ (*this) << file << ":" << line << ": `" << expr << "` evaluates to false\n";
+ }
+ ~ChildAssertHelper() {
+ EXPECT_TRUE(res_) << str();
+ if (!res_) exit(EX_SOFTWARE);
+ }
+
+ private:
+ bool res_;
+ DISALLOW_COPY_AND_ASSIGN(ChildAssertHelper);
+};
+#define CHILD_ASSERT_TRUE(expr) ChildAssertHelper(expr, #expr, __FILE__, __LINE__)
+
+// Run |test_body| in a chroot jail in a forked process. |subdir| is a sub-directory in testdata.
+// Within |test_body|,
+// - non-fatal errors may be reported using EXPECT_* macro as usual.
+// - fatal errors must be reported using CHILD_ASSERT_TRUE macro. ASSERT_* must not be used.
+void ForkTest(const std::string& subdir, const std::function<void(void)>& test_body) {
+ pid_t pid = fork();
+ ASSERT_GE(pid, 0) << "Fork fails: " << strerror(errno);
+ if (pid == 0) {
+ // child
+ CHILD_ASSERT_TRUE(
+ chroot((android::base::GetExecutableDirectory() + "/" + subdir).c_str()) != -1)
+ << "Failed to chroot to " << subdir << ": " << strerror(errno);
+ test_body();
+ // EXPECT_* macros may set the HasFailure bit without calling exit(). Set exit status
+ // accordingly.
+ exit(::testing::Test::HasFailure() ? EX_SOFTWARE : EX_OK);
+ }
+ // parent
+ int status;
+ ASSERT_NE(-1, waitpid(pid, &status, 0)) << "waitpid() fails: " << strerror(errno);
+ ASSERT_TRUE(WIFEXITED(status)) << "Test fails, waitpid() returns " << status;
+ ASSERT_EQ(EX_OK, WEXITSTATUS(status)) << "Test fails, child process returns " << status;
+}
+
+class MockHealth : public android::hardware::health::V2_1::IHealth {
+ MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, registerCallback,
+ (const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));
+ MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, unregisterCallback,
+ (const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));
+ MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, update, ());
+ MOCK_METHOD(Return<void>, getChargeCounter, (getChargeCounter_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getCurrentNow, (getCurrentNow_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getCurrentAverage, (getCurrentAverage_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getCapacity, (getCapacity_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getEnergyCounter, (getEnergyCounter_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getChargeStatus, (getChargeStatus_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getStorageInfo, (getStorageInfo_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getDiskStats, (getDiskStats_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getHealthInfo, (getHealthInfo_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getHealthConfig, (getHealthConfig_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getHealthInfo_2_1, (getHealthInfo_2_1_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, shouldKeepScreenOn, (shouldKeepScreenOn_cb _hidl_cb));
+};
+
+class TestCharger : public Charger {
+ public:
+ // Inherit constructor.
+ using Charger::Charger;
+ // Expose protected functions to be used in tests.
+ void Init(struct healthd_config* config) override { Charger::Init(config); }
+ MOCK_METHOD(int, CreateDisplaySurface, (const std::string& name, GRSurface** surface));
+ MOCK_METHOD(int, CreateMultiDisplaySurface,
+ (const std::string& name, int* frames, int* fps, GRSurface*** surface));
+};
+
+// Intentionally leak TestCharger instance to avoid calling ~HealthLoop() because ~HealthLoop()
+// should never be called. But still verify expected calls upon destruction.
+class VerifiedTestCharger {
+ public:
+ VerifiedTestCharger(TestCharger* charger) : charger_(charger) {
+ testing::Mock::AllowLeak(charger_);
+ }
+ TestCharger& operator*() { return *charger_; }
+ TestCharger* operator->() { return charger_; }
+ ~VerifiedTestCharger() { testing::Mock::VerifyAndClearExpectations(charger_); }
+
+ private:
+ TestCharger* charger_;
+};
+
+// Do not use SetUp and TearDown of a test suite, as they will be invoked in the parent process, not
+// the child process. In particular, if the test suite contains mocks, they will not be verified in
+// the child process. Instead, create mocks within closures in each tests.
+void ExpectChargerResAt(const std::string& root) {
+ sp<NiceMock<MockHealth>> health(new NiceMock<MockHealth>());
+ VerifiedTestCharger charger(new NiceMock<TestCharger>(health));
+
+ // Only one frame in all testdata/**/animation.txt
+ GRSurface* multi[] = {nullptr};
+
+ EXPECT_CALL(*charger, CreateDisplaySurface(StrEq(root + "charger/battery_fail.png"), _))
+ .WillRepeatedly(Invoke([](const auto&, GRSurface** surface) {
+ *surface = nullptr;
+ return 0;
+ }));
+ EXPECT_CALL(*charger,
+ CreateMultiDisplaySurface(StrEq(root + "charger/battery_scale.png"), _, _, _))
+ .WillRepeatedly(Invoke([&](const auto&, int* frames, int* fps, GRSurface*** surface) {
+ *frames = arraysize(multi);
+ *fps = 60; // Unused fps value
+ *surface = multi;
+ return 0;
+ }));
+ struct healthd_config healthd_config;
+ InitHealthdConfig(&healthd_config);
+ charger->Init(&healthd_config);
+};
+
+// Test that if resources does not exist in /res or in /product/etc/res, load from /system.
+TEST(ChargerLoadAnimationRes, Empty) {
+ ForkTest("empty", std::bind(&ExpectChargerResAt, "/system/etc/res/images/"));
+}
+
+// Test loading everything from /res
+TEST(ChargerLoadAnimationRes, Legacy) {
+ ForkTest("legacy", std::bind(&ExpectChargerResAt, "/res/images/"));
+}
+
+// Test loading animation text from /res but images from /system if images does not exist under
+// /res.
+TEST(ChargerLoadAnimationRes, LegacyTextSystemImages) {
+ ForkTest("legacy_text_system_images",
+ std::bind(&ExpectChargerResAt, "/system/etc/res/images/"));
+}
+
+// Test loading everything from /product
+TEST(ChargerLoadAnimationRes, Product) {
+ ForkTest("product", std::bind(&ExpectChargerResAt, "/product/etc/res/images/"));
+}
+
+} // namespace android
diff --git a/healthd/testdata/Android.bp b/healthd/testdata/Android.bp
new file mode 100644
index 0000000..110c79a
--- /dev/null
+++ b/healthd/testdata/Android.bp
@@ -0,0 +1,20 @@
+//
+// 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.
+//
+
+filegroup {
+ name: "libhealthd_charger_test_data",
+ srcs: ["**/*.*"],
+}
diff --git a/healthd/testdata/empty/ensure_directory_creation.txt b/healthd/testdata/empty/ensure_directory_creation.txt
new file mode 100644
index 0000000..36ceff4
--- /dev/null
+++ b/healthd/testdata/empty/ensure_directory_creation.txt
@@ -0,0 +1 @@
+File is placed to ensure directory is created on the device.
diff --git a/healthd/testdata/legacy/res/images/charger/battery_fail.png b/healthd/testdata/legacy/res/images/charger/battery_fail.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/healthd/testdata/legacy/res/images/charger/battery_fail.png
diff --git a/healthd/testdata/legacy/res/images/charger/battery_scale.png b/healthd/testdata/legacy/res/images/charger/battery_scale.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/healthd/testdata/legacy/res/images/charger/battery_scale.png
diff --git a/healthd/testdata/legacy/res/values/charger/animation.txt b/healthd/testdata/legacy/res/values/charger/animation.txt
new file mode 100644
index 0000000..0753336
--- /dev/null
+++ b/healthd/testdata/legacy/res/values/charger/animation.txt
@@ -0,0 +1,9 @@
+# Sample Animation file for testing.
+
+# animation: num_cycles, first_frame_repeats, animation_file
+animation: 2 1 charger/battery_scale
+
+fail: charger/battery_fail
+
+# frame: disp_time min_level max_level
+frame: 15 0 100
diff --git a/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt b/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt
new file mode 100644
index 0000000..0753336
--- /dev/null
+++ b/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt
@@ -0,0 +1,9 @@
+# Sample Animation file for testing.
+
+# animation: num_cycles, first_frame_repeats, animation_file
+animation: 2 1 charger/battery_scale
+
+fail: charger/battery_fail
+
+# frame: disp_time min_level max_level
+frame: 15 0 100
diff --git a/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png b/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png
diff --git a/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png b/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png
diff --git a/healthd/testdata/product/product/etc/res/values/charger/animation.txt b/healthd/testdata/product/product/etc/res/values/charger/animation.txt
new file mode 100644
index 0000000..0753336
--- /dev/null
+++ b/healthd/testdata/product/product/etc/res/values/charger/animation.txt
@@ -0,0 +1,9 @@
+# Sample Animation file for testing.
+
+# animation: num_cycles, first_frame_repeats, animation_file
+animation: 2 1 charger/battery_scale
+
+fail: charger/battery_fail
+
+# frame: disp_time min_level max_level
+frame: 15 0 100
diff --git a/healthd/tests/Android.mk b/healthd/tests/Android.mk
deleted file mode 100644
index 87e8862..0000000
--- a/healthd/tests/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2016 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- AnimationParser_test.cpp \
-
-LOCAL_MODULE := healthd_test
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_LIBRARIES := \
- libhealthd_internal \
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libbase \
- libcutils \
-
-include $(BUILD_NATIVE_TEST)
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 17f509a..6f22ab7 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -32,4 +32,7 @@
<option name="module-name" value="CtsInitTestCases" />
<option name="runtime-hint" value="65s" />
</test>
+ <!-- Controller that will skip the module if a native bridge situation is detected -->
+ <!-- For example: module wants to run arm32 and device is x86 -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" />
</configuration>
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index b423f86..8db9793 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -37,7 +37,15 @@
}
bool BlockDevInitializer::InitDeviceMapper() {
- const std::string dm_path = "/devices/virtual/misc/device-mapper";
+ return InitMiscDevice("device-mapper");
+}
+
+bool BlockDevInitializer::InitDmUser() {
+ return InitMiscDevice("dm-user");
+}
+
+bool BlockDevInitializer::InitMiscDevice(const std::string& name) {
+ const std::string dm_path = "/devices/virtual/misc/" + name;
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
if (uevent.path == dm_path) {
@@ -49,13 +57,13 @@
};
uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
if (!found) {
- LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+ LOG(INFO) << name << " device not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(dm_callback, 10s);
- LOG(INFO) << "Wait for device-mapper returned after " << t;
+ LOG(INFO) << "Wait for " << name << " returned after " << t;
}
if (!found) {
- LOG(ERROR) << "device-mapper device not found after polling timeout";
+ LOG(ERROR) << name << " device not found after polling timeout";
return false;
}
return true;
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index 0d4c6e9..b8dd3f1 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -27,12 +27,15 @@
BlockDevInitializer();
bool InitDeviceMapper();
+ bool InitDmUser();
bool InitDevices(std::set<std::string> devices);
bool InitDmDevice(const std::string& device);
private:
ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
+ bool InitMiscDevice(const std::string& name);
+
std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
};
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 0b456e7..597c32d 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -570,7 +570,6 @@
trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "block");
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
return {};
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
@@ -595,7 +594,6 @@
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "file");
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
@@ -606,7 +604,6 @@
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "file");
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
@@ -617,7 +614,6 @@
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "file");
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
@@ -1177,6 +1173,10 @@
}
// TODO(b/135984674): check that fstab contains /data.
if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
+ std::string proc_mounts_output;
+ android::base::ReadFileToString("/proc/mounts", &proc_mounts_output, true);
+ android::base::WriteStringToFile(proc_mounts_output,
+ "/metadata/userspacereboot/mount_info.txt");
trigger_shutdown("reboot,mount_userdata_failed");
}
if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result.ok()) {
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index a91cd1d..0b9f161 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -28,47 +28,55 @@
namespace init {
static const std::map<std::string, int> cap_map = {
- CAP_MAP_ENTRY(CHOWN),
- CAP_MAP_ENTRY(DAC_OVERRIDE),
- CAP_MAP_ENTRY(DAC_READ_SEARCH),
- CAP_MAP_ENTRY(FOWNER),
- CAP_MAP_ENTRY(FSETID),
- CAP_MAP_ENTRY(KILL),
- CAP_MAP_ENTRY(SETGID),
- CAP_MAP_ENTRY(SETUID),
- CAP_MAP_ENTRY(SETPCAP),
- CAP_MAP_ENTRY(LINUX_IMMUTABLE),
- CAP_MAP_ENTRY(NET_BIND_SERVICE),
- CAP_MAP_ENTRY(NET_BROADCAST),
- CAP_MAP_ENTRY(NET_ADMIN),
- CAP_MAP_ENTRY(NET_RAW),
- CAP_MAP_ENTRY(IPC_LOCK),
- CAP_MAP_ENTRY(IPC_OWNER),
- CAP_MAP_ENTRY(SYS_MODULE),
- CAP_MAP_ENTRY(SYS_RAWIO),
- CAP_MAP_ENTRY(SYS_CHROOT),
- CAP_MAP_ENTRY(SYS_PTRACE),
- CAP_MAP_ENTRY(SYS_PACCT),
- CAP_MAP_ENTRY(SYS_ADMIN),
- CAP_MAP_ENTRY(SYS_BOOT),
- CAP_MAP_ENTRY(SYS_NICE),
- CAP_MAP_ENTRY(SYS_RESOURCE),
- CAP_MAP_ENTRY(SYS_TIME),
- CAP_MAP_ENTRY(SYS_TTY_CONFIG),
- CAP_MAP_ENTRY(MKNOD),
- CAP_MAP_ENTRY(LEASE),
- CAP_MAP_ENTRY(AUDIT_WRITE),
- CAP_MAP_ENTRY(AUDIT_CONTROL),
- CAP_MAP_ENTRY(SETFCAP),
- CAP_MAP_ENTRY(MAC_OVERRIDE),
- CAP_MAP_ENTRY(MAC_ADMIN),
- CAP_MAP_ENTRY(SYSLOG),
- CAP_MAP_ENTRY(WAKE_ALARM),
- CAP_MAP_ENTRY(BLOCK_SUSPEND),
- CAP_MAP_ENTRY(AUDIT_READ),
+ CAP_MAP_ENTRY(CHOWN),
+ CAP_MAP_ENTRY(DAC_OVERRIDE),
+ CAP_MAP_ENTRY(DAC_READ_SEARCH),
+ CAP_MAP_ENTRY(FOWNER),
+ CAP_MAP_ENTRY(FSETID),
+ CAP_MAP_ENTRY(KILL),
+ CAP_MAP_ENTRY(SETGID),
+ CAP_MAP_ENTRY(SETUID),
+ CAP_MAP_ENTRY(SETPCAP),
+ CAP_MAP_ENTRY(LINUX_IMMUTABLE),
+ CAP_MAP_ENTRY(NET_BIND_SERVICE),
+ CAP_MAP_ENTRY(NET_BROADCAST),
+ CAP_MAP_ENTRY(NET_ADMIN),
+ CAP_MAP_ENTRY(NET_RAW),
+ CAP_MAP_ENTRY(IPC_LOCK),
+ CAP_MAP_ENTRY(IPC_OWNER),
+ CAP_MAP_ENTRY(SYS_MODULE),
+ CAP_MAP_ENTRY(SYS_RAWIO),
+ CAP_MAP_ENTRY(SYS_CHROOT),
+ CAP_MAP_ENTRY(SYS_PTRACE),
+ CAP_MAP_ENTRY(SYS_PACCT),
+ CAP_MAP_ENTRY(SYS_ADMIN),
+ CAP_MAP_ENTRY(SYS_BOOT),
+ CAP_MAP_ENTRY(SYS_NICE),
+ CAP_MAP_ENTRY(SYS_RESOURCE),
+ CAP_MAP_ENTRY(SYS_TIME),
+ CAP_MAP_ENTRY(SYS_TTY_CONFIG),
+ CAP_MAP_ENTRY(MKNOD),
+ CAP_MAP_ENTRY(LEASE),
+ CAP_MAP_ENTRY(AUDIT_WRITE),
+ CAP_MAP_ENTRY(AUDIT_CONTROL),
+ CAP_MAP_ENTRY(SETFCAP),
+ CAP_MAP_ENTRY(MAC_OVERRIDE),
+ CAP_MAP_ENTRY(MAC_ADMIN),
+ CAP_MAP_ENTRY(SYSLOG),
+ CAP_MAP_ENTRY(WAKE_ALARM),
+ CAP_MAP_ENTRY(BLOCK_SUSPEND),
+ CAP_MAP_ENTRY(AUDIT_READ),
+#if defined(__BIONIC__)
+ CAP_MAP_ENTRY(PERFMON),
+ CAP_MAP_ENTRY(BPF),
+#endif
};
+#if defined(__BIONIC__)
+static_assert(CAP_LAST_CAP == CAP_BPF, "CAP_LAST_CAP is not CAP_BPF");
+#else
static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
+#endif
static bool ComputeCapAmbientSupported() {
#if defined(__ANDROID__)
diff --git a/init/init.cpp b/init/init.cpp
index ba880ea..7d00538 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,6 +18,7 @@
#include <dirent.h>
#include <fcntl.h>
+#include <paths.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
@@ -309,14 +310,14 @@
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
parser.ParseConfig("/system_ext/etc/init");
- if (!parser.ParseConfig("/product/etc/init")) {
- late_import_paths.emplace_back("/product/etc/init");
+ if (!parser.ParseConfig("/vendor/etc/init")) {
+ late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
- if (!parser.ParseConfig("/vendor/etc/init")) {
- late_import_paths.emplace_back("/vendor/etc/init");
+ if (!parser.ParseConfig("/product/etc/init")) {
+ late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
@@ -727,6 +728,12 @@
InitSecondStageLogging(argv);
LOG(INFO) << "init second stage started!";
+ // Update $PATH in the case the second stage init is newer than first stage init, where it is
+ // first set.
+ if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
+ PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
+ }
+
// Init should not crash because of a dependence on any other process, therefore we ignore
// SIGPIPE and handle EPIPE at the call site directly. Note that setting a signal to SIG_IGN
// is inherited across exec, but custom signal handlers are not. Since we do not want to
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index f8359bc..59cc140 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -44,50 +44,17 @@
namespace init {
namespace {
-static bool BindMount(const std::string& source, const std::string& mount_point,
- bool recursive = false) {
- unsigned long mountflags = MS_BIND;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+static bool BindMount(const std::string& source, const std::string& mount_point) {
+ if (mount(source.c_str(), mount_point.c_str(), nullptr, MS_BIND | MS_REC, nullptr) == -1) {
PLOG(ERROR) << "Failed to bind mount " << source;
return false;
}
return true;
}
-static bool MakeShared(const std::string& mount_point, bool recursive = false) {
- unsigned long mountflags = MS_SHARED;
- if (recursive) {
- mountflags |= MS_REC;
- }
+static bool ChangeMount(const std::string& mount_point, unsigned long mountflags) {
if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Failed to change propagation type to shared";
- return false;
- }
- return true;
-}
-
-static bool MakeSlave(const std::string& mount_point, bool recursive = false) {
- unsigned long mountflags = MS_SLAVE;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Failed to change propagation type to slave";
- return false;
- }
- return true;
-}
-
-static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
- unsigned long mountflags = MS_PRIVATE;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Failed to change propagation type to private";
+ PLOG(ERROR) << "Failed to remount " << mount_point << " as " << std::hex << mountflags;
return false;
}
return true;
@@ -225,17 +192,17 @@
// needed for /foo/bar, then we will make /foo/bar as a mount point (by
// bind-mounting by to itself) and set the propagation type of the mount
// point to private.
- if (!MakeShared("/", true /*recursive*/)) return false;
+ if (!ChangeMount("/", MS_SHARED | MS_REC)) return false;
// /apex is a private mountpoint to give different sets of APEXes for
// the bootstrap and default mount namespaces. The processes running with
// the bootstrap namespace get APEXes from the read-only partition.
- if (!(MakePrivate("/apex"))) return false;
+ if (!(ChangeMount("/apex", MS_PRIVATE))) return false;
// /linkerconfig is a private mountpoint to give a different linker configuration
// based on the mount namespace. Subdirectory will be bind-mounted based on current mount
// namespace
- if (!(MakePrivate("/linkerconfig"))) return false;
+ if (!(ChangeMount("/linkerconfig", MS_PRIVATE))) return false;
// The two mount namespaces present challenges for scoped storage, because
// vold, which is responsible for most of the mounting, lives in the
@@ -266,15 +233,15 @@
if (!mkdir_recursive("/mnt/user", 0755)) return false;
if (!mkdir_recursive("/mnt/installer", 0755)) return false;
if (!mkdir_recursive("/mnt/androidwritable", 0755)) return false;
- if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false;
- if (!(BindMount("/mnt/user", "/mnt/androidwritable", true))) return false;
+ if (!(BindMount("/mnt/user", "/mnt/installer"))) return false;
+ if (!(BindMount("/mnt/user", "/mnt/androidwritable"))) return false;
// First, make /mnt/installer and /mnt/androidwritable a slave bind mount
- if (!(MakeSlave("/mnt/installer"))) return false;
- if (!(MakeSlave("/mnt/androidwritable"))) return false;
+ if (!(ChangeMount("/mnt/installer", MS_SLAVE))) return false;
+ if (!(ChangeMount("/mnt/androidwritable", MS_SLAVE))) return false;
// Then, make it shared again - effectively creating a new peer group, that
// will be inherited by new mount namespaces.
- if (!(MakeShared("/mnt/installer"))) return false;
- if (!(MakeShared("/mnt/androidwritable"))) return false;
+ if (!(ChangeMount("/mnt/installer", MS_SHARED))) return false;
+ if (!(ChangeMount("/mnt/androidwritable", MS_SHARED))) return false;
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
diff --git a/init/perfboot.py b/init/perfboot.py
index 713290b..4b23ad2 100755
--- a/init/perfboot.py
+++ b/init/perfboot.py
@@ -349,9 +349,9 @@
# Filter out invalid data.
end_times = [get_last_value(record, end_tag) for record in record_list
if get_last_value(record, end_tag) != 0]
- print 'mean: ', mean(end_times)
- print 'median:', median(end_times)
- print 'standard deviation:', stddev(end_times)
+ print 'mean:', int(round(mean(end_times))), 'ms'
+ print 'median:', int(round(median(end_times))), 'ms'
+ print 'standard deviation:', int(round(stddev(end_times))), 'ms'
def do_iteration(device, interval_adjuster, event_tags_re, end_tag):
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 1fa3362..a1e0969 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -994,7 +994,7 @@
&property_infos)) {
return;
}
- // Don't check for failure here, so we always have a sane list of properties.
+ // Don't check for failure here, since we don't always have all of these partitions.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 5f8b991..49baf9e 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -807,11 +807,19 @@
auto sigkill_timeout = GetMillisProperty("init.userspace_reboot.sigkill.timeoutmillis", 10s);
LOG(INFO) << "Timeout to terminate services: " << sigterm_timeout.count() << "ms "
<< "Timeout to kill services: " << sigkill_timeout.count() << "ms";
+ std::string services_file_name = "/metadata/userspacereboot/services.txt";
+ const int flags = O_RDWR | O_CREAT | O_SYNC | O_APPEND | O_CLOEXEC;
StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
+ android::base::WriteStringToFd("Post-data services still running: \n", fd);
+ for (const auto& s : stop_first) {
+ if (s->IsRunning()) {
+ android::base::WriteStringToFd(s->name() + "\n", fd);
+ }
+ }
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()) {
@@ -825,8 +833,14 @@
if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */),
sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
+ android::base::WriteStringToFd("Debugging services still running: \n", fd);
+ for (const auto& s : GetDebuggingServices(true)) {
+ if (s->IsRunning()) {
+ android::base::WriteStringToFd(s->name() + "\n", fd);
+ }
+ }
sub_reason = "sigkill_debug";
- // TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " debugging services are still running";
}
{
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 05e632b..f2383d7 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -60,13 +60,14 @@
Result<void> SetUpMountNamespace(bool remount_proc, bool remount_sys) {
constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
- // Recursively remount / as slave like zygote does so unmounting and mounting /proc
- // doesn't interfere with the parent namespace's /proc mount. This will also
- // prevent any other mounts/unmounts initiated by the service from interfering
- // with the parent namespace but will still allow mount events from the parent
+ // Recursively remount / as MS_SLAVE like zygote does so that
+ // unmounting and mounting /proc doesn't interfere with the parent
+ // namespace's /proc mount. This will also prevent any other
+ // mounts/unmounts initiated by the service from interfering with the
+ // parent namespace but will still allow mount events from the parent
// namespace to propagate to the child.
if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
- return ErrnoError() << "Could not remount(/) recursively as slave";
+ return ErrnoError() << "Could not remount(/) recursively as MS_SLAVE";
}
// umount() then mount() /proc and/or /sys
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
index 7e543f2..66a3328 100644
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -54,7 +54,7 @@
TEST_P(InitKillServicesTest, KillCriticalProcesses) {
ExpectKillingServiceRecovers(GetParam());
- // sanity check init is still responding
+ // Ensure that init is still responding
EXPECT_TRUE(SetProperty("test.death.test", "asdf"));
EXPECT_EQ(GetProperty("test.death.test", ""), "asdf");
EXPECT_TRUE(SetProperty("test.death.test", ""));
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index d8d9b36..7cd396a 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -95,20 +95,18 @@
fcntl(device_fd_, F_SETFL, O_NONBLOCK);
}
-bool UeventListener::ReadUevent(Uevent* uevent) const {
+ReadUeventResult UeventListener::ReadUevent(Uevent* uevent) const {
char msg[UEVENT_MSG_LEN + 2];
int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
if (n <= 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
PLOG(ERROR) << "Error reading from Uevent Fd";
}
- return false;
+ return ReadUeventResult::kFailed;
}
if (n >= UEVENT_MSG_LEN) {
LOG(ERROR) << "Uevent overflowed buffer, discarding";
- // Return true here even if we discard as we may have more uevents pending and we
- // want to keep processing them.
- return true;
+ return ReadUeventResult::kInvalid;
}
msg[n] = '\0';
@@ -116,7 +114,7 @@
ParseEvent(msg, uevent);
- return true;
+ return ReadUeventResult::kSuccess;
}
// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel
@@ -137,7 +135,10 @@
close(fd);
Uevent uevent;
- while (ReadUevent(&uevent)) {
+ ReadUeventResult result;
+ while ((result = ReadUevent(&uevent)) != ReadUeventResult::kFailed) {
+ // Skip processing the uevent if it is invalid.
+ if (result == ReadUeventResult::kInvalid) continue;
if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;
}
}
@@ -212,7 +213,10 @@
// We're non-blocking, so if we receive a poll event keep processing until
// we have exhausted all uevent messages.
Uevent uevent;
- while (ReadUevent(&uevent)) {
+ ReadUeventResult result;
+ while ((result = ReadUevent(&uevent)) != ReadUeventResult::kFailed) {
+ // Skip processing the uevent if it is invalid.
+ if (result == ReadUeventResult::kInvalid) continue;
if (callback(uevent) == ListenerAction::kStop) return;
}
}
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
index aea094e..2772860 100644
--- a/init/uevent_listener.h
+++ b/init/uevent_listener.h
@@ -27,7 +27,7 @@
#include "uevent.h"
-#define UEVENT_MSG_LEN 2048
+#define UEVENT_MSG_LEN 8192
namespace android {
namespace init {
@@ -37,6 +37,12 @@
kContinue, // Continue regenerating uevents as we haven't seen the one(s) we're interested in.
};
+enum class ReadUeventResult {
+ kSuccess = 0, // Uevent was successfully read.
+ kFailed, // Uevent reading has failed.
+ kInvalid, // An Invalid Uevent was read (like say, the msg received is >= UEVENT_MSG_LEN).
+};
+
using ListenerCallback = std::function<ListenerAction(const Uevent&)>;
class UeventListener {
@@ -50,7 +56,7 @@
const std::optional<std::chrono::milliseconds> relative_timeout = {}) const;
private:
- bool ReadUevent(Uevent* uevent) const;
+ ReadUeventResult ReadUevent(Uevent* uevent) const;
ListenerAction RegenerateUeventsForDir(DIR* d, const ListenerCallback& callback) const;
android::base::unique_fd device_fd_;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index f4191b9..cc32b6d 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -1584,7 +1584,7 @@
// Verify the flag is set.
ASSERT_EQ(PROT_DEVICE_MAP, map.flags & PROT_DEVICE_MAP);
- // Quick sanity checks.
+ // Quick basic checks of functionality.
uint64_t offset;
ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset));
ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
diff --git a/libcrypto_utils/.clang-format b/libcrypto_utils/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libcrypto_utils/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index d7175e0..923b291 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -23,7 +23,7 @@
},
host_supported: true,
srcs: [
- "android_pubkey.c",
+ "android_pubkey.cpp",
],
cflags: [
"-Wall",
diff --git a/libcrypto_utils/android_pubkey.c b/libcrypto_utils/android_pubkey.cpp
similarity index 65%
rename from libcrypto_utils/android_pubkey.c
rename to libcrypto_utils/android_pubkey.cpp
index 3052e52..21e5663 100644
--- a/libcrypto_utils/android_pubkey.c
+++ b/libcrypto_utils/android_pubkey.cpp
@@ -35,37 +35,29 @@
// little-endian 32 bit words. Note that Android only supports little-endian
// processors, so we don't do any byte order conversions when parsing the binary
// struct.
-typedef struct RSAPublicKey {
- // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
- uint32_t modulus_size_words;
+struct RSAPublicKey {
+ // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
+ uint32_t modulus_size_words;
- // Precomputed montgomery parameter: -1 / n[0] mod 2^32
- uint32_t n0inv;
+ // Precomputed montgomery parameter: -1 / n[0] mod 2^32
+ uint32_t n0inv;
- // RSA modulus as a little-endian array.
- uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
+ // RSA modulus as a little-endian array.
+ uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
- // Montgomery parameter R^2 as a little-endian array of little-endian words.
- uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
+ // Montgomery parameter R^2 as a little-endian array.
+ uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
- // RSA modulus: 3 or 65537
- uint32_t exponent;
-} RSAPublicKey;
-
-// Reverses byte order in |buffer|.
-static void reverse_bytes(uint8_t* buffer, size_t size) {
- for (size_t i = 0; i < (size + 1) / 2; ++i) {
- uint8_t tmp = buffer[i];
- buffer[i] = buffer[size - i - 1];
- buffer[size - i - 1] = tmp;
- }
-}
+ // RSA modulus: 3 or 65537
+ uint32_t exponent;
+};
bool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key) {
const RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
bool ret = false;
- uint8_t modulus_buffer[ANDROID_PUBKEY_MODULUS_SIZE];
RSA* new_key = RSA_new();
+ BIGNUM* n = NULL;
+ BIGNUM* e = NULL;
if (!new_key) {
goto cleanup;
}
@@ -79,19 +71,24 @@
}
// Convert the modulus to big-endian byte order as expected by BN_bin2bn.
- memcpy(modulus_buffer, key_struct->modulus, sizeof(modulus_buffer));
- reverse_bytes(modulus_buffer, sizeof(modulus_buffer));
- new_key->n = BN_bin2bn(modulus_buffer, sizeof(modulus_buffer), NULL);
- if (!new_key->n) {
+ n = BN_le2bn(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, NULL);
+ if (!n) {
goto cleanup;
}
// Read the exponent.
- new_key->e = BN_new();
- if (!new_key->e || !BN_set_word(new_key->e, key_struct->exponent)) {
+ e = BN_new();
+ if (!e || !BN_set_word(e, key_struct->exponent)) {
goto cleanup;
}
+ if (!RSA_set0_key(new_key, n, e, NULL)) {
+ goto cleanup;
+ }
+ // RSA_set0_key takes ownership of its inputs on success.
+ n = NULL;
+ e = NULL;
+
// Note that we don't extract the montgomery parameters n0inv and rr from
// the RSAPublicKey structure. They assume a word size of 32 bits, but
// BoringSSL may use a word size of 64 bits internally, so we're lacking the
@@ -101,24 +98,16 @@
// pre-computed montgomery parameters.
*key = new_key;
+ new_key = NULL;
ret = true;
cleanup:
- if (!ret && new_key) {
- RSA_free(new_key);
- }
+ RSA_free(new_key);
+ BN_free(n);
+ BN_free(e);
return ret;
}
-static bool android_pubkey_encode_bignum(const BIGNUM* num, uint8_t* buffer) {
- if (!BN_bn2bin_padded(buffer, ANDROID_PUBKEY_MODULUS_SIZE, num)) {
- return false;
- }
-
- reverse_bytes(buffer, ANDROID_PUBKEY_MODULUS_SIZE);
- return true;
-}
-
bool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size) {
RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
bool ret = false;
@@ -127,8 +116,7 @@
BIGNUM* n0inv = BN_new();
BIGNUM* rr = BN_new();
- if (sizeof(RSAPublicKey) > size ||
- RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
+ if (sizeof(RSAPublicKey) > size || RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
goto cleanup;
}
@@ -136,27 +124,26 @@
key_struct->modulus_size_words = ANDROID_PUBKEY_MODULUS_SIZE_WORDS;
// Compute and store n0inv = -1 / N[0] mod 2^32.
- if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) ||
- !BN_mod(n0inv, key->n, r32, ctx) ||
+ if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) || !BN_mod(n0inv, RSA_get0_n(key), r32, ctx) ||
!BN_mod_inverse(n0inv, n0inv, r32, ctx) || !BN_sub(n0inv, r32, n0inv)) {
goto cleanup;
}
key_struct->n0inv = (uint32_t)BN_get_word(n0inv);
// Store the modulus.
- if (!android_pubkey_encode_bignum(key->n, key_struct->modulus)) {
+ if (!BN_bn2le_padded(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, RSA_get0_n(key))) {
goto cleanup;
}
// Compute and store rr = (2^(rsa_size)) ^ 2 mod N.
if (!ctx || !rr || !BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8) ||
- !BN_mod_sqr(rr, rr, key->n, ctx) ||
- !android_pubkey_encode_bignum(rr, key_struct->rr)) {
+ !BN_mod_sqr(rr, rr, RSA_get0_n(key), ctx) ||
+ !BN_bn2le_padded(key_struct->rr, ANDROID_PUBKEY_MODULUS_SIZE, rr)) {
goto cleanup;
}
// Store the exponent.
- key_struct->exponent = (uint32_t)BN_get_word(key->e);
+ key_struct->exponent = (uint32_t)BN_get_word(RSA_get0_e(key));
ret = true;
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 8c232f0..233d400 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -122,7 +122,8 @@
return true;
}
- /* If its not a number, assume string, but check if its a sane string */
+ // Non-numeric should be a single ASCII character. Characters after the
+ // first are ignored.
if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
vndk_version.c_str());
@@ -211,13 +212,16 @@
// fallback for APEX w/ use_vendor on Q, which would have still used /dev/ashmem
if (fd < 0) {
+ int saved_errno = errno;
fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
+ if (fd < 0) {
+ /* Q launching devices and newer must not reach here since they should have been
+ * able to open ashmem_device_path */
+ ALOGE("Unable to open ashmem device %s (error = %s) and /dev/ashmem(error = %s)",
+ ashmem_device_path.c_str(), strerror(saved_errno), strerror(errno));
+ return fd;
+ }
}
-
- if (fd < 0) {
- return fd;
- }
-
struct stat st;
int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
if (ret < 0) {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 5805a4d..b9fc82e 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -203,6 +203,7 @@
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/e2fsck" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/snapuserd" },
// generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index c74ee3e..793e2ce 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,7 +75,8 @@
#define ATRACE_TAG_AIDL (1<<24)
#define ATRACE_TAG_NNAPI (1<<25)
#define ATRACE_TAG_RRO (1<<26)
-#define ATRACE_TAG_LAST ATRACE_TAG_RRO
+#define ATRACE_TAG_SYSPROP (1<<27)
+#define ATRACE_TAG_LAST ATRACE_TAG_SYSPROP
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index e4f45a8..b4fe2e6 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -36,7 +36,7 @@
#pragma once
-/* This is the master Users and Groups config for the platform.
+/* This is the main Users and Groups config for the platform.
* DO NOT EVER RENUMBER
*/
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index b94d134..2fe877c 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -38,24 +38,24 @@
int (*netdDeleteTagData)(uint32_t, uid_t);
};
-int dummyTagSocket(int, uint32_t, uid_t) {
+int stubTagSocket(int, uint32_t, uid_t) {
return -EREMOTEIO;
}
-int dummyUntagSocket(int) {
+int stubUntagSocket(int) {
return -EREMOTEIO;
}
-int dummySetCounterSet(uint32_t, uid_t) {
+int stubSetCounterSet(uint32_t, uid_t) {
return -EREMOTEIO;
}
-int dummyDeleteTagData(uint32_t, uid_t) {
+int stubDeleteTagData(uint32_t, uid_t) {
return -EREMOTEIO;
}
netdHandler initHandler(void) {
- netdHandler handler = {dummyTagSocket, dummyUntagSocket, dummySetCounterSet, dummyDeleteTagData};
+ netdHandler handler = {stubTagSocket, stubUntagSocket, stubSetCounterSet, stubDeleteTagData};
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
if (!netdClientHandle) {
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
index bf244d2..40bbd5c 100644
--- a/libcutils/uevent.cpp
+++ b/libcutils/uevent.cpp
@@ -101,7 +101,7 @@
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
- addr.nl_pid = getpid();
+ addr.nl_pid = 0;
addr.nl_groups = 0xffffffff;
s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
diff --git a/libkeyutils/keyutils_test.cpp b/libkeyutils/keyutils_test.cpp
index d41c91b..d03747b 100644
--- a/libkeyutils/keyutils_test.cpp
+++ b/libkeyutils/keyutils_test.cpp
@@ -33,7 +33,7 @@
#include <gtest/gtest.h>
TEST(keyutils, smoke) {
- // Check that the exported type is sane.
+ // Check that the exported type is the right size.
ASSERT_EQ(4U, sizeof(key_serial_t));
// Check that all the functions actually exist.
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 51c5e60..85556e8 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -31,84 +31,13 @@
#include <unordered_map>
#include <log/event_tag_map.h>
-#include <log/log_properties.h>
#include <private/android_logger.h>
#include <utils/FastStrcmp.h>
#include <utils/RWLock.h>
-#include "logd_reader.h"
-
#define OUT_TAG "EventTagMap"
-class MapString {
- private:
- const std::string* alloc; // HAS-AN
- const std::string_view str; // HAS-A
-
- public:
- operator const std::string_view() const {
- return str;
- }
-
- const char* data() const {
- return str.data();
- }
- size_t length() const {
- return str.length();
- }
-
- bool operator==(const MapString& rval) const {
- if (length() != rval.length()) return false;
- if (length() == 0) return true;
- return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
- }
- bool operator!=(const MapString& rval) const {
- return !(*this == rval);
- }
-
- MapString(const char* str, size_t len) : alloc(NULL), str(str, len) {
- }
- explicit MapString(const std::string& str)
- : alloc(new std::string(str)), str(alloc->data(), alloc->length()) {
- }
- MapString(MapString&& rval) noexcept
- : alloc(rval.alloc), str(rval.data(), rval.length()) {
- rval.alloc = NULL;
- }
- explicit MapString(const MapString& rval)
- : alloc(rval.alloc ? new std::string(*rval.alloc) : NULL),
- str(alloc ? alloc->data() : rval.data(), rval.length()) {
- }
-
- ~MapString() {
- if (alloc) delete alloc;
- }
-};
-
-// Hash for MapString
-template <>
-struct std::hash<MapString>
- : public std::unary_function<const MapString&, size_t> {
- size_t operator()(const MapString& __t) const noexcept {
- if (!__t.length()) return 0;
- return std::hash<std::string_view>()(std::string_view(__t));
- }
-};
-
-typedef std::pair<MapString, MapString> TagFmt;
-
-template <>
-struct std::hash<TagFmt> : public std::unary_function<const TagFmt&, size_t> {
- size_t operator()(const TagFmt& __t) const noexcept {
- // Tag is typically unique. Will cost us an extra 100ns for the
- // unordered_map lookup if we instead did a hash that combined
- // both of tag and fmt members, e.g.:
- //
- // return std::hash<MapString>()(__t.first) ^
- // std::hash<MapString>()(__t.second);
- return std::hash<MapString>()(__t.first);
- }
-};
+typedef std::pair<std::string_view, std::string_view> TagFmt;
// Map
struct EventTagMap {
@@ -119,8 +48,7 @@
private:
std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
- std::unordered_map<TagFmt, uint32_t> TagFmt2Idx;
- std::unordered_map<MapString, uint32_t> Tag2Idx;
+ std::unordered_map<std::string_view, uint32_t> Tag2Idx;
// protect unordered sets
android::RWLock rwlock;
@@ -132,7 +60,6 @@
~EventTagMap() {
Idx2TagFmt.clear();
- TagFmt2Idx.clear();
Tag2Idx.clear();
for (size_t which = 0; which < NUM_MAPS; ++which) {
if (mapAddr[which]) {
@@ -144,8 +71,7 @@
bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
const TagFmt* find(uint32_t tag) const;
- int find(TagFmt&& tagfmt) const;
- int find(MapString&& tag) const;
+ int find(std::string_view tag) const;
};
bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
@@ -156,8 +82,7 @@
":%.*s:%.*s)\n";
android::RWLock::AutoWLock writeLock(rwlock);
{
- std::unordered_map<uint32_t, TagFmt>::const_iterator it;
- it = Idx2TagFmt.find(tag);
+ auto it = Idx2TagFmt.find(tag);
if (it != Idx2TagFmt.end()) {
if (verbose) {
fprintf(stderr, errorFormat, it->first, (int)it->second.first.length(),
@@ -173,25 +98,7 @@
}
{
- std::unordered_map<TagFmt, uint32_t>::const_iterator it;
- it = TagFmt2Idx.find(tagfmt);
- if (it != TagFmt2Idx.end()) {
- if (verbose) {
- fprintf(stderr, errorFormat, it->second, (int)it->first.first.length(),
- it->first.first.data(), (int)it->first.second.length(),
- it->first.second.data(), tag, (int)tagfmt.first.length(),
- tagfmt.first.data(), (int)tagfmt.second.length(),
- tagfmt.second.data());
- }
- ret = false;
- } else {
- TagFmt2Idx.emplace(std::make_pair(tagfmt, tag));
- }
- }
-
- {
- std::unordered_map<MapString, uint32_t>::const_iterator it;
- it = Tag2Idx.find(tagfmt.first);
+ auto it = Tag2Idx.find(tagfmt.first);
if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
Tag2Idx.erase(it);
it = Tag2Idx.end();
@@ -205,25 +112,15 @@
}
const TagFmt* EventTagMap::find(uint32_t tag) const {
- std::unordered_map<uint32_t, TagFmt>::const_iterator it;
android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
- it = Idx2TagFmt.find(tag);
+ auto it = Idx2TagFmt.find(tag);
if (it == Idx2TagFmt.end()) return NULL;
return &(it->second);
}
-int EventTagMap::find(TagFmt&& tagfmt) const {
- std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+int EventTagMap::find(std::string_view tag) const {
android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
- it = TagFmt2Idx.find(std::move(tagfmt));
- if (it == TagFmt2Idx.end()) return -1;
- return it->second;
-}
-
-int EventTagMap::find(MapString&& tag) const {
- std::unordered_map<MapString, uint32_t>::const_iterator it;
- android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
- it = Tag2Idx.find(std::move(tag));
+ auto it = Tag2Idx.find(std::move(tag));
if (it == Tag2Idx.end()) return -1;
return it->second;
}
@@ -241,29 +138,20 @@
// successful return, it will be pointing to the last character in the
// tag line (i.e. the character before the start of the next line).
//
-// lineNum = 0 removes verbose comments and requires us to cache the
-// content rather than make direct raw references since the content
-// will disappear after the call. A non-zero lineNum means we own the
-// data and it will outlive the call.
-//
// Returns 0 on success, nonzero on failure.
-static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
+static int scanTagLine(EventTagMap* map, const char*& pData, int line_num) {
char* ep;
unsigned long val = strtoul(pData, &ep, 10);
const char* cp = ep;
if (cp == pData) {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
- }
+ fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", line_num);
errno = EINVAL;
return -1;
}
uint32_t tagIndex = val;
if (tagIndex != val) {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
- }
+ fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", line_num);
errno = ERANGE;
return -1;
}
@@ -272,9 +160,7 @@
}
if (*cp == '\n') {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
- }
+ fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", line_num);
errno = EINVAL;
return -1;
}
@@ -284,10 +170,7 @@
size_t tagLen = cp - tag;
if (!isspace(*cp)) {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp,
- lineNum);
- }
+ fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp, line_num);
errno = EINVAL;
return -1;
}
@@ -317,25 +200,15 @@
while (*cp && (*cp != '\n')) ++cp;
#ifdef DEBUG
- fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - pData), pData);
+ fprintf(stderr, "%d: %p: %.*s\n", line_num, tag, (int)(cp - pData), pData);
#endif
pData = cp;
- if (lineNum) {
- if (map->emplaceUnique(tagIndex,
- TagFmt(std::make_pair(MapString(tag, tagLen),
- MapString(fmt, fmtLen))),
- verbose)) {
- return 0;
- }
- } else {
- // cache
- if (map->emplaceUnique(
- tagIndex,
- TagFmt(std::make_pair(MapString(std::string(tag, tagLen)),
- MapString(std::string(fmt, fmtLen)))))) {
- return 0;
- }
+ if (map->emplaceUnique(
+ tagIndex,
+ TagFmt(std::make_pair(std::string_view(tag, tagLen), std::string_view(fmt, fmtLen))),
+ verbose)) {
+ return 0;
}
errno = EMLINK;
return -1;
@@ -491,57 +364,10 @@
if (map) delete map;
}
-// Cache miss, go to logd to acquire a public reference.
-// Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
-static const TagFmt* __getEventTag([[maybe_unused]] EventTagMap* map, unsigned int tag) {
- // call event tag service to arrange for a new tag
- char* buf = NULL;
- // Can not use android::base::StringPrintf, asprintf + free instead.
- static const char command_template[] = "getEventTag id=%u";
- int ret = asprintf(&buf, command_template, tag);
- if (ret > 0) {
- // Add some buffer margin for an estimate of the full return content.
- size_t size =
- ret - strlen(command_template) +
- strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
- if (size > (size_t)ret) {
- char* np = static_cast<char*>(realloc(buf, size));
- if (np) {
- buf = np;
- } else {
- size = ret;
- }
- } else {
- size = ret;
- }
-#ifdef __ANDROID__
- // Ask event log tag service for an existing entry
- if (SendLogdControlMessage(buf, size) >= 0) {
- buf[size - 1] = '\0';
- char* ep;
- unsigned long val = strtoul(buf, &ep, 10); // return size
- const char* cp = ep;
- if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
- ++cp;
- if (!scanTagLine(map, cp, 0)) {
- free(buf);
- return map->find(tag);
- }
- }
- }
-#endif
- free(buf);
- }
- return NULL;
-}
-
// Look up an entry in the map.
const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsigned int tag) {
if (len) *len = 0;
const TagFmt* str = map->find(tag);
- if (!str) {
- str = __getEventTag(const_cast<EventTagMap*>(map), tag);
- }
if (!str) return NULL;
if (len) *len = str->first.length();
return str->first.data();
@@ -551,98 +377,8 @@
const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len, unsigned int tag) {
if (len) *len = 0;
const TagFmt* str = map->find(tag);
- if (!str) {
- str = __getEventTag(const_cast<EventTagMap*>(map), tag);
- }
if (!str) return NULL;
if (len) *len = str->second.length();
return str->second.data();
}
-// This function is deprecated and replaced with android_lookupEventTag_len
-// since it will cause the map to change from Shared and backed by a file,
-// to Private Dirty and backed up by swap, albeit highly compressible. By
-// deprecating this function everywhere, we save 100s of MB of memory space.
-const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag) {
- size_t len;
- const char* tagStr = android_lookupEventTag_len(map, &len, tag);
-
- if (!tagStr) return tagStr;
- char* cp = const_cast<char*>(tagStr);
- cp += len;
- if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
- return tagStr;
-}
-
-// Look up tagname, generate one if necessary, and return a tag
-int android_lookupEventTagNum(EventTagMap* map, const char* tagname, const char* format, int prio) {
- const char* ep = endOfTag(tagname);
- size_t len = ep - tagname;
- if (!len || *ep) {
- errno = EINVAL;
- return -1;
- }
-
- if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
- !__android_log_is_loggable_len(prio, tagname, len,
- __android_log_is_debuggable()
- ? ANDROID_LOG_VERBOSE
- : ANDROID_LOG_DEBUG)) {
- errno = EPERM;
- return -1;
- }
-
- if (!format) format = "";
- ssize_t fmtLen = strlen(format);
- int ret = map->find(TagFmt(
- std::make_pair(MapString(tagname, len), MapString(format, fmtLen))));
- if (ret != -1) return ret;
-
- // call event tag service to arrange for a new tag
- char* buf = NULL;
- // Can not use android::base::StringPrintf, asprintf + free instead.
- static const char command_template[] = "getEventTag name=%s format=\"%s\"";
- ret = asprintf(&buf, command_template, tagname, format);
- if (ret > 0) {
- // Add some buffer margin for an estimate of the full return content.
- char* cp;
- size_t size =
- ret - strlen(command_template) +
- strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
- if (size > (size_t)ret) {
- cp = static_cast<char*>(realloc(buf, size));
- if (cp) {
- buf = cp;
- } else {
- size = ret;
- }
- } else {
- size = ret;
- }
-#ifdef __ANDROID__
- // Ask event log tag service for an allocation
- if (SendLogdControlMessage(buf, size) >= 0) {
- buf[size - 1] = '\0';
- unsigned long val = strtoul(buf, &cp, 10); // return size
- if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
- val = strtoul(cp + 1, &cp, 10); // allocated tag number
- if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
- free(buf);
- ret = val;
- // cache
- map->emplaceUnique(ret, TagFmt(std::make_pair(
- MapString(std::string(tagname, len)),
- MapString(std::string(format, fmtLen)))));
- return ret;
- }
- }
- }
-#endif
- free(buf);
- }
-
- // Hail Mary
- ret = map->find(MapString(tagname, len));
- if (ret == -1) errno = ESRCH;
- return ret;
-}
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index f7ec208..de49fbf 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -40,14 +40,6 @@
void android_closeEventTagMap(EventTagMap* map);
/*
- * Look up a tag by index. Returns the tag string, or NULL if not found.
- */
-const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag)
- __attribute__((
- deprecated("use android_lookupEventTag_len() instead to minimize "
- "MAP_PRIVATE copy-on-write memory impact")));
-
-/*
* Look up a tag by index. Returns the tag string & string length, or NULL if
* not found. Returned string is not guaranteed to be nul terminated.
*/
@@ -61,12 +53,6 @@
const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len,
unsigned int tag);
-/*
- * Look up tagname, generate one if necessary, and return a tag
- */
-int android_lookupEventTagNum(EventTagMap* map, const char* tagname,
- const char* format, int prio);
-
#ifdef __cplusplus
}
#endif
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 3497d63..2a0230f 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -20,6 +20,7 @@
extern "C" {
#endif
+/* Returns `1` if the device is debuggable or `0` if not. */
int __android_log_is_debuggable();
#ifdef __cplusplus
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 23d76f4..1736934 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -81,10 +81,17 @@
log_id_t android_logger_get_id(struct logger* logger);
+/* Clears the given log buffer. */
int android_logger_clear(struct logger* logger);
+/* Return the allotted size for the given log buffer. */
long android_logger_get_log_size(struct logger* logger);
+/* Set the allotted size for the given log buffer. */
int android_logger_set_log_size(struct logger* logger, unsigned long size);
+/* Return the actual, uncompressed size that can be read from the given log buffer. */
long android_logger_get_log_readable_size(struct logger* logger);
+/* Return the actual, compressed size that the given log buffer is consuming. */
+long android_logger_get_log_consumed_size(struct logger* logger);
+/* Deprecated. Always returns '4' regardless of input. */
int android_logger_get_log_version(struct logger* logger);
struct logger_list;
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index d3b72bc..166f387 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -144,21 +144,6 @@
int __android_log_security_bswrite(int32_t tag, const char* payload);
int __android_log_security(); /* Device Owner is present */
-#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
-#define BOOL_DEFAULT_FALSE 0x0 /* false if property not present */
-#define BOOL_DEFAULT_TRUE 0x1 /* true if property not present */
-#define BOOL_DEFAULT_FLAG_PERSIST 0x2 /* <key>, persist.<key>, ro.<key> */
-#define BOOL_DEFAULT_FLAG_ENG 0x4 /* off for user */
-#define BOOL_DEFAULT_FLAG_SVELTE 0x8 /* off for low_ram */
-bool __android_logger_property_get_bool(const char* key, int flag);
-
-#define LOG_BUFFER_SIZE (256 * 1024) /* Tuned with ro.logd.size per-platform \
- */
-#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
-#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
-unsigned long __android_logger_get_buffer_size(log_id_t logId);
-bool __android_logger_valid_buffer_size(unsigned long value);
-
/* Retrieve the composed event buffer */
int android_log_write_list_buffer(android_log_context ctx, const char** msg);
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 161fcf1..f8d5ef0 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -84,13 +84,10 @@
global:
__android_log_pmsg_file_read;
__android_log_pmsg_file_write;
- __android_logger_get_buffer_size;
- __android_logger_property_get_bool;
android_openEventTagMap;
android_log_processBinaryLogBuffer;
android_log_processLogBuffer;
android_log_read_next;
android_log_write_list_buffer;
- android_lookupEventTagNum;
create_android_log_parser;
};
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 82ed6b2..611caed 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -35,13 +35,14 @@
#include <string>
+#include <android-base/parseint.h>
#include <private/android_logger.h>
#include "logger.h"
// Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
// O_CLOEXEC is always set.
-static int socket_local_client(const std::string& name, int type) {
+static int socket_local_client(const std::string& name, int type, bool timeout) {
sockaddr_un addr = {.sun_family = AF_LOCAL};
std::string path = "/dev/socket/" + name;
@@ -55,6 +56,18 @@
return -1;
}
+ if (timeout) {
+ // Sending and receiving messages should be instantaneous, but we don't want to wait forever if
+ // logd is hung, so we set a gracious 2s timeout.
+ struct timeval t = {2, 0};
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)) == -1) {
+ return -1;
+ }
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)) == -1) {
+ return -1;
+ }
+ }
+
if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
close(fd);
return -1;
@@ -69,7 +82,7 @@
size_t len;
char* cp;
int errno_save = 0;
- int sock = socket_local_client("logd", SOCK_STREAM);
+ int sock = socket_local_client("logd", SOCK_STREAM, true);
if (sock < 0) {
return sock;
}
@@ -148,26 +161,56 @@
return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
}
-/* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger* logger) {
+enum class LogSizeType : uint32_t {
+ kAllotted = 0,
+ kReadable,
+ kConsumed,
+};
+
+static long GetLogSize(struct logger* logger, LogSizeType type) {
if (!android_logger_is_logd(logger)) {
return -EINVAL;
}
uint32_t log_id = android_logger_get_id(logger);
char buf[512];
- snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
+ switch (type) {
+ case LogSizeType::kAllotted:
+ snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
+ break;
+ case LogSizeType::kReadable:
+ snprintf(buf, sizeof(buf), "getLogSizeReadable %" PRIu32, log_id);
+ break;
+ case LogSizeType::kConsumed:
+ snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
+ break;
+ default:
+ abort();
+ }
ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
if (ret < 0) {
return ret;
}
- if ((buf[0] < '0') || ('9' < buf[0])) {
+ long size;
+ if (!android::base::ParseInt(buf, &size)) {
return -1;
}
- return atol(buf);
+ return size;
+}
+
+long android_logger_get_log_size(struct logger* logger) {
+ return GetLogSize(logger, LogSizeType::kAllotted);
+}
+
+long android_logger_get_log_readable_size(struct logger* logger) {
+ return GetLogSize(logger, LogSizeType::kReadable);
+}
+
+long android_logger_get_log_consumed_size(struct logger* logger) {
+ return GetLogSize(logger, LogSizeType::kConsumed);
}
int android_logger_set_log_size(struct logger* logger, unsigned long size) {
@@ -182,31 +225,6 @@
return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
}
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-long android_logger_get_log_readable_size(struct logger* logger) {
- if (!android_logger_is_logd(logger)) {
- return -EINVAL;
- }
-
- uint32_t log_id = android_logger_get_id(logger);
- char buf[512];
- snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
-
- ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
- if (ret < 0) {
- return ret;
- }
-
- if ((buf[0] < '0') || ('9' < buf[0])) {
- return -1;
- }
-
- return atol(buf);
-}
-
int android_logger_get_log_version(struct logger*) {
return 4;
}
@@ -268,7 +286,7 @@
return sock;
}
- sock = socket_local_client("logdr", SOCK_SEQPACKET);
+ sock = socket_local_client("logdr", SOCK_SEQPACKET, false);
if (sock <= 0) {
if ((sock == -1) && errno) {
return -errno;
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index a230749..f5d19ca 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -59,8 +59,7 @@
return;
}
- int new_socket =
- TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ int new_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
if (new_socket <= 0) {
return;
}
@@ -91,8 +90,6 @@
struct iovec newVec[nr + headerLength];
android_log_header_t header;
size_t i, payloadSize;
- static atomic_int dropped;
- static atomic_int droppedSecurity;
GetSocket();
@@ -110,6 +107,7 @@
return 0;
}
+ header.id = logId;
header.tid = gettid();
header.realtime.tv_sec = ts->tv_sec;
header.realtime.tv_nsec = ts->tv_nsec;
@@ -117,44 +115,6 @@
newVec[0].iov_base = (unsigned char*)&header;
newVec[0].iov_len = sizeof(header);
- int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
- if (snapshot) {
- android_log_event_int_t buffer;
-
- header.id = LOG_ID_SECURITY;
- buffer.header.tag = LIBLOG_LOG_TAG;
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = snapshot;
-
- newVec[headerLength].iov_base = &buffer;
- newVec[headerLength].iov_len = sizeof(buffer);
-
- ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, 2));
- if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&droppedSecurity, snapshot, memory_order_relaxed);
- }
- }
- snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
- if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
- ANDROID_LOG_VERBOSE)) {
- android_log_event_int_t buffer;
-
- header.id = LOG_ID_EVENTS;
- buffer.header.tag = LIBLOG_LOG_TAG;
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = snapshot;
-
- newVec[headerLength].iov_base = &buffer;
- newVec[headerLength].iov_len = sizeof(buffer);
-
- ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, 2));
- if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
- }
- }
-
- header.id = logId;
-
for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
newVec[i].iov_base = vec[i - headerLength].iov_base;
payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
@@ -168,11 +128,8 @@
}
}
- // The write below could be lost, but will never block.
- // EAGAIN occurs if logd is overloaded, other errors indicate that something went wrong with
- // the connection, so we reset it and try again.
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
- if (ret < 0 && errno != EAGAIN) {
+ if (ret < 0) {
LogdConnect();
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
@@ -182,14 +139,5 @@
ret = -errno;
}
- if (ret > (ssize_t)sizeof(header)) {
- ret -= sizeof(header);
- } else if (ret < 0) {
- atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
- if (logId == LOG_ID_SECURITY) {
- atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed);
- }
- }
-
return ret;
}
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index 2392112..88f0bf1 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -294,33 +294,12 @@
}
int __android_log_is_debuggable() {
- static uint32_t serial;
- static struct cache_char tag_cache;
- static const char key[] = "ro.debuggable";
- int ret;
+ static int is_debuggable = [] {
+ char value[PROP_VALUE_MAX] = {};
+ return __system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1");
+ }();
- if (tag_cache.c) { /* ro property does not change after set */
- ret = tag_cache.c == '1';
- } else if (lock()) {
- struct cache_char temp_cache = {{NULL, 0xFFFFFFFF}, '\0'};
- refresh_cache(&temp_cache, key);
- ret = temp_cache.c == '1';
- } else {
- int change_detected = check_cache(&tag_cache.cache);
- uint32_t current_serial = __system_property_area_serial();
- if (current_serial != serial) {
- change_detected = 1;
- }
- if (change_detected) {
- refresh_cache(&tag_cache, key);
- serial = current_serial;
- }
- ret = tag_cache.c == '1';
-
- unlock();
- }
-
- return ret;
+ return is_debuggable;
}
/*
@@ -385,216 +364,6 @@
return do_cache2_char(&security);
}
-/*
- * Interface that represents the logd buffer size determination so that others
- * need not guess our intentions.
- */
-
-/* Property helper */
-static bool check_flag(const char* prop, const char* flag) {
- const char* cp = strcasestr(prop, flag);
- if (!cp) {
- return false;
- }
- /* We only will document comma (,) */
- static const char sep[] = ",:;|+ \t\f";
- if ((cp != prop) && !strchr(sep, cp[-1])) {
- return false;
- }
- cp += strlen(flag);
- return !*cp || !!strchr(sep, *cp);
-}
-
-/* cache structure */
-struct cache_property {
- struct cache cache;
- char property[PROP_VALUE_MAX];
-};
-
-static void refresh_cache_property(struct cache_property* cache, const char* key) {
- if (!cache->cache.pinfo) {
- cache->cache.pinfo = __system_property_find(key);
- if (!cache->cache.pinfo) {
- return;
- }
- }
- cache->cache.serial = __system_property_serial(cache->cache.pinfo);
- __system_property_read(cache->cache.pinfo, 0, cache->property);
-}
-
-/* get boolean with the logger twist that supports eng adjustments */
-bool __android_logger_property_get_bool(const char* key, int flag) {
- struct cache_property property = {{NULL, 0xFFFFFFFF}, {0}};
- if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
- char newkey[strlen("persist.") + strlen(key) + 1];
- snprintf(newkey, sizeof(newkey), "ro.%s", key);
- refresh_cache_property(&property, newkey);
- property.cache.pinfo = NULL;
- property.cache.serial = 0xFFFFFFFF;
- snprintf(newkey, sizeof(newkey), "persist.%s", key);
- refresh_cache_property(&property, newkey);
- property.cache.pinfo = NULL;
- property.cache.serial = 0xFFFFFFFF;
- }
-
- refresh_cache_property(&property, key);
-
- if (check_flag(property.property, "true")) {
- return true;
- }
- if (check_flag(property.property, "false")) {
- return false;
- }
- if (property.property[0]) {
- flag &= ~(BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
- }
- if (check_flag(property.property, "eng")) {
- flag |= BOOL_DEFAULT_FLAG_ENG;
- }
- /* this is really a "not" flag */
- if (check_flag(property.property, "svelte")) {
- flag |= BOOL_DEFAULT_FLAG_SVELTE;
- }
-
- /* Sanity Check */
- if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
- flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
- flag |= BOOL_DEFAULT_TRUE;
- }
-
- if ((flag & BOOL_DEFAULT_FLAG_SVELTE) &&
- __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)) {
- return false;
- }
- if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) {
- return false;
- }
-
- return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
-}
-
-bool __android_logger_valid_buffer_size(unsigned long value) {
- return LOG_BUFFER_MIN_SIZE <= value && value <= LOG_BUFFER_MAX_SIZE;
-}
-
-struct cache2_property_size {
- pthread_mutex_t lock;
- uint32_t serial;
- const char* key_persist;
- struct cache_property cache_persist;
- const char* key_ro;
- struct cache_property cache_ro;
- unsigned long (*const evaluate)(const struct cache2_property_size* self);
-};
-
-static inline unsigned long do_cache2_property_size(struct cache2_property_size* self) {
- uint32_t current_serial;
- int change_detected;
- unsigned long v;
-
- if (pthread_mutex_trylock(&self->lock)) {
- /* We are willing to accept some race in this context */
- return self->evaluate(self);
- }
-
- change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
- current_serial = __system_property_area_serial();
- if (current_serial != self->serial) {
- change_detected = 1;
- }
- if (change_detected) {
- refresh_cache_property(&self->cache_persist, self->key_persist);
- refresh_cache_property(&self->cache_ro, self->key_ro);
- self->serial = current_serial;
- }
- v = self->evaluate(self);
-
- pthread_mutex_unlock(&self->lock);
-
- return v;
-}
-
-static unsigned long property_get_size_from_cache(const struct cache_property* cache) {
- char* cp;
- unsigned long value = strtoul(cache->property, &cp, 10);
-
- switch (*cp) {
- case 'm':
- case 'M':
- value *= 1024;
- [[fallthrough]];
- case 'k':
- case 'K':
- value *= 1024;
- [[fallthrough]];
- case '\0':
- break;
-
- default:
- value = 0;
- }
-
- if (!__android_logger_valid_buffer_size(value)) {
- value = 0;
- }
-
- return value;
-}
-
-static unsigned long evaluate_property_get_size(const struct cache2_property_size* self) {
- unsigned long size = property_get_size_from_cache(&self->cache_persist);
- if (size) {
- return size;
- }
- return property_get_size_from_cache(&self->cache_ro);
-}
-
-unsigned long __android_logger_get_buffer_size(log_id_t logId) {
- static const char global_tunable[] = "persist.logd.size"; /* Settings App */
- static const char global_default[] = "ro.logd.size"; /* BoardConfig.mk */
- static struct cache2_property_size global = {
- /* clang-format off */
- PTHREAD_MUTEX_INITIALIZER, 0,
- global_tunable, { { NULL, 0xFFFFFFFF }, {} },
- global_default, { { NULL, 0xFFFFFFFF }, {} },
- evaluate_property_get_size
- /* clang-format on */
- };
- char key_persist[strlen(global_tunable) + strlen(".security") + 1];
- char key_ro[strlen(global_default) + strlen(".security") + 1];
- struct cache2_property_size local = {
- /* clang-format off */
- PTHREAD_MUTEX_INITIALIZER, 0,
- key_persist, { { NULL, 0xFFFFFFFF }, {} },
- key_ro, { { NULL, 0xFFFFFFFF }, {} },
- evaluate_property_get_size
- /* clang-format on */
- };
- unsigned long property_size, default_size;
-
- default_size = do_cache2_property_size(&global);
- if (!default_size) {
- default_size = __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)
- ? LOG_BUFFER_MIN_SIZE /* 64K */
- : LOG_BUFFER_SIZE; /* 256K */
- }
-
- snprintf(key_persist, sizeof(key_persist), "%s.%s", global_tunable,
- android_log_id_to_name(logId));
- snprintf(key_ro, sizeof(key_ro), "%s.%s", global_default, android_log_id_to_name(logId));
- property_size = do_cache2_property_size(&local);
-
- if (!property_size) {
- property_size = default_size;
- }
-
- if (!property_size) {
- property_size = LOG_BUFFER_SIZE;
- }
-
- return property_size;
-}
-
#else
int __android_log_is_loggable(int prio, const char*, int) {
@@ -613,4 +382,4 @@
return 1;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 2a6424b..a17d90c 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -97,6 +97,7 @@
cflags: ["-DNO_PSTORE"],
test_suites: [
"cts",
+ "device-tests",
"vts10",
],
}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3bd5cf2..d2f12d6 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -879,30 +879,6 @@
}
BENCHMARK(BM_lookupEventFormat);
-/*
- * Measure the time it takes for android_lookupEventTagNum plus above
- */
-static void BM_lookupEventTagNum(benchmark::State& state) {
- prechargeEventMap();
-
- std::unordered_set<uint32_t>::const_iterator it = set.begin();
-
- while (state.KeepRunning()) {
- size_t len;
- const char* name = android_lookupEventTag_len(map, &len, (*it));
- std::string Name(name, len);
- const char* format = android_lookupEventFormat_len(map, &len, (*it));
- std::string Format(format, len);
- state.ResumeTiming();
- android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
- ANDROID_LOG_UNKNOWN);
- state.PauseTiming();
- ++it;
- if (it == set.end()) it = set.begin();
- }
-}
-BENCHMARK(BM_lookupEventTagNum);
-
// Must be functionally identical to liblog internal SendLogdControlMessage()
static void send_to_control(char* buf, size_t len) {
int sock =
diff --git a/liblog/tests/liblog_global_state.cpp b/liblog/tests/liblog_global_state.cpp
index 3508818..1d7ff9f 100644
--- a/liblog/tests/liblog_global_state.cpp
+++ b/liblog/tests/liblog_global_state.cpp
@@ -153,56 +153,65 @@
message_seen = false;
}
+static std::string UniqueLogTag() {
+ std::string tag = LOG_TAG;
+ tag += "-" + std::to_string(getpid());
+ return tag;
+}
+
TEST(liblog_global_state, is_loggable_both_default) {
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ auto tag = UniqueLogTag();
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
}
TEST(liblog_global_state, is_loggable_minimum_log_priority_only) {
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ auto tag = UniqueLogTag();
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
EXPECT_EQ(android::base::WARNING, android::base::SetMinimumLogSeverity(android::base::DEBUG));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
EXPECT_EQ(android::base::DEBUG, android::base::SetMinimumLogSeverity(android::base::WARNING));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
}
TEST(liblog_global_state, is_loggable_tag_log_priority_only) {
#ifdef __ANDROID__
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ auto tag = UniqueLogTag();
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
- auto log_tag_property = std::string("log.tag.") + LOG_TAG;
- android::base::SetProperty(log_tag_property, "d");
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ auto log_tag_property = std::string("log.tag.") + tag;
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, "d"));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
- android::base::SetProperty(log_tag_property, "w");
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, "w"));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
- android::base::SetProperty(log_tag_property, "");
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, ""));
#else
GTEST_SKIP() << "No log tag properties on host";
#endif
@@ -210,39 +219,40 @@
TEST(liblog_global_state, is_loggable_both_set) {
#ifdef __ANDROID__
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ auto tag = UniqueLogTag();
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
// When both a tag and a minimum priority are set, we use the lower value of the two.
// tag = warning, minimum_priority = debug, expect 'debug'
- auto log_tag_property = std::string("log.tag.") + LOG_TAG;
- android::base::SetProperty(log_tag_property, "w");
+ auto log_tag_property = std::string("log.tag.") + tag;
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, "w"));
EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
// tag = warning, minimum_priority = warning, expect 'warning'
EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
// tag = debug, minimum_priority = warning, expect 'debug'
- android::base::SetProperty(log_tag_property, "d");
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, "d"));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
// tag = debug, minimum_priority = debug, expect 'debug'
EXPECT_EQ(ANDROID_LOG_WARN, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
- android::base::SetProperty(log_tag_property, "");
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, ""));
#else
GTEST_SKIP() << "No log tag properties on host";
#endif
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index fbc3d7a..c49d87b 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -2768,20 +2768,3 @@
#endif
}
#endif // ENABLE_FLAKY_TESTS
-
-TEST(liblog, android_lookupEventTagNum) {
-#ifdef __ANDROID__
- EventTagMap* map = android_openEventTagMap(NULL);
- EXPECT_TRUE(NULL != map);
- std::string Name = android::base::StringPrintf("a%d", getpid());
- int tag = android_lookupEventTagNum(map, Name.c_str(), "(new|1)",
- ANDROID_LOG_UNKNOWN);
- android_closeEventTagMap(map);
- if (tag == -1) system("tail -3 /dev/event-log-tags >&2");
- EXPECT_NE(-1, tag);
- EXPECT_NE(0, tag);
- EXPECT_GT(UINT32_MAX, (unsigned)tag);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 3e09617..7acd363 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -20,6 +20,7 @@
#include <string>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android/log.h> // minimal logging API
#include <gtest/gtest.h>
@@ -29,6 +30,8 @@
// Do not use anything in log/log_time.h despite side effects of the above.
#include <private/android_logger.h>
+using android::base::GetBoolProperty;
+
TEST(liblog, android_logger_get_) {
#ifdef __ANDROID__
// This test assumes the log buffers are filled with noise from
@@ -38,31 +41,27 @@
for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
log_id_t id = static_cast<log_id_t>(i);
- const char* name = android_log_id_to_name(id);
- if (id != android_name_to_log_id(name)) {
- continue;
- }
- fprintf(stderr, "log buffer %s\r", name);
+ std::string name = android_log_id_to_name(id);
+ fprintf(stderr, "log buffer %s\r", name.c_str());
struct logger* logger;
EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
EXPECT_EQ(id, android_logger_get_id(logger));
ssize_t get_log_size = android_logger_get_log_size(logger);
/* security buffer is allowed to be denied */
- if (strcmp("security", name)) {
- EXPECT_LT(0, get_log_size);
+ if (name != "security") {
+ EXPECT_GT(get_log_size, 0);
// crash buffer is allowed to be empty, that is actually healthy!
- // kernel buffer is allowed to be empty on "user" builds
- // stats buffer is allowed to be empty TEMPORARILY.
- // TODO: remove stats buffer from here once we start to use it in
- // framework (b/68266385).
- EXPECT_LE( // boolean 1 or 0 depending on expected content or empty
- !!((strcmp("crash", name) != 0) &&
- ((strcmp("kernel", name) != 0) ||
- __android_logger_property_get_bool(
- "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG |
- BOOL_DEFAULT_FLAG_SVELTE)) &&
- (strcmp("stats", name) != 0)),
- android_logger_get_log_readable_size(logger));
+ // stats buffer is no longer in use.
+ if (name == "crash" || name == "stats") {
+ continue;
+ }
+
+ // kernel buffer is empty if ro.logd.kernel is false
+ if (name == "kernel" && !GetBoolProperty("ro.logd.kernel", false)) {
+ continue;
+ }
+
+ EXPECT_LE(0, android_logger_get_log_readable_size(logger));
} else {
EXPECT_NE(0, get_log_size);
if (get_log_size < 0) {
@@ -71,7 +70,6 @@
EXPECT_LE(0, android_logger_get_log_readable_size(logger));
}
}
- EXPECT_LT(0, android_logger_get_log_version(logger));
}
android_logger_list_close(logger_list);
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index bbdd317..ceabf62 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -336,7 +336,6 @@
}
ParseKernelCmdlineOptions();
- android::base::SetMinimumLogSeverity(android::base::INFO);
}
void Modprobe::EnableBlocklist(bool enable) {
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 0341902..4518487 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -39,19 +39,21 @@
"Mode": "0755",
"UID": "system",
"GID": "system"
- },
- {
- "Controller": "freezer",
- "Path": "/dev/freezer",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
}
],
"Cgroups2": {
- "Path": "/dev/cg2_bpf",
- "Mode": "0600",
- "UID": "root",
- "GID": "root"
+ "Path": "/sys/fs/cgroup",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system",
+ "Controllers": [
+ {
+ "Controller": "freezer",
+ "Path": "freezer",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ }
+ ]
}
}
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
index f4070c5..13adcae 100644
--- a/libprocessgroup/profiles/cgroups.proto
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -24,19 +24,24 @@
Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
}
-// Next: 6
+// Next: 7
message Cgroup {
string controller = 1 [json_name = "Controller"];
string path = 2 [json_name = "Path"];
string mode = 3 [json_name = "Mode"];
string uid = 4 [json_name = "UID"];
string gid = 5 [json_name = "GID"];
+// Booleans default to false when not specified. File reconstruction fails
+// when a boolean is specified as false, so leave unspecified in that case
+// https://developers.google.com/protocol-buffers/docs/proto3#default
+ bool needs_activation = 6 [json_name = "NeedsActivation"];
}
-// Next: 5
+// Next: 6
message Cgroups2 {
string path = 1 [json_name = "Path"];
string mode = 2 [json_name = "Mode"];
string uid = 3 [json_name = "UID"];
string gid = 4 [json_name = "GID"];
+ repeated Cgroup controllers = 5 [json_name = "Controllers"];
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index bc6bc7c..c4dbf8e 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -53,7 +53,7 @@
{
"Name": "FreezerState",
"Controller": "freezer",
- "File": "frozen/freezer.state"
+ "File": "cgroup.freeze"
}
],
@@ -79,7 +79,7 @@
"Params":
{
"Controller": "freezer",
- "Path": "frozen"
+ "Path": ""
}
}
]
@@ -92,7 +92,7 @@
"Params":
{
"Controller": "freezer",
- "Path": ""
+ "Path": "../"
}
}
]
@@ -538,27 +538,27 @@
]
},
{
- "Name": "FreezerThawed",
+ "Name": "FreezerDisabled",
"Actions": [
{
"Name": "SetAttribute",
"Params":
{
"Name": "FreezerState",
- "Value": "THAWED"
+ "Value": "0"
}
}
]
},
{
- "Name": "FreezerFrozen",
+ "Name": "FreezerEnabled",
"Actions": [
{
"Name": "SetAttribute",
"Params":
{
"Name": "FreezerState",
- "Value": "FROZEN"
+ "Value": "1"
}
}
]
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
index f3d8022..6229e7c 100644
--- a/libsparse/backed_block.cpp
+++ b/libsparse/backed_block.cpp
@@ -25,7 +25,7 @@
struct backed_block {
unsigned int block;
- unsigned int len;
+ uint64_t len;
enum backed_block_type type;
union {
struct {
@@ -60,7 +60,7 @@
return bb->next;
}
-unsigned int backed_block_len(struct backed_block* bb) {
+uint64_t backed_block_len(struct backed_block* bb) {
return bb->len;
}
@@ -270,7 +270,7 @@
}
/* Queues a fill block of memory to be written to the specified data blocks */
-int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t len,
unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
@@ -287,7 +287,7 @@
}
/* Queues a block of memory to be written to the specified data blocks */
-int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+int backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t len,
unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
@@ -305,7 +305,7 @@
/* Queues a chunk of a file on disk to be written to the specified data blocks */
int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
- unsigned int len, unsigned int block) {
+ uint64_t len, unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
return -ENOMEM;
@@ -322,7 +322,7 @@
}
/* Queues a chunk of a fd to be written to the specified data blocks */
-int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,
unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 3a75460..71a8969 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -29,18 +29,18 @@
BACKED_BLOCK_FILL,
};
-int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+int backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t len,
unsigned int block);
-int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t len,
unsigned int block);
int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
- unsigned int len, unsigned int block);
-int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+ uint64_t len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,
unsigned int block);
struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
struct backed_block* backed_block_iter_next(struct backed_block* bb);
-unsigned int backed_block_len(struct backed_block* bb);
+uint64_t backed_block_len(struct backed_block* bb);
unsigned int backed_block_block(struct backed_block* bb);
void* backed_block_data(struct backed_block* bb);
const char* backed_block_filename(struct backed_block* bb);
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 3d5fb0c..2f75349 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -75,8 +75,7 @@
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_data(struct sparse_file *s,
- void *data, unsigned int len, unsigned int block);
+int sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsigned int block);
/**
* sparse_file_add_fill - associate a fill chunk with a sparse file
@@ -93,8 +92,8 @@
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_fill(struct sparse_file *s,
- uint32_t fill_val, unsigned int len, unsigned int block);
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,
+ unsigned int block);
/**
* sparse_file_add_file - associate a chunk of a file with a sparse file
@@ -116,9 +115,8 @@
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_file(struct sparse_file *s,
- const char *filename, int64_t file_offset, unsigned int len,
- unsigned int block);
+int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
+ uint64_t len, unsigned int block);
/**
* sparse_file_add_file - associate a chunk of a file with a sparse file
@@ -143,8 +141,8 @@
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_fd(struct sparse_file *s,
- int fd, int64_t file_offset, unsigned int len, unsigned int block);
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, uint64_t len,
+ unsigned int block);
/**
* sparse_file_write - write a sparse file to a file
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index b883c13..b2c5407 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -65,9 +65,9 @@
};
struct sparse_file_ops {
- int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
- int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
- int (*write_skip_chunk)(struct output_file* out, int64_t len);
+ int (*write_data_chunk)(struct output_file* out, uint64_t len, void* data);
+ int (*write_fill_chunk)(struct output_file* out, uint64_t len, uint32_t fill_val);
+ int (*write_skip_chunk)(struct output_file* out, uint64_t len);
int (*write_end_chunk)(struct output_file* out);
};
@@ -316,7 +316,7 @@
return 0;
}
-static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
+static int write_sparse_skip_chunk(struct output_file* out, uint64_t skip_len) {
chunk_header_t chunk_header;
int ret;
@@ -340,9 +340,10 @@
return 0;
}
-static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+static int write_sparse_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
chunk_header_t chunk_header;
- int rnd_up_len, count;
+ uint64_t rnd_up_len;
+ int count;
int ret;
/* Round up the fill length to a multiple of the block size */
@@ -370,9 +371,9 @@
return 0;
}
-static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) {
+static int write_sparse_data_chunk(struct output_file* out, uint64_t len, void* data) {
chunk_header_t chunk_header;
- int rnd_up_len, zero_len;
+ uint64_t rnd_up_len, zero_len;
int ret;
/* Round up the data length to a multiple of the block size */
@@ -437,9 +438,9 @@
.write_end_chunk = write_sparse_end_chunk,
};
-static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) {
+static int write_normal_data_chunk(struct output_file* out, uint64_t len, void* data) {
int ret;
- unsigned int rnd_up_len = ALIGN(len, out->block_size);
+ uint64_t rnd_up_len = ALIGN(len, out->block_size);
ret = out->ops->write(out, data, len);
if (ret < 0) {
@@ -453,10 +454,10 @@
return ret;
}
-static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+static int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
int ret;
unsigned int i;
- unsigned int write_len;
+ uint64_t write_len;
/* Initialize fill_buf with the fill_val */
for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
@@ -464,7 +465,7 @@
}
while (len) {
- write_len = std::min(len, out->block_size);
+ write_len = std::min(len, (uint64_t)out->block_size);
ret = out->ops->write(out, out->fill_buf, write_len);
if (ret < 0) {
return ret;
@@ -476,7 +477,7 @@
return 0;
}
-static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
+static int write_normal_skip_chunk(struct output_file* out, uint64_t len) {
return out->ops->skip(out, len);
}
@@ -639,16 +640,16 @@
}
/* Write a contiguous region of data blocks from a memory buffer */
-int write_data_chunk(struct output_file* out, unsigned int len, void* data) {
+int write_data_chunk(struct output_file* out, uint64_t len, void* data) {
return out->sparse_ops->write_data_chunk(out, len, data);
}
/* Write a contiguous region of data blocks with a fill value */
-int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+int write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
return out->sparse_ops->write_fill_chunk(out, len, fill_val);
}
-int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
+int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ);
if (!m) return -errno;
@@ -656,7 +657,7 @@
}
/* Write a contiguous region of data blocks from a file */
-int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset) {
+int write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset) {
int ret;
int file_fd = open(file, O_RDONLY | O_BINARY);
@@ -671,6 +672,6 @@
return ret;
}
-int write_skip_chunk(struct output_file* out, int64_t len) {
+int write_skip_chunk(struct output_file* out, uint64_t len) {
return out->sparse_ops->write_skip_chunk(out, len);
}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 278430b..ecbcdf3 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -30,11 +30,11 @@
struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
unsigned int block_size, int64_t len, int gz,
int sparse, int chunks, int crc);
-int write_data_chunk(struct output_file* out, unsigned int len, void* data);
-int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
-int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
-int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset);
-int write_skip_chunk(struct output_file* out, int64_t len);
+int write_data_chunk(struct output_file* out, uint64_t len, void* data);
+int write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val);
+int write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset);
+int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset);
+int write_skip_chunk(struct output_file* out, uint64_t len);
void output_file_close(struct output_file* out);
int read_all(int fd, void* buf, size_t len);
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index 8622b4c..396e7eb 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -50,21 +50,21 @@
free(s);
}
-int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
+int sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsigned int block) {
return backed_block_add_data(s->backed_block_list, data, len, block);
}
-int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,
unsigned int block) {
return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
}
int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
- unsigned int len, unsigned int block) {
+ uint64_t len, unsigned int block) {
return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
}
-int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, uint64_t len,
unsigned int block) {
return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
}
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 9c1621b..3b6cfd8 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -185,7 +185,6 @@
if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
return false;
- // Sanity check.
int type = nh->nlmsg_type;
if (type != RTM_NEWADDR && type != RTM_DELADDR) {
SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type);
@@ -349,7 +348,6 @@
uint8_t type = nh->nlmsg_type;
const char *msgname = rtMessageName(type);
- // Sanity check.
if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
SLOGE("%s: incorrect message type %d (%s)\n", __func__, type, msgname);
return false;
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index fe2f3d6..e90afcd 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -201,50 +201,31 @@
return 0;
}
- int ret = 0;
- int e = 0; // SLOGW and sigaction are not inert regarding errno
int current = 0;
- struct sigaction new_action, old_action;
- memset(&new_action, 0, sizeof(new_action));
- new_action.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &new_action, &old_action);
-
for (;;) {
- ssize_t rc = TEMP_FAILURE_RETRY(
- writev(mSocket, iov + current, iovcnt - current));
-
- if (rc > 0) {
- size_t written = rc;
- while ((current < iovcnt) && (written >= iov[current].iov_len)) {
- written -= iov[current].iov_len;
- current++;
- }
- if (current == iovcnt) {
- break;
- }
- iov[current].iov_base = (char *)iov[current].iov_base + written;
- iov[current].iov_len -= written;
- continue;
- }
+ ssize_t rc = TEMP_FAILURE_RETRY(writev(mSocket, iov + current, iovcnt - current));
if (rc == 0) {
- e = EIO;
+ errno = EIO;
SLOGW("0 length write :(");
- } else {
- e = errno;
- SLOGW("write error (%s)", strerror(e));
+ return -1;
+ } else if (rc < 0) {
+ SLOGW("write error (%s)", strerror(errno));
+ return -1;
}
- ret = -1;
- break;
- }
- sigaction(SIGPIPE, &old_action, &new_action);
-
- if (e != 0) {
- errno = e;
+ size_t written = rc;
+ while (current < iovcnt && written >= iov[current].iov_len) {
+ written -= iov[current].iov_len;
+ current++;
+ }
+ if (current == iovcnt) {
+ return 0;
+ }
+ iov[current].iov_base = (char*)iov[current].iov_base + written;
+ iov[current].iov_len -= written;
}
- return ret;
}
void SocketClient::incRef() {
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 9e2a3cd..bf86e6e 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -465,13 +465,9 @@
eval_info->return_address_undefined = true;
}
break;
- case DWARF_LOCATION_PSEUDO_REGISTER: {
- if (!eval_info->regs_info.regs->SetPseudoRegister(reg, loc->values[0])) {
- last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
- return false;
- }
- break;
- }
+ case DWARF_LOCATION_PSEUDO_REGISTER:
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
default:
break;
}
@@ -543,11 +539,15 @@
// Skip this unknown register.
continue;
}
- }
-
- reg_ptr = eval_info.regs_info.Save(reg);
- if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
- return false;
+ if (!eval_info.regs_info.regs->SetPseudoRegister(reg, entry.second.values[0])) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ } else {
+ reg_ptr = eval_info.regs_info.Save(reg);
+ if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
+ return false;
+ }
}
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 2d867cd..57806c1 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -75,6 +75,7 @@
frame->rel_pc = dex_pc - info->start;
} else {
frame->rel_pc = dex_pc;
+ warnings_ |= WARNING_DEX_PC_NOT_IN_MAP;
return;
}
@@ -142,6 +143,7 @@
void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
const std::vector<std::string>* map_suffixes_to_ignore) {
frames_.clear();
+ warnings_ = WARNING_NONE;
last_error_.code = ERROR_NONE;
last_error_.address = 0;
elf_from_memory_not_file_ = false;
@@ -395,18 +397,20 @@
return true;
}
-FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
+FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc, ArchEnum arch, Maps* maps,
+ JitDebug* jit_debug,
+ std::shared_ptr<Memory> process_memory,
+ bool resolve_names) {
FrameData frame;
- Maps* maps = GetMaps();
MapInfo* map_info = maps->Find(pc);
- if (!map_info) {
+ if (map_info == nullptr || arch == ARCH_UNKNOWN) {
+ frame.pc = pc;
frame.rel_pc = pc;
return frame;
}
- ArchEnum arch = Regs::CurrentArch();
- Elf* elf = map_info->GetElf(GetProcessMemory(), arch);
+ Elf* elf = map_info->GetElf(process_memory, arch);
uint64_t relative_pc = elf->GetRelPc(pc, map_info);
@@ -416,10 +420,9 @@
uint64_t debug_pc = relative_pc;
// If we don't have a valid ELF file, check the JIT.
- if (!elf->valid()) {
- JitDebug jit_debug(GetProcessMemory());
+ if (!elf->valid() && jit_debug != nullptr) {
uint64_t jit_pc = pc - pc_adjustment;
- Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
+ Elf* jit_elf = jit_debug->GetElf(maps, jit_pc);
if (jit_elf != nullptr) {
debug_pc = jit_pc;
elf = jit_elf;
@@ -437,12 +440,17 @@
frame.map_flags = map_info->flags;
frame.map_load_bias = elf->GetLoadBias();
- if (!resolve_names_ ||
- !elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
+ if (!resolve_names ||
+ !elf->GetFunctionName(debug_pc, &frame.function_name, &frame.function_offset)) {
frame.function_name = "";
frame.function_offset = 0;
}
return frame;
}
+FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
+ return BuildFrameFromPcOnly(pc, regs_ ? regs_->Arch() : ARCH_UNKNOWN, maps_, jit_debug_,
+ process_memory_, resolve_names_);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 72ec454..66fefe7 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -21,6 +21,13 @@
namespace unwindstack {
+// A bit map of warnings, multiple warnings can be set at the same time.
+enum WarningCode : uint64_t {
+ WARNING_NONE = 0,
+ WARNING_DEX_PC_NOT_IN_MAP = 0x1, // A dex pc was found, but it doesn't exist
+ // in any valid map.
+};
+
enum ErrorCode : uint8_t {
ERROR_NONE, // No error.
ERROR_MEMORY_INVALID, // Memory read failed.
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 4d49f23..3df8aad 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -113,12 +113,15 @@
ErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
+ uint64_t warnings() { return warnings_; }
// Builds a frame for symbolization using the maps from this unwinder. The
// constructed frame contains just enough information to be used to symbolize
// frames collected by frame-pointer unwinding that's done outside of
// libunwindstack. This is used by tombstoned to symbolize frame pointer-based
// stack traces that are collected by tools such as GWP-ASan and MTE.
+ static FrameData BuildFrameFromPcOnly(uint64_t pc, ArchEnum arch, Maps* maps, JitDebug* jit_debug,
+ std::shared_ptr<Memory> process_memory, bool resolve_names);
FrameData BuildFrameFromPcOnly(uint64_t pc);
protected:
@@ -141,6 +144,7 @@
// file. This is only true if there is an actual file backing up the elf.
bool elf_from_memory_not_file_ = false;
ErrorData last_error_;
+ uint64_t warnings_;
};
class UnwinderFromPid : public Unwinder {
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index d57cd33..a08a8d0 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -492,6 +492,40 @@
EXPECT_EQ(0x80000000U, regs.pc());
}
+TYPED_TEST_P(DwarfSectionImplTest, Eval_pseudo_register_invalid) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ regs.set_pseudo_reg(11);
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_PSEUDO_REGISTER, {20, 0}};
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+
+ loc_regs.clear();
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[12] = DwarfLocation{DWARF_LOCATION_PSEUDO_REGISTER, {20, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_pseudo_register) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ regs.set_pseudo_reg(11);
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[11] = DwarfLocation{DWARF_LOCATION_PSEUDO_REGISTER, {20, 0}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ uint64_t pseudo_value = 0;
+ ASSERT_TRUE(regs.GetPseudoRegister(11, &pseudo_value));
+ EXPECT_EQ(20U, pseudo_value);
+}
+
TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
DwarfCie cie{};
cie.cfa_instructions_offset = 0x3000;
@@ -581,6 +615,7 @@
Eval_invalid_register, Eval_different_reg_locations,
Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+ Eval_pseudo_register_invalid, Eval_pseudo_register,
GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index 75fc9d0..f67d7dc 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -83,15 +83,33 @@
uint64_t sp() override { return fake_sp_; }
void set_pc(uint64_t pc) override { fake_pc_ = pc; }
void set_sp(uint64_t sp) override { fake_sp_ = sp; }
+ void set_pseudo_reg(uint64_t reg) { fake_pseudo_reg_ = reg; }
bool SetPcFromReturnAddress(Memory*) override { return false; }
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+ bool SetPseudoRegister(uint16_t reg, uint64_t value) override {
+ if (fake_pseudo_reg_ != reg) {
+ return false;
+ }
+ fake_pseudo_reg_value_ = value;
+ return true;
+ }
+ bool GetPseudoRegister(uint16_t reg, uint64_t* value) override {
+ if (fake_pseudo_reg_ != reg) {
+ return false;
+ }
+ *value = fake_pseudo_reg_value_;
+ return true;
+ }
+
Regs* Clone() override { return nullptr; }
private:
uint64_t fake_pc_ = 0;
uint64_t fake_sp_ = 0;
+ uint16_t fake_pseudo_reg_ = 0;
+ uint64_t fake_pseudo_reg_value_ = 0;
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index dd33aa9..915f248 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -37,6 +37,7 @@
#include <unwindstack/Unwinder.h>
#include "ElfFake.h"
+#include "ElfTestUtils.h"
#include "MemoryFake.h"
#include "RegsFake.h"
@@ -44,23 +45,31 @@
class UnwinderTest : public ::testing::Test {
protected:
- static void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
- const char* name, Elf* elf = nullptr) {
+ static MapInfo* AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const char* name, Elf* elf = nullptr) {
std::string str_name(name);
maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
+ MapInfo* map_info = maps_->Find(start);
if (elf != nullptr) {
- const auto& map_info = *--maps_->end();
map_info->elf.reset(elf);
}
+ return map_info;
}
static void SetUpTestSuite() {
maps_.reset(new Maps);
- ElfFake* elf = new ElfFake(new MemoryFake);
- ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
- interface_fake->FakeSetBuildID("FAKE");
- elf->FakeSetInterface(interface_fake);
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ ElfFake* elf;
+ ElfInterfaceFake* interface;
+ MapInfo* map_info;
+
+ elf = new ElfFake(new MemoryFake);
+ interface = new ElfInterfaceFake(nullptr);
+ interface->FakeSetBuildID("FAKE");
+ elf->FakeSetInterface(interface);
AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf);
AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
@@ -81,19 +90,17 @@
AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
elf = new ElfFake(new MemoryFake);
- ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+ interface = new ElfInterfaceFake(nullptr);
interface->FakeSetSoname("lib_fake.so");
elf->FakeSetInterface(interface);
- AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
- MapInfo* map_info = maps_->Find(0x43000);
- ASSERT_TRUE(map_info != nullptr);
+ map_info = AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
map_info->elf_start_offset = 0x1d000;
AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
- AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
- const auto& info = *--maps_->end();
- info->load_bias = 0;
+ map_info =
+ AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+ map_info->load_bias = 0;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
@@ -103,40 +110,76 @@
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
- elf);
- const auto& info2 = *--maps_->end();
- info2->elf_offset = 0x8000;
+ map_info = AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/fake/fake_offset.oat", elf);
+ map_info->elf_offset = 0x8000;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- AddMapInfo(0xc0000, 0xc1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/unreadable.so", elf);
- const auto& info3 = *--maps_->end();
- info3->memory_backed_elf = true;
+ map_info = AddMapInfo(0xc0000, 0xc1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/fake/unreadable.so", elf);
+ map_info->memory_backed_elf = true;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- AddMapInfo(0xc1000, 0xc2000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "[vdso]", elf);
- const auto& info4 = *--maps_->end();
- info4->memory_backed_elf = true;
+ map_info = AddMapInfo(0xc1000, 0xc2000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "[vdso]", elf);
+ map_info->memory_backed_elf = true;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- AddMapInfo(0xc2000, 0xc3000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "", elf);
- const auto& info5 = *--maps_->end();
- info5->memory_backed_elf = true;
+ map_info = AddMapInfo(0xc2000, 0xc3000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "", elf);
+ map_info->memory_backed_elf = true;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- AddMapInfo(0xc3000, 0xc4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/memfd:/jit-cache", elf);
- const auto& info6 = *--maps_->end();
- info6->memory_backed_elf = true;
+ map_info = AddMapInfo(0xc3000, 0xc4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/memfd:/jit-cache", elf);
+ map_info->memory_backed_elf = true;
- AddMapInfo(0xd0000, 0xd1000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.apk");
- const auto& info7 = *--maps_->end();
- info7->load_bias = 0;
+ map_info =
+ AddMapInfo(0xd0000, 0xd1000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.apk");
+ map_info->load_bias = 0;
- process_memory_.reset(new MemoryFake);
+ elf = new ElfFake(new MemoryFake);
+ interface = new ElfInterfaceFake(nullptr);
+ elf->FakeSetInterface(interface);
+ interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x1800);
+ interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x1900);
+ interface->FakeSetDataOffset(0x1000);
+ interface->FakeSetDataVaddrStart(0x1000);
+ interface->FakeSetDataVaddrEnd(0x8000);
+ AddMapInfo(0xf0000, 0xf1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/global.so", elf);
+ AddMapInfo(0xf1000, 0xf9000, 0x1000, PROT_READ | PROT_WRITE, "/fake/global.so");
+ // dex debug data
+ memory_->SetData32(0xf180c, 0xf3000);
+ memory_->SetData32(0xf3000, 0xf4000);
+ memory_->SetData32(0xf3004, 0xf4000);
+ memory_->SetData32(0xf3008, 0xf5000);
+ // jit debug data
+ memory_->SetData32(0xf1900, 1);
+ memory_->SetData32(0xf1904, 0);
+ memory_->SetData32(0xf1908, 0xf6000);
+ memory_->SetData32(0xf190c, 0xf6000);
+ memory_->SetData32(0xf6000, 0);
+ memory_->SetData32(0xf6004, 0);
+ memory_->SetData32(0xf6008, 0xf7000);
+ memory_->SetData32(0xf600c, 0);
+ memory_->SetData64(0xf6010, 0x1000);
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetValid(false);
+ elf->FakeSetLoadBias(0x300);
+ map_info = AddMapInfo(0x100000, 0x101000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/fake/jit.so", elf);
+ map_info->elf_start_offset = 0x100;
+ map_info->offset = 0x200;
+
+#if 0
+ elf = new ElfFake(new MemoryFake);
+ interface = new ElfInterfaceFake(nullptr);
+ interface->FakePushFunctionData(FunctionData("Fake0", 10));
+ AddMapInfo(0x110000, 0x111000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/elf.so", elf);
+#endif
}
void SetUp() override {
@@ -147,11 +190,13 @@
static std::unique_ptr<Maps> maps_;
static RegsFake regs_;
+ static MemoryFake* memory_;
static std::shared_ptr<Memory> process_memory_;
};
std::unique_ptr<Maps> UnwinderTest::maps_;
RegsFake UnwinderTest::regs_(5);
+MemoryFake* UnwinderTest::memory_;
std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
TEST_F(UnwinderTest, multiple_frames) {
@@ -168,6 +213,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -233,6 +279,7 @@
unwinder.SetResolveNames(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -293,6 +340,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -323,6 +371,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -353,6 +402,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -384,6 +434,7 @@
unwinder.SetEmbeddedSoname(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -421,6 +472,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -454,6 +506,7 @@
Unwinder unwinder(20, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(20U, unwinder.NumFrames());
@@ -497,6 +550,7 @@
std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
unwinder.Unwind(&skip_libs);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -559,6 +613,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -607,6 +662,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -627,6 +683,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -642,6 +699,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -679,6 +737,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -745,6 +804,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -795,6 +855,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -843,6 +904,7 @@
std::vector<std::string> skip_names{"libanother.so"};
unwinder.Unwind(&skip_names);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(0U, unwinder.NumFrames());
@@ -866,6 +928,7 @@
std::vector<std::string> suffixes{"oat"};
unwinder.Unwind(nullptr, &suffixes);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -925,6 +988,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -984,6 +1048,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1028,6 +1093,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1072,6 +1138,54 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50000U, frame->rel_pc);
+ EXPECT_EQ(0x50000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_not_in_map_valid_dex_files) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0x50000);
+
+ DexFiles dex_files(process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.SetDexFiles(&dex_files, ARCH_ARM);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1119,6 +1233,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -1178,6 +1293,7 @@
Unwinder unwinder(1, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1208,6 +1324,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_TRUE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1238,6 +1355,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1268,6 +1386,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1298,6 +1417,7 @@
Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1474,4 +1594,161 @@
}
}
+TEST_F(UnwinderTest, build_frame_pc_only_errors) {
+ RegsFake regs(10);
+ regs.FakeSetArch(ARCH_ARM);
+ Unwinder unwinder(10, maps_.get(), ®s, process_memory_);
+
+ FrameData frame;
+
+ // Pc not in map
+ frame = unwinder.BuildFrameFromPcOnly(0x10);
+ EXPECT_EQ(0x10U, frame.pc);
+ EXPECT_EQ(0x10U, frame.rel_pc);
+
+ // No regs set
+ unwinder.SetRegs(nullptr);
+ frame = unwinder.BuildFrameFromPcOnly(0x100310);
+ EXPECT_EQ(0x100310U, frame.pc);
+ EXPECT_EQ(0x100310U, frame.rel_pc);
+ unwinder.SetRegs(®s);
+
+ // Invalid elf
+ frame = unwinder.BuildFrameFromPcOnly(0x100310);
+ EXPECT_EQ(0x10030eU, frame.pc);
+ EXPECT_EQ(0x60eU, frame.rel_pc);
+ EXPECT_EQ("/fake/jit.so", frame.map_name);
+ EXPECT_EQ(0x100U, frame.map_elf_start_offset);
+ EXPECT_EQ(0x200U, frame.map_exact_offset);
+ EXPECT_EQ(0x100000U, frame.map_start);
+ EXPECT_EQ(0x101000U, frame.map_end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_flags);
+ EXPECT_EQ(0x300U, frame.map_load_bias);
+ EXPECT_EQ("", frame.function_name);
+ EXPECT_EQ(0U, frame.function_offset);
+}
+
+TEST_F(UnwinderTest, build_frame_pc_valid_elf) {
+ RegsFake regs(10);
+ regs.FakeSetArch(ARCH_ARM);
+ Unwinder unwinder(10, maps_.get(), ®s, process_memory_);
+
+ FrameData frame;
+
+ // Valid elf, no function data.
+ frame = unwinder.BuildFrameFromPcOnly(0x1010);
+ EXPECT_EQ(0x100cU, frame.pc);
+ EXPECT_EQ(0xcU, frame.rel_pc);
+ EXPECT_EQ("/system/fake/libc.so", frame.map_name);
+ EXPECT_EQ(0U, frame.map_elf_start_offset);
+ EXPECT_EQ(0U, frame.map_exact_offset);
+ EXPECT_EQ(0x1000U, frame.map_start);
+ EXPECT_EQ(0x8000U, frame.map_end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
+ EXPECT_EQ(0U, frame.map_load_bias);
+ EXPECT_EQ("", frame.function_name);
+ EXPECT_EQ(0U, frame.function_offset);
+
+ // Valid elf, function data present, but do not resolve.
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
+ unwinder.SetResolveNames(false);
+
+ frame = unwinder.BuildFrameFromPcOnly(0x1010);
+ EXPECT_EQ(0x100cU, frame.pc);
+ EXPECT_EQ(0xcU, frame.rel_pc);
+ EXPECT_EQ("/system/fake/libc.so", frame.map_name);
+ EXPECT_EQ(0U, frame.map_elf_start_offset);
+ EXPECT_EQ(0U, frame.map_exact_offset);
+ EXPECT_EQ(0x1000U, frame.map_start);
+ EXPECT_EQ(0x8000U, frame.map_end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
+ EXPECT_EQ(0U, frame.map_load_bias);
+ EXPECT_EQ("", frame.function_name);
+ EXPECT_EQ(0U, frame.function_offset);
+
+ // Valid elf, function data present.
+ unwinder.SetResolveNames(true);
+
+ frame = unwinder.BuildFrameFromPcOnly(0x1010);
+ EXPECT_EQ(0x100cU, frame.pc);
+ EXPECT_EQ(0xcU, frame.rel_pc);
+ EXPECT_EQ("/system/fake/libc.so", frame.map_name);
+ EXPECT_EQ(0U, frame.map_elf_start_offset);
+ EXPECT_EQ(0U, frame.map_exact_offset);
+ EXPECT_EQ(0x1000U, frame.map_start);
+ EXPECT_EQ(0x8000U, frame.map_end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
+ EXPECT_EQ(0U, frame.map_load_bias);
+ EXPECT_EQ("Frame0", frame.function_name);
+ EXPECT_EQ(10U, frame.function_offset);
+}
+
+TEST_F(UnwinderTest, build_frame_pc_in_jit) {
+ // Create the elf data for the jit debug information.
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ ehdr.e_phoff = 0x50;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ ehdr.e_shoff = 0x100;
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ ehdr.e_shnum = 3;
+ memory_->SetMemory(0xf7000, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr = {};
+ phdr.p_flags = PF_X;
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x100000;
+ phdr.p_vaddr = 0x100000;
+ phdr.p_memsz = 0x1000;
+ memory_->SetMemory(0xf7050, &phdr, sizeof(phdr));
+
+ Elf32_Shdr shdr = {};
+ shdr.sh_type = SHT_NULL;
+ memory_->SetMemory(0xf7100, &shdr, sizeof(shdr));
+
+ shdr.sh_type = SHT_SYMTAB;
+ shdr.sh_link = 2;
+ shdr.sh_addr = 0x300;
+ shdr.sh_offset = 0x300;
+ shdr.sh_entsize = sizeof(Elf32_Sym);
+ shdr.sh_size = shdr.sh_entsize;
+ memory_->SetMemory(0xf7100 + sizeof(shdr), &shdr, sizeof(shdr));
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0x400;
+ shdr.sh_size = 0x100;
+ memory_->SetMemory(0xf7100 + 2 * sizeof(shdr), &shdr, sizeof(shdr));
+
+ Elf32_Sym sym = {};
+ sym.st_shndx = 2;
+ sym.st_info = STT_FUNC;
+ sym.st_value = 0x100300;
+ sym.st_size = 0x100;
+ memory_->SetMemory(0xf7300, &sym, sizeof(sym));
+ memory_->SetMemory(0xf7400, "FakeJitFunction");
+
+ RegsFake regs(10);
+ regs.FakeSetArch(ARCH_ARM);
+ JitDebug jit_debug(process_memory_);
+ Unwinder unwinder(10, maps_.get(), ®s, process_memory_);
+ unwinder.SetJitDebug(&jit_debug, ARCH_ARM);
+
+ FrameData frame = unwinder.BuildFrameFromPcOnly(0x100310);
+ EXPECT_EQ(0x10030eU, frame.pc);
+ EXPECT_EQ(0x60eU, frame.rel_pc);
+ EXPECT_EQ("/fake/jit.so", frame.map_name);
+ EXPECT_EQ(0x100U, frame.map_elf_start_offset);
+ EXPECT_EQ(0x200U, frame.map_exact_offset);
+ EXPECT_EQ(0x100000U, frame.map_start);
+ EXPECT_EQ(0x101000U, frame.map_end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_flags);
+ EXPECT_EQ(0U, frame.map_load_bias);
+ EXPECT_EQ("FakeJitFunction", frame.function_name);
+ EXPECT_EQ(0xeU, frame.function_offset);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
index 94f5a73..0415ef6 100644
--- a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
@@ -16,6 +16,11 @@
#include "UnwinderComponentCreator.h"
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
std::unique_ptr<Regs> GetRegisters(ArchEnum arch) {
switch (arch) {
case unwindstack::ARCH_ARM: {
@@ -109,13 +114,28 @@
return elf;
}
+static constexpr size_t kPageSize = 4096;
+
+static constexpr uint64_t AlignToPage(uint64_t address) {
+ return (address + kPageSize - 1) & ~(kPageSize - 1);
+}
+
std::unique_ptr<Maps> GetMaps(FuzzedDataProvider* data_provider) {
std::unique_ptr<Maps> maps = std::make_unique<Maps>();
+ std::map<uint64_t, uint64_t> map_ends;
uint8_t entry_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxMapEntryCount);
for (uint8_t i = 0; i < entry_count; i++) {
- uint64_t start = data_provider->ConsumeIntegral<uint64_t>();
- uint64_t end = data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX);
- uint64_t offset = data_provider->ConsumeIntegral<uint64_t>();
+ uint64_t start = AlignToPage(data_provider->ConsumeIntegral<uint64_t>());
+ uint64_t end = AlignToPage(data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX));
+ // Make sure not to add overlapping maps, that is not something that can
+ // happen in the real world.
+ auto entry = map_ends.upper_bound(start);
+ if (entry != map_ends.end() && end > entry->second) {
+ continue;
+ }
+ map_ends[end] = start;
+
+ uint64_t offset = AlignToPage(data_provider->ConsumeIntegral<uint64_t>());
std::string map_info_name = data_provider->ConsumeRandomLengthString(kMaxMapInfoNameLen);
uint8_t flags = PROT_READ | PROT_WRITE;
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 022dedf..9c012a9 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -205,6 +205,7 @@
shared_libs: [
"libutils",
"libbase",
+ "liblog",
],
}
@@ -238,6 +239,66 @@
srcs: ["Vector_fuzz.cpp"],
}
+cc_fuzz {
+ name: "libutils_fuzz_printer",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["Printer_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_callstack",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["CallStack_fuzz.cpp"],
+ shared_libs: [
+ "libutilscallstack",
+ ],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_process_callstack",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["ProcessCallStack_fuzz.cpp"],
+ shared_libs: [
+ "libutilscallstack",
+ ],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_stopwatch",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["StopWatch_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_propertymap",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["PropertyMap_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_rwlock",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["RWLock_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_refbase",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["RefBase_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_lrucache",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["LruCache_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_looper",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["Looper_fuzz.cpp"],
+}
+
cc_test {
name: "libutils_test",
host_supported: true,
diff --git a/libutils/CallStack_fuzz.cpp b/libutils/CallStack_fuzz.cpp
new file mode 100644
index 0000000..e89b5b7
--- /dev/null
+++ b/libutils/CallStack_fuzz.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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 <memory.h>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/CallStack.h"
+
+static constexpr int MAX_STRING_SIZE = 500;
+static constexpr int MAX_IGNORE_DEPTH = 200;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ size_t ignoreDepth = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_IGNORE_DEPTH);
+ int logPriority = dataProvider.ConsumeIntegral<int>();
+ pid_t tid = dataProvider.ConsumeIntegral<pid_t>();
+ std::string logTag = dataProvider.ConsumeRandomLengthString(MAX_STRING_SIZE);
+ std::string prefix = dataProvider.ConsumeRandomLengthString(MAX_STRING_SIZE);
+
+ const char* logTagChars = logTag.c_str();
+ const char* prefixChars = prefix.c_str();
+
+ android::CallStack::CallStackUPtr callStack = android::CallStack::getCurrent(ignoreDepth);
+ android::CallStack* callstackPtr = callStack.get();
+ android::CallStack::logStack(logTagChars, callstackPtr,
+ static_cast<android_LogPriority>(logPriority));
+ android::CallStack::stackToString(prefixChars);
+
+ callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
+ callstackPtr->clear();
+ callstackPtr->getCurrent(ignoreDepth);
+ callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
+ callstackPtr->update(ignoreDepth, tid);
+ callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
+
+ return 0;
+}
diff --git a/libutils/Looper_fuzz.cpp b/libutils/Looper_fuzz.cpp
new file mode 100644
index 0000000..c3ae54e
--- /dev/null
+++ b/libutils/Looper_fuzz.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 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/select.h>
+
+#include <iostream>
+
+#include <utils/Looper.h>
+
+#include "Looper_test_pipe.h"
+#include "fuzzer/FuzzedDataProvider.h"
+
+using android::Looper;
+using android::sp;
+
+// We don't want this to bog down fuzzing
+static constexpr int MAX_POLL_DELAY = 50;
+static constexpr int MAX_OPERATIONS = 500;
+
+void doNothing() {}
+void* doNothingPointer = reinterpret_cast<void*>(doNothing);
+
+static int noopCallback(int, int, void*) {
+ return 0;
+}
+
+std::vector<std::function<void(FuzzedDataProvider*, sp<Looper>, Pipe)>> operations = {
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe) -> void {
+ looper->pollOnce(dataProvider->ConsumeIntegralInRange<int>(0, MAX_POLL_DELAY));
+ },
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe) -> void {
+ looper->pollAll(dataProvider->ConsumeIntegralInRange<int>(0, MAX_POLL_DELAY));
+ },
+ // events and callback are nullptr
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+ looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+ dataProvider->ConsumeIntegral<int>(), nullptr, nullptr);
+ },
+ // Events is nullptr
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+ looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+ dataProvider->ConsumeIntegral<int>(), noopCallback, nullptr);
+ },
+ // callback is nullptr
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+ looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+ dataProvider->ConsumeIntegral<int>(), nullptr, doNothingPointer);
+ },
+ // callback and events both set
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+ looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+ dataProvider->ConsumeIntegral<int>(), noopCallback, doNothingPointer);
+ },
+
+ [](FuzzedDataProvider*, sp<Looper> looper, Pipe) -> void { looper->wake(); },
+ [](FuzzedDataProvider*, sp<Looper>, Pipe pipeObj) -> void { pipeObj.writeSignal(); }};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ Pipe pipeObj;
+ FuzzedDataProvider dataProvider(data, size);
+ sp<Looper> looper = new Looper(dataProvider.ConsumeBool());
+
+ size_t opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+ operations[op](&dataProvider, looper, pipeObj);
+ }
+ // Clear our pointer
+ looper.clear();
+ return 0;
+}
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 37bdf05..34f424b 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -2,12 +2,13 @@
// Copyright 2010 The Android Open Source Project
//
-#include <utils/Looper.h>
-#include <utils/Timers.h>
-#include <utils/StopWatch.h>
#include <gtest/gtest.h>
-#include <unistd.h>
#include <time.h>
+#include <unistd.h>
+#include <utils/Looper.h>
+#include <utils/StopWatch.h>
+#include <utils/Timers.h>
+#include "Looper_test_pipe.h"
#include <utils/threads.h>
@@ -24,41 +25,6 @@
MSG_TEST4 = 4,
};
-class Pipe {
-public:
- int sendFd;
- int receiveFd;
-
- Pipe() {
- int fds[2];
- ::pipe(fds);
-
- receiveFd = fds[0];
- sendFd = fds[1];
- }
-
- ~Pipe() {
- if (sendFd != -1) {
- ::close(sendFd);
- }
-
- if (receiveFd != -1) {
- ::close(receiveFd);
- }
- }
-
- status_t writeSignal() {
- ssize_t nWritten = ::write(sendFd, "*", 1);
- return nWritten == 1 ? 0 : -errno;
- }
-
- status_t readSignal() {
- char buf[1];
- ssize_t nRead = ::read(receiveFd, buf, 1);
- return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
- }
-};
-
class DelayedTask : public Thread {
int mDelayMillis;
diff --git a/libutils/Looper_test_pipe.h b/libutils/Looper_test_pipe.h
new file mode 100644
index 0000000..77b7b8b
--- /dev/null
+++ b/libutils/Looper_test_pipe.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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 <unistd.h>
+/**
+ * A pipe class for use when testing or fuzzing Looper
+ */
+class Pipe {
+ public:
+ int sendFd;
+ int receiveFd;
+
+ Pipe() {
+ int fds[2];
+ ::pipe(fds);
+
+ receiveFd = fds[0];
+ sendFd = fds[1];
+ }
+
+ ~Pipe() {
+ if (sendFd != -1) {
+ ::close(sendFd);
+ }
+
+ if (receiveFd != -1) {
+ ::close(receiveFd);
+ }
+ }
+
+ android::status_t writeSignal() {
+ ssize_t nWritten = ::write(sendFd, "*", 1);
+ return nWritten == 1 ? 0 : -errno;
+ }
+
+ android::status_t readSignal() {
+ char buf[1];
+ ssize_t nRead = ::read(receiveFd, buf, 1);
+ return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
+ }
+};
diff --git a/libutils/LruCache_fuzz.cpp b/libutils/LruCache_fuzz.cpp
new file mode 100644
index 0000000..f8bacfc
--- /dev/null
+++ b/libutils/LruCache_fuzz.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 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 <functional>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/LruCache.h"
+#include "utils/StrongPointer.h"
+
+typedef android::LruCache<size_t, size_t> FuzzCache;
+
+static constexpr uint32_t MAX_CACHE_ENTRIES = 800;
+
+class NoopRemovedCallback : public android::OnEntryRemoved<size_t, size_t> {
+ public:
+ void operator()(size_t&, size_t&) {
+ // noop
+ }
+};
+
+static NoopRemovedCallback callback;
+
+static const std::vector<std::function<void(FuzzedDataProvider*, FuzzCache*)>> operations = {
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->removeOldest(); },
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->peekOldestValue(); },
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->clear(); },
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->size(); },
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void {
+ android::LruCache<size_t, size_t>::Iterator iter(*cache);
+ while (iter.next()) {
+ iter.key();
+ iter.value();
+ }
+ },
+ [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
+ size_t key = dataProvider->ConsumeIntegral<size_t>();
+ size_t val = dataProvider->ConsumeIntegral<size_t>();
+ cache->put(key, val);
+ },
+ [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
+ size_t key = dataProvider->ConsumeIntegral<size_t>();
+ cache->get(key);
+ },
+ [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
+ size_t key = dataProvider->ConsumeIntegral<size_t>();
+ cache->remove(key);
+ },
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void {
+ cache->setOnEntryRemovedListener(&callback);
+ }};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ FuzzCache cache(MAX_CACHE_ENTRIES);
+ while (dataProvider.remaining_bytes() > 0) {
+ uint8_t op = dataProvider.ConsumeIntegral<uint8_t>() % operations.size();
+ operations[op](&dataProvider, &cache);
+ }
+
+ return 0;
+}
diff --git a/libutils/Printer_fuzz.cpp b/libutils/Printer_fuzz.cpp
new file mode 100755
index 0000000..0180d41
--- /dev/null
+++ b/libutils/Printer_fuzz.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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 "android-base/file.h"
+#include "android/log.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/Printer.h"
+#include "utils/String8.h"
+static constexpr int MAX_STR_SIZE = 1000;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ android::String8 outStr = android::String8();
+ // Line indent/formatting
+ uint indent = dataProvider.ConsumeIntegral<uint>();
+ std::string prefix = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+ std::string line = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+
+ // Misc properties
+ std::string logTag = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+ android_LogPriority priority =
+ static_cast<android_LogPriority>(dataProvider.ConsumeIntegral<int>());
+ bool ignoreBlankLines = dataProvider.ConsumeBool();
+
+ TemporaryFile tf;
+ android::FdPrinter filePrinter = android::FdPrinter(tf.fd, indent, prefix.c_str());
+ android::String8Printer stringPrinter = android::String8Printer(&outStr);
+ android::PrefixPrinter printer = android::PrefixPrinter(stringPrinter, prefix.c_str());
+ android::LogPrinter logPrinter =
+ android::LogPrinter(logTag.c_str(), priority, prefix.c_str(), ignoreBlankLines);
+
+ printer.printLine(line.c_str());
+ printer.printFormatLine("%s", line.c_str());
+ logPrinter.printLine(line.c_str());
+ logPrinter.printFormatLine("%s", line.c_str());
+ filePrinter.printLine(line.c_str());
+ filePrinter.printFormatLine("%s", line.c_str());
+ return 0;
+}
diff --git a/libutils/ProcessCallStack_fuzz.cpp b/libutils/ProcessCallStack_fuzz.cpp
new file mode 100644
index 0000000..30136cd
--- /dev/null
+++ b/libutils/ProcessCallStack_fuzz.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 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 <atomic>
+#include <thread>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/ProcessCallStack.h"
+using android::ProcessCallStack;
+
+static constexpr int MAX_NAME_SIZE = 1000;
+static constexpr int MAX_LOG_META_SIZE = 1000;
+static constexpr uint8_t MAX_THREADS = 10;
+
+std::atomic_bool ranCallStackUpdate(false);
+void loop() {
+ while (!ranCallStackUpdate.load()) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ }
+}
+
+void spawnThreads(FuzzedDataProvider* dataProvider) {
+ std::vector<std::thread> threads = std::vector<std::thread>();
+
+ // Get the number of threads to generate
+ uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_THREADS);
+
+ // Generate threads
+ for (uint8_t i = 0; i < count; i++) {
+ std::string threadName =
+ dataProvider->ConsumeRandomLengthString(MAX_NAME_SIZE).append(std::to_string(i));
+ std::thread th = std::thread(loop);
+ pthread_setname_np(th.native_handle(), threadName.c_str());
+ threads.push_back(move(th));
+ }
+
+ // Collect thread information
+ ProcessCallStack callStack = ProcessCallStack();
+ callStack.update();
+
+ // Tell our patiently waiting threads they can be done now.
+ ranCallStackUpdate.store(true);
+
+ std::string logTag = dataProvider->ConsumeRandomLengthString(MAX_LOG_META_SIZE);
+ std::string prefix = dataProvider->ConsumeRandomLengthString(MAX_LOG_META_SIZE);
+ // Both of these, along with dump, all call print() under the hood,
+ // Which is covered by the Printer fuzzer.
+ callStack.log(logTag.c_str());
+ callStack.toString(prefix.c_str());
+
+ // Check size
+ callStack.size();
+
+ // wait for any remaining threads
+ for (auto& thread : threads) {
+ thread.join();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ spawnThreads(&dataProvider);
+ return 0;
+}
diff --git a/libutils/PropertyMap_fuzz.cpp b/libutils/PropertyMap_fuzz.cpp
new file mode 100755
index 0000000..fd50729
--- /dev/null
+++ b/libutils/PropertyMap_fuzz.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 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 "android-base/file.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/PropertyMap.h"
+#include "utils/String8.h"
+
+static constexpr int MAX_FILE_SIZE = 256;
+static constexpr int MAX_STR_LEN = 2048;
+static constexpr int MAX_OPERATIONS = 1000;
+
+static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap)>>
+ operations = {
+ [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+ propertyMap.getProperties();
+ },
+ [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void {
+ propertyMap.clear();
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ android::String8 key = android::String8(keyStr.c_str());
+ propertyMap.hasProperty(key);
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ android::String8 key = android::String8(keyStr.c_str());
+ android::String8 out;
+ propertyMap.tryGetProperty(key, out);
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ TemporaryFile tf;
+ // Generate file contents
+ std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE);
+ // If we have string contents, dump them into the file.
+ // Otherwise, just leave it as an empty file.
+ if (contents.length() > 0) {
+ const char* bytes = contents.c_str();
+ android::base::WriteStringToFd(bytes, tf.fd);
+ }
+ android::PropertyMap* mapPtr = &propertyMap;
+ android::PropertyMap::load(android::String8(tf.path), &mapPtr);
+ },
+ [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void {
+ std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ std::string valStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN);
+ android::String8 key = android::String8(keyStr.c_str());
+ android::String8 val = android::String8(valStr.c_str());
+ propertyMap.addProperty(key, val);
+ },
+};
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ android::PropertyMap proprtyMap = android::PropertyMap();
+
+ int opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+ operations[op](&dataProvider, proprtyMap);
+ }
+ return 0;
+}
diff --git a/libutils/RWLock_fuzz.cpp b/libutils/RWLock_fuzz.cpp
new file mode 100755
index 0000000..e075905
--- /dev/null
+++ b/libutils/RWLock_fuzz.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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 <functional>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/RWLock.h"
+
+static constexpr int MAX_OPERATIONS = 100;
+static constexpr int MAX_NAME_LEN = 2048;
+
+static const std::vector<std::function<void(android::RWLock*)>> operations = {
+ [](android::RWLock* lock) -> void {
+ // This might return a non-zero value if already locked
+ // Either way we are definitely locked now.
+ lock->tryWriteLock();
+ },
+ [](android::RWLock* lock) -> void { lock->tryReadLock(); },
+ [](android::RWLock* lock) -> void { lock->unlock(); },
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ std::string nameStr = dataProvider.ConsumeRandomLengthString(MAX_NAME_LEN);
+ int type = dataProvider.ConsumeIntegral<int>();
+ android::RWLock rwLock = android::RWLock(type, nameStr.c_str());
+ std::vector<uint8_t> opsToRun = dataProvider.ConsumeRemainingBytes<uint8_t>();
+ int opsRun = 0;
+ for (auto it : opsToRun) {
+ if (opsRun++ >= MAX_OPERATIONS) {
+ break;
+ }
+ it = it % operations.size();
+ operations[it](&rwLock);
+ }
+ rwLock.unlock();
+ return 0;
+}
diff --git a/libutils/RefBase_fuzz.cpp b/libutils/RefBase_fuzz.cpp
new file mode 100755
index 0000000..2a92531
--- /dev/null
+++ b/libutils/RefBase_fuzz.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright 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 <atomic>
+#include <thread>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/RefBase.h"
+#include "utils/StrongPointer.h"
+using android::RefBase;
+using android::sp;
+using android::wp;
+
+static constexpr int REFBASE_INITIAL_STRONG_VALUE = (1 << 28);
+static constexpr int REFBASE_MAX_COUNT = 0xfffff;
+
+static constexpr int MAX_OPERATIONS = 100;
+static constexpr int MAX_THREADS = 10;
+
+bool canDecrementStrong(RefBase* ref) {
+ // There's an assert around decrementing the strong count too much that causes an artificial
+ // crash This is just running BAD_STRONG from RefBase
+ const int32_t count = ref->getStrongCount() - 1;
+ return !(count == 0 || ((count) & (~(REFBASE_MAX_COUNT | REFBASE_INITIAL_STRONG_VALUE))) != 0);
+}
+bool canDecrementWeak(RefBase* ref) {
+ const int32_t count = ref->getWeakRefs()->getWeakCount() - 1;
+ return !((count) == 0 || ((count) & (~REFBASE_MAX_COUNT)) != 0);
+}
+
+struct RefBaseSubclass : public RefBase {
+ RefBaseSubclass() {}
+ virtual ~RefBaseSubclass() {}
+};
+
+std::vector<std::function<void(RefBaseSubclass*)>> operations = {
+ [](RefBaseSubclass* ref) -> void { ref->getStrongCount(); },
+ [](RefBaseSubclass* ref) -> void { ref->printRefs(); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->printRefs(); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->getWeakCount(); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->refBase(); },
+ [](RefBaseSubclass* ref) -> void { ref->incStrong(nullptr); },
+ [](RefBaseSubclass* ref) -> void {
+ if (canDecrementStrong(ref)) {
+ ref->decStrong(nullptr);
+ }
+ },
+ [](RefBaseSubclass* ref) -> void { ref->forceIncStrong(nullptr); },
+ [](RefBaseSubclass* ref) -> void { ref->createWeak(nullptr); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->attemptIncStrong(nullptr); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->attemptIncWeak(nullptr); },
+ [](RefBaseSubclass* ref) -> void {
+ if (canDecrementWeak(ref)) {
+ ref->getWeakRefs()->decWeak(nullptr);
+ }
+ },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->incWeak(nullptr); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->printRefs(); },
+};
+
+void loop(RefBaseSubclass* loopRef, const std::vector<uint8_t>& fuzzOps) {
+ for (auto op : fuzzOps) {
+ operations[op % operations.size()](loopRef);
+ }
+}
+
+void spawnThreads(FuzzedDataProvider* dataProvider) {
+ std::vector<std::thread> threads = std::vector<std::thread>();
+
+ // Get the number of threads to generate
+ uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_THREADS);
+
+ // Generate threads
+ for (uint8_t i = 0; i < count; i++) {
+ RefBaseSubclass* threadRef = new RefBaseSubclass();
+ uint8_t opCount = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_OPERATIONS);
+ std::vector<uint8_t> threadOperations = dataProvider->ConsumeBytes<uint8_t>(opCount);
+ std::thread tmp = std::thread(loop, threadRef, threadOperations);
+ threads.push_back(move(tmp));
+ }
+
+ for (auto& th : threads) {
+ th.join();
+ }
+}
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ spawnThreads(&dataProvider);
+ return 0;
+}
diff --git a/libutils/StopWatch_fuzz.cpp b/libutils/StopWatch_fuzz.cpp
new file mode 100644
index 0000000..63d8a28
--- /dev/null
+++ b/libutils/StopWatch_fuzz.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 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 "fuzzer/FuzzedDataProvider.h"
+#include "utils/StopWatch.h"
+
+static constexpr int MAX_OPERATIONS = 100;
+static constexpr int MAX_NAME_LEN = 2048;
+
+static const std::vector<std::function<void(android::StopWatch)>> operations = {
+ [](android::StopWatch stopWatch) -> void { stopWatch.reset(); },
+ [](android::StopWatch stopWatch) -> void { stopWatch.lap(); },
+ [](android::StopWatch stopWatch) -> void { stopWatch.elapsedTime(); },
+ [](android::StopWatch stopWatch) -> void { stopWatch.name(); },
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ std::string nameStr = dataProvider.ConsumeRandomLengthString(MAX_NAME_LEN);
+ int clockVal = dataProvider.ConsumeIntegral<int>();
+ android::StopWatch stopWatch = android::StopWatch(nameStr.c_str(), clockVal);
+ std::vector<uint8_t> opsToRun = dataProvider.ConsumeRemainingBytes<uint8_t>();
+ int opsRun = 0;
+ for (auto it : opsToRun) {
+ if (opsRun++ >= MAX_OPERATIONS) {
+ break;
+ }
+ it = it % operations.size();
+ operations[it](stopWatch);
+ }
+ return 0;
+}
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 73ec1be..9c71141 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -39,8 +39,15 @@
*/
int64_t uptimeMillis()
{
- int64_t when = systemTime(SYSTEM_TIME_MONOTONIC);
- return (int64_t) nanoseconds_to_milliseconds(when);
+ return nanoseconds_to_milliseconds(uptimeNanos());
+}
+
+/*
+ * public static native long uptimeNanos();
+ */
+int64_t uptimeNanos()
+{
+ return systemTime(SYSTEM_TIME_MONOTONIC);
}
/*
diff --git a/libutils/SystemClock_test.cpp b/libutils/SystemClock_test.cpp
index 5ad060b..7449dad 100644
--- a/libutils/SystemClock_test.cpp
+++ b/libutils/SystemClock_test.cpp
@@ -29,16 +29,24 @@
TEST(SystemClock, SystemClock) {
auto startUptimeMs = android::uptimeMillis();
+ auto startUptimeNs = android::uptimeNanos();
auto startRealtimeMs = android::elapsedRealtime();
auto startRealtimeNs = android::elapsedRealtimeNano();
ASSERT_GT(startUptimeMs, 0)
<< "uptimeMillis() reported an impossible uptime";
+ ASSERT_GT(startUptimeNs, 0)
+ << "uptimeNanos() reported an impossible uptime";
ASSERT_GE(startRealtimeMs, startUptimeMs)
<< "elapsedRealtime() thinks we've suspended for negative time";
- ASSERT_GE(startRealtimeNs, startUptimeMs * MS_IN_NS)
+ ASSERT_GE(startRealtimeNs, startUptimeNs)
<< "elapsedRealtimeNano() thinks we've suspended for negative time";
+ ASSERT_GE(startUptimeNs, startUptimeMs * MS_IN_NS)
+ << "uptimeMillis() and uptimeNanos() are inconsistent";
+ ASSERT_LT(startUptimeNs, (startUptimeMs + SLACK_MS) * MS_IN_NS)
+ << "uptimeMillis() and uptimeNanos() are inconsistent";
+
ASSERT_GE(startRealtimeNs, startRealtimeMs * MS_IN_NS)
<< "elapsedRealtime() and elapsedRealtimeNano() are inconsistent";
ASSERT_LT(startRealtimeNs, (startRealtimeMs + SLACK_MS) * MS_IN_NS)
@@ -51,6 +59,7 @@
ASSERT_EQ(nanosleepErr, 0) << "nanosleep() failed: " << strerror(errno);
auto endUptimeMs = android::uptimeMillis();
+ auto endUptimeNs = android::uptimeNanos();
auto endRealtimeMs = android::elapsedRealtime();
auto endRealtimeNs = android::elapsedRealtimeNano();
@@ -58,6 +67,10 @@
<< "uptimeMillis() advanced too little after nanosleep()";
EXPECT_LT(endUptimeMs - startUptimeMs, SLEEP_MS + SLACK_MS)
<< "uptimeMillis() advanced too much after nanosleep()";
+ EXPECT_GE(endUptimeNs - startUptimeNs, SLEEP_NS)
+ << "uptimeNanos() advanced too little after nanosleep()";
+ EXPECT_LT(endUptimeNs - startUptimeNs, SLEEP_NS + SLACK_NS)
+ << "uptimeNanos() advanced too much after nanosleep()";
EXPECT_GE(endRealtimeMs - startRealtimeMs, SLEEP_MS)
<< "elapsedRealtime() advanced too little after nanosleep()";
EXPECT_LT(endRealtimeMs - startRealtimeMs, SLEEP_MS + SLACK_MS)
@@ -67,6 +80,11 @@
EXPECT_LT(endRealtimeNs - startRealtimeNs, SLEEP_NS + SLACK_NS)
<< "elapsedRealtimeNano() advanced too much after nanosleep()";
+ EXPECT_GE(endUptimeNs, endUptimeMs * MS_IN_NS)
+ << "uptimeMillis() and uptimeNanos() are inconsistent after nanosleep()";
+ EXPECT_LT(endUptimeNs, (endUptimeMs + SLACK_MS) * MS_IN_NS)
+ << "uptimeMillis() and uptimeNanos() are inconsistent after nanosleep()";
+
EXPECT_GE(endRealtimeNs, endRealtimeMs * MS_IN_NS)
<< "elapsedRealtime() and elapsedRealtimeNano() are inconsistent after nanosleep()";
EXPECT_LT(endRealtimeNs, (endRealtimeMs + SLACK_MS) * MS_IN_NS)
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 147db54..55eadb0 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -302,7 +302,8 @@
}
#if defined(__ANDROID__)
-int androidSetThreadPriority(pid_t tid, int pri, bool change_policy) {
+namespace {
+int androidSetThreadPriorityInternal(pid_t tid, int pri, bool change_policy) {
int rc = 0;
int lasterr = 0;
int curr_pri = getpriority(PRIO_PROCESS, tid);
@@ -334,6 +335,15 @@
return rc;
}
+} // namespace
+
+int androidSetThreadPriority(pid_t tid, int pri) {
+ return androidSetThreadPriorityInternal(tid, pri, true);
+}
+
+int androidSetThreadPriorityAndPolicy(pid_t tid, int pri, bool change_policy) {
+ return androidSetThreadPriorityInternal(tid, pri, change_policy);
+}
int androidGetThreadPriority(pid_t tid) {
return getpriority(PRIO_PROCESS, tid);
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index b08e061..b6e457b 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -162,9 +162,9 @@
if (index >= src_len) {
return -1;
}
- size_t dummy_index;
+ size_t unused_index;
if (next_index == nullptr) {
- next_index = &dummy_index;
+ next_index = &unused_index;
}
size_t num_read;
int32_t ret = utf32_at_internal(src + index, &num_read);
diff --git a/libutils/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
index 3c30a2a..cdb5442 100644
--- a/libutils/include/utils/AndroidThreads.h
+++ b/libutils/include/utils/AndroidThreads.h
@@ -78,9 +78,12 @@
// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION
// if the priority set failed, else another value if just the group set failed;
// in either case errno is set. Thread ID zero means current thread.
+// This is equivalent to androidSetThreadPriorityAndPolicy(tid, prio, true);
+extern int androidSetThreadPriority(pid_t tid, int prio);
+
// Parameter "change_policy" indicates if sched policy should be changed. It needs
// not be checked again if the change is done elsewhere like activity manager.
-extern int androidSetThreadPriority(pid_t tid, int prio, bool change_policy = true);
+extern int androidSetThreadPriorityAndPolicy(pid_t tid, int prio, bool change_policy);
// Get the current priority of a particular thread. Returns one of the
// ANDROID_PRIORITY constants or a negative result in case of error.
diff --git a/libutils/include/utils/Debug.h b/libutils/include/utils/Debug.h
index 96bd70e..c3b9026 100644
--- a/libutils/include/utils/Debug.h
+++ b/libutils/include/utils/Debug.h
@@ -14,27 +14,9 @@
* limitations under the License.
*/
-#ifndef ANDROID_UTILS_DEBUG_H
-#define ANDROID_UTILS_DEBUG_H
+#pragma once
-#include <stdint.h>
-#include <sys/types.h>
+// Note: new code should use static_assert directly.
-namespace android {
-// ---------------------------------------------------------------------------
-
-#ifdef __cplusplus
-template<bool> struct CompileTimeAssert;
-template<> struct CompileTimeAssert<true> {};
-#define COMPILE_TIME_ASSERT(_exp) \
- template class CompileTimeAssert< (_exp) >;
-#endif
-
-// DO NOT USE: Please use static_assert instead
-#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \
- CompileTimeAssert<( _exp )>();
-
-// ---------------------------------------------------------------------------
-} // namespace android
-
-#endif // ANDROID_UTILS_DEBUG_H
+#define COMPILE_TIME_ASSERT static_assert
+#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE static_assert
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 17c5e10..8aa381a 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_UTILS_FLATTENABLE_H
-#define ANDROID_UTILS_FLATTENABLE_H
+#pragma once
// DO NOT USE: please use parcelable instead
// This code is deprecated and will not be supported via AIDL code gen. For data
@@ -25,7 +24,6 @@
#include <string.h>
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/Debug.h>
#include <type_traits>
@@ -217,5 +215,3 @@
};
} // namespace android
-
-#endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/libutils/include/utils/SystemClock.h b/libutils/include/utils/SystemClock.h
index f816fba..892104c 100644
--- a/libutils/include/utils/SystemClock.h
+++ b/libutils/include/utils/SystemClock.h
@@ -23,6 +23,7 @@
namespace android {
int64_t uptimeMillis();
+int64_t uptimeNanos();
int64_t elapsedRealtime();
int64_t elapsedRealtimeNano();
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
index ddf71de..be35ea2 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -23,15 +23,13 @@
#include <log/log.h>
#include <utils/TypeHelpers.h>
#include <utils/VectorImpl.h>
-
-/*
- * Used to blacklist some functions from CFI.
- *
- */
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
+/*
+ * Used to exclude some functions from CFI.
+ */
#if __has_attribute(no_sanitize)
#define UTILS_VECTOR_NO_CFI __attribute__((no_sanitize("cfi")))
#else
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index d15fa2b..6b7e016 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -64,6 +64,7 @@
using android::base::ParseUint;
using android::base::Split;
using android::base::StringPrintf;
+using android::base::WriteFully;
class Logcat {
public:
@@ -616,15 +617,15 @@
if (long_options[option_index].name == wrap_str) {
mode |= ANDROID_LOG_WRAP | ANDROID_LOG_NONBLOCK;
// ToDo: implement API that supports setting a wrap timeout
- size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
- if (optarg && (!ParseUint(optarg, &dummy) || dummy < 1)) {
+ size_t timeout = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
+ if (optarg && (!ParseUint(optarg, &timeout) || timeout < 1)) {
error(EXIT_FAILURE, 0, "%s %s out of range.",
long_options[option_index].name, optarg);
}
- if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
+ if (timeout != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
fprintf(stderr, "WARNING: %s %u seconds, ignoring %zu\n",
long_options[option_index].name, ANDROID_LOG_WRAP_DEFAULT_TIMEOUT,
- dummy);
+ timeout);
}
break;
}
@@ -864,8 +865,7 @@
if (consolePipe) {
// need the trailing '\0'
- if (!android::base::WriteFully(fd, pipePurpose.c_str(),
- pipePurpose.size() + 1)) {
+ if (!WriteFully(fd, pipePurpose.c_str(), pipePurpose.size() + 1)) {
close(fd);
return EXIT_FAILURE;
}
@@ -1064,19 +1064,23 @@
if (getLogSize) {
long size = android_logger_get_log_size(logger);
long readable = android_logger_get_log_readable_size(logger);
+ long consumed = android_logger_get_log_consumed_size(logger);
if (size < 0 || readable < 0) {
ReportErrorName(buffer_name, security_buffer_selected, &get_size_failures);
} else {
auto size_format = format_of_size(size);
auto readable_format = format_of_size(readable);
+ auto consumed_format = format_of_size(consumed);
std::string str = android::base::StringPrintf(
- "%s: ring buffer is %lu %sB (%lu %sB consumed),"
+ "%s: ring buffer is %lu %sB (%lu %sB consumed, %lu %sB readable),"
" max entry is %d B, max payload is %d B\n",
- buffer_name, size_format.first, size_format.second, readable_format.first,
- readable_format.second, (int)LOGGER_ENTRY_MAX_LEN,
- (int)LOGGER_ENTRY_MAX_PAYLOAD);
- TEMP_FAILURE_RETRY(write(output_fd_.get(), str.data(), str.length()));
+ buffer_name, size_format.first, size_format.second, consumed_format.first,
+ consumed_format.second, readable_format.first, readable_format.second,
+ (int)LOGGER_ENTRY_MAX_LEN, (int)LOGGER_ENTRY_MAX_PAYLOAD);
+ if (!WriteFully(output_fd_, str.data(), str.length())) {
+ error(EXIT_FAILURE, errno, "Failed to write to output fd");
+ }
}
}
}
@@ -1146,7 +1150,9 @@
if (*cp == '\n') ++cp;
size_t len = strlen(cp);
- TEMP_FAILURE_RETRY(write(output_fd_.get(), cp, len));
+ if (!WriteFully(output_fd_, cp, len)) {
+ error(EXIT_FAILURE, errno, "Failed to write to output fd");
+ }
return EXIT_SUCCESS;
}
@@ -1190,7 +1196,9 @@
PrintDividers(log_msg.id(), printDividers);
if (print_binary_) {
- TEMP_FAILURE_RETRY(write(output_fd_.get(), &log_msg, log_msg.len()));
+ if (!WriteFully(output_fd_, &log_msg, log_msg.len())) {
+ error(EXIT_FAILURE, errno, "Failed to write to output fd");
+ }
} else {
ProcessBuffer(&log_msg);
}
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 61aa938..0860000 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -1654,48 +1654,6 @@
EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, ""));
}
-
- {
- // Invent new entries because existing can not serve
- EventTagMap* map = android_openEventTagMap(nullptr);
- ASSERT_TRUE(nullptr != map);
- static const char name[] = logcat_executable ".descriptive-monotonic";
- int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
- ANDROID_LOG_UNKNOWN);
- android_closeEventTagMap(map);
- ASSERT_NE(-1, myTag);
-
- const struct tag sync = { (uint32_t)myTag, name };
-
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)7;
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=7s"));
- }
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)62;
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:02"));
- }
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)3673;
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:01:13"));
- }
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)(86400 + 7200 + 180 + 58);
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=1d 2:03:58"));
- }
- }
}
static bool reportedSecurity(const char* command) {
diff --git a/logd/Android.bp b/logd/Android.bp
index 036cb7e..7f67ab0 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -57,6 +57,7 @@
"LogReaderList.cpp",
"LogReaderThread.cpp",
"LogBufferElement.cpp",
+ "LogSize.cpp",
"LogStatistics.cpp",
"LogTags.cpp",
"PruneList.cpp",
@@ -177,6 +178,27 @@
},
test_suites: [
"cts",
+ "device-tests",
"vts10",
],
}
+
+cc_binary {
+ name: "replay_messages",
+ defaults: ["logd_defaults"],
+ host_supported: true,
+
+ srcs: [
+ "ReplayMessages.cpp",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "liblogd",
+ "libselinux",
+ "libz",
+ "libzstd",
+ ],
+}
diff --git a/logd/ChattyLogBuffer.h b/logd/ChattyLogBuffer.h
index ce3dc7b..b4d3a2f 100644
--- a/logd/ChattyLogBuffer.h
+++ b/logd/ChattyLogBuffer.h
@@ -25,7 +25,6 @@
#include <android-base/thread_annotations.h>
#include <android/log.h>
#include <private/android_filesystem_config.h>
-#include <sysutils/SocketClient.h>
#include "LogBuffer.h"
#include "LogBufferElement.h"
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 2eeb0d9..0ba1621 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -32,6 +32,7 @@
#include <string>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include <log/log_properties.h>
@@ -46,6 +47,7 @@
registerCmd(new ClearCmd(this));
registerCmd(new GetBufSizeCmd(this));
registerCmd(new SetBufSizeCmd(this));
+ registerCmd(new GetBufSizeReadableCmd(this));
registerCmd(new GetBufSizeUsedCmd(this));
registerCmd(new GetStatisticsCmd(this));
registerCmd(new SetPruneListCmd(this));
@@ -63,53 +65,58 @@
}
}
-int CommandListener::ClearCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
+template <typename F>
+static int LogIdCommand(SocketClient* cli, int argc, char** argv, F&& function) {
setname();
+ if (argc < 2) {
+ cli->sendMsg("Missing Argument");
+ return 0;
+ }
+
+ int log_id;
+ if (!android::base::ParseInt(argv[1], &log_id, static_cast<int>(LOG_ID_MAIN),
+ static_cast<int>(LOG_ID_KERNEL))) {
+ cli->sendMsg("Range Error");
+ return 0;
+ }
+
+ function(static_cast<log_id_t>(log_id));
+ return 0;
+}
+
+int CommandListener::ClearCmd::runCommand(SocketClient* cli, int argc, char** argv) {
uid_t uid = cli->getUid();
if (clientHasLogCredentials(cli)) {
uid = AID_ROOT;
}
- if (argc < 2) {
- cli->sendMsg("Missing Argument");
- return 0;
- }
-
- int id = atoi(argv[1]);
- if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
- cli->sendMsg("Range Error");
- return 0;
- }
-
- cli->sendMsg(buf()->Clear((log_id_t)id, uid) ? "success" : "busy");
- return 0;
+ return LogIdCommand(cli, argc, argv, [&](log_id_t id) {
+ cli->sendMsg(buf()->Clear(id, uid) ? "success" : "busy");
+ });
}
-int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
- setname();
- if (argc < 2) {
- cli->sendMsg("Missing Argument");
- return 0;
- }
+template <typename F>
+static int LogSizeCommand(SocketClient* cli, int argc, char** argv, F&& size_function) {
+ return LogIdCommand(cli, argc, argv, [&](log_id_t log_id) {
+ cli->sendMsg(std::to_string(size_function(log_id)).c_str());
+ });
+}
- int id = atoi(argv[1]);
- if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
- cli->sendMsg("Range Error");
- return 0;
- }
+int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+ return LogSizeCommand(cli, argc, argv, [this](log_id_t id) { return buf()->GetSize(id); });
+}
- unsigned long size = buf()->GetSize((log_id_t)id);
- char buf[512];
- snprintf(buf, sizeof(buf), "%lu", size);
- cli->sendMsg(buf);
- return 0;
+int CommandListener::GetBufSizeReadableCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+ return LogSizeCommand(cli, argc, argv,
+ [this](log_id_t id) { return stats()->SizeReadable(id); });
+}
+
+int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+ return LogSizeCommand(cli, argc, argv, [this](log_id_t id) { return stats()->Sizes(id); });
}
int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
- setname();
if (!clientHasLogCredentials(cli)) {
cli->sendMsg("Permission Denied");
return 0;
@@ -119,42 +126,11 @@
cli->sendMsg("Missing Argument");
return 0;
}
+ size_t size = atol(argv[2]);
- int id = atoi(argv[1]);
- if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
- cli->sendMsg("Range Error");
- return 0;
- }
-
- unsigned long size = atol(argv[2]);
- if (buf()->SetSize((log_id_t)id, size)) {
- cli->sendMsg("Range Error");
- return 0;
- }
-
- cli->sendMsg("success");
- return 0;
-}
-
-int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
- setname();
- if (argc < 2) {
- cli->sendMsg("Missing Argument");
- return 0;
- }
-
- int id = atoi(argv[1]);
- if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
- cli->sendMsg("Range Error");
- return 0;
- }
-
- unsigned long size = stats()->Sizes((log_id_t)id);
- char buf[512];
- snprintf(buf, sizeof(buf), "%lu", size);
- cli->sendMsg(buf);
- return 0;
+ return LogIdCommand(cli, argc, argv, [&](log_id_t log_id) {
+ cli->sendMsg(buf()->SetSize(log_id, size) ? "success" : "busy");
+ });
}
// This returns a string with a length prefix with the format <length>\n<data>\n\f. The length
@@ -179,8 +155,7 @@
return android::base::StringPrintf("%zu\n%s\n\f", total_size, str.c_str());
}
-int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
+int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc, char** argv) {
setname();
uid_t uid = cli->getUid();
if (clientHasLogCredentials(cli)) {
@@ -245,8 +220,7 @@
return 0;
}
-int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
+int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc, char** argv) {
setname();
uid_t uid = cli->getUid();
if (clientHasLogCredentials(cli)) {
@@ -290,8 +264,7 @@
return 0;
}
-int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
- char** /*argv*/) {
+int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int, char**) {
setname();
LOG(INFO) << "logd reinit";
@@ -314,8 +287,7 @@
return 0;
}
-int CommandListener::ExitCmd::runCommand(SocketClient* cli, int /*argc*/,
- char** /*argv*/) {
+int CommandListener::ExitCmd::runCommand(SocketClient* cli, int, char**) {
setname();
cli->sendMsg("success");
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index c3080ab..8e12634 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -57,6 +57,7 @@
LogCmd(Clear, clear);
LogCmd(GetBufSize, getLogSize);
LogCmd(SetBufSize, setLogSize);
+ LogCmd(GetBufSizeReadable, getLogSizeReadable);
LogCmd(GetBufSizeUsed, getLogSizeUsed);
LogCmd(GetStatistics, getStatistics);
LogCmd(GetPruneList, getPruneList);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 0ce9796..0e17476 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -32,7 +32,7 @@
#include <sstream>
#include <android-base/macros.h>
-#include <log/log_properties.h>
+#include <android-base/properties.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -40,6 +40,8 @@
#include "LogUtils.h"
#include "libaudit.h"
+using android::base::GetBoolProperty;
+
#define KMSG_PRIORITY(PRI) \
'<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
'0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
@@ -48,8 +50,8 @@
: SocketListener(getLogSocket(), false),
logbuf(buf),
fdDmesg(fdDmesg),
- main(__android_logger_property_get_bool("ro.logd.auditd.main", BOOL_DEFAULT_TRUE)),
- events(__android_logger_property_get_bool("ro.logd.auditd.events", BOOL_DEFAULT_TRUE)),
+ main(GetBoolProperty("ro.logd.auditd.main", true)),
+ events(GetBoolProperty("ro.logd.auditd.events", true)),
initialized(false),
stats_(stats) {
static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index c5d333a..a98c4b9 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -68,8 +68,8 @@
log_time realtime)>& filter) = 0;
virtual bool Clear(log_id_t id, uid_t uid) = 0;
- virtual unsigned long GetSize(log_id_t id) = 0;
- virtual int SetSize(log_id_t id, unsigned long size) = 0;
+ virtual size_t GetSize(log_id_t id) = 0;
+ virtual bool SetSize(log_id_t id, size_t size) = 0;
virtual uint64_t sequence() const = 0;
};
diff --git a/logd/LogBufferTest.cpp b/logd/LogBufferTest.cpp
index 47d2a2f..1911105 100644
--- a/logd/LogBufferTest.cpp
+++ b/logd/LogBufferTest.cpp
@@ -34,16 +34,6 @@
using android::base::Split;
using android::base::StringPrintf;
-#ifndef __ANDROID__
-unsigned long __android_logger_get_buffer_size(log_id_t) {
- return 1024 * 1024;
-}
-
-bool __android_logger_valid_buffer_size(unsigned long) {
- return true;
-}
-#endif
-
char* android::uidToName(uid_t) {
return nullptr;
}
diff --git a/logd/LogBufferTest.h b/logd/LogBufferTest.h
index 1fd22c2..eeeb980 100644
--- a/logd/LogBufferTest.h
+++ b/logd/LogBufferTest.h
@@ -75,6 +75,8 @@
} else {
FAIL() << "Unknown buffer type selected for test";
}
+
+ log_id_for_each(i) { log_buffer_->SetSize(i, 1024 * 1024); }
}
void LogMessages(const std::vector<LogMessage>& messages) {
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index dbdf7fd..d6c7d25 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -381,8 +381,8 @@
}
if ((i == 9) && (cp[i] == ' ')) {
int pid = 0;
- char dummy;
- if (sscanf(cp + 4, "%d%c", &pid, &dummy) == 2) {
+ char placeholder;
+ if (sscanf(cp + 4, "%d%c", &pid, &placeholder) == 2) {
buf = cp + 10; // skip-it-all
return pid;
}
@@ -392,8 +392,8 @@
// Mediatek kernels with modified printk
if (*cp == '[') {
int pid = 0;
- char dummy;
- if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &dummy) == 2) {
+ char placeholder;
+ if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &placeholder) == 2) {
return pid;
}
break; // Only the first one
@@ -703,7 +703,7 @@
p = " ";
b = 1;
}
- // paranoid sanity check, can not happen ...
+ // This shouldn't happen, but clamp the size if it does.
if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
b = LOGGER_ENTRY_MAX_PAYLOAD;
}
@@ -712,7 +712,7 @@
}
// calculate buffer copy requirements
ssize_t n = 1 + taglen + 1 + b + 1;
- // paranoid sanity check, first two just can not happen ...
+ // Extra checks for likely impossible cases.
if ((taglen > n) || (b > n) || (n > (ssize_t)USHRT_MAX) || (n <= 0)) {
return -EINVAL;
}
@@ -722,7 +722,7 @@
// If we malloc'd this buffer, we could get away without n's USHRT_MAX
// test above, but we would then required a max(n, USHRT_MAX) as
// truncating length argument to logbuf->log() below. Gain is protection
- // of stack sanity and speedup, loss is truncated long-line content.
+ // against stack corruption and speedup, loss is truncated long-line content.
char newstr[n];
char* np = newstr;
diff --git a/logd/LogReader.h b/logd/LogReader.h
index b85a584..a4e52c4 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -22,8 +22,6 @@
#include "LogReaderList.h"
#include "LogReaderThread.h"
-#define LOGD_SNDTIMEO 32
-
class LogReader : public SocketListener {
public:
explicit LogReader(LogBuffer* logbuf, LogReaderList* reader_list);
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
index 1855c0e..20624f2 100644
--- a/logd/LogReaderThread.h
+++ b/logd/LogReaderThread.h
@@ -27,7 +27,6 @@
#include <memory>
#include <log/log.h>
-#include <sysutils/SocketClient.h>
#include "LogBuffer.h"
#include "LogWriter.h"
diff --git a/logd/LogSize.cpp b/logd/LogSize.cpp
new file mode 100644
index 0000000..fe829ba
--- /dev/null
+++ b/logd/LogSize.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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 <LogSize.h>
+
+#include <array>
+#include <optional>
+#include <string>
+
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+
+bool IsValidBufferSize(size_t value) {
+ return kLogBufferMinSize <= value && value <= kLogBufferMaxSize;
+}
+
+static std::optional<size_t> GetBufferSizeProperty(const std::string& key) {
+ std::string value = android::base::GetProperty(key, "");
+ if (value.empty()) {
+ return {};
+ }
+
+ uint32_t size;
+ if (!android::base::ParseByteCount(value, &size)) {
+ return {};
+ }
+
+ if (!IsValidBufferSize(size)) {
+ return {};
+ }
+
+ return size;
+}
+
+size_t GetBufferSizeFromProperties(log_id_t log_id) {
+ std::string buffer_name = android_log_id_to_name(log_id);
+ std::array<std::string, 4> properties = {
+ "persist.logd.size." + buffer_name,
+ "ro.logd.size." + buffer_name,
+ "persist.logd.size",
+ "ro.logd.size",
+ };
+
+ for (const auto& property : properties) {
+ if (auto size = GetBufferSizeProperty(property)) {
+ return *size;
+ }
+ }
+
+ if (android::base::GetBoolProperty("ro.config.low_ram", false)) {
+ return kLogBufferMinSize;
+ }
+
+ return kDefaultLogBufferSize;
+}
diff --git a/logd/LogSize.h b/logd/LogSize.h
new file mode 100644
index 0000000..d5716ff
--- /dev/null
+++ b/logd/LogSize.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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 <stddef.h>
+
+#include <log/log.h>
+
+static constexpr size_t kDefaultLogBufferSize = 256 * 1024;
+static constexpr size_t kLogBufferMinSize = 64 * 1024;
+static constexpr size_t kLogBufferMaxSize = 256 * 1024 * 1024;
+
+bool IsValidBufferSize(size_t value);
+
+// This returns the buffer size as set in system properties for use in LogBuffer::Init().
+// Note that `logcat -G` calls LogBuffer::SetSize(), which configures log buffer sizes without
+// setting these properties, so this function should never be used except for LogBuffer::Init().
+// LogBuffer::GetSize() should be used instead within logd. Other processes can use
+// android_logger_get_log_size() or `logcat -g` to query the actual allotted buffer size.
+size_t GetBufferSizeFromProperties(log_id_t log_id);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 1705d48..87069b3 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -26,8 +26,10 @@
#include <unistd.h>
#include <list>
+#include <vector>
#include <android-base/logging.h>
+#include <android-base/strings.h>
#include <private/android_logger.h>
#include "LogBufferElement.h"
@@ -61,7 +63,8 @@
return std::string(msg, len);
}
-LogStatistics::LogStatistics(bool enable_statistics, bool track_total_size)
+LogStatistics::LogStatistics(bool enable_statistics, bool track_total_size,
+ std::optional<log_time> start_time)
: enable(enable_statistics), track_total_size_(track_total_size) {
log_time now(CLOCK_REALTIME);
log_id_for_each(id) {
@@ -70,8 +73,13 @@
mDroppedElements[id] = 0;
mSizesTotal[id] = 0;
mElementsTotal[id] = 0;
- mOldest[id] = now;
- mNewest[id] = now;
+ if (start_time) {
+ mOldest[id] = *start_time;
+ mNewest[id] = *start_time;
+ } else {
+ mOldest[id] = now;
+ mNewest[id] = now;
+ }
mNewestDropped[id] = now;
}
}
@@ -784,6 +792,31 @@
return output;
}
+std::string LogStatistics::ReportInteresting() const {
+ auto lock = std::lock_guard{lock_};
+
+ std::vector<std::string> items;
+
+ log_id_for_each(i) { items.emplace_back(std::to_string(mElements[i])); }
+
+ log_id_for_each(i) { items.emplace_back(std::to_string(mSizes[i])); }
+
+ log_id_for_each(i) {
+ items.emplace_back(std::to_string(overhead_[i] ? *overhead_[i] : mSizes[i]));
+ }
+
+ log_id_for_each(i) {
+ uint64_t oldest = mOldest[i].msec() / 1000;
+ uint64_t newest = mNewest[i].msec() / 1000;
+
+ int span = newest - oldest;
+
+ items.emplace_back(std::to_string(span));
+ }
+
+ return android::base::Join(items, ",");
+}
+
std::string LogStatistics::Format(uid_t uid, pid_t pid, unsigned int logMask) const {
auto lock = std::lock_guard{lock_};
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index d440a8c..faf9283 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -515,7 +515,8 @@
}
public:
- LogStatistics(bool enable_statistics, bool track_total_size);
+ LogStatistics(bool enable_statistics, bool track_total_size,
+ std::optional<log_time> start_time = {});
void AddTotal(log_id_t log_id, uint16_t size) EXCLUDES(lock_);
@@ -543,7 +544,7 @@
bool ShouldPrune(log_id id, unsigned long max_size, unsigned long* prune_rows) const
EXCLUDES(lock_);
- // Snapshot of the sizes for a given log buffer.
+ // Return the consumed size of the given buffer.
size_t Sizes(log_id_t id) const EXCLUDES(lock_) {
auto lock = std::lock_guard{lock_};
if (overhead_[id]) {
@@ -551,11 +552,19 @@
}
return mSizes[id];
}
+
+ // Return the uncompressed size of the contents of the given buffer.
+ size_t SizeReadable(log_id_t id) const EXCLUDES(lock_) {
+ auto lock = std::lock_guard{lock_};
+ return mSizes[id];
+ }
+
// TODO: Get rid of this entirely.
static size_t sizesTotal() {
return SizesTotal;
}
+ std::string ReportInteresting() const EXCLUDES(lock_);
std::string Format(uid_t uid, pid_t pid, unsigned int logMask) const EXCLUDES(lock_);
const char* PidToName(pid_t pid) const EXCLUDES(lock_);
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 1b7107f..6ab3b48 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -24,6 +24,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/uio.h>
#include <unistd.h>
#include <string>
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index df78a50..c0f62d3 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -20,12 +20,13 @@
#include <sys/types.h>
#include <private/android_logger.h>
-#include <sysutils/SocketClient.h>
#include <utils/FastStrcmp.h>
// Hijack this header as a common include file used by most all sources
// to report some utilities defined here and there.
+#define LOGD_SNDTIMEO 32
+
namespace android {
// Furnished in main.cpp. Caller must own and free returned value
diff --git a/logd/README.compression.md b/logd/README.compression.md
new file mode 100644
index 0000000..4ba634a
--- /dev/null
+++ b/logd/README.compression.md
@@ -0,0 +1,81 @@
+# Log Compression instead of Chatty in Android S
+
+## The problem
+
+* Log buffer space is precious, but suffers from the tragedy of the commons
+* Log spam fills the buffers making them less useful in logcat/bugreports
+* “Spam” is often in the eye of the beholder: which messages are important depends on what you’re trying to debug
+
+## The idea
+
+* Chatty isn’t helping as much as we’d hoped, and is surprisingly expensive
+* Compress logs to make more efficient use of the buffer
+* Address the root cause of log spam at its source:
+ * Do not hide log spam at runtime, which de-incentivize fixes
+ * Add presubmit coverage similar to SELinux violations to keep log spam down
+
+---
+
+## Chatty in Theory
+
+* Delete messages classified as spam to extend the range of logs from other sources
+* “Spam” defined as:
+ * Logs from UIDs whose logs consume over 12.5% of a log buffer
+ * Back-to-back exact duplicate messages
+
+## Chatty in Practice
+
+* Developer confusion about missing and de-duplicated logs
+* Lowered incentive to fix the root cause of bad logging behavior
+* High CPU overhead
+* Memory usage greatly exceeds configured buffer size
+* Only marginal increase in log range
+
+---
+
+## Log Compression in Theory
+
+* Store many more logs in the same log buffer size => better for diagnosis
+* Memory usage stays below configured log size => better system health
+* No gaps in logs, no de-duplicated logs => no developer confusion
+* No hiding bad behavior => increased accountability/incentive to fix root causes
+
+## Log Compression Preliminary Results
+
+* Captured 2, 5 day periods of full time personal usage of Pixel 4 and replayed the logs offline
+* Compression vs Chatty:
+ * **3.5x more log messages on average**
+ * **50% less CPU usage**
+ * **50% less memory usage**
+
+---
+
+## Log Messages in 1MB
+
+* The number of log messages still available in logcat after ‘Message Count’ messages have been logged to a 1MB log buffer
+* Note: ‘Simple’ is the Chatty code without log spam detection and without de-duplication.
+
+
+
+---
+
+## CPU Time
+
+* Total CPU time on ARM64 (Walleye) and 32bit x86 (Cuttlefish)
+* X axis represents different log buffer size configurations.
+ * Chatty uses significantly more CPU time at 1MB (the default Pixel configuration)
+ * Chatty scales poorly with increased log buffer sizes
+* Note: “simple” isn’t “compression without actually compressing”, it’s “chatty without doing the chatty elimination”, which is why “simple” is more expensive than “compression” on walleye.
+
+
+
+
+---
+
+## Memory Usage
+
+* The memory used by ‘Message Count’ messages, on both Walleye and Cuttlefish
+* Note: Chatty does not consider the metadata (UID, PID, timestamp, etc) in its calculation of log buffer size, so a 1MB log buffer will consume more than 1MB. Note that there are 8 log buffers, 5 of which are typically filled.
+
+
+
diff --git a/logd/README.property b/logd/README.property
index 1d7d990..8fd7f48 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -7,8 +7,8 @@
ro.logd.auditd.events bool true selinux audit messages sent to events.
persist.logd.security bool false Enable security buffer.
ro.organization_owned bool false Override persist.logd.security to false
-ro.logd.kernel bool+ svelte+ Enable klogd daemon
-ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
+ro.logd.kernel bool svelte+ Enable klogd daemon
+logd.statistics bool svelte+ Enable logcat -S statistics.
ro.debuggable number if not "1", logd.statistics &
ro.logd.kernel default false.
logd.logpersistd.enable bool auto Safe to start logpersist daemon service
@@ -57,11 +57,8 @@
NB:
- auto - managed by /init
-- bool+ - "true", "false" and comma separated list of "eng" (forced false if
- ro.debuggable is not "1") or "svelte" (forced false if ro.config.low_ram is
- true).
- svelte - see ro.config.low_ram for details.
-- svelte+ - see ro.config.low_ram and ro.debuggable for details.
+- svelte+ - If empty, default to true if `ro.config.low_ram == false && ro.debuggable == true`
- ro - <base property> temporary override, ro.<base property> platform default.
- persist - <base property> override, persist.<base property> platform default.
- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
diff --git a/logd/README.replay.md b/logd/README.replay.md
new file mode 100644
index 0000000..5f7ec9e
--- /dev/null
+++ b/logd/README.replay.md
@@ -0,0 +1,46 @@
+logd can record and replay log messages for offline analysis.
+
+Recording Messages
+------------------
+
+logd has a `RecordingLogBuffer` buffer that records messages to /data/misc/logd/recorded-messages.
+It stores messages in memory until that file is accessible, in order to capture all messages since
+the beginning of boot. It is only meant for logging developers to use and must be manually enabled
+in by adding `RecordingLogBuffer.cpp` to `Android.bp` and setting
+`log_buffer = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);` in `main.cpp`.
+
+Recording messages may delay the Log() function from completing and it is highly recommended to make
+the logd socket in `liblog` blocking, by removing `SOCK_NONBLOCK` from the `socket()` call in
+`liblog/logd_writer.cpp`.
+
+Replaying Messages
+------------------
+
+Recorded messages can be replayed offline with the `replay_messages` tool. It runs on host and
+device and supports the following options:
+
+1. `interesting` - this prints 'interesting' statistics for each of the log buffer types (simple,
+ chatty, serialized). The statistics are:
+ 1. Log Entry Count
+ 2. Size (the uncompressed size of the log messages in bytes)
+ 3. Overhead (the total cost of the log messages in memory in bytes)
+ 4. Range (the range of time that the logs cover in seconds)
+2. `memory_usage BUFFER_TYPE` - this prints the memory usage (sum of private dirty pages of the
+ `replay_messages` process). Note that the input file is mmap()'ed as RO/Shared so it does not
+ appear in these dirty pages, and a baseline is taken before allocating the log buffers, so only
+ their contributions are measured. The tool outputs the memory usage every 100,000 messages.
+3. `latency BUFFER_TYPE` - this prints statistics of the latency of the Log() function for the given
+ buffer type. It specifically prints the 1st, 2nd, and 3rd quartiles; the 95th, 99th, and 99.99th
+ percentiles; and the maximum latency.
+4. `print_logs BUFFER_TYPE [buffers] [print_point]` - this prints the logs as processed by the given
+ buffer_type from the buffers specified by `buffers` starting after the number of logs specified by
+ `print_point` have been logged. This acts as if a user called `logcat` immediately after the
+ specified logs have been logged, which is particularly useful since it will show the chatty
+ pruning messages at that point. It additionally prints the statistics from `logcat -S` after the
+ logs.
+ `buffers` is a comma separated list of the numeric buffer id values from `<android/log.h>`. For
+ example, `0,1,3` represents the main, radio, and system buffers. It can can also be `all`.
+ `print_point` is an positive integer. If it is unspecified, logs are printed after the entire
+ input file is consumed.
+5. `nothing BUFFER_TYPE` - this does nothing other than read the input file and call Log() for the
+ given buffer type. This is used for profiling CPU usage of strictly the log buffer.
diff --git a/trusty/libtrusty/tipc_ioctl.h b/logd/RecordedLogMessage.h
similarity index 65%
rename from trusty/libtrusty/tipc_ioctl.h
rename to logd/RecordedLogMessage.h
index 27da56a..f18c422 100644
--- a/trusty/libtrusty/tipc_ioctl.h
+++ b/logd/RecordedLogMessage.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,13 +14,17 @@
* limitations under the License.
*/
-#ifndef _TIPC_IOCTL_H
-#define _TIPC_IOCTL_H
+#pragma once
-#include <linux/ioctl.h>
-#include <linux/types.h>
+#include <inttypes.h>
-#define TIPC_IOC_MAGIC 'r'
-#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char *)
+#include <log/log_time.h>
-#endif
+struct __attribute__((packed)) RecordedLogMessage {
+ uint32_t uid;
+ uint32_t pid;
+ uint32_t tid;
+ log_time realtime;
+ uint16_t msg_len;
+ uint8_t log_id;
+};
diff --git a/logd/RecordingLogBuffer.cpp b/logd/RecordingLogBuffer.cpp
new file mode 100644
index 0000000..f5991f3
--- /dev/null
+++ b/logd/RecordingLogBuffer.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 "RecordingLogBuffer.h"
+
+#include <android-base/file.h>
+
+static void WriteLogMessage(int fd, const RecordedLogMessage& meta, const std::string& msg) {
+ android::base::WriteFully(fd, &meta, sizeof(meta));
+ android::base::WriteFully(fd, msg.c_str(), meta.msg_len);
+}
+
+void RecordingLogBuffer::RecordLogMessage(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
+ pid_t tid, const char* msg, uint16_t len) {
+ auto lock = std::lock_guard{lock_};
+ if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ len = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+
+ RecordedLogMessage recorded_log_message = {
+ .uid = uid,
+ .pid = static_cast<uint32_t>(pid),
+ .tid = static_cast<uint32_t>(tid),
+ .realtime = realtime,
+ .msg_len = len,
+ .log_id = static_cast<uint8_t>(log_id),
+ };
+
+ if (!fd_.ok()) {
+ fd_.reset(open("/data/misc/logd/recorded-messages",
+ O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC, 0666));
+ if (!fd_.ok()) {
+ since_boot_messages_.emplace_back(recorded_log_message, std::string(msg, len));
+ return;
+ } else {
+ for (const auto& [meta, msg] : since_boot_messages_) {
+ WriteLogMessage(fd_.get(), meta, msg);
+ }
+ }
+ }
+
+ WriteLogMessage(fd_.get(), recorded_log_message, std::string(msg, len));
+}
+
+int RecordingLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len) {
+ RecordLogMessage(log_id, realtime, uid, pid, tid, msg, len);
+ return SimpleLogBuffer::Log(log_id, realtime, uid, pid, tid, msg, len);
+}
\ No newline at end of file
diff --git a/logd/RecordingLogBuffer.h b/logd/RecordingLogBuffer.h
new file mode 100644
index 0000000..49a0aba
--- /dev/null
+++ b/logd/RecordingLogBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * 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 "SimpleLogBuffer.h"
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "RecordedLogMessage.h"
+
+class RecordingLogBuffer : public SimpleLogBuffer {
+ public:
+ RecordingLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats)
+ : SimpleLogBuffer(reader_list, tags, stats) {}
+
+ int Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
+ uint16_t len) override;
+
+ private:
+ void RecordLogMessage(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len);
+
+ std::vector<std::pair<RecordedLogMessage, std::string>> since_boot_messages_;
+ android::base::unique_fd fd_;
+};
diff --git a/logd/ReplayMessages.cpp b/logd/ReplayMessages.cpp
new file mode 100644
index 0000000..56509ec
--- /dev/null
+++ b/logd/ReplayMessages.cpp
@@ -0,0 +1,472 @@
+/*
+ * 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 <inttypes.h>
+
+#include <chrono>
+#include <map>
+
+#include <android-base/file.h>
+#include <android-base/mapped_file.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <android/log.h>
+#include <log/log_time.h>
+#include <log/logprint.h>
+
+#include "ChattyLogBuffer.h"
+#include "LogBuffer.h"
+#include "LogStatistics.h"
+#include "RecordedLogMessage.h"
+#include "SerializedLogBuffer.h"
+#include "SimpleLogBuffer.h"
+
+using android::base::MappedFile;
+using android::base::ParseInt;
+using android::base::ParseUint;
+using android::base::Split;
+
+char* android::uidToName(uid_t) {
+ return nullptr;
+}
+
+static size_t GetPrivateDirty() {
+ // Allocate once and hope that we don't need to reallocate >40000, to prevent heap fragmentation
+ static std::string smaps(40000, '\0');
+ android::base::ReadFileToString("/proc/self/smaps", &smaps);
+
+ size_t result = 0;
+ size_t base = 0;
+ size_t found;
+ while (true) {
+ found = smaps.find("Private_Dirty:", base);
+ if (found == smaps.npos) break;
+
+ found += sizeof("Private_Dirty:");
+
+ result += atoi(&smaps[found]);
+
+ base = found + 1;
+ }
+
+ return result;
+}
+
+static AndroidLogFormat* GetLogFormat() {
+ static AndroidLogFormat* format = [] {
+ auto* format = android_log_format_new();
+ android_log_setPrintFormat(format, android_log_formatFromString("threadtime"));
+ android_log_setPrintFormat(format, android_log_formatFromString("uid"));
+ return format;
+ }();
+ return format;
+}
+
+static void PrintMessage(struct log_msg* buf) {
+ bool is_binary =
+ buf->id() == LOG_ID_EVENTS || buf->id() == LOG_ID_STATS || buf->id() == LOG_ID_SECURITY;
+
+ AndroidLogEntry entry;
+ int err;
+ if (is_binary) {
+ char binaryMsgBuf[1024];
+ err = android_log_processBinaryLogBuffer(&buf->entry, &entry, nullptr, binaryMsgBuf,
+ sizeof(binaryMsgBuf));
+ } else {
+ err = android_log_processLogBuffer(&buf->entry, &entry);
+ }
+ if (err < 0) {
+ fprintf(stderr, "Error parsing log message\n");
+ }
+
+ android_log_printLogLine(GetLogFormat(), STDOUT_FILENO, &entry);
+}
+
+static log_time GetFirstTimeStamp(const MappedFile& recorded_messages) {
+ if (sizeof(RecordedLogMessage) >= recorded_messages.size()) {
+ fprintf(stderr, "At least one log message must be present in the input\n");
+ exit(1);
+ }
+
+ auto* meta = reinterpret_cast<RecordedLogMessage*>(recorded_messages.data());
+ return meta->realtime;
+}
+
+static LogMask BuffersToLogMask(const char* buffers) {
+ if (buffers == nullptr || !strcmp(buffers, "all")) {
+ return kLogMaskAll;
+ }
+ auto string_ids = Split(buffers, ",");
+ LogMask log_mask = 0;
+ for (const auto& string_id : string_ids) {
+ int buffer_id;
+ if (!ParseInt(string_id, &buffer_id, 0, 7)) {
+ fprintf(stderr, "Could not parse buffer_id '%s'\n", string_id.c_str());
+ exit(1);
+ }
+ log_mask |= 1 << buffer_id;
+ }
+ return log_mask;
+}
+
+class StdoutWriter : public LogWriter {
+ public:
+ StdoutWriter() : LogWriter(0, true) {}
+ bool Write(const logger_entry& entry, const char* message) override {
+ struct log_msg log_msg;
+ log_msg.entry = entry;
+ if (log_msg.entry.len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ fprintf(stderr, "payload too large %" PRIu16, log_msg.entry.len);
+ exit(1);
+ }
+ memcpy(log_msg.msg(), message, log_msg.entry.len);
+
+ PrintMessage(&log_msg);
+
+ return true;
+ }
+
+ void Shutdown() override {
+ fprintf(stderr, "LogWriter::Shutdown() called\n");
+ exit(1);
+ }
+
+ std::string name() const override { return "stdout writer"; }
+};
+
+class Operation {
+ public:
+ virtual ~Operation() {}
+
+ virtual void Begin() {}
+ virtual void Log(const RecordedLogMessage& meta, const char* msg) = 0;
+ virtual void End() {}
+};
+
+class PrintInteresting : public Operation {
+ public:
+ PrintInteresting(log_time first_log_timestamp)
+ : stats_simple_{false, false, first_log_timestamp},
+ stats_chatty_{false, false, first_log_timestamp},
+ stats_serialized_{false, true, first_log_timestamp} {}
+
+ void Begin() override {
+ printf("message_count,simple_main_lines,simple_radio_lines,simple_events_lines,simple_"
+ "system_lines,simple_crash_lines,simple_stats_lines,simple_security_lines,simple_"
+ "kernel_lines,simple_main_size,simple_radio_size,simple_events_size,simple_system_"
+ "size,simple_crash_size,simple_stats_size,simple_security_size,simple_kernel_size,"
+ "simple_main_overhead,simple_radio_overhead,simple_events_overhead,simple_system_"
+ "overhead,simple_crash_overhead,simple_stats_overhead,simple_security_overhead,"
+ "simple_kernel_overhead,simple_main_range,simple_radio_range,simple_events_range,"
+ "simple_system_range,simple_crash_range,simple_stats_range,simple_security_range,"
+ "simple_kernel_range,chatty_main_lines,chatty_radio_lines,chatty_events_lines,"
+ "chatty_system_lines,chatty_crash_lines,chatty_stats_lines,chatty_security_lines,"
+ "chatty_"
+ "kernel_lines,chatty_main_size,chatty_radio_size,chatty_events_size,chatty_system_"
+ "size,chatty_crash_size,chatty_stats_size,chatty_security_size,chatty_kernel_size,"
+ "chatty_main_overhead,chatty_radio_overhead,chatty_events_overhead,chatty_system_"
+ "overhead,chatty_crash_overhead,chatty_stats_overhead,chatty_security_overhead,"
+ "chatty_kernel_overhead,chatty_main_range,chatty_radio_range,chatty_events_range,"
+ "chatty_system_range,chatty_crash_range,chatty_stats_range,chatty_security_range,"
+ "chatty_kernel_range,serialized_main_lines,serialized_radio_lines,serialized_events_"
+ "lines,serialized_"
+ "system_lines,serialized_crash_lines,serialized_stats_lines,serialized_security_"
+ "lines,serialized_"
+ "kernel_lines,serialized_main_size,serialized_radio_size,serialized_events_size,"
+ "serialized_system_"
+ "size,serialized_crash_size,serialized_stats_size,serialized_security_size,"
+ "serialized_kernel_size,"
+ "serialized_main_overhead,serialized_radio_overhead,serialized_events_overhead,"
+ "serialized_system_"
+ "overhead,serialized_crash_overhead,serialized_stats_overhead,serialized_security_"
+ "overhead,"
+ "serialized_kernel_overhead,serialized_main_range,serialized_radio_range,serialized_"
+ "events_range,"
+ "serialized_system_range,serialized_crash_range,serialized_stats_range,serialized_"
+ "security_range,"
+ "serialized_kernel_range\n");
+ }
+
+ void Log(const RecordedLogMessage& meta, const char* msg) override {
+ simple_log_buffer_.Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid,
+ meta.pid, meta.tid, msg, meta.msg_len);
+
+ chatty_log_buffer_.Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid,
+ meta.pid, meta.tid, msg, meta.msg_len);
+
+ serialized_log_buffer_.Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid,
+ meta.pid, meta.tid, msg, meta.msg_len);
+
+ if (num_message_ % 10000 == 0) {
+ printf("%" PRIu64 ",%s,%s,%s\n", num_message_,
+ stats_simple_.ReportInteresting().c_str(),
+ stats_chatty_.ReportInteresting().c_str(),
+ stats_serialized_.ReportInteresting().c_str());
+ }
+
+ num_message_++;
+ }
+
+ private:
+ uint64_t num_message_ = 1;
+
+ LogReaderList reader_list_;
+ LogTags tags_;
+ PruneList prune_list_;
+
+ LogStatistics stats_simple_;
+ SimpleLogBuffer simple_log_buffer_{&reader_list_, &tags_, &stats_simple_};
+
+ LogStatistics stats_chatty_;
+ ChattyLogBuffer chatty_log_buffer_{&reader_list_, &tags_, &prune_list_, &stats_chatty_};
+
+ LogStatistics stats_serialized_;
+ SerializedLogBuffer serialized_log_buffer_{&reader_list_, &tags_, &stats_serialized_};
+};
+
+class SingleBufferOperation : public Operation {
+ public:
+ SingleBufferOperation(log_time first_log_timestamp, const char* buffer) {
+ if (!strcmp(buffer, "simple")) {
+ stats_.reset(new LogStatistics{false, false, first_log_timestamp});
+ log_buffer_.reset(new SimpleLogBuffer(&reader_list_, &tags_, stats_.get()));
+ } else if (!strcmp(buffer, "chatty")) {
+ stats_.reset(new LogStatistics{false, false, first_log_timestamp});
+ log_buffer_.reset(
+ new ChattyLogBuffer(&reader_list_, &tags_, &prune_list_, stats_.get()));
+ } else if (!strcmp(buffer, "serialized")) {
+ stats_.reset(new LogStatistics{false, true, first_log_timestamp});
+ log_buffer_.reset(new SerializedLogBuffer(&reader_list_, &tags_, stats_.get()));
+ } else {
+ fprintf(stderr, "invalid log buffer type '%s'\n", buffer);
+ abort();
+ }
+ }
+
+ void Log(const RecordedLogMessage& meta, const char* msg) override {
+ PreOperation();
+ log_buffer_->Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid, meta.pid,
+ meta.tid, msg, meta.msg_len);
+
+ Operation();
+
+ num_message_++;
+ }
+
+ virtual void PreOperation() {}
+ virtual void Operation() {}
+
+ protected:
+ uint64_t num_message_ = 1;
+
+ LogReaderList reader_list_;
+ LogTags tags_;
+ PruneList prune_list_;
+
+ std::unique_ptr<LogStatistics> stats_;
+ std::unique_ptr<LogBuffer> log_buffer_;
+};
+
+class PrintMemory : public SingleBufferOperation {
+ public:
+ PrintMemory(log_time first_log_timestamp, const char* buffer)
+ : SingleBufferOperation(first_log_timestamp, buffer) {}
+
+ void Operation() override {
+ if (num_message_ % 100000 == 0) {
+ printf("%" PRIu64 ",%s\n", num_message_,
+ std::to_string(GetPrivateDirty() - baseline_memory_).c_str());
+ }
+ }
+
+ private:
+ size_t baseline_memory_ = GetPrivateDirty();
+};
+
+class PrintLogs : public SingleBufferOperation {
+ public:
+ PrintLogs(log_time first_log_timestamp, const char* buffer, const char* buffers,
+ const char* print_point)
+ : SingleBufferOperation(first_log_timestamp, buffer) {
+ mask_ = BuffersToLogMask(buffers);
+ if (print_point != nullptr) {
+ uint64_t result = 0;
+ if (!ParseUint(print_point, &result)) {
+ fprintf(stderr, "Could not parse print point '%s'\n", print_point);
+ exit(1);
+ }
+ print_point_ = result;
+ }
+ }
+
+ void Operation() override {
+ if (print_point_ && num_message_ >= *print_point_) {
+ End();
+ exit(0);
+ }
+ }
+
+ void End() override {
+ std::unique_ptr<LogWriter> test_writer(new StdoutWriter());
+ std::unique_ptr<FlushToState> flush_to_state = log_buffer_->CreateFlushToState(1, mask_);
+ log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr);
+
+ auto stats_string = stats_->Format(0, 0, mask_);
+ printf("%s\n", stats_string.c_str());
+ }
+
+ private:
+ LogMask mask_ = kLogMaskAll;
+ std::optional<uint64_t> print_point_;
+};
+
+class PrintLatency : public SingleBufferOperation {
+ public:
+ PrintLatency(log_time first_log_timestamp, const char* buffer)
+ : SingleBufferOperation(first_log_timestamp, buffer) {}
+
+ void PreOperation() override { operation_start_ = std::chrono::steady_clock::now(); }
+
+ void Operation() override {
+ auto end = std::chrono::steady_clock::now();
+ auto duration = (end - operation_start_).count();
+ durations_.emplace_back(duration);
+ }
+
+ void End() override {
+ std::sort(durations_.begin(), durations_.end());
+ auto q1 = durations_.size() / 4;
+ auto q2 = durations_.size() / 2;
+ auto q3 = 3 * durations_.size() / 4;
+
+ auto p95 = 95 * durations_.size() / 100;
+ auto p99 = 99 * durations_.size() / 100;
+ auto p9999 = 9999 * durations_.size() / 10000;
+
+ printf("q1: %lld q2: %lld q3: %lld p95: %lld p99: %lld p99.99: %lld max: %lld\n",
+ durations_[q1], durations_[q2], durations_[q3], durations_[p95], durations_[p99],
+ durations_[p9999], durations_.back());
+ }
+
+ private:
+ std::chrono::steady_clock::time_point operation_start_;
+ std::vector<long long> durations_;
+};
+
+class PrintAllLogs : public SingleBufferOperation {
+ public:
+ PrintAllLogs(log_time first_log_timestamp, const char* buffer, const char* buffers)
+ : SingleBufferOperation(first_log_timestamp, buffer) {
+ LogMask mask = BuffersToLogMask(buffers);
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ std::unique_ptr<LogWriter> stdout_writer(new StdoutWriter());
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(stdout_writer),
+ false, 0, mask, 0, {}, 1, {}));
+ reader_list_.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ void Operation() override {
+ // If the rate of reading logs is slower than the rate of incoming logs, then the reader
+ // thread is disconnected to not overflow log buffers, therefore we artificially slow down
+ // the incoming log rate.
+ usleep(100);
+ }
+};
+
+int main(int argc, char** argv) {
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s FILE OPERATION [BUFFER] [OPTIONS]\n", argv[0]);
+ return 1;
+ }
+
+ if (strcmp(argv[2], "interesting") != 0 && argc < 4) {
+ fprintf(stderr, "Operations other than 'interesting' require a BUFFER argument\n");
+ return 1;
+ }
+
+ int recorded_messages_fd = open(argv[1], O_RDONLY);
+ if (recorded_messages_fd == -1) {
+ fprintf(stderr, "Couldn't open input file\n");
+ return 1;
+ }
+ struct stat fd_stat;
+ if (fstat(recorded_messages_fd, &fd_stat) != 0) {
+ fprintf(stderr, "Couldn't fstat input file\n");
+ return 1;
+ }
+ auto recorded_messages = MappedFile::FromFd(recorded_messages_fd, 0,
+ static_cast<size_t>(fd_stat.st_size), PROT_READ);
+ if (recorded_messages == nullptr) {
+ fprintf(stderr, "Couldn't mmap input file\n");
+ return 1;
+ }
+
+ // LogStatistics typically uses 'now()' to initialize its log range state, but this doesn't work
+ // when replaying older logs, so we instead give it the timestamp from the first log.
+ log_time first_log_timestamp = GetFirstTimeStamp(*recorded_messages);
+
+ std::unique_ptr<Operation> operation;
+ if (!strcmp(argv[2], "interesting")) {
+ operation.reset(new PrintInteresting(first_log_timestamp));
+ } else if (!strcmp(argv[2], "memory_usage")) {
+ operation.reset(new PrintMemory(first_log_timestamp, argv[3]));
+ } else if (!strcmp(argv[2], "latency")) {
+ operation.reset(new PrintLatency(first_log_timestamp, argv[3]));
+ } else if (!strcmp(argv[2], "print_logs")) {
+ operation.reset(new PrintLogs(first_log_timestamp, argv[3], argc > 4 ? argv[4] : nullptr,
+ argc > 5 ? argv[5] : nullptr));
+ } else if (!strcmp(argv[2], "print_all_logs")) {
+ operation.reset(
+ new PrintAllLogs(first_log_timestamp, argv[3], argc > 4 ? argv[4] : nullptr));
+ } else if (!strcmp(argv[2], "nothing")) {
+ operation.reset(new SingleBufferOperation(first_log_timestamp, argv[3]));
+ } else {
+ fprintf(stderr, "unknown operation '%s'\n", argv[2]);
+ return 1;
+ }
+
+ // LogBuffer::Log() won't log without this on host.
+ __android_log_set_minimum_priority(ANDROID_LOG_VERBOSE);
+ // But we still want to suppress messages <= error to not interrupt the rest of the output.
+ __android_log_set_logger([](const struct __android_log_message* log_message) {
+ if (log_message->priority < ANDROID_LOG_ERROR) {
+ return;
+ }
+ __android_log_stderr_logger(log_message);
+ });
+
+ operation->Begin();
+
+ uint64_t read_position = 0;
+ while (read_position + sizeof(RecordedLogMessage) < recorded_messages->size()) {
+ auto* meta =
+ reinterpret_cast<RecordedLogMessage*>(recorded_messages->data() + read_position);
+ if (read_position + sizeof(RecordedLogMessage) + meta->msg_len >=
+ recorded_messages->size()) {
+ break;
+ }
+ char* msg = recorded_messages->data() + read_position + sizeof(RecordedLogMessage);
+ read_position += sizeof(RecordedLogMessage) + meta->msg_len;
+
+ operation->Log(*meta, msg);
+ }
+
+ operation->End();
+
+ return 0;
+}
diff --git a/logd/SerializedLogBuffer.cpp b/logd/SerializedLogBuffer.cpp
index 972a3f3..5012d3d 100644
--- a/logd/SerializedLogBuffer.cpp
+++ b/logd/SerializedLogBuffer.cpp
@@ -23,6 +23,7 @@
#include <android-base/logging.h>
#include <android-base/scopeguard.h>
+#include "LogSize.h"
#include "LogStatistics.h"
#include "SerializedFlushToState.h"
@@ -34,8 +35,8 @@
void SerializedLogBuffer::Init() {
log_id_for_each(i) {
- if (SetSize(i, __android_logger_get_buffer_size(i))) {
- SetSize(i, LOG_BUFFER_MIN_SIZE);
+ if (!SetSize(i, GetBufferSizeFromProperties(i))) {
+ SetSize(i, kLogBufferMinSize);
}
}
@@ -74,6 +75,10 @@
return -EINVAL;
}
+ if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ len = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+
if (!ShouldLog(log_id, msg, len)) {
stats_->AddTotal(log_id, len);
return -EACCES;
@@ -134,33 +139,42 @@
}
}
-bool SerializedLogBuffer::Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) {
- // Don't prune logs that are newer than the point at which any reader threads are reading from.
- LogReaderThread* oldest = nullptr;
+void SerializedLogBuffer::Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) {
auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
- for (const auto& reader_thread : reader_list_->reader_threads()) {
- if (!reader_thread->IsWatching(log_id)) {
- continue;
- }
- if (!oldest || oldest->start() > reader_thread->start() ||
- (oldest->start() == reader_thread->start() &&
- reader_thread->deadline().time_since_epoch().count() != 0)) {
- oldest = reader_thread.get();
- }
- }
auto& log_buffer = logs_[log_id];
auto it = log_buffer.begin();
while (it != log_buffer.end()) {
- if (oldest != nullptr && it->highest_sequence_number() >= oldest->start()) {
- break;
+ for (const auto& reader_thread : reader_list_->reader_threads()) {
+ if (!reader_thread->IsWatching(log_id)) {
+ continue;
+ }
+
+ if (reader_thread->deadline().time_since_epoch().count() != 0) {
+ // Always wake up wrapped readers when pruning. 'Wrapped' readers are an
+ // optimization that allows the reader to wait until logs starting at a specified
+ // time stamp are about to be pruned. This is error-prone however, since if that
+ // timestamp is about to be pruned, the reader is not likely to read the messages
+ // fast enough to not back-up logd. Instead, we can achieve an nearly-as-efficient
+ // but not error-prune batching effect by waking the reader whenever any chunk is
+ // about to be pruned.
+ reader_thread->triggerReader_Locked();
+ }
+
+ // Some readers may be still reading from this log chunk, log a warning that they are
+ // about to lose logs.
+ // TODO: We should forcefully disconnect the reader instead, such that the reader itself
+ // has an indication that they've lost logs.
+ if (reader_thread->start() <= it->highest_sequence_number()) {
+ LOG(WARNING) << "Skipping entries from slow reader, " << reader_thread->name()
+ << ", from LogBuffer::Prune()";
+ }
}
// Increment ahead of time since we're going to erase this iterator from the list.
auto it_to_prune = it++;
- // The sequence number check ensures that all readers have read all logs in this chunk, but
- // they may still hold a reference to the chunk to track their last read log_position.
+ // Readers may have a reference to the chunk to track their last read log_position.
// Notify them to delete the reference.
NotifyReadersOfPrune(log_id, it_to_prune);
@@ -175,42 +189,11 @@
RemoveChunkFromStats(log_id, *it_to_prune);
log_buffer.erase(it_to_prune);
if (buffer_size >= bytes_to_free) {
- return true;
+ return;
}
bytes_to_free -= buffer_size;
}
}
-
- // If we've deleted all buffers without bytes_to_free hitting 0, then we're called by Clear()
- // and should return true.
- if (it == log_buffer.end()) {
- return true;
- }
-
- // Otherwise we are stuck due to a reader, so mitigate it.
- CHECK(oldest != nullptr);
- KickReader(oldest, log_id, bytes_to_free);
- return false;
-}
-
-// If the selected reader is blocking our pruning progress, decide on
-// what kind of mitigation is necessary to unblock the situation.
-void SerializedLogBuffer::KickReader(LogReaderThread* reader, log_id_t id, size_t bytes_to_free) {
- if (bytes_to_free >= max_size_[id]) { // +100%
- // A misbehaving or slow reader is dropped if we hit too much memory pressure.
- LOG(WARNING) << "Kicking blocked reader, " << reader->name()
- << ", from LogBuffer::kickMe()";
- reader->release_Locked();
- } else if (reader->deadline().time_since_epoch().count() != 0) {
- // Allow a blocked WRAP deadline reader to trigger and start reporting the log data.
- reader->triggerReader_Locked();
- } else {
- // Tell slow reader to skip entries to catch up.
- unsigned long prune_rows = bytes_to_free / 300;
- LOG(WARNING) << "Skipping " << prune_rows << " entries from slow reader, " << reader->name()
- << ", from LogBuffer::kickMe()";
- reader->triggerSkip_Locked(id, prune_rows);
- }
}
std::unique_ptr<FlushToState> SerializedLogBuffer::CreateFlushToState(uint64_t start,
@@ -251,12 +234,18 @@
}
}
+ // We copy the log entry such that we can flush it without the lock. We never block pruning
+ // waiting for this Flush() to complete.
+ constexpr size_t kMaxEntrySize = sizeof(*entry) + LOGGER_ENTRY_MAX_PAYLOAD + 1;
+ unsigned char entry_copy[kMaxEntrySize] __attribute__((uninitialized));
+ CHECK_LT(entry->msg_len(), LOGGER_ENTRY_MAX_PAYLOAD + 1);
+ memcpy(entry_copy, entry, sizeof(*entry) + entry->msg_len());
lock.unlock();
- // We never prune logs equal to or newer than any LogReaderThreads' `start` value, so the
- // `entry` pointer is safe here without the lock
- if (!entry->Flush(writer, log_id)) {
+
+ if (!reinterpret_cast<SerializedLogEntry*>(entry_copy)->Flush(writer, log_id)) {
return false;
}
+
lock.lock();
}
@@ -265,41 +254,14 @@
}
bool SerializedLogBuffer::Clear(log_id_t id, uid_t uid) {
- // Try three times to clear, then disconnect the readers and try one final time.
- for (int retry = 0; retry < 3; ++retry) {
- {
- auto lock = std::lock_guard{lock_};
- bool prune_success = Prune(id, ULONG_MAX, uid);
- if (prune_success) {
- return true;
- }
- }
- sleep(1);
- }
- // Check if it is still busy after the sleep, we try to prune one entry, not another clear run,
- // so we are looking for the quick side effect of the return value to tell us if we have a
- // _blocked_ reader.
- bool busy = false;
- {
- auto lock = std::lock_guard{lock_};
- busy = !Prune(id, 1, uid);
- }
- // It is still busy, disconnect all readers.
- if (busy) {
- auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
- for (const auto& reader_thread : reader_list_->reader_threads()) {
- if (reader_thread->IsWatching(id)) {
- LOG(WARNING) << "Kicking blocked reader, " << reader_thread->name()
- << ", from LogBuffer::clear()";
- reader_thread->release_Locked();
- }
- }
- }
auto lock = std::lock_guard{lock_};
- return Prune(id, ULONG_MAX, uid);
+ Prune(id, ULONG_MAX, uid);
+
+ // Clearing SerializedLogBuffer never waits for readers and therefore is always successful.
+ return true;
}
-unsigned long SerializedLogBuffer::GetSizeUsed(log_id_t id) {
+size_t SerializedLogBuffer::GetSizeUsed(log_id_t id) {
size_t total_size = 0;
for (const auto& chunk : logs_[id]) {
total_size += chunk.PruneSize();
@@ -307,7 +269,7 @@
return total_size;
}
-unsigned long SerializedLogBuffer::GetSize(log_id_t id) {
+size_t SerializedLogBuffer::GetSize(log_id_t id) {
auto lock = std::lock_guard{lock_};
return max_size_[id];
}
@@ -315,10 +277,10 @@
// New SerializedLogChunk objects will be allocated according to the new size, but older one are
// unchanged. MaybePrune() is called on the log buffer to reduce it to an appropriate size if the
// new size is lower.
-int SerializedLogBuffer::SetSize(log_id_t id, unsigned long size) {
+bool SerializedLogBuffer::SetSize(log_id_t id, size_t size) {
// Reasonable limits ...
- if (!__android_logger_valid_buffer_size(size)) {
- return -1;
+ if (!IsValidBufferSize(size)) {
+ return false;
}
auto lock = std::lock_guard{lock_};
@@ -326,5 +288,5 @@
MaybePrune(id);
- return 0;
+ return true;
}
diff --git a/logd/SerializedLogBuffer.h b/logd/SerializedLogBuffer.h
index a03050e..294cfe6 100644
--- a/logd/SerializedLogBuffer.h
+++ b/logd/SerializedLogBuffer.h
@@ -47,27 +47,25 @@
log_time realtime)>& filter) override;
bool Clear(log_id_t id, uid_t uid) override;
- unsigned long GetSize(log_id_t id) override;
- int SetSize(log_id_t id, unsigned long size) override;
+ size_t GetSize(log_id_t id) override;
+ bool SetSize(log_id_t id, size_t size) override;
uint64_t sequence() const override { return sequence_.load(std::memory_order_relaxed); }
private:
bool ShouldLog(log_id_t log_id, const char* msg, uint16_t len);
void MaybePrune(log_id_t log_id) REQUIRES(lock_);
- bool Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) REQUIRES(lock_);
- void KickReader(LogReaderThread* reader, log_id_t id, size_t bytes_to_free)
- REQUIRES_SHARED(lock_);
+ void Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) REQUIRES(lock_);
void NotifyReadersOfPrune(log_id_t log_id, const std::list<SerializedLogChunk>::iterator& chunk)
REQUIRES(reader_list_->reader_threads_lock());
void RemoveChunkFromStats(log_id_t log_id, SerializedLogChunk& chunk);
- unsigned long GetSizeUsed(log_id_t id) REQUIRES(lock_);
+ size_t GetSizeUsed(log_id_t id) REQUIRES(lock_);
LogReaderList* reader_list_;
LogTags* tags_;
LogStatistics* stats_;
- unsigned long max_size_[LOG_ID_MAX] GUARDED_BY(lock_) = {};
+ size_t max_size_[LOG_ID_MAX] GUARDED_BY(lock_) = {};
std::list<SerializedLogChunk> logs_[LOG_ID_MAX] GUARDED_BY(lock_);
RwLock lock_;
diff --git a/logd/SimpleLogBuffer.cpp b/logd/SimpleLogBuffer.cpp
index ec08d54..b00dd25 100644
--- a/logd/SimpleLogBuffer.cpp
+++ b/logd/SimpleLogBuffer.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include "LogBufferElement.h"
+#include "LogSize.h"
SimpleLogBuffer::SimpleLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats)
: reader_list_(reader_list), tags_(tags), stats_(stats) {
@@ -29,8 +30,8 @@
void SimpleLogBuffer::Init() {
log_id_for_each(i) {
- if (SetSize(i, __android_logger_get_buffer_size(i))) {
- SetSize(i, LOG_BUFFER_MIN_SIZE);
+ if (!SetSize(i, GetBufferSizeFromProperties(i))) {
+ SetSize(i, kLogBufferMinSize);
}
}
@@ -247,22 +248,22 @@
}
// get the total space allocated to "id"
-unsigned long SimpleLogBuffer::GetSize(log_id_t id) {
+size_t SimpleLogBuffer::GetSize(log_id_t id) {
auto lock = SharedLock{lock_};
size_t retval = max_size_[id];
return retval;
}
// set the total space allocated to "id"
-int SimpleLogBuffer::SetSize(log_id_t id, unsigned long size) {
+bool SimpleLogBuffer::SetSize(log_id_t id, size_t size) {
// Reasonable limits ...
- if (!__android_logger_valid_buffer_size(size)) {
- return -1;
+ if (!IsValidBufferSize(size)) {
+ return false;
}
auto lock = std::lock_guard{lock_};
max_size_[id] = size;
- return 0;
+ return true;
}
void SimpleLogBuffer::MaybePrune(log_id_t id) {
diff --git a/logd/SimpleLogBuffer.h b/logd/SimpleLogBuffer.h
index 9f7d699..8e5b50e 100644
--- a/logd/SimpleLogBuffer.h
+++ b/logd/SimpleLogBuffer.h
@@ -41,8 +41,8 @@
log_time realtime)>& filter) override;
bool Clear(log_id_t id, uid_t uid) override;
- unsigned long GetSize(log_id_t id) override;
- int SetSize(log_id_t id, unsigned long size) override final;
+ size_t GetSize(log_id_t id) override;
+ bool SetSize(log_id_t id, size_t size) override final;
uint64_t sequence() const override { return sequence_.load(std::memory_order_relaxed); }
@@ -60,7 +60,7 @@
LogStatistics* stats() { return stats_; }
LogReaderList* reader_list() { return reader_list_; }
- unsigned long max_size(log_id_t id) REQUIRES_SHARED(lock_) { return max_size_[id]; }
+ size_t max_size(log_id_t id) REQUIRES_SHARED(lock_) { return max_size_[id]; }
std::list<LogBufferElement>& logs() { return logs_; }
RwLock lock_;
@@ -75,7 +75,7 @@
std::atomic<uint64_t> sequence_ = 1;
- unsigned long max_size_[LOG_ID_MAX] GUARDED_BY(lock_);
+ size_t max_size_[LOG_ID_MAX] GUARDED_BY(lock_);
std::list<LogBufferElement> logs_ GUARDED_BY(lock_);
// Keeps track of the iterator to the oldest log message of a given log type, as an
// optimization when pruning logs. Use GetOldest() to retrieve.
diff --git a/logd/doc_images/cpu_cuttlefish.png b/logd/doc_images/cpu_cuttlefish.png
new file mode 100644
index 0000000..8d809ca
--- /dev/null
+++ b/logd/doc_images/cpu_cuttlefish.png
Binary files differ
diff --git a/logd/doc_images/cpu_walleye.png b/logd/doc_images/cpu_walleye.png
new file mode 100644
index 0000000..39c951b
--- /dev/null
+++ b/logd/doc_images/cpu_walleye.png
Binary files differ
diff --git a/logd/doc_images/memory_usage.png b/logd/doc_images/memory_usage.png
new file mode 100644
index 0000000..434d6d3
--- /dev/null
+++ b/logd/doc_images/memory_usage.png
Binary files differ
diff --git a/logd/doc_images/total_log_count.png b/logd/doc_images/total_log_count.png
new file mode 100644
index 0000000..e73c2c1
--- /dev/null
+++ b/logd/doc_images/total_log_count.png
Binary files differ
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 8309f95..d71a2f9 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -30,16 +30,6 @@
#define MIN_TAG_ID 1000
#define TAG_MOD 10
-#ifndef __ANDROID__
-unsigned long __android_logger_get_buffer_size(log_id_t) {
- return 1024 * 1024;
-}
-
-bool __android_logger_valid_buffer_size(unsigned long) {
- return true;
-}
-#endif
-
char* android::uidToName(uid_t) {
return strdup("fake");
}
diff --git a/logd/logd_test.cpp b/logd/logd_test.cpp
index ed34ea4..828f580 100644
--- a/logd/logd_test.cpp
+++ b/logd/logd_test.cpp
@@ -30,6 +30,7 @@
#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include <gtest/gtest.h>
#include <log/log_read.h>
@@ -39,7 +40,9 @@
#include <selinux/selinux.h>
#endif
-#include "LogReader.h" // pickup LOGD_SNDTIMEO
+#include "LogUtils.h" // For LOGD_SNDTIMEO.
+
+using android::base::unique_fd;
#ifdef __ANDROID__
static void send_to_control(char* buf, size_t len) {
@@ -162,6 +165,7 @@
}
#endif
+#ifdef LOGD_ENABLE_FLAKY_TESTS
TEST(logd, statistics) {
#ifdef __ANDROID__
size_t len;
@@ -237,6 +241,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
#ifdef __ANDROID__
static void caught_signal(int /* signum */) {
@@ -720,6 +725,7 @@
}
#endif
+#ifdef LOGD_ENABLE_FLAKY_TESTS
// b/27242723 confirmed fixed
TEST(logd, SNDTIMEO) {
#ifdef __ANDROID__
@@ -777,6 +783,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
TEST(logd, getEventTag_list) {
#ifdef __ANDROID__
@@ -833,126 +840,33 @@
#endif
}
+TEST(logd, no_epipe) {
#ifdef __ANDROID__
-static inline uint32_t get4LE(const uint8_t* src) {
- return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
+ // Actually generating SIGPIPE in logd is racy, since we need to close the socket quicker than
+ // logd finishes writing the data to it, so we try 10 times, which should be enough to trigger
+ // SIGPIPE if logd isn't ignoring SIGPIPE
+ for (int i = 0; i < 10; ++i) {
+ unique_fd sock1(
+ socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
+ ASSERT_GT(sock1, 0);
+ unique_fd sock2(
+ socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
+ ASSERT_GT(sock2, 0);
-static inline uint32_t get4LE(const char* src) {
- return get4LE(reinterpret_cast<const uint8_t*>(src));
-}
-#endif
+ std::string message = "getStatistics 0 1 2 3 4 5 6 7";
-void __android_log_btwrite_multiple__helper(int count) {
-#ifdef __ANDROID__
- log_time ts(CLOCK_MONOTONIC);
- usleep(100);
- log_time ts1(CLOCK_MONOTONIC);
+ ASSERT_GT(write(sock1, message.c_str(), message.length()), 0);
+ sock1.reset();
+ ASSERT_GT(write(sock2, message.c_str(), message.length()), 0);
- // We fork to create a unique pid for the submitted log messages
- // so that we do not collide with the other _multiple_ tests.
+ struct pollfd p = {.fd = sock2, .events = POLLIN, .revents = 0};
- pid_t pid = fork();
-
- if (pid == 0) {
- // child
- for (int i = count; i; --i) {
- ASSERT_LT(
- 0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
- usleep(100);
- }
- ASSERT_LT(0,
- __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
- usleep(1000000);
-
- _exit(0);
+ int ret = poll(&p, 1, 20);
+ EXPECT_EQ(ret, 1);
+ EXPECT_TRUE(p.revents & POLLIN);
+ EXPECT_FALSE(p.revents & POLL_ERR);
}
-
- siginfo_t info = {};
- ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
- ASSERT_EQ(0, info.si_status);
-
- struct logger_list* logger_list;
- ASSERT_TRUE(nullptr != (logger_list = android_logger_list_open(LOG_ID_EVENTS,
- ANDROID_LOG_NONBLOCK, 0, pid)));
-
- int expected_count = (count < 2) ? count : 2;
- int expected_chatty_count = (count <= 2) ? 0 : 1;
- int expected_identical_count = (count < 2) ? 0 : (count - 2);
- static const int expected_expire_count = 0;
-
- count = 0;
- int second_count = 0;
- int chatty_count = 0;
- int identical_count = 0;
- int expire_count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
-
- if ((log_msg.entry.pid != pid) || (log_msg.entry.len < (4 + 1 + 8)) ||
- (log_msg.id() != LOG_ID_EVENTS))
- continue;
-
- char* eventData = log_msg.msg();
- if (!eventData) continue;
-
- uint32_t tag = get4LE(eventData);
-
- if ((eventData[4] == EVENT_TYPE_LONG) &&
- (log_msg.entry.len == (4 + 1 + 8))) {
- if (tag != 0) continue;
-
- log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
- if (ts == *tx) {
- ++count;
- } else if (ts1 == *tx) {
- ++second_count;
- }
- } else if (eventData[4] == EVENT_TYPE_STRING) {
- if (tag != CHATTY_LOG_TAG) continue;
- ++chatty_count;
- // int len = get4LE(eventData + 4 + 1);
- log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
- const char* cp;
- if ((cp = strstr(eventData + 4 + 1 + 4, " identical "))) {
- unsigned val = 0;
- sscanf(cp, " identical %u lines", &val);
- identical_count += val;
- } else if ((cp = strstr(eventData + 4 + 1 + 4, " expire "))) {
- unsigned val = 0;
- sscanf(cp, " expire %u lines", &val);
- expire_count += val;
- }
- }
- }
-
- android_logger_list_close(logger_list);
-
- EXPECT_EQ(expected_count, count);
- EXPECT_EQ(1, second_count);
- EXPECT_EQ(expected_chatty_count, chatty_count);
- EXPECT_EQ(expected_identical_count, identical_count);
- EXPECT_EQ(expected_expire_count, expire_count);
#else
- count = 0;
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-
-TEST(logd, multiple_test_1) {
- __android_log_btwrite_multiple__helper(1);
-}
-
-TEST(logd, multiple_test_2) {
- __android_log_btwrite_multiple__helper(2);
-}
-
-TEST(logd, multiple_test_3) {
- __android_log_btwrite_multiple__helper(3);
-}
-
-TEST(logd, multiple_test_10) {
- __android_log_btwrite_multiple__helper(10);
-}
diff --git a/logd/main.cpp b/logd/main.cpp
index 897e11e..c92c5b7 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -62,7 +62,9 @@
#include "SerializedLogBuffer.h"
#include "SimpleLogBuffer.h"
+using android::base::GetBoolProperty;
using android::base::GetProperty;
+using android::base::SetProperty;
#define KMSG_PRIORITY(PRI) \
'<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
@@ -82,9 +84,10 @@
PLOG(FATAL) << "failed to set batch scheduler";
}
- if (!__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
- prctl(PR_SET_DUMPABLE, 0) == -1) {
- PLOG(FATAL) << "failed to clear PR_SET_DUMPABLE";
+ if (!GetBoolProperty("ro.debuggable", false)) {
+ if (prctl(PR_SET_DUMPABLE, 0) == -1) {
+ PLOG(FATAL) << "failed to clear PR_SET_DUMPABLE";
+ }
}
std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(), cap_free);
@@ -110,6 +113,14 @@
}
}
+// GetBoolProperty that defaults to true if `ro.debuggable == true && ro.config.low_rawm == false`.
+static bool GetBoolPropertyEngSvelteDefault(const std::string& name) {
+ bool default_value =
+ GetBoolProperty("ro.debuggable", false) && !GetBoolProperty("ro.config.low_ram", false);
+
+ return GetBoolProperty(name, default_value);
+}
+
char* android::uidToName(uid_t u) {
struct Userdata {
uid_t uid;
@@ -207,6 +218,8 @@
// logging plugins like auditd and restart control. Additional
// transitory per-client threads are created for each reader.
int main(int argc, char* argv[]) {
+ // We want EPIPE when a reader disconnects, not to terminate logd.
+ signal(SIGPIPE, SIG_IGN);
// logd is written under the assumption that the timezone is UTC.
// If TZ is not set, persist.sys.timezone is looked up in some time utility
// libc functions, including mktime. It confuses the logd time handling,
@@ -236,10 +249,9 @@
}
int fdPmesg = -1;
- bool klogd = __android_logger_property_get_bool(
- "ro.logd.kernel",
- BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+ bool klogd = GetBoolPropertyEngSvelteDefault("ro.logd.kernel");
if (klogd) {
+ SetProperty("ro.logd.kernel", "true");
static const char proc_kmsg[] = "/proc/kmsg";
fdPmesg = android_get_control_file(proc_kmsg);
if (fdPmesg < 0) {
@@ -249,7 +261,7 @@
if (fdPmesg < 0) PLOG(ERROR) << "Failed to open " << proc_kmsg;
}
- bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
+ bool auditd = GetBoolProperty("ro.logd.auditd", true);
DropPrivs(klogd, auditd);
// A cache of event log tags
@@ -258,13 +270,11 @@
// Pruning configuration.
PruneList prune_list;
- std::string buffer_type = GetProperty("logd.buffer_type", "chatty");
+ std::string buffer_type = GetProperty("logd.buffer_type", "serialized");
// Partial (required for chatty) or full logging statistics.
- bool enable_full_log_statistics = __android_logger_property_get_bool(
- "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
- BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
- LogStatistics log_statistics(enable_full_log_statistics, buffer_type == "serialized");
+ LogStatistics log_statistics(GetBoolPropertyEngSvelteDefault("logd.statistics"),
+ buffer_type == "serialized");
// Serves the purpose of managing the last logs times read on a socket connection, and as a
// reader lock on a range of log entries.
@@ -309,9 +319,7 @@
// and LogReader is notified to send updates to connected clients.
LogAudit* al = nullptr;
if (auditd) {
- int dmesg_fd = __android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
- ? fdDmesg
- : -1;
+ int dmesg_fd = GetBoolProperty("ro.logd.auditd.dmesg", true) ? fdDmesg : -1;
al = new LogAudit(log_buffer, dmesg_fd, &log_statistics);
}
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index a643062..3907413 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -139,7 +139,7 @@
auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
- // Sanity check
+ // Smoke test
auto root_node = property_info_area->root_node();
EXPECT_STREQ("root", root_node.name());
EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
diff --git a/qemu_pipe/qemu_pipe.cpp b/qemu_pipe/qemu_pipe.cpp
index beeccb0..03afb21 100644
--- a/qemu_pipe/qemu_pipe.cpp
+++ b/qemu_pipe/qemu_pipe.cpp
@@ -35,7 +35,6 @@
#endif
int qemu_pipe_open(const char* pipeName) {
- // Sanity check.
if (!pipeName) {
errno = EINVAL;
return -1;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 4215dab..9d8100f 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -81,6 +81,11 @@
# Mount tracefs
mount tracefs tracefs /sys/kernel/tracing
+ # create sys dirctory
+ mkdir /dev/sys 0755 system system
+ mkdir /dev/sys/fs 0755 system system
+ mkdir /dev/sys/block 0755 system system
+
# 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
@@ -322,16 +327,6 @@
chmod 0664 /dev/cpuset/restricted/tasks
chmod 0664 /dev/cpuset/tasks
- # freezer cgroup entries
- mkdir /dev/freezer/frozen
- write /dev/freezer/frozen/freezer.state FROZEN
- chown system system /dev/freezer/cgroup.procs
- chown system system /dev/freezer/frozen
- chown system system /dev/freezer/frozen/freezer.state
- chown system system /dev/freezer/frozen/cgroup.procs
-
- chmod 0664 /dev/freezer/frozen/freezer.state
-
# make the PSI monitor accessible to others
chown system system /proc/pressure/memory
chmod 0664 /proc/pressure/memory
@@ -346,8 +341,6 @@
# This is needed by any process that uses socket tagging.
chmod 0644 /dev/xt_qtaguid
- chown root root /dev/cg2_bpf
- chmod 0600 /dev/cg2_bpf
mount bpf bpf /sys/fs/bpf nodev noexec nosuid
# Create location for fs_mgr to store abbreviated output from filesystem
@@ -520,6 +513,7 @@
mkdir /metadata/bootstat 0750 system log
mkdir /metadata/ota 0700 root system
mkdir /metadata/ota/snapshots 0700 root system
+ mkdir /metadata/userspacereboot 0770 root system
mkdir /metadata/apex 0700 root system
mkdir /metadata/apex/sessions 0700 root system
@@ -874,18 +868,26 @@
chown root system /sys/block/zram0/writeback
chmod 0664 /sys/block/zram0/writeback
+ # to access F2FS sysfs on dm-<num> directly
+ mkdir /dev/sys/fs/by-name 0755 system system
+ symlink /sys/fs/f2fs/${dev.mnt.blk.data} /dev/sys/fs/by-name/userdata
+
+ # to access dm-<num> sysfs
+ mkdir /dev/sys/block/by-name 0755 system system
+ symlink /sys/devices/virtual/block/${dev.mnt.blk.data} /dev/sys/block/by-name/userdata
+
# F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,
# to avoid power consumption when system becomes mostly idle. Be careful
# to make it too large, since it may bring userdata loss, if they
# are not aware of using fsync()/sync() to prepare sudden power-cut.
- write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
- write /sys/fs/f2fs/${dev.mnt.blk.data}/gc_urgent_sleep_time 50
- write /sys/fs/f2fs/${dev.mnt.blk.data}/iostat_enable 1
+ write /dev/sys/fs/by-name/userdata/cp_interval 200
+ write /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time 50
+ write /dev/sys/fs/by-name/userdata/iostat_enable 1
# limit discard size to 128MB in order to avoid long IO latency
# for filesystem tuning first (dm or sda)
# Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
- write /sys/devices/virtual/block/${dev.mnt.blk.data}/queue/discard_max_bytes 134217728
+ write /dev/sys/block/by-name/userdata/queue/discard_max_bytes 134217728
# Permissions for System Server and daemons.
chown system system /sys/power/autosleep
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 3bee875..4510dc7 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -98,7 +98,7 @@
toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
-toybox: acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
env expand expr fallocate false find free getenforce getprop groups
head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
@@ -118,7 +118,7 @@
toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
sendevent start stop top
-toybox: acpi base64 basename blockdev bzcat cal cat chcon chgrp chmod
+toybox (0.7.0-ish): acpi base64 basename blockdev bzcat cal cat chcon chgrp chmod
chown chroot cksum clear comm cmp cp cpio cut date df dirname dmesg
dos2unix du echo env expand expr fallocate false find flock free
getenforce getprop groups head hostname hwclock id ifconfig inotifyd
@@ -140,7 +140,7 @@
toolbox: getevent newfs\_msdos
-toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
dos2unix du echo env expand expr fallocate false file find flock free
getenforce getprop groups gunzip gzip head hostname hwclock id ifconfig
@@ -166,7 +166,7 @@
toolbox: getevent getprop newfs\_msdos
-toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
dos2unix du echo env expand expr fallocate false file find flock fmt free
getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
@@ -192,7 +192,7 @@
toolbox: getevent getprop
-toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
+toybox (0.8.0-ish): acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
false fgrep file find flock fmt free freeramdisk fsfreeze getconf
@@ -224,7 +224,7 @@
toolbox: getevent getprop setprop start stop
-toybox: acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
+toybox (0.8.3-ish): acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
chown chroot chrt cksum clear cmp comm cp cpio cut date dd devmem
df diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
false fgrep file find flock fmt free freeramdisk fsfreeze fsync getconf
diff --git a/trusty/confirmationui/NotSoSecureInput.cpp b/trusty/confirmationui/NotSoSecureInput.cpp
index 3d9a2d6..18e45cd 100644
--- a/trusty/confirmationui/NotSoSecureInput.cpp
+++ b/trusty/confirmationui/NotSoSecureInput.cpp
@@ -82,7 +82,7 @@
/**
* This is an implementation of the SecureInput protocol in unserspace. This is
- * just an example and should not be used as is. The protocol implemented her
+ * just an example and should not be used as is. The protocol implemented here
* should be used by a trusted input device that can assert user events with
* high assurance even if the HLOS kernel is compromised. A confirmationui HAL
* that links directly against this implementation is not secure and shal not be
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
index d149664..e416fb2 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -56,9 +56,9 @@
SizedBuffer hidl_vec2sized_buffer(const hidl_vec<uint8_t>& vec) {
if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) return {};
- auto dummy = new uint8_t[vec.size()];
- std::copy(vec.begin(), vec.end(), dummy);
- return {dummy, static_cast<uint32_t>(vec.size())};
+ auto buffer = new uint8_t[vec.size()];
+ std::copy(vec.begin(), vec.end(), buffer);
+ return {buffer, static_cast<uint32_t>(vec.size())};
}
Return<void> TrustyGateKeeperDevice::enroll(uint32_t uid,
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index f3ef747..750a9d7 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -173,7 +173,7 @@
}
GetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {
- // Dummy empty buffer to allow ForwardCommand to have something to serialize
+ // Empty buffer to allow ForwardCommand to have something to serialize
Buffer request;
GetHmacSharingParametersResponse response;
ForwardCommand(KM_GET_HMAC_SHARING_PARAMETERS, request, &response);
diff --git a/trusty/libtrusty/include/trusty/ipc.h b/trusty/libtrusty/include/trusty/ipc.h
new file mode 100644
index 0000000..1fa6fe4
--- /dev/null
+++ b/trusty/libtrusty/include/trusty/ipc.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#ifndef _UAPI_LINUX_TRUSTY_IPC_H_
+#define _UAPI_LINUX_TRUSTY_IPC_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/uio.h>
+
+/**
+ * enum transfer_kind - How to send an fd to Trusty
+ * @TRUSTY_SHARE: Memory will be accessible by Linux and Trusty. On ARM it will
+ * be mapped as nonsecure. Suitable for shared memory. The paired
+ * fd must be a "memfd".
+ * @TRUSTY_LEND: Memory will be accessible only to Trusty. On ARM it will be
+ * transitioned to "Secure" memory if Trusty is in TrustZone.
+ * This transfer kind is suitable for donating video buffers or
+ * other similar resources. The paired fd may need to come from a
+ * platform-specific allocator for memory that may be
+ * transitioned to "Secure".
+ *
+ * Describes how the user would like the resource in question to be sent to
+ * Trusty. Options may be valid only for certain kinds of fds.
+ */
+enum transfer_kind {
+ TRUSTY_SHARE = 0,
+ TRUSTY_LEND = 1,
+};
+
+/**
+ * struct trusty_shm - Describes a transfer of memory to Trusty
+ * @fd: The fd to transfer
+ * @transfer: How to transfer it - see &enum transfer_kind
+ */
+struct trusty_shm {
+ __s32 fd;
+ __u32 transfer;
+};
+
+/**
+ * struct tipc_send_msg_req - Request struct for @TIPC_IOC_SEND_MSG
+ * @iov: Pointer to an array of &struct iovec describing data to be sent
+ * @shm: Pointer to an array of &struct trusty_shm describing any file
+ * descriptors to be transferred.
+ * @iov_cnt: Number of elements in the @iov array
+ * @shm_cnt: Number of elements in the @shm array
+ */
+struct tipc_send_msg_req {
+ __u64 iov;
+ __u64 shm;
+ __u64 iov_cnt;
+ __u64 shm_cnt;
+};
+
+#define TIPC_IOC_MAGIC 'r'
+#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char*)
+#define TIPC_IOC_SEND_MSG _IOW(TIPC_IOC_MAGIC, 0x81, struct tipc_send_msg_req)
+
+#if defined(CONFIG_COMPAT)
+#define TIPC_IOC_CONNECT_COMPAT _IOW(TIPC_IOC_MAGIC, 0x80, compat_uptr_t)
+#endif
+
+#endif
diff --git a/trusty/libtrusty/include/trusty/tipc.h b/trusty/libtrusty/include/trusty/tipc.h
index a3f2a3f..b44afd3 100644
--- a/trusty/libtrusty/include/trusty/tipc.h
+++ b/trusty/libtrusty/include/trusty/tipc.h
@@ -21,7 +21,11 @@
extern "C" {
#endif
+#include <sys/uio.h>
+#include <trusty/ipc.h>
+
int tipc_connect(const char *dev_name, const char *srv_name);
+ssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shm, int shmcnt);
int tipc_close(int fd);
#ifdef __cplusplus
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index d20d4ee..ca581dc 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -21,6 +21,8 @@
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
+#define __USE_GNU
+#include <sys/mman.h>
#include <sys/uio.h>
#include <trusty/tipc.h>
@@ -39,6 +41,7 @@
static const char *closer2_name = "com.android.ipc-unittest.srv.closer2";
static const char *closer3_name = "com.android.ipc-unittest.srv.closer3";
static const char *main_ctrl_name = "com.android.ipc-unittest.ctrl";
+static const char* receiver_name = "com.android.trusty.memref.receiver";
static const char *_sopts = "hsvD:t:r:m:b:";
static const struct option _lopts[] = {
@@ -66,25 +69,25 @@
"\n"
;
-static const char *usage_long =
-"\n"
-"The following tests are available:\n"
-" connect - connect to datasink service\n"
-" connect_foo - connect to non existing service\n"
-" burst_write - send messages to datasink service\n"
-" echo - send/receive messages to echo service\n"
-" select - test select call\n"
-" blocked_read - test blocked read\n"
-" closer1 - connection closed by remote (test1)\n"
-" closer2 - connection closed by remote (test2)\n"
-" closer3 - connection closed by remote (test3)\n"
-" ta2ta-ipc - execute TA to TA unittest\n"
-" dev-uuid - print device uuid\n"
-" ta-access - test ta-access flags\n"
-" writev - writev test\n"
-" readv - readv test\n"
-"\n"
-;
+static const char* usage_long =
+ "\n"
+ "The following tests are available:\n"
+ " connect - connect to datasink service\n"
+ " connect_foo - connect to non existing service\n"
+ " burst_write - send messages to datasink service\n"
+ " echo - send/receive messages to echo service\n"
+ " select - test select call\n"
+ " blocked_read - test blocked read\n"
+ " closer1 - connection closed by remote (test1)\n"
+ " closer2 - connection closed by remote (test2)\n"
+ " closer3 - connection closed by remote (test3)\n"
+ " ta2ta-ipc - execute TA to TA unittest\n"
+ " dev-uuid - print device uuid\n"
+ " ta-access - test ta-access flags\n"
+ " writev - writev test\n"
+ " readv - readv test\n"
+ " send-fd - transmit memfd to trusty, use as shm\n"
+ "\n";
static uint opt_repeat = 1;
static uint opt_msgsize = 32;
@@ -885,6 +888,66 @@
return 0;
}
+static int send_fd_test(void) {
+ int ret;
+ int memfd = -1;
+ int fd = -1;
+ volatile char* buf = MAP_FAILED;
+
+ fd = tipc_connect(dev_name, receiver_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to test support TA - is it missing?\n");
+ ret = -1;
+ goto cleanup;
+ }
+
+ memfd = memfd_create("tipc-send-fd", 0);
+ if (memfd < 0) {
+ fprintf(stderr, "Failed to create memfd: %s\n", strerror(errno));
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (ftruncate(memfd, PAGE_SIZE) < 0) {
+ fprintf(stderr, "Failed to resize memfd: %s\n", strerror(errno));
+ ret = -1;
+ goto cleanup;
+ }
+
+ buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0);
+ if (buf == MAP_FAILED) {
+ fprintf(stderr, "Failed to map memfd: %s\n", strerror(errno));
+ ret = -1;
+ goto cleanup;
+ }
+
+ strcpy((char*)buf, "From NS");
+
+ struct trusty_shm shm = {
+ .fd = memfd,
+ .transfer = TRUSTY_SHARE,
+ };
+
+ ssize_t rc = tipc_send(fd, NULL, 0, &shm, 1);
+ if (rc < 0) {
+ fprintf(stderr, "tipc_send failed\n");
+ ret = rc;
+ goto cleanup;
+ }
+ char c;
+ read(fd, &c, 1);
+ tipc_close(fd);
+
+ ret = strcmp("Hello from Trusty!", (const char*)buf) ? (-1) : 0;
+
+cleanup:
+ if (buf != MAP_FAILED) {
+ munmap((char*)buf, PAGE_SIZE);
+ }
+ close(memfd);
+ tipc_close(fd);
+ return ret;
+}
int main(int argc, char **argv)
{
@@ -933,10 +996,12 @@
rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
} else if (strcmp(test_name, "readv") == 0) {
rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
- } else {
- fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
- print_usage_and_exit(argv[0], EXIT_FAILURE, true);
- }
+ } else if (strcmp(test_name, "send-fd") == 0) {
+ rc = send_fd_test();
+ } else {
+ fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
+ print_usage_and_exit(argv[0], EXIT_FAILURE, true);
+ }
- return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/trusty/libtrusty/trusty.c b/trusty/libtrusty/trusty.c
index a6238af..ad4d8cd 100644
--- a/trusty/libtrusty/trusty.c
+++ b/trusty/libtrusty/trusty.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -27,7 +27,7 @@
#include <log/log.h>
-#include "tipc_ioctl.h"
+#include <trusty/ipc.h>
int tipc_connect(const char *dev_name, const char *srv_name)
{
@@ -55,6 +55,22 @@
return fd;
}
+ssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,
+ int shmcnt) {
+ struct tipc_send_msg_req req;
+ req.iov = (__u64)iov;
+ req.iov_cnt = (__u64)iovcnt;
+ req.shm = (__u64)shms;
+ req.shm_cnt = (__u64)shmcnt;
+
+ int rc = ioctl(fd, TIPC_IOC_SEND_MSG, &req);
+ if (rc < 0) {
+ ALOGE("%s: failed to send message (err=%d)\n", __func__, rc);
+ }
+
+ return rc;
+}
+
void tipc_close(int fd)
{
close(fd);
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
new file mode 100644
index 0000000..fd353d1
--- /dev/null
+++ b/trusty/trusty-test.mk
@@ -0,0 +1,16 @@
+# 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.
+
+PRODUCT_PACKAGES += \
+ spiproxyd \
diff --git a/trusty/utils/spiproxyd/Android.bp b/trusty/utils/spiproxyd/Android.bp
new file mode 100644
index 0000000..c1d0987
--- /dev/null
+++ b/trusty/utils/spiproxyd/Android.bp
@@ -0,0 +1,36 @@
+// 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.
+
+cc_binary {
+ name: "spiproxyd",
+ vendor: true,
+
+ srcs: [
+ "main.c",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libtrusty",
+ ],
+
+ init_rc: [
+ "proxy.rc",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/trusty/utils/spiproxyd/main.c b/trusty/utils/spiproxyd/main.c
new file mode 100644
index 0000000..c10866b
--- /dev/null
+++ b/trusty/utils/spiproxyd/main.c
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "spiproxyd"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <log/log.h>
+#include <stdlib.h>
+#include <string.h>
+#include <trusty/tipc.h>
+#include <unistd.h>
+
+int handle_msg(int trusty_dev_fd, int spi_dev_fd) {
+ int rc;
+ uint8_t msg_buf[4096];
+ size_t msg_len;
+
+ /* read request from SPI Trusty app */
+ rc = read(trusty_dev_fd, &msg_buf, sizeof(msg_buf));
+ if (rc < 0) {
+ ALOGE("failed (%d) to read request from TA\n", rc);
+ return rc;
+ }
+ msg_len = rc;
+
+ /* forward request to SPI host device */
+ rc = write(spi_dev_fd, &msg_buf, msg_len);
+ if (rc < 0 || (size_t)rc != msg_len) {
+ ALOGE("failed (%d) to forward request to host\n", rc);
+ return rc < 0 ? rc : -1;
+ }
+
+ /* read response from SPI host device */
+ rc = read(spi_dev_fd, &msg_buf, sizeof(msg_buf));
+ if (rc < 0) {
+ ALOGE("failed (%d) to read response from host\n", rc);
+ return rc;
+ }
+ msg_len = rc;
+
+ /* forward response to SPI Trusty app */
+ rc = write(trusty_dev_fd, &msg_buf, msg_len);
+ if (rc < 0 || (size_t)rc != msg_len) {
+ ALOGE("failed (%d) to forward response to TA\n", rc);
+ return rc < 0 ? rc : -1;
+ }
+
+ return 0;
+}
+
+int event_loop(int trusty_dev_fd, int spi_dev_fd) {
+ while (true) {
+ int rc = handle_msg(trusty_dev_fd, spi_dev_fd);
+ if (rc < 0) {
+ ALOGE("exiting event loop\n");
+ return EXIT_FAILURE;
+ }
+ }
+}
+
+static void show_usage() {
+ ALOGE("usage: spiproxyd -t TRUSTY_DEVICE -s SPI_DEVICE -p SPI_PROXY_PORT\n");
+}
+
+static void parse_args(int argc, char* argv[], const char** trusty_dev_name,
+ const char** spi_dev_name, const char** spi_proxy_port) {
+ int opt;
+ while ((opt = getopt(argc, argv, "ht:s:p:")) != -1) {
+ switch (opt) {
+ case 'h':
+ show_usage();
+ exit(EXIT_SUCCESS);
+ break;
+ case 't':
+ *trusty_dev_name = strdup(optarg);
+ break;
+ case 's':
+ *spi_dev_name = strdup(optarg);
+ break;
+ case 'p':
+ *spi_proxy_port = strdup(optarg);
+ break;
+ default:
+ show_usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ if (!*trusty_dev_name || !*spi_dev_name || !*spi_proxy_port) {
+ show_usage();
+ exit(EXIT_FAILURE);
+ }
+}
+
+int main(int argc, char* argv[]) {
+ int rc;
+ const char* trusty_dev_name = NULL;
+ const char* spi_dev_name = NULL;
+ const char* spi_proxy_port = NULL;
+ int trusty_dev_fd;
+ int spi_dev_fd;
+
+ parse_args(argc, argv, &trusty_dev_name, &spi_dev_name, &spi_proxy_port);
+
+ rc = tipc_connect(trusty_dev_name, spi_proxy_port);
+ if (rc < 0) {
+ ALOGE("failed (%d) to connect to SPI proxy port\n", rc);
+ return rc;
+ }
+ trusty_dev_fd = rc;
+
+ rc = open(spi_dev_name, O_RDWR, 0);
+ if (rc < 0) {
+ ALOGE("failed (%d) to open SPI device\n", rc);
+ return rc;
+ }
+ spi_dev_fd = rc;
+
+ return event_loop(trusty_dev_fd, spi_dev_fd);
+}
diff --git a/trusty/utils/spiproxyd/proxy.rc b/trusty/utils/spiproxyd/proxy.rc
new file mode 100644
index 0000000..7d63e6a
--- /dev/null
+++ b/trusty/utils/spiproxyd/proxy.rc
@@ -0,0 +1,20 @@
+# 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.
+
+service spiproxyd /vendor/bin/spiproxyd -t /dev/trusty-ipc-dev0 \
+ -s /dev/vport3p2 -p com.android.trusty.spi.proxy
+ class main
+ user system
+ group system
+ oneshot