Merge "Store userspace reboot info in /metadata"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 7933629..89bd66a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -16,16 +16,25 @@
"name": "adb_tls_connection_test"
},
{
+ "name": "CtsFsMgrTestCases"
+ },
+ {
"name": "CtsInitTestCases"
},
{
+ "name": "CtsLiblogTestCases"
+ },
+ {
+ "name": "CtsLogdTestCases"
+ },
+ {
"name": "debuggerd_test"
},
{
- "name": "CtsFsMgrTestCases"
+ "name": "fs_mgr_vendor_overlay_test"
},
{
- "name": "fs_mgr_vendor_overlay_test"
+ "name": "init_kill_services_test"
},
{
"name": "libpackagelistparser_test"
@@ -58,5 +67,10 @@
{
"name": "propertyinfoserializer_tests"
}
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/tests/StagedInstallTest"
+ }
]
}
diff --git a/adb/Android.bp b/adb/Android.bp
index 2fc205f..8addf95 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -12,6 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+tidy_errors = [
+ "-*",
+ "bugprone-inaccurate-erase",
+]
+
cc_defaults {
name: "adb_defaults",
@@ -73,6 +78,10 @@
],
},
},
+
+ tidy: true,
+ tidy_checks: tidy_errors,
+ tidy_checks_as_errors: tidy_errors,
}
cc_defaults {
@@ -108,6 +117,7 @@
static_libs: [
"libadb_crypto",
"libadb_pairing_connection",
+ "libadb_sysdeps",
"libadb_tls_connection",
"libadbd",
"libadbd_core",
@@ -158,6 +168,7 @@
"services.cpp",
"sockets.cpp",
"socket_spec.cpp",
+ "sysdeps/env.cpp",
"sysdeps/errno.cpp",
"transport.cpp",
"transport_fd.cpp",
@@ -252,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"],
@@ -265,6 +313,7 @@
"libadb_pairing_auth_static",
"libadb_pairing_connection_static",
"libadb_protos_static",
+ "libadb_sysdeps",
"libadb_tls_connection_static",
"libbase",
"libcutils",
@@ -321,6 +370,7 @@
"libadb_pairing_auth",
"libadb_pairing_connection",
"libadb_protos",
+ "libadb_sysdeps",
"libadb_tls_connection",
"libandroidfw",
"libapp_processes_protos_full",
@@ -822,6 +872,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/adb_install.cpp b/adb/client/adb_install.cpp
index d6f536e..ea50f59 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -876,7 +876,7 @@
"-S",
std::to_string(sb.st_size),
session_id_str,
- android::base::StringPrintf("%d_%s", i, android::base::Basename(file).c_str()),
+ android::base::StringPrintf("%d_%s", i, android::base::Basename(split).c_str()),
"-",
};
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index b674a81..db4c479 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -400,12 +400,12 @@
for (const std::string& path : paths) {
int wd = inotify_add_watch(infd, path.c_str(), IN_CREATE | IN_MOVED_TO);
if (wd < 0) {
- PLOG(ERROR) << "failed to inotify_add_watch on path '" << path;
+ PLOG(ERROR) << "failed to inotify_add_watch on path '" << path << "'";
continue;
}
g_monitored_paths[wd] = path;
- LOG(INFO) << "watch descriptor " << wd << " registered for " << path;
+ LOG(INFO) << "watch descriptor " << wd << " registered for '" << path << "'";
}
fdevent* event = fdevent_create(infd, adb_auth_inotify_update, nullptr);
diff --git a/adb/client/bugreport.cpp b/adb/client/bugreport.cpp
index e162aaa..f2e722a 100644
--- a/adb/client/bugreport.cpp
+++ b/adb/client/bugreport.cpp
@@ -104,7 +104,9 @@
SetLineMessage("pulling");
status_ =
br_->DoSyncPull(srcs, destination.c_str(), false, line_message_.c_str()) ? 0 : 1;
- if (status_ != 0) {
+ if (status_ == 0) {
+ printf("Bug report copied to %s\n", destination.c_str());
+ } else {
fprintf(stderr,
"Bug report finished but could not be copied to '%s'.\n"
"Try to run 'adb pull %s <directory>'\n"
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/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 9db2453..a0fc9ca 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -249,6 +249,9 @@
return false;
}
+ // Remove any services with the same instance name, as it may be a stale registration.
+ removeDNSService(regType_.c_str(), serviceName_.c_str());
+
// Add to the service registry before trying to auto-connect, since socket_spec_connect will
// check these registries for the ip address when connecting via mdns instance name.
int adbSecureServiceType = serviceIndex();
@@ -268,13 +271,6 @@
return false;
}
- if (!services->empty()) {
- // Remove the previous resolved service, if any.
- services->erase(std::remove_if(services->begin(), services->end(),
- [&](std::unique_ptr<ResolvedService>& service) {
- return (serviceName_ == service->serviceName());
- }));
- }
services->push_back(std::unique_ptr<ResolvedService>(this));
if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
@@ -327,6 +323,8 @@
static bool connectByServiceName(const ServiceRegistry& services,
const std::string& service_name);
+ static void removeDNSService(const char* regType, const char* serviceName);
+
private:
int clientVersion_ = ADB_SECURE_CLIENT_VERSION;
std::string addr_format_;
@@ -396,6 +394,37 @@
return false;
}
+// static
+void ResolvedService::removeDNSService(const char* regType, const char* serviceName) {
+ D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
+ int index = adb_DNSServiceIndexByName(regType);
+ ServiceRegistry* services;
+ switch (index) {
+ case kADBTransportServiceRefIndex:
+ services = sAdbTransportServices;
+ break;
+ case kADBSecurePairingServiceRefIndex:
+ services = sAdbSecurePairingServices;
+ break;
+ case kADBSecureConnectServiceRefIndex:
+ services = sAdbSecureConnectServices;
+ break;
+ default:
+ return;
+ }
+
+ if (services->empty()) {
+ return;
+ }
+
+ std::string sName(serviceName);
+ services->erase(std::remove_if(services->begin(), services->end(),
+ [&sName](std::unique_ptr<ResolvedService>& service) {
+ return (sName == service->serviceName());
+ }),
+ services->end());
+}
+
void adb_secure_foreach_pairing_service(const char* service_name,
adb_secure_foreach_service_callback cb) {
ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, service_name, cb);
@@ -481,35 +510,6 @@
std::string regType_;
};
-static void adb_RemoveDNSService(const char* regType, const char* serviceName) {
- D("%s: regType=[%s] serviceName=[%s]", __func__, regType, serviceName);
- int index = adb_DNSServiceIndexByName(regType);
- ResolvedService::ServiceRegistry* services;
- switch (index) {
- case kADBTransportServiceRefIndex:
- services = ResolvedService::sAdbTransportServices;
- break;
- case kADBSecurePairingServiceRefIndex:
- services = ResolvedService::sAdbSecurePairingServices;
- break;
- case kADBSecureConnectServiceRefIndex:
- services = ResolvedService::sAdbSecureConnectServices;
- break;
- default:
- return;
- }
-
- if (services->empty()) {
- return;
- }
-
- std::string sName(serviceName);
- services->erase(std::remove_if(services->begin(), services->end(),
- [&sName](std::unique_ptr<ResolvedService>& service) {
- return (sName == service->serviceName());
- }));
-}
-
// Returns the version the device wanted to advertise,
// or -1 if parsing fails.
static int parse_version_from_txt_record(uint16_t txtLen, const unsigned char* txtRecord) {
@@ -612,7 +612,7 @@
} else {
D("%s: Discover lost serviceName=[%s] regtype=[%s] domain=[%s]", __func__, serviceName,
regtype, domain);
- adb_RemoveDNSService(regtype, serviceName);
+ ResolvedService::removeDNSService(regtype, serviceName);
}
}
@@ -680,6 +680,13 @@
std::optional<MdnsInfo> mdns_get_connect_service_info(std::string_view name) {
CHECK(!name.empty());
+ // only adb server creates these registries
+ if (!ResolvedService::sAdbTransportServices && !ResolvedService::sAdbSecureConnectServices) {
+ return std::nullopt;
+ }
+ CHECK(ResolvedService::sAdbTransportServices);
+ CHECK(ResolvedService::sAdbSecureConnectServices);
+
auto mdns_instance = mdns::mdns_parse_instance_name(name);
if (!mdns_instance.has_value()) {
D("Failed to parse mDNS name [%s]", name.data());
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/adb/test_adb.py b/adb/test_adb.py
index b9f0d54..a32d875 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -618,21 +618,37 @@
finally:
zeroconf_ctx.unregister_service(info)
+@contextlib.contextmanager
+def zeroconf_register_services(zeroconf_ctx, infos):
+ """Context manager for multiple zeroconf services
+
+ Registers all services given and unregisters all on cleanup. Returns the ServiceInfo
+ list supplied.
+ """
+
+ try:
+ for info in infos:
+ zeroconf_ctx.register_service(info)
+ yield infos
+ finally:
+ for info in infos:
+ zeroconf_ctx.unregister_service(info)
+
"""Should match the service names listed in adb_mdns.h"""
class MdnsTest:
"""Tests for adb mdns."""
+ @staticmethod
+ def _mdns_services(port):
+ output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
+ return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
+ @staticmethod
+ def _devices(port):
+ output = subprocess.check_output(["adb", "-P", str(port), "devices"])
+ return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
+
class Base(unittest.TestCase):
- @staticmethod
- def _mdns_services(port):
- output = subprocess.check_output(["adb", "-P", str(port), "mdns", "services"])
- return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
-
- @staticmethod
- def _devices(port):
- output = subprocess.check_output(["adb", "-P", str(port), "devices"])
- return [x.split("\t") for x in output.decode("utf8").strip().splitlines()[1:]]
-
@contextlib.contextmanager
def _adb_mdns_connect(self, server_port, mdns_instance, serial, should_connect):
"""Context manager for an ADB connection.
@@ -691,6 +707,50 @@
for line in MdnsTest._mdns_services(server_port)))
@unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
+ def test_mdns_services_register_unregister_multiple(self):
+ """Ensure that `adb mdns services` correctly adds and removes multiple services
+ """
+ from zeroconf import IPVersion, ServiceInfo
+
+ with adb_server() as server_port:
+ output = subprocess.check_output(["adb", "-P", str(server_port),
+ "mdns", "services"]).strip()
+ self.assertTrue(output.startswith(b"List of discovered mdns services"))
+
+ """TODO(joshuaduong): Add ipv6 tests once we have it working in adb"""
+ """Register/Unregister a service"""
+ with zeroconf_context(IPVersion.V4Only) as zc:
+ srvs = {
+ 'mdns_name': ["testservice0", "testservice1", "testservice2"],
+ 'mdns_type': "_" + self.service_name + "._tcp.",
+ 'ipaddr': [
+ socket.inet_aton("192.168.0.1"),
+ socket.inet_aton("10.0.0.255"),
+ socket.inet_aton("172.16.1.100")],
+ 'port': [10000, 20000, 65535]}
+ srv_infos = []
+ for i in range(len(srvs['mdns_name'])):
+ srv_infos.append(ServiceInfo(
+ srvs['mdns_type'] + "local.",
+ name=srvs['mdns_name'][i] + "." + srvs['mdns_type'] + "local.",
+ addresses=[srvs['ipaddr'][i]],
+ port=srvs['port'][i]))
+
+ """ Register all devices, then unregister"""
+ with zeroconf_register_services(zc, srv_infos) as infos:
+ """Give adb some time to register the service"""
+ time.sleep(1)
+ for i in range(len(srvs['mdns_name'])):
+ self.assertTrue(any((srvs['mdns_name'][i] in line and srvs['mdns_type'] in line)
+ for line in MdnsTest._mdns_services(server_port)))
+
+ """Give adb some time to unregister the service"""
+ time.sleep(1)
+ for i in range(len(srvs['mdns_name'])):
+ self.assertFalse(any((srvs['mdns_name'][i] in line and srvs['mdns_type'] in line)
+ for line in MdnsTest._mdns_services(server_port)))
+
+ @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
def test_mdns_connect(self):
"""Ensure that `adb connect` by mdns instance name works (for non-pairing services)
"""
diff --git a/adb/transport.cpp b/adb/transport.cpp
index b6b6984..c33d5af 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -1533,8 +1533,7 @@
keys_.pop_front();
}
- std::shared_ptr<RSA> result = keys_[0];
- return result;
+ return Key();
}
void atransport::ResetKeys() {
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/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 5d6cee4..8979e9a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -436,6 +436,8 @@
{"reboot,userspace_failed,watchdog_fork", 188},
{"reboot,userspace_failed,*", 189},
{"reboot,mount_userdata_failed", 190},
+ {"reboot,forcedsilent", 191},
+ {"reboot,forcednonsilent", 192},
};
// Converts a string value representing the reason the system booted to an
diff --git a/code_coverage/seccomp_policy/code_coverage.arm.policy b/code_coverage/seccomp_policy/code_coverage.arm.policy
index d6784e3..b80910f 100644
--- a/code_coverage/seccomp_policy/code_coverage.arm.policy
+++ b/code_coverage/seccomp_policy/code_coverage.arm.policy
@@ -6,6 +6,7 @@
write: 1
fcntl64: 1
fstat64: 1
+ftruncate64: 1
geteuid32: 1
_llseek: 1
mmap2: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.arm64.policy b/code_coverage/seccomp_policy/code_coverage.arm64.policy
index 4c3dd26..7040ea2 100644
--- a/code_coverage/seccomp_policy/code_coverage.arm64.policy
+++ b/code_coverage/seccomp_policy/code_coverage.arm64.policy
@@ -6,6 +6,7 @@
write: 1
fcntl: 1
fstat: 1
+ftruncate: 1
geteuid: 1
lseek: 1
mmap: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.policy.def b/code_coverage/seccomp_policy/code_coverage.policy.def
index f136084..599c4a4 100644
--- a/code_coverage/seccomp_policy/code_coverage.policy.def
+++ b/code_coverage/seccomp_policy/code_coverage.policy.def
@@ -22,6 +22,7 @@
#if defined(__LP64__)
fcntl: 1
fstat: 1
+ftruncate: 1
geteuid: 1
lseek: 1
mmap: 1
@@ -29,6 +30,7 @@
#else
fcntl64: 1
fstat64: 1
+ftruncate64: 1
geteuid32: 1
_llseek: 1
mmap2: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86.policy b/code_coverage/seccomp_policy/code_coverage.x86.policy
index 24ff8b9..f8e0cc0 100644
--- a/code_coverage/seccomp_policy/code_coverage.x86.policy
+++ b/code_coverage/seccomp_policy/code_coverage.x86.policy
@@ -6,6 +6,7 @@
write: 1
fcntl64: 1
fstat64: 1
+ftruncate64: 1
geteuid32: 1
_llseek: 1
mmap2: 1
diff --git a/code_coverage/seccomp_policy/code_coverage.x86_64.policy b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
index 3081036..dcf2f9a 100644
--- a/code_coverage/seccomp_policy/code_coverage.x86_64.policy
+++ b/code_coverage/seccomp_policy/code_coverage.x86_64.policy
@@ -6,6 +6,7 @@
write: 1
fcntl: 1
fstat: 1
+ftruncate: 1
geteuid: 1
lseek: 1
mmap: 1
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 9d7658e..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>
@@ -172,6 +173,8 @@
void StartCrasher(const std::string& crash_type);
void FinishCrasher();
void AssertDeath(int signo);
+
+ static void Trap(void* ptr);
};
CrasherTest::CrasherTest() {
@@ -334,6 +337,48 @@
R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))");
}
+// Marked as weak to prevent the compiler from removing the malloc in the caller. In theory, the
+// compiler could still clobber the argument register before trapping, but that's unlikely.
+__attribute__((weak)) void CrasherTest::Trap(void* ptr ATTRIBUTE_UNUSED) {
+ __builtin_trap();
+}
+
+TEST_F(CrasherTest, heap_addr_in_register) {
+#if defined(__i386__)
+ GTEST_SKIP() << "architecture does not pass arguments in registers";
+#endif
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ // Crash with a heap pointer in the first argument register.
+ Trap(malloc(1));
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ int status;
+ ASSERT_EQ(crasher_pid, TIMEOUT(30, waitpid(crasher_pid, &status, 0)));
+ ASSERT_TRUE(WIFSIGNALED(status)) << "crasher didn't terminate via a signal";
+ // Don't test the signal number because different architectures use different signals for
+ // __builtin_trap().
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+#if defined(__aarch64__)
+ ASSERT_MATCH(result, "memory near x0");
+#elif defined(__arm__)
+ ASSERT_MATCH(result, "memory near r0");
+#elif defined(__x86_64__)
+ ASSERT_MATCH(result, "memory near rdi");
+#else
+ ASSERT_TRUE(false) << "unsupported architecture";
+#endif
+}
+
#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
static void SetTagCheckingLevelSync() {
int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
@@ -512,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 c8a3431..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,24 +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;
}
- // 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
+ // 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 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);
@@ -187,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
@@ -197,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;
@@ -226,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/fastboot.bash b/fastboot/fastboot.bash
index cb1d354..406e8b8 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $i -eq $COMP_CWORD ]]; then
- partitions="boot bootloader dtbo modem odm oem product radio recovery system vbmeta vendor"
+ partitions="boot bootloader dtbo modem odm odm_dlkm oem product radio recovery system vbmeta vendor vendor_dlkm"
COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
else
_fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 0e9713d..d33c987 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -145,6 +145,7 @@
{ "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, ImageType::BootCritical },
{ "dts", "dt.img", "dt.sig", "dts", true, ImageType::BootCritical },
{ "odm", "odm.img", "odm.sig", "odm", true, ImageType::Normal },
+ { "odm_dlkm", "odm_dlkm.img", "odm_dlkm.sig", "odm_dlkm", true, ImageType::Normal },
{ "product", "product.img", "product.sig", "product", true, ImageType::Normal },
{ "recovery", "recovery.img", "recovery.sig", "recovery", true, ImageType::BootCritical },
{ "super", "super.img", "super.sig", "super", true, ImageType::Extra },
@@ -166,6 +167,10 @@
"vendor_boot.img", "vendor_boot.sig",
"vendor_boot",
true, ImageType::BootCritical },
+ { "vendor_dlkm",
+ "vendor_dlkm.img", "vendor_dlkm.sig",
+ "vendor_dlkm",
+ true, ImageType::Normal },
{ nullptr, "vendor_other.img", "vendor.sig", "vendor", true, ImageType::Normal },
// clang-format on
};
@@ -992,10 +997,69 @@
fb->GetVar("partition-type:vbmeta_b", &partition_type) == fastboot::SUCCESS;
}
+static std::string fb_fix_numeric_var(std::string var) {
+ // Some bootloaders (angler, for example), send spurious leading whitespace.
+ var = android::base::Trim(var);
+ // Some bootloaders (hammerhead, for example) use implicit hex.
+ // This code used to use strtol with base 16.
+ if (!android::base::StartsWith(var, "0x")) var = "0x" + var;
+ return var;
+}
+
+static void copy_boot_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
+ if (buf->sz < AVB_FOOTER_SIZE) {
+ return;
+ }
+
+ std::string partition_size_str;
+ if (fb->GetVar("partition-size:" + partition, &partition_size_str) != fastboot::SUCCESS) {
+ die("cannot get boot partition size");
+ }
+
+ partition_size_str = fb_fix_numeric_var(partition_size_str);
+ int64_t partition_size;
+ if (!android::base::ParseInt(partition_size_str, &partition_size)) {
+ die("Couldn't parse partition size '%s'.", partition_size_str.c_str());
+ }
+ if (partition_size == buf->sz) {
+ return;
+ }
+ if (partition_size < buf->sz) {
+ die("boot partition is smaller than boot image");
+ }
+
+ std::string data;
+ if (!android::base::ReadFdToString(buf->fd, &data)) {
+ die("Failed reading from boot");
+ }
+
+ uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
+ if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
+ return;
+ }
+
+ int fd = make_temporary_fd("boot rewriting");
+ if (!android::base::WriteStringToFd(data, fd)) {
+ die("Failed writing to modified boot");
+ }
+ lseek(fd, partition_size - AVB_FOOTER_SIZE, SEEK_SET);
+ if (!android::base::WriteStringToFd(data.substr(footer_offset), fd)) {
+ die("Failed copying AVB footer in boot");
+ }
+ close(buf->fd);
+ buf->fd = fd;
+ buf->sz = partition_size;
+ lseek(fd, 0, SEEK_SET);
+}
+
static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
{
sparse_file** s;
+ if (partition == "boot" || partition == "boot_a" || partition == "boot_b") {
+ copy_boot_avb_footer(partition, buf);
+ }
+
// Rewrite vbmeta if that's what we're flashing and modification has been requested.
if (g_disable_verity || g_disable_verification) {
if (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b") {
@@ -1491,15 +1555,6 @@
fb->RawCommand(command, "");
}
-static std::string fb_fix_numeric_var(std::string var) {
- // Some bootloaders (angler, for example), send spurious leading whitespace.
- var = android::base::Trim(var);
- // Some bootloaders (hammerhead, for example) use implicit hex.
- // This code used to use strtol with base 16.
- if (!android::base::StartsWith(var, "0x")) var = "0x" + var;
- return var;
-}
-
static unsigned fb_get_flash_block_size(std::string name) {
std::string sizeString;
if (fb->GetVar(name, &sizeString) != fastboot::SUCCESS || sizeString.empty()) {
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index bd76ff4..9b5e5f7 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -45,6 +45,7 @@
#include <vector>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <gtest/gtest.h>
#include "fastboot_driver.h"
@@ -76,8 +77,7 @@
}
bool FastBootTest::IsFastbootOverTcp() {
- // serial contains ":" is treated as host ip and port number
- return (device_serial.find(":") != std::string::npos);
+ return android::base::StartsWith(device_serial, "tcp:");
}
bool FastBootTest::UsbStillAvailible() {
@@ -182,19 +182,14 @@
}
void FastBootTest::ConnectTcpFastbootDevice() {
- std::size_t found = device_serial.find(":");
- if (found != std::string::npos) {
- for (int i = 0; i < MAX_TCP_TRIES && !transport; i++) {
- std::string error;
- std::unique_ptr<Transport> tcp(
- tcp::Connect(device_serial.substr(0, found), tcp::kDefaultPort, &error)
- .release());
- if (tcp)
- transport =
- std::unique_ptr<TransportSniffer>(new TransportSniffer(std::move(tcp), 0));
- if (transport != nullptr) break;
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- }
+ for (int i = 0; i < MAX_TCP_TRIES && !transport; i++) {
+ std::string error;
+ std::unique_ptr<Transport> tcp(
+ tcp::Connect(device_serial.substr(4), tcp::kDefaultPort, &error).release());
+ if (tcp)
+ transport = std::unique_ptr<TransportSniffer>(new TransportSniffer(std::move(tcp), 0));
+ if (transport != nullptr) break;
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
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/Android.bp b/fs_mgr/Android.bp
index f5daf91..ac784b2 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -149,6 +149,14 @@
darwin: {
enabled: false,
},
+ vendor: {
+ cflags: [
+ // Skipping entries in fstab should only be done in a system
+ // process as the config file is in /system_ext.
+ // Remove the op from the vendor variant.
+ "-DNO_SKIP_MOUNT",
+ ],
+ },
},
export_include_dirs: ["include_fstab"],
header_libs: [
@@ -162,10 +170,13 @@
defaults: ["fs_mgr_defaults"],
static_libs: [
"libavb_user",
+ "libutils",
+ "libvold_binder",
],
shared_libs: [
"libbootloader_message",
"libbase",
+ "libbinder",
"libcutils",
"libcrypto",
"libext4_utils",
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 b2c7a27..d6fd513 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -521,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)) {
@@ -582,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()};
@@ -696,7 +696,7 @@
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);
}
}
@@ -976,6 +976,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");
@@ -1019,7 +1032,8 @@
class CheckpointManager {
public:
- CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
+ CheckpointManager(int needs_checkpoint = -1, bool metadata_encrypted = false)
+ : needs_checkpoint_(needs_checkpoint), metadata_encrypted_(metadata_encrypted) {}
bool NeedsCheckpoint() {
if (needs_checkpoint_ != UNKNOWN) {
@@ -1037,7 +1051,7 @@
return true;
}
- if (entry->fs_mgr_flags.checkpoint_blk) {
+ if (entry->fs_mgr_flags.checkpoint_blk && !metadata_encrypted_) {
call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device}, nullptr);
}
@@ -1112,10 +1126,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);
}
@@ -1146,6 +1159,7 @@
enum { UNKNOWN = -1, NO = 0, YES = 1 };
int needs_checkpoint_;
+ bool metadata_encrypted_;
std::map<std::string, std::string> device_map_;
};
@@ -1365,6 +1379,7 @@
avb_handle = AvbHandle::Open();
if (!avb_handle) {
LERROR << "Failed to open AvbHandle";
+ set_type_property(encryptable);
return FS_MGR_MNTALL_FAIL;
}
}
@@ -1421,6 +1436,7 @@
attempted_entry.mount_point},
nullptr)) {
LERROR << "Encryption failed";
+ set_type_property(encryptable);
return FS_MGR_MNTALL_FAIL;
}
}
@@ -1518,6 +1534,8 @@
}
}
+ set_type_property(encryptable);
+
#if ALLOW_ADBD_DISABLE_VERITY == 1 // "userdebug" build
fs_mgr_overlayfs_mount_all(fstab);
#endif
@@ -1779,11 +1797,11 @@
// in turn, and stop on 1st success, or no more match.
static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
const std::string& n_blk_device, const char* tmp_mount_point,
- int needs_checkpoint) {
+ int needs_checkpoint, bool metadata_encrypted) {
int mount_errors = 0;
int first_mount_errno = 0;
std::string mount_point;
- CheckpointManager checkpoint_manager(needs_checkpoint);
+ CheckpointManager checkpoint_manager(needs_checkpoint, metadata_encrypted);
AvbUniquePtr avb_handle(nullptr);
if (!fstab) {
@@ -1893,12 +1911,13 @@
}
int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
- return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
+ return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1, false);
}
int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
- bool needs_checkpoint) {
- return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint);
+ bool needs_checkpoint, bool metadata_encrypted) {
+ return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint,
+ metadata_encrypted);
}
/*
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index f333a85..233d15f 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -640,13 +640,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 =
@@ -696,7 +697,9 @@
TransformFstabForDsu(fstab, Split(lp_names, ","));
}
+#ifndef NO_SKIP_MOUNT
SkipMountingPartitions(fstab);
+#endif
EnableMandatoryFlags(fstab);
return true;
@@ -726,11 +729,14 @@
return false;
}
+#ifndef NO_SKIP_MOUNT
SkipMountingPartitions(fstab);
+#endif
return true;
}
+#ifndef NO_SKIP_MOUNT
// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
// device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
@@ -762,6 +768,7 @@
return true;
}
+#endif
// Loads the fstab file and combines with fstab entries passed in from device tree.
bool ReadDefaultFstab(Fstab* fstab) {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 1fa1aa1..a7704de 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -809,15 +809,26 @@
entry.fs_type = mnt_type;
if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
- entry.flags = MS_NOATIME;
- if (readonly) {
- entry.flags |= MS_RDONLY;
- } else {
+ entry.flags = MS_NOATIME | MS_RDONLY;
+ auto mounted = true;
+ if (!readonly) {
+ if (entry.fs_type == "ext4") {
+ // check if ext4 de-dupe
+ entry.flags |= MS_RDONLY;
+ auto save_errno = errno;
+ mounted = fs_mgr_do_mount_one(entry) == 0;
+ if (mounted) {
+ mounted = !fs_mgr_has_shared_blocks(entry.mount_point, entry.blk_device);
+ fs_mgr_overlayfs_umount_scratch();
+ }
+ errno = save_errno;
+ }
+ entry.flags &= ~MS_RDONLY;
fs_mgr_set_blk_ro(device_path, false);
}
entry.fs_mgr_flags.check = true;
auto save_errno = errno;
- auto mounted = fs_mgr_do_mount_one(entry) == 0;
+ if (mounted) mounted = fs_mgr_do_mount_one(entry) == 0;
if (!mounted) {
if ((entry.fs_type == "f2fs") && ext4) {
entry.fs_type = "ext4";
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 052efa7..b8b074e 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -24,6 +24,7 @@
#include <unistd.h>
#include <string>
+#include <thread>
#include <utility>
#include <vector>
@@ -31,6 +32,8 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
+#include <android/os/IVold.h>
+#include <binder/IServiceManager.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
#include <fec/io.h>
@@ -103,8 +106,23 @@
::exit(0); // SUCCESS
}
+static android::sp<android::os::IVold> GetVold() {
+ while (true) {
+ if (auto sm = android::defaultServiceManager()) {
+ if (auto binder = sm->getService(android::String16("vold"))) {
+ if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
+ return vold;
+ }
+ }
+ }
+ std::this_thread::sleep_for(2s);
+ }
+}
+
} // namespace
+using namespace std::chrono_literals;
+
enum RemountStatus {
REMOUNT_SUCCESS = 0,
NOT_USERDEBUG,
@@ -117,7 +135,9 @@
BAD_OVERLAY,
NO_MOUNTS,
REMOUNT_FAILED,
- MUST_REBOOT
+ MUST_REBOOT,
+ BINDER_ERROR,
+ CHECKPOINTING
};
static int do_remount(int argc, char* argv[]) {
@@ -194,6 +214,22 @@
return NO_FSTAB;
}
+ if (android::base::GetBoolProperty("ro.virtual_ab.enabled", false) &&
+ !android::base::GetBoolProperty("ro.virtual_ab.retrofit", false)) {
+ // Virtual A/B devices can use /data as backing storage; make sure we're
+ // not checkpointing.
+ auto vold = GetVold();
+ bool checkpointing = false;
+ if (!vold->isCheckpointing(&checkpointing).isOk()) {
+ LOG(ERROR) << "Could not determine checkpointing status.";
+ return BINDER_ERROR;
+ }
+ if (checkpointing) {
+ LOG(ERROR) << "Cannot use remount when a checkpoint is in progress.";
+ return CHECKPOINTING;
+ }
+ }
+
// Generate the list of supported overlayfs mount points.
auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 86090c1..2a67b8c 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -69,7 +69,7 @@
int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
char* tmp_mount_point);
int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
- char* tmp_mount_point, bool need_cp);
+ char* tmp_mount_point, bool need_cp, bool metadata_encrypted);
int fs_mgr_do_mount_one(const android::fs_mgr::FstabEntry& entry,
const std::string& mount_point = "");
int fs_mgr_do_tmpfs_mount(const char *n_name);
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/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 250cb82..8788b5a 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -280,5 +280,12 @@
return android::base::Join(argv, " ");
}
+std::string DmTargetUser::GetParameterString() const {
+ std::vector<std::string> argv;
+ argv.push_back(std::to_string(start()));
+ argv.push_back(std::to_string(size()));
+ return android::base::Join(argv, " ");
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index f986cfe..57e3884 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -309,6 +309,14 @@
bool is_hw_wrapped_ = false;
};
+class DmTargetUser final : public DmTarget {
+ public:
+ DmTargetUser(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+
+ std::string name() const override { return "user"; }
+ std::string GetParameterString() const override;
+};
+
} // namespace dm
} // namespace android
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.cpp b/fs_mgr/liblp/builder.cpp
index c37d70e..623293e 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -31,6 +31,22 @@
namespace android {
namespace fs_mgr {
+std::ostream& operator<<(std::ostream& os, const Extent& extent) {
+ switch (extent.GetExtentType()) {
+ case ExtentType::kZero: {
+ os << "type: Zero";
+ break;
+ }
+ case ExtentType::kLinear: {
+ auto linear_extent = static_cast<const LinearExtent*>(&extent);
+ os << "type: Linear, physical sectors: " << linear_extent->physical_sector()
+ << ", end sectors: " << linear_extent->end_sector();
+ break;
+ }
+ }
+ return os;
+}
+
bool LinearExtent::AddTo(LpMetadata* out) const {
if (device_index_ >= out->block_devices.size()) {
LERROR << "Extent references unknown block device.";
@@ -41,6 +57,17 @@
return true;
}
+bool LinearExtent::operator==(const android::fs_mgr::Extent& other) const {
+ if (other.GetExtentType() != ExtentType::kLinear) {
+ return false;
+ }
+
+ auto other_ptr = static_cast<const LinearExtent*>(&other);
+ return num_sectors_ == other_ptr->num_sectors_ &&
+ physical_sector_ == other_ptr->physical_sector_ &&
+ device_index_ == other_ptr->device_index_;
+}
+
bool LinearExtent::OverlapsWith(const LinearExtent& other) const {
if (device_index_ != other.device_index()) {
return false;
@@ -64,6 +91,10 @@
return true;
}
+bool ZeroExtent::operator==(const android::fs_mgr::Extent& other) const {
+ return other.GetExtentType() == ExtentType::kZero && num_sectors_ == other.num_sectors();
+}
+
Partition::Partition(std::string_view name, std::string_view group_name, uint32_t attributes)
: name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
@@ -205,11 +236,18 @@
}
}
- if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false) &&
- !always_keep_source_slot) {
- if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
- target_slot_number)) {
- return nullptr;
+ if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+ if (always_keep_source_slot) {
+ // always_keep_source_slot implies the target build does not support snapshots.
+ // Clear unsupported attributes.
+ SetMetadataHeaderV0(metadata.get());
+ } else {
+ // !always_keep_source_slot implies the target build supports snapshots. Do snapshot
+ // updates.
+ if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
+ target_slot_number)) {
+ return nullptr;
+ }
}
}
@@ -511,7 +549,7 @@
return partitions_.back().get();
}
-Partition* MetadataBuilder::FindPartition(std::string_view name) {
+Partition* MetadataBuilder::FindPartition(std::string_view name) const {
for (const auto& partition : partitions_) {
if (partition->name() == name) {
return partition.get();
@@ -520,7 +558,7 @@
return nullptr;
}
-PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) {
+PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) const {
for (const auto& group : groups_) {
if (group->name() == group_name) {
return group.get();
@@ -1263,5 +1301,50 @@
return geometry_.logical_block_size;
}
+bool MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
+ const MetadataBuilder& source_metadata, uint32_t source_slot_number,
+ const MetadataBuilder& target_metadata, uint32_t target_slot_number,
+ const std::vector<std::string>& partitions) {
+ for (const auto& base_name : partitions) {
+ // Find the partition in metadata with the slot suffix.
+ auto target_partition_name = base_name + SlotSuffixForSlotNumber(target_slot_number);
+ const auto target_partition = target_metadata.FindPartition(target_partition_name);
+ if (!target_partition) {
+ LERROR << "Failed to find partition " << target_partition_name << " in metadata slot "
+ << target_slot_number;
+ return false;
+ }
+
+ auto source_partition_name = base_name + SlotSuffixForSlotNumber(source_slot_number);
+ const auto source_partition = source_metadata.FindPartition(source_partition_name);
+ if (!source_partition) {
+ LERROR << "Failed to find partition " << source_partition << " in metadata slot "
+ << source_slot_number;
+ return false;
+ }
+
+ // We expect the partitions in the target metadata to have the identical extents as the
+ // one in the source metadata. Because they are copied in NewForUpdate.
+ if (target_partition->extents().size() != source_partition->extents().size()) {
+ LERROR << "Extents count mismatch for partition " << base_name << " target slot has "
+ << target_partition->extents().size() << ", source slot has "
+ << source_partition->extents().size();
+ return false;
+ }
+
+ for (size_t i = 0; i < target_partition->extents().size(); i++) {
+ const auto& src_extent = *source_partition->extents()[i];
+ const auto& tgt_extent = *target_partition->extents()[i];
+ if (tgt_extent != src_extent) {
+ LERROR << "Extents " << i << " is different for partition " << base_name;
+ LERROR << "tgt extent " << tgt_extent << "; src extent " << src_extent;
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 1a3250a..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);
}
@@ -947,9 +947,10 @@
}
static void AddPartition(const std::unique_ptr<MetadataBuilder>& builder,
- const std::string& partition_name, uint64_t num_sectors,
- uint64_t start_sector, std::vector<Interval>* intervals) {
- Partition* p = builder->AddPartition(partition_name, "group", 0);
+ const std::string& partition_name, const std::string& group_name,
+ uint64_t num_sectors, uint64_t start_sector,
+ std::vector<Interval>* intervals = nullptr) {
+ Partition* p = builder->AddPartition(partition_name, group_name, 0);
ASSERT_NE(p, nullptr);
ASSERT_TRUE(builder->AddLinearExtent(p, "super", num_sectors, start_sector));
ASSERT_EQ(p->extents().size(), 1);
@@ -977,17 +978,17 @@
ASSERT_TRUE(builder->AddGroup("group", 0));
std::vector<Interval> old_intervals;
- AddPartition(builder, "system", 10229008, 2048, &old_intervals);
- AddPartition(builder, "test_a", 648, 12709888, &old_intervals);
- AddPartition(builder, "test_b", 625184, 12711936, &old_intervals);
- AddPartition(builder, "test_c", 130912, 13338624, &old_intervals);
- AddPartition(builder, "test_d", 888, 13469696, &old_intervals);
- AddPartition(builder, "test_e", 888, 13471744, &old_intervals);
- AddPartition(builder, "test_f", 888, 13475840, &old_intervals);
- AddPartition(builder, "test_g", 888, 13477888, &old_intervals);
+ AddPartition(builder, "system", "group", 10229008, 2048, &old_intervals);
+ AddPartition(builder, "test_a", "group", 648, 12709888, &old_intervals);
+ AddPartition(builder, "test_b", "group", 625184, 12711936, &old_intervals);
+ AddPartition(builder, "test_c", "group", 130912, 13338624, &old_intervals);
+ AddPartition(builder, "test_d", "group", 888, 13469696, &old_intervals);
+ AddPartition(builder, "test_e", "group", 888, 13471744, &old_intervals);
+ AddPartition(builder, "test_f", "group", 888, 13475840, &old_intervals);
+ AddPartition(builder, "test_g", "group", 888, 13477888, &old_intervals);
// Don't track the first vendor interval, since it will get extended.
- AddPartition(builder, "vendor", 2477920, 10231808, nullptr);
+ AddPartition(builder, "vendor", "group", 2477920, 10231808, nullptr);
std::vector<Interval> new_intervals;
@@ -1066,3 +1067,30 @@
ASSERT_NE(p, nullptr);
ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL));
}
+
+TEST_F(BuilderTest, VerifyExtent) {
+ auto source_builder = MetadataBuilder::New(4096 * 50, 40960, 2);
+ ASSERT_NE(source_builder, nullptr);
+ ASSERT_TRUE(source_builder->AddGroup("test_group_a", 40960));
+ ASSERT_TRUE(source_builder->AddGroup("test_group_b", 40960));
+ AddPartition(source_builder, "system_a", "test_group_a", 8192, 2048);
+ AddPartition(source_builder, "vendor_a", "test_group_a", 10240, 10240);
+ AddPartition(source_builder, "system_b", "test_group_b", 8192, 20480);
+
+ auto target_builder = MetadataBuilder::New(4096 * 50, 40960, 2);
+ ASSERT_NE(target_builder, nullptr);
+ ASSERT_TRUE(target_builder->AddGroup("test_group_b", 40960));
+ AddPartition(target_builder, "system_b", "test_group_b", 8192, 2048);
+ AddPartition(target_builder, "vendor_b", "test_group_b", 10240, 10240);
+
+ ASSERT_TRUE(MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
+ *source_builder, 0, *target_builder, 1, std::vector<std::string>{"system", "vendor"}));
+
+ target_builder->RemovePartition("vendor_b");
+ ASSERT_FALSE(target_builder->VerifyExtentsAgainstSourceMetadata(
+ *source_builder, 0, *target_builder, 1, std::vector<std::string>{"vendor"}));
+
+ AddPartition(target_builder, "vendor_b", "test_group_b", 1000, 10240);
+ ASSERT_FALSE(target_builder->VerifyExtentsAgainstSourceMetadata(
+ *source_builder, 0, *target_builder, 1, std::vector<std::string>{"vendor"}));
+}
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/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 89a47b1..54f31bc 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -42,6 +42,11 @@
// Name of the default group in a metadata.
static constexpr std::string_view kDefaultGroup = "default";
+enum class ExtentType {
+ kZero,
+ kLinear,
+};
+
// Abstraction around dm-targets that can be encoded into logical partition tables.
class Extent {
public:
@@ -50,6 +55,10 @@
virtual bool AddTo(LpMetadata* out) const = 0;
virtual LinearExtent* AsLinearExtent() { return nullptr; }
+ virtual ExtentType GetExtentType() const = 0;
+
+ virtual bool operator==(const Extent& other) const = 0;
+ virtual bool operator!=(const Extent& other) const { return !(*this == other); }
uint64_t num_sectors() const { return num_sectors_; }
void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
@@ -58,6 +67,8 @@
uint64_t num_sectors_;
};
+std::ostream& operator<<(std::ostream& os, const Extent& extent);
+
// This corresponds to a dm-linear target.
class LinearExtent final : public Extent {
public:
@@ -66,6 +77,9 @@
bool AddTo(LpMetadata* metadata) const override;
LinearExtent* AsLinearExtent() override { return this; }
+ ExtentType GetExtentType() const override { return ExtentType::kLinear; }
+
+ bool operator==(const Extent& other) const override;
uint64_t physical_sector() const { return physical_sector_; }
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
@@ -87,6 +101,9 @@
explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
bool AddTo(LpMetadata* out) const override;
+ ExtentType GetExtentType() const override { return ExtentType::kZero; }
+
+ bool operator==(const Extent& other) const override;
};
class PartitionGroup final {
@@ -208,8 +225,10 @@
// metadata may not have the target slot's devices listed yet, in which
// case, it is automatically upgraded to include all available block
// devices.
- // If |always_keep_source_slot| is set, on a Virtual A/B device, source slot
- // partitions are kept. This is useful when applying a downgrade package.
+ // If |always_keep_source_slot| is set, on a Virtual A/B device
+ // - source slot partitions are kept.
+ // - UPDATED flag is cleared.
+ // This is useful when applying a downgrade package.
static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,
const std::string& source_partition,
uint32_t source_slot_number,
@@ -241,6 +260,14 @@
return New(device_info, metadata_max_size, metadata_slot_count);
}
+ // Verifies that the given partitions in the metadata have the same extents as the source
+ // metadata.
+ static bool VerifyExtentsAgainstSourceMetadata(const MetadataBuilder& source_metadata,
+ uint32_t source_slot_number,
+ const MetadataBuilder& target_metadata,
+ uint32_t target_slot_number,
+ const std::vector<std::string>& partitions);
+
// Define a new partition group. By default there is one group called
// "default", with an unrestricted size. A non-zero size will restrict the
// total space used by all partitions in the group.
@@ -265,10 +292,10 @@
void RemovePartition(std::string_view name);
// Find a partition by name. If no partition is found, nullptr is returned.
- Partition* FindPartition(std::string_view name);
+ Partition* FindPartition(std::string_view name) const;
// Find a group by name. If no group is found, nullptr is returned.
- PartitionGroup* FindGroup(std::string_view name);
+ PartitionGroup* FindGroup(std::string_view name) const;
// Add a predetermined extent to a partition.
bool AddLinearExtent(Partition* partition, const std::string& block_device,
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/utility.cpp b/fs_mgr/liblp/utility.cpp
index 48c5c83..d8e171b 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -15,6 +15,7 @@
*/
#include <fcntl.h>
+#include <inttypes.h>
#include <stdint.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -29,6 +30,7 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <ext4_utils/ext4_utils.h>
#include <openssl/sha.h>
@@ -285,5 +287,42 @@
return true;
}
+inline std::string ToHexString(uint64_t value) {
+ return android::base::StringPrintf("0x%" PRIx64, value);
+}
+
+void SetMetadataHeaderV0(LpMetadata* metadata) {
+ if (metadata->header.minor_version <= LP_METADATA_MINOR_VERSION_MIN) {
+ return;
+ }
+ LINFO << "Forcefully setting metadata header version " << LP_METADATA_MAJOR_VERSION << "."
+ << metadata->header.minor_version << " to " << LP_METADATA_MAJOR_VERSION << "."
+ << LP_METADATA_MINOR_VERSION_MIN;
+ metadata->header.minor_version = LP_METADATA_MINOR_VERSION_MIN;
+ metadata->header.header_size = sizeof(LpMetadataHeaderV1_0);
+
+ // Retrofit Virtual A/B devices should have version 10.1, so flags shouldn't be set.
+ // Warn if this is the case, but zero it out anyways.
+ if (metadata->header.flags) {
+ LWARN << "Zeroing unexpected flags: " << ToHexString(metadata->header.flags);
+ }
+
+ // Zero out all fields beyond LpMetadataHeaderV0.
+ static_assert(sizeof(metadata->header) > sizeof(LpMetadataHeaderV1_0));
+ memset(reinterpret_cast<uint8_t*>(&metadata->header) + sizeof(LpMetadataHeaderV1_0), 0,
+ sizeof(metadata->header) - sizeof(LpMetadataHeaderV1_0));
+
+ // Clear partition attributes unknown to V0.
+ // On retrofit Virtual A/B devices, UPDATED flag may be set, so only log info here.
+ for (auto& partition : metadata->partitions) {
+ if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK_V0) {
+ LINFO << "Clearing " << GetPartitionName(partition)
+ << " partition attribute: " << ToHexString(partition.attributes);
+ }
+
+ partition.attributes &= LP_PARTITION_ATTRIBUTE_MASK_V0;
+ }
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index c4fe3ed..aa3a6a0 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -103,6 +103,10 @@
bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
uint32_t target_slot_number);
+// Forcefully set metadata header version to 1.0, clearing any incompatible flags and attributes
+// so that when downgrading to a build with liblp V0, the device still boots.
+void SetMetadataHeaderV0(LpMetadata* metadata);
+
} // namespace fs_mgr
} // namespace android
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/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
index 0e57674..5995cef 100755
--- a/fs_mgr/libsnapshot/fuzz.sh
+++ b/fs_mgr/libsnapshot/fuzz.sh
@@ -11,7 +11,7 @@
build_normal() (
pushd $(gettop)
- NATIVE_COVERAGE="" NATIVE_LINE_COVERAGE="" COVERAGE_PATHS="" m ${FUZZ_TARGET}
+ NATIVE_COVERAGE="" NATIVE_LINE_COVERAGE="" NATIVE_COVERAGE_PATHS="" m ${FUZZ_TARGET}
ret=$?
popd
return ${ret}
@@ -19,7 +19,7 @@
build_cov() {
pushd $(gettop)
- NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET}
+ NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" NATIVE_COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET}
ret=$?
popd
return ${ret}
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..0b40fd6
--- /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) {}
+
+ 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()) {}
+
+ 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 5909cff..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();
@@ -1846,7 +1845,7 @@
PLOG(ERROR) << "Open failed: " << file;
return nullptr;
}
- if (lock_flags != 0 && flock(fd, lock_flags) < 0) {
+ if (lock_flags != 0 && TEMP_FAILURE_RETRY(flock(fd, lock_flags)) < 0) {
PLOG(ERROR) << "Acquire flock failed: " << file;
return nullptr;
}
@@ -1857,7 +1856,7 @@
}
SnapshotManager::LockedFile::~LockedFile() {
- if (flock(fd_, LOCK_UN) < 0) {
+ if (TEMP_FAILURE_RETRY(flock(fd_, LOCK_UN)) < 0) {
PLOG(ERROR) << "Failed to unlock file: " << path_;
}
}
@@ -2277,6 +2276,10 @@
auto operations_it = install_operation_map.find(target_partition->name());
if (operations_it != install_operation_map.end()) {
cow_creator->operations = operations_it->second;
+ } else {
+ LOG(INFO) << target_partition->name()
+ << " isn't included in the payload, skipping the cow creation.";
+ continue;
}
cow_creator->extra_extents.clear();
@@ -2516,7 +2519,19 @@
LOG(INFO) << "EnsureMetadataMounted does nothing in Android mode.";
return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice());
}
- return AutoUnmountDevice::New(device_->GetMetadataDir());
+ auto ret = AutoUnmountDevice::New(device_->GetMetadataDir());
+ if (ret == nullptr) return nullptr;
+
+ // In rescue mode, it is possible to erase and format metadata, but /metadata/ota is not
+ // created to execute snapshot updates. Hence, subsequent calls is likely to fail because
+ // Lock*() fails. By failing early and returning nullptr here, update_engine_sideload can
+ // treat this case as if /metadata is not mounted.
+ if (!LockShared()) {
+ LOG(WARNING) << "/metadata is mounted, but errors occur when acquiring a shared lock. "
+ "Subsequent calls to SnapshotManager will fail. Unmounting /metadata now.";
+ return nullptr;
+ }
+ return ret;
}
bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
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 60bf796..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;
}
@@ -62,6 +64,7 @@
std::string(it->second) + target_suffix_, &p});
}
}
+
}
bool SnapshotMetadataUpdater::ShrinkPartitions() const {
@@ -82,6 +85,18 @@
}
bool SnapshotMetadataUpdater::DeletePartitions() const {
+ // For partial update, not all dynamic partitions are included in the payload.
+ // TODO(xunchang) delete the untouched partitions whose group is in the payload.
+ // e.g. Delete vendor in the following scenario
+ // On device:
+ // Group A: system, vendor
+ // In payload:
+ // Group A: system
+ if (partial_update_) {
+ LOG(INFO) << "Skip deleting partitions for partial update";
+ return true;
+ }
+
std::vector<std::string> partitions_to_delete;
// Don't delete partitions in groups where the group name doesn't have target_suffix,
// e.g. default.
@@ -139,6 +154,11 @@
}
bool SnapshotMetadataUpdater::DeleteGroups() const {
+ if (partial_update_) {
+ LOG(INFO) << "Skip deleting groups for partial update";
+ return true;
+ }
+
std::vector<std::string> existing_groups = builder_->ListGroups();
for (const auto& existing_group_name : existing_groups) {
// Don't delete groups without target suffix, e.g. default.
@@ -154,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.h b/fs_mgr/libsnapshot/snapshot_metadata_updater.h
index 83c9460..5b1cbf9 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater.h
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater.h
@@ -79,6 +79,7 @@
const std::string target_suffix_;
std::vector<Group> groups_;
std::vector<Partition> partitions_;
+ bool partial_update_{false};
};
} // namespace snapshot
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 f82a602..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>
@@ -52,10 +53,19 @@
bool TestPartitionOpener::GetInfo(const std::string& partition_name,
android::fs_mgr::BlockDeviceInfo* info) const {
- if (partition_name == "super") {
- return PartitionOpener::GetInfo(fake_super_path_, info);
+ if (partition_name != "super") {
+ return PartitionOpener::GetInfo(partition_name, info);
}
- return PartitionOpener::GetInfo(partition_name, info);
+
+ if (PartitionOpener::GetInfo(fake_super_path_, info)) {
+ // SnapshotUpdateTest uses a relatively small super partition, which requires a small
+ // alignment and 0 offset to work. For the purpose of this test, hardcode the alignment
+ // and offset. This test isn't about testing liblp or libdm.
+ info->alignment_offset = 0;
+ info->alignment = std::min<uint32_t>(info->alignment, static_cast<uint32_t>(128_KiB));
+ return true;
+ }
+ return false;
}
std::string TestPartitionOpener::GetDeviceString(const std::string& partition_name) const {
@@ -232,5 +242,9 @@
return bsize_;
}
+bool IsVirtualAbEnabled() {
+ return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
index 8a11eaa..202e39b 100644
--- a/fs_mgr/libsnapshot/update_engine/update_metadata.proto
+++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
@@ -77,4 +77,5 @@
message DeltaArchiveManifest {
repeated PartitionUpdate partitions = 13;
optional DynamicPartitionMetadata dynamic_partition_metadata = 15;
+ optional bool partial_update = 16;
}
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 82c4262..d56f7f2 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -15,13 +15,17 @@
adb remount tests
---color Dress output with highlighting colors
---help This help
---no-wait-screen Do not wait for display screen to settle
---print-time Report the test duration
---serial Specify device (must if multiple are present)
---wait-adb <duration> adb wait timeout
---wait-fastboot <duration> fastboot wait timeout
+-c --color Dress output with highlighting colors
+-h --help This help
+-D --no-wait-screen Do not wait for display screen to settle
+-t --print-time Report the test duration
+-s --serial Specify device (must if multiple are present)"
+if [ -n "`which timeout`" ]; then
+ USAGE="${USAGE}
+-a --wait-adb <duration> adb wait timeout
+-f --wait-fastboot <duration> fastboot wait timeout"
+fi
+USAGE="${USAGE}
Conditions:
- Must be a userdebug build.
@@ -46,10 +50,10 @@
ESCAPE="`echo | tr '\n' '\033'`"
# A _real_ embedded carriage return character
CR="`echo | tr '\n' '\r'`"
-GREEN="${ESCAPE}[38;5;40m"
-RED="${ESCAPE}[38;5;196m"
-ORANGE="${ESCAPE}[38;5;255:165:0m"
-BLUE="${ESCAPE}[35m"
+GREEN="${ESCAPE}[32m"
+RED="${ESCAPE}[31m"
+YELLOW="${ESCAPE}[33m"
+BLUE="${ESCAPE}[34m"
NORMAL="${ESCAPE}[0m"
TMPDIR=${TMPDIR:-/tmp}
print_time=false
@@ -72,7 +76,7 @@
if [ -n "${ANDROID_SERIAL}" ]; then
grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
else
- wc -l | grep '^1$' >/dev/null
+ wc -l | grep "^[${SPACE}${TAB}]*1\$" >/dev/null
fi
}
@@ -85,7 +89,7 @@
if [ -n "${ANDROID_SERIAL}" ]; then
grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
else
- wc -l | grep '^1$' >/dev/null
+ wc -l | grep "^[${SPACE}${TAB}]*1\$" >/dev/null
fi
}
@@ -100,7 +104,7 @@
grep "^${ANDROID_SERIAL}[${SPACE}${TAB}][${SPACE}${TAB}]*recovery\$" >/dev/null
return ${?}
fi
- if echo "${list}" | wc -l | grep '^1$' >/dev/null; then
+ if echo "${list}" | wc -l | grep "^[${SPACE}${TAB}]*1\$" >/dev/null; then
echo "${list}" |
grep "[${SPACE}${TAB}]recovery\$" >/dev/null
return ${?}
@@ -143,7 +147,7 @@
adb logcat "${@}" </dev/null |
tr -d '\r' |
grep -v 'logd : logdr: UID=' |
- sed -e '${/------- beginning of kernel/d}' -e 's/^[0-1][0-9]-[0-3][0-9] //'
+ sed -e '${ /------- beginning of kernel/d }' -e 's/^[0-1][0-9]-[0-3][0-9] //'
}
[ "USAGE: avc_check >/dev/stderr
@@ -160,7 +164,7 @@
if [ -z "${L}" ]; then
return
fi
- echo "${ORANGE}[ WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
echo "${L}" | sed "s/^/${INDENT}/" >&2
}
@@ -284,7 +288,7 @@
local start=`date +%s`
local duration=
local ret
- if [ -n "${1}" ]; then
+ if [ -n "${1}" -a -n "`which timeout`" ]; then
USB_DEVICE=`usb_devnum --next`
duration=`format_duration ${1}`
echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
@@ -299,7 +303,7 @@
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
fi
fi
local end=`date +%s`
@@ -359,18 +363,22 @@
echo "(In adb mode `adb_user`)"
else
echo "(USB stack borken for ${USB_ADDRESS})"
- USB_DEVICE=`usb_devnum`
- if [ -n "${USB_DEVICE}" ]; then
- echo "# lsusb -v -s ${USB_DEVICE#dev}"
- local D=`lsusb -v -s ${USB_DEVICE#dev} 2>&1`
- if [ -n "${D}" ]; then
- echo "${D}"
- else
- lsusb -v
+ if [ -n "`which usb_devnum`" ]; then
+ USB_DEVICE=`usb_devnum`
+ if [ -n "`which lsusb`" ]; then
+ if [ -n "${USB_DEVICE}" ]; then
+ echo "# lsusb -v -s ${USB_DEVICE#dev}"
+ local D=`lsusb -v -s ${USB_DEVICE#dev} 2>&1`
+ if [ -n "${D}" ]; then
+ echo "${D}"
+ else
+ lsusb -v
+ fi
+ else
+ echo "# lsusb -v (expected device missing)"
+ lsusb -v
+ fi
fi
- else
- echo "# lsusb -v (expected device missing)"
- lsusb -v
fi >&2
fi
}
@@ -382,7 +390,7 @@
local ret
# fastboot has no wait-for-device, but it does an automatic
# wait and requires (even a nonsensical) command to do so.
- if [ -n "${1}" ]; then
+ if [ -n "${1}" -a -n "`which timeout`" ]; then
USB_DEVICE=`usb_devnum --next`
echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null
@@ -398,7 +406,7 @@
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
fi >&2
fi
return ${ret}
@@ -409,7 +417,7 @@
Returns: waits until the device has returned for recovery or optional timeout" ]
recovery_wait() {
local ret
- if [ -n "${1}" ]; then
+ if [ -n "${1}" -a -n "`which timeout`" ]; then
USB_DEVICE=`usb_devnum --next`
echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null
@@ -423,7 +431,7 @@
if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ echo "${YELLOW}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
fi >&2
fi
return ${ret}
@@ -732,6 +740,7 @@
grep -v \
-e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
-e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
+ -e "^\(ramdumpfs\) " \
-e " functionfs " \
-e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-e "^rootfs / rootfs rw," \
@@ -753,13 +762,28 @@
## MAINLINE
##
-OPTIONS=`getopt --alternative --unquoted \
- --longoptions help,serial:,colour,color,no-colour,no-color \
- --longoptions wait-adb:,wait-fastboot: \
- --longoptions wait-screen,wait-display \
- --longoptions no-wait-screen,no-wait-display \
- --longoptions gtest_print_time,print-time \
- -- "?hs:" ${*}` ||
+HOSTOS=`uname`
+GETOPTS="--alternative --unquoted
+ --longoptions help,serial:,colour,color,no-colour,no-color
+ --longoptions wait-adb:,wait-fastboot:
+ --longoptions wait-screen,wait-display
+ --longoptions no-wait-screen,no-wait-display
+ --longoptions gtest_print_time,print-time
+ --"
+if [ "Darwin" = "${HOSTOS}" ]; then
+ GETOPTS=
+ USAGE="`echo \"${USAGE}\" |
+ sed 's/--color/ /g
+ 1s/--help/-h/
+ s/--help/ /g
+ s/--no-wait-screen/ /g
+ s/--print-time/ /g
+ 1s/--serial/-s/
+ s/--serial/ /g
+ s/--wait-adb/ /g
+ s/--wait-fastboot/ /g'`"
+fi
+OPTIONS=`getopt ${GETOPTS} "?a:cCdDf:hs:t" ${*}` ||
( echo "${USAGE}" >&2 ; false ) ||
die "getopt failure"
set -- ${OPTIONS}
@@ -775,26 +799,26 @@
export ANDROID_SERIAL=${2}
shift
;;
- --color | --colour)
+ -c | --color | --colour)
color=true
;;
- --no-color | --no-colour)
+ -C | --no-color | --no-colour)
color=false
;;
- --no-wait-display | --no-wait-screen)
+ -D | --no-wait-display | --no-wait-screen)
screen_wait=false
;;
- --wait-display | --wait-screen)
+ -d | --wait-display | --wait-screen)
screen_wait=true
;;
- --print-time | --gtest_print_time)
+ -t | --print-time | --gtest_print_time)
print_time=true
;;
- --wait-adb)
+ -a | --wait-adb)
ADB_WAIT=${2}
shift
;;
- --wait-fastboot)
+ -f | --wait-fastboot)
FASTBOOT_WAIT=${2}
shift
;;
@@ -815,7 +839,7 @@
if ! ${color}; then
GREEN=""
RED=""
- ORANGE=""
+ YELLOW=""
BLUE=""
NORMAL=""
fi
@@ -827,14 +851,14 @@
inFastboot && die "device in fastboot mode"
inRecovery && die "device in recovery mode"
if ! inAdb; then
- echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} device not in adb mode" >&2
adb_wait ${ADB_WAIT}
fi
inAdb || die "specified device not in adb mode"
isDebuggable || die "device not a debug build"
enforcing=true
if ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then
- echo "${ORANGE}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
enforcing=false
fi
@@ -846,9 +870,13 @@
[ -n "${D}" ] || D=`get_property ro.boot.serialno`
[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
USB_SERIAL=
-[ -z "${ANDROID_SERIAL}" ] || USB_SERIAL=`find /sys/devices -name serial |
- grep usb |
- xargs -r grep -l ${ANDROID_SERIAL}`
+if [ -n "${ANDROID_SERIAL}" -a "Darwin" != "${HOSTOS}" ]; then
+ USB_SERIAL="`find /sys/devices -name serial | grep usb`"
+ if [ -n "${USB_SERIAL}" ]; then
+ USB_SERIAL=`echo "${USB_SERIAL}" |
+ xargs grep -l ${ANDROID_SERIAL}`
+ fi
+fi
USB_ADDRESS=
if [ -n "${USB_SERIAL}" ]; then
USB_ADDRESS=${USB_SERIAL%/serial}
@@ -860,13 +888,16 @@
BUILD_DESCRIPTION=`get_property ro.build.description`
[ -z "${BUILD_DESCRIPTION}" ] ||
echo "${BLUE}[ INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
+KERNEL_VERSION="`adb_su cat /proc/version </dev/null 2>/dev/null`"
+[ -z "${KERNEL_VERSION}" ] ||
+ echo "${BLUE}[ INFO ]${NORMAL} ${KERNEL_VERSION}" >&2
ACTIVE_SLOT=`get_active_slot`
[ -z "${ACTIVE_SLOT}" ] ||
echo "${BLUE}[ INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
# Acquire list of system partitions
-PARTITIONS=`adb_su cat /vendor/etc/fstab* |
+PARTITIONS=`adb_su cat /vendor/etc/fstab* </dev/null |
skip_administrative_mounts |
sed -n "s@^\([^ ${TAB}/][^ ${TAB}/]*\)[ ${TAB}].*[, ${TAB}]ro[, ${TAB}].*@\1@p" |
sort -u |
@@ -903,9 +934,12 @@
done
# If reboot too soon after fresh flash, could trip device update failure logic
+if ${screen_wait}; then
+ echo "${YELLOW}[ WARNING ]${NORMAL} waiting for screen to come up. Consider --no-wait-screen option" >&2
+fi
if ! wait_for_screen && ${screen_wait}; then
screen_wait=false
- echo "${ORANGE}[ WARNING ]${NORMAL} not healthy, no launcher, skipping wait for screen" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} not healthy, no launcher, skipping wait for screen" >&2
fi
# Can we test remount -R command?
@@ -954,7 +988,7 @@
adb_su remount -R system </dev/null
err=${?}
if [ "${err}" != 0 ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} adb shell su root remount -R system = ${err}, likely did not reboot!" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} adb shell su root remount -R system = ${err}, likely did not reboot!" >&2
T="-t ${T}"
else
# Rebooted, logcat will be meaningless, and last logcat will likely be clear
@@ -980,7 +1014,7 @@
adb_sh grep "nodev${TAB}overlay" /proc/filesystems </dev/null >/dev/null 2>/dev/null &&
echo "${GREEN}[ OK ]${NORMAL} overlay module present" >&2 ||
(
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay module not present" >&2 &&
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlay module not present" >&2 &&
false
) ||
overlayfs_supported=false
@@ -989,7 +1023,7 @@
echo "${GREEN}[ OK ]${NORMAL} overlay module supports override_creds" >&2 ||
case `adb_sh uname -r </dev/null` in
4.[456789].* | 4.[1-9][0-9]* | [56789].*)
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
overlayfs_supported=false
;;
*)
@@ -1011,14 +1045,14 @@
reboot=false
for d in ${OVERLAYFS_BACKING}; do
if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
- echo "${ORANGE}[ WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
adb_sh rm -rf /${d}/overlay </dev/null ||
die "/${d}/overlay wipe"
reboot=true
fi
done
if ${reboot}; then
- echo "${ORANGE}[ WARNING ]${NORMAL} rebooting before test" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} rebooting before test" >&2
adb_reboot &&
adb_wait ${ADB_WAIT} ||
die "lost device after reboot after wipe `usb_status`"
@@ -1030,7 +1064,7 @@
D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` &&
echo "${H}" &&
echo "${D}" &&
- echo "${ORANGE}[ WARNING ]${NORMAL} overlays present before setup" >&2 ||
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlays present before setup" >&2 ||
echo "${GREEN}[ OK ]${NORMAL} no overlay present before setup" >&2
overlayfs_needed=true
D=`adb_sh cat /proc/mounts </dev/null |
@@ -1083,7 +1117,7 @@
if [ X"${D}" != X"${H}" ]; then
echo "${H}"
if [ X"${D}" != X"${D##*setup failed}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} overlayfs setup whined" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlayfs setup whined" >&2
fi
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
@@ -1130,7 +1164,7 @@
elif ${rebooted}; then
echo "${GREEN}[ OK ]${NORMAL} verity already disabled" >&2
else
- echo "${ORANGE}[ WARNING ]${NORMAL} verity already disabled" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} verity already disabled" >&2
fi
echo "${GREEN}[ RUN ]${NORMAL} remount" >&2
@@ -1160,7 +1194,7 @@
die -t ${T} "overlay takeover failed"
fi
echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover not complete" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlay takeover not complete" >&2
if [ -z "${virtual_ab}" ]; then
scratch_partition=scratch
fi
@@ -1292,7 +1326,7 @@
fixup_from_recovery() {
inRecovery || return 1
- echo "${ORANGE}[ ERROR ]${NORMAL} Device in recovery" >&2
+ echo "${YELLOW}[ ERROR ]${NORMAL} Device in recovery" >&2
adb reboot </dev/null
adb_wait ${ADB_WAIT}
}
@@ -1312,7 +1346,7 @@
adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
skip_administrative_mounts |
grep -v ' \(erofs\|squashfs\|ext4\|f2fs\|vfat\) ' &&
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
echo "${GREEN}[ OK ]${NORMAL} overlay takeover in first stage init" >&2
fi
@@ -1373,20 +1407,20 @@
is_userspace_fastboot=false
if ! ${is_bootloader_fastboot}; then
- echo "${ORANGE}[ WARNING ]${NORMAL} does not support fastboot, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} does not support fastboot, skipping"
elif [ -z "${ANDROID_PRODUCT_OUT}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} build tree not setup, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} build tree not setup, skipping"
elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} vendor image missing, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} vendor image missing, skipping"
elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} wrong vendor image, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} wrong vendor image, skipping"
elif [ -z "${ANDROID_HOST_OUT}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} please run lunch, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} please run lunch, skipping"
elif ! (
adb_cat /vendor/build.prop |
cmp -s ${ANDROID_PRODUCT_OUT}/vendor/build.prop
) >/dev/null 2>/dev/null; then
- echo "${ORANGE}[ WARNING ]${NORMAL} vendor image signature mismatch, skipping"
+ echo "${YELLOW}[ WARNING ]${NORMAL} vendor image signature mismatch, skipping"
else
wait_for_screen
avc_check
@@ -1432,7 +1466,7 @@
fi
fastboot reboot ||
die "can not reboot out of fastboot"
- echo "${ORANGE}[ WARNING ]${NORMAL} adb after fastboot"
+ echo "${YELLOW}[ WARNING ]${NORMAL} adb after fastboot"
adb_wait ${ADB_WAIT} ||
fixup_from_recovery ||
die "did not reboot after formatting ${scratch_partition} `usb_status`"
@@ -1449,8 +1483,8 @@
if ${is_userspace_fastboot}; then
die "overlay supposed to be minus /vendor takeover after flash vendor"
else
- echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
fi
fi
B="`adb_cat /system/hello`"
@@ -1468,7 +1502,7 @@
check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
vendor content after flash vendor
else
- echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ echo "${YELLOW}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
--warning vendor content after flash vendor
fi
@@ -1489,7 +1523,7 @@
L=
D="${H%?Now reboot your device for settings to take effect*}"
if [ X"${H}" != X"${D}" ]; then
- echo "${ORANGE}[ WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
+ echo "${YELLOW}[ WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
adb_reboot &&
adb_wait ${ADB_WAIT} &&
@@ -1547,7 +1581,7 @@
err=${?}
if [ X"${D}" != "${D%?Now reboot your device for settings to take effect*}" ]
then
- echo "${ORANGE}[ WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
+ echo "${YELLOW}[ WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
adb_reboot &&
adb_wait ${ADB_WAIT} &&
adb_root ||
@@ -1580,9 +1614,9 @@
if [ -n "${ACTIVE_SLOT}" ]; then
local active_slot=`get_active_slot`
if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
- echo "${ORANGE}[ ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ echo "${YELLOW}[ ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
else
- echo "${ORANGE}[ ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
+ echo "${YELLOW}[ ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
fi >&2
fastboot --set-active=${ACTIVE_SLOT}
fi
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 2738457..7a3d9a9 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -174,6 +174,8 @@
}
return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
cow_device, mode, chunk_size);
+ } else if (target_type == "user") {
+ return std::make_unique<DmTargetUser>(start_sector, num_sectors);
} else {
std::cerr << "Unrecognized target type: " << target_type << std::endl;
return nullptr;
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/Android.bp b/init/Android.bp
index edf9099..3f2cd07 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -130,6 +130,7 @@
"libpropertyinfoserializer",
"libpropertyinfoparser",
"libsnapshot_init",
+ "libxml2",
"lib_apex_manifest_proto_lite",
"update_metadata-protos",
],
@@ -164,6 +165,9 @@
"selinux_policy_version",
],
srcs: init_common_sources + init_device_sources,
+ generated_sources: [
+ "apex-info-list",
+ ],
whole_static_libs: [
"libcap",
"com.android.sysprop.apex",
@@ -178,6 +182,12 @@
target: {
recovery: {
cflags: ["-DRECOVERY"],
+ exclude_static_libs: [
+ "libxml2",
+ ],
+ exclude_generated_sources: [
+ "apex-info-list",
+ ],
exclude_shared_libs: [
"libbinder",
"libutils",
@@ -212,6 +222,9 @@
target: {
recovery: {
cflags: ["-DRECOVERY"],
+ exclude_static_libs: [
+ "libxml2",
+ ],
exclude_shared_libs: [
"libbinder",
"libutils",
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/README.md b/init/README.md
index 188f19b..c3b64f6 100644
--- a/init/README.md
+++ b/init/README.md
@@ -197,11 +197,14 @@
Currently defaults to root. (??? probably should default to nobody)
`interface <interface name> <instance name>`
-> Associates this service with a list of the HIDL services that it provides. The interface name
- must be a fully-qualified name and not a value name. For instance, this is used to allow
- hwservicemanager to lazily start services. When multiple interfaces are served, this tag should
- be used multiple times.
- For example: interface vendor.foo.bar@1.0::IBaz default
+> Associates this service with a list of the AIDL or HIDL services that it provides. The interface
+ name must be a fully-qualified name and not a value name. For instance, this is used to allow
+ servicemanager or hwservicemanager to lazily start services. When multiple interfaces are served,
+ this tag should be used multiple times. An example of an entry for a HIDL
+ interface is `interface vendor.foo.bar@1.0::IBaz default`. For an AIDL interface, use
+ `interface aidl <instance name>`. The instance name for an AIDL interface is
+ whatever is registered with servicemanager, and these can be listed with `adb
+ shell dumpsys -l`.
`ioprio <class> <priority>`
> Sets the IO priority and IO priority class for this service via the SYS_ioprio_set syscall.
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 824bd1e..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.
@@ -1225,6 +1221,20 @@
return {};
}
+static Result<void> MountLinkerConfigForDefaultNamespace() {
+ // No need to mount linkerconfig for default mount namespace if the path does not exist (which
+ // would mean it is already mounted)
+ if (access("/linkerconfig/default", 0) != 0) {
+ return {};
+ }
+
+ if (mount("/linkerconfig/default", "/linkerconfig", nullptr, MS_BIND | MS_REC, nullptr) != 0) {
+ return ErrnoError() << "Failed to mount linker configuration for default mount namespace.";
+ }
+
+ return {};
+}
+
static bool IsApexUpdatable() {
static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
return updatable;
@@ -1323,11 +1333,14 @@
}
static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
- if (SwitchToDefaultMountNamespace()) {
- return {};
- } else {
- return Error() << "Failed to enter into default mount namespace";
+ if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {
+ return result.error();
}
+ if (auto result = MountLinkerConfigForDefaultNamespace(); !result.ok()) {
+ return result.error();
+ }
+ LOG(INFO) << "Switched to default mount namespace";
+ return {};
}
// Builtin-function-map start
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/devices.cpp b/init/devices.cpp
index 9fbec64..53ca875 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -193,7 +193,8 @@
while (directory != "/" && directory != ".") {
std::string subsystem_link_path;
if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
- subsystem_link_path == sysfs_mount_point_ + "/bus/platform") {
+ (subsystem_link_path == sysfs_mount_point_ + "/bus/platform" ||
+ subsystem_link_path == sysfs_mount_point_ + "/bus/amba")) {
// We need to remove the mount point that we added above before returning.
directory.erase(0, sysfs_mount_point_.size());
*platform_device_path = directory;
diff --git a/init/init.cpp b/init/init.cpp
index 631db8e..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
@@ -868,6 +875,8 @@
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
+ // Restore prio before main loop
+ setpriority(PRIO_PROCESS, 0, 0);
while (true) {
// By default, sleep until something happens.
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 07b4724..fa65740 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -17,6 +17,7 @@
#include <functional>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <gtest/gtest.h>
#include "action.h"
@@ -32,6 +33,8 @@
#include "service_parser.h"
#include "util.h"
+using android::base::GetIntProperty;
+
namespace android {
namespace init {
@@ -240,6 +243,10 @@
}
TEST(init, RejectsCriticalAndOneshotService) {
+ if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
+ GTEST_SKIP() << "Test only valid for devices launching with R or later";
+ }
+
std::string init_script =
R"init(
service A something
diff --git a/init/main.cpp b/init/main.cpp
index 38bc74b..23f5530 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -52,7 +52,8 @@
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
-
+ // Boost prio which will be restored later
+ setpriority(PRIO_PROCESS, 0, -20);
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index f3b584c..59cc140 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -27,58 +27,34 @@
#include <android-base/properties.h>
#include <android-base/result.h>
#include <android-base/unique_fd.h>
-#include <apex_manifest.pb.h>
#include "util.h"
+#ifndef RECOVERY
+#define ACTIVATE_FLATTENED_APEX 1
+#endif
+
+#ifdef ACTIVATE_FLATTENED_APEX
+#include <apex_manifest.pb.h>
+#include <com_android_apex.h>
+#include <selinux/android.h>
+#endif // ACTIVATE_FLATTENED_APEX
+
namespace android {
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;
@@ -106,6 +82,8 @@
return updatable;
}
+#ifdef ACTIVATE_FLATTENED_APEX
+
static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
return ErrnoError() << "Could not create mount point " << mount_path;
@@ -116,7 +94,7 @@
return {};
}
-static Result<std::string> GetApexName(const std::string& apex_dir) {
+static Result<apex::proto::ApexManifest> GetApexManifest(const std::string& apex_dir) {
const std::string manifest_path = apex_dir + "/apex_manifest.pb";
std::string content;
if (!android::base::ReadFileToString(manifest_path, &content)) {
@@ -126,11 +104,12 @@
if (!manifest.ParseFromString(content)) {
return Error() << "Can't parse manifest file: " << manifest_path;
}
- return manifest.name();
+ return manifest;
}
+template <typename Fn>
static Result<void> ActivateFlattenedApexesFrom(const std::string& from_dir,
- const std::string& to_dir) {
+ const std::string& to_dir, Fn on_activate) {
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(from_dir.c_str()), closedir);
if (!dir) {
return {};
@@ -140,15 +119,16 @@
if (entry->d_name[0] == '.') continue;
if (entry->d_type == DT_DIR) {
const std::string apex_path = from_dir + "/" + entry->d_name;
- const auto apex_name = GetApexName(apex_path);
- if (!apex_name.ok()) {
- LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_name.error();
+ const auto apex_manifest = GetApexManifest(apex_path);
+ if (!apex_manifest.ok()) {
+ LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
continue;
}
- const std::string mount_path = to_dir + "/" + (*apex_name);
+ const std::string mount_path = to_dir + "/" + apex_manifest->name();
if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
return result;
}
+ on_activate(apex_path, *apex_manifest);
}
}
return {};
@@ -167,28 +147,36 @@
"/vendor/apex",
};
+ std::vector<com::android::apex::ApexInfo> apex_infos;
+ auto on_activate = [&](const std::string& apex_path,
+ const apex::proto::ApexManifest& apex_manifest) {
+ apex_infos.emplace_back(apex_manifest.name(), apex_path, apex_path, apex_manifest.version(),
+ apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true);
+ };
+
for (const auto& dir : kBuiltinDirsForApexes) {
- if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop); !result.ok()) {
+ if (auto result = ActivateFlattenedApexesFrom(dir, kApexTop, on_activate); !result.ok()) {
LOG(ERROR) << result.error();
return false;
}
}
+
+ std::ostringstream oss;
+ com::android::apex::ApexInfoList apex_info_list(apex_infos);
+ com::android::apex::write(oss, apex_info_list);
+ const std::string kApexInfoList = kApexTop + "/apex-info-list.xml";
+ if (!android::base::WriteStringToFile(oss.str(), kApexInfoList)) {
+ PLOG(ERROR) << "Failed to write " << kApexInfoList;
+ return false;
+ }
+ if (selinux_android_restorecon(kApexInfoList.c_str(), 0) != 0) {
+ PLOG(ERROR) << "selinux_android_restorecon(" << kApexInfoList << ") failed";
+ }
+
return true;
}
-static Result<void> MountLinkerConfigForDefaultNamespace() {
- // No need to mount linkerconfig for default mount namespace if the path does not exist (which
- // would mean it is already mounted)
- if (access("/linkerconfig/default", 0) != 0) {
- return {};
- }
-
- if (mount("/linkerconfig/default", "/linkerconfig", nullptr, MS_BIND | MS_REC, nullptr) != 0) {
- return ErrnoError() << "Failed to mount linker configuration for default mount namespace.";
- }
-
- return {};
-}
+#endif // ACTIVATE_FLATTENED_APEX
static android::base::unique_fd bootstrap_ns_fd;
static android::base::unique_fd default_ns_fd;
@@ -204,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
@@ -245,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();
@@ -283,47 +271,27 @@
default_ns_fd.reset(OpenMountNamespace());
default_ns_id = GetMountNamespaceId();
}
-
+#ifdef ACTIVATE_FLATTENED_APEX
success &= ActivateFlattenedApexesIfPossible();
-
+#endif
LOG(INFO) << "SetupMountNamespaces done";
return success;
}
-bool SwitchToDefaultMountNamespace() {
- if (IsRecoveryMode()) {
- // we don't have multiple namespaces in recovery mode
- return true;
+Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {
+ if (IsRecoveryMode() || !IsApexUpdatable()) {
+ // we don't have multiple namespaces in recovery mode or if apex is not updatable
+ return {};
}
- if (default_ns_id != GetMountNamespaceId()) {
- if (setns(default_ns_fd.get(), CLONE_NEWNS) == -1) {
- PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
- return false;
- }
-
- if (auto result = MountLinkerConfigForDefaultNamespace(); !result.ok()) {
- LOG(ERROR) << result.error();
- return false;
+ const auto& ns_id = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_id : default_ns_id;
+ const auto& ns_fd = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_fd : default_ns_fd;
+ const auto& ns_name = target_mount_namespace == NS_BOOTSTRAP ? "bootstrap" : "default";
+ if (ns_id != GetMountNamespaceId() && ns_fd.get() != -1) {
+ if (setns(ns_fd.get(), CLONE_NEWNS) == -1) {
+ return ErrnoError() << "Failed to switch to " << ns_name << " mount namespace.";
}
}
-
- LOG(INFO) << "Switched to default mount namespace";
- return true;
-}
-
-bool SwitchToBootstrapMountNamespaceIfNeeded() {
- if (IsRecoveryMode()) {
- // we don't have multiple namespaces in recovery mode
- return true;
- }
- if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
- IsApexUpdatable()) {
- if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
- PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
- return false;
- }
- }
- return true;
+ return {};
}
} // namespace init
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
index c41a449..d4d6f82 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -16,12 +16,15 @@
#pragma once
+#include <android-base/result.h>
+
namespace android {
namespace init {
+enum MountNamespace { NS_BOOTSTRAP, NS_DEFAULT };
+
bool SetupMountNamespaces();
-bool SwitchToDefaultMountNamespace();
-bool SwitchToBootstrapMountNamespaceIfNeeded();
+base::Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace);
} // namespace init
} // namespace android
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 82f5b8c..a1e0969 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -51,6 +51,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -74,6 +75,7 @@
using namespace std::literals;
using android::base::GetProperty;
+using android::base::ParseInt;
using android::base::ReadFileToString;
using android::base::Split;
using android::base::StartsWith;
@@ -630,9 +632,11 @@
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
- static constexpr const char* const kVendorPathPrefixes[2] = {
+ static constexpr const char* const kVendorPathPrefixes[4] = {
"/vendor",
"/odm",
+ "/vendor_dlkm",
+ "/odm_dlkm",
};
const char* context = kInitContext;
@@ -711,8 +715,8 @@
if (it == properties->end()) {
(*properties)[key] = value;
} else if (it->second != value) {
- LOG(WARNING) << "Overriding previous 'ro.' property '" << key << "':'"
- << it->second << "' with new value '" << value << "'";
+ LOG(WARNING) << "Overriding previous property '" << key << "':'" << it->second
+ << "' with new value '" << value << "'";
it->second = value;
}
} else {
@@ -886,24 +890,62 @@
load_properties_from_file("/prop.default", nullptr, &properties);
}
+ // /<part>/etc/build.prop is the canonical location of the build-time properties since S.
+ // Falling back to /<part>/defalt.prop and /<part>/build.prop only when legacy path has to
+ // be supported, which is controlled by the support_legacy_path_until argument.
+ const auto load_properties_from_partition = [&properties](const std::string& partition,
+ int support_legacy_path_until) {
+ auto path = "/" + partition + "/etc/build.prop";
+ if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
+ return;
+ }
+ // To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a
+ // separate map. Then by comparing its value with legacy_version, we know that if the
+ // partition is old enough so that we need to respect the legacy paths.
+ std::map<std::string, std::string> temp;
+ auto legacy_path1 = "/" + partition + "/default.prop";
+ auto legacy_path2 = "/" + partition + "/build.prop";
+ load_properties_from_file(legacy_path1.c_str(), nullptr, &temp);
+ load_properties_from_file(legacy_path2.c_str(), nullptr, &temp);
+ bool support_legacy_path = false;
+ auto version_prop_name = "ro." + partition + ".build.version.sdk";
+ auto it = temp.find(version_prop_name);
+ if (it == temp.end()) {
+ // This is embarassing. Without the prop, we can't determine how old the partition is.
+ // Let's be conservative by assuming it is very very old.
+ support_legacy_path = true;
+ } else if (int value;
+ ParseInt(it->second.c_str(), &value) && value <= support_legacy_path_until) {
+ support_legacy_path = true;
+ }
+ if (support_legacy_path) {
+ // We don't update temp into properties directly as it might skip any (future) logic
+ // for resolving duplicates implemented in load_properties_from_file. Instead, read
+ // the files again into the properties map.
+ load_properties_from_file(legacy_path1.c_str(), nullptr, &properties);
+ load_properties_from_file(legacy_path2.c_str(), nullptr, &properties);
+ } else {
+ LOG(FATAL) << legacy_path1 << " and " << legacy_path2 << " were not loaded "
+ << "because " << version_prop_name << "(" << it->second << ") is newer "
+ << "than " << support_legacy_path_until;
+ }
+ };
+
+ // Order matters here. The more the partition is specific to a product, the higher its
+ // precedence is.
load_properties_from_file("/system/build.prop", nullptr, &properties);
- load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
-
- // TODO(b/117892318): uncomment the following condition when vendor.imgs for
- // aosp_* targets are all updated.
-// if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
- load_properties_from_file("/vendor/default.prop", nullptr, &properties);
-// }
+ load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
+ // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
+ // all updated.
+ // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
+ load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+ // }
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+ load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);
+ load_properties_from_file("/odm_dlkm/etc/build.prop", nullptr, &properties);
+ load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
+ load_properties_from_partition("product", /* support_legacy_path_until */ 30);
- if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
- load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
- } else {
- load_properties_from_file("/odm/default.prop", nullptr, &properties);
- load_properties_from_file("/odm/build.prop", nullptr, &properties);
- }
-
- load_properties_from_file("/product/build.prop", nullptr, &properties);
load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
if (access(kDebugRamdiskProp, R_OK) == 0) {
@@ -952,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 310a99a..0cce6a3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -544,6 +544,18 @@
return still_running;
}
+static Result<void> UnmountAllApexes() {
+ const char* args[] = {"/system/bin/apexd", "--unmount-all"};
+ int status;
+ if (logwrap_fork_execvp(arraysize(args), args, &status, false, LOG_KLOG, true, nullptr) != 0) {
+ return ErrnoError() << "Failed to call '/system/bin/apexd --unmount-all'";
+ }
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ return {};
+ }
+ return Error() << "'/system/bin/apexd --unmount-all' failed : " << status;
+}
+
//* Reboot / shutdown the system.
// cmd ANDROID_RB_* as defined in android_reboot.h
// reason Reason string like "reboot", "shutdown,userrequested"
@@ -698,6 +710,11 @@
// 5. drop caches and disable zram backing device, if exist
KillZramBackingDevice();
+ LOG(INFO) << "Ready to unmount apexes. So far shutdown sequence took " << t;
+ // 6. unmount active apexes, otherwise they might prevent clean unmount of /data.
+ if (auto ret = UnmountAllApexes(); !ret.ok()) {
+ LOG(ERROR) << ret.error();
+ }
UmountStat stat =
TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
// Follow what linux shutdown is doing: one more sync with little bit delay
@@ -736,18 +753,6 @@
StartSendingMessages();
}
-static Result<void> UnmountAllApexes() {
- const char* args[] = {"/system/bin/apexd", "--unmount-all"};
- int status;
- if (logwrap_fork_execvp(arraysize(args), args, &status, false, LOG_KLOG, true, nullptr) != 0) {
- return ErrnoError() << "Failed to call '/system/bin/apexd --unmount-all'";
- }
- if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
- return {};
- }
- return Error() << "'/system/bin/apexd --unmount-all' failed : " << status;
-}
-
static std::chrono::milliseconds GetMillisProperty(const std::string& name,
std::chrono::milliseconds default_value) {
auto value = GetUintProperty(name, static_cast<uint64_t>(default_value.count()));
@@ -845,7 +850,7 @@
sub_reason = "apex";
return result;
}
- if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+ if (!SwitchToMountNamespaceIfNeeded(NS_BOOTSTRAP)) {
sub_reason = "ns_switch";
return Error() << "Failed to switch to bootstrap namespace";
}
diff --git a/init/service.cpp b/init/service.cpp
index 165b848..68365b3 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -465,6 +465,16 @@
pre_apexd_ = true;
}
+ // For pre-apexd services, override mount namespace as "bootstrap" one before starting.
+ // Note: "ueventd" is supposed to be run in "default" mount namespace even if it's pre-apexd
+ // to support loading firmwares from APEXes.
+ std::optional<MountNamespace> override_mount_namespace;
+ if (name_ == "ueventd") {
+ override_mount_namespace = NS_DEFAULT;
+ } else if (pre_apexd_) {
+ override_mount_namespace = NS_BOOTSTRAP;
+ }
+
post_data_ = ServiceList::GetInstance().IsPostData();
LOG(INFO) << "starting service '" << name_ << "'...";
@@ -496,7 +506,8 @@
if (pid == 0) {
umask(077);
- if (auto result = EnterNamespaces(namespaces_, name_, pre_apexd_); !result.ok()) {
+ if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace);
+ !result.ok()) {
LOG(FATAL) << "Service '" << name_
<< "' failed to set up namespaces: " << result.error();
}
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 484c2c8..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
@@ -194,7 +195,8 @@
return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
}
-Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name,
+ std::optional<MountNamespace> override_mount_namespace) {
for (const auto& [nstype, path] : info.namespaces_to_enter) {
if (auto result = EnterNamespace(nstype, path.c_str()); !result.ok()) {
return result;
@@ -202,9 +204,10 @@
}
#if defined(__ANDROID__)
- if (pre_apexd) {
- if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
- return Error() << "could not enter into the bootstrap mount namespace";
+ if (override_mount_namespace.has_value()) {
+ if (auto result = SwitchToMountNamespaceIfNeeded(override_mount_namespace.value());
+ !result.ok()) {
+ return result;
}
}
#endif
diff --git a/init/service_utils.h b/init/service_utils.h
index 3f1071e..e74f8c1 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -19,12 +19,14 @@
#include <sys/resource.h>
#include <sys/types.h>
+#include <optional>
#include <string>
#include <vector>
#include <android-base/unique_fd.h>
#include <cutils/iosched_policy.h>
+#include "mount_namespace.h"
#include "result.h"
namespace android {
@@ -66,7 +68,8 @@
// Pair of namespace type, path to name.
std::vector<std::pair<int, std::string>> namespaces_to_enter;
};
-Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd);
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name,
+ std::optional<MountNamespace> override_mount_namespace);
struct ProcessAttributes {
std::string console;
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index f3dd538..9d4ea8c 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -18,6 +18,8 @@
#include <fcntl.h>
#include <poll.h>
+#include <sys/time.h>
+#include <sys/resource.h>
#include <unistd.h>
#include <android-base/file.h>
@@ -181,6 +183,8 @@
trigger_shutdown = [](const std::string& command) { shutdown_command = command; };
auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
+ // Restore prio before main loop
+ setpriority(PRIO_PROCESS, 0, 0);
subcontext_process.MainLoop();
return 0;
}
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/init/ueventd.cpp b/init/ueventd.cpp
index 7514b61..54659c5 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -321,6 +321,8 @@
while (waitpid(-1, nullptr, WNOHANG) > 0) {
}
+ // Restore prio before main loop
+ setpriority(PRIO_PROCESS, 0, 0);
uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {
for (auto& uevent_handler : uevent_handlers) {
uevent_handler->HandleUevent(uevent);
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index f71d0c3..22f381c 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -311,6 +311,8 @@
}
};
+std::recursive_mutex FuseBridgeLoop::mutex_;
+
FuseBridgeLoop::FuseBridgeLoop() : opened_(true) {
base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
if (epoll_fd.get() == -1) {
@@ -328,7 +330,7 @@
std::unique_ptr<FuseBridgeEntry> bridge(
new FuseBridgeEntry(mount_id, std::move(dev_fd), std::move(proxy_fd)));
- std::lock_guard<std::mutex> lock(mutex_);
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
if (!opened_) {
LOG(ERROR) << "Tried to add a mount to a closed bridge";
return false;
@@ -372,7 +374,7 @@
const bool wait_result = epoll_controller_->Wait(bridges_.size(), &entries);
LOG(VERBOSE) << "Receive epoll events";
{
- std::lock_guard<std::mutex> lock(mutex_);
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
if (!(wait_result && ProcessEventLocked(entries, callback))) {
for (auto it = bridges_.begin(); it != bridges_.end();) {
callback->OnClosed(it->second->mount_id());
@@ -385,5 +387,13 @@
}
}
+void FuseBridgeLoop::Lock() {
+ mutex_.lock();
+}
+
+void FuseBridgeLoop::Unlock() {
+ mutex_.unlock();
+}
+
} // namespace fuse
} // namespace android
diff --git a/libappfuse/include/libappfuse/FuseBridgeLoop.h b/libappfuse/include/libappfuse/FuseBridgeLoop.h
index 6bfda98..d5fc28f 100644
--- a/libappfuse/include/libappfuse/FuseBridgeLoop.h
+++ b/libappfuse/include/libappfuse/FuseBridgeLoop.h
@@ -50,6 +50,10 @@
// thread from one which invokes |Start|.
bool AddBridge(int mount_id, base::unique_fd dev_fd, base::unique_fd proxy_fd);
+ static void Lock();
+
+ static void Unlock();
+
private:
bool ProcessEventLocked(const std::unordered_set<FuseBridgeEntry*>& entries,
FuseBridgeLoopCallback* callback);
@@ -60,7 +64,7 @@
std::map<int, std::unique_ptr<FuseBridgeEntry>> bridges_;
// Lock for multi-threading.
- std::mutex mutex_;
+ static std::recursive_mutex mutex_;
bool opened_;
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index f75e8df..c7969f2 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -96,6 +96,8 @@
cc_library {
name: "libbacktrace",
vendor_available: false,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
recovery_available: true,
apex_available: [
"//apex_available:platform",
@@ -120,6 +122,9 @@
recovery: {
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
},
+ native_bridge: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ },
},
}
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/logprint.cpp b/liblog/logprint.cpp
index 238431f..a5c5edd 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -78,18 +78,21 @@
static bool descriptive_output = false;
/*
- * gnome-terminal color tags
- * See http://misc.flogisoft.com/bash/tip_colors_and_formatting
- * for ideas on how to set the forground color of the text for xterm.
- * The color manipulation character stream is defined as:
- * ESC [ 3 8 ; 5 ; <color#> m
+ * 8-bit color tags. See ECMA-48 Set Graphics Rendition in
+ * [console_codes(4)](https://man7.org/linux/man-pages/man4/console_codes.4.html).
+ *
+ * The text manipulation character stream is defined as:
+ * ESC [ <parameter #> m
+ *
+ * We use "set <color> foreground" escape sequences instead of
+ * "256/24-bit foreground color". This allows colors to render
+ * according to user preferences in terminal emulator settings
*/
-#define ANDROID_COLOR_BLUE 75
-#define ANDROID_COLOR_DEFAULT 231
-#define ANDROID_COLOR_GREEN 40
-#define ANDROID_COLOR_ORANGE 166
-#define ANDROID_COLOR_RED 196
-#define ANDROID_COLOR_YELLOW 226
+#define ANDROID_COLOR_BLUE 34
+#define ANDROID_COLOR_DEFAULT 39
+#define ANDROID_COLOR_GREEN 32
+#define ANDROID_COLOR_RED 31
+#define ANDROID_COLOR_YELLOW 33
static FilterInfo* filterinfo_new(const char* tag, android_LogPriority pri) {
FilterInfo* p_ret;
@@ -165,7 +168,7 @@
case ANDROID_LOG_VERBOSE: return ANDROID_COLOR_DEFAULT;
case ANDROID_LOG_DEBUG: return ANDROID_COLOR_BLUE;
case ANDROID_LOG_INFO: return ANDROID_COLOR_GREEN;
- case ANDROID_LOG_WARN: return ANDROID_COLOR_ORANGE;
+ case ANDROID_LOG_WARN: return ANDROID_COLOR_YELLOW;
case ANDROID_LOG_ERROR: return ANDROID_COLOR_RED;
case ANDROID_LOG_FATAL: return ANDROID_COLOR_RED;
case ANDROID_LOG_SILENT: return ANDROID_COLOR_DEFAULT;
@@ -1499,7 +1502,7 @@
*/
if (p_format->colored_output) {
prefixLen =
- snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm", colorFromPri(entry->priority));
+ snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[%dm", colorFromPri(entry->priority));
prefixLen = MIN(prefixLen, sizeof(prefixBuf));
const char suffixContents[] = "\x1B[0m";
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index 0e39aab..5640900 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -96,7 +96,7 @@
((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
(logger_list->start.tv_nsec <= buf.l.realtime.tv_nsec)))) &&
(!logger_list->pid || (logger_list->pid == buf.p.pid))) {
- char* msg = reinterpret_cast<char*>(&log_msg->entry) + log_msg->entry.hdr_size;
+ char* msg = reinterpret_cast<char*>(&log_msg->entry) + sizeof(log_msg->entry);
*msg = buf.prio;
fd = atomic_load(&logger_list->fd);
if (fd <= 0) {
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index f5e060c..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,245 +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) {
- static long pages, pagesize;
- unsigned long maximum;
-
- if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
- return false;
- }
-
- if (!pages) {
- pages = sysconf(_SC_PHYS_PAGES);
- }
- if (pages < 1) {
- return true;
- }
-
- if (!pagesize) {
- pagesize = sysconf(_SC_PAGESIZE);
- if (pagesize <= 1) {
- pagesize = PAGE_SIZE;
- }
- }
-
- /* maximum memory impact a somewhat arbitrary ~3% */
- pages = (pages + 31) / 32;
- maximum = pages * pagesize;
-
- if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
- return true;
- }
-
- return value <= maximum;
-}
-
-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) {
@@ -642,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/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index a7687af..baee4f9 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -36,8 +36,7 @@
std::vector<std::string>* post_dependencies);
void ResetModuleCount() { module_count_ = 0; }
int GetModuleCount() { return module_count_; }
- void EnableBlacklist(bool enable);
- void EnableVerbose(bool enable);
+ void EnableBlocklist(bool enable);
private:
std::string MakeCanonical(const std::string& module_path);
@@ -55,7 +54,7 @@
bool ParseSoftdepCallback(const std::vector<std::string>& args);
bool ParseLoadCallback(const std::vector<std::string>& args);
bool ParseOptionsCallback(const std::vector<std::string>& args);
- bool ParseBlacklistCallback(const std::vector<std::string>& args);
+ bool ParseBlocklistCallback(const std::vector<std::string>& args);
void ParseKernelCmdlineOptions();
void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);
@@ -65,8 +64,8 @@
std::vector<std::pair<std::string, std::string>> module_post_softdep_;
std::vector<std::string> module_load_;
std::unordered_map<std::string, std::string> module_options_;
- std::set<std::string> module_blacklist_;
+ std::set<std::string> module_blocklist_;
std::unordered_set<std::string> module_loaded_;
int module_count_ = 0;
- bool blacklist_enabled = false;
+ bool blocklist_enabled = false;
};
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index d193796..ceabf62 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -194,17 +194,17 @@
return true;
}
-bool Modprobe::ParseBlacklistCallback(const std::vector<std::string>& args) {
+bool Modprobe::ParseBlocklistCallback(const std::vector<std::string>& args) {
auto it = args.begin();
const std::string& type = *it++;
- if (type != "blacklist") {
- LOG(ERROR) << "non-blacklist line encountered in modules.blacklist";
+ if (type != "blocklist") {
+ LOG(ERROR) << "non-blocklist line encountered in modules.blocklist";
return false;
}
if (args.size() != 2) {
- LOG(ERROR) << "lines in modules.blacklist must have exactly 2 entries, not " << args.size();
+ LOG(ERROR) << "lines in modules.blocklist must have exactly 2 entries, not " << args.size();
return false;
}
@@ -214,7 +214,7 @@
if (canonical_name.empty()) {
return false;
}
- this->module_blacklist_.emplace(canonical_name);
+ this->module_blocklist_.emplace(canonical_name);
return true;
}
@@ -331,24 +331,15 @@
auto options_callback = std::bind(&Modprobe::ParseOptionsCallback, this, _1);
ParseCfg(base_path + "/modules.options", options_callback);
- auto blacklist_callback = std::bind(&Modprobe::ParseBlacklistCallback, this, _1);
- ParseCfg(base_path + "/modules.blacklist", blacklist_callback);
+ auto blocklist_callback = std::bind(&Modprobe::ParseBlocklistCallback, this, _1);
+ ParseCfg(base_path + "/modules.blocklist", blocklist_callback);
}
ParseKernelCmdlineOptions();
- android::base::SetMinimumLogSeverity(android::base::INFO);
}
-void Modprobe::EnableBlacklist(bool enable) {
- blacklist_enabled = enable;
-}
-
-void Modprobe::EnableVerbose(bool enable) {
- if (enable) {
- android::base::SetMinimumLogSeverity(android::base::VERBOSE);
- } else {
- android::base::SetMinimumLogSeverity(android::base::INFO);
- }
+void Modprobe::EnableBlocklist(bool enable) {
+ blocklist_enabled = enable;
}
std::vector<std::string> Modprobe::GetDependencies(const std::string& module) {
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 6589708..fb1f5e7 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -35,7 +35,7 @@
android::base::unique_fd fd(
TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (fd == -1) {
- LOG(ERROR) << "Could not open module '" << path_name << "'";
+ PLOG(ERROR) << "Could not open module '" << path_name << "'";
return false;
}
@@ -49,7 +49,7 @@
options = options + " " + parameters;
}
- LOG(INFO) << "Loading module " << path_name << " with args \"" << options << "\"";
+ LOG(INFO) << "Loading module " << path_name << " with args '" << options << "'";
int ret = syscall(__NR_finit_module, fd.get(), options.c_str(), 0);
if (ret != 0) {
if (errno == EEXIST) {
@@ -57,7 +57,7 @@
module_loaded_.emplace(canonical_name);
return true;
}
- LOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
+ PLOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
return false;
}
@@ -80,8 +80,8 @@
bool Modprobe::ModuleExists(const std::string& module_name) {
struct stat fileStat;
- if (blacklist_enabled && module_blacklist_.count(module_name)) {
- LOG(INFO) << "module " << module_name << " is blacklisted";
+ if (blocklist_enabled && module_blocklist_.count(module_name)) {
+ LOG(INFO) << "module " << module_name << " is blocklisted";
return false;
}
auto deps = GetDependencies(module_name);
diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp
index 9ee5ba7..e79bfaf 100644
--- a/libmodprobe/libmodprobe_ext_test.cpp
+++ b/libmodprobe/libmodprobe_ext_test.cpp
@@ -72,7 +72,7 @@
bool Modprobe::ModuleExists(const std::string& module_name) {
auto deps = GetDependencies(module_name);
- if (blacklist_enabled && module_blacklist_.count(module_name)) {
+ if (blocklist_enabled && module_blocklist_.count(module_name)) {
return false;
}
if (deps.empty()) {
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index eea0abd..5919c49 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -113,9 +113,9 @@
"options test9.ko param_x=1 param_y=2 param_z=3\n"
"options test100.ko param_1=1\n";
- const std::string modules_blacklist =
- "blacklist test9.ko\n"
- "blacklist test3.ko\n";
+ const std::string modules_blocklist =
+ "blocklist test9.ko\n"
+ "blocklist test3.ko\n";
const std::string modules_load =
"test4.ko\n"
@@ -139,7 +139,7 @@
0600, getuid(), getgid()));
ASSERT_TRUE(android::base::WriteStringToFile(modules_load, dir_path + "/modules.load", 0600,
getuid(), getgid()));
- ASSERT_TRUE(android::base::WriteStringToFile(modules_blacklist, dir_path + "/modules.blacklist",
+ ASSERT_TRUE(android::base::WriteStringToFile(modules_blocklist, dir_path + "/modules.blocklist",
0600, getuid(), getgid()));
for (auto i = test_modules.begin(); i != test_modules.end(); ++i) {
@@ -176,6 +176,6 @@
EXPECT_TRUE(modules_loaded == expected_after_remove);
- m.EnableBlacklist(true);
+ m.EnableBlocklist(true);
EXPECT_FALSE(m.LoadWithAliases("test4", true));
}
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 268496f..65371fa 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -23,6 +23,21 @@
export_include_dirs: ["include"],
}
+cc_library_static {
+ name: "libipchecksum",
+
+ srcs: [
+ "checksum.c",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ export_include_dirs: ["include"],
+}
+
cc_binary {
name: "dhcpdbg",
diff --git a/libpixelflinger/include/pixelflinger/format.h b/libpixelflinger/include/pixelflinger/format.h
index 82eeca4..d429477 100644
--- a/libpixelflinger/include/pixelflinger/format.h
+++ b/libpixelflinger/include/pixelflinger/format.h
@@ -82,7 +82,7 @@
GGL_INDEX_CR = 2,
};
-typedef struct {
+typedef struct GGLFormat {
#ifdef __cplusplus
enum {
ALPHA = GGL_INDEX_ALPHA,
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 a515e58..c4dbf8e 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -49,6 +49,11 @@
"Name": "UClampMax",
"Controller": "cpu",
"File": "cpu.uclamp.max"
+ },
+ {
+ "Name": "FreezerState",
+ "Controller": "freezer",
+ "File": "cgroup.freeze"
}
],
@@ -74,7 +79,7 @@
"Params":
{
"Controller": "freezer",
- "Path": "frozen"
+ "Path": ""
}
}
]
@@ -87,7 +92,7 @@
"Params":
{
"Controller": "freezer",
- "Path": ""
+ "Path": "../"
}
}
]
@@ -531,6 +536,32 @@
}
}
]
+ },
+ {
+ "Name": "FreezerDisabled",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "0"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "FreezerEnabled",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "FreezerState",
+ "Value": "1"
+ }
+ }
+ ]
}
],
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 4af4589..a638fca 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -44,6 +44,11 @@
#define TASK_PROFILE_DB_FILE "/etc/task_profiles.json"
#define TASK_PROFILE_DB_VENDOR_FILE "/vendor/etc/task_profiles.json"
+void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
+ controller_ = controller;
+ file_name_ = file_name;
+}
+
bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
std::string subgroup;
if (!controller()->GetTaskGroup(tid, &subgroup)) {
@@ -380,15 +385,16 @@
std::string controller_name = attr[i]["Controller"].asString();
std::string file_attr = attr[i]["File"].asString();
- if (attributes_.find(name) == attributes_.end()) {
- auto controller = cg_map.FindController(controller_name);
- if (controller.HasValue()) {
+ auto controller = cg_map.FindController(controller_name);
+ if (controller.HasValue()) {
+ auto iter = attributes_.find(name);
+ if (iter == attributes_.end()) {
attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
} else {
- LOG(WARNING) << "Controller " << controller_name << " is not found";
+ iter->second->Reset(controller, file_attr);
}
} else {
- LOG(WARNING) << "Attribute " << name << " is already defined";
+ LOG(WARNING) << "Controller " << controller_name << " is not found";
}
}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 28bc00c..2983a09 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -33,6 +33,7 @@
const CgroupController* controller() const { return &controller_; }
const std::string& file_name() const { return file_name_; }
+ void Reset(const CgroupController& controller, const std::string& file_name);
bool GetPathForTask(int tid, std::string* path) const;
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 15b0d89..ae45742 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -27,6 +27,8 @@
name: "libprocinfo",
defaults: ["libprocinfo_defaults"],
vendor_available: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
recovery_available: true,
vndk: {
enabled: true,
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/Android.bp b/libunwindstack/Android.bp
index 3c44534..8cc780a 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -124,6 +124,8 @@
name: "libunwindstack",
vendor_available: true,
recovery_available: true,
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -145,6 +147,11 @@
exclude_srcs: ["DexFile.cpp"],
exclude_shared_libs: ["libdexfile_support"],
},
+ native_bridge: {
+ cflags: ["-UDEXFILE_SUPPORT"],
+ exclude_srcs: ["DexFile.cpp"],
+ exclude_shared_libs: ["libdexfile_support"],
+ },
},
apex_available: [
@@ -334,6 +341,37 @@
}
//-------------------------------------------------------------------------
+// Fuzzers
+//-------------------------------------------------------------------------
+cc_defaults {
+ name: "libunwindstack_fuzz_defaults",
+ host_supported: true,
+ defaults: ["libunwindstack_flags"],
+ cflags: [
+ "-Wno-exit-time-destructors",
+ "-g",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "liblzma",
+ "libunwindstack",
+ "libdexfile_support",
+ ],
+}
+
+cc_fuzz {
+ name: "libunwindstack_fuzz_unwinder",
+ defaults: ["libunwindstack_fuzz_defaults"],
+ srcs: [
+ "tests/MemoryFake.cpp",
+ "tests/ElfFake.cpp",
+ "tests/fuzz/UnwinderComponentCreator.cpp",
+ "tests/fuzz/UnwinderFuzz.cpp",
+ ],
+}
+
+//-------------------------------------------------------------------------
// Tools
//-------------------------------------------------------------------------
cc_defaults {
@@ -451,3 +489,4 @@
"tests/GenGnuDebugdata.cpp",
],
}
+
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index c128b9b..c6db209 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -26,7 +26,9 @@
#include <unwindstack/DwarfError.h>
#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
+#include <unwindstack/MachineArm64.h>
#include "DwarfCfa.h"
#include "DwarfEncoding.h"
@@ -204,8 +206,12 @@
bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
uint64_t* cur_pc) {
const auto* cfa = &DwarfCfaInfo::kTable[op];
- if (cfa->name[0] == '\0') {
- log(indent, "Illegal");
+ if (cfa->name[0] == '\0' || (arch_ != ARCH_ARM64 && op == 0x2d)) {
+ if (op == 0x2d) {
+ log(indent, "Illegal (Only valid on aarch64)");
+ } else {
+ log(indent, "Illegal");
+ }
log(indent, "Raw Data: 0x%02x", op);
return true;
}
@@ -514,6 +520,24 @@
return true;
}
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_aarch64_negate_ra_state(dwarf_loc_regs_t* loc_regs) {
+ // Only supported on aarch64.
+ if (arch_ != ARCH_ARM64) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ auto cfa_location = loc_regs->find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
+ if (cfa_location == loc_regs->end()) {
+ (*loc_regs)[Arm64Reg::ARM64_PREG_RA_SIGN_STATE] = {.type = DWARF_LOCATION_PSEUDO_REGISTER,
+ .values = {1}};
+ } else {
+ cfa_location->second.values[0] ^= 1;
+ }
+ return true;
+}
+
const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
{
// 0x00 DW_CFA_nop
@@ -699,7 +723,13 @@
{"", 0, 0, {}, {}}, // 0x2a illegal cfa
{"", 0, 0, {}, {}}, // 0x2b illegal cfa
{"", 0, 0, {}, {}}, // 0x2c illegal cfa
- {"", 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+ {
+ "DW_CFA_AARCH64_negate_ra_state", // 0x2d DW_CFA_AARCH64_negate_ra_state
+ 3,
+ 0,
+ {},
+ {},
+ },
{
"DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
2,
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index 569c17c..d627e15 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -31,6 +31,9 @@
namespace unwindstack {
+// Forward declarations.
+enum ArchEnum : uint8_t;
+
// DWARF Standard home: http://dwarfstd.org/
// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
@@ -72,7 +75,8 @@
typedef typename std::make_signed<AddressType>::type SignedType;
public:
- DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
+ DwarfCfa(DwarfMemory* memory, const DwarfFde* fde, ArchEnum arch)
+ : memory_(memory), fde_(fde), arch_(arch) {}
virtual ~DwarfCfa() = default;
bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
@@ -99,6 +103,7 @@
DwarfErrorData last_error_;
DwarfMemory* memory_;
const DwarfFde* fde_;
+ ArchEnum arch_;
AddressType cur_pc_;
const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
@@ -128,6 +133,7 @@
bool cfa_val_offset_sf(dwarf_loc_regs_t*);
bool cfa_val_expression(dwarf_loc_regs_t*);
bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
+ bool cfa_aarch64_negate_ra_state(dwarf_loc_regs_t*);
using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
constexpr static process_func kCallbackTable[64] = {
@@ -221,8 +227,9 @@
nullptr,
// 0x2c illegal cfa
nullptr,
- // 0x2d DW_CFA_GNU_window_save (Treat this as illegal)
- nullptr,
+ // 0x2d DW_CFA_AARCH64_negate_ra_state (aarch64 only)
+ // DW_CFA_GNU_window_save on other architectures.
+ &DwarfCfa::cfa_aarch64_negate_ra_state,
// 0x2e DW_CFA_GNU_args_size
&DwarfCfa::cfa_nop,
// 0x2f DW_CFA_GNU_negative_offset_extended
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 18bd490..bf86e6e 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -21,6 +21,7 @@
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfSection.h>
#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@@ -49,7 +50,7 @@
// Now get the location information for this pc.
dwarf_loc_regs_t loc_regs;
- if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+ if (!GetCfaLocationInfo(pc, fde, &loc_regs, regs->Arch())) {
return false;
}
loc_regs.cie = fde->cie;
@@ -464,6 +465,9 @@
eval_info->return_address_undefined = true;
}
break;
+ case DWARF_LOCATION_PSEUDO_REGISTER:
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
default:
break;
}
@@ -491,6 +495,10 @@
// Always set the dex pc to zero when evaluating.
cur_regs->set_dex_pc(0);
+ // Reset necessary pseudo registers before evaluation.
+ // This is needed for ARM64, for example.
+ regs->ResetPseudoRegisters();
+
EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
.cie = cie,
.regular_memory = regular_memory,
@@ -527,13 +535,19 @@
AddressType* reg_ptr;
if (reg >= cur_regs->total_regs()) {
- // 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 (entry.second.type != DWARF_LOCATION_PSEUDO_REGISTER) {
+ // Skip this unknown register.
+ continue;
+ }
+ 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;
+ }
}
}
@@ -554,8 +568,8 @@
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
- dwarf_loc_regs_t* loc_regs) {
- DwarfCfa<AddressType> cfa(&memory_, fde);
+ dwarf_loc_regs_t* loc_regs, ArchEnum arch) {
+ DwarfCfa<AddressType> cfa(&memory_, fde, arch);
// Look for the cached copy of the cie data.
auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
@@ -576,8 +590,9 @@
}
template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
- DwarfCfa<AddressType> cfa(&memory_, fde);
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde,
+ ArchEnum arch) {
+ DwarfCfa<AddressType> cfa(&memory_, fde, arch);
// Always print the cie information.
const DwarfCie* cie = fde->cie;
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index 5b7431a..b496187 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -30,7 +30,10 @@
namespace unwindstack {
RegsArm64::RegsArm64()
- : RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
+ : RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {
+ ResetPseudoRegisters();
+ pac_mask_ = 0;
+}
ArchEnum RegsArm64::Arch() {
return ARCH_ARM64;
@@ -45,6 +48,23 @@
}
void RegsArm64::set_pc(uint64_t pc) {
+ // If the target is aarch64 then the return address may have been
+ // signed using the Armv8.3-A Pointer Authentication extension. The
+ // original return address can be restored by stripping out the
+ // authentication code using a mask or xpaclri. xpaclri is a NOP on
+ // pre-Armv8.3-A architectures.
+ if ((0 != pc) && IsRASigned()) {
+ if (pac_mask_) {
+ pc &= ~pac_mask_;
+#if defined(__aarch64__)
+ } else {
+ register uint64_t x30 __asm("x30") = pc;
+ // This is XPACLRI.
+ asm("hint 0x7" : "+r"(x30));
+ pc = x30;
+#endif
+ }
+ }
regs_[ARM64_REG_PC] = pc;
}
@@ -144,6 +164,37 @@
return true;
}
+void RegsArm64::ResetPseudoRegisters(void) {
+ // DWARF for AArch64 says RA_SIGN_STATE should be initialized to 0.
+ this->SetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, 0);
+}
+
+bool RegsArm64::SetPseudoRegister(uint16_t id, uint64_t value) {
+ if ((id >= Arm64Reg::ARM64_PREG_FIRST) && (id < Arm64Reg::ARM64_PREG_LAST)) {
+ pseudo_regs_[id - Arm64Reg::ARM64_PREG_FIRST] = value;
+ return true;
+ }
+ return false;
+}
+
+bool RegsArm64::GetPseudoRegister(uint16_t id, uint64_t* value) {
+ if ((id >= Arm64Reg::ARM64_PREG_FIRST) && (id < Arm64Reg::ARM64_PREG_LAST)) {
+ *value = pseudo_regs_[id - Arm64Reg::ARM64_PREG_FIRST];
+ return true;
+ }
+ return false;
+}
+
+bool RegsArm64::IsRASigned() {
+ uint64_t value;
+ auto result = this->GetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, &value);
+ return (result && (value != 0));
+}
+
+void RegsArm64::SetPACMask(uint64_t mask) {
+ pac_mask_ = mask;
+}
+
Regs* RegsArm64::Clone() {
return new RegsArm64(*this);
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 2d867cd..b904632 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -75,7 +75,7 @@
frame->rel_pc = dex_pc - info->start;
} else {
frame->rel_pc = dex_pc;
- return;
+ warnings_ |= WARNING_DEX_PC_NOT_IN_MAP;
}
if (!resolve_names_) {
@@ -142,6 +142,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;
diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
index 3d50ccf..bf45bc7 100644
--- a/libunwindstack/include/unwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -33,6 +33,7 @@
DWARF_LOCATION_REGISTER,
DWARF_LOCATION_EXPRESSION,
DWARF_LOCATION_VAL_EXPRESSION,
+ DWARF_LOCATION_PSEUDO_REGISTER,
};
struct DwarfLocation {
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index c244749..af823da 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -31,6 +31,7 @@
namespace unwindstack {
// Forward declarations.
+enum ArchEnum : uint8_t;
class Memory;
class Regs;
template <typename AddressType>
@@ -90,13 +91,14 @@
virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
- virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
+ virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde, ArchEnum arch) = 0;
virtual void GetFdes(std::vector<const DwarfFde*>* fdes) = 0;
virtual const DwarfFde* GetFdeFromPc(uint64_t pc) = 0;
- virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
+ virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs,
+ ArchEnum arch) = 0;
virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
@@ -140,9 +142,10 @@
bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
Regs* regs, bool* finished) override;
- bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
+ bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs,
+ ArchEnum arch) override;
- bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
+ bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde, ArchEnum arch) override;
protected:
bool GetNextCieOrFde(const DwarfFde** fde_entry);
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/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
index e953335..358e3d9 100644
--- a/libunwindstack/include/unwindstack/MachineArm64.h
+++ b/libunwindstack/include/unwindstack/MachineArm64.h
@@ -60,6 +60,13 @@
ARM64_REG_SP = ARM64_REG_R31,
ARM64_REG_LR = ARM64_REG_R30,
+
+ // Pseudo registers. These are not machine registers.
+
+ // AARCH64 Return address signed state pseudo-register
+ ARM64_PREG_RA_SIGN_STATE = 34,
+ ARM64_PREG_FIRST = ARM64_PREG_RA_SIGN_STATE,
+ ARM64_PREG_LAST,
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index a367e6c..5f42565 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -64,6 +64,10 @@
uint64_t dex_pc() { return dex_pc_; }
void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
+ virtual void ResetPseudoRegisters() {}
+ virtual bool SetPseudoRegister(uint16_t, uint64_t) { return false; }
+ virtual bool GetPseudoRegister(uint16_t, uint64_t*) { return false; }
+
virtual bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) = 0;
virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index 2b3ddeb..bf7ab15 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -22,6 +22,7 @@
#include <functional>
#include <unwindstack/Elf.h>
+#include <unwindstack/MachineArm64.h>
#include <unwindstack/Regs.h>
namespace unwindstack {
@@ -48,11 +49,25 @@
void set_pc(uint64_t pc) override;
void set_sp(uint64_t sp) override;
+ void ResetPseudoRegisters() override;
+
+ bool SetPseudoRegister(uint16_t id, uint64_t value) override;
+
+ bool GetPseudoRegister(uint16_t id, uint64_t* value) override;
+
+ bool IsRASigned();
+
+ void SetPACMask(uint64_t mask);
+
Regs* Clone() override final;
static Regs* Read(void* data);
static Regs* CreateFromUcontext(void* ucontext);
+
+ protected:
+ uint64_t pseudo_regs_[Arm64Reg::ARM64_PREG_LAST - Arm64Reg::ARM64_PREG_FIRST];
+ uint64_t pac_mask_;
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 4d49f23..a974b63 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -113,6 +113,7 @@
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
@@ -141,6 +142,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/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index def4088..2b5a8dc 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -26,6 +26,7 @@
#include <unwindstack/DwarfLocation.h>
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
#include "DwarfCfa.h"
@@ -57,7 +58,7 @@
fde_.pc_end = 0x2000;
fde_.pc_end = 0x10000;
fde_.cie = &cie_;
- cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_, ARCH_UNKNOWN));
}
MemoryFake memory_;
@@ -72,8 +73,8 @@
TYPED_TEST_P(DwarfCfaLogTest, cfa_illegal) {
for (uint8_t i = 0x17; i < 0x3f; i++) {
- if (i == 0x2e || i == 0x2f) {
- // Skip gnu extension ops.
+ if (i == 0x2d || i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops and aarch64 specialized op.
continue;
}
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
@@ -763,6 +764,26 @@
ASSERT_EQ("", GetFakeLogBuf());
}
+TYPED_TEST_P(DwarfCfaLogTest, cfa_aarch64_negate_ra_state) {
+ // Verify that if the cfa op is handled properly depending on aarch.
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2d});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
+ std::string expected = "4 unwind Illegal (Only valid on aarch64)\n";
+ expected += "4 unwind Raw Data: 0x2d\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->cfa_.reset(new DwarfCfa<TypeParam>(this->dmem_.get(), &this->fde_, ARCH_ARM64));
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
+ expected = "4 unwind DW_CFA_AARCH64_negate_ra_state\n";
+ expected += "4 unwind Raw Data: 0x2d\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
REGISTER_TYPED_TEST_SUITE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
@@ -771,7 +792,8 @@
cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
- cfa_gnu_negative_offset_extended, cfa_register_override);
+ cfa_gnu_negative_offset_extended, cfa_register_override,
+ cfa_aarch64_negate_ra_state);
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfCfaLogTest, DwarfCfaLogTestTypes);
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 9c6ab05..ea7e708 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -25,7 +25,9 @@
#include <unwindstack/DwarfLocation.h>
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
+#include <unwindstack/MachineArm64.h>
#include "DwarfCfa.h"
@@ -55,7 +57,7 @@
fde_.pc_start = 0x2000;
fde_.cie = &cie_;
- cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_, ARCH_UNKNOWN));
}
MemoryFake memory_;
@@ -70,8 +72,8 @@
TYPED_TEST_P(DwarfCfaTest, cfa_illegal) {
for (uint8_t i = 0x17; i < 0x3f; i++) {
- if (i == 0x2e || i == 0x2f) {
- // Skip gnu extension ops.
+ if (i == 0x2d || i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops and aarch64 specialized op.
continue;
}
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
@@ -952,6 +954,57 @@
ASSERT_EQ("", GetFakeLogBuf());
}
+TYPED_TEST_P(DwarfCfaTest, cfa_aarch64_negate_ra_state) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2d});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->LastErrorCode());
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->cfa_.reset(new DwarfCfa<TypeParam>(this->dmem_.get(), &this->fde_, ARCH_ARM64));
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ auto location = loc_regs.find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_PSEUDO_REGISTER, location->second.type);
+ ASSERT_EQ(1U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Verify that the value is set to 0 after another evaluation.
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ location = loc_regs.find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_PSEUDO_REGISTER, location->second.type);
+ ASSERT_EQ(0U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Verify that the value is set to 1 again after a third op.
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ location = loc_regs.find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_PSEUDO_REGISTER, location->second.type);
+ ASSERT_EQ(1U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
REGISTER_TYPED_TEST_SUITE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
@@ -960,7 +1013,7 @@
cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
cfa_val_offset, cfa_val_offset_sf, cfa_val_expression,
cfa_gnu_args_size, cfa_gnu_negative_offset_extended,
- cfa_register_override);
+ cfa_register_override, cfa_aarch64_negate_ra_state);
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfCfaTest, DwarfCfaTestTypes);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index cac59b7..a08a8d0 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -20,6 +20,7 @@
#include <unwindstack/DwarfError.h>
#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Elf.h>
#include "DwarfEncoding.h"
@@ -491,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;
@@ -505,7 +540,7 @@
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
dwarf_loc_regs_t loc_regs;
- ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+ ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs, ARCH_UNKNOWN));
ASSERT_EQ(2U, loc_regs.size());
auto entry = loc_regs.find(2);
@@ -535,7 +570,7 @@
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
dwarf_loc_regs_t loc_regs;
- ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
+ ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs, ARCH_UNKNOWN));
ASSERT_EQ(2U, loc_regs.size());
auto entry = loc_regs.find(6);
@@ -560,7 +595,7 @@
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
- ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde));
+ ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde, ARCH_UNKNOWN));
ASSERT_EQ(
"4 unwind DW_CFA_nop\n"
@@ -580,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/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 953dc75..febd6d3 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -20,8 +20,10 @@
#include <gtest/gtest.h>
#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Elf.h>
#include "MemoryFake.h"
+#include "RegsFake.h"
namespace unwindstack {
@@ -35,13 +37,14 @@
MOCK_METHOD(bool, Eval, (const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*),
(override));
- MOCK_METHOD(bool, Log, (uint8_t, uint64_t, const DwarfFde*), (override));
+ MOCK_METHOD(bool, Log, (uint8_t, uint64_t, const DwarfFde*, ArchEnum arch), (override));
MOCK_METHOD(void, GetFdes, (std::vector<const DwarfFde*>*), (override));
MOCK_METHOD(const DwarfFde*, GetFdeFromPc, (uint64_t), (override));
- MOCK_METHOD(bool, GetCfaLocationInfo, (uint64_t, const DwarfFde*, dwarf_loc_regs_t*), (override));
+ MOCK_METHOD(bool, GetCfaLocationInfo,
+ (uint64_t, const DwarfFde*, dwarf_loc_regs_t*, ArchEnum arch), (override));
MOCK_METHOD(uint64_t, GetCieOffsetFromFde32, (uint32_t), (override));
@@ -56,8 +59,11 @@
MemoryFake memory_;
std::unique_ptr<MockDwarfSection> section_;
+ static RegsFake regs_;
};
+RegsFake DwarfSectionTest::regs_(10);
+
TEST_F(DwarfSectionTest, Step_fail_fde) {
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(nullptr));
@@ -73,7 +79,7 @@
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
bool finished;
- ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
+ ASSERT_FALSE(section_->Step(0x1000, ®s_, nullptr, &finished));
}
TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
@@ -83,11 +89,11 @@
fde.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
- EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_, ::testing::_))
.WillOnce(::testing::Return(false));
bool finished;
- ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
+ ASSERT_FALSE(section_->Step(0x1000, ®s_, nullptr, &finished));
}
TEST_F(DwarfSectionTest, Step_pass) {
@@ -97,19 +103,19 @@
fde.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
- EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_, ::testing::_))
.WillOnce(::testing::Return(true));
MemoryFake process;
- EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, ®s_, ::testing::_))
.WillOnce(::testing::Return(true));
bool finished;
- ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
}
static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
- dwarf_loc_regs_t* loc_regs) {
+ dwarf_loc_regs_t* loc_regs, ArchEnum) {
loc_regs->pc_start = fde->pc_start;
loc_regs->pc_end = fde->pc_end;
return true;
@@ -123,17 +129,17 @@
fde.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
- EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
MemoryFake process;
- EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, ®s_, ::testing::_))
.WillRepeatedly(::testing::Return(true));
bool finished;
- ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
- ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
- ASSERT_TRUE(section_->Step(0x1500, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1500, ®s_, &process, &finished));
}
TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
@@ -143,26 +149,26 @@
fde0.pc_end = 0x2000;
fde0.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde0));
- EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
MemoryFake process;
- EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, ®s_, ::testing::_))
.WillRepeatedly(::testing::Return(true));
bool finished;
- ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
DwarfFde fde1{};
fde1.pc_start = 0x500;
fde1.pc_end = 0x800;
fde1.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x600)).WillOnce(::testing::Return(&fde1));
- EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+ EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
- ASSERT_TRUE(section_->Step(0x600, nullptr, &process, &finished));
- ASSERT_TRUE(section_->Step(0x700, nullptr, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x600, ®s_, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x700, ®s_, &process, &finished));
}
} // namespace unwindstack
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/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index e4fc6f0..acf72de 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -247,6 +247,14 @@
EXPECT_EQ(0xc200000000U, mips64.pc());
}
+TEST_F(RegsTest, arm64_strip_pac_mask) {
+ RegsArm64 arm64;
+ arm64.SetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, 1);
+ arm64.SetPACMask(0x007fff8000000000ULL);
+ arm64.set_pc(0x0020007214bb3a04ULL);
+ EXPECT_EQ(0x0000007214bb3a04ULL, arm64.pc());
+}
+
TEST_F(RegsTest, machine_type) {
RegsArm arm_regs;
EXPECT_EQ(ARCH_ARM, arm_regs.Arch());
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index dd33aa9..d10af2f 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -168,6 +168,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 +234,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 +295,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 +326,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 +357,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 +389,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 +427,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 +461,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 +505,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 +568,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 +617,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 +638,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 +654,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 +692,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 +759,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 +810,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 +859,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 +883,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 +943,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 +1003,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 +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());
@@ -1072,6 +1093,7 @@
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());
@@ -1119,6 +1141,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 +1201,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 +1232,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 +1263,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 +1294,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 +1325,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());
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
index 6a3e91a..eb2b01d 100644
--- a/libunwindstack/tests/VerifyBionicTerminationTest.cpp
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -55,7 +55,7 @@
return DWARF_LOCATION_INVALID;
}
dwarf_loc_regs_t regs;
- if (!section->GetCfaLocationInfo(rel_pc, fde, ®s)) {
+ if (!section->GetCfaLocationInfo(rel_pc, fde, ®s, ARCH_UNKNOWN)) {
return DWARF_LOCATION_INVALID;
}
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
new file mode 100644
index 0000000..0415ef6
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
@@ -0,0 +1,376 @@
+/*
+ * 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 "UnwinderComponentCreator.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+std::unique_ptr<Regs> GetRegisters(ArchEnum arch) {
+ switch (arch) {
+ case unwindstack::ARCH_ARM: {
+ std::unique_ptr<unwindstack::RegsArm> regs = std::make_unique<unwindstack::RegsArm>();
+ return regs;
+ }
+ case unwindstack::ARCH_ARM64: {
+ std::unique_ptr<unwindstack::RegsArm64> regs = std::make_unique<unwindstack::RegsArm64>();
+ return regs;
+ }
+ case unwindstack::ARCH_X86: {
+ std::unique_ptr<unwindstack::RegsX86> regs = std::make_unique<unwindstack::RegsX86>();
+ return regs;
+ }
+ case unwindstack::ARCH_X86_64: {
+ std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>();
+ return regs;
+ }
+ case unwindstack::ARCH_MIPS: {
+ std::unique_ptr<unwindstack::RegsMips> regs = std::make_unique<unwindstack::RegsMips>();
+ return regs;
+ }
+ case unwindstack::ARCH_MIPS64: {
+ std::unique_ptr<unwindstack::RegsMips64> regs = std::make_unique<unwindstack::RegsMips64>();
+ return regs;
+ }
+ case unwindstack::ARCH_UNKNOWN:
+ default: {
+ std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>();
+ return regs;
+ }
+ }
+}
+
+ArchEnum GetArch(FuzzedDataProvider* data_provider) {
+ uint8_t arch = data_provider->ConsumeIntegralInRange<uint8_t>(1, kArchCount);
+ return static_cast<ArchEnum>(arch);
+}
+
+void ElfAddMapInfo(Maps* maps, 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));
+ if (elf != nullptr) {
+ const auto& map_info = *--maps->end();
+ map_info->elf.reset(elf);
+ }
+}
+
+void ElfPushFakeFunctionData(FuzzedDataProvider* data_provider, ElfInterfaceFake* elf) {
+ uint8_t func_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxFuncCount);
+ for (uint8_t i = 0; i < func_count; i++) {
+ std::string func_name = data_provider->ConsumeRandomLengthString(kMaxFuncNameLen);
+ bool global = data_provider->ConsumeBool();
+ if (global) {
+ elf->FakeSetGlobalVariable(func_name, data_provider->ConsumeIntegral<uint64_t>());
+ } else {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData(func_name, i));
+ }
+ }
+}
+void ElfPushFakeStepData(FuzzedDataProvider* data_provider) {
+ uint8_t step_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxStepCount);
+ for (uint8_t i = 0; i < step_count; i++) {
+ uint64_t pc = data_provider->ConsumeIntegral<uint64_t>();
+ uint64_t sp = data_provider->ConsumeIntegral<uint64_t>();
+ bool finished = i + 1 == step_count;
+ ElfInterfaceFake::FakePushStepData(StepData(pc, sp, finished));
+ }
+}
+
+ElfFake* PopulateElfFake(FuzzedDataProvider* data_provider) {
+ // This will be passed to a smart pointer in ElfAddMapInfo.
+ ElfFake* elf = new ElfFake(new MemoryFake);
+
+ // This will be handled by a smart pointer within Elf.
+ ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
+ std::string build_id = data_provider->ConsumeRandomLengthString(kMaxBuildIdLen);
+ interface_fake->FakeSetBuildID(build_id);
+ std::string so_name = data_provider->ConsumeRandomLengthString(kMaxSoNameLen);
+ interface_fake->FakeSetSoname(so_name.c_str());
+
+ elf->FakeSetArch(GetArch(data_provider));
+ elf->FakeSetLoadBias(data_provider->ConsumeIntegral<uint64_t>());
+
+ ElfPushFakeFunctionData(data_provider, interface_fake);
+ ElfPushFakeStepData(data_provider);
+
+ elf->FakeSetInterface(interface_fake);
+ ElfInterfaceFake::FakeClear();
+ 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 = 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;
+
+ bool exec = data_provider->ConsumeBool();
+ if (exec) {
+ flags |= PROT_EXEC;
+ }
+
+ bool shouldAddElf = data_provider->ConsumeBool();
+ if (shouldAddElf) {
+ ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str(),
+ PopulateElfFake(data_provider));
+ } else {
+ ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str());
+ }
+ }
+ maps->Sort();
+ return maps;
+}
+
+// This code (until PutElfFilesInMemory) is pretty much directly copied from JitDebugTest.cpp
+// There's a few minor modifications, most notably, all methods accept a MemoryFake pointer, and
+// PutElfInMemory inserts JIT data when called.
+void WriteDescriptor32(MemoryFake* memory, uint64_t addr, uint32_t entry) {
+ // Format of the 32 bit JITDescriptor structure:
+ // uint32_t version
+ memory->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory->SetData32(addr + 4, 0);
+ // uint32_t relevant_entry
+ memory->SetData32(addr + 8, 0);
+ // uint32_t first_entry
+ memory->SetData32(addr + 12, entry);
+}
+
+void WriteDescriptor64(MemoryFake* memory, uint64_t addr, uint64_t entry) {
+ // Format of the 64 bit JITDescriptor structure:
+ // uint32_t version
+ memory->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory->SetData32(addr + 4, 0);
+ // uint64_t relevant_entry
+ memory->SetData64(addr + 8, 0);
+ // uint64_t first_entry
+ memory->SetData64(addr + 16, entry);
+}
+
+void WriteEntry32Pack(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next,
+ uint32_t elf_addr, uint64_t elf_size) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory->SetData32(addr, next);
+ // uint32_t prev
+ memory->SetData32(addr + 4, prev);
+ // uint32_t symfile_addr
+ memory->SetData32(addr + 8, elf_addr);
+ // uint64_t symfile_size
+ memory->SetData64(addr + 12, elf_size);
+}
+
+void WriteEntry32Pad(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next,
+ uint32_t elf_addr, uint64_t elf_size) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory->SetData32(addr, next);
+ // uint32_t prev
+ memory->SetData32(addr + 4, prev);
+ // uint32_t symfile_addr
+ memory->SetData32(addr + 8, elf_addr);
+ // uint32_t pad
+ memory->SetData32(addr + 12, 0);
+ // uint64_t symfile_size
+ memory->SetData64(addr + 16, elf_size);
+}
+
+void WriteEntry64(MemoryFake* memory, uint64_t addr, uint64_t prev, uint64_t next,
+ uint64_t elf_addr, uint64_t elf_size) {
+ // Format of the 64 bit JITCodeEntry structure:
+ // uint64_t next
+ memory->SetData64(addr, next);
+ // uint64_t prev
+ memory->SetData64(addr + 8, prev);
+ // uint64_t symfile_addr
+ memory->SetData64(addr + 16, elf_addr);
+ // uint64_t symfile_size
+ memory->SetData64(addr + 24, elf_size);
+}
+
+template <typename EhdrType, typename ShdrType>
+void PutElfInMemory(MemoryFake* memory, uint64_t offset, uint8_t class_type, uint8_t machine_type,
+ uint32_t pc, uint32_t size) {
+ EhdrType ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ uint64_t sh_offset = sizeof(ehdr);
+ memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+ ehdr.e_ident[EI_CLASS] = class_type;
+ ehdr.e_machine = machine_type;
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(ShdrType);
+ ehdr.e_shnum = 3;
+ memory->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+ ShdrType shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+ memory->SetMemory(offset + 0x500, ".debug_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+ // Now add a single cie/fde.
+ uint64_t dwarf_offset = offset + 0x600;
+ if (class_type == ELFCLASS32) {
+ // CIE 32 information.
+ memory->SetData32(dwarf_offset, 0xfc);
+ memory->SetData32(dwarf_offset + 0x4, 0xffffffff);
+ memory->SetData8(dwarf_offset + 0x8, 1);
+ memory->SetData8(dwarf_offset + 0x9, '\0');
+ memory->SetData8(dwarf_offset + 0xa, 0x4);
+ memory->SetData8(dwarf_offset + 0xb, 0x4);
+ memory->SetData8(dwarf_offset + 0xc, 0x1);
+
+ // FDE 32 information.
+ memory->SetData32(dwarf_offset + 0x100, 0xfc);
+ memory->SetData32(dwarf_offset + 0x104, 0);
+ memory->SetData32(dwarf_offset + 0x108, pc);
+ memory->SetData32(dwarf_offset + 0x10c, size);
+ } else {
+ // CIE 64 information.
+ memory->SetData32(dwarf_offset, 0xffffffff);
+ memory->SetData64(dwarf_offset + 4, 0xf4);
+ memory->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL);
+ memory->SetData8(dwarf_offset + 0x14, 1);
+ memory->SetData8(dwarf_offset + 0x15, '\0');
+ memory->SetData8(dwarf_offset + 0x16, 0x4);
+ memory->SetData8(dwarf_offset + 0x17, 0x4);
+ memory->SetData8(dwarf_offset + 0x18, 0x1);
+
+ // FDE 64 information.
+ memory->SetData32(dwarf_offset + 0x100, 0xffffffff);
+ memory->SetData64(dwarf_offset + 0x104, 0xf4);
+ memory->SetData64(dwarf_offset + 0x10c, 0);
+ memory->SetData64(dwarf_offset + 0x114, pc);
+ memory->SetData64(dwarf_offset + 0x11c, size);
+ }
+}
+
+void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider) {
+ uint8_t elf_file_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxJitElfFiles);
+ int entry_offset = 0;
+ int prev_jit_addr = 0;
+ for (uint8_t i = 0; i < elf_file_count; i++) {
+ uint64_t offset = data_provider->ConsumeIntegral<uint64_t>();
+ // Technically the max valid value is ELFCLASSNUM - 1 (2), but
+ // we want to test values outside of that range.
+ uint8_t class_type = data_provider->ConsumeIntegral<uint8_t>();
+ // Same here, EM_NUM is 253, max valid machine type is 252
+ uint8_t machine_type = data_provider->ConsumeIntegral<uint8_t>();
+ uint32_t pc = data_provider->ConsumeIntegral<uint32_t>();
+ uint32_t size = data_provider->ConsumeIntegral<uint32_t>();
+ bool sixty_four_bit = data_provider->ConsumeBool();
+ bool write_jit = data_provider->ConsumeBool();
+ if (sixty_four_bit) {
+ PutElfInMemory<Elf64_Ehdr, Elf64_Shdr>(memory, offset, class_type, machine_type, pc, size);
+ } else {
+ PutElfInMemory<Elf32_Ehdr, Elf32_Shdr>(memory, offset, class_type, machine_type, pc, size);
+ }
+ if (write_jit) {
+ bool use_pad = data_provider->ConsumeBool();
+ // It is possible this will overwrite part of the ELF.
+ // This provides an interesting test of how malformed ELF
+ // data is handled.
+ uint64_t cur_descriptor_addr = 0x11800 + entry_offset;
+ uint64_t cur_jit_addr = 0x200000 + entry_offset;
+ uint64_t next_jit_addr = cur_jit_addr + size;
+ if (sixty_four_bit) {
+ WriteDescriptor64(memory, 0x11800, cur_jit_addr);
+ WriteEntry64(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+ } else {
+ // Loop back. Again, this may corrupt data,
+ // but that will allow for testing edge cases with
+ // malformed JIT data.
+ if (cur_jit_addr > UINT32_MAX) {
+ entry_offset = 0;
+ cur_jit_addr = 0x200000;
+ cur_descriptor_addr = 0x11800;
+ next_jit_addr = cur_jit_addr + size;
+ }
+ WriteDescriptor32(memory, cur_descriptor_addr, cur_jit_addr);
+ if (use_pad) {
+ WriteEntry32Pad(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+ } else {
+ WriteEntry32Pack(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size);
+ }
+ }
+ entry_offset += size;
+ prev_jit_addr = cur_jit_addr;
+ }
+ }
+}
+
+std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len,
+ uint max_strings) {
+ uint str_count = data_provider->ConsumeIntegralInRange<uint>(0, max_strings);
+ std::vector<std::string> strings;
+ for (uint i = 0; i < str_count; i++) {
+ strings.push_back(data_provider->ConsumeRandomLengthString(max_str_len));
+ }
+ return strings;
+}
+
+std::unique_ptr<DexFiles> GetDexFiles(FuzzedDataProvider* data_provider,
+ std::shared_ptr<Memory> memory, uint max_library_length,
+ uint max_libraries) {
+ std::vector<std::string> search_libs =
+ GetStringList(data_provider, max_library_length, max_libraries);
+ if (search_libs.size() <= 0) {
+ return std::make_unique<DexFiles>(memory);
+ }
+
+ return std::make_unique<DexFiles>(memory, search_libs);
+}
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.h b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
new file mode 100644
index 0000000..09b3379
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
+#define _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
+
+#include <elf.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsMips.h>
+#include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+
+#include "../ElfFake.h"
+#include "../MemoryFake.h"
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using unwindstack::ArchEnum;
+using unwindstack::DexFiles;
+using unwindstack::Elf;
+using unwindstack::ElfFake;
+using unwindstack::ElfInterfaceFake;
+using unwindstack::FunctionData;
+using unwindstack::Maps;
+using unwindstack::Memory;
+using unwindstack::MemoryFake;
+using unwindstack::Regs;
+using unwindstack::StepData;
+
+static constexpr uint8_t kArchCount = 6;
+
+static constexpr uint8_t kMaxSoNameLen = 150;
+
+static constexpr uint8_t kMaxFuncNameLen = 50;
+static constexpr uint8_t kMaxFuncCount = 100;
+
+static constexpr uint8_t kMaxJitElfFiles = 20;
+static constexpr uint8_t kJitElfPadding = 32;
+
+static constexpr uint8_t kMaxStepCount = 100;
+static constexpr uint8_t kMaxMapEntryCount = 50;
+static constexpr uint8_t kMaxBuildIdLen = 100;
+static constexpr uint8_t kMaxMapInfoNameLen = 150;
+
+std::unique_ptr<unwindstack::Regs> GetRegisters(unwindstack::ArchEnum arch);
+std::unique_ptr<unwindstack::Maps> GetMaps(FuzzedDataProvider* data_provider);
+std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len,
+ uint max_strings);
+unwindstack::ArchEnum GetArch(FuzzedDataProvider* data_provider);
+
+void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name,
+ Elf* elf = nullptr);
+void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider);
+
+std::unique_ptr<unwindstack::DexFiles> GetDexFiles(FuzzedDataProvider* data_provider,
+ std::shared_ptr<unwindstack::Memory> memory,
+ uint max_libraries, uint max_library_length);
+#endif // _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
diff --git a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
new file mode 100644
index 0000000..2f4986a
--- /dev/null
+++ b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 <iostream>
+#include <vector>
+
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Unwinder.h>
+
+#include "../MemoryFake.h"
+#include "UnwinderComponentCreator.h"
+#include "fuzzer/FuzzedDataProvider.h"
+
+namespace unwindstack {
+
+static constexpr int kMaxUnwindStringLen = 50;
+static constexpr int kMaxUnwindStrings = 50;
+
+void PerformUnwind(FuzzedDataProvider* data_provider, Unwinder* unwinder) {
+ // 0 = don't set any values
+ // 1 = set initial_map_names_to_skip
+ // 2 = set map_suffixes_to_ignore
+ // 3 = set both
+ uint8_t set_values = data_provider->ConsumeIntegral<uint8_t>() % 4;
+ if (set_values == 0) {
+ unwinder->Unwind();
+ } else if (set_values == 1) {
+ // Only setting initial_map_names_to_skip
+ std::vector<std::string> skip_names =
+ GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+ unwinder->Unwind(&skip_names, nullptr);
+ } else if (set_values == 2) {
+ // Only setting map_suffixes_to_ignore
+ std::vector<std::string> ignore_suffixes =
+ GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+ unwinder->Unwind(nullptr, &ignore_suffixes);
+ } else if (set_values == 3) {
+ // Setting both values
+ std::vector<std::string> skip_names =
+ GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+ std::vector<std::string> ignore_suffixes =
+ GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings);
+
+ unwinder->Unwind(&skip_names, &ignore_suffixes);
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider data_provider(data, size);
+
+ // We need to construct an unwinder.
+ // Generate the Maps:
+ std::unique_ptr<Maps> maps = GetMaps(&data_provider);
+
+ // Generate the Regs:
+ uint8_t arch_val = data_provider.ConsumeIntegralInRange<uint8_t>(1, kArchCount);
+ ArchEnum arch = static_cast<ArchEnum>(arch_val);
+ std::unique_ptr<Regs> regs = GetRegisters(arch);
+
+ // Generate memory:
+ std::shared_ptr<Memory> memory = std::make_shared<MemoryFake>();
+ PutElfFilesInMemory(reinterpret_cast<MemoryFake*>(memory.get()), &data_provider);
+
+ size_t max_frames = data_provider.ConsumeIntegralInRange<size_t>(0, 5000);
+
+ std::unique_ptr<JitDebug> jit_debug_ptr = std::make_unique<JitDebug>(memory);
+
+ // Create instance
+ Unwinder unwinder(max_frames, maps.get(), regs.get(), memory);
+ unwinder.SetJitDebug(jit_debug_ptr.get(), arch);
+ unwinder.SetResolveNames(data_provider.ConsumeBool());
+ // Call unwind
+ PerformUnwind(&data_provider, &unwinder);
+
+ // Run some additional logic that changes after unwind
+ uint64_t pc = data_provider.ConsumeIntegral<uint64_t>();
+ unwinder.BuildFrameFromPcOnly(pc);
+ unwinder.ConsumeFrames();
+ return 0;
+}
+} // namespace unwindstack
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 7a6d8ba..a5002f2 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -96,7 +96,7 @@
printf(" <%s>", name.c_str());
}
printf("\n");
- if (!section->Log(2, UINT64_MAX, fde)) {
+ if (!section->Log(2, UINT64_MAX, fde, elf->arch())) {
printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
}
}
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 0cbcac5..68e0273 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -64,7 +64,8 @@
}
}
-void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uint8_t class_type) {
+void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uint8_t class_type,
+ ArchEnum arch) {
const DwarfFde* fde = section->GetFdeFromPc(pc);
if (fde == nullptr) {
printf(" No fde found.\n");
@@ -72,7 +73,7 @@
}
dwarf_loc_regs_t regs;
- if (!section->GetCfaLocationInfo(pc, fde, ®s)) {
+ if (!section->GetCfaLocationInfo(pc, fde, ®s, arch)) {
printf(" Cannot get location information.\n");
return;
}
@@ -128,6 +129,11 @@
break;
}
+ case DWARF_LOCATION_PSEUDO_REGISTER: {
+ printf("%" PRId64 " (pseudo)\n", loc->values[0]);
+ break;
+ }
+
case DWARF_LOCATION_UNDEFINED:
printf("undefine\n");
break;
@@ -199,7 +205,7 @@
DwarfSection* section = interface->eh_frame();
if (section != nullptr) {
printf("\neh_frame:\n");
- PrintRegInformation(section, elf.memory(), pc, elf.class_type());
+ PrintRegInformation(section, elf.memory(), pc, elf.class_type(), elf.arch());
} else {
printf("\nno eh_frame information\n");
}
@@ -207,7 +213,7 @@
section = interface->debug_frame();
if (section != nullptr) {
printf("\ndebug_frame:\n");
- PrintRegInformation(section, elf.memory(), pc, elf.class_type());
+ PrintRegInformation(section, elf.memory(), pc, elf.class_type(), elf.arch());
printf("\n");
} else {
printf("\nno debug_frame information\n");
@@ -219,7 +225,8 @@
section = gnu_debugdata_interface->eh_frame();
if (section != nullptr) {
printf("\ngnu_debugdata (eh_frame):\n");
- PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+ PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type(),
+ elf.arch());
printf("\n");
} else {
printf("\nno gnu_debugdata (eh_frame)\n");
@@ -228,7 +235,8 @@
section = gnu_debugdata_interface->debug_frame();
if (section != nullptr) {
printf("\ngnu_debugdata (debug_frame):\n");
- PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
+ PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type(),
+ elf.arch());
printf("\n");
} else {
printf("\nno gnu_debugdata (debug_frame)\n");
diff --git a/libutils/Android.bp b/libutils/Android.bp
index ea39d34..9c012a9 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -78,6 +78,9 @@
"libcutils",
"liblog",
],
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
target: {
android: {
@@ -169,6 +172,8 @@
cc_library {
name: "libutilscallstack",
defaults: ["libutils_defaults"],
+ // TODO(b/153609531): remove when no longer needed.
+ native_bridge_supported: true,
srcs: [
"CallStack.cpp",
@@ -200,6 +205,7 @@
shared_libs: [
"libutils",
"libbase",
+ "liblog",
],
}
@@ -233,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,
@@ -243,6 +309,7 @@
"LruCache_test.cpp",
"Mutex_test.cpp",
"SharedBuffer_test.cpp",
+ "Singleton_test.cpp",
"String8_test.cpp",
"String16_test.cpp",
"StrongPointer_test.cpp",
@@ -279,6 +346,11 @@
},
},
+ data_libs: [
+ "libutils_test_singleton1",
+ "libutils_test_singleton2",
+ ],
+
cflags: [
"-Wall",
"-Wextra",
@@ -289,29 +361,10 @@
test_suites: ["device-tests"],
}
-// TODO: the test infrastructure isn't yet capable of running this,
-// so it's broken out into its own test so that the main libutils_tests
-// can be in presubmit even if this can't.
-
-cc_test {
- name: "libutils_singleton_test",
- srcs: ["Singleton_test.cpp"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- shared_libs: ["libbase"],
-
- required: [
- ":libutils_test_singleton1",
- ":libutils_test_singleton2",
- ],
-}
-
cc_test_library {
name: "libutils_test_singleton1",
host_supported: true,
- relative_install_path: "libutils_test",
+ installable: false,
srcs: ["Singleton_test1.cpp"],
cflags: [
"-Wall",
@@ -322,7 +375,7 @@
cc_test_library {
name: "libutils_test_singleton2",
host_supported: true,
- relative_install_path: "libutils_test",
+ installable: false,
srcs: ["Singleton_test2.cpp"],
cflags: [
"-Wall",
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/String8.cpp b/libutils/String8.cpp
index d00e39c..c837891 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -424,7 +424,7 @@
char* buf = lockBuffer(len);
buf += start;
while (length > 0) {
- *buf = tolower(*buf);
+ *buf = static_cast<char>(tolower(*buf));
buf++;
length--;
}
@@ -448,7 +448,7 @@
char* buf = lockBuffer(len);
buf += start;
while (length > 0) {
- *buf = toupper(*buf);
+ *buf = static_cast<char>(toupper(*buf));
buf++;
length--;
}
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/BitSet.h b/libutils/include/utils/BitSet.h
index 8abfb1a..0fb1a6a 100644
--- a/libutils/include/utils/BitSet.h
+++ b/libutils/include/utils/BitSet.h
@@ -21,9 +21,12 @@
#include <utils/TypeHelpers.h>
/*
- * Contains some bit manipulation helpers.
+ * A class to provide efficient manipulation of bitsets.
*
- * DO NOT USE: std::bitset<32> or std::bitset<64> preferred
+ * Consider using std::bitset<32> or std::bitset<64> if all you want is a class to do basic bit
+ * manipulation (i.e. AND / OR / XOR / flip / etc). These classes are only needed if you want to
+ * efficiently perform operations like finding the first set bit in a bitset and you want to
+ * avoid using the built-in functions (e.g. __builtin_clz) on std::bitset::to_ulong.
*/
namespace android {
@@ -46,7 +49,9 @@
// Returns the number of marked bits in the set.
inline uint32_t count() const { return count(value); }
- static inline uint32_t count(uint32_t value) { return __builtin_popcountl(value); }
+ static inline uint32_t count(uint32_t value) {
+ return static_cast<uint32_t>(__builtin_popcountl(value));
+ }
// Returns true if the bit set does not contain any marked bits.
inline bool isEmpty() const { return isEmpty(value); }
@@ -128,7 +133,7 @@
}
static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) {
- return __builtin_popcountl(value & ~(0xffffffffUL >> n));
+ return static_cast<uint32_t>(__builtin_popcountl(value & ~(0xffffffffUL >> n)));
}
inline bool operator== (const BitSet32& other) const { return value == other.value; }
@@ -153,17 +158,17 @@
// input, which is only guaranteed to be 16b, not 32. The compiler should optimize this away.
static inline uint32_t clz_checked(uint32_t value) {
if (sizeof(unsigned int) == sizeof(uint32_t)) {
- return __builtin_clz(value);
+ return static_cast<uint32_t>(__builtin_clz(value));
} else {
- return __builtin_clzl(value);
+ return static_cast<uint32_t>(__builtin_clzl(value));
}
}
static inline uint32_t ctz_checked(uint32_t value) {
if (sizeof(unsigned int) == sizeof(uint32_t)) {
- return __builtin_ctz(value);
+ return static_cast<uint32_t>(__builtin_ctz(value));
} else {
- return __builtin_ctzl(value);
+ return static_cast<uint32_t>(__builtin_ctzl(value));
}
}
};
@@ -188,7 +193,9 @@
// Returns the number of marked bits in the set.
inline uint32_t count() const { return count(value); }
- static inline uint32_t count(uint64_t value) { return __builtin_popcountll(value); }
+ static inline uint32_t count(uint64_t value) {
+ return static_cast<uint32_t>(__builtin_popcountll(value));
+ }
// Returns true if the bit set does not contain any marked bits.
inline bool isEmpty() const { return isEmpty(value); }
@@ -219,26 +226,32 @@
// Result is undefined if all bits are unmarked.
inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }
- static inline uint32_t firstMarkedBit(uint64_t value) { return __builtin_clzll(value); }
+ static inline uint32_t firstMarkedBit(uint64_t value) {
+ return static_cast<uint32_t>(__builtin_clzll(value));
+ }
// Finds the first unmarked bit in the set.
// Result is undefined if all bits are marked.
inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }
- static inline uint32_t firstUnmarkedBit(uint64_t value) { return __builtin_clzll(~ value); }
+ static inline uint32_t firstUnmarkedBit(uint64_t value) {
+ return static_cast<uint32_t>(__builtin_clzll(~value));
+ }
// Finds the last marked bit in the set.
// Result is undefined if all bits are unmarked.
inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }
- static inline uint32_t lastMarkedBit(uint64_t value) { return 63 - __builtin_ctzll(value); }
+ static inline uint32_t lastMarkedBit(uint64_t value) {
+ return static_cast<uint32_t>(63 - __builtin_ctzll(value));
+ }
// Finds the first marked bit in the set and clears it. Returns the bit index.
// Result is undefined if all bits are unmarked.
inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }
static inline uint32_t clearFirstMarkedBit(uint64_t& value) {
- uint64_t n = firstMarkedBit(value);
+ uint32_t n = firstMarkedBit(value);
clearBit(value, n);
return n;
}
@@ -248,7 +261,7 @@
inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }
static inline uint32_t markFirstUnmarkedBit(uint64_t& value) {
- uint64_t n = firstUnmarkedBit(value);
+ uint32_t n = firstUnmarkedBit(value);
markBit(value, n);
return n;
}
@@ -258,7 +271,7 @@
inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }
static inline uint32_t clearLastMarkedBit(uint64_t& value) {
- uint64_t n = lastMarkedBit(value);
+ uint32_t n = lastMarkedBit(value);
clearBit(value, n);
return n;
}
@@ -268,7 +281,7 @@
inline uint32_t getIndexOfBit(uint32_t n) const { return getIndexOfBit(value, n); }
static inline uint32_t getIndexOfBit(uint64_t value, uint32_t n) {
- return __builtin_popcountll(value & ~(0xffffffffffffffffULL >> n));
+ return static_cast<uint32_t>(__builtin_popcountll(value & ~(0xffffffffffffffffULL >> n)));
}
inline bool operator== (const BitSet64& other) const { return value == other.value; }
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/llkd/README.md b/llkd/README.md
index 191f988..6f92f14 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -1,199 +1,237 @@
-Android Live-LocK Daemon
-========================
+<!--
+Project: /_project.yaml
+Book: /_book.yaml
-Introduction
-------------
+{% include "_versions.html" %}
+-->
-Android Live-LocK Daemon (llkd) is used to catch kernel deadlocks and mitigate.
+<!--
+ Copyright 2020 The Android Open Source Project
-Code is structured to allow integration into another service as either as part
-of the main loop, or spun off as a thread should that be necessary. A default
-standalone implementation is provided by llkd component.
+ 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
-The 'C' interface from libllkd component is thus:
+ http://www.apache.org/licenses/LICENSE-2.0
- #include "llkd.h"
- bool llkInit(const char* threadname) /* return true if enabled */
- unsigned llkCheckMillseconds(void) /* ms to sleep for next check */
+ 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.
+-->
-If a threadname is provided, a thread will be automatically spawned, otherwise
-caller must call llkCheckMilliseconds in its main loop. Function will return
-the period of time before the next expected call to this handler.
+# Android Live-LocK Daemon (llkd)
-Operations
-----------
+Android 10 <!-- {{ androidQVersionNumber }} --> includes the Android Live-LocK Daemon
+(`llkd`), which is designed to catch and mitigate kernel deadlocks. The `llkd`
+component provides a default standalone implementation, but you can
+alternatively integrate the `llkd` code into another service, either as part of
+the main loop or as a separate thread.
-There are two detection scenarios. Persistent D or Z state, and persistent
+## Detection scenarios <!-- {:#detection-scenarios} -->
+
+The `llkd` has two detection scenarios: Persistent D or Z state, and persistent
stack signature.
-If a thread is in D or Z state with no forward progress for longer than
-ro.llk.timeout_ms, or ro.llk.[D|Z].timeout_ms, kill the process or parent
-process respectively. If another scan shows the same process continues to
-exist, then have a confirmed live-lock condition and need to panic. Panic
-the kernel in a manner to provide the greatest bugreporting details as to the
-condition. Add a alarm self watchdog should llkd ever get locked up that is
-double the expected time to flow through the mainloop. Sampling is every
-ro.llk_sample_ms.
+### Persistent D or Z state <!-- {:#persistent-d-or-z-state} -->
-For usedebug releases only, persistent stack signature checking is enabled.
-If a thread in any state but Z, has a persistent listed ro.llk.stack kernel
-symbol always being reported, even if there is forward scheduling progress, for
-longer than ro.llk.timeout_ms, or ro.llk.stack.timeout_ms, then issue a kill
-to the process. If another scan shows the same process continues to exist,
-then have a confirmed live-lock condition and need to panic. There is no
-ABA detection since forward scheduling progress is allowed, thus the condition
-for the symbols are:
+If a thread is in D (uninterruptible sleep) or Z (zombie) state with no forward
+progress for longer than `ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms`, the
+`llkd` kills the process (or parent process). If a subsequent scan shows the
+same process continues to exist, the `llkd` confirms a live-lock condition and
+panics the kernel in a manner that provides the most detailed bug report for the
+condition.
-- Check is looking for " __symbol__+0x" or " __symbol__.cfi+0x" in
- /proc/__pid__/stack.
-- The __symbol__ should be rare and short lived enough that on a typical
- system the function is seen at most only once in a sample over the timeout
- period of ro.llk.stack.timeout_ms, samples occur every ro.llk.check_ms. This
- can be the only way to prevent a false trigger as there is no ABA protection.
-- Persistent continuously when the live lock condition exists.
-- Should be just below the function that is calling the lock that could
- contend, because if the lock is below or in the symbol function, the
- symbol will show in all affected processes, not just the one that
- caused the lockup.
+The `llkd` includes a self watchdog that alarms if `llkd` locks up; watchdog is
+double the expected time to flow through the mainloop and sampling is every
+`ro.llk_sample_ms`.
-Default will not monitor init, or [kthreadd] and all that [kthreadd] spawns.
-This reduces the effectiveness of llkd by limiting its coverage. If there is
-value in covering [kthreadd] spawned threads, the requirement will be that
-the drivers not remain in a persistent 'D' state, or that they have mechanisms
-to recover the thread should it be killed externally (this is good driver
-coding hygiene, a common request to add such to publicly reviewed kernel.org
-maintained drivers). For instance use wait_event_interruptible() instead of
-wait_event(). The blacklists can be adjusted accordingly if these
-conditions are met to cover kernel components. For the stack symbol checking,
-there is an additional process blacklist so that we do not incide sepolicy
-violations on services that block ptrace operations.
+### Persistent stack signature <!-- {:#persistent-stack-signature} -->
-An accompanying gTest set have been added, and will setup a persistent D or Z
-process, with and without forward progress, but not in a live-lock state
-because that would require a buggy kernel, or a module or kernel modification
-to stimulate. The test will check that llkd will mitigate first by killing
-the appropriate process. D state is setup by vfork() waiting for exec() in
-child process. Z state is setup by fork() and an un-waited for child process.
-Should be noted that both of these conditions should never happen on Android
-on purpose, and llkd effectively sweeps up processes that create these
-conditions. If the test can, it will reconfigure llkd to expedite the test
-duration by adjusting the ro.llk.* Android properties. Tests run the D state
-with some scheduling progress to ensure that ABA checking prevents false
-triggers. If 100% reliable ABA on platform, then ro.llk.killtest can be
-set to false; however this will result in some of the unit tests to panic
-kernel instead of deal with more graceful kill operation.
+For userdebug releases, the `llkd` can detect kernel live-locks using persistent
+stack signature checking. If a thread in any state except Z has a persistent
+listed `ro.llk.stack` kernel symbol that is reported for longer than
+`ro.llk.timeout_ms` or `ro.llk.stack.timeout_ms`, the `llkd` kills the process
+(even if there is forward scheduling progress). If a subsequent scan shows the
+same process continues to exist, the `llkd` confirms a live-lock condition and
+panics the kernel in a manner that provides the most detailed bug report for the
+condition.
-Android Properties
-------------------
+Note: Because forward scheduling progress is allowed, the `llkd` does not
+perform [ABA detection](https://en.wikipedia.org/wiki/ABA_problem){:.external}.
-The following are the Android Properties llkd respond to.
-*prop*_ms named properties are in milliseconds.
-Properties that use comma (*,*) separator for lists, use a leading separator to
-preserve default and add or subtract entries with (*optional*) plus (*+*) and
-minus (*-*) prefixes respectively.
-For these lists, the string "*false*" is synonymous with an *empty* list,
-and *blank* or *missing* resorts to the specified *default* value.
+The `lldk` check persists continuously when the live lock condition exists and
+looks for the composed strings `" symbol+0x"` or `" symbol.cfi+0x"` in the
+`/proc/pid/stack` file on Linux. The list of symbols is in `ro.llk.stack` and
+defaults to the comma-separated list of
+"`cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable`".
-#### ro.config.low_ram
-device is configured with limited memory.
+Symbols should be rare and short-lived enough that on a typical system the
+function is seen only once in a sample over the timeout period of
+`ro.llk.stack.timeout_ms` (samples occur every `ro.llk.check_ms`). Due to lack
+of ABA protection, this is the only way to prevent a false trigger. The symbol
+function must appear below the function calling the lock that could contend. If
+the lock is below or in the symbol function, the symbol appears in all affected
+processes, not just the one that caused the lockup.
-#### ro.debuggable
-device is configured for userdebug or eng build.
+## Coverage <!-- {:#coverage} -->
-#### ro.llk.sysrq_t
-default not ro.config.low_ram, or ro.debuggable if property is "eng".
-if true do sysrq t (dump all threads).
+The default implementation of `llkd` does not monitor `init`, `[kthreadd]`, or
+`[kthreadd]` spawns. For the `llkd` to cover `[kthreadd]`-spawned threads:
-#### ro.llk.enable
-default false, allow live-lock daemon to be enabled.
+* Drivers must not remain in a persistent D state,
-#### llk.enable
-default ro.llk.enable, and evaluated for eng.
+OR
-#### ro.khungtask.enable
-default false, allow [khungtask] daemon to be enabled.
+* Drivers must have mechanisms to recover the thread should it be killed
+ externally. For example, use `wait_event_interruptible()` instead of
+ `wait_event()`.
-#### khungtask.enable
-default ro.khungtask.enable and evaluated for eng.
+If one of the above conditions is met, the `llkd` ignorelist can be adjusted to
+cover kernel components. Stack symbol checking involves an additional process
+ignore list to prevent sepolicy violations on services that block `ptrace`
+operations.
-#### ro.llk.mlockall
-default false, enable call to mlockall().
+## Android properties <!-- {:#android-properties} -->
-#### ro.khungtask.timeout
-default value 12 minutes, [khungtask] maximum timelimit.
+The `llkd` responds to several Android properties (listed below).
-#### ro.llk.timeout_ms
-default 10 minutes, D or Z maximum timelimit, double this value and it sets
-the alarm watchdog for llkd.
+* Properties named `prop_ms` are in milliseconds.
+* Properties that use comma (,) separator for lists use a leading separator to
+ preserve the default entry, then add or subtract entries with optional plus
+ (+) and minus (-) prefixes respectively. For these lists, the string "false"
+ is synonymous with an empty list, and blank or missing entries resort to the
+ specified default value.
-#### ro.llk.D.timeout_ms
-default ro.llk.timeout_ms, D maximum timelimit.
+### ro.config.low_ram <!-- {:#ro-config-low-ram} -->
-#### ro.llk.Z.timeout_ms
-default ro.llk.timeout_ms, Z maximum timelimit.
+Device is configured with limited memory.
-#### ro.llk.stack.timeout_ms
-default ro.llk.timeout_ms,
-checking for persistent stack symbols maximum timelimit.
-Only active on userdebug or eng builds.
+### ro.debuggable <!-- {:#ro-debuggable} -->
-#### ro.llk.check_ms
-default 2 minutes samples of threads for D or Z.
+Device is configured for userdebug or eng build.
-#### ro.llk.stack
-default cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
-comma separated list of kernel symbols.
-Look for kernel stack symbols that if ever persistently present can
-indicate a subsystem is locked up.
-Beware, check does not on purpose do forward scheduling ABA except by polling
-every ro.llk_check_ms over the period ro.llk.stack.timeout_ms, so stack symbol
-should be exceptionally rare and fleeting.
-One must be convinced that it is virtually *impossible* for symbol to show up
-persistently in all samples of the stack.
-Again, looks for a match for either " **symbol**+0x" or " **symbol**.cfi+0x"
-in stack expansion.
-Only available on userdebug or eng builds, limited privileges due to security
-concerns on user builds prevents this checking.
+### ro.llk.sysrq_t <!-- {:#ro-llk-sysrq-t} -->
-#### ro.llk.blacklist.process
-default 0,1,2 (kernel, init and [kthreadd]) plus process names
-init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,
-[watchdogd],[watchdogd/0],...,[watchdogd/***get_nprocs**-1*].
-Do not watch these processes. A process can be comm, cmdline or pid reference.
-NB: automated default here can be larger than the current maximum property
-size of 92.
-NB: false is a very very very unlikely process to want to blacklist.
+If property is "eng", the default is not `ro.config.low_ram` or `ro.debuggable`.
+If true, dump all threads (`sysrq t`).
-#### ro.llk.blacklist.parent
-default 0,2,adbd&[setsid] (kernel, [kthreadd] and adbd *only for zombie setsid*).
-Do not watch processes that have this parent.
-An ampersand (*&*) separator is used to specify that the parent is ignored
-only in combination with the target child process.
-Ampersand was selected because it is never part of a process name,
-however a setprop in the shell requires it to be escaped or quoted;
-init rc file where this is normally specified does not have this issue.
-A parent or target processes can be specified as comm, cmdline or pid reference.
+### ro.llk.enable <!-- {:#ro-llk-enable} -->
-#### ro.llk.blacklist.uid
-default *empty* or false, comma separated list of uid numbers or names.
-Do not watch processes that match this uid.
+Allow live-lock daemon to be enabled. Default is false.
-#### ro.llk.blacklist.process.stack
-default process names init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd.
-This subset of processes are not monitored for live lock stack signatures.
-Also prevents the sepolicy violation associated with processes that block
-ptrace, as these can not be checked anyways.
-Only active on userdebug and eng builds.
+### llk.enable <!-- {:#llk-enable} -->
-Architectural Concerns
-----------------------
+Evaluated for eng builds. Default is `ro.llk.enable`.
-- built-in [khungtask] daemon is too generic and trips on driver code that
- sits around in D state too much. To switch to S instead makes the task(s)
- killable, so the drivers should be able to resurrect them if needed.
-- Properties are limited to 92 characters.
-- Create kernel module and associated gTest to actually test panic.
-- Create gTest to test out blacklist (ro.llk.blacklist.*properties* generally
- not be inputs). Could require more test-only interfaces to libllkd.
-- Speed up gTest using something else than ro.llk.*properties*, which should
- not be inputs as they should be baked into the product.
+### ro.khungtask.enable <!-- {:#ro-khungtask-enable} -->
+
+Allow `[khungtask]` daemon to be enabled. Default is false.
+
+### khungtask.enable <!-- {:#khungtask-enable} -->
+
+Evaluated for eng builds. Default is `ro.khungtask.enable`.
+
+### ro.llk.mlockall <!-- {:#ro-llk-mlockall} -->
+
+Enable call to `mlockall()`. Default is false.
+
+### ro.khungtask.timeout <!-- {:#ro-khungtask-timeout} -->
+
+`[khungtask]` maximum time limit. Default is 12 minutes.
+
+### ro.llk.timeout_ms <!-- {:#ro-llk-timeout-ms} -->
+
+D or Z maximum time limit. Default is 10 minutes. Double this value to set the
+alarm watchdog for `llkd`.
+
+### ro.llk.D.timeout_ms <!-- {:#ro-llk-D-timeout-ms} -->
+
+D maximum time limit. Default is `ro.llk.timeout_ms`.
+
+### ro.llk.Z.timeout_ms <!-- {:#ro-llk-Z-timeout-ms} -->
+
+Z maximum time limit. Default is `ro.llk.timeout_ms`.
+
+### ro.llk.stack.timeout_ms <!-- {:#ro-llk-stack-timeout-ms} -->
+
+Checks for persistent stack symbols maximum time limit. Default is
+`ro.llk.timeout_ms`. **Active only on userdebug or eng builds**.
+
+### ro.llk.check_ms <!-- {:#ro-llk-check-ms} -->
+
+Samples of threads for D or Z. Default is two minutes.
+
+### ro.llk.stack <!-- {:#ro-llk-stack} -->
+
+Checks for kernel stack symbols that if persistently present can indicate a
+subsystem is locked up. Default is
+`cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable`
+comma-separated list of kernel symbols. The check doesn't do forward scheduling
+ABA except by polling every `ro.llk_check_ms` over the period
+`ro.llk.stack.timeout_ms`, so stack symbols should be exceptionally rare and
+fleeting (it is highly unlikely for a symbol to show up persistently in all
+samples of the stack). Checks for a match for `" symbol+0x"` or
+`" symbol.cfi+0x"` in stack expansion. **Available only on userdebug or eng
+builds**; security concerns on user builds result in limited privileges that
+prevent this check.
+
+### ro.llk.ignorelist.process <!-- {:#ro-llk-ignorelist-process} -->
+
+The `llkd` does not watch the specified processes. Default is `0,1,2` (`kernel`,
+`init`, and `[kthreadd]`) plus process names
+`init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1]`.
+A process can be a `comm`, `cmdline`, or `pid` reference. An automated default
+can be larger than the current maximum property size of 92.
+
+Note: `false` is an extremely unlikely process to want to ignore.
+
+### ro.llk.ignorelist.parent <!-- {:#ro-llk-ignorelist-parent} -->
+
+The `llkd` does not watch processes that have the specified parent(s). Default
+is `0,2,adbd&[setsid]` (`kernel`, `[kthreadd]`, and `adbd` only for zombie
+`setsid`). An ampersand (&) separator specifies that the parent is ignored only
+in combination with the target child process. Ampersand was selected because it
+is never part of a process name; however, a `setprop` in the shell requires the
+ampersand to be escaped or quoted, although the `init rc` file where this is
+normally specified does not have this issue. A parent or target process can be a
+`comm`, `cmdline`, or `pid` reference.
+
+### ro.llk.ignorelist.uid <!-- {:#ro-llk-ignorelist-uid} -->
+
+The `llkd` does not watch processes that match the specified uid(s).
+Comma-separated list of uid numbers or names. Default is empty or false.
+
+### ro.llk.ignorelist.process.stack <!-- {:#ro-llk-ignorelist-process-stack} -->
+
+The `llkd` does not monitor the specified subset of processes for live lock stack
+signatures. Default is process names
+`init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd`. Prevents the sepolicy
+violation associated with processes that block `ptrace` (as these can't be
+checked). **Active only on userdebug and eng builds**. For details on build
+types, refer to [Building Android](/setup/build/building#choose-a-target).
+
+## Architectural concerns <!-- {:#architectural-concerns} -->
+
+* Properties are limited to 92 characters. However, this is not limited for
+ defaults defined in the `include/llkd.h` file in the sources.
+* The built-in `[khungtask]` daemon is too generic and trips on driver code that
+ sits around in D state too much. Switching drivers to sleep, or S state,
+ would make task(s) killable, and need to be resurrectable by drivers on an
+ as-need basis.
+
+## Library interface (optional) <!-- {:#library-interface-optional} -->
+
+You can optionally incorporate the `llkd` into another privileged daemon using
+the following C interface from the `libllkd` component:
+
+```
+#include "llkd.h"
+bool llkInit(const char* threadname) /* return true if enabled */
+unsigned llkCheckMillseconds(void) /* ms to sleep for next check */
+```
+
+If a threadname is provided, a thread automatically spawns, otherwise the caller
+must call `llkCheckMilliseconds` in its main loop. The function returns the
+period of time before the next expected call to this handler.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 3586ca1..4b20a56 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -30,37 +30,37 @@
unsigned llkCheckMilliseconds(void);
/* clang-format off */
-#define LLK_ENABLE_WRITEABLE_PROPERTY "llk.enable"
-#define LLK_ENABLE_PROPERTY "ro." LLK_ENABLE_WRITEABLE_PROPERTY
-#define LLK_ENABLE_DEFAULT false /* "eng" and userdebug true */
-#define KHT_ENABLE_WRITEABLE_PROPERTY "khungtask.enable"
-#define KHT_ENABLE_PROPERTY "ro." KHT_ENABLE_WRITEABLE_PROPERTY
-#define LLK_ENABLE_SYSRQ_T_PROPERTY "ro.llk.sysrq_t"
-#define LLK_ENABLE_SYSRQ_T_DEFAULT true
-#define LLK_MLOCKALL_PROPERTY "ro.llk.mlockall"
-#define LLK_MLOCKALL_DEFAULT true
-#define LLK_KILLTEST_PROPERTY "ro.llk.killtest"
-#define LLK_KILLTEST_DEFAULT true
-#define LLK_TIMEOUT_MS_PROPERTY "ro.llk.timeout_ms"
-#define KHT_TIMEOUT_PROPERTY "ro.khungtask.timeout"
-#define LLK_D_TIMEOUT_MS_PROPERTY "ro.llk.D.timeout_ms"
-#define LLK_Z_TIMEOUT_MS_PROPERTY "ro.llk.Z.timeout_ms"
-#define LLK_STACK_TIMEOUT_MS_PROPERTY "ro.llk.stack.timeout_ms"
-#define LLK_CHECK_MS_PROPERTY "ro.llk.check_ms"
+#define LLK_ENABLE_WRITEABLE_PROPERTY "llk.enable"
+#define LLK_ENABLE_PROPERTY "ro." LLK_ENABLE_WRITEABLE_PROPERTY
+#define LLK_ENABLE_DEFAULT false /* "eng" and userdebug true */
+#define KHT_ENABLE_WRITEABLE_PROPERTY "khungtask.enable"
+#define KHT_ENABLE_PROPERTY "ro." KHT_ENABLE_WRITEABLE_PROPERTY
+#define LLK_ENABLE_SYSRQ_T_PROPERTY "ro.llk.sysrq_t"
+#define LLK_ENABLE_SYSRQ_T_DEFAULT true
+#define LLK_MLOCKALL_PROPERTY "ro.llk.mlockall"
+#define LLK_MLOCKALL_DEFAULT true
+#define LLK_KILLTEST_PROPERTY "ro.llk.killtest"
+#define LLK_KILLTEST_DEFAULT true
+#define LLK_TIMEOUT_MS_PROPERTY "ro.llk.timeout_ms"
+#define KHT_TIMEOUT_PROPERTY "ro.khungtask.timeout"
+#define LLK_D_TIMEOUT_MS_PROPERTY "ro.llk.D.timeout_ms"
+#define LLK_Z_TIMEOUT_MS_PROPERTY "ro.llk.Z.timeout_ms"
+#define LLK_STACK_TIMEOUT_MS_PROPERTY "ro.llk.stack.timeout_ms"
+#define LLK_CHECK_MS_PROPERTY "ro.llk.check_ms"
/* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
-#define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
-#define LLK_CHECK_STACK_PROPERTY "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT \
+#define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
+#define LLK_CHECK_STACK_PROPERTY "ro.llk.stack"
+#define LLK_CHECK_STACK_DEFAULT \
"cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable"
-#define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
-#define LLK_BLACKLIST_PROCESS_DEFAULT \
+#define LLK_IGNORELIST_PROCESS_PROPERTY "ro.llk.ignorelist.process"
+#define LLK_IGNORELIST_PROCESS_DEFAULT \
"0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
-#define LLK_BLACKLIST_PARENT_PROPERTY "ro.llk.blacklist.parent"
-#define LLK_BLACKLIST_PARENT_DEFAULT "0,2,[kthreadd],adbd&[setsid]"
-#define LLK_BLACKLIST_UID_PROPERTY "ro.llk.blacklist.uid"
-#define LLK_BLACKLIST_UID_DEFAULT ""
-#define LLK_BLACKLIST_STACK_PROPERTY "ro.llk.blacklist.process.stack"
-#define LLK_BLACKLIST_STACK_DEFAULT "init,lmkd.llkd,llkd,keystore,ueventd,apexd"
+#define LLK_IGNORELIST_PARENT_PROPERTY "ro.llk.ignorelist.parent"
+#define LLK_IGNORELIST_PARENT_DEFAULT "0,2,[kthreadd],adbd&[setsid]"
+#define LLK_IGNORELIST_UID_PROPERTY "ro.llk.ignorelist.uid"
+#define LLK_IGNORELIST_UID_DEFAULT ""
+#define LLK_IGNORELIST_STACK_PROPERTY "ro.llk.ignorelist.process.stack"
+#define LLK_IGNORELIST_STACK_DEFAULT "init,lmkd.llkd,llkd,keystore,ueventd,apexd"
/* clang-format on */
__END_DECLS
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 8ad9900..a24d900 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -98,26 +98,26 @@
std::unordered_set<std::string> llkCheckStackSymbols;
#endif
-// Blacklist variables, initialized with comma separated lists of high false
+// Ignorelist variables, initialized with comma separated lists of high false
// positive and/or dangerous references, e.g. without self restart, for pid,
// ppid, name and uid:
// list of pids, or tids or names to skip. kernel pid (0), init pid (1),
// [kthreadd] pid (2), ourselves, "init", "[kthreadd]", "lmkd", "llkd" or
// combinations of watchdogd in kernel and user space.
-std::unordered_set<std::string> llkBlacklistProcess;
+std::unordered_set<std::string> llkIgnorelistProcess;
// list of parent pids, comm or cmdline names to skip. default:
// kernel pid (0), [kthreadd] (2), or ourselves, enforced and implied
-std::unordered_set<std::string> llkBlacklistParent;
+std::unordered_set<std::string> llkIgnorelistParent;
// list of parent and target processes to skip. default:
// adbd *and* [setsid]
-std::unordered_map<std::string, std::unordered_set<std::string>> llkBlacklistParentAndChild;
+std::unordered_map<std::string, std::unordered_set<std::string>> llkIgnorelistParentAndChild;
// list of uids, and uid names, to skip, default nothing
-std::unordered_set<std::string> llkBlacklistUid;
+std::unordered_set<std::string> llkIgnorelistUid;
#ifdef __PTRACE_ENABLED__
// list of names to skip stack checking. "init", "lmkd", "llkd", "keystore" or
// "logd" (if not userdebug).
-std::unordered_set<std::string> llkBlacklistStack;
+std::unordered_set<std::string> llkIgnorelistStack;
#endif
class dir {
@@ -626,9 +626,9 @@
return flag ? "true" : "false";
}
-std::string llkFormat(const std::unordered_set<std::string>& blacklist) {
+std::string llkFormat(const std::unordered_set<std::string>& ignorelist) {
std::string ret;
- for (const auto& entry : blacklist) {
+ for (const auto& entry : ignorelist) {
if (!ret.empty()) ret += ",";
ret += entry;
}
@@ -636,10 +636,10 @@
}
std::string llkFormat(
- const std::unordered_map<std::string, std::unordered_set<std::string>>& blacklist,
+ const std::unordered_map<std::string, std::unordered_set<std::string>>& ignorelist,
bool leading_comma = false) {
std::string ret;
- for (const auto& entry : blacklist) {
+ for (const auto& entry : ignorelist) {
for (const auto& target : entry.second) {
if (leading_comma || !ret.empty()) ret += ",";
ret += entry.first + "&" + target;
@@ -699,61 +699,61 @@
}
bool llkSkipName(const std::string& name,
- const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
- if (name.empty() || blacklist.empty()) return false;
+ const std::unordered_set<std::string>& ignorelist = llkIgnorelistProcess) {
+ if (name.empty() || ignorelist.empty()) return false;
- return blacklist.find(name) != blacklist.end();
+ return ignorelist.find(name) != ignorelist.end();
}
bool llkSkipProc(proc* procp,
- const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
+ const std::unordered_set<std::string>& ignorelist = llkIgnorelistProcess) {
if (!procp) return false;
- if (llkSkipName(std::to_string(procp->pid), blacklist)) return true;
- if (llkSkipName(procp->getComm(), blacklist)) return true;
- if (llkSkipName(procp->getCmdline(), blacklist)) return true;
- if (llkSkipName(android::base::Basename(procp->getCmdline()), blacklist)) return true;
+ if (llkSkipName(std::to_string(procp->pid), ignorelist)) return true;
+ if (llkSkipName(procp->getComm(), ignorelist)) return true;
+ if (llkSkipName(procp->getCmdline(), ignorelist)) return true;
+ if (llkSkipName(android::base::Basename(procp->getCmdline()), ignorelist)) return true;
return false;
}
const std::unordered_set<std::string>& llkSkipName(
const std::string& name,
- const std::unordered_map<std::string, std::unordered_set<std::string>>& blacklist) {
+ const std::unordered_map<std::string, std::unordered_set<std::string>>& ignorelist) {
static const std::unordered_set<std::string> empty;
- if (name.empty() || blacklist.empty()) return empty;
- auto found = blacklist.find(name);
- if (found == blacklist.end()) return empty;
+ if (name.empty() || ignorelist.empty()) return empty;
+ auto found = ignorelist.find(name);
+ if (found == ignorelist.end()) return empty;
return found->second;
}
bool llkSkipPproc(proc* pprocp, proc* procp,
const std::unordered_map<std::string, std::unordered_set<std::string>>&
- blacklist = llkBlacklistParentAndChild) {
- if (!pprocp || !procp || blacklist.empty()) return false;
- if (llkSkipProc(procp, llkSkipName(std::to_string(pprocp->pid), blacklist))) return true;
- if (llkSkipProc(procp, llkSkipName(pprocp->getComm(), blacklist))) return true;
- if (llkSkipProc(procp, llkSkipName(pprocp->getCmdline(), blacklist))) return true;
+ ignorelist = llkIgnorelistParentAndChild) {
+ if (!pprocp || !procp || ignorelist.empty()) return false;
+ if (llkSkipProc(procp, llkSkipName(std::to_string(pprocp->pid), ignorelist))) return true;
+ if (llkSkipProc(procp, llkSkipName(pprocp->getComm(), ignorelist))) return true;
+ if (llkSkipProc(procp, llkSkipName(pprocp->getCmdline(), ignorelist))) return true;
return llkSkipProc(procp,
- llkSkipName(android::base::Basename(pprocp->getCmdline()), blacklist));
+ llkSkipName(android::base::Basename(pprocp->getCmdline()), ignorelist));
}
bool llkSkipPid(pid_t pid) {
- return llkSkipName(std::to_string(pid), llkBlacklistProcess);
+ return llkSkipName(std::to_string(pid), llkIgnorelistProcess);
}
bool llkSkipPpid(pid_t ppid) {
- return llkSkipName(std::to_string(ppid), llkBlacklistParent);
+ return llkSkipName(std::to_string(ppid), llkIgnorelistParent);
}
bool llkSkipUid(uid_t uid) {
// Match by number?
- if (llkSkipName(std::to_string(uid), llkBlacklistUid)) {
+ if (llkSkipName(std::to_string(uid), llkIgnorelistUid)) {
return true;
}
// Match by name?
auto pwd = ::getpwuid(uid);
return (pwd != nullptr) && __predict_true(pwd->pw_name != nullptr) &&
- __predict_true(pwd->pw_name[0] != '\0') && llkSkipName(pwd->pw_name, llkBlacklistUid);
+ __predict_true(pwd->pw_name[0] != '\0') && llkSkipName(pwd->pw_name, llkIgnorelistUid);
}
bool getValidTidDir(dirent* dp, std::string* piddir) {
@@ -811,7 +811,7 @@
}
// Don't check process that are known to block ptrace, save sepolicy noise.
- if (llkSkipProc(procp, llkBlacklistStack)) return false;
+ if (llkSkipProc(procp, llkIgnorelistStack)) return false;
auto kernel_stack = ReadFile(piddir + "/stack");
if (kernel_stack.empty()) {
LOG(VERBOSE) << piddir << "/stack empty comm=" << procp->getComm()
@@ -917,12 +917,12 @@
<< LLK_CHECK_MS_PROPERTY "=" << llkFormat(llkCheckMs) << "\n"
#ifdef __PTRACE_ENABLED__
<< LLK_CHECK_STACK_PROPERTY "=" << llkFormat(llkCheckStackSymbols) << "\n"
- << LLK_BLACKLIST_STACK_PROPERTY "=" << llkFormat(llkBlacklistStack) << "\n"
+ << LLK_IGNORELIST_STACK_PROPERTY "=" << llkFormat(llkIgnorelistStack) << "\n"
#endif
- << LLK_BLACKLIST_PROCESS_PROPERTY "=" << llkFormat(llkBlacklistProcess) << "\n"
- << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent)
- << llkFormat(llkBlacklistParentAndChild, true) << "\n"
- << LLK_BLACKLIST_UID_PROPERTY "=" << llkFormat(llkBlacklistUid);
+ << LLK_IGNORELIST_PROCESS_PROPERTY "=" << llkFormat(llkIgnorelistProcess) << "\n"
+ << LLK_IGNORELIST_PARENT_PROPERTY "=" << llkFormat(llkIgnorelistParent)
+ << llkFormat(llkIgnorelistParentAndChild, true) << "\n"
+ << LLK_IGNORELIST_UID_PROPERTY "=" << llkFormat(llkIgnorelistUid);
}
void* llkThread(void* obj) {
@@ -932,14 +932,14 @@
std::string name = std::to_string(::gettid());
if (!llkSkipName(name)) {
- llkBlacklistProcess.emplace(name);
+ llkIgnorelistProcess.emplace(name);
}
name = static_cast<const char*>(obj);
prctl(PR_SET_NAME, name.c_str());
if (__predict_false(!llkSkipName(name))) {
- llkBlacklistProcess.insert(name);
+ llkIgnorelistProcess.insert(name);
}
- // No longer modifying llkBlacklistProcess.
+ // No longer modifying llkIgnorelistProcess.
llkRunning = true;
llkLogConfig();
while (llkRunning) {
@@ -1122,12 +1122,12 @@
}
if (pprocp) {
if (llkSkipPproc(pprocp, procp)) break;
- if (llkSkipProc(pprocp, llkBlacklistParent)) break;
+ if (llkSkipProc(pprocp, llkIgnorelistParent)) break;
} else {
- if (llkSkipName(std::to_string(ppid), llkBlacklistParent)) break;
+ if (llkSkipName(std::to_string(ppid), llkIgnorelistParent)) break;
}
- if ((llkBlacklistUid.size() != 0) && llkSkipUid(procp->getUid())) {
+ if ((llkIgnorelistUid.size() != 0) && llkSkipUid(procp->getUid())) {
continue;
}
@@ -1320,29 +1320,29 @@
if (debuggable) {
llkCheckStackSymbols = llkSplit(LLK_CHECK_STACK_PROPERTY, LLK_CHECK_STACK_DEFAULT);
}
- std::string defaultBlacklistStack(LLK_BLACKLIST_STACK_DEFAULT);
- if (!debuggable) defaultBlacklistStack += ",logd,/system/bin/logd";
- llkBlacklistStack = llkSplit(LLK_BLACKLIST_STACK_PROPERTY, defaultBlacklistStack);
+ std::string defaultIgnorelistStack(LLK_IGNORELIST_STACK_DEFAULT);
+ if (!debuggable) defaultIgnorelistStack += ",logd,/system/bin/logd";
+ llkIgnorelistStack = llkSplit(LLK_IGNORELIST_STACK_PROPERTY, defaultIgnorelistStack);
#endif
- std::string defaultBlacklistProcess(
- std::to_string(kernelPid) + "," + std::to_string(initPid) + "," +
- std::to_string(kthreaddPid) + "," + std::to_string(::getpid()) + "," +
- std::to_string(::gettid()) + "," LLK_BLACKLIST_PROCESS_DEFAULT);
+ std::string defaultIgnorelistProcess(
+ std::to_string(kernelPid) + "," + std::to_string(initPid) + "," +
+ std::to_string(kthreaddPid) + "," + std::to_string(::getpid()) + "," +
+ std::to_string(::gettid()) + "," LLK_IGNORELIST_PROCESS_DEFAULT);
if (threadname) {
- defaultBlacklistProcess += ","s + threadname;
+ defaultIgnorelistProcess += ","s + threadname;
}
for (int cpu = 1; cpu < get_nprocs_conf(); ++cpu) {
- defaultBlacklistProcess += ",[watchdog/" + std::to_string(cpu) + "]";
+ defaultIgnorelistProcess += ",[watchdog/" + std::to_string(cpu) + "]";
}
- llkBlacklistProcess = llkSplit(LLK_BLACKLIST_PROCESS_PROPERTY, defaultBlacklistProcess);
+ llkIgnorelistProcess = llkSplit(LLK_IGNORELIST_PROCESS_PROPERTY, defaultIgnorelistProcess);
if (!llkSkipName("[khungtaskd]")) { // ALWAYS ignore as special
- llkBlacklistProcess.emplace("[khungtaskd]");
+ llkIgnorelistProcess.emplace("[khungtaskd]");
}
- llkBlacklistParent = llkSplit(LLK_BLACKLIST_PARENT_PROPERTY,
- std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
- "," LLK_BLACKLIST_PARENT_DEFAULT);
- // derive llkBlacklistParentAndChild by moving entries with '&' from above
- for (auto it = llkBlacklistParent.begin(); it != llkBlacklistParent.end();) {
+ llkIgnorelistParent = llkSplit(LLK_IGNORELIST_PARENT_PROPERTY,
+ std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
+ "," LLK_IGNORELIST_PARENT_DEFAULT);
+ // derive llkIgnorelistParentAndChild by moving entries with '&' from above
+ for (auto it = llkIgnorelistParent.begin(); it != llkIgnorelistParent.end();) {
auto pos = it->find('&');
if (pos == std::string::npos) {
++it;
@@ -1350,18 +1350,18 @@
}
auto parent = it->substr(0, pos);
auto child = it->substr(pos + 1);
- it = llkBlacklistParent.erase(it);
+ it = llkIgnorelistParent.erase(it);
- auto found = llkBlacklistParentAndChild.find(parent);
- if (found == llkBlacklistParentAndChild.end()) {
- llkBlacklistParentAndChild.emplace(std::make_pair(
+ auto found = llkIgnorelistParentAndChild.find(parent);
+ if (found == llkIgnorelistParentAndChild.end()) {
+ llkIgnorelistParentAndChild.emplace(std::make_pair(
std::move(parent), std::unordered_set<std::string>({std::move(child)})));
} else {
found->second.emplace(std::move(child));
}
}
- llkBlacklistUid = llkSplit(LLK_BLACKLIST_UID_PROPERTY, LLK_BLACKLIST_UID_DEFAULT);
+ llkIgnorelistUid = llkSplit(LLK_IGNORELIST_UID_PROPERTY, LLK_IGNORELIST_UID_DEFAULT);
// internal watchdog
::signal(SIGALRM, llkAlarmHandler);
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 0056c80..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:
@@ -333,14 +334,14 @@
This can individually control each buffer's size with -b.
-S, --statistics Output statistics.
--pid can be used to provide pid specific stats.
- -p, --prune Print prune white and ~black list. Service is specified as UID,
- UID/PID or /PID. Weighed for quicker pruning if prefix with ~,
- otherwise weighed for longevity if unadorned. All other pruning
- activity is oldest first. Special case ~! represents an automatic
- quicker pruning for the noisiest UID as determined by the current
- statistics.
- -P, --prune='<list> ...' Set prune white and ~black list, using same format as listed above.
- Must be quoted.
+ -p, --prune Print prune rules. Each rule is specified as UID, UID/PID or /PID. A
+ '~' prefix indicates that elements matching the rule should be pruned
+ with higher priority otherwise they're pruned with lower priority. All
+ other pruning activity is oldest first. Special case ~! represents an
+ automatic pruning for the noisiest UID as determined by the current
+ statistics. Special case ~1000/! represents pruning of the worst PID
+ within AID_SYSTEM when AID_SYSTEM is the noisiest UID.
+ -P, --prune='<list> ...' Set prune rules, using same format as listed above. Must be quoted.
Filtering:
-s Set default filter to silent. Equivalent to filterspec '*:S'
@@ -402,8 +403,8 @@
" time — Display the date, invocation time, priority/tag, and PID of the\n"
" process issuing the message.\n"
"\nAdverb modifiers can be used in combination:\n"
- " color — Display in highlighted color to match priority. i.e. \x1B[38;5;231mVERBOSE\n"
- " \x1B[38;5;75mDEBUG \x1B[38;5;40mINFO \x1B[38;5;166mWARNING \x1B[38;5;196mERROR FATAL\x1B[0m\n"
+ " color — Display in highlighted color to match priority. i.e. \x1B[39mVERBOSE\n"
+ " \x1B[34mDEBUG \x1B[32mINFO \x1B[33mWARNING \x1B[31mERROR FATAL\x1B[0m\n"
" descriptive — events logs only, descriptions from event-log-tags database.\n"
" epoch — Display time as seconds since Jan 1 1970.\n"
" monotonic — Display time as cpu seconds since last boot.\n"
@@ -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 a2daeb0..0860000 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -1301,7 +1301,7 @@
}
#endif
-static bool get_white_black(char** list) {
+static bool get_prune_rules(char** list) {
FILE* fp = popen(logcat_executable " -p 2>/dev/null", "r");
if (fp == NULL) {
fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
@@ -1334,7 +1334,7 @@
return *list != NULL;
}
-static bool set_white_black(const char* list) {
+static bool set_prune_rules(const char* list) {
char buffer[BIG_BUFFER];
snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
list ? list : "");
@@ -1363,28 +1363,28 @@
return pclose(fp) == 0;
}
-TEST(logcat, white_black_adjust) {
+TEST(logcat, prune_rules_adjust) {
char* list = NULL;
char* adjust = NULL;
- get_white_black(&list);
+ get_prune_rules(&list);
static const char adjustment[] = "~! 300/20 300/25 2000 ~1000/5 ~1000/30";
- ASSERT_EQ(true, set_white_black(adjustment));
- ASSERT_EQ(true, get_white_black(&adjust));
+ ASSERT_EQ(true, set_prune_rules(adjustment));
+ ASSERT_EQ(true, get_prune_rules(&adjust));
EXPECT_STREQ(adjustment, adjust);
free(adjust);
adjust = NULL;
static const char adjustment2[] = "300/20 300/21 2000 ~1000";
- ASSERT_EQ(true, set_white_black(adjustment2));
- ASSERT_EQ(true, get_white_black(&adjust));
+ ASSERT_EQ(true, set_prune_rules(adjustment2));
+ ASSERT_EQ(true, get_prune_rules(&adjust));
EXPECT_STREQ(adjustment2, adjust);
free(adjust);
adjust = NULL;
- ASSERT_EQ(true, set_white_black(list));
- get_white_black(&adjust);
+ ASSERT_EQ(true, set_prune_rules(list));
+ get_prune_rules(&adjust);
EXPECT_STREQ(list ? list : "", adjust ? adjust : "");
free(adjust);
adjust = NULL;
@@ -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 1f6ab34..7f67ab0 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -31,7 +31,11 @@
cc_defaults {
name: "logd_defaults",
- shared_libs: ["libbase"],
+ shared_libs: [
+ "libbase",
+ "libz",
+ ],
+ static_libs: ["libzstd"],
cflags: [
"-Wextra",
"-Wthread-safety",
@@ -40,6 +44,7 @@
lto: {
thin: true,
},
+ cpp_std: "experimental",
}
cc_library_static {
@@ -48,12 +53,17 @@
host_supported: true,
srcs: [
"ChattyLogBuffer.cpp",
+ "CompressionEngine.cpp",
"LogReaderList.cpp",
"LogReaderThread.cpp",
"LogBufferElement.cpp",
+ "LogSize.cpp",
"LogStatistics.cpp",
- "LogWhiteBlackList.cpp",
"LogTags.cpp",
+ "PruneList.cpp",
+ "SerializedFlushToState.cpp",
+ "SerializedLogBuffer.cpp",
+ "SerializedLogChunk.cpp",
"SimpleLogBuffer.cpp",
],
logtags: ["event.logtags"],
@@ -132,6 +142,8 @@
"ChattyLogBufferTest.cpp",
"logd_test.cpp",
"LogBufferTest.cpp",
+ "SerializedLogChunkTest.cpp",
+ "SerializedFlushToStateTest.cpp",
],
static_libs: [
@@ -140,6 +152,8 @@
"liblog",
"liblogd",
"libselinux",
+ "libz",
+ "libzstd",
],
}
@@ -164,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.cpp b/logd/ChattyLogBuffer.cpp
index f92fe65..fd183e4 100644
--- a/logd/ChattyLogBuffer.cpp
+++ b/logd/ChattyLogBuffer.cpp
@@ -298,36 +298,39 @@
// invariably move the logs value down faster as less chatty sources would be
// expired in the noise.
//
-// The first loop performs blacklisting and worst offender pruning. Falling
-// through when there are no notable worst offenders and have not hit the
-// region lock preventing further worst offender pruning. This loop also looks
-// after managing the chatty log entries and merging to help provide
-// statistical basis for blame. The chatty entries are not a notification of
-// how much logs you may have, but instead represent how much logs you would
-// have had in a virtual log buffer that is extended to cover all the in-memory
-// logs without loss. They last much longer than the represented pruned logs
-// since they get multiplied by the gains in the non-chatty log sources.
+// The first pass prunes elements that match 3 possible rules:
+// 1) A high priority prune rule, for example ~100/20, which indicates elements from UID 100 and PID
+// 20 should be pruned in this first pass.
+// 2) The default chatty pruning rule, ~!. This rule sums the total size spent on log messages for
+// each UID this log buffer. If the highest sum consumes more than 12.5% of the log buffer, then
+// these elements from that UID are pruned.
+// 3) The default AID_SYSTEM pruning rule, ~1000/!. This rule is a special case to 2), if
+// AID_SYSTEM is the top consumer of the log buffer, then this rule sums the total size spent on
+// log messages for each PID in AID_SYSTEM in this log buffer and prunes elements from the PID
+// with the highest sum.
+// This pass reevaluates the sums for rules 2) and 3) for every log message pruned. It creates
+// 'chatty' entries for the elements that it prunes and merges related chatty entries together. It
+// completes when one of three conditions have been met:
+// 1) The requested element count has been pruned.
+// 2) There are no elements that match any of these rules.
+// 3) A reader is referencing the oldest element that would match these rules.
//
-// The second loop get complicated because an algorithm of watermarks and
-// history is maintained to reduce the order and keep processing time
-// down to a minimum at scale. These algorithms can be costly in the face
-// of larger log buffers, or severly limited processing time granted to a
-// background task at lowest priority.
+// The second pass prunes elements starting from the beginning of the log. It skips elements that
+// match any low priority prune rules. It completes when one of three conditions have been met:
+// 1) The requested element count has been pruned.
+// 2) All elements except those mwatching low priority prune rules have been pruned.
+// 3) A reader is referencing the oldest element that would match these rules.
//
-// This second loop does straight-up expiration from the end of the logs
-// (again, remember for the specified log buffer id) but does some whitelist
-// preservation. Thus whitelist is a Hail Mary low priority, blacklists and
-// spam filtration all take priority. This second loop also checks if a region
-// lock is causing us to buffer too much in the logs to help the reader(s),
-// and will tell the slowest reader thread to skip log entries, and if
-// persistent and hits a further threshold, kill the reader thread.
+// The final pass only happens if there are any low priority prune rules and if the first two passes
+// were unable to prune the requested number of elements. It prunes elements all starting from the
+// beginning of the log, regardless of if they match any low priority prune rules.
//
-// The third thread is optional, and only gets hit if there was a whitelist
-// and more needs to be pruned against the backstop of the region lock.
-//
+// If the requested number of logs was unable to be pruned, KickReader() is called to mitigate the
+// situation before the next call to Prune() and the function returns false. Otherwise, if the
+// requested number of logs or all logs present in the buffer are pruned, in the case of Clear(),
+// it returns true.
bool ChattyLogBuffer::Prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
LogReaderThread* oldest = nullptr;
- bool busy = false;
bool clearAll = pruneRows == ULONG_MAX;
auto reader_threads_lock = std::lock_guard{reader_list()->reader_threads_lock()};
@@ -359,21 +362,20 @@
}
if (oldest && oldest->start() <= element.sequence()) {
- busy = true;
KickReader(oldest, id, pruneRows);
- break;
+ return false;
}
it = Erase(it);
if (--pruneRows == 0) {
- break;
+ return true;
}
}
- return busy;
+ return true;
}
- // prune by worst offenders; by blacklist, UID, and by PID of system UID
- bool hasBlacklist = (id != LOG_ID_SECURITY) && prune_->naughty();
+ // First prune pass.
+ bool check_high_priority = id != LOG_ID_SECURITY && prune_->HasHighPriorityPruneRules();
while (!clearAll && (pruneRows > 0)) {
// recalculate the worst offender on every batched pass
int worst = -1; // not valid for uid() or getKey()
@@ -381,7 +383,7 @@
size_t second_worst_sizes = 0;
pid_t worstPid = 0; // POSIX guarantees PID != 0
- if (worstUidEnabledForLogid(id) && prune_->worstUidEnabled()) {
+ if (worstUidEnabledForLogid(id) && prune_->worst_uid_enabled()) {
// Calculate threshold as 12.5% of available storage
size_t threshold = max_size(id) / 8;
@@ -391,14 +393,14 @@
} else {
stats()->WorstTwoUids(id, threshold, &worst, &worst_sizes, &second_worst_sizes);
- if (worst == AID_SYSTEM && prune_->worstPidOfSystemEnabled()) {
+ if (worst == AID_SYSTEM && prune_->worst_pid_of_system_enabled()) {
stats()->WorstTwoSystemPids(id, worst_sizes, &worstPid, &second_worst_sizes);
}
}
}
- // skip if we have neither worst nor naughty filters
- if ((worst == -1) && !hasBlacklist) {
+ // skip if we have neither a worst UID or high priority prune rules
+ if (worst == -1 && !check_high_priority) {
break;
}
@@ -440,7 +442,6 @@
LogBufferElement& element = *it;
if (oldest && oldest->start() <= element.sequence()) {
- busy = true;
// Do not let chatty eliding trigger any reader mitigation
break;
}
@@ -467,7 +468,7 @@
int key = (id == LOG_ID_EVENTS || id == LOG_ID_SECURITY) ? element.GetTag()
: element.uid();
- if (hasBlacklist && prune_->naughty(&element)) {
+ if (check_high_priority && prune_->IsHighPriority(&element)) {
last.clear(&element);
it = Erase(it);
if (dropped) {
@@ -560,15 +561,17 @@
}
last.clear();
- if (!kick || !prune_->worstUidEnabled()) {
+ if (!kick || !prune_->worst_uid_enabled()) {
break; // the following loop will ask bad clients to skip/drop
}
}
- bool whitelist = false;
- bool hasWhitelist = (id != LOG_ID_SECURITY) && prune_->nice() && !clearAll;
+ // Second prune pass.
+ bool skipped_low_priority_prune = false;
+ bool check_low_priority =
+ id != LOG_ID_SECURITY && prune_->HasLowPriorityPruneRules() && !clearAll;
it = GetOldest(id);
- while ((pruneRows > 0) && (it != logs().end())) {
+ while (pruneRows > 0 && it != logs().end()) {
LogBufferElement& element = *it;
if (element.log_id() != id) {
@@ -577,14 +580,12 @@
}
if (oldest && oldest->start() <= element.sequence()) {
- busy = true;
- if (!whitelist) KickReader(oldest, id, pruneRows);
+ if (!skipped_low_priority_prune) KickReader(oldest, id, pruneRows);
break;
}
- if (hasWhitelist && !element.dropped_count() && prune_->nice(&element)) {
- // WhiteListed
- whitelist = true;
+ if (check_low_priority && !element.dropped_count() && prune_->IsLowPriority(&element)) {
+ skipped_low_priority_prune = true;
it++;
continue;
}
@@ -593,10 +594,10 @@
pruneRows--;
}
- // Do not save the whitelist if we are reader range limited
- if (whitelist && (pruneRows > 0)) {
+ // Third prune pass.
+ if (skipped_low_priority_prune && pruneRows > 0) {
it = GetOldest(id);
- while ((it != logs().end()) && (pruneRows > 0)) {
+ while (it != logs().end() && pruneRows > 0) {
LogBufferElement& element = *it;
if (element.log_id() != id) {
@@ -605,7 +606,6 @@
}
if (oldest && oldest->start() <= element.sequence()) {
- busy = true;
KickReader(oldest, id, pruneRows);
break;
}
@@ -615,5 +615,5 @@
}
}
- return (pruneRows > 0) && busy;
+ return pruneRows == 0 || it == logs().end();
}
diff --git a/logd/ChattyLogBuffer.h b/logd/ChattyLogBuffer.h
index 6f60272..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"
@@ -33,8 +32,8 @@
#include "LogReaderThread.h"
#include "LogStatistics.h"
#include "LogTags.h"
-#include "LogWhiteBlackList.h"
#include "LogWriter.h"
+#include "PruneList.h"
#include "SimpleLogBuffer.h"
#include "rwlock.h"
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 9764c44..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) ? "busy" : "success");
- 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)) {
@@ -215,15 +190,13 @@
return 0;
}
-int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
- int /*argc*/, char** /*argv*/) {
+int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli, int, char**) {
setname();
- cli->sendMsg(PackageString(prune()->format()).c_str());
+ cli->sendMsg(PackageString(prune()->Format()).c_str());
return 0;
}
-int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
+int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc, char** argv) {
setname();
if (!clientHasLogCredentials(cli)) {
cli->sendMsg("Permission Denied");
@@ -238,20 +211,16 @@
str += argv[i];
}
- int ret = prune()->init(str.c_str());
-
- if (ret) {
+ if (!prune()->Init(str.c_str())) {
cli->sendMsg("Invalid");
return 0;
}
cli->sendMsg("success");
-
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)) {
@@ -295,13 +264,12 @@
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";
buf()->Init();
- prune()->init(nullptr);
+ prune()->Init(nullptr);
// This only works on userdebug and eng devices to re-read the
// /data/misc/logd/event-log-tags file right after /data is mounted.
@@ -319,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 a55a393..8e12634 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -23,7 +23,7 @@
#include "LogListener.h"
#include "LogStatistics.h"
#include "LogTags.h"
-#include "LogWhiteBlackList.h"
+#include "PruneList.h"
class CommandListener : public FrameworkListener {
public:
@@ -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/CompressionEngine.cpp b/logd/CompressionEngine.cpp
new file mode 100644
index 0000000..da2628c
--- /dev/null
+++ b/logd/CompressionEngine.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 "CompressionEngine.h"
+
+#include <limits>
+
+#include <android-base/logging.h>
+#include <zlib.h>
+#include <zstd.h>
+
+CompressionEngine& CompressionEngine::GetInstance() {
+ static CompressionEngine* engine = new ZstdCompressionEngine();
+ return *engine;
+}
+
+bool ZlibCompressionEngine::Compress(SerializedData& in, size_t data_length, SerializedData& out) {
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ int ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
+ if (ret != Z_OK) {
+ LOG(FATAL) << "deflateInit() failed";
+ }
+
+ CHECK_LE(data_length, in.size());
+ CHECK_LE(in.size(), std::numeric_limits<uint32_t>::max());
+ uint32_t deflate_bound = deflateBound(&strm, in.size());
+
+ out.Resize(deflate_bound);
+
+ strm.avail_in = data_length;
+ strm.next_in = in.data();
+ strm.avail_out = out.size();
+ strm.next_out = out.data();
+ ret = deflate(&strm, Z_FINISH);
+ CHECK_EQ(ret, Z_STREAM_END);
+
+ uint32_t compressed_size = strm.total_out;
+ deflateEnd(&strm);
+
+ out.Resize(compressed_size);
+
+ return true;
+}
+
+bool ZlibCompressionEngine::Decompress(SerializedData& in, SerializedData& out) {
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = in.size();
+ strm.next_in = in.data();
+ strm.avail_out = out.size();
+ strm.next_out = out.data();
+
+ inflateInit(&strm);
+ int ret = inflate(&strm, Z_NO_FLUSH);
+
+ CHECK_EQ(strm.avail_in, 0U);
+ CHECK_EQ(strm.avail_out, 0U);
+ CHECK_EQ(ret, Z_STREAM_END);
+ inflateEnd(&strm);
+
+ return true;
+}
+
+bool ZstdCompressionEngine::Compress(SerializedData& in, size_t data_length, SerializedData& out) {
+ CHECK_LE(data_length, in.size());
+
+ size_t compress_bound = ZSTD_compressBound(data_length);
+ out.Resize(compress_bound);
+
+ size_t out_size = ZSTD_compress(out.data(), out.size(), in.data(), data_length, 1);
+ if (ZSTD_isError(out_size)) {
+ LOG(FATAL) << "ZSTD_compress failed: " << ZSTD_getErrorName(out_size);
+ }
+ out.Resize(out_size);
+
+ return true;
+}
+
+bool ZstdCompressionEngine::Decompress(SerializedData& in, SerializedData& out) {
+ size_t result = ZSTD_decompress(out.data(), out.size(), in.data(), in.size());
+ if (ZSTD_isError(result)) {
+ LOG(FATAL) << "ZSTD_decompress failed: " << ZSTD_getErrorName(result);
+ }
+ CHECK_EQ(result, out.size());
+ return true;
+}
diff --git a/logd/CompressionEngine.h b/logd/CompressionEngine.h
new file mode 100644
index 0000000..0f760ed
--- /dev/null
+++ b/logd/CompressionEngine.h
@@ -0,0 +1,45 @@
+/*
+ * 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 <memory>
+
+#include "SerializedData.h"
+
+class CompressionEngine {
+ public:
+ static CompressionEngine& GetInstance();
+
+ virtual ~CompressionEngine(){};
+
+ virtual bool Compress(SerializedData& in, size_t data_length, SerializedData& out) = 0;
+ // Decompress the contents of `in` into `out`. `out.size()` must be set to the decompressed
+ // size of the contents.
+ virtual bool Decompress(SerializedData& in, SerializedData& out) = 0;
+};
+
+class ZlibCompressionEngine : public CompressionEngine {
+ public:
+ bool Compress(SerializedData& in, size_t data_length, SerializedData& out) override;
+ bool Decompress(SerializedData& in, SerializedData& out) override;
+};
+
+class ZstdCompressionEngine : public CompressionEngine {
+ public:
+ bool Compress(SerializedData& in, size_t data_length, SerializedData& out) override;
+ bool Decompress(SerializedData& in, SerializedData& out) override;
+};
\ No newline at end of file
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/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index ef9f1cf..26affa8 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -61,7 +61,7 @@
}
}
-LogBufferElement::LogBufferElement(LogBufferElement&& elem)
+LogBufferElement::LogBufferElement(LogBufferElement&& elem) noexcept
: uid_(elem.uid_),
pid_(elem.pid_),
tid_(elem.tid_),
@@ -99,6 +99,10 @@
}
LogStatisticsElement LogBufferElement::ToLogStatisticsElement() const {
+ // Estimate the size of this element in the parent std::list<> by adding two void*'s
+ // corresponding to the next/prev pointers and aligning to 64 bit.
+ uint16_t element_in_list_size =
+ (sizeof(*this) + 2 * sizeof(void*) + sizeof(uint64_t) - 1) & -sizeof(uint64_t);
return LogStatisticsElement{
.uid = uid(),
.pid = pid(),
@@ -109,6 +113,7 @@
.msg_len = msg_len(),
.dropped_count = dropped_count(),
.log_id = log_id(),
+ .total_len = static_cast<uint16_t>(element_in_list_size + msg_len()),
};
}
@@ -134,7 +139,7 @@
char* retval = nullptr;
char buffer[256];
snprintf(buffer, sizeof(buffer), "/proc/%u/comm", tid);
- int fd = open(buffer, O_RDONLY);
+ int fd = open(buffer, O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
ssize_t ret = read(fd, buffer, sizeof(buffer));
if (ret >= (ssize_t)sizeof(buffer)) {
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index fd5d88f..b263ca5 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -37,7 +37,7 @@
LogBufferElement(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
uint64_t sequence, const char* msg, uint16_t len);
LogBufferElement(const LogBufferElement& elem);
- LogBufferElement(LogBufferElement&& elem);
+ LogBufferElement(LogBufferElement&& elem) noexcept;
~LogBufferElement();
uint32_t GetTag() const;
diff --git a/logd/LogBufferTest.cpp b/logd/LogBufferTest.cpp
index e651b4f..1911105 100644
--- a/logd/LogBufferTest.cpp
+++ b/logd/LogBufferTest.cpp
@@ -26,6 +26,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include "LogBuffer.h"
#include "LogReaderThread.h"
#include "LogWriter.h"
@@ -33,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;
}
@@ -240,7 +231,7 @@
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
std::unique_ptr<LogReaderThread> log_reader(
new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
- 0, ~0, 0, {}, 1, {}));
+ 0, kLogMaskAll, 0, {}, 1, {}));
reader_list_.reader_threads().emplace_back(std::move(log_reader));
}
@@ -314,7 +305,7 @@
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
std::unique_ptr<LogReaderThread> log_reader(
new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
- 0, ~0, 0, {}, 1, {}));
+ 0, kLogMaskAll, 0, {}, 1, {}));
reader_list_.reader_threads().emplace_back(std::move(log_reader));
}
@@ -348,7 +339,7 @@
std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
std::unique_ptr<LogReaderThread> log_reader(
new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), true,
- 0, ~0, 0, {}, 3, {}));
+ 0, kLogMaskAll, 0, {}, 3, {}));
reader_list_.reader_threads().emplace_back(std::move(log_reader));
}
@@ -363,4 +354,96 @@
CompareLogMessages(expected_log_messages, read_log_messages);
}
-INSTANTIATE_TEST_CASE_P(LogBufferTests, LogBufferTest, testing::Values("chatty", "simple"));
+TEST_P(LogBufferTest, clear_logs) {
+ // Log 3 initial logs.
+ std::vector<LogMessage> log_messages = {
+ {{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
+ "first"},
+ {{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
+ "second"},
+ {{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_MAIN, .uid = 0},
+ "third"},
+ };
+ FixupMessages(&log_messages);
+ LogMessages(log_messages);
+
+ std::vector<LogMessage> read_log_messages;
+ bool released = false;
+
+ // Connect a blocking reader.
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages, &released));
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(test_writer), false,
+ 0, kLogMaskAll, 0, {}, 1, {}));
+ reader_list_.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ // Wait up to 250ms for the reader to read the first 3 logs.
+ constexpr int kMaxRetryCount = 50;
+ int count = 0;
+ for (; count < kMaxRetryCount; ++count) {
+ usleep(5000);
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ if (reader_list_.reader_threads().back()->start() == 4) {
+ break;
+ }
+ }
+ ASSERT_LT(count, kMaxRetryCount);
+
+ // Clear the log buffer.
+ log_buffer_->Clear(LOG_ID_MAIN, 0);
+
+ // Log 3 more logs.
+ std::vector<LogMessage> after_clear_messages = {
+ {{.pid = 1, .tid = 2, .sec = 10000, .nsec = 20001, .lid = LOG_ID_MAIN, .uid = 0},
+ "4th"},
+ {{.pid = 10, .tid = 2, .sec = 10000, .nsec = 20002, .lid = LOG_ID_MAIN, .uid = 0},
+ "5th"},
+ {{.pid = 100, .tid = 2, .sec = 10000, .nsec = 20003, .lid = LOG_ID_MAIN, .uid = 0},
+ "6th"},
+ };
+ FixupMessages(&after_clear_messages);
+ LogMessages(after_clear_messages);
+
+ // Wait up to 250ms for the reader to read the 3 additional logs.
+ for (count = 0; count < kMaxRetryCount; ++count) {
+ usleep(5000);
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ if (reader_list_.reader_threads().back()->start() == 7) {
+ break;
+ }
+ }
+ ASSERT_LT(count, kMaxRetryCount);
+
+ // Release the reader, wait for it to get the signal then check that it has been deleted.
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ reader_list_.reader_threads().back()->release_Locked();
+ }
+ while (!released) {
+ usleep(5000);
+ }
+ {
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ EXPECT_EQ(0U, reader_list_.reader_threads().size());
+ }
+
+ // Check that we have read all 6 messages.
+ std::vector<LogMessage> expected_log_messages = log_messages;
+ expected_log_messages.insert(expected_log_messages.end(), after_clear_messages.begin(),
+ after_clear_messages.end());
+ CompareLogMessages(expected_log_messages, read_log_messages);
+
+ // Finally, call FlushTo and ensure that only the 3 logs after the clear remain in the buffer.
+ std::vector<LogMessage> read_log_messages_after_clear;
+ std::unique_ptr<LogWriter> test_writer(new TestWriter(&read_log_messages_after_clear, nullptr));
+ std::unique_ptr<FlushToState> flush_to_state = log_buffer_->CreateFlushToState(1, kLogMaskAll);
+ EXPECT_TRUE(log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr));
+ EXPECT_EQ(7ULL, flush_to_state->start());
+ CompareLogMessages(after_clear_messages, read_log_messages_after_clear);
+}
+
+INSTANTIATE_TEST_CASE_P(LogBufferTests, LogBufferTest,
+ testing::Values("chatty", "serialized", "simple"));
diff --git a/logd/LogBufferTest.h b/logd/LogBufferTest.h
index f91a1b5..eeeb980 100644
--- a/logd/LogBufferTest.h
+++ b/logd/LogBufferTest.h
@@ -25,7 +25,8 @@
#include "LogReaderList.h"
#include "LogStatistics.h"
#include "LogTags.h"
-#include "LogWhiteBlackList.h"
+#include "PruneList.h"
+#include "SerializedLogBuffer.h"
#include "SimpleLogBuffer.h"
struct LogMessage {
@@ -67,11 +68,15 @@
void SetUp() override {
if (GetParam() == "chatty") {
log_buffer_.reset(new ChattyLogBuffer(&reader_list_, &tags_, &prune_, &stats_));
+ } else if (GetParam() == "serialized") {
+ log_buffer_.reset(new SerializedLogBuffer(&reader_list_, &tags_, &stats_));
} else if (GetParam() == "simple") {
log_buffer_.reset(new SimpleLogBuffer(&reader_list_, &tags_, &stats_));
} 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) {
@@ -84,6 +89,6 @@
LogReaderList reader_list_;
LogTags tags_;
PruneList prune_;
- LogStatistics stats_{false};
+ LogStatistics stats_{false, true};
std::unique_ptr<LogBuffer> log_buffer_;
};
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 1ea87a9..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
@@ -665,10 +665,9 @@
((size == 2) && (isdigit(tag[0]) || isdigit(tag[1]))) ||
// register names like x18 but not driver names like en0
((size == 3) && (isdigit(tag[1]) && isdigit(tag[2]))) ||
- // blacklist
+ // ignore
((size == cpuLen) && !fastcmp<strncmp>(tag, cpu, cpuLen)) ||
- ((size == warningLen) &&
- !fastcmp<strncasecmp>(tag, warning, warningLen)) ||
+ ((size == warningLen) && !fastcmp<strncasecmp>(tag, warning, warningLen)) ||
((size == errorLen) && !fastcmp<strncasecmp>(tag, error, errorLen)) ||
((size == infoLen) && !fastcmp<strncasecmp>(tag, info, infoLen))) {
p = start;
@@ -704,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;
}
@@ -713,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;
}
@@ -723,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/LogListener.h b/logd/LogListener.h
index c114e38..566af5b 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -20,7 +20,7 @@
class LogListener {
public:
- LogListener(LogBuffer* buf);
+ explicit LogListener(LogBuffer* buf);
bool StartListener();
private:
diff --git a/logd/LogPermissions.cpp b/logd/LogPermissions.cpp
index 8f02d5a..3fd0ea1 100644
--- a/logd/LogPermissions.cpp
+++ b/logd/LogPermissions.cpp
@@ -89,7 +89,7 @@
//
for (int retry = 3; !(ret = foundGid && foundUid && foundLog) && retry;
--retry) {
- FILE* file = fopen(filename, "r");
+ FILE* file = fopen(filename, "re");
if (!file) {
continue;
}
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index f71133d..6c97693 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -127,7 +127,7 @@
if (cp) {
logMask = 0;
cp += sizeof(_logIds) - 1;
- while (*cp && *cp != '\0') {
+ while (*cp != '\0') {
int val = 0;
while (isdigit(*cp)) {
val = val * 10 + *cp - '0';
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.cpp b/logd/LogReaderThread.cpp
index dc8582d..4a8be01 100644
--- a/logd/LogReaderThread.cpp
+++ b/logd/LogReaderThread.cpp
@@ -25,8 +25,6 @@
#include "LogBuffer.h"
#include "LogReaderList.h"
-using namespace std::placeholders;
-
LogReaderThread::LogReaderThread(LogBuffer* log_buffer, LogReaderList* reader_list,
std::unique_ptr<LogWriter> writer, bool non_block,
unsigned long tail, LogMask log_mask, pid_t pid,
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
index bf70b94..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"
@@ -60,6 +59,7 @@
std::string name() const { return writer_->name(); }
uint64_t start() const { return flush_to_state_->start(); }
std::chrono::steady_clock::time_point deadline() const { return deadline_; }
+ FlushToState& flush_to_state() { return *flush_to_state_; }
private:
void ThreadFunction();
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 d49982a..87069b3 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -26,7 +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"
@@ -60,7 +63,9 @@
return std::string(msg, len);
}
-LogStatistics::LogStatistics(bool enable_statistics) : enable(enable_statistics) {
+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) {
mSizes[id] = 0;
@@ -68,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;
}
}
@@ -88,7 +98,7 @@
} else {
char buffer[512];
snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
- int fd = open(buffer, O_RDONLY);
+ int fd = open(buffer, O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
ssize_t ret = read(fd, buffer, sizeof(buffer));
if (ret > 0) {
@@ -113,10 +123,15 @@
++mElementsTotal[log_id];
}
-void LogStatistics::Add(const LogStatisticsElement& element) {
+void LogStatistics::Add(LogStatisticsElement element) {
auto lock = std::lock_guard{lock_};
+
+ if (!track_total_size_) {
+ element.total_len = element.msg_len;
+ }
+
log_id_t log_id = element.log_id;
- uint16_t size = element.msg_len;
+ uint16_t size = element.total_len;
mSizes[log_id] += size;
++mElements[log_id];
@@ -184,10 +199,15 @@
}
}
-void LogStatistics::Subtract(const LogStatisticsElement& element) {
+void LogStatistics::Subtract(LogStatisticsElement element) {
auto lock = std::lock_guard{lock_};
+
+ if (!track_total_size_) {
+ element.total_len = element.msg_len;
+ }
+
log_id_t log_id = element.log_id;
- uint16_t size = element.msg_len;
+ uint16_t size = element.total_len;
mSizes[log_id] -= size;
--mElements[log_id];
if (element.dropped_count) {
@@ -230,7 +250,9 @@
// Atomically set an entry to drop
// entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::Drop(const LogStatisticsElement& element) {
+void LogStatistics::Drop(LogStatisticsElement element) {
+ CHECK_EQ(element.dropped_count, 0U);
+
auto lock = std::lock_guard{lock_};
log_id_t log_id = element.log_id;
uint16_t size = element.msg_len;
@@ -265,6 +287,43 @@
tagNameTable.Subtract(TagNameKey(element), element);
}
+void LogStatistics::Erase(LogStatisticsElement element) {
+ CHECK_GT(element.dropped_count, 0U);
+ CHECK_EQ(element.msg_len, 0U);
+
+ auto lock = std::lock_guard{lock_};
+
+ if (!track_total_size_) {
+ element.total_len = 0;
+ }
+
+ log_id_t log_id = element.log_id;
+ --mElements[log_id];
+ --mDroppedElements[log_id];
+ mSizes[log_id] -= element.total_len;
+
+ uidTable[log_id].Erase(element.uid, element);
+ if (element.uid == AID_SYSTEM) {
+ pidSystemTable[log_id].Erase(element.pid, element);
+ }
+
+ if (!enable) {
+ return;
+ }
+
+ pidTable.Erase(element.pid, element);
+ tidTable.Erase(element.tid, element);
+
+ uint32_t tag = element.tag;
+ if (tag) {
+ if (log_id == LOG_ID_SECURITY) {
+ securityTagTable.Erase(tag, element);
+ } else {
+ tagTable.Erase(tag, element);
+ }
+ }
+}
+
const char* LogStatistics::UidToName(uid_t uid) const {
auto lock = std::lock_guard{lock_};
return UidToNameLocked(uid);
@@ -733,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_};
@@ -876,12 +960,20 @@
if (els) {
oldLength = output.length();
if (spaces < 0) spaces = 0;
- // estimate the std::list overhead.
- static const size_t overhead =
- ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) &
- -sizeof(uint64_t)) +
- sizeof(std::list<LogBufferElement*>);
- size_t szs = mSizes[id] + els * overhead;
+ size_t szs = 0;
+ if (overhead_[id]) {
+ szs = *overhead_[id];
+ } else if (track_total_size_) {
+ szs = mSizes[id];
+ } else {
+ // Legacy fallback for Chatty without track_total_size_
+ // Estimate the size of this element in the parent std::list<> by adding two void*'s
+ // corresponding to the next/prev pointers and aligning to 64 bit.
+ static const size_t overhead =
+ (sizeof(LogBufferElement) + 2 * sizeof(void*) + sizeof(uint64_t) - 1) &
+ -sizeof(uint64_t);
+ szs = mSizes[id] + els * overhead;
+ }
totalSize += szs;
output += android::base::StringPrintf("%*s%zu", spaces, "", szs);
spaces -= output.length() - oldLength;
@@ -944,7 +1036,7 @@
uid_t pidToUid(pid_t pid) {
char buffer[512];
snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid);
- FILE* fp = fopen(buffer, "r");
+ FILE* fp = fopen(buffer, "re");
if (fp) {
while (fgets(buffer, sizeof(buffer), fp)) {
int uid = AID_LOGD;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 200c228..faf9283 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -57,6 +57,7 @@
uint16_t msg_len;
uint16_t dropped_count;
log_id_t log_id;
+ uint16_t total_len;
};
template <typename TKey, typename TEntry>
@@ -128,9 +129,9 @@
if (++index < (ssize_t)len) {
size_t num = len - index - 1;
if (num) {
- memmove(&out_keys[index + 1], &out_keys[index], num * sizeof(out_keys[0]));
+ memmove(&out_keys[index + 1], &out_keys[index], num * sizeof(const TKey*));
memmove(&out_entries[index + 1], &out_entries[index],
- num * sizeof(out_entries[0]));
+ num * sizeof(const TEntry*));
}
out_keys[index] = &key;
out_entries[index] = &entry;
@@ -172,6 +173,13 @@
}
}
+ void Erase(const TKey& key, const LogStatisticsElement& element) {
+ iterator it = map.find(key);
+ if (it != map.end()) {
+ it->second.Erase(element);
+ }
+ }
+
iterator begin() { return map.begin(); }
const_iterator begin() const { return map.begin(); }
iterator end() { return map.end(); }
@@ -181,15 +189,17 @@
class EntryBase {
public:
EntryBase() : size_(0) {}
- explicit EntryBase(const LogStatisticsElement& element) : size_(element.msg_len) {}
+ explicit EntryBase(const LogStatisticsElement& element) : size_(element.total_len) {}
size_t getSizes() const { return size_; }
- void Add(const LogStatisticsElement& element) { size_ += element.msg_len; }
+ void Add(const LogStatisticsElement& element) { size_ += element.total_len; }
bool Subtract(const LogStatisticsElement& element) {
- size_ -= element.msg_len;
- return !size_;
+ size_ -= element.total_len;
+ return size_ == 0;
}
+ void Drop(const LogStatisticsElement& element) { size_ -= element.msg_len; }
+ void Erase(const LogStatisticsElement& element) { size_ -= element.total_len; }
static constexpr size_t PRUNED_LEN = 14;
static constexpr size_t TOTAL_LEN = 80;
@@ -229,11 +239,11 @@
}
bool Subtract(const LogStatisticsElement& element) {
dropped_ -= element.dropped_count;
- return EntryBase::Subtract(element) && !dropped_;
+ return EntryBase::Subtract(element) && dropped_ == 0;
}
void Drop(const LogStatisticsElement& element) {
dropped_ += 1;
- EntryBase::Subtract(element);
+ EntryBase::Drop(element);
}
private:
@@ -477,15 +487,15 @@
tagNameTable.sizeOf() +
(pidTable.size() * sizeof(pidTable_t::iterator)) +
(tagTable.size() * sizeof(tagTable_t::iterator));
- for (auto it : pidTable) {
+ for (const auto& it : pidTable) {
const char* name = it.second.name();
if (name) size += strlen(name) + 1;
}
- for (auto it : tidTable) {
+ for (const auto& it : tidTable) {
const char* name = it.second.name();
if (name) size += strlen(name) + 1;
}
- for (auto it : tagNameTable) {
+ for (const auto& it : tagNameTable) {
size += sizeof(std::string);
size_t len = it.first.size();
// Account for short string optimization: if the string's length is <= 22 bytes for 64
@@ -505,20 +515,24 @@
}
public:
- LogStatistics(bool enable_statistics);
+ 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_);
- void Add(const LogStatisticsElement& entry) EXCLUDES(lock_);
- void Subtract(const LogStatisticsElement& entry) EXCLUDES(lock_);
- // entry->setDropped(1) must follow this call
- void Drop(const LogStatisticsElement& entry) EXCLUDES(lock_);
- // Correct for coalescing two entries referencing dropped content
- void Erase(const LogStatisticsElement& element) EXCLUDES(lock_) {
- auto lock = std::lock_guard{lock_};
- log_id_t log_id = element.log_id;
- --mElements[log_id];
- --mDroppedElements[log_id];
- }
+
+ // Add is for adding an element to the log buffer. It may be a chatty element in the case of
+ // log deduplication. Add the total size of the element to statistics.
+ void Add(LogStatisticsElement entry) EXCLUDES(lock_);
+ // Subtract is for removing an element from the log buffer. It may be a chatty element.
+ // Subtract the total size of the element from statistics.
+ void Subtract(LogStatisticsElement entry) EXCLUDES(lock_);
+ // Drop is for converting a normal element into a chatty element. entry->setDropped(1) must
+ // follow this call. Subtract only msg_len from statistics, since a chatty element will remain.
+ void Drop(LogStatisticsElement entry) EXCLUDES(lock_);
+ // Erase is for coalescing two chatty elements into one. Erase() is called on the element that
+ // is removed from the log buffer. Subtract the total size of the element, which is by
+ // definition only the size of the LogBufferElement + list overhead for chatty elements.
+ void Erase(LogStatisticsElement element) EXCLUDES(lock_);
void WorstTwoUids(log_id id, size_t threshold, int* worst, size_t* worst_sizes,
size_t* second_worst_sizes) const EXCLUDES(lock_);
@@ -530,22 +544,38 @@
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]) {
+ return *overhead_[id];
+ }
+ 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_);
uid_t PidToUid(pid_t pid) EXCLUDES(lock_);
const char* UidToName(uid_t uid) const EXCLUDES(lock_);
+ void set_overhead(log_id_t id, size_t size) {
+ auto lock = std::lock_guard{lock_};
+ overhead_[id] = size;
+ }
+
private:
template <typename TKey, typename TEntry>
void WorstTwoWithThreshold(const LogHashtable<TKey, TEntry>& table, size_t threshold,
@@ -559,4 +589,7 @@
const char* UidToNameLocked(uid_t uid) const REQUIRES(lock_);
mutable std::mutex lock_;
+ bool track_total_size_;
+
+ std::optional<size_t> overhead_[LOG_ID_MAX] GUARDED_BY(lock_);
};
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 8e18f9d..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>
@@ -276,7 +277,9 @@
cp++;
}
} else if (warn) {
+#ifdef __ANDROID__
LOG(ERROR) << "Cannot read " << filename;
+#endif
}
}
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/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
deleted file mode 100644
index 88a3bdc..0000000
--- a/logd/LogWhiteBlackList.cpp
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2014 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 <ctype.h>
-
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-
-#include "LogWhiteBlackList.h"
-
-Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) {
-}
-
-int Prune::cmp(uid_t uid, pid_t pid) const {
- if ((mUid == uid_all) || (mUid == uid)) {
- if (mPid == pid_all) {
- return 0;
- }
- return pid - mPid;
- }
- return uid - mUid;
-}
-
-std::string Prune::format() {
- if (mUid != uid_all) {
- if (mPid != pid_all) {
- return android::base::StringPrintf("%u/%u", mUid, mPid);
- }
- return android::base::StringPrintf("%u", mUid);
- }
- if (mPid != pid_all) {
- return android::base::StringPrintf("/%u", mPid);
- }
- // NB: mPid == pid_all can not happen if mUid == uid_all
- return std::string("/");
-}
-
-PruneList::PruneList() {
- init(nullptr);
-}
-
-PruneList::~PruneList() {
- PruneCollection::iterator it;
- for (it = mNice.begin(); it != mNice.end();) {
- it = mNice.erase(it);
- }
- for (it = mNaughty.begin(); it != mNaughty.end();) {
- it = mNaughty.erase(it);
- }
-}
-
-int PruneList::init(const char* str) {
- mWorstUidEnabled = true;
- mWorstPidOfSystemEnabled = true;
- PruneCollection::iterator it;
- for (it = mNice.begin(); it != mNice.end();) {
- it = mNice.erase(it);
- }
- for (it = mNaughty.begin(); it != mNaughty.end();) {
- it = mNaughty.erase(it);
- }
-
- // default here means take ro.logd.filter, persist.logd.filter then
- // internal default in that order.
- if (str && !strcmp(str, "default")) {
- str = nullptr;
- }
- if (str && !strcmp(str, "disable")) {
- str = "";
- }
-
- std::string filter;
-
- if (str) {
- filter = str;
- } else {
- filter = android::base::GetProperty("ro.logd.filter", "default");
- auto persist_filter = android::base::GetProperty("persist.logd.filter", "default");
- // default here means take ro.logd.filter
- if (persist_filter != "default") {
- filter = persist_filter;
- }
- }
-
- // default here means take internal default.
- if (filter == "default") {
- // See README.property for description of filter format
- filter = "~! ~1000/!";
- }
- if (filter == "disable") {
- filter = "";
- }
-
- mWorstUidEnabled = false;
- mWorstPidOfSystemEnabled = false;
-
- for (str = filter.c_str(); *str; ++str) {
- if (isspace(*str)) {
- continue;
- }
-
- PruneCollection* list;
- if ((*str == '~') || (*str == '!')) { // ~ supported, ! undocumented
- ++str;
- // special case, translates to worst UID at priority in blacklist
- if (*str == '!') {
- mWorstUidEnabled = true;
- ++str;
- if (!*str) {
- break;
- }
- if (!isspace(*str)) {
- return 1;
- }
- continue;
- }
- // special case, translated to worst PID of System at priority
- static const char worstPid[] = "1000/!";
- if (!strncmp(str, worstPid, sizeof(worstPid) - 1)) {
- mWorstPidOfSystemEnabled = true;
- str += sizeof(worstPid) - 1;
- if (!*str) {
- break;
- }
- if (!isspace(*str)) {
- return 1;
- }
- continue;
- }
- if (!*str) {
- return 1;
- }
- list = &mNaughty;
- } else {
- list = &mNice;
- }
-
- uid_t uid = Prune::uid_all;
- if (isdigit(*str)) {
- uid = 0;
- do {
- uid = uid * 10 + *str++ - '0';
- } while (isdigit(*str));
- }
-
- pid_t pid = Prune::pid_all;
- if (*str == '/') {
- ++str;
- if (isdigit(*str)) {
- pid = 0;
- do {
- pid = pid * 10 + *str++ - '0';
- } while (isdigit(*str));
- }
- }
-
- if ((uid == Prune::uid_all) && (pid == Prune::pid_all)) {
- return 1;
- }
-
- if (*str && !isspace(*str)) {
- return 1;
- }
-
- // insert sequentially into list
- PruneCollection::iterator it = list->begin();
- while (it != list->end()) {
- Prune& p = *it;
- int m = uid - p.mUid;
- if (m == 0) {
- if (p.mPid == p.pid_all) {
- break;
- }
- if ((pid == p.pid_all) && (p.mPid != p.pid_all)) {
- it = list->erase(it);
- continue;
- }
- m = pid - p.mPid;
- }
- if (m <= 0) {
- if (m < 0) {
- list->insert(it, Prune(uid, pid));
- }
- break;
- }
- ++it;
- }
- if (it == list->end()) {
- list->push_back(Prune(uid, pid));
- }
- if (!*str) {
- break;
- }
- }
-
- return 0;
-}
-
-std::string PruneList::format() {
- static const char nice_format[] = " %s";
- const char* fmt = nice_format + 1;
-
- std::string string;
-
- if (mWorstUidEnabled) {
- string = "~!";
- fmt = nice_format;
- if (mWorstPidOfSystemEnabled) {
- string += " ~1000/!";
- }
- }
-
- PruneCollection::iterator it;
-
- for (it = mNice.begin(); it != mNice.end(); ++it) {
- string += android::base::StringPrintf(fmt, (*it).format().c_str());
- fmt = nice_format;
- }
-
- static const char naughty_format[] = " ~%s";
- fmt = naughty_format + (*fmt != ' ');
- for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
- string += android::base::StringPrintf(fmt, (*it).format().c_str());
- fmt = naughty_format;
- }
-
- return string;
-}
-
-// ToDo: Lists are in sorted order, Prune->cmp() returns + or -
-// If there is scaling issues, resort to a better algorithm than linear
-// based on these assumptions.
-
-bool PruneList::naughty(LogBufferElement* element) {
- PruneCollection::iterator it;
- for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
- if (!(*it).cmp(element)) {
- return true;
- }
- }
- return false;
-}
-
-bool PruneList::nice(LogBufferElement* element) {
- PruneCollection::iterator it;
- for (it = mNice.begin(); it != mNice.end(); ++it) {
- if (!(*it).cmp(element)) {
- return true;
- }
- }
- return false;
-}
diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h
deleted file mode 100644
index 447ab87..0000000
--- a/logd/LogWhiteBlackList.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2014 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 <sys/types.h>
-
-#include <string.h>
-#include <list>
-
-#include "LogBufferElement.h"
-
-class Prune {
- friend class PruneList;
-
- const uid_t mUid;
- const pid_t mPid;
- int cmp(uid_t uid, pid_t pid) const;
-
- public:
- static const uid_t uid_all = (uid_t)-1;
- static const pid_t pid_all = (pid_t)-1;
-
- Prune(uid_t uid, pid_t pid);
-
- uid_t getUid() const {
- return mUid;
- }
- pid_t getPid() const {
- return mPid;
- }
-
- int cmp(LogBufferElement* e) const { return cmp(e->uid(), e->pid()); }
-
- std::string format();
-};
-
-typedef std::list<Prune> PruneCollection;
-
-class PruneList {
- PruneCollection mNaughty;
- PruneCollection mNice;
- bool mWorstUidEnabled;
- bool mWorstPidOfSystemEnabled;
-
- public:
- PruneList();
- ~PruneList();
-
- int init(const char* str);
-
- bool naughty(LogBufferElement* element);
- bool naughty(void) {
- return !mNaughty.empty();
- }
- bool nice(LogBufferElement* element);
- bool nice(void) {
- return !mNice.empty();
- }
- bool worstUidEnabled() const {
- return mWorstUidEnabled;
- }
- bool worstPidOfSystemEnabled() const {
- return mWorstPidOfSystemEnabled;
- }
-
- std::string format();
-};
diff --git a/logd/PruneList.cpp b/logd/PruneList.cpp
new file mode 100644
index 0000000..c3859f3
--- /dev/null
+++ b/logd/PruneList.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2014 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 "PruneList.h"
+
+#include <ctype.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+bool Prune::Matches(LogBufferElement* element) const {
+ return (uid_ == UID_ALL || uid_ == element->uid()) &&
+ (pid_ == PID_ALL || pid_ == element->pid());
+}
+
+std::string Prune::Format() const {
+ if (uid_ != UID_ALL) {
+ if (pid_ != PID_ALL) {
+ return android::base::StringPrintf("%u/%u", uid_, pid_);
+ }
+ return android::base::StringPrintf("%u", uid_);
+ }
+ if (pid_ != PID_ALL) {
+ return android::base::StringPrintf("/%u", pid_);
+ }
+ // NB: pid_ == PID_ALL can not happen if uid_ == UID_ALL
+ return std::string("/");
+}
+
+PruneList::PruneList() {
+ Init(nullptr);
+}
+
+bool PruneList::Init(const char* str) {
+ high_priority_prune_.clear();
+ low_priority_prune_.clear();
+
+ // default here means take ro.logd.filter, persist.logd.filter then internal default in order.
+ if (str && !strcmp(str, "default")) {
+ str = nullptr;
+ }
+ if (str && !strcmp(str, "disable")) {
+ str = "";
+ }
+
+ std::string filter;
+
+ if (str) {
+ filter = str;
+ } else {
+ filter = android::base::GetProperty("ro.logd.filter", "default");
+ auto persist_filter = android::base::GetProperty("persist.logd.filter", "default");
+ // default here means take ro.logd.filter
+ if (persist_filter != "default") {
+ filter = persist_filter;
+ }
+ }
+
+ // default here means take internal default.
+ if (filter == "default") {
+ filter = "~! ~1000/!";
+ }
+ if (filter == "disable") {
+ filter = "";
+ }
+
+ worst_uid_enabled_ = false;
+ worst_pid_of_system_enabled_ = false;
+
+ for (str = filter.c_str(); *str; ++str) {
+ if (isspace(*str)) {
+ continue;
+ }
+
+ std::list<Prune>* list;
+ if (*str == '~' || *str == '!') { // ~ supported, ! undocumented
+ ++str;
+ // special case, prune the worst UID of those using at least 1/8th of the buffer.
+ if (*str == '!') {
+ worst_uid_enabled_ = true;
+ ++str;
+ if (!*str) {
+ break;
+ }
+ if (!isspace(*str)) {
+ LOG(ERROR) << "Nothing expected after '~!', but found '" << str << "'";
+ return false;
+ }
+ continue;
+ }
+ // special case, translated to worst PID of System at priority
+ static const char WORST_SYSTEM_PID[] = "1000/!";
+ if (!strncmp(str, WORST_SYSTEM_PID, sizeof(WORST_SYSTEM_PID) - 1)) {
+ worst_pid_of_system_enabled_ = true;
+ str += sizeof(WORST_SYSTEM_PID) - 1;
+ if (!*str) {
+ break;
+ }
+ if (!isspace(*str)) {
+ LOG(ERROR) << "Nothing expected after '~1000/!', but found '" << str << "'";
+ return false;
+ }
+ continue;
+ }
+ if (!*str) {
+ LOG(ERROR) << "Expected UID or PID after '~', but found nothing";
+ return false;
+ }
+ list = &high_priority_prune_;
+ } else {
+ list = &low_priority_prune_;
+ }
+
+ uid_t uid = Prune::UID_ALL;
+ if (isdigit(*str)) {
+ uid = 0;
+ do {
+ uid = uid * 10 + *str++ - '0';
+ } while (isdigit(*str));
+ }
+
+ pid_t pid = Prune::PID_ALL;
+ if (*str == '/') {
+ ++str;
+ if (isdigit(*str)) {
+ pid = 0;
+ do {
+ pid = pid * 10 + *str++ - '0';
+ } while (isdigit(*str));
+ }
+ }
+
+ if (uid == Prune::UID_ALL && pid == Prune::PID_ALL) {
+ LOG(ERROR) << "Expected UID/PID combination, but found none";
+ return false;
+ }
+
+ if (*str && !isspace(*str)) {
+ LOG(ERROR) << "Nothing expected after UID/PID combination, but found '" << str << "'";
+ return false;
+ }
+
+ list->emplace_back(uid, pid);
+ if (!*str) {
+ break;
+ }
+ }
+
+ return true;
+}
+
+std::string PruneList::Format() const {
+ std::vector<std::string> prune_rules;
+
+ if (worst_uid_enabled_) {
+ prune_rules.emplace_back("~!");
+ }
+ if (worst_pid_of_system_enabled_) {
+ prune_rules.emplace_back("~1000/!");
+ }
+ for (const auto& rule : low_priority_prune_) {
+ prune_rules.emplace_back(rule.Format());
+ }
+ for (const auto& rule : high_priority_prune_) {
+ prune_rules.emplace_back("~" + rule.Format());
+ }
+ return android::base::Join(prune_rules, " ");
+}
+
+bool PruneList::IsHighPriority(LogBufferElement* element) const {
+ for (const auto& rule : high_priority_prune_) {
+ if (rule.Matches(element)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PruneList::IsLowPriority(LogBufferElement* element) const {
+ for (const auto& rule : low_priority_prune_) {
+ if (rule.Matches(element)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/logd/PruneList.h b/logd/PruneList.h
new file mode 100644
index 0000000..94de5c5
--- /dev/null
+++ b/logd/PruneList.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 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 <sys/types.h>
+
+#include <string.h>
+#include <list>
+
+#include "LogBufferElement.h"
+
+class Prune {
+ public:
+ static const uid_t UID_ALL = (uid_t)-1;
+ static const pid_t PID_ALL = (pid_t)-1;
+
+ Prune(uid_t uid, pid_t pid) : uid_(uid), pid_(pid) {}
+
+ bool Matches(LogBufferElement* element) const;
+ std::string Format() const;
+
+ uid_t uid() const { return uid_; }
+ pid_t pid() const { return pid_; }
+
+ private:
+ const uid_t uid_;
+ const pid_t pid_;
+};
+
+class PruneList {
+ public:
+ PruneList();
+
+ bool Init(const char* str);
+ std::string Format() const;
+
+ bool IsHighPriority(LogBufferElement* element) const;
+ bool IsLowPriority(LogBufferElement* element) const;
+
+ bool HasHighPriorityPruneRules() const { return !high_priority_prune_.empty(); }
+ bool HasLowPriorityPruneRules() const { return !low_priority_prune_.empty(); }
+
+ bool worst_uid_enabled() const { return worst_uid_enabled_; }
+ bool worst_pid_of_system_enabled() const { return worst_pid_of_system_enabled_; }
+
+ private:
+ std::list<Prune> high_priority_prune_;
+ std::list<Prune> low_priority_prune_;
+
+ bool worst_uid_enabled_;
+ bool worst_pid_of_system_enabled_;
+};
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 6a9369a..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
@@ -52,21 +52,21 @@
log.tag.<tag> string persist The <tag> specific logging level.
persist.log.tag.<tag> string build default for log.tag.<tag>
+logd.buffer_type string (empty) Set the log buffer type. Current choices are 'simple',
+ 'chatty', or 'serialized'. Defaults to 'chatty' if empty.
+
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.
- number - support multipliers (K or M) for convenience. Range is limited
to between 64K and 256M for log buffer sizes. Individual log buffer ids
such as main, system, ... override global default.
-- Pruning filter is of form of a space-separated list of [~][UID][/PID]
- references, where '~' prefix means to blacklist otherwise whitelist. For
- blacklisting, UID or PID may be a '!' to instead reference the chattiest
- client, with the restriction that the PID must be in the UID group 1000
- (system or AID_SYSTEM).
+- Pruning filter rules are specified as UID, UID/PID or /PID. A '~' prefix indicates that elements
+ matching the rule should be pruned with higher priority otherwise they're pruned with lower
+ priority. All other pruning activity is oldest first. Special case ~! represents an automatic
+ pruning for the noisiest UID as determined by the current statistics. Special case ~1000/!
+ represents pruning of the worst PID within AID_SYSTEM when AID_SYSTEM is the noisiest UID.
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%
copy from trusty/libtrusty/tipc_ioctl.h
copy 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/SerializedData.h b/logd/SerializedData.h
new file mode 100644
index 0000000..d3d1e18
--- /dev/null
+++ b/logd/SerializedData.h
@@ -0,0 +1,55 @@
+/*
+ * 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 <sys/types.h>
+
+#include <algorithm>
+#include <memory>
+
+// This class is used instead of std::string or std::vector because their clear(), erase(), etc
+// functions don't actually deallocate. shrink_to_fit() does deallocate but is not guaranteed to
+// work and swapping with an empty string/vector is clunky.
+class SerializedData {
+ public:
+ SerializedData() {}
+ SerializedData(size_t size) : data_(new uint8_t[size]), size_(size) {}
+
+ void Resize(size_t new_size) {
+ if (size_ == 0) {
+ data_.reset(new uint8_t[new_size]);
+ size_ = new_size;
+ } else if (new_size == 0) {
+ data_.reset();
+ size_ = 0;
+ } else if (new_size != size_) {
+ std::unique_ptr<uint8_t[]> new_data(new uint8_t[new_size]);
+ size_t copy_size = std::min(size_, new_size);
+ memcpy(new_data.get(), data_.get(), copy_size);
+ data_.swap(new_data);
+ size_ = new_size;
+ }
+ }
+
+ uint8_t* data() { return data_.get(); }
+ const uint8_t* data() const { return data_.get(); }
+ size_t size() const { return size_; }
+
+ private:
+ std::unique_ptr<uint8_t[]> data_;
+ size_t size_ = 0;
+};
\ No newline at end of file
diff --git a/logd/SerializedFlushToState.cpp b/logd/SerializedFlushToState.cpp
new file mode 100644
index 0000000..b02ccc3
--- /dev/null
+++ b/logd/SerializedFlushToState.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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 "SerializedFlushToState.h"
+
+#include <android-base/logging.h>
+
+SerializedFlushToState::SerializedFlushToState(uint64_t start, LogMask log_mask)
+ : FlushToState(start, log_mask) {
+ log_id_for_each(i) {
+ if (((1 << i) & log_mask) == 0) {
+ continue;
+ }
+ logs_needed_from_next_position_[i] = true;
+ }
+}
+
+SerializedFlushToState::~SerializedFlushToState() {
+ log_id_for_each(i) {
+ if (log_positions_[i]) {
+ log_positions_[i]->buffer_it->DecReaderRefCount();
+ }
+ }
+}
+
+void SerializedFlushToState::CreateLogPosition(log_id_t log_id) {
+ CHECK(!logs_[log_id].empty());
+ LogPosition log_position;
+ auto it = logs_[log_id].begin();
+ while (it != logs_[log_id].end() && start() > it->highest_sequence_number()) {
+ ++it;
+ }
+ if (it == logs_[log_id].end()) {
+ --it;
+ }
+ it->IncReaderRefCount();
+ log_position.buffer_it = it;
+
+ // Find the offset of the first log with sequence number >= start().
+ int read_offset = 0;
+ while (read_offset < it->write_offset()) {
+ const auto* entry = it->log_entry(read_offset);
+ if (entry->sequence() >= start()) {
+ break;
+ }
+ read_offset += entry->total_len();
+ }
+ log_position.read_offset = read_offset;
+
+ log_positions_[log_id].emplace(log_position);
+}
+
+void SerializedFlushToState::AddMinHeapEntry(log_id_t log_id) {
+ auto& buffer_it = log_positions_[log_id]->buffer_it;
+ auto read_offset = log_positions_[log_id]->read_offset;
+
+ // If there is another log to read in this buffer, add it to the min heap.
+ if (read_offset < buffer_it->write_offset()) {
+ auto* entry = buffer_it->log_entry(read_offset);
+ min_heap_.emplace(log_id, entry);
+ } else if (read_offset == buffer_it->write_offset()) {
+ // If there are no more logs to read in this buffer and it's the last buffer, then
+ // set logs_needed_from_next_position_ to wait until more logs get logged.
+ if (buffer_it == std::prev(logs_[log_id].end())) {
+ logs_needed_from_next_position_[log_id] = true;
+ } else {
+ // Otherwise, if there is another buffer piece, move to that and do the same check.
+ buffer_it->DecReaderRefCount();
+ ++buffer_it;
+ buffer_it->IncReaderRefCount();
+ log_positions_[log_id]->read_offset = 0;
+ if (buffer_it->write_offset() == 0) {
+ logs_needed_from_next_position_[log_id] = true;
+ } else {
+ auto* entry = buffer_it->log_entry(0);
+ min_heap_.emplace(log_id, entry);
+ }
+ }
+ } else {
+ // read_offset > buffer_it->write_offset() should never happen.
+ CHECK(false);
+ }
+}
+
+void SerializedFlushToState::CheckForNewLogs() {
+ log_id_for_each(i) {
+ if (!logs_needed_from_next_position_[i]) {
+ continue;
+ }
+ if (!log_positions_[i]) {
+ if (logs_[i].empty()) {
+ continue;
+ }
+ CreateLogPosition(i);
+ }
+ logs_needed_from_next_position_[i] = false;
+ // If it wasn't possible to insert, logs_needed_from_next_position will be set back to true.
+ AddMinHeapEntry(i);
+ }
+}
+
+MinHeapElement SerializedFlushToState::PopNextUnreadLog() {
+ auto top = min_heap_.top();
+ min_heap_.pop();
+
+ auto* entry = top.entry;
+ auto log_id = top.log_id;
+
+ log_positions_[log_id]->read_offset += entry->total_len();
+
+ logs_needed_from_next_position_[log_id] = true;
+
+ return top;
+}
+
+void SerializedFlushToState::Prune(log_id_t log_id,
+ const std::list<SerializedLogChunk>::iterator& buffer_it) {
+ // If we don't have a position for this log or if we're not referencing buffer_it, ignore.
+ if (!log_positions_[log_id].has_value() || log_positions_[log_id]->buffer_it != buffer_it) {
+ return;
+ }
+
+ // // Decrease the ref count since we're deleting our reference.
+ buffer_it->DecReaderRefCount();
+
+ // Delete in the reference.
+ log_positions_[log_id].reset();
+
+ // Remove the MinHeapElement referencing log_id, if it exists, but retain the others.
+ std::vector<MinHeapElement> old_elements;
+ while (!min_heap_.empty()) {
+ auto& element = min_heap_.top();
+ if (element.log_id != log_id) {
+ old_elements.emplace_back(element);
+ }
+ min_heap_.pop();
+ }
+ for (auto&& element : old_elements) {
+ min_heap_.emplace(element);
+ }
+
+ // Finally set logs_needed_from_next_position_, so CheckForNewLogs() will re-create the
+ // log_position_ object during the next read.
+ logs_needed_from_next_position_[log_id] = true;
+}
diff --git a/logd/SerializedFlushToState.h b/logd/SerializedFlushToState.h
new file mode 100644
index 0000000..0b20822
--- /dev/null
+++ b/logd/SerializedFlushToState.h
@@ -0,0 +1,103 @@
+/*
+ * 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 <bitset>
+#include <list>
+#include <queue>
+
+#include "LogBuffer.h"
+#include "SerializedLogChunk.h"
+#include "SerializedLogEntry.h"
+
+struct LogPosition {
+ std::list<SerializedLogChunk>::iterator buffer_it;
+ int read_offset;
+};
+
+struct MinHeapElement {
+ MinHeapElement(log_id_t log_id, const SerializedLogEntry* entry)
+ : log_id(log_id), entry(entry) {}
+ log_id_t log_id;
+ const SerializedLogEntry* entry;
+ // The change of comparison operators is intentional, std::priority_queue uses operator<() to
+ // compare but creates a max heap. Since we want a min heap, we return the opposite result.
+ bool operator<(const MinHeapElement& rhs) const {
+ return entry->sequence() > rhs.entry->sequence();
+ }
+};
+
+// This class tracks the specific point where a FlushTo client has read through the logs. It
+// directly references the std::list<> iterators from the parent SerializedLogBuffer and the offset
+// into each log chunk where it has last read. All interactions with this class, except for its
+// construction, must be done with SerializedLogBuffer::lock_ held. No log chunks that it
+// references may be pruned, which is handled by ensuring prune does not touch any log chunk with
+// highest sequence number greater or equal to start().
+class SerializedFlushToState : public FlushToState {
+ public:
+ // Initializes this state object. For each log buffer set in log_mask, this sets
+ // logs_needed_from_next_position_.
+ SerializedFlushToState(uint64_t start, LogMask log_mask);
+
+ // Decrease the reference of all referenced logs. This happens when a reader is disconnected.
+ ~SerializedFlushToState() override;
+
+ // We can't hold SerializedLogBuffer::lock_ in the constructor, so we must initialize logs here.
+ void InitializeLogs(std::list<SerializedLogChunk>* logs) {
+ if (logs_ == nullptr) logs_ = logs;
+ }
+
+ bool HasUnreadLogs() {
+ CheckForNewLogs();
+ return !min_heap_.empty();
+ }
+
+ // Pops the next unread log from the min heap and sets logs_needed_from_next_position_ to
+ // indicate that we're waiting for more logs from the associated log buffer.
+ MinHeapElement PopNextUnreadLog();
+
+ // If the parent log buffer prunes logs, the reference that this class contains may become
+ // invalid, so this must be called first to drop the reference to buffer_it, if any.
+ void Prune(log_id_t log_id, const std::list<SerializedLogChunk>::iterator& buffer_it);
+
+ private:
+ // If there is a log in the serialized log buffer for `log_id` at the read_offset, add it to the
+ // min heap for reading, otherwise set logs_needed_from_next_position_ to indicate that we're
+ // waiting for the next log.
+ void AddMinHeapEntry(log_id_t log_id);
+
+ // Create a LogPosition object for the given log_id by searching through the log chunks for the
+ // first chunk and then first log entry within that chunk that is greater or equal to start().
+ void CreateLogPosition(log_id_t log_id);
+
+ // Checks to see if any log buffers set in logs_needed_from_next_position_ have new logs and
+ // calls AddMinHeapEntry() if so.
+ void CheckForNewLogs();
+
+ std::list<SerializedLogChunk>* logs_ = nullptr;
+ // An optional structure that contains an iterator to the serialized log buffer and offset into
+ // it that this logger should handle next.
+ std::optional<LogPosition> log_positions_[LOG_ID_MAX];
+ // A bit for each log that is set if a given log_id has no logs or if this client has read all
+ // of its logs. In order words: `logs_[i].empty() || (buffer_it == std::prev(logs_.end) &&
+ // next_log_position == logs_write_position_)`. These will be re-checked in each
+ // loop in case new logs came in.
+ std::bitset<LOG_ID_MAX> logs_needed_from_next_position_ = {};
+ // A min heap that has up to one entry per log buffer, sorted by sequence number, of the next
+ // element that this reader should read.
+ std::priority_queue<MinHeapElement> min_heap_;
+};
diff --git a/logd/SerializedFlushToStateTest.cpp b/logd/SerializedFlushToStateTest.cpp
new file mode 100644
index 0000000..f4515c8
--- /dev/null
+++ b/logd/SerializedFlushToStateTest.cpp
@@ -0,0 +1,290 @@
+/*
+ * 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 "SerializedFlushToState.h"
+
+#include <map>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+using android::base::Join;
+using android::base::StringPrintf;
+
+constexpr size_t kChunkSize = 3 * 4096;
+
+class SerializedFlushToStateTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ // This test spams many unneeded INFO logs, so we suppress them.
+ old_log_severity_ = android::base::SetMinimumLogSeverity(android::base::WARNING);
+ }
+ void TearDown() override { android::base::SetMinimumLogSeverity(old_log_severity_); }
+
+ std::string TestReport(const std::vector<uint64_t>& expected,
+ const std::vector<uint64_t>& read) {
+ auto sequence_to_log_id = [&](uint64_t sequence) -> int {
+ for (const auto& [log_id, sequences] : sequence_numbers_per_buffer_) {
+ if (std::find(sequences.begin(), sequences.end(), sequence) != sequences.end()) {
+ return log_id;
+ }
+ }
+ return -1;
+ };
+
+ std::map<int, std::vector<uint64_t>> missing_sequences;
+ std::vector<uint64_t> missing_expected;
+ std::set_difference(expected.begin(), expected.end(), read.begin(), read.end(),
+ std::back_inserter(missing_expected));
+ for (uint64_t sequence : missing_expected) {
+ int log_id = sequence_to_log_id(sequence);
+ missing_sequences[log_id].emplace_back(sequence);
+ }
+
+ std::map<int, std::vector<uint64_t>> extra_sequences;
+ std::vector<uint64_t> extra_read;
+ std::set_difference(read.begin(), read.end(), expected.begin(), expected.end(),
+ std::back_inserter(extra_read));
+ for (uint64_t sequence : extra_read) {
+ int log_id = sequence_to_log_id(sequence);
+ extra_sequences[log_id].emplace_back(sequence);
+ }
+
+ std::vector<std::string> errors;
+ for (const auto& [log_id, sequences] : missing_sequences) {
+ errors.emplace_back(
+ StringPrintf("Log id %d missing %zu sequences", log_id, sequences.size()));
+ }
+
+ for (const auto& [log_id, sequences] : extra_sequences) {
+ errors.emplace_back(
+ StringPrintf("Log id %d has extra %zu sequences", log_id, sequences.size()));
+ }
+
+ return Join(errors, ", ");
+ }
+
+ // Read sequence numbers in order from SerializedFlushToState for every mask combination and all
+ // sequence numbers from 0 through the highest logged sequence number + 1.
+ // This assumes that all of the logs have already been written.
+ void TestAllReading() {
+ uint64_t max_sequence = sequence_ + 1;
+ uint32_t max_mask = (1 << LOG_ID_MAX) - 1;
+ for (uint64_t sequence = 0; sequence < max_sequence; ++sequence) {
+ for (uint32_t mask = 0; mask < max_mask; ++mask) {
+ auto state = SerializedFlushToState{sequence, mask};
+ state.InitializeLogs(log_chunks_);
+ TestReading(sequence, mask, state);
+ }
+ }
+ }
+
+ // Similar to TestAllReading() except that it doesn't assume any logs are in the buffer, instead
+ // it calls write_logs() in a loop for sequence/mask combination. It clears log_chunks_ and
+ // sequence_numbers_per_buffer_ between calls, such that only the sequence numbers written in
+ // the previous call to write_logs() are expected.
+ void TestAllReadingWithFutureMessages(const std::function<bool(int)>& write_logs) {
+ uint64_t max_sequence = sequence_ + 1;
+ uint32_t max_mask = (1 << LOG_ID_MAX) - 1;
+ for (uint64_t sequence = 1; sequence < max_sequence; ++sequence) {
+ for (uint32_t mask = 1; mask < max_mask; ++mask) {
+ log_id_for_each(i) { log_chunks_[i].clear(); }
+ auto state = SerializedFlushToState{sequence, mask};
+ state.InitializeLogs(log_chunks_);
+ int loop_count = 0;
+ while (write_logs(loop_count++)) {
+ TestReading(sequence, mask, state);
+ sequence_numbers_per_buffer_.clear();
+ }
+ }
+ }
+ }
+
+ void TestReading(uint64_t start, LogMask log_mask, SerializedFlushToState& state) {
+ std::vector<uint64_t> expected_sequence;
+ log_id_for_each(i) {
+ if (((1 << i) & log_mask) == 0) {
+ continue;
+ }
+ for (const auto& sequence : sequence_numbers_per_buffer_[i]) {
+ if (sequence >= start) {
+ expected_sequence.emplace_back(sequence);
+ }
+ }
+ }
+ std::sort(expected_sequence.begin(), expected_sequence.end());
+
+ std::vector<uint64_t> read_sequence;
+
+ while (state.HasUnreadLogs()) {
+ auto top = state.PopNextUnreadLog();
+ read_sequence.emplace_back(top.entry->sequence());
+ }
+
+ EXPECT_TRUE(std::is_sorted(read_sequence.begin(), read_sequence.end()));
+
+ EXPECT_EQ(expected_sequence.size(), read_sequence.size());
+
+ EXPECT_EQ(expected_sequence, read_sequence)
+ << "start: " << start << " log_mask: " << log_mask << " "
+ << TestReport(expected_sequence, read_sequence);
+ }
+
+ // Add a chunk with the given messages to the a given log buffer. Keep track of the sequence
+ // numbers for future validation. Optionally mark the block as having finished writing.
+ void AddChunkWithMessages(bool finish_writing, int buffer,
+ const std::vector<std::string>& messages) {
+ auto chunk = SerializedLogChunk{kChunkSize};
+ for (const auto& message : messages) {
+ auto sequence = sequence_++;
+ sequence_numbers_per_buffer_[buffer].emplace_back(sequence);
+ ASSERT_TRUE(chunk.CanLog(message.size() + 1));
+ chunk.Log(sequence, log_time(), 0, 1, 1, message.c_str(), message.size() + 1);
+ }
+ if (finish_writing) {
+ chunk.FinishWriting();
+ }
+ log_chunks_[buffer].emplace_back(std::move(chunk));
+ }
+
+ android::base::LogSeverity old_log_severity_;
+ std::map<int, std::vector<uint64_t>> sequence_numbers_per_buffer_;
+ std::list<SerializedLogChunk> log_chunks_[LOG_ID_MAX];
+ uint64_t sequence_ = 1;
+};
+
+// 0: multiple chunks, with variable number of entries, with/without finishing writing
+// 1: 1 chunk with 1 log and finished writing
+// 2: 1 chunk with 1 log and not finished writing
+// 3: 1 chunk with 0 logs and not finished writing
+// 4: 1 chunk with 0 logs and finished writing (impossible, but SerializedFlushToState handles it)
+// 5-7: 0 chunks
+TEST_F(SerializedFlushToStateTest, smoke) {
+ AddChunkWithMessages(true, 0, {"1st", "2nd"});
+ AddChunkWithMessages(true, 1, {"3rd"});
+ AddChunkWithMessages(false, 0, {"4th"});
+ AddChunkWithMessages(true, 0, {"4th", "5th", "more", "even", "more", "go", "here"});
+ AddChunkWithMessages(false, 2, {"6th"});
+ AddChunkWithMessages(true, 0, {"7th"});
+ AddChunkWithMessages(false, 3, {});
+ AddChunkWithMessages(true, 4, {});
+
+ TestAllReading();
+}
+
+TEST_F(SerializedFlushToStateTest, random) {
+ srand(1);
+ for (int count = 0; count < 20; ++count) {
+ unsigned int num_messages = 1 + rand() % 15;
+ auto messages = std::vector<std::string>{num_messages, "same message"};
+
+ bool compress = rand() % 2;
+ int buf = rand() % LOG_ID_MAX;
+
+ AddChunkWithMessages(compress, buf, messages);
+ }
+
+ TestAllReading();
+}
+
+// Same start as smoke, but we selectively write logs to the buffers and ensure they're read.
+TEST_F(SerializedFlushToStateTest, future_writes) {
+ auto write_logs = [&](int loop_count) {
+ switch (loop_count) {
+ case 0:
+ // Initial writes.
+ AddChunkWithMessages(true, 0, {"1st", "2nd"});
+ AddChunkWithMessages(true, 1, {"3rd"});
+ AddChunkWithMessages(false, 0, {"4th"});
+ AddChunkWithMessages(true, 0, {"4th", "5th", "more", "even", "more", "go", "here"});
+ AddChunkWithMessages(false, 2, {"6th"});
+ AddChunkWithMessages(true, 0, {"7th"});
+ AddChunkWithMessages(false, 3, {});
+ AddChunkWithMessages(true, 4, {});
+ break;
+ case 1:
+ // Smoke test, add a simple chunk.
+ AddChunkWithMessages(true, 0, {"1st", "2nd"});
+ break;
+ case 2:
+ // Add chunks to all but one of the logs.
+ AddChunkWithMessages(true, 0, {"1st", "2nd"});
+ AddChunkWithMessages(true, 1, {"1st", "2nd"});
+ AddChunkWithMessages(true, 2, {"1st", "2nd"});
+ AddChunkWithMessages(true, 3, {"1st", "2nd"});
+ AddChunkWithMessages(true, 4, {"1st", "2nd"});
+ AddChunkWithMessages(true, 5, {"1st", "2nd"});
+ AddChunkWithMessages(true, 6, {"1st", "2nd"});
+ break;
+ case 3:
+ // Finally add chunks to all logs.
+ AddChunkWithMessages(true, 0, {"1st", "2nd"});
+ AddChunkWithMessages(true, 1, {"1st", "2nd"});
+ AddChunkWithMessages(true, 2, {"1st", "2nd"});
+ AddChunkWithMessages(true, 3, {"1st", "2nd"});
+ AddChunkWithMessages(true, 4, {"1st", "2nd"});
+ AddChunkWithMessages(true, 5, {"1st", "2nd"});
+ AddChunkWithMessages(true, 6, {"1st", "2nd"});
+ AddChunkWithMessages(true, 7, {"1st", "2nd"});
+ break;
+ default:
+ return false;
+ }
+ return true;
+ };
+
+ TestAllReadingWithFutureMessages(write_logs);
+}
+
+TEST_F(SerializedFlushToStateTest, no_dangling_references) {
+ AddChunkWithMessages(true, 0, {"1st", "2nd"});
+ AddChunkWithMessages(true, 0, {"3rd", "4th"});
+
+ auto state = SerializedFlushToState{1, kLogMaskAll};
+ state.InitializeLogs(log_chunks_);
+
+ ASSERT_EQ(log_chunks_[0].size(), 2U);
+ auto first_chunk = log_chunks_[0].begin();
+ auto second_chunk = std::next(first_chunk);
+
+ ASSERT_TRUE(state.HasUnreadLogs());
+ auto first_log = state.PopNextUnreadLog();
+ EXPECT_STREQ(first_log.entry->msg(), "1st");
+ EXPECT_EQ(first_chunk->reader_ref_count(), 1U);
+ EXPECT_EQ(second_chunk->reader_ref_count(), 0U);
+
+ ASSERT_TRUE(state.HasUnreadLogs());
+ auto second_log = state.PopNextUnreadLog();
+ EXPECT_STREQ(second_log.entry->msg(), "2nd");
+ EXPECT_EQ(first_chunk->reader_ref_count(), 1U);
+ EXPECT_EQ(second_chunk->reader_ref_count(), 0U);
+
+ ASSERT_TRUE(state.HasUnreadLogs());
+ auto third_log = state.PopNextUnreadLog();
+ EXPECT_STREQ(third_log.entry->msg(), "3rd");
+ EXPECT_EQ(first_chunk->reader_ref_count(), 0U);
+ EXPECT_EQ(second_chunk->reader_ref_count(), 1U);
+
+ ASSERT_TRUE(state.HasUnreadLogs());
+ auto fourth_log = state.PopNextUnreadLog();
+ EXPECT_STREQ(fourth_log.entry->msg(), "4th");
+ EXPECT_EQ(first_chunk->reader_ref_count(), 0U);
+ EXPECT_EQ(second_chunk->reader_ref_count(), 1U);
+
+ EXPECT_FALSE(state.HasUnreadLogs());
+}
\ No newline at end of file
diff --git a/logd/SerializedLogBuffer.cpp b/logd/SerializedLogBuffer.cpp
new file mode 100644
index 0000000..5012d3d
--- /dev/null
+++ b/logd/SerializedLogBuffer.cpp
@@ -0,0 +1,292 @@
+/*
+ * 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 "SerializedLogBuffer.h"
+
+#include <sys/prctl.h>
+
+#include <limits>
+
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+
+#include "LogSize.h"
+#include "LogStatistics.h"
+#include "SerializedFlushToState.h"
+
+SerializedLogBuffer::SerializedLogBuffer(LogReaderList* reader_list, LogTags* tags,
+ LogStatistics* stats)
+ : reader_list_(reader_list), tags_(tags), stats_(stats) {
+ Init();
+}
+
+void SerializedLogBuffer::Init() {
+ log_id_for_each(i) {
+ if (!SetSize(i, GetBufferSizeFromProperties(i))) {
+ SetSize(i, kLogBufferMinSize);
+ }
+ }
+
+ // Release any sleeping reader threads to dump their current content.
+ auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
+ for (const auto& reader_thread : reader_list_->reader_threads()) {
+ reader_thread->triggerReader_Locked();
+ }
+}
+
+bool SerializedLogBuffer::ShouldLog(log_id_t log_id, const char* msg, uint16_t len) {
+ if (log_id == LOG_ID_SECURITY) {
+ return true;
+ }
+
+ int prio = ANDROID_LOG_INFO;
+ const char* tag = nullptr;
+ size_t tag_len = 0;
+ if (IsBinary(log_id)) {
+ int32_t tag_int = MsgToTag(msg, len);
+ tag = tags_->tagToName(tag_int);
+ if (tag) {
+ tag_len = strlen(tag);
+ }
+ } else {
+ prio = *msg;
+ tag = msg + 1;
+ tag_len = strnlen(tag, len - 1);
+ }
+ return __android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE);
+}
+
+int SerializedLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len) {
+ if (log_id >= LOG_ID_MAX || len == 0) {
+ 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;
+ }
+
+ auto sequence = sequence_.fetch_add(1, std::memory_order_relaxed);
+
+ auto lock = std::lock_guard{lock_};
+
+ if (logs_[log_id].empty()) {
+ logs_[log_id].push_back(SerializedLogChunk(max_size_[log_id] / 4));
+ }
+
+ auto total_len = sizeof(SerializedLogEntry) + len;
+ if (!logs_[log_id].back().CanLog(total_len)) {
+ logs_[log_id].back().FinishWriting();
+ logs_[log_id].push_back(SerializedLogChunk(max_size_[log_id] / 4));
+ }
+
+ auto entry = logs_[log_id].back().Log(sequence, realtime, uid, pid, tid, msg, len);
+ stats_->Add(entry->ToLogStatisticsElement(log_id));
+
+ MaybePrune(log_id);
+
+ reader_list_->NotifyNewLog(1 << log_id);
+ return len;
+}
+
+void SerializedLogBuffer::MaybePrune(log_id_t log_id) {
+ size_t total_size = GetSizeUsed(log_id);
+ size_t after_size = total_size;
+ if (total_size > max_size_[log_id]) {
+ Prune(log_id, total_size - max_size_[log_id], 0);
+ after_size = GetSizeUsed(log_id);
+ LOG(INFO) << "Pruned Logs from log_id: " << log_id << ", previous size: " << total_size
+ << " after size: " << after_size;
+ }
+
+ stats_->set_overhead(log_id, after_size);
+}
+
+void SerializedLogBuffer::RemoveChunkFromStats(log_id_t log_id, SerializedLogChunk& chunk) {
+ chunk.IncReaderRefCount();
+ int read_offset = 0;
+ while (read_offset < chunk.write_offset()) {
+ auto* entry = chunk.log_entry(read_offset);
+ stats_->Subtract(entry->ToLogStatisticsElement(log_id));
+ read_offset += entry->total_len();
+ }
+ chunk.DecReaderRefCount();
+}
+
+void SerializedLogBuffer::NotifyReadersOfPrune(
+ log_id_t log_id, const std::list<SerializedLogChunk>::iterator& chunk) {
+ for (const auto& reader_thread : reader_list_->reader_threads()) {
+ auto& state = reinterpret_cast<SerializedFlushToState&>(reader_thread->flush_to_state());
+ state.Prune(log_id, chunk);
+ }
+}
+
+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()};
+
+ auto& log_buffer = logs_[log_id];
+ auto it = log_buffer.begin();
+ while (it != log_buffer.end()) {
+ 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++;
+
+ // 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);
+
+ if (uid != 0) {
+ // Reorder the log buffer to remove logs from the given UID. If there are no logs left
+ // in the buffer after the removal, delete it.
+ if (it_to_prune->ClearUidLogs(uid, log_id, stats_)) {
+ log_buffer.erase(it_to_prune);
+ }
+ } else {
+ size_t buffer_size = it_to_prune->PruneSize();
+ RemoveChunkFromStats(log_id, *it_to_prune);
+ log_buffer.erase(it_to_prune);
+ if (buffer_size >= bytes_to_free) {
+ return;
+ }
+ bytes_to_free -= buffer_size;
+ }
+ }
+}
+
+std::unique_ptr<FlushToState> SerializedLogBuffer::CreateFlushToState(uint64_t start,
+ LogMask log_mask) {
+ return std::make_unique<SerializedFlushToState>(start, log_mask);
+}
+
+bool SerializedLogBuffer::FlushTo(
+ LogWriter* writer, FlushToState& abstract_state,
+ const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime)>& filter) {
+ auto lock = std::unique_lock{lock_};
+
+ auto& state = reinterpret_cast<SerializedFlushToState&>(abstract_state);
+ state.InitializeLogs(logs_);
+
+ while (state.HasUnreadLogs()) {
+ MinHeapElement top = state.PopNextUnreadLog();
+ auto* entry = top.entry;
+ auto log_id = top.log_id;
+
+ if (entry->sequence() < state.start()) {
+ continue;
+ }
+ state.set_start(entry->sequence());
+
+ if (!writer->privileged() && entry->uid() != writer->uid()) {
+ continue;
+ }
+
+ if (filter) {
+ auto ret = filter(log_id, entry->pid(), entry->sequence(), entry->realtime());
+ if (ret == FilterResult::kSkip) {
+ continue;
+ }
+ if (ret == FilterResult::kStop) {
+ break;
+ }
+ }
+
+ // 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();
+
+ if (!reinterpret_cast<SerializedLogEntry*>(entry_copy)->Flush(writer, log_id)) {
+ return false;
+ }
+
+ lock.lock();
+ }
+
+ state.set_start(state.start() + 1);
+ return true;
+}
+
+bool SerializedLogBuffer::Clear(log_id_t id, uid_t uid) {
+ auto lock = std::lock_guard{lock_};
+ Prune(id, ULONG_MAX, uid);
+
+ // Clearing SerializedLogBuffer never waits for readers and therefore is always successful.
+ return true;
+}
+
+size_t SerializedLogBuffer::GetSizeUsed(log_id_t id) {
+ size_t total_size = 0;
+ for (const auto& chunk : logs_[id]) {
+ total_size += chunk.PruneSize();
+ }
+ return total_size;
+}
+
+size_t SerializedLogBuffer::GetSize(log_id_t id) {
+ auto lock = std::lock_guard{lock_};
+ return max_size_[id];
+}
+
+// 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.
+bool SerializedLogBuffer::SetSize(log_id_t id, size_t size) {
+ // Reasonable limits ...
+ if (!IsValidBufferSize(size)) {
+ return false;
+ }
+
+ auto lock = std::lock_guard{lock_};
+ max_size_[id] = size;
+
+ MaybePrune(id);
+
+ return true;
+}
diff --git a/logd/SerializedLogBuffer.h b/logd/SerializedLogBuffer.h
new file mode 100644
index 0000000..294cfe6
--- /dev/null
+++ b/logd/SerializedLogBuffer.h
@@ -0,0 +1,73 @@
+/*
+ * 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 <atomic>
+#include <bitset>
+#include <list>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <vector>
+
+#include <android-base/thread_annotations.h>
+
+#include "LogBuffer.h"
+#include "LogReaderList.h"
+#include "LogStatistics.h"
+#include "LogTags.h"
+#include "SerializedLogChunk.h"
+#include "SerializedLogEntry.h"
+#include "rwlock.h"
+
+class SerializedLogBuffer final : public LogBuffer {
+ public:
+ SerializedLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats);
+ void Init() override;
+
+ 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;
+ std::unique_ptr<FlushToState> CreateFlushToState(uint64_t start, LogMask log_mask) override;
+ bool FlushTo(LogWriter* writer, FlushToState& state,
+ const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
+ log_time realtime)>& filter) override;
+
+ bool Clear(log_id_t id, uid_t uid) 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_);
+ 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);
+ size_t GetSizeUsed(log_id_t id) REQUIRES(lock_);
+
+ LogReaderList* reader_list_;
+ LogTags* tags_;
+ LogStatistics* stats_;
+
+ size_t max_size_[LOG_ID_MAX] GUARDED_BY(lock_) = {};
+ std::list<SerializedLogChunk> logs_[LOG_ID_MAX] GUARDED_BY(lock_);
+ RwLock lock_;
+
+ std::atomic<uint64_t> sequence_ = 1;
+};
diff --git a/logd/SerializedLogChunk.cpp b/logd/SerializedLogChunk.cpp
new file mode 100644
index 0000000..de641d6
--- /dev/null
+++ b/logd/SerializedLogChunk.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 "SerializedLogChunk.h"
+
+#include <android-base/logging.h>
+
+#include "CompressionEngine.h"
+
+SerializedLogChunk::~SerializedLogChunk() {
+ CHECK_EQ(reader_ref_count_, 0U);
+}
+
+void SerializedLogChunk::Compress() {
+ if (compressed_log_.size() == 0) {
+ CompressionEngine::GetInstance().Compress(contents_, write_offset_, compressed_log_);
+ LOG(INFO) << "Compressed Log, buffer max size: " << contents_.size()
+ << " size used: " << write_offset_
+ << " compressed size: " << compressed_log_.size();
+ }
+}
+
+// TODO: Develop a better reference counting strategy to guard against the case where the writer is
+// much faster than the reader, and we needlessly compess / decompress the logs.
+void SerializedLogChunk::IncReaderRefCount() {
+ if (++reader_ref_count_ != 1 || writer_active_) {
+ return;
+ }
+ contents_.Resize(write_offset_);
+ CompressionEngine::GetInstance().Decompress(compressed_log_, contents_);
+}
+
+void SerializedLogChunk::DecReaderRefCount() {
+ CHECK_NE(reader_ref_count_, 0U);
+ if (--reader_ref_count_ != 0) {
+ return;
+ }
+ if (!writer_active_) {
+ contents_.Resize(0);
+ }
+}
+
+bool SerializedLogChunk::ClearUidLogs(uid_t uid, log_id_t log_id, LogStatistics* stats) {
+ CHECK_EQ(reader_ref_count_, 0U);
+ if (write_offset_ == 0) {
+ return true;
+ }
+
+ IncReaderRefCount();
+
+ int read_offset = 0;
+ int new_write_offset = 0;
+ while (read_offset < write_offset_) {
+ const auto* entry = log_entry(read_offset);
+ if (entry->uid() == uid) {
+ read_offset += entry->total_len();
+ if (stats != nullptr) {
+ stats->Subtract(entry->ToLogStatisticsElement(log_id));
+ }
+ continue;
+ }
+ size_t entry_total_len = entry->total_len();
+ if (read_offset != new_write_offset) {
+ memmove(contents_.data() + new_write_offset, contents_.data() + read_offset,
+ entry_total_len);
+ }
+ read_offset += entry_total_len;
+ new_write_offset += entry_total_len;
+ }
+
+ if (new_write_offset == 0) {
+ DecReaderRefCount();
+ return true;
+ }
+
+ // Clear the old compressed logs and set write_offset_ appropriately to compress the new
+ // partially cleared log.
+ if (new_write_offset != write_offset_) {
+ compressed_log_.Resize(0);
+ write_offset_ = new_write_offset;
+ Compress();
+ }
+
+ DecReaderRefCount();
+
+ return false;
+}
+
+bool SerializedLogChunk::CanLog(size_t len) {
+ return write_offset_ + len <= contents_.size();
+}
+
+SerializedLogEntry* SerializedLogChunk::Log(uint64_t sequence, log_time realtime, uid_t uid,
+ pid_t pid, pid_t tid, const char* msg, uint16_t len) {
+ auto new_log_address = contents_.data() + write_offset_;
+ auto* entry = new (new_log_address) SerializedLogEntry(uid, pid, tid, sequence, realtime, len);
+ memcpy(entry->msg(), msg, len);
+ write_offset_ += entry->total_len();
+ highest_sequence_number_ = sequence;
+ return entry;
+}
\ No newline at end of file
diff --git a/logd/SerializedLogChunk.h b/logd/SerializedLogChunk.h
new file mode 100644
index 0000000..0991eac
--- /dev/null
+++ b/logd/SerializedLogChunk.h
@@ -0,0 +1,76 @@
+/*
+ * 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 <sys/types.h>
+
+#include "LogWriter.h"
+#include "SerializedData.h"
+#include "SerializedLogEntry.h"
+
+class SerializedLogChunk {
+ public:
+ explicit SerializedLogChunk(size_t size) : contents_(size) {}
+ SerializedLogChunk(SerializedLogChunk&& other) noexcept = default;
+ ~SerializedLogChunk();
+
+ void Compress();
+ void IncReaderRefCount();
+ void DecReaderRefCount();
+
+ // Must have no readers referencing this. Return true if there are no logs left in this chunk.
+ bool ClearUidLogs(uid_t uid, log_id_t log_id, LogStatistics* stats);
+
+ bool CanLog(size_t len);
+ SerializedLogEntry* Log(uint64_t sequence, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
+ const char* msg, uint16_t len);
+
+ // If this buffer has been compressed, we only consider its compressed size when accounting for
+ // memory consumption for pruning. This is since the uncompressed log is only by used by
+ // readers, and thus not a representation of how much these logs cost to keep in memory.
+ size_t PruneSize() const {
+ return sizeof(*this) + (compressed_log_.size() ?: contents_.size());
+ }
+
+ void FinishWriting() {
+ writer_active_ = false;
+ Compress();
+ if (reader_ref_count_ == 0) {
+ contents_.Resize(0);
+ }
+ }
+
+ const SerializedLogEntry* log_entry(int offset) const {
+ return reinterpret_cast<const SerializedLogEntry*>(data() + offset);
+ }
+ const uint8_t* data() const { return contents_.data(); }
+ int write_offset() const { return write_offset_; }
+ uint64_t highest_sequence_number() const { return highest_sequence_number_; }
+
+ // Exposed for testing
+ uint32_t reader_ref_count() const { return reader_ref_count_; }
+
+ private:
+ // The decompressed contents of this log buffer. Deallocated when the ref_count reaches 0 and
+ // writer_active_ is false.
+ SerializedData contents_;
+ int write_offset_ = 0;
+ uint32_t reader_ref_count_ = 0;
+ bool writer_active_ = true;
+ uint64_t highest_sequence_number_ = 1;
+ SerializedData compressed_log_;
+};
diff --git a/logd/SerializedLogChunkTest.cpp b/logd/SerializedLogChunkTest.cpp
new file mode 100644
index 0000000..f10b9c6
--- /dev/null
+++ b/logd/SerializedLogChunkTest.cpp
@@ -0,0 +1,283 @@
+/*
+ * 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 "SerializedLogChunk.h"
+
+#include <limits>
+
+#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <gtest/gtest.h>
+
+using android::base::StringPrintf;
+
+TEST(SerializedLogChunk, smoke) {
+ size_t chunk_size = 10 * 4096;
+ auto chunk = SerializedLogChunk{chunk_size};
+ EXPECT_EQ(chunk_size + sizeof(SerializedLogChunk), chunk.PruneSize());
+
+ static const char log_message[] = "log message";
+ size_t expected_total_len = sizeof(SerializedLogEntry) + sizeof(log_message);
+ ASSERT_TRUE(chunk.CanLog(expected_total_len));
+ EXPECT_TRUE(chunk.CanLog(chunk_size));
+ EXPECT_FALSE(chunk.CanLog(chunk_size + 1));
+
+ log_time time(CLOCK_REALTIME);
+ auto* entry = chunk.Log(1234, time, 0, 1, 2, log_message, sizeof(log_message));
+ ASSERT_NE(nullptr, entry);
+
+ EXPECT_EQ(1234U, entry->sequence());
+ EXPECT_EQ(time, entry->realtime());
+ EXPECT_EQ(0U, entry->uid());
+ EXPECT_EQ(1, entry->pid());
+ EXPECT_EQ(2, entry->tid());
+ EXPECT_EQ(sizeof(log_message), entry->msg_len());
+ EXPECT_STREQ(log_message, entry->msg());
+ EXPECT_EQ(expected_total_len, entry->total_len());
+
+ EXPECT_FALSE(chunk.CanLog(chunk_size));
+ EXPECT_EQ(static_cast<int>(expected_total_len), chunk.write_offset());
+ EXPECT_EQ(1234U, chunk.highest_sequence_number());
+}
+
+TEST(SerializedLogChunk, fill_log_exactly) {
+ static const char log_message[] = "this is a log message";
+ size_t individual_message_size = sizeof(SerializedLogEntry) + sizeof(log_message);
+ size_t chunk_size = individual_message_size * 3;
+ auto chunk = SerializedLogChunk{chunk_size};
+ EXPECT_EQ(chunk_size + sizeof(SerializedLogChunk), chunk.PruneSize());
+
+ ASSERT_TRUE(chunk.CanLog(individual_message_size));
+ EXPECT_NE(nullptr, chunk.Log(1, log_time(), 1000, 1, 1, log_message, sizeof(log_message)));
+
+ ASSERT_TRUE(chunk.CanLog(individual_message_size));
+ EXPECT_NE(nullptr, chunk.Log(2, log_time(), 1000, 2, 1, log_message, sizeof(log_message)));
+
+ ASSERT_TRUE(chunk.CanLog(individual_message_size));
+ EXPECT_NE(nullptr, chunk.Log(3, log_time(), 1000, 3, 1, log_message, sizeof(log_message)));
+
+ EXPECT_FALSE(chunk.CanLog(1));
+}
+
+TEST(SerializedLogChunk, three_logs) {
+ size_t chunk_size = 10 * 4096;
+ auto chunk = SerializedLogChunk{chunk_size};
+
+ chunk.Log(2, log_time(0x1234, 0x5678), 0x111, 0x222, 0x333, "initial message",
+ strlen("initial message"));
+ chunk.Log(3, log_time(0x2345, 0x6789), 0x444, 0x555, 0x666, "second message",
+ strlen("second message"));
+ auto uint64_t_max = std::numeric_limits<uint64_t>::max();
+ auto uint32_t_max = std::numeric_limits<uint32_t>::max();
+ chunk.Log(uint64_t_max, log_time(uint32_t_max, uint32_t_max), uint32_t_max, uint32_t_max,
+ uint32_t_max, "last message", strlen("last message"));
+
+ static const char expected_buffer_data[] =
+ "\x11\x01\x00\x00\x22\x02\x00\x00\x33\x03\x00\x00" // UID PID TID
+ "\x02\x00\x00\x00\x00\x00\x00\x00" // Sequence
+ "\x34\x12\x00\x00\x78\x56\x00\x00" // Timestamp
+ "\x0F\x00initial message" // msg_len + message
+ "\x44\x04\x00\x00\x55\x05\x00\x00\x66\x06\x00\x00" // UID PID TID
+ "\x03\x00\x00\x00\x00\x00\x00\x00" // Sequence
+ "\x45\x23\x00\x00\x89\x67\x00\x00" // Timestamp
+ "\x0E\x00second message" // msg_len + message
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" // UID PID TID
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" // Sequence
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" // Timestamp
+ "\x0C\x00last message"; // msg_len + message
+
+ for (size_t i = 0; i < chunk_size; ++i) {
+ if (i < sizeof(expected_buffer_data)) {
+ EXPECT_EQ(static_cast<uint8_t>(expected_buffer_data[i]), chunk.data()[i])
+ << "position: " << i;
+ } else {
+ EXPECT_EQ(0, chunk.data()[i]) << "position: " << i;
+ }
+ }
+}
+
+// Check that the CHECK() in DecReaderRefCount() if the ref count goes bad is caught.
+TEST(SerializedLogChunk, catch_DecCompressedRef_CHECK) {
+ size_t chunk_size = 10 * 4096;
+ auto chunk = SerializedLogChunk{chunk_size};
+ EXPECT_DEATH({ chunk.DecReaderRefCount(); }, "");
+}
+
+// Check that the CHECK() in ClearUidLogs() if the ref count is greater than 0 is caught.
+TEST(SerializedLogChunk, catch_ClearUidLogs_CHECK) {
+ size_t chunk_size = 10 * 4096;
+ auto chunk = SerializedLogChunk{chunk_size};
+ chunk.IncReaderRefCount();
+ EXPECT_DEATH({ chunk.ClearUidLogs(1000, LOG_ID_MAIN, nullptr); }, "");
+ chunk.DecReaderRefCount();
+}
+
+class UidClearTest : public testing::TestWithParam<bool> {
+ protected:
+ template <typename Write, typename Check>
+ void Test(const Write& write, const Check& check, bool expected_result) {
+ write(chunk_);
+
+ bool finish_writing = GetParam();
+ if (finish_writing) {
+ chunk_.FinishWriting();
+ }
+ EXPECT_EQ(expected_result, chunk_.ClearUidLogs(kUidToClear, LOG_ID_MAIN, nullptr));
+ if (finish_writing) {
+ chunk_.IncReaderRefCount();
+ }
+
+ check(chunk_);
+
+ if (finish_writing) {
+ chunk_.DecReaderRefCount();
+ }
+ }
+
+ static constexpr size_t kChunkSize = 10 * 4096;
+ static constexpr uid_t kUidToClear = 1000;
+ static constexpr uid_t kOtherUid = 1234;
+
+ SerializedLogChunk chunk_{kChunkSize};
+};
+
+// Test that ClearUidLogs() is a no-op if there are no logs of that UID in the buffer.
+TEST_P(UidClearTest, no_logs_in_chunk) {
+ auto write = [](SerializedLogChunk&) {};
+ auto check = [](SerializedLogChunk&) {};
+
+ Test(write, check, true);
+}
+
+// Test that ClearUidLogs() is a no-op if there are no logs of that UID in the buffer.
+TEST_P(UidClearTest, no_logs_from_uid) {
+ static const char msg[] = "this is a log message";
+ auto write = [](SerializedLogChunk& chunk) {
+ chunk.Log(1, log_time(), kOtherUid, 1, 2, msg, sizeof(msg));
+ };
+
+ auto check = [](SerializedLogChunk& chunk) {
+ auto* entry = chunk.log_entry(0);
+ EXPECT_STREQ(msg, entry->msg());
+ };
+
+ Test(write, check, false);
+}
+
+// Test that ClearUidLogs() returns true if all logs in a given buffer correspond to the given UID.
+TEST_P(UidClearTest, all_single) {
+ static const char msg[] = "this is a log message";
+ auto write = [](SerializedLogChunk& chunk) {
+ chunk.Log(1, log_time(), kUidToClear, 1, 2, msg, sizeof(msg));
+ };
+ auto check = [](SerializedLogChunk&) {};
+
+ Test(write, check, true);
+}
+
+// Test that ClearUidLogs() returns true if all logs in a given buffer correspond to the given UID.
+TEST_P(UidClearTest, all_multiple) {
+ static const char msg[] = "this is a log message";
+ auto write = [](SerializedLogChunk& chunk) {
+ chunk.Log(2, log_time(), kUidToClear, 1, 2, msg, sizeof(msg));
+ chunk.Log(3, log_time(), kUidToClear, 1, 2, msg, sizeof(msg));
+ chunk.Log(4, log_time(), kUidToClear, 1, 2, msg, sizeof(msg));
+ };
+ auto check = [](SerializedLogChunk&) {};
+
+ Test(write, check, true);
+}
+
+static std::string MakePrintable(const uint8_t* in, size_t length) {
+ std::string result;
+ for (size_t i = 0; i < length; ++i) {
+ uint8_t c = in[i];
+ if (isprint(c)) {
+ result.push_back(c);
+ } else {
+ result.append(StringPrintf("\\%02x", static_cast<int>(c) & 0xFF));
+ }
+ }
+ return result;
+}
+
+// This test clears UID logs at the beginning and end of the buffer, as well as two back to back
+// logs in the interior.
+TEST_P(UidClearTest, clear_beginning_and_end) {
+ static const char msg1[] = "this is a log message";
+ static const char msg2[] = "non-cleared message";
+ static const char msg3[] = "back to back cleared messages";
+ static const char msg4[] = "second in a row gone";
+ static const char msg5[] = "but we save this one";
+ static const char msg6[] = "and this 1!";
+ static const char msg7[] = "the last one goes too";
+ auto write = [](SerializedLogChunk& chunk) {
+ ASSERT_NE(nullptr, chunk.Log(1, log_time(), kUidToClear, 1, 2, msg1, sizeof(msg1)));
+ ASSERT_NE(nullptr, chunk.Log(2, log_time(), kOtherUid, 1, 2, msg2, sizeof(msg2)));
+ ASSERT_NE(nullptr, chunk.Log(3, log_time(), kUidToClear, 1, 2, msg3, sizeof(msg3)));
+ ASSERT_NE(nullptr, chunk.Log(4, log_time(), kUidToClear, 1, 2, msg4, sizeof(msg4)));
+ ASSERT_NE(nullptr, chunk.Log(5, log_time(), kOtherUid, 1, 2, msg5, sizeof(msg5)));
+ ASSERT_NE(nullptr, chunk.Log(6, log_time(), kOtherUid, 1, 2, msg6, sizeof(msg6)));
+ ASSERT_NE(nullptr, chunk.Log(7, log_time(), kUidToClear, 1, 2, msg7, sizeof(msg7)));
+ };
+
+ auto check = [](SerializedLogChunk& chunk) {
+ size_t read_offset = 0;
+ auto* entry = chunk.log_entry(read_offset);
+ EXPECT_STREQ(msg2, entry->msg());
+ read_offset += entry->total_len();
+
+ entry = chunk.log_entry(read_offset);
+ EXPECT_STREQ(msg5, entry->msg());
+ read_offset += entry->total_len();
+
+ entry = chunk.log_entry(read_offset);
+ EXPECT_STREQ(msg6, entry->msg()) << MakePrintable(chunk.data(), chunk.write_offset());
+ read_offset += entry->total_len();
+
+ EXPECT_EQ(static_cast<int>(read_offset), chunk.write_offset());
+ };
+ Test(write, check, false);
+}
+
+// This tests the opposite case of beginning_and_end, in which we don't clear the beginning or end
+// logs. There is a single log pruned in the middle instead of back to back logs.
+TEST_P(UidClearTest, save_beginning_and_end) {
+ static const char msg1[] = "saved first message";
+ static const char msg2[] = "cleared interior message";
+ static const char msg3[] = "last message stays";
+ auto write = [](SerializedLogChunk& chunk) {
+ ASSERT_NE(nullptr, chunk.Log(1, log_time(), kOtherUid, 1, 2, msg1, sizeof(msg1)));
+ ASSERT_NE(nullptr, chunk.Log(2, log_time(), kUidToClear, 1, 2, msg2, sizeof(msg2)));
+ ASSERT_NE(nullptr, chunk.Log(3, log_time(), kOtherUid, 1, 2, msg3, sizeof(msg3)));
+ };
+
+ auto check = [](SerializedLogChunk& chunk) {
+ size_t read_offset = 0;
+ auto* entry = chunk.log_entry(read_offset);
+ EXPECT_STREQ(msg1, entry->msg());
+ read_offset += entry->total_len();
+
+ entry = chunk.log_entry(read_offset);
+ EXPECT_STREQ(msg3, entry->msg());
+ read_offset += entry->total_len();
+
+ EXPECT_EQ(static_cast<int>(read_offset), chunk.write_offset());
+ };
+ Test(write, check, false);
+}
+
+INSTANTIATE_TEST_CASE_P(UidClearTests, UidClearTest, testing::Values(true, false));
diff --git a/logd/SerializedLogEntry.h b/logd/SerializedLogEntry.h
new file mode 100644
index 0000000..2f2c244
--- /dev/null
+++ b/logd/SerializedLogEntry.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <log/log.h>
+#include <log/log_read.h>
+
+#include "LogStatistics.h"
+#include "LogWriter.h"
+
+// These structs are packed into a single chunk of memory for each log type within a
+// SerializedLogChunk object. Their message is contained immediately at the end of the struct. The
+// address of the next log in the buffer is *this + sizeof(SerializedLogEntry) + msg_len_. If that
+// value would overflow the chunk of memory associated with the SerializedLogChunk object, then a
+// new SerializedLogChunk must be allocated to contain the next SerializedLogEntry.
+class __attribute__((packed)) SerializedLogEntry {
+ public:
+ SerializedLogEntry(uid_t uid, pid_t pid, pid_t tid, uint64_t sequence, log_time realtime,
+ uint16_t len)
+ : uid_(uid),
+ pid_(pid),
+ tid_(tid),
+ sequence_(sequence),
+ realtime_(realtime),
+ msg_len_(len) {}
+ SerializedLogEntry(const SerializedLogEntry& elem) = delete;
+ SerializedLogEntry& operator=(const SerializedLogEntry& elem) = delete;
+ ~SerializedLogEntry() {
+ // Never place anything in this destructor. This class is in place constructed and never
+ // destructed.
+ }
+
+ LogStatisticsElement ToLogStatisticsElement(log_id_t log_id) const {
+ return LogStatisticsElement{
+ .uid = uid(),
+ .pid = pid(),
+ .tid = tid(),
+ .tag = IsBinary(log_id) ? MsgToTag(msg(), msg_len()) : 0,
+ .realtime = realtime(),
+ .msg = msg(),
+ .msg_len = msg_len(),
+ .dropped_count = 0,
+ .log_id = log_id,
+ .total_len = total_len(),
+ };
+ }
+
+ bool Flush(LogWriter* writer, log_id_t log_id) const {
+ struct logger_entry entry = {};
+
+ entry.hdr_size = sizeof(struct logger_entry);
+ entry.lid = log_id;
+ entry.pid = pid();
+ entry.tid = tid();
+ entry.uid = uid();
+ entry.sec = realtime().tv_sec;
+ entry.nsec = realtime().tv_nsec;
+ entry.len = msg_len();
+
+ return writer->Write(entry, msg());
+ }
+
+ uid_t uid() const { return uid_; }
+ pid_t pid() const { return pid_; }
+ pid_t tid() const { return tid_; }
+ uint16_t msg_len() const { return msg_len_; }
+ uint64_t sequence() const { return sequence_; }
+ log_time realtime() const { return realtime_; }
+
+ char* msg() { return reinterpret_cast<char*>(this) + sizeof(*this); }
+ const char* msg() const { return reinterpret_cast<const char*>(this) + sizeof(*this); }
+ uint16_t total_len() const { return sizeof(*this) + msg_len_; }
+
+ private:
+ const uint32_t uid_;
+ const uint32_t pid_;
+ const uint32_t tid_;
+ const uint64_t sequence_;
+ const log_time realtime_;
+ const uint16_t msg_len_;
+};
diff --git a/logd/SimpleLogBuffer.cpp b/logd/SimpleLogBuffer.cpp
index 292a7e4..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);
}
}
@@ -212,65 +213,57 @@
return true;
}
-// clear all rows of type "id" from the buffer.
bool SimpleLogBuffer::Clear(log_id_t id, uid_t uid) {
- bool busy = true;
- // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
- for (int retry = 4;;) {
- if (retry == 1) { // last pass
- // Check if it is still busy after the sleep, we say 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.
- {
- auto lock = std::lock_guard{lock_};
- busy = Prune(id, 1, uid);
- }
- // It is still busy, blocked reader(s), lets kill them all!
- // otherwise, lets be a good citizen and preserve the slow
- // readers and let the clear run (below) deal with determining
- // if we are still blocked and return an error code to caller.
- 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();
- }
- }
- }
- }
+ // 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_};
- busy = Prune(id, ULONG_MAX, uid);
+ if (Prune(id, ULONG_MAX, uid)) {
+ return true;
+ }
}
-
- if (!busy || !--retry) {
- break;
- }
- sleep(1); // Let reader(s) catch up after notification
+ sleep(1);
}
- return busy;
+ // 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);
}
// 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) {
@@ -313,16 +306,16 @@
if (oldest && oldest->start() <= element.sequence()) {
KickReader(oldest, id, prune_rows);
- return true;
+ return false;
}
stats_->Subtract(element.ToLogStatisticsElement());
it = Erase(it);
if (--prune_rows == 0) {
- return false;
+ return true;
}
}
- return false;
+ return true;
}
std::list<LogBufferElement>::iterator SimpleLogBuffer::Erase(
diff --git a/logd/SimpleLogBuffer.h b/logd/SimpleLogBuffer.h
index 2172507..8e5b50e 100644
--- a/logd/SimpleLogBuffer.h
+++ b/logd/SimpleLogBuffer.h
@@ -31,7 +31,7 @@
public:
SimpleLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats);
~SimpleLogBuffer();
- void Init() override;
+ void Init() override final;
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;
@@ -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;
+ 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/Android.bp b/logd/fuzz/Android.bp
index f65fbdf..d346cd7 100644
--- a/logd/fuzz/Android.bp
+++ b/logd/fuzz/Android.bp
@@ -13,11 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-cc_fuzz {
- name: "log_buffer_log_fuzzer",
- srcs: [
- "log_buffer_log_fuzzer.cpp",
- ],
+
+cc_defaults {
+ name: "log_fuzzer_defaults",
static_libs: [
"libbase",
"libcutils",
@@ -25,7 +23,28 @@
"liblog",
"liblogd",
"libcutils",
- "libsysutils",
+ "libz",
+ "libzstd",
],
- cflags: ["-Werror"],
+ cflags: ["-Wextra"],
+ host_supported: true,
+}
+
+cc_fuzz {
+ name: "log_buffer_log_fuzzer",
+ defaults: ["log_fuzzer_defaults"],
+ srcs: [
+ "log_buffer_log_fuzzer.cpp",
+ ],
+}
+
+cc_fuzz {
+ name: "serialized_log_buffer_fuzzer",
+ defaults: ["log_fuzzer_defaults"],
+ srcs: [
+ "serialized_log_buffer_fuzzer.cpp",
+ ],
+ corpus: [
+ "corpus/logentry_use_after_compress",
+ ]
}
diff --git a/logd/fuzz/corpus/logentry_use_after_compress b/logd/fuzz/corpus/logentry_use_after_compress
new file mode 100644
index 0000000..2081b13
--- /dev/null
+++ b/logd/fuzz/corpus/logentry_use_after_compress
Binary files differ
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index b576ddf..d71a2f9 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -15,10 +15,13 @@
*/
#include <string>
+#include <android-base/logging.h>
+
#include "../ChattyLogBuffer.h"
#include "../LogReaderList.h"
#include "../LogReaderThread.h"
#include "../LogStatistics.h"
+#include "../SerializedLogBuffer.h"
// We don't want to waste a lot of entropy on messages
#define MAX_MSG_LENGTH 5
@@ -27,7 +30,10 @@
#define MIN_TAG_ID 1000
#define TAG_MOD 10
-namespace android {
+char* android::uidToName(uid_t) {
+ return strdup("fake");
+}
+
struct LogInput {
public:
log_id_t log_id;
@@ -79,9 +85,13 @@
return 1;
}
-char* uidToName(uid_t) {
- return strdup("fake");
-}
+class NoopWriter : public LogWriter {
+ public:
+ NoopWriter() : LogWriter(0, true) {}
+ bool Write(const logger_entry&, const char*) override { return true; }
+
+ std::string name() const override { return "noop_writer"; }
+};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// We want a random tag length and a random remaining message length
@@ -89,25 +99,50 @@
return 0;
}
+ android::base::SetMinimumLogSeverity(android::base::ERROR);
+
LogReaderList reader_list;
LogTags tags;
PruneList prune_list;
- LogStatistics stats(true);
- LogBuffer* log_buffer = new ChattyLogBuffer(&reader_list, &tags, &prune_list, &stats);
+ LogStatistics stats(true, true);
+ std::unique_ptr<LogBuffer> log_buffer;
+#ifdef FUZZ_SERIALIZED
+ log_buffer.reset(new SerializedLogBuffer(&reader_list, &tags, &stats));
+#else
+ log_buffer.reset(new ChattyLogBuffer(&reader_list, &tags, &prune_list, &stats));
+#endif
size_t data_left = size;
const uint8_t** pdata = &data;
- prune_list.init(nullptr);
+ prune_list.Init(nullptr);
// We want to get pruning code to get called.
log_id_for_each(i) { log_buffer->SetSize(i, 10000); }
while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
- if (!write_log_messages(pdata, &data_left, log_buffer, &stats)) {
+ if (!write_log_messages(pdata, &data_left, log_buffer.get(), &stats)) {
return 0;
}
}
+ // Read out all of the logs.
+ {
+ auto lock = std::unique_lock{reader_list.reader_threads_lock()};
+ std::unique_ptr<LogWriter> test_writer(new NoopWriter());
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer.get(), &reader_list, std::move(test_writer), true, 0,
+ kLogMaskAll, 0, {}, 1, {}));
+ reader_list.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ // Wait until the reader has finished.
+ while (true) {
+ usleep(50);
+ auto lock = std::unique_lock{reader_list.reader_threads_lock()};
+ if (reader_list.reader_threads().size() == 0) {
+ break;
+ }
+ }
+
log_id_for_each(i) { log_buffer->Clear(i, 0); }
return 0;
}
-} // namespace android
diff --git a/trusty/libtrusty/tipc_ioctl.h b/logd/fuzz/serialized_log_buffer_fuzzer.cpp
similarity index 69%
rename from trusty/libtrusty/tipc_ioctl.h
rename to logd/fuzz/serialized_log_buffer_fuzzer.cpp
index 27da56a..d4795b0 100644
--- a/trusty/libtrusty/tipc_ioctl.h
+++ b/logd/fuzz/serialized_log_buffer_fuzzer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,13 +14,6 @@
* limitations under the License.
*/
-#ifndef _TIPC_IOCTL_H
-#define _TIPC_IOCTL_H
+#define FUZZ_SERIALIZED
-#include <linux/ioctl.h>
-#include <linux/types.h>
-
-#define TIPC_IOC_MAGIC 'r'
-#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char *)
-
-#endif
+#include "log_buffer_log_fuzzer.cpp"
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 773ffb8..c92c5b7 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -38,6 +38,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>
@@ -58,8 +59,13 @@
#include "LogStatistics.h"
#include "LogTags.h"
#include "LogUtils.h"
+#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, \
'0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, '>'
@@ -68,49 +74,51 @@
// has a 'sigstop' feature that sends SIGSTOP to a service immediately before calling exec(). This
// allows debuggers, etc to be attached to logd at the very beginning, while still having init
// handle the user, groups, capabilities, files, etc setup.
-static int drop_privs(bool klogd, bool auditd) {
- sched_param param = {};
-
+static void DropPrivs(bool klogd, bool auditd) {
if (set_sched_policy(0, SP_BACKGROUND) < 0) {
- PLOG(ERROR) << "failed to set background scheduling policy";
- return -1;
+ PLOG(FATAL) << "failed to set background scheduling policy";
}
+ sched_param param = {};
if (sched_setscheduler((pid_t)0, SCHED_BATCH, ¶m) < 0) {
- PLOG(ERROR) << "failed to set batch scheduler";
- return -1;
+ 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(ERROR) << "failed to clear PR_SET_DUMPABLE";
- return -1;
+ 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);
if (cap_clear(caps.get()) < 0) {
- return -1;
+ PLOG(FATAL) << "cap_clear() failed";
}
- std::vector<cap_value_t> cap_value;
if (klogd) {
- cap_value.emplace_back(CAP_SYSLOG);
+ cap_value_t cap_syslog = CAP_SYSLOG;
+ if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, &cap_syslog, CAP_SET) < 0 ||
+ cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, &cap_syslog, CAP_SET) < 0) {
+ PLOG(FATAL) << "Failed to set CAP_SYSLOG";
+ }
}
if (auditd) {
- cap_value.emplace_back(CAP_AUDIT_CONTROL);
- }
-
- if (cap_set_flag(caps.get(), CAP_PERMITTED, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
- return -1;
- }
- if (cap_set_flag(caps.get(), CAP_EFFECTIVE, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
- return -1;
+ cap_value_t cap_audit_control = CAP_AUDIT_CONTROL;
+ if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, &cap_audit_control, CAP_SET) < 0 ||
+ cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, &cap_audit_control, CAP_SET) < 0) {
+ PLOG(FATAL) << "Failed to set CAP_AUDIT_CONTROL";
+ }
}
if (cap_set_proc(caps.get()) < 0) {
- PLOG(ERROR) << "failed to set CAP_SYSLOG or CAP_AUDIT_CONTROL";
- return -1;
+ PLOG(FATAL) << "cap_set_proc() failed";
}
+}
- return 0;
+// 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) {
@@ -210,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,
@@ -239,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) {
@@ -252,10 +261,8 @@
if (fdPmesg < 0) PLOG(ERROR) << "Failed to open " << proc_kmsg;
}
- bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
- if (drop_privs(klogd, auditd) != 0) {
- return EXIT_FAILURE;
- }
+ bool auditd = GetBoolProperty("ro.logd.auditd", true);
+ DropPrivs(klogd, auditd);
// A cache of event log tags
LogTags log_tags;
@@ -263,28 +270,31 @@
// Pruning configuration.
PruneList prune_list;
- // 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);
+ std::string buffer_type = GetProperty("logd.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.
+ // Partial (required for chatty) or full logging statistics.
+ 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.
LogReaderList reader_list;
// LogBuffer is the object which is responsible for holding all log entries.
- LogBuffer* logBuf;
- if (true) {
- logBuf = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
+ LogBuffer* log_buffer = nullptr;
+ if (buffer_type == "chatty") {
+ log_buffer = new ChattyLogBuffer(&reader_list, &log_tags, &prune_list, &log_statistics);
+ } else if (buffer_type == "serialized") {
+ log_buffer = new SerializedLogBuffer(&reader_list, &log_tags, &log_statistics);
+ } else if (buffer_type == "simple") {
+ log_buffer = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);
} else {
- logBuf = new SimpleLogBuffer(&reader_list, &log_tags, &log_statistics);
+ LOG(FATAL) << "buffer_type must be one of 'chatty', 'serialized', or 'simple'";
}
// LogReader listens on /dev/socket/logdr. When a client
// connects, log entries in the LogBuffer are written to the client.
- LogReader* reader = new LogReader(logBuf, &reader_list);
+ LogReader* reader = new LogReader(log_buffer, &reader_list);
if (reader->startListener()) {
return EXIT_FAILURE;
}
@@ -292,14 +302,14 @@
// LogListener listens on /dev/socket/logdw for client
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
- LogListener* swl = new LogListener(logBuf);
+ LogListener* swl = new LogListener(log_buffer);
if (!swl->StartListener()) {
return EXIT_FAILURE;
}
// Command listener listens on /dev/socket/logd for incoming logd
// administrative commands.
- CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list, &log_statistics);
+ CommandListener* cl = new CommandListener(log_buffer, &log_tags, &prune_list, &log_statistics);
if (cl->startListener()) {
return EXIT_FAILURE;
}
@@ -309,15 +319,13 @@
// 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;
- al = new LogAudit(logBuf, dmesg_fd, &log_statistics);
+ int dmesg_fd = GetBoolProperty("ro.logd.auditd.dmesg", true) ? fdDmesg : -1;
+ al = new LogAudit(log_buffer, dmesg_fd, &log_statistics);
}
LogKlog* kl = nullptr;
if (klogd) {
- kl = new LogKlog(logBuf, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
+ kl = new LogKlog(log_buffer, fdDmesg, fdPmesg, al != nullptr, &log_statistics);
}
readDmesg(al, kl);
diff --git a/logd/rwlock.h b/logd/rwlock.h
index 2b27ff1..c37721e 100644
--- a/logd/rwlock.h
+++ b/logd/rwlock.h
@@ -43,7 +43,7 @@
class SCOPED_CAPABILITY SharedLock {
public:
- SharedLock(RwLock& lock) ACQUIRE_SHARED(lock) : lock_(lock) { lock_.lock_shared(); }
+ explicit SharedLock(RwLock& lock) ACQUIRE_SHARED(lock) : lock_(lock) { lock_.lock_shared(); }
~SharedLock() RELEASE() { lock_.unlock(); }
void lock_shared() ACQUIRE_SHARED() { lock_.lock_shared(); }
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/Android.mk b/rootdir/Android.mk
index a9d0ed0..77fa94e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -120,6 +120,27 @@
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr
+
+# For /vendor_dlkm partition.
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor_dlkm
+# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+# both devices with and without /vendor_dlkm partition. Those symlinks are for
+# devices without /vendor_dlkm partition. For devices with /vendor_dlkm
+# partition, mount vendor_dlkm.img under /vendor_dlkm will hide those symlinks.
+# Note that /vendor_dlkm/lib is omitted because vendor DLKMs should be accessed
+# via /vendor/lib/modules directly.
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/vendor_dlkm/etc $(TARGET_ROOT_OUT)/vendor_dlkm/etc
+
+# For /odm_dlkm partition.
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm_dlkm
+# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+# both devices with and without /odm_dlkm partition. Those symlinks are for
+# devices without /odm_dlkm partition. For devices with /odm_dlkm
+# partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.
+# Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
+# via /odm/lib/modules directly.
+LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
+
ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
else
diff --git a/rootdir/init.rc b/rootdir/init.rc
index d9a3dd1..44c8f27 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
@@ -319,16 +324,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 0444 /dev/freezer/frozen/freezer.state
-
# make the PSI monitor accessible to others
chown system system /proc/pressure/memory
chmod 0664 /proc/pressure/memory
@@ -343,8 +338,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
@@ -445,6 +438,9 @@
# Load persist properties and override properties (if enabled) from /data.
trigger load_persist_props_action
+ # Should be before netd, but after apex, properties and logging is available.
+ trigger load_bpf_programs
+
# Now we can start zygote for devices with file based encryption
trigger zygote-start
@@ -490,6 +486,9 @@
chown root log /proc/slabinfo
chmod 0440 /proc/slabinfo
+ chown root log /proc/pagetypeinfo
+ chmod 0440 /proc/pagetypeinfo
+
#change permissions on kmsg & sysrq-trigger so bugreports can grab kthread stacks
chown root system /proc/kmsg
chmod 0440 /proc/kmsg
@@ -515,6 +514,13 @@
mkdir /metadata/apex 0700 root system
mkdir /metadata/apex/sessions 0700 root system
+ # On some devices we see a weird behaviour in which /metadata/apex doesn't
+ # have a correct label. To workaround this bug, explicitly call restorecon
+ # on /metadata/apex. For most of the boot sequences /metadata/apex will
+ # already have a correct selinux label, meaning that this call will be a
+ # no-op.
+ restorecon_recursive /metadata/apex
+
on late-fs
# Ensure that tracefs has the correct permissions.
# This does not work correctly if it is called in post-fs.
@@ -855,17 +861,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 /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/toolbox/OWNERS b/toolbox/OWNERS
index 682a067..7529cb9 100644
--- a/toolbox/OWNERS
+++ b/toolbox/OWNERS
@@ -1 +1 @@
-enh@google.com
+include platform/system/core:/janitors/OWNERS
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 1b5f54e..7df7b71 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -17,11 +17,16 @@
#include <ctype.h>
#include <getopt.h>
#include <stdlib.h>
-#include <iostream>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/strings.h>
#include <modprobe/modprobe.h>
+namespace {
+
enum modprobe_mode {
AddModulesMode,
RemoveModulesMode,
@@ -29,50 +34,107 @@
ShowDependenciesMode,
};
-static void print_usage(void) {
- std::cerr << "Usage:" << std::endl;
- std::cerr << std::endl;
- std::cerr << " modprobe [-alrqvsDb] [-d DIR] [MODULE]+" << std::endl;
- std::cerr << " modprobe [-alrqvsDb] [-d DIR] MODULE [symbol=value][...]" << std::endl;
- std::cerr << std::endl;
- std::cerr << "Options:" << std::endl;
- std::cerr << " -b: Apply blacklist to module names too" << std::endl;
- std::cerr << " -d: Load modules from DIR, option may be used multiple times" << std::endl;
- std::cerr << " -D: Print dependencies for modules only, do not load";
- std::cerr << " -h: Print this help" << std::endl;
- std::cerr << " -l: List modules matching pattern" << std::endl;
- std::cerr << " -r: Remove MODULE (multiple modules may be specified)" << std::endl;
- std::cerr << " -q: Quiet" << std::endl;
- std::cerr << " -v: Verbose" << std::endl;
- std::cerr << std::endl;
+void print_usage(void) {
+ LOG(INFO) << "Usage:";
+ LOG(INFO);
+ // -d option is required on Android
+ LOG(INFO) << " modprobe [options] -d DIR [--all=FILE|MODULE]...";
+ LOG(INFO) << " modprobe [options] -d DIR MODULE [symbol=value]...";
+ LOG(INFO);
+ LOG(INFO) << "Options:";
+ LOG(INFO) << " --all=FILE: FILE to acquire module names from";
+ LOG(INFO) << " -b, --use-blocklist: Apply blocklist to module names too";
+ LOG(INFO) << " -d, --dirname=DIR: Load modules from DIR, option may be used multiple times";
+ LOG(INFO) << " -D, --show-depends: Print dependencies for modules only, do not load";
+ LOG(INFO) << " -h, --help: Print this help";
+ LOG(INFO) << " -l, --list: List modules matching pattern";
+ LOG(INFO) << " -r, --remove: Remove MODULE (multiple modules may be specified)";
+ LOG(INFO) << " -s, --syslog: print to syslog also";
+ LOG(INFO) << " -q, --quiet: disable messages";
+ LOG(INFO) << " -v, --verbose: enable more messages, even more with a second -v";
+ LOG(INFO);
}
-#define check_mode() \
- if (mode != AddModulesMode) { \
- std::cerr << "Error, multiple mode flags specified" << std::endl; \
- print_usage(); \
- return EXIT_FAILURE; \
+#define check_mode() \
+ if (mode != AddModulesMode) { \
+ LOG(ERROR) << "multiple mode flags specified"; \
+ print_usage(); \
+ return EXIT_FAILURE; \
}
+std::string stripComments(const std::string& str) {
+ for (std::string rv = str;;) {
+ auto comment = rv.find('#');
+ if (comment == std::string::npos) return rv;
+ auto end = rv.find('\n', comment);
+ if (end != std::string::npos) end = end - comment;
+ rv.erase(comment, end);
+ }
+ /* NOTREACHED */
+}
+
+auto syslog = false;
+
+void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+ const char* file, unsigned int line, const char* message) {
+ android::base::StdioLogger(id, severity, tag, file, line, message);
+ if (syslog && message[0]) {
+ android::base::KernelLogger(id, severity, tag, file, line, message);
+ }
+}
+
+} // anonymous namespace
+
extern "C" int modprobe_main(int argc, char** argv) {
+ android::base::InitLogging(argv, MyLogger);
+ android::base::SetMinimumLogSeverity(android::base::INFO);
+
std::vector<std::string> modules;
std::string module_parameters;
+ std::string mods;
std::vector<std::string> mod_dirs;
modprobe_mode mode = AddModulesMode;
- bool blacklist = false;
- bool verbose = false;
+ bool blocklist = false;
int rv = EXIT_SUCCESS;
int opt;
- while ((opt = getopt(argc, argv, "abd:Dhlqrv")) != -1) {
+ int option_index = 0;
+ // NB: We have non-standard short options -l and -D to make it easier for
+ // OEMs to transition from toybox.
+ // clang-format off
+ static struct option long_options[] = {
+ { "all", optional_argument, 0, 'a' },
+ { "use-blocklist", no_argument, 0, 'b' },
+ { "dirname", required_argument, 0, 'd' },
+ { "show-depends", no_argument, 0, 'D' },
+ { "help", no_argument, 0, 'h' },
+ { "list", no_argument, 0, 'l' },
+ { "quiet", no_argument, 0, 'q' },
+ { "remove", no_argument, 0, 'r' },
+ { "syslog", no_argument, 0, 's' },
+ { "verbose", no_argument, 0, 'v' },
+ };
+ // clang-format on
+ while ((opt = getopt_long(argc, argv, "a::bd:Dhlqrsv", long_options, &option_index)) != -1) {
switch (opt) {
case 'a':
// toybox modprobe supported -a to load multiple modules, this
- // is supported here by default, ignore flag
+ // is supported here by default, ignore flag if no argument.
check_mode();
+ if (optarg == NULL) break;
+ if (!android::base::ReadFileToString(optarg, &mods)) {
+ PLOG(ERROR) << "Failed to open " << optarg;
+ rv = EXIT_FAILURE;
+ }
+ for (auto mod : android::base::Split(stripComments(mods), "\n")) {
+ mod = android::base::Trim(mod);
+ if (mod == "") continue;
+ if (std::find(modules.begin(), modules.end(), mod) != modules.end()) continue;
+ modules.emplace_back(mod);
+ }
break;
case 'b':
- blacklist = true;
+ blocklist = true;
break;
case 'd':
mod_dirs.emplace_back(optarg);
@@ -82,24 +144,33 @@
mode = ShowDependenciesMode;
break;
case 'h':
+ android::base::SetMinimumLogSeverity(android::base::INFO);
print_usage();
- return EXIT_SUCCESS;
+ return rv;
case 'l':
check_mode();
mode = ListModulesMode;
break;
case 'q':
- verbose = false;
+ android::base::SetMinimumLogSeverity(android::base::WARNING);
break;
case 'r':
check_mode();
mode = RemoveModulesMode;
break;
+ case 's':
+ syslog = true;
+ break;
case 'v':
- verbose = true;
+ if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
+ android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ } else {
+ android::base::SetMinimumLogSeverity(android::base::DEBUG);
+ }
break;
default:
- std::cerr << "Unrecognized option: " << opt << std::endl;
+ LOG(ERROR) << "Unrecognized option: " << opt;
+ print_usage();
return EXIT_FAILURE;
}
}
@@ -118,60 +189,54 @@
}
}
- if (verbose) {
- std::cout << "mode is " << mode << std::endl;
- std::cout << "verbose is " << verbose << std::endl;
- std::cout << "mod_dirs is: " << android::base::Join(mod_dirs, "") << std::endl;
- std::cout << "modules is: " << android::base::Join(modules, "") << std::endl;
- std::cout << "module parameters is: " << android::base::Join(module_parameters, "")
- << std::endl;
- }
+ LOG(DEBUG) << "mode is " << mode;
+ LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
+ LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
+ LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
if (modules.empty()) {
if (mode == ListModulesMode) {
// emulate toybox modprobe list with no pattern (list all)
modules.emplace_back("*");
} else {
- std::cerr << "No modules given." << std::endl;
+ LOG(ERROR) << "No modules given.";
print_usage();
return EXIT_FAILURE;
}
}
if (mod_dirs.empty()) {
- std::cerr << "No module configuration directories given." << std::endl;
+ LOG(ERROR) << "No module configuration directories given.";
print_usage();
return EXIT_FAILURE;
}
if (parameter_count && modules.size() > 1) {
- std::cerr << "Only one module may be loaded when specifying module parameters."
- << std::endl;
+ LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
print_usage();
return EXIT_FAILURE;
}
Modprobe m(mod_dirs);
- m.EnableVerbose(verbose);
- if (blacklist) {
- m.EnableBlacklist(true);
+ if (blocklist) {
+ m.EnableBlocklist(true);
}
for (const auto& module : modules) {
switch (mode) {
case AddModulesMode:
if (!m.LoadWithAliases(module, true, module_parameters)) {
- std::cerr << "Failed to load module " << module;
+ PLOG(ERROR) << "Failed to load module " << module;
rv = EXIT_FAILURE;
}
break;
case RemoveModulesMode:
if (!m.Remove(module)) {
- std::cerr << "Failed to remove module " << module;
+ PLOG(ERROR) << "Failed to remove module " << module;
rv = EXIT_FAILURE;
}
break;
case ListModulesMode: {
std::vector<std::string> list = m.ListModules(module);
- std::cout << android::base::Join(list, "\n") << std::endl;
+ LOG(INFO) << android::base::Join(list, "\n");
break;
}
case ShowDependenciesMode: {
@@ -182,17 +247,17 @@
rv = EXIT_FAILURE;
break;
}
- std::cout << "Dependencies for " << module << ":" << std::endl;
- std::cout << "Soft pre-dependencies:" << std::endl;
- std::cout << android::base::Join(pre_deps, "\n") << std::endl;
- std::cout << "Hard dependencies:" << std::endl;
- std::cout << android::base::Join(deps, "\n") << std::endl;
- std::cout << "Soft post-dependencies:" << std::endl;
- std::cout << android::base::Join(post_deps, "\n") << std::endl;
+ LOG(INFO) << "Dependencies for " << module << ":";
+ LOG(INFO) << "Soft pre-dependencies:";
+ LOG(INFO) << android::base::Join(pre_deps, "\n");
+ LOG(INFO) << "Hard dependencies:";
+ LOG(INFO) << android::base::Join(deps, "\n");
+ LOG(INFO) << "Soft post-dependencies:";
+ LOG(INFO) << android::base::Join(post_deps, "\n");
break;
}
default:
- std::cerr << "Bad mode";
+ LOG(ERROR) << "Bad mode";
rv = EXIT_FAILURE;
}
}
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/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 7dfd0d0..d1ed649 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -231,7 +231,7 @@
if (req->read_size) {
/* Prepare SECURITY PROTOCOL IN command. */
- out_cdb.length = __builtin_bswap32(req->read_size);
+ in_cdb.length = __builtin_bswap32(req->read_size);
sg_io_hdr_t io_hdr;
set_sg_io_hdr(&io_hdr, SG_DXFER_FROM_DEV, sizeof(in_cdb), sizeof(sense_buffer),
req->read_size, read_buf, (unsigned char*)&in_cdb, sense_buffer);
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/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
index af97eba..5de1efa 100644
--- a/trusty/utils/rpmb_dev/rpmb_dev.c
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -591,7 +591,7 @@
return EXIT_SUCCESS;
}
- open_flags = O_RDWR;
+ open_flags = O_RDWR | O_SYNC;
if (init) {
open_flags |= O_CREAT | O_TRUNC;
}
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