Merge "Fix GetGlobalVariableOffset with tagged pointer in aarch64"
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index c8dbf77..dcf92be 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -3,3 +3,6 @@
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+
+[Hook Scripts]
+aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/adb/Android.bp b/adb/Android.bp
index 6386a78..432770c 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -165,17 +165,26 @@
"adb_unique_fd.cpp",
"adb_utils.cpp",
"fdevent/fdevent.cpp",
- "fdevent/fdevent_poll.cpp",
"services.cpp",
"sockets.cpp",
"socket_spec.cpp",
"sysdeps/errno.cpp",
"transport.cpp",
"transport_fd.cpp",
- "transport_local.cpp",
"types.cpp",
]
+libadb_darwin_srcs = [
+ "fdevent/fdevent_poll.cpp",
+]
+
+libadb_windows_srcs = [
+ "fdevent/fdevent_poll.cpp",
+ "sysdeps_win32.cpp",
+ "sysdeps/win32/errno.cpp",
+ "sysdeps/win32/stat.cpp",
+]
+
libadb_posix_srcs = [
"sysdeps_unix.cpp",
"sysdeps/posix/network.cpp",
@@ -207,6 +216,7 @@
"client/adb_wifi.cpp",
"client/usb_libusb.cpp",
"client/usb_dispatch.cpp",
+ "client/transport_local.cpp",
"client/transport_mdns.cpp",
"client/transport_usb.cpp",
"client/pairing/pairing_client.cpp",
@@ -219,7 +229,7 @@
srcs: ["client/usb_linux.cpp"] + libadb_linux_srcs,
},
darwin: {
- srcs: ["client/usb_osx.cpp"],
+ srcs: ["client/usb_osx.cpp"] + libadb_darwin_srcs,
},
not_windows: {
srcs: libadb_posix_srcs,
@@ -228,10 +238,7 @@
enabled: true,
srcs: [
"client/usb_windows.cpp",
- "sysdeps_win32.cpp",
- "sysdeps/win32/errno.cpp",
- "sysdeps/win32/stat.cpp",
- ],
+ ] + libadb_windows_srcs,
shared_libs: ["AdbWinApi"],
},
},
@@ -383,10 +390,11 @@
compile_multilib: "both",
srcs: libadb_srcs + libadb_linux_srcs + libadb_posix_srcs + [
+ "daemon/adb_wifi.cpp",
"daemon/auth.cpp",
"daemon/jdwp_service.cpp",
"daemon/logging.cpp",
- "daemon/adb_wifi.cpp",
+ "daemon/transport_local.cpp",
],
generated_headers: ["platform_tools_version"],
@@ -585,7 +593,6 @@
cc_binary {
name: "adbd",
defaults: ["adbd_defaults", "host_adbd_supported", "libadbd_binary_dependencies"],
- stl: "libc++_static",
recovery_available: true,
apex_available: ["com.android.adbd"],
@@ -695,6 +702,7 @@
"daemon/shell_service_test.cpp",
"shell_service_protocol.cpp",
"shell_service_protocol_test.cpp",
+ "mdns_test.cpp",
],
shared_libs: [
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 08d3904..dcec0ba 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -149,7 +149,7 @@
case A_WRTE: tag = "WRTE"; break;
case A_AUTH: tag = "AUTH"; break;
case A_STLS:
- tag = "ATLS";
+ tag = "STLS";
break;
default: tag = "????"; break;
}
@@ -1076,6 +1076,25 @@
g_reject_kill_server = value;
}
+static bool handle_mdns_request(std::string_view service, int reply_fd) {
+ if (!android::base::ConsumePrefix(&service, "mdns:")) {
+ return false;
+ }
+
+ if (service == "check") {
+ std::string check = mdns_check();
+ SendOkay(reply_fd, check);
+ return true;
+ }
+ if (service == "services") {
+ std::string services_list = mdns_list_discovered_services();
+ SendOkay(reply_fd, services_list);
+ return true;
+ }
+
+ return false;
+}
+
HostRequestResult handle_host_request(std::string_view service, TransportType type,
const char* serial, TransportId transport_id, int reply_fd,
asocket* s) {
@@ -1320,6 +1339,10 @@
return HostRequestResult::Handled;
}
+ if (handle_mdns_request(service, reply_fd)) {
+ return HostRequestResult::Handled;
+ }
+
return HostRequestResult::Unhandled;
}
diff --git a/adb/adb_mdns.h b/adb/adb_mdns.h
index 6b37355..3111248 100644
--- a/adb/adb_mdns.h
+++ b/adb/adb_mdns.h
@@ -19,9 +19,14 @@
#include <android-base/macros.h>
-const char* kADBServiceType = "_adb._tcp";
-const char* kADBSecurePairingServiceType = "_adb_secure_pairing._tcp";
-const char* kADBSecureConnectServiceType = "_adb_secure_connect._tcp";
+// The rules for Service Names [RFC6335] state that they may be no more
+// than fifteen characters long (not counting the mandatory underscore),
+// consisting of only letters, digits, and hyphens, must begin and end
+// with a letter or digit, must not contain consecutive hyphens, and
+// must contain at least one letter.
+#define ADB_MDNS_SERVICE_TYPE "adb"
+#define ADB_MDNS_TLS_PAIRING_TYPE "adb-tls-pairing"
+#define ADB_MDNS_TLS_CONNECT_TYPE "adb-tls-connect"
const int kADBTransportServiceRefIndex = 0;
const int kADBSecurePairingServiceRefIndex = 1;
@@ -71,11 +76,10 @@
const char* kADBSecureConnectServiceTxtRecord =
ADB_SECURE_SERVICE_VERSION_TXT_RECORD(ADB_SECURE_SERVICE_VERSION);
-const char* kADBDNSServices[] = {
- kADBServiceType,
- kADBSecurePairingServiceType,
- kADBSecureConnectServiceType,
-};
+#define ADB_FULL_MDNS_SERVICE_TYPE(atype) ("_" atype "._tcp")
+const char* kADBDNSServices[] = {ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_SERVICE_TYPE),
+ ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_PAIRING_TYPE),
+ ADB_FULL_MDNS_SERVICE_TYPE(ADB_MDNS_TLS_CONNECT_TYPE)};
const char* kADBDNSServiceTxtRecords[] = {
nullptr,
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index cea24fe..c579dde 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -89,18 +89,13 @@
int adb_trace_mask;
-std::string get_trace_setting_from_env() {
+std::string get_trace_setting() {
+#if ADB_HOST
const char* setting = getenv("ADB_TRACE");
if (setting == nullptr) {
setting = "";
}
-
- return std::string(setting);
-}
-
-std::string get_trace_setting() {
-#if ADB_HOST
- return get_trace_setting_from_env();
+ return setting;
#else
return android::base::GetProperty("persist.adb.trace_mask", "");
#endif
diff --git a/adb/adb_wifi.h b/adb/adb_wifi.h
index 585748c..3a6b0b1 100644
--- a/adb/adb_wifi.h
+++ b/adb/adb_wifi.h
@@ -27,6 +27,9 @@
std::string& response);
bool adb_wifi_is_known_host(const std::string& host);
+std::string mdns_check();
+std::string mdns_list_discovered_services();
+
#else // !ADB_HOST
struct AdbdAuthContext;
diff --git a/adb/apex/Android.bp b/adb/apex/Android.bp
index 4346f67..ddb17da 100644
--- a/adb/apex/Android.bp
+++ b/adb/apex/Android.bp
@@ -1,6 +1,7 @@
apex_defaults {
name: "com.android.adbd-defaults",
updatable: true,
+ min_sdk_version: "R",
binaries: ["adbd"],
compile_multilib: "both",
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 0ad0465..31c938c 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -416,14 +416,18 @@
return android::base::StringPrintf("%s:%s", prefix, command);
}
-const FeatureSet& adb_get_feature_set() {
- static const android::base::NoDestructor<FeatureSet> features([] {
- std::string result;
- if (!adb_query(format_host_command("features"), &result, &result)) {
- fprintf(stderr, "failed to get feature set: %s\n", result.c_str());
- return FeatureSet{};
- }
- return StringToFeatureSet(result);
- }());
- return *features;
+const std::optional<FeatureSet>& adb_get_feature_set(std::string* error) {
+ static std::string cached_error [[clang::no_destroy]];
+ static const std::optional<FeatureSet> features
+ [[clang::no_destroy]] ([]() -> std::optional<FeatureSet> {
+ std::string result;
+ if (adb_query(format_host_command("features"), &result, &cached_error)) {
+ return StringToFeatureSet(result);
+ }
+ return std::nullopt;
+ }());
+ if (!features && error) {
+ *error = cached_error;
+ }
+ return features;
}
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index bf0be40..caf4e86 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -76,7 +76,7 @@
std::string format_host_command(const char* _Nonnull command);
// Get the feature set of the current preferred transport.
-const FeatureSet& adb_get_feature_set();
+const std::optional<FeatureSet>& adb_get_feature_set(std::string* _Nullable error);
#if defined(__linux__)
// Get the path of a file containing the path to the server executable, if the socket spec set via
@@ -90,8 +90,9 @@
// ADB Secure DNS service interface. Used to query what ADB Secure DNS services have been
// resolved, and to run some kind of callback for each one.
-using adb_secure_foreach_service_callback = std::function<void(
- const char* _Nonnull service_name, const char* _Nonnull ip_address, uint16_t port)>;
+using adb_secure_foreach_service_callback =
+ std::function<void(const char* _Nonnull service_name, const char* _Nonnull reg_type,
+ const char* _Nonnull ip_address, uint16_t port)>;
// Queries pairing/connect services that have been discovered and resolved.
// If |host_name| is not null, run |cb| only for services
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 3810cc8..e562f8b 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -57,11 +57,12 @@
}
static bool can_use_feature(const char* feature) {
- auto&& features = adb_get_feature_set();
- if (features.empty()) {
+ // We ignore errors here, if the device is missing, we'll notice when we try to push install.
+ auto&& features = adb_get_feature_set(nullptr);
+ if (!features) {
return false;
}
- return CanUseFeature(features, feature);
+ return CanUseFeature(*features, feature);
}
static InstallMode best_install_mode() {
@@ -153,6 +154,14 @@
*buf = '\0';
}
+static unique_fd send_command(const std::vector<std::string>& cmd_args, std::string* error) {
+ if (is_abb_exec_supported()) {
+ return send_abb_exec_command(cmd_args, error);
+ } else {
+ return unique_fd(adb_connect(android::base::Join(cmd_args, " "), error));
+ }
+}
+
static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) {
printf("Performing Streamed Install\n");
@@ -225,12 +234,7 @@
cmd_args.push_back("--apex");
}
- unique_fd remote_fd;
- if (use_abb_exec) {
- remote_fd = send_abb_exec_command(cmd_args, &error);
- } else {
- remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error));
- }
+ unique_fd remote_fd = send_command(cmd_args, &error);
if (remote_fd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
return 1;
@@ -546,24 +550,28 @@
if (first_apk == -1) error_exit("need APK file on command line");
- std::string install_cmd;
- if (best_install_mode() == INSTALL_PUSH) {
- install_cmd = "exec:pm";
- } else {
- install_cmd = "exec:cmd package";
- }
+ const bool use_abb_exec = is_abb_exec_supported();
- std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64,
- install_cmd.c_str(), total_size);
+ const std::string install_cmd =
+ use_abb_exec ? "package"
+ : best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package";
+
+ std::vector<std::string> cmd_args = {install_cmd, "install-create", "-S",
+ std::to_string(total_size)};
+ cmd_args.reserve(first_apk + 4);
for (int i = 1; i < first_apk; i++) {
- cmd += " " + escape_arg(argv[i]);
+ if (use_abb_exec) {
+ cmd_args.push_back(argv[i]);
+ } else {
+ cmd_args.push_back(escape_arg(argv[i]));
+ }
}
// Create install session
std::string error;
char buf[BUFSIZ];
{
- unique_fd fd(adb_connect(cmd, &error));
+ unique_fd fd = send_command(cmd_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
return EXIT_FAILURE;
@@ -585,6 +593,7 @@
fputs(buf, stderr);
return EXIT_FAILURE;
}
+ const auto session_id_str = std::to_string(session_id);
// Valid session, now stream the APKs
bool success = true;
@@ -597,10 +606,15 @@
goto finalize_session;
}
- std::string cmd =
- android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -",
- install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
- session_id, android::base::Basename(file).c_str());
+ std::vector<std::string> cmd_args = {
+ install_cmd,
+ "install-write",
+ "-S",
+ std::to_string(sb.st_size),
+ session_id_str,
+ android::base::Basename(file),
+ "-",
+ };
unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
if (local_fd < 0) {
@@ -610,7 +624,7 @@
}
std::string error;
- unique_fd remote_fd(adb_connect(cmd, &error));
+ unique_fd remote_fd = send_command(cmd_args, &error);
if (remote_fd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
success = false;
@@ -635,10 +649,13 @@
finalize_session:
// Commit session if we streamed everything okay; otherwise abandon.
- std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
- success ? "commit" : "abandon", session_id);
+ std::vector<std::string> service_args = {
+ install_cmd,
+ success ? "install-commit" : "install-abandon",
+ session_id_str,
+ };
{
- unique_fd fd(adb_connect(service, &error));
+ unique_fd fd = send_command(service_args, &error);
if (fd < 0) {
fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
return EXIT_FAILURE;
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 4b2fa04..b674a81 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -504,6 +504,12 @@
}).detach();
}
+// Callback given to SSL_set_cert_cb to select a certificate when server requests
+// for a certificate. This is where the server will give us a CA-issuer list, and
+// figure out if the server knows any of our public keys. We currently always return
+// 1 here to indicate success, since we always try a key here (in the case of no auth).
+// See https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_set_cert_cb
+// for more details.
int adb_tls_set_certificate(SSL* ssl) {
LOG(INFO) << __func__;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 6a7493f..f0a287d 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -127,6 +127,8 @@
" localfilesystem:<unix domain socket name>\n"
" reverse --remove REMOTE remove specific reverse socket connection\n"
" reverse --remove-all remove all reverse socket connections from device\n"
+ " mdns check check if mdns discovery is available\n"
+ " mdns services list all discovered services\n"
"\n"
"file transfer:\n"
" push [--sync] [-z ALGORITHM] [-Z] LOCAL... REMOTE\n"
@@ -238,6 +240,7 @@
" $ANDROID_SERIAL serial number to connect to (see -s)\n"
" $ANDROID_LOG_TAGS tags to be used by logcat (see logcat --help)\n"
" $ADB_LOCAL_TRANSPORT_MAX_PORT max emulator scan port (default 5585, 16 emus)\n"
+ " $ADB_MDNS_AUTO_CONNECT comma-separated list of mdns services to allow auto-connect (default adb-tls-connect)\n"
);
// clang-format on
}
@@ -672,16 +675,17 @@
}
static int adb_shell(int argc, const char** argv) {
- auto&& features = adb_get_feature_set();
- if (features.empty()) {
- return 1;
+ std::string error;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ error_exit("%s", error.c_str());
}
enum PtyAllocationMode { kPtyAuto, kPtyNo, kPtyYes, kPtyDefinitely };
// Defaults.
- char escape_char = '~'; // -e
- bool use_shell_protocol = CanUseFeature(features, kFeatureShell2); // -x
+ char escape_char = '~'; // -e
+ bool use_shell_protocol = CanUseFeature(*features, kFeatureShell2); // -x
PtyAllocationMode tty = use_shell_protocol ? kPtyAuto : kPtyDefinitely; // -t/-T
// Parse shell-specific command-line options.
@@ -757,7 +761,7 @@
if (!use_shell_protocol) {
if (shell_type_arg != kShellServiceArgPty) {
fprintf(stderr, "error: %s only supports allocating a pty\n",
- !CanUseFeature(features, kFeatureShell2) ? "device" : "-x");
+ !CanUseFeature(*features, kFeatureShell2) ? "device" : "-x");
return 1;
} else {
// If we're not using the shell protocol, the type argument must be empty.
@@ -777,11 +781,13 @@
}
static int adb_abb(int argc, const char** argv) {
- auto&& features = adb_get_feature_set();
- if (features.empty()) {
+ std::string error;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ error_exit("%s", error.c_str());
return 1;
}
- if (!CanUseFeature(features, kFeatureAbb)) {
+ if (!CanUseFeature(*features, kFeatureAbb)) {
error_exit("abb is not supported by the device");
}
@@ -1159,9 +1165,9 @@
// Use shell protocol if it's supported and the caller doesn't explicitly
// disable it.
if (!disable_shell_protocol) {
- auto&& features = adb_get_feature_set();
- if (!features.empty()) {
- use_shell_protocol = CanUseFeature(features, kFeatureShell2);
+ auto&& features = adb_get_feature_set(nullptr);
+ if (features) {
+ use_shell_protocol = CanUseFeature(*features, kFeatureShell2);
} else {
// Device was unreachable.
attempt_connection = false;
@@ -1810,12 +1816,13 @@
}
return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
} else if (!strcmp(argv[0], "remount")) {
- auto&& features = adb_get_feature_set();
- if (features.empty()) {
- return 1;
+ std::string error;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ error_exit("%s", error.c_str());
}
- if (CanUseFeature(features, kFeatureRemountShell)) {
+ if (CanUseFeature(*features, kFeatureRemountShell)) {
std::vector<const char*> args = {"shell"};
args.insert(args.cend(), argv, argv + argc);
return adb_shell_noinput(args.size(), args.data());
@@ -1906,6 +1913,29 @@
ReadOrderlyShutdown(fd);
return 0;
+ } else if (!strcmp(argv[0], "mdns")) {
+ --argc;
+ if (argc < 1) error_exit("mdns requires an argument");
+ ++argv;
+
+ std::string error;
+ if (!adb_check_server_version(&error)) {
+ error_exit("failed to check server version: %s", error.c_str());
+ }
+
+ std::string query = "host:mdns:";
+ if (!strcmp(argv[0], "check")) {
+ if (argc != 1) error_exit("mdns %s doesn't take any arguments", argv[0]);
+ query += "check";
+ } else if (!strcmp(argv[0], "services")) {
+ if (argc != 1) error_exit("mdns %s doesn't take any arguments", argv[0]);
+ query += "services";
+ printf("List of discovered mdns services\n");
+ } else {
+ error_exit("unknown mdns command [%s]", argv[0]);
+ }
+
+ return adb_query_command(query);
}
/* do_sync_*() commands */
else if (!strcmp(argv[0], "ls")) {
@@ -2034,11 +2064,12 @@
} else if (!strcmp(argv[0], "track-jdwp")) {
return adb_connect_command("track-jdwp");
} else if (!strcmp(argv[0], "track-app")) {
- auto&& features = adb_get_feature_set();
- if (features.empty()) {
- return 1;
+ std::string error;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ error_exit("%s", error.c_str());
}
- if (!CanUseFeature(features, kFeatureTrackApp)) {
+ if (!CanUseFeature(*features, kFeatureTrackApp)) {
error_exit("track-app is not supported by the device");
}
TrackAppStreamsCallback callback;
@@ -2064,13 +2095,14 @@
return 0;
} else if (!strcmp(argv[0], "features")) {
// Only list the features common to both the adb client and the device.
- auto&& features = adb_get_feature_set();
- if (features.empty()) {
- return 1;
+ std::string error;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ error_exit("%s", error.c_str());
}
- for (const std::string& name : features) {
- if (CanUseFeature(features, name)) {
+ for (const std::string& name : *features) {
+ if (CanUseFeature(*features, name)) {
printf("%s\n", name.c_str());
}
}
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index f2c673a..7185939 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -225,21 +225,22 @@
class SyncConnection {
public:
- SyncConnection()
- : acknowledgement_buffer_(sizeof(sync_status) + SYNC_DATA_MAX),
- features_(adb_get_feature_set()) {
+ SyncConnection() : acknowledgement_buffer_(sizeof(sync_status) + SYNC_DATA_MAX) {
acknowledgement_buffer_.resize(0);
max = SYNC_DATA_MAX; // TODO: decide at runtime.
- if (features_.empty()) {
- Error("failed to get feature set");
+ std::string error;
+ auto&& features = adb_get_feature_set(&error);
+ if (!features) {
+ Error("failed to get feature set: %s", error.c_str());
} else {
- have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
- have_ls_v2_ = CanUseFeature(features_, kFeatureLs2);
- have_sendrecv_v2_ = CanUseFeature(features_, kFeatureSendRecv2);
- have_sendrecv_v2_brotli_ = CanUseFeature(features_, kFeatureSendRecv2Brotli);
- have_sendrecv_v2_lz4_ = CanUseFeature(features_, kFeatureSendRecv2LZ4);
- have_sendrecv_v2_dry_run_send_ = CanUseFeature(features_, kFeatureSendRecv2DryRunSend);
+ features_ = &*features;
+ have_stat_v2_ = CanUseFeature(*features, kFeatureStat2);
+ have_ls_v2_ = CanUseFeature(*features, kFeatureLs2);
+ have_sendrecv_v2_ = CanUseFeature(*features, kFeatureSendRecv2);
+ have_sendrecv_v2_brotli_ = CanUseFeature(*features, kFeatureSendRecv2Brotli);
+ have_sendrecv_v2_lz4_ = CanUseFeature(*features, kFeatureSendRecv2LZ4);
+ have_sendrecv_v2_dry_run_send_ = CanUseFeature(*features, kFeatureSendRecv2DryRunSend);
std::string error;
fd.reset(adb_connect("sync:", &error));
if (fd < 0) {
@@ -283,7 +284,7 @@
return compression;
}
- const FeatureSet& Features() const { return features_; }
+ const FeatureSet& Features() const { return *features_; }
bool IsValid() { return fd >= 0; }
@@ -921,7 +922,7 @@
private:
std::deque<std::pair<std::string, std::string>> deferred_acknowledgements_;
Block acknowledgement_buffer_;
- const FeatureSet& features_;
+ const FeatureSet* features_ = nullptr;
bool have_stat_v2_;
bool have_ls_v2_;
bool have_sendrecv_v2_;
diff --git a/adb/client/incremental_server.cpp b/adb/client/incremental_server.cpp
index 1a1361c..bfe18c0 100644
--- a/adb/client/incremental_server.cpp
+++ b/adb/client/incremental_server.cpp
@@ -264,7 +264,7 @@
char* pendingBlocks_ = nullptr;
// True when client notifies that all the data has been received
- bool servingComplete_;
+ bool servingComplete_ = false;
};
bool IncrementalServer::SkipToRequest(void* buffer, size_t* size, bool blocking) {
diff --git a/adb/client/incremental_utils.h b/adb/client/incremental_utils.h
index d969d94..fe2914d 100644
--- a/adb/client/incremental_utils.h
+++ b/adb/client/incremental_utils.h
@@ -25,6 +25,8 @@
#include <stdint.h>
+#include <android-base/off64_t.h>
+
namespace incremental {
using Size = int64_t;
diff --git a/adb/transport_local.cpp b/adb/client/transport_local.cpp
similarity index 78%
rename from adb/transport_local.cpp
rename to adb/client/transport_local.cpp
index 5ec8e16..15a0724 100644
--- a/adb/transport_local.cpp
+++ b/adb/client/transport_local.cpp
@@ -38,10 +38,6 @@
#include <android-base/thread_annotations.h>
#include <cutils/sockets.h>
-#if !ADB_HOST
-#include <android-base/properties.h>
-#endif
-
#include "adb.h"
#include "adb_io.h"
#include "adb_unique_fd.h"
@@ -49,8 +45,6 @@
#include "socket_spec.h"
#include "sysdeps/chrono.h"
-#if ADB_HOST
-
// Android Wear has been using port 5601 in all of its documentation/tooling,
// but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX].
// Avoid stomping on their port by restricting the active scanning range.
@@ -76,9 +70,8 @@
// We keep a map from emulator port to transport.
// TODO: weak_ptr?
-static auto& local_transports GUARDED_BY(local_transports_lock) =
- *new std::unordered_map<int, atransport*>();
-#endif /* ADB_HOST */
+static std::unordered_map<int, atransport*> local_transports
+ [[clang::no_destroy]] GUARDED_BY(local_transports_lock);
bool local_connect(int port) {
std::string dummy;
@@ -140,21 +133,19 @@
}
}
-
int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error) {
unique_fd fd;
-#if ADB_HOST
if (find_emulator_transport_by_adb_port(adb_port) != nullptr ||
find_emulator_transport_by_console_port(console_port) != nullptr) {
return -1;
}
- const char *host = getenv("ADBHOST");
+ const char* host = getenv("ADBHOST");
if (host) {
fd.reset(network_connect(host, adb_port, SOCK_STREAM, 0, error));
}
-#endif
+
if (fd < 0) {
fd.reset(network_loopback_client(adb_port, SOCK_STREAM, error));
}
@@ -173,8 +164,6 @@
return -1;
}
-#if ADB_HOST
-
static void PollAllLocalPortsForEmulator() {
// Try to connect to any number of running emulator instances.
for (int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; port <= adb_local_transport_max_port;
@@ -194,8 +183,8 @@
// Retry emulators just kicked.
static std::vector<RetryPort>& retry_ports = *new std::vector<RetryPort>;
-std::mutex &retry_ports_lock = *new std::mutex;
-std::condition_variable &retry_ports_cond = *new std::condition_variable;
+std::mutex& retry_ports_lock = *new std::mutex;
+std::condition_variable& retry_ports_cond = *new std::condition_variable;
static void client_socket_thread(std::string_view) {
adb_thread_setname("client_socket_thread");
@@ -220,7 +209,7 @@
std::vector<RetryPort> next_ports;
for (auto& port : ports) {
VLOG(TRANSPORT) << "retry port " << port.port << ", last retry_count "
- << port.retry_count;
+ << port.retry_count;
if (local_connect(port.port)) {
VLOG(TRANSPORT) << "retry port " << port.port << " successfully";
continue;
@@ -240,77 +229,12 @@
}
}
-#else // !ADB_HOST
-
-void server_socket_thread(std::function<unique_fd(std::string_view, std::string*)> listen_func,
- std::string_view addr) {
- adb_thread_setname("server socket");
-
- unique_fd serverfd;
- std::string error;
-
- while (serverfd == -1) {
- errno = 0;
- serverfd = listen_func(addr, &error);
- if (errno == EAFNOSUPPORT || errno == EINVAL || errno == EPROTONOSUPPORT) {
- D("unrecoverable error: '%s'", error.c_str());
- return;
- } else if (serverfd < 0) {
- D("server: cannot bind socket yet: %s", error.c_str());
- std::this_thread::sleep_for(1s);
- continue;
- }
- close_on_exec(serverfd.get());
- }
-
- while (true) {
- D("server: trying to get new connection from fd %d", serverfd.get());
- unique_fd fd(adb_socket_accept(serverfd, nullptr, nullptr));
- if (fd >= 0) {
- D("server: new connection on fd %d", fd.get());
- close_on_exec(fd.get());
- disable_tcp_nagle(fd.get());
- std::string serial = android::base::StringPrintf("host-%d", fd.get());
- // We don't care about port value in "register_socket_transport" as it is used
- // only from ADB_HOST. "server_socket_thread" is never called from ADB_HOST.
- register_socket_transport(
- std::move(fd), std::move(serial), 0, 1,
- [](atransport*) { return ReconnectResult::Abort; }, false);
- }
- }
- D("transport: server_socket_thread() exiting");
-}
-
-#endif
-
-#if !ADB_HOST
-unique_fd adb_listen(std::string_view addr, std::string* error) {
- return unique_fd{socket_spec_listen(addr, error, nullptr)};
-}
-#endif
-
void local_init(const std::string& addr) {
-#if ADB_HOST
D("transport: local client init");
std::thread(client_socket_thread, addr).detach();
adb_local_transport_max_port_env_override();
-#elif !defined(__ANDROID__)
- // Host adbd.
- D("transport: local server init");
- std::thread(server_socket_thread, adb_listen, addr).detach();
-#else
- D("transport: local server init");
- // For the adbd daemon in the system image we need to distinguish
- // between the device, and the emulator.
- if (addr.starts_with("tcp:") && use_qemu_goldfish()) {
- std::thread(qemu_socket_thread, addr).detach();
- } else {
- std::thread(server_socket_thread, adb_listen, addr).detach();
- }
-#endif // !ADB_HOST
}
-#if ADB_HOST
struct EmulatorConnection : public FdConnection {
EmulatorConnection(unique_fd fd, int local_port)
: FdConnection(std::move(fd)), local_port_(local_port) {}
@@ -336,7 +260,7 @@
/* Only call this function if you already hold local_transports_lock. */
static atransport* find_emulator_transport_by_adb_port_locked(int adb_port)
- REQUIRES(local_transports_lock) {
+ REQUIRES(local_transports_lock) {
auto it = local_transports.find(adb_port);
if (it == local_transports.end()) {
return nullptr;
@@ -352,7 +276,6 @@
atransport* find_emulator_transport_by_console_port(int console_port) {
return find_transport(getEmulatorSerialString(console_port).c_str());
}
-#endif
std::string getEmulatorSerialString(int console_port) {
return android::base::StringPrintf("emulator-%d", console_port);
@@ -363,7 +286,6 @@
t->type = kTransportLocal;
-#if ADB_HOST
// Emulator connection.
if (local) {
auto emulator_connection = std::make_unique<EmulatorConnection>(std::move(fd), adb_port);
@@ -380,7 +302,6 @@
return fail;
}
-#endif
// Regular tcp connection.
auto fd_connection = std::make_unique<FdConnection>(std::move(fd));
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 22b9b18..2b6aa7c 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -26,6 +26,7 @@
#include <memory>
#include <thread>
+#include <unordered_set>
#include <vector>
#include <android-base/stringprintf.h>
@@ -42,27 +43,75 @@
static DNSServiceRef service_refs[kNumADBDNSServices];
static fdevent* service_ref_fdes[kNumADBDNSServices];
+static auto& g_autoconn_whitelist = *new std::unordered_set<int>();
-static int adb_DNSServiceIndexByName(const char* regType) {
+static int adb_DNSServiceIndexByName(std::string_view regType) {
for (int i = 0; i < kNumADBDNSServices; ++i) {
- if (!strncmp(regType, kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
+ if (!strncmp(regType.data(), kADBDNSServices[i], strlen(kADBDNSServices[i]))) {
return i;
}
}
return -1;
}
-static bool adb_DNSServiceShouldConnect(const char* regType, const char* serviceName) {
- int index = adb_DNSServiceIndexByName(regType);
- if (index == kADBTransportServiceRefIndex) {
- // Ignore adb-EMULATOR* service names, as it interferes with the
- // emulator ports that are already connected.
- if (android::base::StartsWith(serviceName, "adb-EMULATOR")) {
- LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]";
- return false;
+static void config_auto_connect_services() {
+ // ADB_MDNS_AUTO_CONNECT is a comma-delimited list of mdns services
+ // that are allowed to auto-connect. By default, only allow "adb-tls-connect"
+ // to auto-connect, since this is filtered down to auto-connect only to paired
+ // devices.
+ g_autoconn_whitelist.insert(kADBSecureConnectServiceRefIndex);
+ const char* srvs = getenv("ADB_MDNS_AUTO_CONNECT");
+ if (!srvs) {
+ return;
+ }
+
+ if (strcmp(srvs, "0") == 0) {
+ D("Disabling all auto-connecting");
+ g_autoconn_whitelist.clear();
+ return;
+ }
+
+ if (strcmp(srvs, "1") == 0) {
+ D("Allow all auto-connecting");
+ g_autoconn_whitelist.insert(kADBTransportServiceRefIndex);
+ return;
+ }
+
+ // Selectively choose which services to allow auto-connect.
+ // E.g. ADB_MDNS_AUTO_CONNECT=adb,adb-tls-connect would allow
+ // _adb._tcp and _adb-tls-connnect._tcp services to auto-connect.
+ auto srvs_list = android::base::Split(srvs, ",");
+ std::unordered_set<int> new_whitelist;
+ for (const auto& item : srvs_list) {
+ auto full_srv = android::base::StringPrintf("_%s._tcp", item.data());
+ int idx = adb_DNSServiceIndexByName(full_srv);
+ if (idx >= 0) {
+ new_whitelist.insert(idx);
}
}
- return (index == kADBTransportServiceRefIndex || index == kADBSecureConnectServiceRefIndex);
+
+ if (!new_whitelist.empty()) {
+ g_autoconn_whitelist = std::move(new_whitelist);
+ }
+}
+
+static bool adb_DNSServiceShouldAutoConnect(const char* regType, const char* serviceName) {
+ // Try to auto-connect to any "_adb" or "_adb-tls-connect" services excluding emulator services.
+ int index = adb_DNSServiceIndexByName(regType);
+ if (index != kADBTransportServiceRefIndex && index != kADBSecureConnectServiceRefIndex) {
+ return false;
+ }
+ if (g_autoconn_whitelist.find(index) == g_autoconn_whitelist.end()) {
+ D("Auto-connect for regType '%s' disabled", regType);
+ return false;
+ }
+ // Ignore adb-EMULATOR* service names, as it interferes with the
+ // emulator ports that are already connected.
+ if (android::base::StartsWith(serviceName, "adb-EMULATOR")) {
+ LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]";
+ return false;
+ }
+ return true;
}
// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
@@ -196,7 +245,7 @@
// adb secure service needs to do something different from just
// connecting here.
- if (adb_DNSServiceShouldConnect(regType_.c_str(), serviceName_.c_str())) {
+ if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) {
std::string response;
D("Attempting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)", serviceName_.c_str(),
regType_.c_str(), ip_addr_, port_);
@@ -216,6 +265,9 @@
int adbSecureServiceType = serviceIndex();
switch (adbSecureServiceType) {
+ case kADBTransportServiceRefIndex:
+ sAdbTransportServices->push_back(this);
+ break;
case kADBSecurePairingServiceRefIndex:
sAdbSecurePairingServices->push_back(this);
break;
@@ -233,16 +285,21 @@
std::string serviceName() const { return serviceName_; }
+ std::string regType() const { return regType_; }
+
std::string ipAddress() const { return ip_addr_; }
uint16_t port() const { return port_; }
using ServiceRegistry = std::vector<ResolvedService*>;
+ // unencrypted tcp connections
+ static ServiceRegistry* sAdbTransportServices;
+
static ServiceRegistry* sAdbSecurePairingServices;
static ServiceRegistry* sAdbSecureConnectServices;
- static void initAdbSecure();
+ static void initAdbServiceRegistries();
static void forEachService(const ServiceRegistry& services, const std::string& hostname,
adb_secure_foreach_service_callback cb);
@@ -264,13 +321,19 @@
};
// static
+std::vector<ResolvedService*>* ResolvedService::sAdbTransportServices = NULL;
+
+// static
std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL;
// static
std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL;
// static
-void ResolvedService::initAdbSecure() {
+void ResolvedService::initAdbServiceRegistries() {
+ if (!sAdbTransportServices) {
+ sAdbTransportServices = new ServiceRegistry;
+ }
if (!sAdbSecurePairingServices) {
sAdbSecurePairingServices = new ServiceRegistry;
}
@@ -283,17 +346,18 @@
void ResolvedService::forEachService(const ServiceRegistry& services,
const std::string& wanted_service_name,
adb_secure_foreach_service_callback cb) {
- initAdbSecure();
+ initAdbServiceRegistries();
for (auto service : services) {
auto service_name = service->serviceName();
+ auto reg_type = service->regType();
auto ip = service->ipAddress();
auto port = service->port();
if (wanted_service_name == "") {
- cb(service_name.c_str(), ip.c_str(), port);
+ cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
} else if (service_name == wanted_service_name) {
- cb(service_name.c_str(), ip.c_str(), port);
+ cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port);
}
}
}
@@ -301,7 +365,7 @@
// static
bool ResolvedService::connectByServiceName(const ServiceRegistry& services,
const std::string& service_name) {
- initAdbSecure();
+ initAdbServiceRegistries();
for (auto service : services) {
if (service_name == service->serviceName()) {
D("Got service_name match [%s]", service->serviceName().c_str());
@@ -398,6 +462,9 @@
int index = adb_DNSServiceIndexByName(regType);
ResolvedService::ServiceRegistry* services;
switch (index) {
+ case kADBTransportServiceRefIndex:
+ services = ResolvedService::sAdbTransportServices;
+ break;
case kADBSecurePairingServiceRefIndex:
services = ResolvedService::sAdbSecurePairingServices;
break;
@@ -521,8 +588,15 @@
}
void init_mdns_transport_discovery_thread(void) {
- int errorCodes[kNumADBDNSServices];
+ config_auto_connect_services();
+ std::string res;
+ std::for_each(g_autoconn_whitelist.begin(), g_autoconn_whitelist.end(), [&](const int& i) {
+ res += kADBDNSServices[i];
+ res += ",";
+ });
+ D("mdns auto-connect whitelist: [%s]", res.data());
+ int errorCodes[kNumADBDNSServices];
for (int i = 0; i < kNumADBDNSServices; ++i) {
errorCodes[i] = DNSServiceBrowse(&service_refs[i], 0, 0, kADBDNSServices[i], nullptr,
on_service_browsed, nullptr);
@@ -542,6 +616,34 @@
}
void init_mdns_transport_discovery(void) {
- ResolvedService::initAdbSecure();
+ ResolvedService::initAdbServiceRegistries();
std::thread(init_mdns_transport_discovery_thread).detach();
}
+
+std::string mdns_check() {
+ uint32_t daemon_version;
+ uint32_t sz = sizeof(daemon_version);
+
+ auto dnserr = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &daemon_version, &sz);
+ std::string result = "ERROR: mdns daemon unavailable";
+ if (dnserr != kDNSServiceErr_NoError) {
+ return result;
+ }
+
+ result = android::base::StringPrintf("mdns daemon version [%u]", daemon_version);
+ return result;
+}
+
+std::string mdns_list_discovered_services() {
+ std::string result;
+ auto cb = [&](const char* service_name, const char* reg_type, const char* ip_addr,
+ uint16_t port) {
+ result += android::base::StringPrintf("%s\t%s\t%s:%u\n", service_name, reg_type, ip_addr,
+ port);
+ };
+
+ ResolvedService::forEachService(*ResolvedService::sAdbTransportServices, "", cb);
+ ResolvedService::forEachService(*ResolvedService::sAdbSecureConnectServices, "", cb);
+ ResolvedService::forEachService(*ResolvedService::sAdbSecurePairingServices, "", cb);
+ return result;
+}
diff --git a/adb/coverage/.gitignore b/adb/coverage/.gitignore
new file mode 100644
index 0000000..b6a2582
--- /dev/null
+++ b/adb/coverage/.gitignore
@@ -0,0 +1,2 @@
+/adbd.profdata
+/report
diff --git a/adb/coverage/gen_coverage.sh b/adb/coverage/gen_coverage.sh
new file mode 100755
index 0000000..cced62a
--- /dev/null
+++ b/adb/coverage/gen_coverage.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+OUTPUT_DIR=$(dirname "$0")
+. "$OUTPUT_DIR"/include.sh
+
+TRACEDIR=`mktemp -d`
+
+### Make sure we can connect to the device.
+
+# Get the device's wlan0 address.
+IP_ADDR=$(adb shell ip route get 0.0.0.0 oif wlan0 | sed -En -e 's/.*src (\S+)\s.*/\1/p')
+REMOTE_PORT=5555
+REMOTE=$IP_ADDR:$REMOTE_PORT
+LOCAL_SERIAL=$(adb shell getprop ro.serialno)
+
+# Check that we can connect to it.
+adb disconnect
+adb tcpip $REMOTE_PORT
+
+# TODO: Add `adb transport-id` and wait-for-offline on it.
+sleep 5
+
+adb connect $REMOTE
+
+REMOTE_FETCHED_SERIAL=$(adb -s $REMOTE shell getprop ro.serialno)
+
+if [[ "$LOCAL_SERIAL" != "$REMOTE_FETCHED_SERIAL" ]]; then
+ echo "Mismatch: local serial = $LOCAL_SERIAL, remote serial = $REMOTE_FETCHED_SERIAL"
+ exit 1
+fi
+
+# Back to USB, and make sure adbd is root.
+adb disconnect $REMOTE
+
+adb root
+adb wait-for-device usb
+
+# TODO: Add `adb transport-id` and wait-for-offline on it.
+sleep 5
+
+adb wait-for-device
+
+### Run the adb unit tests and fetch traces from them.
+mkdir "$TRACEDIR"/test_traces
+adb shell rm -rf /data/local/tmp/adb_coverage
+adb shell mkdir /data/local/tmp/adb_coverage
+
+for TEST in $ADB_TESTS; do
+ adb shell LLVM_PROFILE_FILE=/data/local/tmp/adb_coverage/$TEST.profraw /data/nativetest64/$TEST/$TEST
+ adb pull /data/local/tmp/adb_coverage/$TEST.profraw "$TRACEDIR"/test_traces/
+done
+
+adb pull /data/local/tmp/adb_coverage "$TRACEDIR"/test_traces
+
+# Clear logcat and increase the buffer to something ridiculous so we can fetch the pids of adbd later.
+adb shell logcat -c -G128M
+
+# Turn on extremely verbose logging so as to not count debug logging against us.
+adb shell setprop persist.adb.trace_mask 1
+
+### Run test_device.py over USB.
+adb shell killall adbd
+
+# TODO: Add `adb transport-id` and wait-for-offline on it.
+sleep 5
+
+adb wait-for-device shell rm -rf "/data/misc/trace/*" /data/local/tmp/adb_coverage/
+"$OUTPUT_DIR"/../test_device.py
+
+# Do a usb reset to exercise the disconnect code.
+adb_usbreset
+adb wait-for-device
+
+# Dump traces from the currently running adbd.
+adb shell killall -37 adbd
+
+echo Waiting for adbd to finish dumping traces
+sleep 5
+
+# Restart adbd in tcp mode.
+adb tcpip $REMOTE_PORT
+sleep 5
+adb connect $REMOTE
+adb -s $REMOTE wait-for-device
+
+# Run test_device.py again.
+ANDROID_SERIAL=$REMOTE "$OUTPUT_DIR"/../test_device.py
+
+# Dump traces again.
+adb disconnect $REMOTE
+adb shell killall -37 adbd
+
+echo Waiting for adbd to finish dumping traces
+sleep 5
+
+adb pull /data/misc/trace "$TRACEDIR"/
+echo Pulled traces to $TRACEDIR
+
+# Identify which of the trace files are actually adbd, in case something else exited simultaneously.
+ADBD_PIDS=$(adb shell "logcat -d -s adbd --format=process | grep 'adbd started' | cut -c 3-7 | tr -d ' ' | sort | uniq")
+mkdir "$TRACEDIR"/adbd_traces
+
+adb shell 'setprop persist.adb.trace_mask 0; killall adbd'
+
+IFS=$'\n'
+for PID in $ADBD_PIDS; do
+ cp "$TRACEDIR"/trace/clang-$PID-*.profraw "$TRACEDIR"/adbd_traces 2>/dev/null || true
+done
+unset IFS
+
+### Merge the traces.
+llvm-profdata merge --output="$OUTPUT_DIR"/adbd.profdata "$TRACEDIR"/adbd_traces/* "$TRACEDIR"/test_traces/*
diff --git a/adb/coverage/include.sh b/adb/coverage/include.sh
new file mode 100644
index 0000000..45ebc34
--- /dev/null
+++ b/adb/coverage/include.sh
@@ -0,0 +1,5 @@
+ADB_TESTS="adbd_test adb_crypto_test adb_pairing_auth_test adb_pairing_connection_test adb_tls_connection_test"
+ADB_TEST_BINARIES=""
+for TEST in $ADB_TESTS; do
+ ADB_TEST_BINARIES="--object=$ANDROID_PRODUCT_OUT/data/nativetest64/$TEST/$TEST $ADB_TEST_BINARIES"
+done
diff --git a/adb/coverage/report.sh b/adb/coverage/report.sh
new file mode 100755
index 0000000..257310c
--- /dev/null
+++ b/adb/coverage/report.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+OUTPUT_DIR=$(realpath $(dirname "$0"))
+. "$OUTPUT_DIR"/include.sh
+
+rm -rf "$OUTPUT_DIR"/report
+
+cd $ANDROID_BUILD_TOP
+llvm-cov show --instr-profile="$OUTPUT_DIR"/adbd.profdata \
+ $ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
+ /proc/self/cwd/system/core/adb \
+ $ADB_TEST_BINARIES \
+ --show-region-summary=false \
+ --format=html -o "$OUTPUT_DIR"/report
+
+llvm-cov report --instr-profile="$OUTPUT_DIR"/adbd.profdata \
+ $ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
+ /proc/self/cwd/system/core/adb \
+ $ADB_TEST_BINARIES \
+ --show-region-summary=false
diff --git a/adb/coverage/show.sh b/adb/coverage/show.sh
new file mode 100755
index 0000000..3b2faa3
--- /dev/null
+++ b/adb/coverage/show.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+OUTPUT_DIR=$(realpath $(dirname "$0"))
+. "$OUTPUT_DIR"/include.sh
+
+BASE_PATH=/proc/self/cwd/system/core/adb
+PATHS=""
+if [[ $# == 0 ]]; then
+ PATHS=$BASE_PATH
+else
+ for arg in "$@"; do
+ PATHS="$PATHS $BASE_PATH/$arg"
+ done
+fi
+
+cd $ANDROID_BUILD_TOP
+llvm-cov show --instr-profile="$OUTPUT_DIR"/adbd.profdata \
+ $ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \
+ $PATHS \
+ $ADB_TEST_BINARIES
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 9e02e89..55b7783 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -301,6 +301,8 @@
setup_adb(addrs);
}
+ LOG(INFO) << "adbd started";
+
D("adbd_main(): pre init_jdwp()");
init_jdwp();
D("adbd_main(): post init_jdwp()");
diff --git a/adb/daemon/transport_local.cpp b/adb/daemon/transport_local.cpp
new file mode 100644
index 0000000..9e0b887
--- /dev/null
+++ b/adb/daemon/transport_local.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2007 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 TRACE_TAG TRANSPORT
+
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <condition_variable>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/parsenetaddress.h>
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+#include <cutils/sockets.h>
+
+#if !ADB_HOST
+#include <android-base/properties.h>
+#endif
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "socket_spec.h"
+#include "sysdeps/chrono.h"
+
+void server_socket_thread(std::function<unique_fd(std::string_view, std::string*)> listen_func,
+ std::string_view addr) {
+ adb_thread_setname("server socket");
+
+ unique_fd serverfd;
+ std::string error;
+
+ while (serverfd == -1) {
+ errno = 0;
+ serverfd = listen_func(addr, &error);
+ if (errno == EAFNOSUPPORT || errno == EINVAL || errno == EPROTONOSUPPORT) {
+ D("unrecoverable error: '%s'", error.c_str());
+ return;
+ } else if (serverfd < 0) {
+ D("server: cannot bind socket yet: %s", error.c_str());
+ std::this_thread::sleep_for(1s);
+ continue;
+ }
+ close_on_exec(serverfd.get());
+ }
+
+ while (true) {
+ D("server: trying to get new connection from fd %d", serverfd.get());
+ unique_fd fd(adb_socket_accept(serverfd, nullptr, nullptr));
+ if (fd >= 0) {
+ D("server: new connection on fd %d", fd.get());
+ close_on_exec(fd.get());
+ disable_tcp_nagle(fd.get());
+ std::string serial = android::base::StringPrintf("host-%d", fd.get());
+ // We don't care about port value in "register_socket_transport" as it is used
+ // only from ADB_HOST. "server_socket_thread" is never called from ADB_HOST.
+ register_socket_transport(
+ std::move(fd), std::move(serial), 0, 1,
+ [](atransport*) { return ReconnectResult::Abort; }, false);
+ }
+ }
+ D("transport: server_socket_thread() exiting");
+}
+
+unique_fd adb_listen(std::string_view addr, std::string* error) {
+ return unique_fd{socket_spec_listen(addr, error, nullptr)};
+}
+
+void local_init(const std::string& addr) {
+#if !defined(__ANDROID__)
+ // Host adbd.
+ D("transport: local server init");
+ std::thread(server_socket_thread, adb_listen, addr).detach();
+#else
+ D("transport: local server init");
+ // For the adbd daemon in the system image we need to distinguish
+ // between the device, and the emulator.
+ if (addr.starts_with("tcp:") && use_qemu_goldfish()) {
+ std::thread(qemu_socket_thread, addr).detach();
+ } else {
+ std::thread(server_socket_thread, adb_listen, addr).detach();
+ }
+#endif // !ADB_HOST
+}
+
+int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) {
+ t->type = kTransportLocal;
+ auto fd_connection = std::make_unique<FdConnection>(std::move(fd));
+ t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection)));
+ return 0;
+}
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
index fd55020..70cb9b3 100644
--- a/adb/fdevent/fdevent.cpp
+++ b/adb/fdevent/fdevent.cpp
@@ -27,7 +27,10 @@
#include "adb_utils.h"
#include "fdevent.h"
#include "fdevent_epoll.h"
+
+#if !defined(__linux__)
#include "fdevent_poll.h"
+#endif
using namespace std::chrono_literals;
using std::chrono::duration_cast;
diff --git a/adb/libs/adbconnection/Android.bp b/adb/libs/adbconnection/Android.bp
index ce2ab51..f7d2dc1 100644
--- a/adb/libs/adbconnection/Android.bp
+++ b/adb/libs/adbconnection/Android.bp
@@ -53,6 +53,7 @@
linux: {
version_script: "libadbconnection_client.map.txt",
},
+ darwin: { enabled: false },
},
stubs: {
symbol_file: "libadbconnection_client.map.txt",
diff --git a/adb/libs/adbconnection/adbconnection_client.cpp b/adb/libs/adbconnection/adbconnection_client.cpp
index c132342..7e16148 100644
--- a/adb/libs/adbconnection/adbconnection_client.cpp
+++ b/adb/libs/adbconnection/adbconnection_client.cpp
@@ -140,7 +140,13 @@
int rc = connect(ctx->control_socket_.get(), reinterpret_cast<sockaddr*>(&addr), addr_len);
if (rc != 0) {
- PLOG(ERROR) << "failed to connect to jdwp control socket";
+ if (errno == ECONNREFUSED) {
+ // On userdebug devices, every Java process is debuggable, so if adbd is explicitly turned
+ // off, this would spew enormous amounts of red-herring errors.
+ LOG(DEBUG) << "failed to connect to jdwp control socket, adbd not running?";
+ } else {
+ PLOG(ERROR) << "failed to connect to jdwp control socket";
+ }
return nullptr;
}
diff --git a/adb/mdns_test.cpp b/adb/mdns_test.cpp
new file mode 100644
index 0000000..1f662c1
--- /dev/null
+++ b/adb/mdns_test.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "adb_mdns.h"
+
+static bool isValidMdnsServiceName(std::string_view name) {
+ // The rules for Service Names [RFC6335] state that they may be no more
+ // than fifteen characters long (not counting the mandatory underscore),
+ // consisting of only letters, digits, and hyphens, must begin and end
+ // with a letter or digit, must not contain consecutive hyphens, and
+ // must contain at least one letter.
+
+ // No more than 15 characters long
+ if (name.empty() || name.size() > 15) {
+ return false;
+ }
+
+ bool hasAtLeastOneLetter = false;
+ bool sawHyphen = false;
+ for (size_t i = 0; i < name.size(); ++i) {
+ // Must contain at least one letter
+ // Only contains letters, digits and hyphens
+ if (name[i] == '-') {
+ // Cannot be at beginning or end
+ if (i == 0 || i == name.size() - 1) {
+ return false;
+ }
+ if (sawHyphen) {
+ // Consecutive hyphen found
+ return false;
+ }
+ sawHyphen = true;
+ continue;
+ }
+
+ sawHyphen = false;
+ if ((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z')) {
+ hasAtLeastOneLetter = true;
+ continue;
+ }
+
+ if (name[i] >= '0' && name[i] <= '9') {
+ continue;
+ }
+
+ // Invalid character
+ return false;
+ }
+
+ return hasAtLeastOneLetter;
+}
+
+TEST(mdns, test_isValidMdnsServiceName) {
+ // Longer than 15 characters
+ EXPECT_FALSE(isValidMdnsServiceName("abcd1234abcd1234"));
+
+ // Contains invalid characters
+ EXPECT_FALSE(isValidMdnsServiceName("a*a"));
+ EXPECT_FALSE(isValidMdnsServiceName("a_a"));
+ EXPECT_FALSE(isValidMdnsServiceName("_a"));
+
+ // Does not begin or end with letter or digit
+ EXPECT_FALSE(isValidMdnsServiceName(""));
+ EXPECT_FALSE(isValidMdnsServiceName("-"));
+ EXPECT_FALSE(isValidMdnsServiceName("-a"));
+ EXPECT_FALSE(isValidMdnsServiceName("-1"));
+ EXPECT_FALSE(isValidMdnsServiceName("a-"));
+ EXPECT_FALSE(isValidMdnsServiceName("1-"));
+
+ // Contains consecutive hyphens
+ EXPECT_FALSE(isValidMdnsServiceName("a--a"));
+
+ // Does not contain at least one letter
+ EXPECT_FALSE(isValidMdnsServiceName("1"));
+ EXPECT_FALSE(isValidMdnsServiceName("12"));
+ EXPECT_FALSE(isValidMdnsServiceName("1-2"));
+
+ // Some valid names
+ EXPECT_TRUE(isValidMdnsServiceName("a"));
+ EXPECT_TRUE(isValidMdnsServiceName("a1"));
+ EXPECT_TRUE(isValidMdnsServiceName("1A"));
+ EXPECT_TRUE(isValidMdnsServiceName("aZ"));
+ EXPECT_TRUE(isValidMdnsServiceName("a-Z"));
+ EXPECT_TRUE(isValidMdnsServiceName("a-b-Z"));
+ EXPECT_TRUE(isValidMdnsServiceName("abc-def-123-456"));
+}
+
+TEST(mdns, ServiceName_RFC6335) {
+ EXPECT_TRUE(isValidMdnsServiceName(ADB_MDNS_SERVICE_TYPE));
+ EXPECT_TRUE(isValidMdnsServiceName(ADB_MDNS_TLS_PAIRING_TYPE));
+ EXPECT_TRUE(isValidMdnsServiceName(ADB_MDNS_TLS_CONNECT_TYPE));
+}
diff --git a/adb/pairing_connection/Android.bp b/adb/pairing_connection/Android.bp
index 707161b..9595511 100644
--- a/adb/pairing_connection/Android.bp
+++ b/adb/pairing_connection/Android.bp
@@ -84,7 +84,7 @@
static_libs: [
"libadb_protos",
// Statically link libadb_tls_connection because it is not
- // ABI-stable.
+ // ABI-stable.
"libadb_tls_connection",
"libprotobuf-cpp-lite",
],
@@ -133,7 +133,6 @@
"//frameworks/base/services:__subpackages__",
],
- host_supported: true,
recovery_available: false,
stl: "libc++_static",
diff --git a/adb/services.cpp b/adb/services.cpp
index d87948c..19a9030 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -202,11 +202,22 @@
transport_id, &is_ambiguous, &error);
for (const auto& state : states) {
- // wait-for-disconnect uses kCsOffline, we don't actually want to wait for 'offline'.
- if ((t == nullptr && state == kCsOffline) || (t != nullptr && state == kCsAny) ||
- (t != nullptr && state == t->GetConnectionState())) {
- SendOkay(fd);
- return;
+ if (state == kCsOffline) {
+ // Special case for wait-for-disconnect:
+ // We want to wait for USB devices to completely disappear, but TCP devices can
+ // go into the offline state, since we automatically reconnect.
+ if (!t) {
+ SendOkay(fd);
+ return;
+ } else if (!t->GetUsbHandle()) {
+ SendOkay(fd);
+ return;
+ }
+ } else {
+ if (t && (state == kCsAny || state == t->GetConnectionState())) {
+ SendOkay(fd);
+ return;
+ }
}
}
diff --git a/adb/test_adb.py b/adb/test_adb.py
index c872fb0..03bdcbd 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -32,6 +32,8 @@
import time
import unittest
import warnings
+from importlib import util
+from parameterized import parameterized_class
def find_open_port():
# Find an open port.
@@ -576,6 +578,98 @@
# If the power event was detected, the adb shell command should be broken very quickly.
self.assertLess(end - start, 2)
+"""Use 'adb mdns check' to see if mdns discovery is available."""
+def is_adb_mdns_available():
+ with adb_server() as server_port:
+ output = subprocess.check_output(["adb", "-P", str(server_port),
+ "mdns", "check"]).strip()
+ return output.startswith(b"mdns daemon version")
+
+"""Check if we have zeroconf python library installed"""
+def is_zeroconf_installed():
+ zeroconf_spec = util.find_spec("zeroconf")
+ return zeroconf_spec is not None
+
+@contextlib.contextmanager
+def zeroconf_context(ipversion):
+ from zeroconf import Zeroconf
+ """Context manager for a zeroconf instance
+
+ This creates a zeroconf instance and returns it.
+ """
+
+ try:
+ zeroconf = Zeroconf(ip_version=ipversion)
+ yield zeroconf
+ finally:
+ zeroconf.close()
+
+@contextlib.contextmanager
+def zeroconf_register_service(zeroconf_ctx, info):
+ """Context manager for a zeroconf service
+
+ Registers a service and unregisters it on cleanup. Returns the ServiceInfo
+ supplied.
+ """
+
+ try:
+ zeroconf_ctx.register_service(info)
+ yield info
+ finally:
+ zeroconf_ctx.unregister_service(info)
+
+"""Should match the service names listed in adb_mdns.h"""
+@parameterized_class(('service_name',), [
+ ("adb",),
+ ("adb-tls-connect",),
+ ("adb-tls-pairing",),
+])
+@unittest.skipIf(not is_adb_mdns_available(), "mdns feature not available")
+class MdnsTest(unittest.TestCase):
+ """Tests for adb mdns."""
+
+ @unittest.skipIf(not is_zeroconf_installed(), "zeroconf library not installed")
+ def test_mdns_services_register_unregister(self):
+ """Ensure that `adb mdns services` correctly adds and removes a service
+ """
+ from zeroconf import IPVersion, ServiceInfo
+
+ 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:]]
+
+ 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"))
+ print(f"services={_mdns_services(server_port)}")
+
+ """TODO(joshuaduong): Add ipv6 tests once we have it working in adb"""
+ """Register/Unregister a service"""
+ with zeroconf_context(IPVersion.V4Only) as zc:
+ serv_instance = "my_fake_test_service"
+ serv_type = "_" + self.service_name + "._tcp."
+ serv_ipaddr = socket.inet_aton("1.2.3.4")
+ serv_port = 12345
+ service_info = ServiceInfo(
+ serv_type + "local.",
+ name=serv_instance + "." + serv_type + "local.",
+ addresses=[serv_ipaddr],
+ port=serv_port)
+ print(f"Registering {serv_instance}.{serv_type} ...")
+ with zeroconf_register_service(zc, service_info) as info:
+ """Give adb some time to register the service"""
+ time.sleep(0.25)
+ print(f"services={_mdns_services(server_port)}")
+ self.assertTrue(any((serv_instance in line and serv_type in line)
+ for line in _mdns_services(server_port)))
+
+ """Give adb some time to unregister the service"""
+ print("Unregistering mdns service...")
+ time.sleep(0.25)
+ print(f"services={_mdns_services(server_port)}")
+ self.assertFalse(any((serv_instance in line and serv_type in line)
+ for line in _mdns_services(server_port)))
def main():
"""Main entrypoint."""
diff --git a/adb/tools/Android.bp b/adb/tools/Android.bp
index 71e32b7..a7af53c 100644
--- a/adb/tools/Android.bp
+++ b/adb/tools/Android.bp
@@ -34,3 +34,26 @@
],
},
}
+
+cc_binary_host {
+ name: "adb_usbreset",
+
+ defaults: ["adb_defaults"],
+
+ srcs: [
+ "adb_usbreset.cpp",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libusb",
+ ],
+
+ stl: "libc++_static",
+
+ dist: {
+ targets: [
+ "sdk",
+ ],
+ },
+}
diff --git a/adb/tools/adb_usbreset.cpp b/adb/tools/adb_usbreset.cpp
new file mode 100644
index 0000000..6f141bd
--- /dev/null
+++ b/adb/tools/adb_usbreset.cpp
@@ -0,0 +1,188 @@
+// 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 <err.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string>
+#include <string_view>
+#include <variant>
+#include <vector>
+
+#include <libusb/libusb.h>
+
+struct AllDevices {};
+struct SingleDevice {};
+struct Serial {
+ std::string_view serial;
+};
+
+using DeviceSelection = std::variant<std::monostate, AllDevices, SingleDevice, Serial>;
+
+[[noreturn]] static void Usage(int rc) {
+ fprintf(stderr, "usage: [ANDROID_SERIAL=SERIAL] usbreset [-d] [-s SERIAL]\n");
+ fprintf(stderr, "\t-a --all\t\tReset all connected devices\n");
+ fprintf(stderr, "\t-d --device\t\tReset the single connected device\n");
+ fprintf(stderr, "\t-s --serial\t\tReset device with specified serial\n");
+ exit(rc);
+}
+
+static void SetOption(DeviceSelection* out, DeviceSelection in) {
+ if (!std::get_if<std::monostate>(out)) {
+ printf("error: multiple device selection options provided\n");
+ Usage(1);
+ }
+
+ *out = in;
+}
+
+static __attribute__((format(printf, 2, 3))) void PrintLibusbError(int err, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ vprintf(fmt, args);
+ va_end(args);
+
+ printf(": %s", libusb_strerror(static_cast<libusb_error>(err)));
+}
+
+static bool IsAdbInterface(const libusb_interface_descriptor* desc) {
+ return desc->bInterfaceClass == 0xFF && desc->bInterfaceSubClass == 0x42 &&
+ desc->bInterfaceProtocol == 0x1;
+}
+
+int main(int argc, char** argv) {
+ std::variant<std::monostate, AllDevices, SingleDevice, Serial> selection;
+
+ static constexpr struct option long_opts[] = {
+ {"all", 0, 0, 'a'}, {"help", 0, 0, 'h'}, {"serial", required_argument, 0, 's'},
+ {"device", 0, 0, 'd'}, {0, 0, 0, 0},
+ };
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "adhs:", long_opts, nullptr)) != -1) {
+ if (opt == 'h') {
+ Usage(0);
+ } else if (opt == 'a') {
+ SetOption(&selection, AllDevices{});
+ } else if (opt == 's') {
+ SetOption(&selection, Serial{optarg});
+ } else if (opt == 'd') {
+ SetOption(&selection, Serial{optarg});
+ } else {
+ errx(1, "unknown option: '%c'", opt);
+ }
+ }
+
+ if (std::get_if<std::monostate>(&selection)) {
+ const char* env = getenv("ANDROID_SERIAL");
+ if (env) {
+ SetOption(&selection, Serial{env});
+ } else {
+ fprintf(stderr, "adb_usbreset: no device specified\n");
+ Usage(1);
+ }
+ }
+
+ libusb_context* ctx;
+ int rc = libusb_init(&ctx);
+ if (rc != LIBUSB_SUCCESS) {
+ PrintLibusbError(rc, "error: failed to initialize libusb");
+ exit(1);
+ }
+
+ libusb_device** device_list;
+ ssize_t device_count = libusb_get_device_list(ctx, &device_list);
+ if (device_count < 0) {
+ PrintLibusbError(device_count, "error: failed to list devices");
+ exit(1);
+ }
+
+ std::vector<std::pair<std::string, libusb_device_handle*>> selected_devices;
+ for (int i = 0; i < device_count; ++i) {
+ libusb_device* device = device_list[i];
+ libusb_device_descriptor device_desc;
+
+ // Always succeeds for LIBUSB_API_VERSION >= 0x01000102.
+ libusb_get_device_descriptor(device, &device_desc);
+ static_assert(LIBUSB_API_VERSION >= 0x01000102);
+
+ libusb_config_descriptor* config_desc;
+ rc = libusb_get_active_config_descriptor(device, &config_desc);
+ if (rc != 0) {
+ PrintLibusbError(rc, "warning: failed to get config descriptor");
+ continue;
+ }
+
+ bool found_adb_interface = false;
+ for (int i = 0; i < config_desc->bNumInterfaces; ++i) {
+ if (IsAdbInterface(&config_desc->interface[i].altsetting[0])) {
+ found_adb_interface = true;
+ break;
+ }
+ }
+
+ if (found_adb_interface) {
+ libusb_device_handle* device_handle;
+ rc = libusb_open(device, &device_handle);
+ if (rc != 0) {
+ PrintLibusbError(rc, "warning: failed to open device");
+ continue;
+ }
+
+ char buf[128];
+ rc = libusb_get_string_descriptor_ascii(device_handle, device_desc.iSerialNumber,
+ reinterpret_cast<unsigned char*>(buf),
+ sizeof(buf));
+
+ if (rc < 0) {
+ PrintLibusbError(rc, "warning: failed to get device serial");
+ continue;
+ }
+
+ std::string serial(buf, buf + rc);
+ if (auto s = std::get_if<Serial>(&selection)) {
+ if (s->serial == serial) {
+ selected_devices.push_back(std::make_pair(std::move(serial), device_handle));
+ }
+ } else {
+ selected_devices.push_back(std::make_pair(std::move(serial), device_handle));
+ }
+ }
+ }
+
+ if (selected_devices.empty()) {
+ errx(1, "no devices match criteria");
+ } else if (std::get_if<SingleDevice>(&selection) && selected_devices.size() != 1) {
+ errx(1, "more than 1 device connected");
+ }
+
+ bool success = true;
+ for (auto& [serial, device_handle] : selected_devices) {
+ rc = libusb_reset_device(device_handle);
+ // libusb_reset_device will try to restore the previous state, and will return
+ // LIBUSB_ERROR_NOT_FOUND if it can't.
+ if (rc == 0 || rc == LIBUSB_ERROR_NOT_FOUND) {
+ printf("%s: successfully reset\n", serial.c_str());
+ } else {
+ PrintLibusbError(rc, "%s: failed to reset", serial.c_str());
+ success = false;
+ }
+ }
+
+ return !success;
+}
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 963c3c1..25ed366 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -505,11 +505,10 @@
int osh = cast_handle_to_int(adb_get_os_handle(fd_));
#if ADB_HOST
- tls_ = TlsConnection::Create(TlsConnection::Role::Client,
+ tls_ = TlsConnection::Create(TlsConnection::Role::Client, x509_str, evp_str, osh);
#else
- tls_ = TlsConnection::Create(TlsConnection::Role::Server,
+ tls_ = TlsConnection::Create(TlsConnection::Role::Server, x509_str, evp_str, osh);
#endif
- x509_str, evp_str, osh);
CHECK(tls_);
#if ADB_HOST
// TLS 1.3 gives the client no message if the server rejected the
diff --git a/base/Android.bp b/base/Android.bp
index 894ad6c..61fbc3d 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -51,6 +51,7 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ min_sdk_version: "29",
}
cc_defaults {
@@ -132,6 +133,7 @@
"//apex_available:anyapex",
"//apex_available:platform",
],
+ min_sdk_version: "29",
}
cc_library_static {
@@ -157,6 +159,7 @@
"errors_test.cpp",
"expected_test.cpp",
"file_test.cpp",
+ "logging_splitters_test.cpp",
"logging_test.cpp",
"macros_test.cpp",
"mapped_file_test.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index 6321fc6..97cc2b2 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -225,7 +225,7 @@
content->reserve(sb.st_size);
}
- char buf[BUFSIZ];
+ char buf[BUFSIZ] __attribute__((__uninitialized__));
ssize_t n;
while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
content->append(buf, n);
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index 9603bb1..9470344 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -182,7 +182,7 @@
!std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
std::is_convertible_v<U&&, T> /* non-explicit */
)>
- // NOLINTNEXTLINE(google-explicit-constructor)
+ // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
template <class U = T _ENABLE_IF(
@@ -192,6 +192,7 @@
!std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
!std::is_convertible_v<U&&, T> /* explicit */
)>
+ // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
template<class G = E _ENABLE_IF(
@@ -387,13 +388,9 @@
template<class T1, class E1, class T2, class E2>
constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return *x == *y;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return *x == *y;
}
template<class T1, class E1, class T2, class E2>
@@ -581,35 +578,23 @@
template<class E1, class E2>
constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return true;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return true;
}
template<class T1, class E1, class E2>
constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return false;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return false;
}
template<class E1, class T2, class E2>
constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
- if (x.has_value() != y.has_value()) {
- return false;
- } else if (!x.has_value()) {
- return x.error() == y.error();
- } else {
- return false;
- }
+ if (x.has_value() != y.has_value()) return false;
+ if (!x.has_value()) return x.error() == y.error();
+ return false;
}
template<class E>
@@ -623,7 +608,7 @@
std::is_constructible_v<E, Err> &&
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
!std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
- // NOLINTNEXTLINE(google-explicit-constructor)
+ // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload)
constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
template<class U, class... Args _ENABLE_IF(
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index accc225..26827fb 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -118,8 +118,10 @@
void SetDefaultTag(const std::string& tag);
-// We expose this even though it is the default because a user that wants to
-// override the default log buffer will have to construct this themselves.
+// The LogdLogger sends chunks of up to ~4000 bytes at a time to logd. It does not prevent other
+// threads from writing to logd between sending each chunk, so other threads may interleave their
+// messages. If preventing interleaving is required, then a custom logger that takes a lock before
+// calling this logger should be provided.
class LogdLogger {
public:
explicit LogdLogger(LogId default_log_id = android::base::MAIN);
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
index 5e65876..56a4f3e 100644
--- a/base/include/android-base/result.h
+++ b/base/include/android-base/result.h
@@ -130,6 +130,7 @@
template <typename T>
Error& operator<<(T&& t) {
+ // NOLINTNEXTLINE(bugprone-suspicious-semicolon)
if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
errno_ = t.code();
return (*this) << t.message();
diff --git a/base/logging.cpp b/base/logging.cpp
index cd460eb..5bd21da 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -61,6 +61,7 @@
#include <android-base/threads.h>
#include "liblog_symbols.h"
+#include "logging_splitters.h"
namespace android {
namespace base {
@@ -190,12 +191,6 @@
}
}
-static std::mutex& LoggingLock() {
- static auto& logging_lock = *new std::mutex();
- return logging_lock;
-}
-
-// Only used for Q fallback.
static LogFunction& Logger() {
#ifdef __ANDROID__
static auto& logger = *new LogFunction(LogdLogger());
@@ -205,7 +200,6 @@
return logger;
}
-// Only used for Q fallback.
static AbortFunction& Aborter() {
static auto& aborter = *new AbortFunction(DefaultAborter);
return aborter;
@@ -241,8 +235,8 @@
static LogSeverity gMinimumLogSeverity = INFO;
#if defined(__linux__)
-void KernelLogger(android::base::LogId, android::base::LogSeverity severity,
- const char* tag, const char*, unsigned int, const char* msg) {
+static void KernelLogLine(const char* msg, int length, android::base::LogSeverity severity,
+ const char* tag) {
// clang-format off
static constexpr int kLogSeverityToKernelLogLevel[] = {
[android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
@@ -266,8 +260,8 @@
// The kernel's printk buffer is only 1024 bytes.
// TODO: should we automatically break up long lines into multiple lines?
// Or we could log but with something like "..." at the end?
- char buf[1024];
- size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg);
+ char buf[1024] __attribute__((__uninitialized__));
+ size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg);
if (size > sizeof(buf)) {
size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
level, tag, size);
@@ -278,6 +272,11 @@
iov[0].iov_len = size;
TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
}
+
+void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
+ const char*, unsigned int, const char* full_message) {
+ SplitByLines(full_message, KernelLogLine, severity, tag);
+}
#endif
void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
@@ -290,21 +289,10 @@
#else
localtime_r(&t, &now);
#endif
+ auto output_string =
+ StderrOutputGenerator(now, getpid(), GetThreadId(), severity, tag, file, line, message);
- char timestamp[32];
- strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
-
- static const char log_characters[] = "VDIWEFF";
- static_assert(arraysize(log_characters) - 1 == FATAL + 1,
- "Mismatch in size of log_characters and values in LogSeverity");
- char severity_char = log_characters[severity];
- if (file != nullptr) {
- fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
- timestamp, getpid(), GetThreadId(), file, line, message);
- } else {
- fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n", tag ? tag : "nullptr", severity_char,
- timestamp, getpid(), GetThreadId(), message);
- }
+ fputs(output_string.c_str(), stderr);
}
void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
@@ -326,26 +314,9 @@
abort();
}
-
-LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
-}
-
-void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
- const char* file, unsigned int line,
- const char* message) {
- int32_t priority = LogSeverityToPriority(severity);
- if (id == DEFAULT) {
- id = default_log_id_;
- }
-
+static void LogdLogChunk(LogId id, LogSeverity severity, const char* tag, const char* message) {
int32_t lg_id = LogIdTolog_id_t(id);
-
- char log_message_with_file[4068]; // LOGGER_ENTRY_MAX_PAYLOAD, not available in the NDK.
- if (priority == ANDROID_LOG_FATAL && file != nullptr) {
- snprintf(log_message_with_file, sizeof(log_message_with_file), "%s:%u] %s", file, line,
- message);
- message = log_message_with_file;
- }
+ int32_t priority = LogSeverityToPriority(severity);
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
@@ -357,6 +328,17 @@
}
}
+LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {}
+
+void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ if (id == DEFAULT) {
+ id = default_log_id_;
+ }
+
+ SplitByLogdChunks(id, severity, tag, file, line, message, LogdLogChunk);
+}
+
void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
SetLogger(std::forward<LogFunction>(logger));
SetAborter(std::forward<AbortFunction>(aborter));
@@ -416,45 +398,27 @@
}
void SetLogger(LogFunction&& logger) {
+ Logger() = std::move(logger);
+
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
- // We need to atomically swap the old and new pointers since other threads may be logging.
- // We know all threads will be using the new logger after __android_log_set_logger() returns,
- // so we can delete it then.
- // This leaks one std::function<> per instance of libbase if multiple copies of libbase within a
- // single process call SetLogger(). That is the same cost as having a static
- // std::function<>, which is the not-thread-safe alternative.
- static std::atomic<LogFunction*> logger_function(nullptr);
- auto* old_logger_function = logger_function.exchange(new LogFunction(logger));
liblog_functions->__android_log_set_logger([](const struct __android_log_message* log_message) {
auto log_id = log_id_tToLogId(log_message->buffer_id);
auto severity = PriorityToLogSeverity(log_message->priority);
- auto& function = *logger_function.load(std::memory_order_acquire);
- function(log_id, severity, log_message->tag, log_message->file, log_message->line,
+ Logger()(log_id, severity, log_message->tag, log_message->file, log_message->line,
log_message->message);
});
- delete old_logger_function;
- } else {
- std::lock_guard<std::mutex> lock(LoggingLock());
- Logger() = std::move(logger);
}
}
void SetAborter(AbortFunction&& aborter) {
+ Aborter() = std::move(aborter);
+
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
- // See the comment in SetLogger().
- static std::atomic<AbortFunction*> abort_function(nullptr);
- auto* old_abort_function = abort_function.exchange(new AbortFunction(aborter));
- liblog_functions->__android_log_set_aborter([](const char* abort_message) {
- auto& function = *abort_function.load(std::memory_order_acquire);
- function(abort_message);
- });
- delete old_abort_function;
- } else {
- std::lock_guard<std::mutex> lock(LoggingLock());
- Aborter() = std::move(aborter);
+ liblog_functions->__android_log_set_aborter(
+ [](const char* abort_message) { Aborter()(abort_message); });
}
}
@@ -535,26 +499,8 @@
#endif
}
- {
- // Do the actual logging with the lock held.
- std::lock_guard<std::mutex> lock(LoggingLock());
- if (msg.find('\n') == std::string::npos) {
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
- msg.c_str());
- } else {
- msg += '\n';
- size_t i = 0;
- while (i < msg.size()) {
- size_t nl = msg.find('\n', i);
- msg[nl] = '\0';
- LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
- &msg[i]);
- // Undo the zero-termination so we can give the complete message to the aborter.
- msg[nl] = '\n';
- i = nl + 1;
- }
- }
- }
+ LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
+ msg.c_str());
// Abort if necessary.
if (data_->GetSeverity() == FATAL) {
diff --git a/base/logging_splitters.h b/base/logging_splitters.h
new file mode 100644
index 0000000..2ec2b20
--- /dev/null
+++ b/base/logging_splitters.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068 // This constant is not in the NDK.
+
+namespace android {
+namespace base {
+
+// This splits the message up line by line, by calling log_function with a pointer to the start of
+// each line and the size up to the newline character. It sends size = -1 for the final line.
+template <typename F, typename... Args>
+static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
+ const char* newline = strchr(msg, '\n');
+ while (newline != nullptr) {
+ log_function(msg, newline - msg, args...);
+ msg = newline + 1;
+ newline = strchr(msg, '\n');
+ }
+
+ log_function(msg, -1, args...);
+}
+
+// This splits the message up into chunks that logs can process delimited by new lines. It calls
+// log_function with the exact null terminated message that should be sent to logd.
+// Note, despite the loops and snprintf's, if severity is not fatal and there are no new lines,
+// this function simply calls log_function with msg without any extra overhead.
+template <typename F>
+static void SplitByLogdChunks(LogId log_id, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* msg, const F& log_function) {
+ // The maximum size of a payload, after the log header that logd will accept is
+ // LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload to find the size of
+ // the string that we can log in each pass.
+ // The protocol is documented in liblog/README.protocol.md.
+ // Specifically we subtract a byte for the priority, the length of the tag + its null terminator,
+ // and an additional byte for the null terminator on the payload. We subtract an additional 32
+ // bytes for slack, similar to java/android/util/Log.java.
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+ if (max_size <= 0) {
+ abort();
+ }
+ // If we're logging a fatal message, we'll append the file and line numbers.
+ bool add_file = file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT);
+
+ std::string file_header;
+ if (add_file) {
+ file_header = StringPrintf("%s:%u] ", file, line);
+ }
+ int file_header_size = file_header.size();
+
+ __attribute__((uninitialized)) char logd_chunk[max_size + 1];
+ ptrdiff_t chunk_position = 0;
+
+ auto call_log_function = [&]() {
+ log_function(log_id, severity, tag, logd_chunk);
+ chunk_position = 0;
+ };
+
+ auto write_to_logd_chunk = [&](const char* message, int length) {
+ int size_written = 0;
+ const char* new_line = chunk_position > 0 ? "\n" : "";
+ if (add_file) {
+ size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+ "%s%s%.*s", new_line, file_header.c_str(), length, message);
+ } else {
+ size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position,
+ "%s%.*s", new_line, length, message);
+ }
+
+ // This should never fail, if it does and we set size_written to 0, which will skip this line
+ // and move to the next one.
+ if (size_written < 0) {
+ size_written = 0;
+ }
+ chunk_position += size_written;
+ };
+
+ const char* newline = strchr(msg, '\n');
+ while (newline != nullptr) {
+ // If we have data in the buffer and this next line doesn't fit, write the buffer.
+ if (chunk_position != 0 && chunk_position + (newline - msg) + 1 + file_header_size > max_size) {
+ call_log_function();
+ }
+
+ // Otherwise, either the next line fits or we have any empty buffer and too large of a line to
+ // ever fit, in both cases, we add it to the buffer and continue.
+ write_to_logd_chunk(msg, newline - msg);
+
+ msg = newline + 1;
+ newline = strchr(msg, '\n');
+ }
+
+ // If we have left over data in the buffer and we can fit the rest of msg, add it to the buffer
+ // then write the buffer.
+ if (chunk_position != 0 &&
+ chunk_position + static_cast<int>(strlen(msg)) + 1 + file_header_size <= max_size) {
+ write_to_logd_chunk(msg, -1);
+ call_log_function();
+ } else {
+ // If the buffer is not empty and we can't fit the rest of msg into it, write its contents.
+ if (chunk_position != 0) {
+ call_log_function();
+ }
+ // Then write the rest of the msg.
+ if (add_file) {
+ snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(), msg);
+ log_function(log_id, severity, tag, logd_chunk);
+ } else {
+ log_function(log_id, severity, tag, msg);
+ }
+ }
+}
+
+static std::pair<int, int> CountSizeAndNewLines(const char* message) {
+ int size = 0;
+ int new_lines = 0;
+ while (*message != '\0') {
+ size++;
+ if (*message == '\n') {
+ ++new_lines;
+ }
+ ++message;
+ }
+ return {size, new_lines};
+}
+
+// This adds the log header to each line of message and returns it as a string intended to be
+// written to stderr.
+static std::string StderrOutputGenerator(const struct tm& now, int pid, uint64_t tid,
+ LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ char timestamp[32];
+ strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
+
+ static const char log_characters[] = "VDIWEFF";
+ static_assert(arraysize(log_characters) - 1 == FATAL + 1,
+ "Mismatch in size of log_characters and values in LogSeverity");
+ char severity_char = log_characters[severity];
+ std::string line_prefix;
+ if (file != nullptr) {
+ line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " %s:%u] ", tag ? tag : "nullptr",
+ severity_char, timestamp, pid, tid, file, line);
+ } else {
+ line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " ", tag ? tag : "nullptr", severity_char,
+ timestamp, pid, tid);
+ }
+
+ auto [size, new_lines] = CountSizeAndNewLines(message);
+ std::string output_string;
+ output_string.reserve(size + new_lines * line_prefix.size() + 1);
+
+ auto concat_lines = [&](const char* message, int size) {
+ output_string.append(line_prefix);
+ if (size == -1) {
+ output_string.append(message);
+ } else {
+ output_string.append(message, size);
+ }
+ output_string.append("\n");
+ };
+ SplitByLines(message, concat_lines);
+ return output_string;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_splitters_test.cpp b/base/logging_splitters_test.cpp
new file mode 100644
index 0000000..679d19e
--- /dev/null
+++ b/base/logging_splitters_test.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logging_splitters.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+void TestNewlineSplitter(const std::string& input,
+ const std::vector<std::string>& expected_output) {
+ std::vector<std::string> output;
+ auto logger_function = [&](const char* msg, int length) {
+ if (length == -1) {
+ output.push_back(msg);
+ } else {
+ output.push_back(std::string(msg, length));
+ }
+ };
+ SplitByLines(input.c_str(), logger_function);
+
+ EXPECT_EQ(expected_output, output);
+}
+
+TEST(logging_splitters, NewlineSplitter_EmptyString) {
+ TestNewlineSplitter("", std::vector<std::string>{""});
+}
+
+TEST(logging_splitters, NewlineSplitter_BasicString) {
+ TestNewlineSplitter("normal string", std::vector<std::string>{"normal string"});
+}
+
+TEST(logging_splitters, NewlineSplitter_ormalBasicStringTrailingNewline) {
+ TestNewlineSplitter("normal string\n", std::vector<std::string>{"normal string", ""});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineTrailing) {
+ TestNewlineSplitter("normal string\nsecond string\nthirdstring",
+ std::vector<std::string>{"normal string", "second string", "thirdstring"});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineTrailingNewline) {
+ TestNewlineSplitter(
+ "normal string\nsecond string\nthirdstring\n",
+ std::vector<std::string>{"normal string", "second string", "thirdstring", ""});
+}
+
+TEST(logging_splitters, NewlineSplitter_MultilineEmbeddedNewlines) {
+ TestNewlineSplitter(
+ "normal string\n\n\nsecond string\n\nthirdstring\n",
+ std::vector<std::string>{"normal string", "", "", "second string", "", "thirdstring", ""});
+}
+
+void TestLogdChunkSplitter(const std::string& tag, const std::string& file,
+ const std::string& input,
+ const std::vector<std::string>& expected_output) {
+ std::vector<std::string> output;
+ auto logger_function = [&](LogId, LogSeverity, const char*, const char* msg) {
+ output.push_back(msg);
+ };
+
+ SplitByLogdChunks(MAIN, FATAL, tag.c_str(), file.empty() ? nullptr : file.c_str(), 1000,
+ input.c_str(), logger_function);
+
+ auto return_lengths = [&] {
+ std::string sizes;
+ sizes += "expected_output sizes:";
+ for (const auto& string : expected_output) {
+ sizes += " " + std::to_string(string.size());
+ }
+ sizes += "\noutput sizes:";
+ for (const auto& string : output) {
+ sizes += " " + std::to_string(string.size());
+ }
+ return sizes;
+ };
+
+ EXPECT_EQ(expected_output, output) << return_lengths();
+}
+
+TEST(logging_splitters, LogdChunkSplitter_EmptyString) {
+ TestLogdChunkSplitter("tag", "", "", std::vector<std::string>{""});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_BasicString) {
+ TestLogdChunkSplitter("tag", "", "normal string", std::vector<std::string>{"normal string"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_NormalBasicStringTrailingNewline) {
+ TestLogdChunkSplitter("tag", "", "normal string\n", std::vector<std::string>{"normal string\n"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineTrailing) {
+ TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring",
+ std::vector<std::string>{"normal string\nsecond string\nthirdstring"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineTrailingNewline) {
+ TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring\n",
+ std::vector<std::string>{"normal string\nsecond string\nthirdstring\n"});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultilineEmbeddedNewlines) {
+ TestLogdChunkSplitter(
+ "tag", "", "normal string\n\n\nsecond string\n\nthirdstring\n",
+ std::vector<std::string>{"normal string\n\n\nsecond string\n\nthirdstring\n"});
+}
+
+// This test should return the same string, the logd logger itself will truncate down to size.
+// This has historically been the behavior both in libbase and liblog.
+TEST(logging_splitters, LogdChunkSplitter_HugeLineNoNewline) {
+ auto long_string = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
+ ASSERT_EQ(LOGGER_ENTRY_MAX_PAYLOAD, static_cast<int>(long_string.size()));
+
+ TestLogdChunkSplitter("tag", "", long_string, std::vector{long_string});
+}
+
+std::string ReduceToMaxSize(const std::string& tag, const std::string& string) {
+ return string.substr(0, LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_MultipleHugeLineNoNewline) {
+ auto long_string_x = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x');
+ auto long_string_y = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'y');
+ auto long_string_z = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'z');
+
+ auto long_strings = long_string_x + '\n' + long_string_y + '\n' + long_string_z;
+
+ std::string tag = "tag";
+ std::vector expected = {ReduceToMaxSize(tag, long_string_x), ReduceToMaxSize(tag, long_string_y),
+ long_string_z};
+
+ TestLogdChunkSplitter(tag, "", long_strings, expected);
+}
+
+// With a ~4k buffer, we should print 2 long strings per logger call.
+TEST(logging_splitters, LogdChunkSplitter_Multiple2kLines) {
+ std::vector expected = {
+ std::string(2000, 'a') + '\n' + std::string(2000, 'b'),
+ std::string(2000, 'c') + '\n' + std::string(2000, 'd'),
+ std::string(2000, 'e') + '\n' + std::string(2000, 'f'),
+ };
+
+ auto long_strings = Join(expected, '\n');
+
+ TestLogdChunkSplitter("tag", "", long_strings, expected);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_ExactSizedLines) {
+ const char* tag = "tag";
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35;
+ auto long_string_a = std::string(max_size, 'a');
+ auto long_string_b = std::string(max_size, 'b');
+ auto long_string_c = std::string(max_size, 'c');
+
+ auto long_strings = long_string_a + '\n' + long_string_b + '\n' + long_string_c;
+
+ TestLogdChunkSplitter(tag, "", long_strings,
+ std::vector{long_string_a, long_string_b, long_string_c});
+}
+
+TEST(logging_splitters, LogdChunkSplitter_UnderEqualOver) {
+ std::string tag = "tag";
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
+
+ auto first_string_size = 1000;
+ auto first_string = std::string(first_string_size, 'a');
+ auto second_string_size = max_size - first_string_size - 1;
+ auto second_string = std::string(second_string_size, 'b');
+
+ auto exact_string = std::string(max_size, 'c');
+
+ auto large_string = std::string(max_size + 50, 'd');
+
+ auto final_string = std::string("final string!\n\nfinal \n \n final \n");
+
+ std::vector expected = {first_string + '\n' + second_string, exact_string,
+ ReduceToMaxSize(tag, large_string), final_string};
+
+ std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
+ final_string};
+ auto long_strings = Join(input_strings, '\n');
+
+ TestLogdChunkSplitter(tag, "", long_strings, expected);
+}
+
+TEST(logging_splitters, LogdChunkSplitter_WithFile) {
+ std::string tag = "tag";
+ std::string file = "/path/to/myfile.cpp";
+ int line = 1000;
+ auto file_header = StringPrintf("%s:%d] ", file.c_str(), line);
+ ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35;
+
+ auto first_string_size = 1000;
+ auto first_string = std::string(first_string_size, 'a');
+ auto second_string_size = max_size - first_string_size - 1 - 2 * file_header.size();
+ auto second_string = std::string(second_string_size, 'b');
+
+ auto exact_string = std::string(max_size - file_header.size(), 'c');
+
+ auto large_string = std::string(max_size + 50, 'd');
+
+ auto final_string = std::string("final string!");
+
+ std::vector expected = {
+ file_header + first_string + '\n' + file_header + second_string, file_header + exact_string,
+ file_header + ReduceToMaxSize(file_header + tag, large_string), file_header + final_string};
+
+ std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string,
+ final_string};
+ auto long_strings = Join(input_strings, '\n');
+
+ TestLogdChunkSplitter(tag, file, long_strings, expected);
+}
+
+// We set max_size based off of tag, so if it's too large, the buffer will be sized wrong.
+// We could recover from this, but it's certainly an error for someone to attempt to use a tag this
+// large, so we abort instead.
+TEST(logging_splitters, LogdChunkSplitter_TooLongTag) {
+ auto long_tag = std::string(5000, 'x');
+ auto logger_function = [](LogId, LogSeverity, const char*, const char*) {};
+ ASSERT_DEATH(
+ SplitByLogdChunks(MAIN, ERROR, long_tag.c_str(), nullptr, 0, "message", logger_function), "");
+}
+
+// We do handle excessively large file names correctly however.
+TEST(logging_splitters, LogdChunkSplitter_TooLongFile) {
+ auto long_file = std::string(5000, 'x');
+ std::string tag = "tag";
+
+ std::vector expected = {ReduceToMaxSize(tag, long_file), ReduceToMaxSize(tag, long_file)};
+
+ TestLogdChunkSplitter(tag, long_file, "can't see me\nor me", expected);
+}
+
+void TestStderrOutputGenerator(const char* tag, const char* file, int line, const char* message,
+ const std::string& expected) {
+ // All log messages will show "01-01 00:00:00"
+ struct tm now = {
+ .tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 1,
+ .tm_mon = 0,
+ .tm_year = 1970,
+ };
+
+ int pid = 1234; // All log messages will have 1234 for their PID.
+ uint64_t tid = 4321; // All log messages will have 4321 for their TID.
+
+ auto result = StderrOutputGenerator(now, pid, tid, ERROR, tag, file, line, message);
+ EXPECT_EQ(expected, result);
+}
+
+TEST(logging_splitters, StderrOutputGenerator_Basic) {
+ TestStderrOutputGenerator(nullptr, nullptr, 0, "simple message",
+ "nullptr E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator("tag", nullptr, 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator(
+ "tag", "/path/to/some/file", 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 /path/to/some/file:0] simple message\n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_NewlineTagAndFile) {
+ TestStderrOutputGenerator("tag\n\n", nullptr, 0, "simple message",
+ "tag\n\n E 01-01 00:00:00 1234 4321 simple message\n");
+ TestStderrOutputGenerator(
+ "tag", "/path/to/some/file\n\n", 0, "simple message",
+ "tag E 01-01 00:00:00 1234 4321 /path/to/some/file\n\n:0] simple message\n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_TrailingNewLine) {
+ TestStderrOutputGenerator(
+ "tag", nullptr, 0, "simple message\n",
+ "tag E 01-01 00:00:00 1234 4321 simple message\ntag E 01-01 00:00:00 1234 4321 \n");
+}
+
+TEST(logging_splitters, StderrOutputGenerator_MultiLine) {
+ const char* expected_result =
+ "tag E 01-01 00:00:00 1234 4321 simple message\n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 another message \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 final message \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n"
+ "tag E 01-01 00:00:00 1234 4321 \n";
+
+ TestStderrOutputGenerator("tag", nullptr, 0,
+ "simple message\n\n\nanother message \n\n final message \n\n\n",
+ expected_result);
+}
+
+TEST(logging_splitters, StderrOutputGenerator_MultiLineLong) {
+ auto long_string_a = std::string(4000, 'a');
+ auto long_string_b = std::string(4000, 'b');
+
+ auto message = long_string_a + '\n' + long_string_b;
+ auto expected_result = "tag E 01-01 00:00:00 1234 4321 " + long_string_a + '\n' +
+ "tag E 01-01 00:00:00 1234 4321 " + long_string_b + '\n';
+ TestStderrOutputGenerator("tag", nullptr, 0, message.c_str(), expected_result);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 3a453e6..593e2c1 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -24,8 +24,10 @@
#include <regex>
#include <string>
+#include <thread>
#include "android-base/file.h"
+#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/test_utils.h"
@@ -596,7 +598,7 @@
CapturedStderr cap;
LOG(FATAL) << "foo\nbar";
- EXPECT_EQ(CountLineAborter::newline_count, 1U + 1U); // +1 for final '\n'.
+ EXPECT_EQ(CountLineAborter::newline_count, 1U);
}
__attribute__((constructor)) void TestLoggingInConstructor() {
@@ -617,3 +619,55 @@
// Whereas ERROR logging includes the program name.
ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str());
}
+
+TEST(logging, ForkSafe) {
+#if !defined(_WIN32)
+ using namespace android::base;
+ SetLogger(
+ [&](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) { sleep(3); });
+
+ auto guard = make_scope_guard([&] {
+#ifdef __ANDROID__
+ SetLogger(LogdLogger());
+#else
+ SetLogger(StderrLogger);
+#endif
+ });
+
+ auto thread = std::thread([] {
+ LOG(ERROR) << "This should sleep for 3 seconds, long enough to fork another process, if there "
+ "is no intervention";
+ });
+ thread.detach();
+
+ auto pid = fork();
+ ASSERT_NE(-1, pid);
+
+ if (pid == 0) {
+ // Reset the logger, so the next message doesn't sleep().
+ SetLogger([](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) {});
+ LOG(ERROR) << "This should succeed in the child, only if libbase is forksafe.";
+ _exit(EXIT_SUCCESS);
+ }
+
+ // Wait for up to 3 seconds for the child to exit.
+ int tries = 3;
+ bool found_child = false;
+ while (tries-- > 0) {
+ auto result = waitpid(pid, nullptr, WNOHANG);
+ EXPECT_NE(-1, result);
+ if (result == pid) {
+ found_child = true;
+ break;
+ }
+ sleep(1);
+ }
+
+ EXPECT_TRUE(found_child);
+
+ // Kill the child if it did not exit.
+ if (!found_child) {
+ kill(pid, SIGKILL);
+ }
+#endif
+}
diff --git a/base/stringprintf.cpp b/base/stringprintf.cpp
index 78e1e8d..e83ab13 100644
--- a/base/stringprintf.cpp
+++ b/base/stringprintf.cpp
@@ -25,7 +25,7 @@
void StringAppendV(std::string* dst, const char* format, va_list ap) {
// First try with a small fixed size buffer
- char space[1024];
+ char space[1024] __attribute__((__uninitialized__));
// It's possible for methods that use a va_list to invalidate
// the data in it upon use. The fix is to make a copy
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e0168d5..d6b2e25 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -43,6 +43,7 @@
#include <android-base/unique_fd.h>
#include <android/log.h>
#include <log/log.h>
+#include <log/log_read.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
#include <unwindstack/DexFiles.h>
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 884856d..3a2deb7 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -145,6 +145,7 @@
static_libs: [
"libhealthhalutils",
"libsnapshot_nobinder",
+ "update_metadata-protos",
],
header_libs: [
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b8eee4a..2553353 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -625,7 +625,7 @@
if (!sm) {
return device->WriteFail("Unable to create SnapshotManager");
}
- if (!sm->HandleImminentDataWipe()) {
+ if (!sm->FinishMergeInRecovery()) {
return device->WriteFail("Unable to finish snapshot merge");
}
} else {
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index f579078..ca782b9 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -42,7 +42,7 @@
$ adb push <source> <destination>
$ adb reboot
-Note that you can replace these two lines:
+Note that you can replace these two lines in the above sequence:
$ adb disable-verity
$ adb reboot
@@ -51,7 +51,7 @@
$ adb remount -R
-**Note:** _adb reboot -R_ won’t reboot if the device is already in the adb remount state.
+**Note:** _adb remount -R_ won’t reboot if the device is already in the adb remount state.
None of this changes if OverlayFS needs to be engaged.
The decisions whether to use traditional direct file-system remount,
diff --git a/fs_mgr/clean_scratch_files.rc b/fs_mgr/clean_scratch_files.rc
index 738d1aa..25a7e69 100644
--- a/fs_mgr/clean_scratch_files.rc
+++ b/fs_mgr/clean_scratch_files.rc
@@ -1,2 +1,2 @@
on post-fs-data && property:ro.debuggable=1
- exec_background - root root -- clean_scratch_files
+ exec_background - root root -- /system/bin/clean_scratch_files
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 1486e87..d2daaa1 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -62,6 +62,7 @@
#include <fs_mgr_overlayfs.h>
#include <fscrypt/fscrypt.h>
#include <libdm/dm.h>
+#include <libdm/loop_control.h>
#include <liblp/metadata_format.h>
#include <linux/fs.h>
#include <linux/loop.h>
@@ -105,6 +106,7 @@
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
using android::dm::DmTargetLinear;
+using android::dm::LoopControl;
// Realistically, this file should be part of the android::fs_mgr namespace;
using namespace android::fs_mgr;
@@ -358,7 +360,7 @@
const struct ext4_super_block* sb, int* fs_stat) {
bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
bool want_quota = entry.fs_mgr_flags.quota;
- bool want_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false);
+ bool want_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
if (has_quota == want_quota) {
return;
@@ -521,7 +523,8 @@
static void tune_casefold(const std::string& blk_device, 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("ro.emulated_storage.casefold", false);
+ bool wants_casefold =
+ android::base::GetBoolProperty("external_storage.casefold.enabled", false);
if (!wants_casefold || has_casefold) return;
@@ -1907,19 +1910,6 @@
return InstallZramDevice(bdev);
}
- // Get free loopback
- unique_fd loop_fd(TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC)));
- if (loop_fd.get() == -1) {
- PERROR << "Cannot open loop-control";
- return false;
- }
-
- int num = ioctl(loop_fd.get(), LOOP_CTL_GET_FREE);
- if (num == -1) {
- PERROR << "Cannot get free loop slot";
- return false;
- }
-
// Prepare target path
unique_fd target_fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)));
if (target_fd.get() == -1) {
@@ -1931,25 +1921,21 @@
return false;
}
- // Connect loopback (device_fd) to target path (target_fd)
- std::string device = android::base::StringPrintf("/dev/block/loop%d", num);
- unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
- if (device_fd.get() == -1) {
- PERROR << "Cannot open /dev/block/loop" << num;
- return false;
- }
-
- if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get())) {
- PERROR << "Cannot set loopback to target path";
+ // Allocate loop device and attach it to file_path.
+ LoopControl loop_control;
+ std::string device;
+ if (!loop_control.Attach(target_fd.get(), 5s, &device)) {
return false;
}
// set block size & direct IO
- if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096)) {
- PWARNING << "Cannot set 4KB blocksize to /dev/block/loop" << num;
+ unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
+ if (device_fd.get() == -1) {
+ PERROR << "Cannot open " << device;
+ return false;
}
- if (ioctl(device_fd.get(), LOOP_SET_DIRECT_IO, 1)) {
- PWARNING << "Cannot set direct_io to /dev/block/loop" << num;
+ if (!LoopControl::EnableDirectIo(device_fd.get())) {
+ return false;
}
return InstallZramDevice(device);
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 7be024f..301c907 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -121,7 +121,7 @@
}
static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt_footer,
- bool needs_projid, bool needs_casefold) {
+ bool needs_projid, bool needs_casefold, bool fs_compress) {
if (!dev_sz) {
int rc = get_dev_sz(fs_blkdev, &dev_sz);
if (rc) {
@@ -147,6 +147,12 @@
args.push_back("-C");
args.push_back("utf8");
}
+ if (fs_compress) {
+ args.push_back("-O");
+ args.push_back("compression");
+ args.push_back("-O");
+ args.push_back("extra_attr");
+ }
args.push_back(fs_blkdev.c_str());
args.push_back(size_str.c_str());
@@ -160,13 +166,13 @@
bool needs_projid = false;
if (entry.mount_point == "/data") {
- needs_casefold = android::base::GetBoolProperty("ro.emulated_storage.casefold", false);
- needs_projid = android::base::GetBoolProperty("ro.emulated_storage.projid", false);
+ needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false);
+ needs_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
}
if (entry.fs_type == "f2fs") {
return format_f2fs(entry.blk_device, entry.length, crypt_footer, needs_projid,
- needs_casefold);
+ needs_casefold, entry.fs_mgr_flags.fs_compress);
} else if (entry.fs_type == "ext4") {
return format_ext4(entry.blk_device, entry.mount_point, crypt_footer, needs_projid,
entry.fs_mgr_flags.ext_meta_csum);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index b218f21..0825a77 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -178,6 +178,7 @@
CheckFlag("slotselect_other", slot_select_other);
CheckFlag("fsverity", fs_verity);
CheckFlag("metadata_csum", ext_meta_csum);
+ CheckFlag("fscompress", fs_compress);
#undef CheckFlag
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 24cbad7..052efa7 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -105,20 +105,23 @@
} // namespace
+enum RemountStatus {
+ REMOUNT_SUCCESS = 0,
+ NOT_USERDEBUG,
+ BADARG,
+ NOT_ROOT,
+ NO_FSTAB,
+ UNKNOWN_PARTITION,
+ INVALID_PARTITION,
+ VERITY_PARTITION,
+ BAD_OVERLAY,
+ NO_MOUNTS,
+ REMOUNT_FAILED,
+ MUST_REBOOT
+};
+
static int do_remount(int argc, char* argv[]) {
- enum {
- SUCCESS = 0,
- NOT_USERDEBUG,
- BADARG,
- NOT_ROOT,
- NO_FSTAB,
- UNKNOWN_PARTITION,
- INVALID_PARTITION,
- VERITY_PARTITION,
- BAD_OVERLAY,
- NO_MOUNTS,
- REMOUNT_FAILED,
- } retval = SUCCESS;
+ RemountStatus retval = REMOUNT_SUCCESS;
// If somehow this executable is delivered on a "user" build, it can
// not function, so providing a clear message to the caller rather than
@@ -304,8 +307,7 @@
if (partitions.empty() || just_disabled_verity) {
if (reboot_later) reboot(setup_overlayfs);
if (user_please_reboot_later) {
- LOG(INFO) << "Now reboot your device for settings to take effect";
- return 0;
+ return MUST_REBOOT;
}
LOG(WARNING) << "No partitions to remount";
return retval;
@@ -394,6 +396,12 @@
int main(int argc, char* argv[]) {
android::base::InitLogging(argv, MyLogger);
int result = do_remount(argc, argv);
- printf("remount %s\n", result ? "failed" : "succeeded");
+ if (result == MUST_REBOOT) {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
+ } else if (result == REMOUNT_SUCCESS) {
+ printf("remount succeeded\n");
+ } else {
+ printf("remount failed\n");
+ }
return result;
}
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 79d9402..7216402 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -84,6 +84,7 @@
bool slot_select_other : 1;
bool fs_verity : 1;
bool ext_meta_csum : 1;
+ bool fs_compress : 1;
} fs_mgr_flags = {};
bool is_encryptable() const {
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 9d18a44..cae43e6 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -69,6 +69,7 @@
"libdm",
"libfs_mgr",
"liblog",
+ "libgsi",
],
data: [
@@ -90,6 +91,7 @@
cc_test {
name: "fiemap_image_test",
static_libs: [
+ "libcrypto_utils",
"libdm",
"libext4_utils",
"libfs_mgr",
@@ -98,7 +100,6 @@
shared_libs: [
"libbase",
"libcrypto",
- "libcrypto_utils",
"libcutils",
"liblog",
],
@@ -117,6 +118,7 @@
"-DSKIP_TEST_IN_PRESUBMIT",
],
static_libs: [
+ "libcrypto_utils",
"libdm",
"libext4_utils",
"libfs_mgr",
@@ -125,7 +127,6 @@
shared_libs: [
"libbase",
"libcrypto",
- "libcrypto_utils",
"libcutils",
"liblog",
],
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 22a3722..3c8ab42 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -35,6 +35,7 @@
#include <libdm/loop_control.h>
#include <libfiemap/fiemap_writer.h>
#include <libfiemap/split_fiemap_writer.h>
+#include <libgsi/libgsi.h>
#include "utility.h"
@@ -148,7 +149,10 @@
FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
EXPECT_EQ(fptr->size(), gBlockSize);
EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
- EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+
+ if (!android::gsi::IsGsiRunning()) {
+ EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
+ }
}
TEST_F(FiemapWriterTest, CheckFileCreated) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 5388b44..6663391 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -131,132 +131,6 @@
ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
}
-// This fixture is for tests against a simulated device environment. Rather
-// than use /data, we create an image and then layer a new filesystem within
-// it. Each test then decides how to mount and create layered images. This
-// allows us to test FBE vs FDE configurations.
-class ImageTest : public ::testing::Test {
- public:
- ImageTest() : dm_(DeviceMapper::Instance()) {}
-
- void SetUp() override {
- manager_ = ImageManager::Open(kMetadataPath, gDataPath);
- ASSERT_NE(manager_, nullptr);
-
- manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- submanager_ = ImageManager::Open(kMetadataPath + "/mnt"s, gDataPath + "/mnt"s);
- ASSERT_NE(submanager_, nullptr);
-
- submanager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
-
- // Ensure that metadata is cleared in between runs.
- submanager_->RemoveAllImages();
- manager_->RemoveAllImages();
-
- const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- base_name_ = tinfo->name();
- test_image_name_ = base_name_ + "-base";
- wrapper_device_name_ = base_name_ + "-wrapper";
-
- ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize * 16, false, nullptr));
- ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &base_device_));
- }
-
- void TearDown() override {
- submanager_->UnmapImageDevice(test_image_name_);
- umount(gDataMountPath.c_str());
- dm_.DeleteDeviceIfExists(wrapper_device_name_);
- manager_->UnmapImageDevice(base_name_);
- manager_->DeleteBackingImage(base_name_);
- }
-
- protected:
- bool DoFormat(const std::string& device) {
- // clang-format off
- std::vector<std::string> mkfs_args = {
- "/system/bin/mke2fs",
- "-F",
- "-b 4096",
- "-t ext4",
- "-m 0",
- "-O has_journal",
- device,
- ">/dev/null",
- "2>/dev/null",
- "</dev/null",
- };
- // clang-format on
- auto command = android::base::Join(mkfs_args, " ");
- return system(command.c_str()) == 0;
- }
-
- std::unique_ptr<ImageManager> manager_;
- std::unique_ptr<ImageManager> submanager_;
-
- DeviceMapper& dm_;
- std::string base_name_;
- std::string base_device_;
- std::string test_image_name_;
- std::string wrapper_device_name_;
-};
-
-TEST_F(ImageTest, DirectMount) {
- ASSERT_TRUE(DoFormat(base_device_));
- ASSERT_EQ(mount(base_device_.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
- ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
- std::string path;
- ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
- ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/loop"));
-}
-
-TEST_F(ImageTest, IndirectMount) {
-#ifdef SKIP_TEST_IN_PRESUBMIT
- GTEST_SKIP() << "WIP failure b/148874852";
-#endif
- // Create a simple wrapper around the base device that we'll mount from
- // instead. This will simulate the code paths for dm-crypt/default-key/bow
- // and force us to use device-mapper rather than loop devices.
- uint64_t device_size = 0;
- {
- unique_fd fd(open(base_device_.c_str(), O_RDWR | O_CLOEXEC));
- ASSERT_GE(fd, 0);
- device_size = get_block_device_size(fd);
- ASSERT_EQ(device_size, kTestImageSize * 16);
- }
- uint64_t num_sectors = device_size / 512;
-
- auto& dm = DeviceMapper::Instance();
-
- DmTable table;
- table.Emplace<DmTargetLinear>(0, num_sectors, base_device_, 0);
- ASSERT_TRUE(dm.CreateDevice(wrapper_device_name_, table));
-
- // Format and mount.
- std::string wrapper_device;
- ASSERT_TRUE(dm.GetDmDevicePathByName(wrapper_device_name_, &wrapper_device));
- ASSERT_TRUE(WaitForFile(wrapper_device, 5s));
- ASSERT_TRUE(DoFormat(wrapper_device));
- ASSERT_EQ(mount(wrapper_device.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
-
- ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));
-
- std::set<std::string> backing_devices;
- auto init = [&](std::set<std::string> devices) -> bool {
- backing_devices = std::move(devices);
- return true;
- };
-
- std::string path;
- ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
- ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
- ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
- ASSERT_TRUE(submanager_->MapAllImages(init));
- ASSERT_FALSE(backing_devices.empty());
- ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
-}
-
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/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index d496466..c37d70e 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -19,6 +19,7 @@
#include <string.h>
#include <algorithm>
+#include <limits>
#include <android-base/unique_fd.h>
@@ -40,6 +41,20 @@
return true;
}
+bool LinearExtent::OverlapsWith(const LinearExtent& other) const {
+ if (device_index_ != other.device_index()) {
+ return false;
+ }
+ return physical_sector() < other.end_sector() && other.physical_sector() < end_sector();
+}
+
+bool LinearExtent::OverlapsWith(const Interval& interval) const {
+ if (device_index_ != interval.device_index) {
+ return false;
+ }
+ return physical_sector() < interval.end && interval.start < end_sector();
+}
+
Interval LinearExtent::AsInterval() const {
return Interval(device_index(), physical_sector(), end_sector());
}
@@ -355,7 +370,10 @@
}
// Align the metadata size up to the nearest sector.
- metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
+ if (!AlignTo(metadata_max_size, LP_SECTOR_SIZE, &metadata_max_size)) {
+ LERROR << "Max metadata size " << metadata_max_size << " is too large.";
+ return false;
+ }
// Validate and build the block device list.
uint32_t logical_block_size = 0;
@@ -387,10 +405,15 @@
// untouched to be compatible code that looks for an MBR. Thus we
// start counting free sectors at sector 1, not 0.
uint64_t free_area_start = LP_SECTOR_SIZE;
- if (out.alignment || out.alignment_offset) {
- free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset);
+ bool ok;
+ if (out.alignment) {
+ ok = AlignTo(free_area_start, out.alignment, &free_area_start);
} else {
- free_area_start = AlignTo(free_area_start, logical_block_size);
+ ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+ }
+ if (!ok) {
+ LERROR << "Integer overflow computing free area start";
+ return false;
}
out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
@@ -427,10 +450,15 @@
// Compute the first free sector, factoring in alignment.
uint64_t free_area_start = total_reserved;
+ bool ok;
if (super.alignment || super.alignment_offset) {
- free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset);
+ ok = AlignTo(free_area_start, super.alignment, &free_area_start);
} else {
- free_area_start = AlignTo(free_area_start, logical_block_size);
+ ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+ }
+ if (!ok) {
+ LERROR << "Integer overflow computing free area start";
+ return false;
}
super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
@@ -530,7 +558,11 @@
const Interval& current = extents[i];
DCHECK(previous.device_index == current.device_index);
- uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end);
+ uint64_t aligned;
+ if (!AlignSector(block_devices_[current.device_index], previous.end, &aligned)) {
+ LERROR << "Sector " << previous.end << " caused integer overflow.";
+ continue;
+ }
if (aligned >= current.start) {
// There is no gap between these two extents, try the next one.
// Note that we check with >= instead of >, since alignment may
@@ -716,7 +748,10 @@
// Choose an aligned sector for the midpoint. This could lead to one half
// being slightly larger than the other, but this will not restrict the
// size of partitions (it might lead to one extra extent if "B" overflows).
- midpoint = AlignSector(super, midpoint);
+ if (!AlignSector(super, midpoint, &midpoint)) {
+ LERROR << "Unexpected integer overflow aligning midpoint " << midpoint;
+ return free_list;
+ }
std::vector<Interval> first_half;
std::vector<Interval> second_half;
@@ -754,7 +789,11 @@
// If the sector ends where the next aligned chunk begins, then there's
// no missing gap to try and allocate.
const auto& block_device = block_devices_[extent->device_index()];
- uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
+ uint64_t next_aligned_sector;
+ if (!AlignSector(block_device, extent->end_sector(), &next_aligned_sector)) {
+ LERROR << "Integer overflow aligning sector " << extent->end_sector();
+ return nullptr;
+ }
if (extent->end_sector() == next_aligned_sector) {
return nullptr;
}
@@ -774,8 +813,7 @@
bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
const LinearExtent& candidate) const {
for (const auto& region : regions) {
- if (region.device_index == candidate.device_index() &&
- (candidate.OwnsSector(region.start) || candidate.OwnsSector(region.end))) {
+ if (candidate.OverlapsWith(region)) {
return true;
}
}
@@ -786,11 +824,10 @@
for (const auto& partition : partitions_) {
for (const auto& extent : partition->extents()) {
LinearExtent* linear = extent->AsLinearExtent();
- if (!linear || linear->device_index() != candidate.device_index()) {
+ if (!linear) {
continue;
}
- if (linear->OwnsSector(candidate.physical_sector()) ||
- linear->OwnsSector(candidate.end_sector() - 1)) {
+ if (linear->OverlapsWith(candidate)) {
return true;
}
}
@@ -913,13 +950,19 @@
return size;
}
-uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device,
- uint64_t sector) const {
+bool MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector,
+ uint64_t* out) const {
// Note: when reading alignment info from the Kernel, we don't assume it
// is aligned to the sector size, so we round up to the nearest sector.
uint64_t lba = sector * LP_SECTOR_SIZE;
- uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset);
- return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+ if (!AlignTo(lba, block_device.alignment, out)) {
+ return false;
+ }
+ if (!AlignTo(*out, LP_SECTOR_SIZE, out)) {
+ return false;
+ }
+ *out /= LP_SECTOR_SIZE;
+ return true;
}
bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
@@ -993,7 +1036,12 @@
bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,
const std::vector<Interval>& free_region_hint) {
// Align the space needed up to the nearest sector.
- uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
+ uint64_t aligned_size;
+ if (!AlignTo(requested_size, geometry_.logical_block_size, &aligned_size)) {
+ LERROR << "Cannot resize partition " << partition->name() << " to " << requested_size
+ << " bytes; integer overflow.";
+ return false;
+ }
uint64_t old_size = partition->size();
if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index ca8df61..1a3250a 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -174,7 +174,7 @@
ASSERT_NE(exported, nullptr);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
- EXPECT_EQ(super_device->first_logical_sector, 1472);
+ EXPECT_EQ(super_device->first_logical_sector, 1536);
// Alignment offset without alignment doesn't mean anything.
device_info.alignment = 0;
@@ -190,7 +190,7 @@
ASSERT_NE(exported, nullptr);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
- EXPECT_EQ(super_device->first_logical_sector, 174);
+ EXPECT_EQ(super_device->first_logical_sector, 168);
// Test a small alignment with no alignment offset.
device_info.alignment = 11 * 1024;
@@ -200,7 +200,7 @@
ASSERT_NE(exported, nullptr);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
- EXPECT_EQ(super_device->first_logical_sector, 160);
+ EXPECT_EQ(super_device->first_logical_sector, 154);
}
TEST_F(BuilderTest, InternalPartitionAlignment) {
@@ -228,13 +228,14 @@
ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
EXPECT_EQ(extent.num_sectors, 80);
+ uint64_t aligned_lba;
uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
- uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
+ ASSERT_TRUE(AlignTo(lba, device_info.alignment, &aligned_lba));
EXPECT_EQ(lba, aligned_lba);
}
// Sanity check one extent.
- EXPECT_EQ(exported->extents.back().target_data, 3008);
+ EXPECT_EQ(exported->extents.back().target_data, 3072);
}
TEST_F(BuilderTest, UseAllDiskSpace) {
@@ -652,7 +653,7 @@
};
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, "system_a", 65536, 2);
ASSERT_NE(builder, nullptr);
- EXPECT_EQ(builder->AllocatableSpace(), 467238912);
+ EXPECT_EQ(builder->AllocatableSpace(), 467402752);
// Create a partition that spans 3 devices.
Partition* p = builder->AddPartition("system_a", 0);
@@ -675,17 +676,17 @@
EXPECT_EQ(metadata->block_devices[2].alignment, 786432);
EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);
ASSERT_EQ(metadata->extents.size(), 3);
- EXPECT_EQ(metadata->extents[0].num_sectors, 522304);
+ EXPECT_EQ(metadata->extents[0].num_sectors, 522752);
EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(metadata->extents[0].target_data, 1984);
+ EXPECT_EQ(metadata->extents[0].target_data, 1536);
EXPECT_EQ(metadata->extents[0].target_source, 0);
- EXPECT_EQ(metadata->extents[1].num_sectors, 260672);
+ EXPECT_EQ(metadata->extents[1].num_sectors, 260608);
EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(metadata->extents[1].target_data, 1472);
+ EXPECT_EQ(metadata->extents[1].target_data, 1536);
EXPECT_EQ(metadata->extents[1].target_source, 1);
- EXPECT_EQ(metadata->extents[2].num_sectors, 129088);
+ EXPECT_EQ(metadata->extents[2].num_sectors, 128704);
EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(metadata->extents[2].target_data, 1472);
+ EXPECT_EQ(metadata->extents[2].target_data, 1536);
EXPECT_EQ(metadata->extents[2].target_source, 2);
}
@@ -937,3 +938,131 @@
EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2));
EXPECT_EQ(exported->header.flags, 0x5e5e5e5e);
}
+
+static Interval ToInterval(const std::unique_ptr<Extent>& extent) {
+ if (LinearExtent* le = extent->AsLinearExtent()) {
+ return le->AsInterval();
+ }
+ return {0, 0, 0};
+}
+
+static void AddPartition(const std::unique_ptr<MetadataBuilder>& builder,
+ const std::string& partition_name, uint64_t num_sectors,
+ uint64_t start_sector, std::vector<Interval>* intervals) {
+ Partition* p = builder->AddPartition(partition_name, "group", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(p, "super", num_sectors, start_sector));
+ ASSERT_EQ(p->extents().size(), 1);
+
+ if (!intervals) {
+ return;
+ }
+
+ auto new_interval = ToInterval(p->extents().back());
+ std::vector<Interval> new_intervals = {new_interval};
+
+ auto overlap = Interval::Intersect(*intervals, new_intervals);
+ ASSERT_TRUE(overlap.empty());
+
+ intervals->push_back(new_interval);
+}
+
+TEST_F(BuilderTest, CollidedExtents) {
+ BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
+ std::vector<BlockDeviceInfo> block_devices = {super};
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("group", 0));
+
+ std::vector<Interval> old_intervals;
+ AddPartition(builder, "system", 10229008, 2048, &old_intervals);
+ AddPartition(builder, "test_a", 648, 12709888, &old_intervals);
+ AddPartition(builder, "test_b", 625184, 12711936, &old_intervals);
+ AddPartition(builder, "test_c", 130912, 13338624, &old_intervals);
+ AddPartition(builder, "test_d", 888, 13469696, &old_intervals);
+ AddPartition(builder, "test_e", 888, 13471744, &old_intervals);
+ AddPartition(builder, "test_f", 888, 13475840, &old_intervals);
+ AddPartition(builder, "test_g", 888, 13477888, &old_intervals);
+
+ // Don't track the first vendor interval, since it will get extended.
+ AddPartition(builder, "vendor", 2477920, 10231808, nullptr);
+
+ std::vector<Interval> new_intervals;
+
+ Partition* p = builder->FindPartition("vendor");
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 1282031616));
+ ASSERT_GE(p->extents().size(), 1);
+ for (const auto& extent : p->extents()) {
+ new_intervals.push_back(ToInterval(extent));
+ }
+
+ std::vector<Interval> overlap = Interval::Intersect(old_intervals, new_intervals);
+ ASSERT_TRUE(overlap.empty());
+}
+
+TEST_F(BuilderTest, LinearExtentOverlap) {
+ LinearExtent extent(20, 0, 10);
+
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 10}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{50, 0, 10}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 0, 30}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{10, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{40, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 15}));
+
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 0}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{50, 1, 10}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{40, 1, 0}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 15}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 10}));
+}
+
+TEST_F(BuilderTest, AlignFreeRegion) {
+ BlockDeviceInfo super("super", 8_GiB, 786432, 0, 4096);
+ std::vector<BlockDeviceInfo> block_devices = {super};
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* p = builder->AddPartition("system", "default", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(p, "super", 64, (super.alignment + 4096) / 512));
+
+ p = builder->AddPartition("vendor", "default", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 2_GiB));
+
+ const auto& extents = p->extents();
+ ASSERT_EQ(extents.size(), 2);
+
+ LinearExtent* e1 = extents[0]->AsLinearExtent();
+ ASSERT_NE(e1, nullptr);
+ LinearExtent* e2 = extents[1]->AsLinearExtent();
+ ASSERT_NE(e2, nullptr);
+
+ // The misaligned partition starting at sector 1544 should not cause any
+ // overlap with previous extents. We should see vendor punch a hole where
+ // "system" is, extending the hole up to the next aligned block.
+ EXPECT_EQ(e1->physical_sector(), 1536);
+ EXPECT_EQ(e1->end_sector(), 1544);
+ EXPECT_EQ(e2->physical_sector(), 3072);
+ EXPECT_EQ(e2->end_sector(), 4197368);
+}
+
+TEST_F(BuilderTest, ResizeOverflow) {
+ BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
+ std::vector<BlockDeviceInfo> block_devices = {super};
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("group", 0));
+
+ Partition* p = builder->AddPartition("system", "default", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL));
+}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 382d53d..6af9d94 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -50,12 +50,7 @@
// Sanity 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.alignment_offset % LP_SECTOR_SIZE, 0);
- EXPECT_LE(device_info.alignment_offset, INT_MAX);
EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
-
- // Having an alignment offset > alignment doesn't really make sense.
- EXPECT_LT(device_info.alignment_offset, device_info.alignment);
}
TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f7738fb..732dbea 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -71,9 +71,8 @@
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
uint32_t device_index() const { return device_index_; }
- bool OwnsSector(uint64_t sector) const {
- return sector >= physical_sector_ && sector < end_sector();
- }
+ bool OverlapsWith(const LinearExtent& other) const;
+ bool OverlapsWith(const Interval& interval) const;
Interval AsInterval() const;
@@ -360,7 +359,7 @@
bool GrowPartition(Partition* partition, uint64_t aligned_size,
const std::vector<Interval>& free_region_hint);
void ShrinkPartition(Partition* partition, uint64_t aligned_size);
- uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const;
+ bool AlignSector(const LpMetadataBlockDevice& device, uint64_t sector, uint64_t* out) const;
uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 0661769..c4fe3ed 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <limits>
#include <string>
#include <string_view>
@@ -66,27 +67,26 @@
void SHA256(const void* data, size_t length, uint8_t out[32]);
// Align |base| such that it is evenly divisible by |alignment|, which does not
-// have to be a power of two.
-constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
+// have to be a power of two. Return false on overflow.
+template <typename T>
+bool AlignTo(T base, uint32_t alignment, T* out) {
+ static_assert(std::numeric_limits<T>::is_integer);
+ static_assert(!std::numeric_limits<T>::is_signed);
if (!alignment) {
- return base;
+ *out = base;
+ return true;
}
- uint64_t remainder = base % alignment;
+ T remainder = base % alignment;
if (remainder == 0) {
- return base;
+ *out = base;
+ return true;
}
- return base + (alignment - remainder);
-}
-
-// Same as the above |AlignTo|, except that |base| is only aligned when added to
-// |alignment_offset|.
-constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment_offset) {
- uint64_t aligned = AlignTo(base, alignment) + alignment_offset;
- if (aligned - alignment >= base) {
- // We overaligned (base < alignment_offset).
- return aligned - alignment;
+ T to_add = alignment - remainder;
+ if (to_add > std::numeric_limits<T>::max() - base) {
+ return false;
}
- return aligned;
+ *out = base + to_add;
+ return true;
}
// Update names from C++ strings.
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index cac3989..fc90872 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <optional>
+
#include <gtest/gtest.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
@@ -58,15 +60,28 @@
EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);
}
+std::optional<uint64_t> AlignTo(uint64_t base, uint32_t alignment) {
+ uint64_t r;
+ if (!AlignTo(base, alignment, &r)) {
+ return {};
+ }
+ return {r};
+}
+
TEST(liblp, AlignTo) {
- EXPECT_EQ(AlignTo(37, 0), 37);
- EXPECT_EQ(AlignTo(1024, 1024), 1024);
- EXPECT_EQ(AlignTo(555, 1024), 1024);
- EXPECT_EQ(AlignTo(555, 1000), 1000);
- EXPECT_EQ(AlignTo(0, 1024), 0);
- EXPECT_EQ(AlignTo(54, 32, 30), 62);
- EXPECT_EQ(AlignTo(32, 32, 30), 62);
- EXPECT_EQ(AlignTo(17, 32, 30), 30);
+ EXPECT_EQ(AlignTo(37, 0), std::optional<uint64_t>(37));
+ EXPECT_EQ(AlignTo(1024, 1024), std::optional<uint64_t>(1024));
+ EXPECT_EQ(AlignTo(555, 1024), std::optional<uint64_t>(1024));
+ EXPECT_EQ(AlignTo(555, 1000), std::optional<uint64_t>(1000));
+ EXPECT_EQ(AlignTo(0, 1024), std::optional<uint64_t>(0));
+ EXPECT_EQ(AlignTo(54, 32), std::optional<uint64_t>(64));
+ EXPECT_EQ(AlignTo(32, 32), std::optional<uint64_t>(32));
+ EXPECT_EQ(AlignTo(17, 32), std::optional<uint64_t>(32));
+
+ auto u32limit = std::numeric_limits<uint32_t>::max();
+ auto u64limit = std::numeric_limits<uint64_t>::max();
+ EXPECT_EQ(AlignTo(u64limit - u32limit + 1, u32limit), std::optional<uint64_t>{u64limit});
+ EXPECT_EQ(AlignTo(std::numeric_limits<uint64_t>::max(), 2), std::optional<uint64_t>{});
}
TEST(liblp, GetPartitionSlotSuffix) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index a209ea6..e916693 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -26,7 +26,6 @@
"libbase",
"libcutils",
"liblog",
- "liblp",
],
static_libs: [
"libdm",
@@ -73,6 +72,7 @@
"device_info.cpp",
"snapshot.cpp",
"snapshot_stats.cpp",
+ "snapshot_stub.cpp",
"snapshot_metadata_updater.cpp",
"partition_cow_creator.cpp",
"return.cpp",
@@ -100,6 +100,7 @@
cc_library_static {
name: "libsnapshot_init",
+ native_coverage : true,
defaults: ["libsnapshot_defaults"],
srcs: [":libsnapshot_sources"],
recovery_available: true,
@@ -160,23 +161,23 @@
"snapshot_test.cpp",
],
shared_libs: [
- "android.hardware.boot@1.0",
- "android.hardware.boot@1.1",
"libbinder",
"libcrypto",
"libhidlbase",
"libprotobuf-cpp-lite",
- "libsparse",
"libutils",
"libz",
],
static_libs: [
+ "android.hardware.boot@1.0",
+ "android.hardware.boot@1.1",
"libfs_mgr",
"libgsi",
"libgmock",
"liblp",
"libsnapshot",
"libsnapshot_test_helpers",
+ "libsparse",
],
header_libs: [
"libstorage_literals_headers",
@@ -209,6 +210,7 @@
static_libs: [
"libfstab",
"libsnapshot",
+ "update_metadata-protos",
],
shared_libs: [
"android.hardware.boot@1.0",
@@ -226,3 +228,73 @@
"libutils",
],
}
+
+cc_test {
+ name: "snapshot_power_test",
+ srcs: [
+ "power_test.cpp",
+ ],
+ static_libs: [
+ "libsnapshot",
+ "update_metadata-protos",
+ ],
+ shared_libs: [
+ "libbase",
+ "libfs_mgr_binder",
+ "liblog",
+ ],
+ gtest: false,
+}
+
+cc_fuzz {
+ name: "libsnapshot_fuzzer",
+
+ // TODO(b/154633114): make host supported.
+ // host_supported: true,
+
+ native_coverage : true,
+ srcs: [
+ // Compile the protobuf definition again with type full.
+ "android/snapshot/snapshot_fuzz.proto",
+ "update_engine/update_metadata.proto",
+ "fuzz_utils.cpp",
+ "snapshot_fuzz.cpp",
+ "snapshot_fuzz_utils.cpp",
+
+ // Compile libsnapshot sources directly to avoid dependency
+ // to update_metadata-protos
+ ":libsnapshot_sources",
+ ],
+ static_libs: [
+ "libbase",
+ "libcrypto_static",
+ "libcutils",
+ "libext2_uuid",
+ "libext4_utils",
+ "libfstab",
+ "libfs_mgr",
+ "libgtest", // from libsnapshot_test_helpers
+ "libgmock", // from libsnapshot_test_helpers
+ "liblog",
+ "liblp",
+ "libsnapshot_test_helpers",
+ "libprotobuf-mutator",
+ ],
+ header_libs: [
+ "libfiemap_headers",
+ "libstorage_literals_headers",
+ ],
+ proto: {
+ type: "full",
+ canonical_path_from_root: false,
+ local_include_dirs: ["."],
+ },
+
+ fuzz_config: {
+ cc: ["android-virtual-ab+bugs@google.com"],
+ componentid: 30545,
+ hotlists: ["1646452"],
+ fuzz_on_haiku_host: false,
+ fuzz_on_haiku_device: true,
+ },
+}
diff --git a/fs_mgr/libsnapshot/PowerTest.md b/fs_mgr/libsnapshot/PowerTest.md
new file mode 100644
index 0000000..0b0cb5d
--- /dev/null
+++ b/fs_mgr/libsnapshot/PowerTest.md
@@ -0,0 +1,40 @@
+snapshot\_power\_test
+---------------------
+
+snapshot\_power\_test is a standalone test to simulate power failures during a snapshot-merge operation.
+
+### Test Setup
+
+Start by creating two large files that will be used as the pre-merge and post-merge state. You can take two different partition images (for example, a product.img from two separate builds), or just create random data:
+
+ dd if=/dev/urandom of=pre-merge count=1024 bs=1048576
+ dd if=/dev/urandom of=post-merge count=1024 bs=1048576
+
+Next, push these files to an unencrypted directory on the device:
+
+ adb push pre-merge /data/local/unencrypted
+ adb push post-merge /data/local/unencrypted
+
+Next, run the test setup:
+
+ adb sync data
+ adb shell /data/nativetest64/snapshot_power_test/snapshot_power_test \
+ /data/local/unencrypted/pre-merge \
+ /data/local/unencrypted/post-merge
+
+This will create the necessary fiemap-based images.
+
+### Running
+The actual test can be run via `run_power_test.sh`. Its syntax is:
+
+ run_power_test.sh <POST_MERGE_FILE>
+
+`POST_MERGE_FILE` should be the path on the device of the image to validate the merge against. Example:
+
+ run_power_test.sh /data/local/unencrypted/post-merge
+
+The device will begin the merge with a 5% chance of injecting a kernel crash every 10ms. The device should be capable of rebooting normally without user intervention. Once the merge has completed, the test will run a final check command to validate the contents of the snapshot against the post-merge file. It will error if there are any incorrect blocks.
+
+Two environment variables can be passed to `run_power_test.sh`:
+1. `FAIL_RATE` - A fraction between 0 and 100 (inclusive) indicating the probability the device should inject a kernel crash every 10ms.
+2. `DEVICE_SERIAL` - If multiple devices are attached to adb, this argument is passed as the serial to select (to `adb -s`).
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
new file mode 100644
index 0000000..91fbb60
--- /dev/null
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
@@ -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.
+
+syntax = "proto3";
+package android.snapshot;
+
+import "update_engine/update_metadata.proto";
+
+// Controls the behavior of IDeviceInfo.
+// Next: 6
+message FuzzDeviceInfoData {
+ bool slot_suffix_is_a = 1;
+ bool is_overlayfs_setup = 2;
+ bool allow_set_boot_control_merge_status = 3;
+ bool allow_set_slot_as_unbootable = 4;
+ bool is_recovery = 5;
+}
+
+// Controls the behavior of the test SnapshotManager.
+// Next: 2
+message FuzzSnapshotManagerData {
+ bool is_local_image_manager = 1;
+}
+
+// A simplified version of CreateLogicalPartitionParams for fuzzing.
+// Next: 9
+message CreateLogicalPartitionParamsProto {
+ bool use_correct_super = 1;
+ string block_device = 2;
+ bool has_metadata_slot = 3;
+ uint32 metadata_slot = 4;
+ string partition_name = 5;
+ bool force_writable = 6;
+ int64 timeout_millis = 7;
+ string device_name = 8;
+}
+
+// Mimics the API of ISnapshotManager. Defines one action on the snapshot
+// manager.
+// Next: 18
+message SnapshotManagerActionProto {
+ message NoArgs {}
+ message ProcessUpdateStateArgs {
+ bool has_before_cancel = 1;
+ bool fail_before_cancel = 2;
+ }
+ message CreateLogicalAndSnapshotPartitionsArgs {
+ bool use_correct_super = 1;
+ string super = 2;
+ int64 timeout_millis = 3;
+ }
+ message RecoveryCreateSnapshotDevicesArgs {
+ bool has_metadata_device_object = 1;
+ bool metadata_mounted = 2;
+ }
+ oneof value {
+ NoArgs begin_update = 1;
+ NoArgs cancel_update = 2;
+ bool finished_snapshot_writes = 3;
+ NoArgs initiate_merge = 4;
+ ProcessUpdateStateArgs process_update_state = 5;
+ bool get_update_state = 6;
+ chromeos_update_engine.DeltaArchiveManifest create_update_snapshots = 7;
+ CreateLogicalPartitionParamsProto map_update_snapshot = 8;
+ string unmap_update_snapshot = 9;
+ NoArgs need_snapshots_in_first_stage_mount = 10;
+ CreateLogicalAndSnapshotPartitionsArgs create_logical_and_snapshot_partitions = 11;
+ bool handle_imminent_data_wipe = 12;
+ NoArgs recovery_create_snapshot_devices = 13;
+ RecoveryCreateSnapshotDevicesArgs recovery_create_snapshot_devices_with_metadata = 14;
+ NoArgs dump = 15;
+ NoArgs ensure_metadata_mounted = 16;
+ NoArgs get_snapshot_merge_stats_instance = 17;
+ }
+}
+
+// Includes all data that needs to be fuzzed.
+message SnapshotFuzzData {
+ FuzzDeviceInfoData device_info_data = 1;
+ FuzzSnapshotManagerData manager_data = 2;
+
+ // If true:
+ // - if super_data is empty, create empty super partition metadata.
+ // - otherwise, create super partition metadata accordingly.
+ // If false, no valid super partition metadata (it is zeroed)
+ bool is_super_metadata_valid = 3;
+ chromeos_update_engine.DeltaArchiveManifest super_data = 4;
+
+ // More data used to prep the test before running actions.
+ reserved 5 to 9999;
+ repeated SnapshotManagerActionProto actions = 10000;
+}
diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
new file mode 100755
index 0000000..2910129
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+PROJECT_PATH=system/core/fs_mgr/libsnapshot
+FUZZ_TARGET=libsnapshot_fuzzer
+TARGET_ARCH=$(get_build_var TARGET_ARCH)
+FUZZ_BINARY=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}
+DEVICE_CORPSE_DIR=/data/local/tmp/${FUZZ_TARGET}
+DEVICE_GCOV_DIR=/data/local/tmp/${FUZZ_TARGET}/gcov
+HOST_SCRATCH_DIR=/tmp/${FUZZ_TARGET}
+GCOV_TOOL=${HOST_SCRATCH_DIR}/llvm-gcov
+
+build_normal() (
+ pushd $(gettop)
+ NATIVE_COVERAGE="" NATIVE_LINE_COVERAGE="" COVERAGE_PATHS="" m ${FUZZ_TARGET}
+ ret=$?
+ popd
+ return ${ret}
+)
+
+build_cov() {
+ pushd $(gettop)
+ NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET}
+ ret=$?
+ popd
+ return ${ret}
+}
+
+prepare_device() {
+ adb root && adb remount &&
+ adb shell mkdir -p ${DEVICE_CORPSE_DIR} &&
+ adb shell rm -rf ${DEVICE_GCOV_DIR} &&
+ adb shell mkdir -p ${DEVICE_GCOV_DIR}
+}
+
+push_binary() {
+ adb push ${ANDROID_PRODUCT_OUT}/${FUZZ_BINARY} ${FUZZ_BINARY}
+}
+
+prepare_host() {
+ which lcov || {
+ echo "please run:";
+ echo " sudo apt-get install lcov ";
+ return 1;
+ }
+ rm -rf ${HOST_SCRATCH_DIR} &&
+ mkdir -p ${HOST_SCRATCH_DIR}
+}
+
+# run_snapshot_fuzz -runs=10000
+generate_corpus() {
+ [[ "$@" ]] || { echo "run with -runs=X"; return 1; }
+
+ prepare_device &&
+ build_normal &&
+ push_binary &&
+ adb shell ${FUZZ_BINARY} "$@" ${DEVICE_CORPSE_DIR}
+}
+
+run_snapshot_fuzz() {
+ prepare_device &&
+ build_cov &&
+ push_binary &&
+ adb shell GCOV_PREFIX=${DEVICE_GCOV_DIR} GCOV_PREFIX_STRIP=3 \
+ ${FUZZ_BINARY} \
+ -runs=0 \
+ ${DEVICE_CORPSE_DIR}
+}
+
+show_fuzz_result() {
+ prepare_host &&
+ unzip -o -j -d ${HOST_SCRATCH_DIR} ${ANDROID_PRODUCT_OUT}/coverage/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}.zip &&
+ adb shell find ${DEVICE_GCOV_DIR} -type f | xargs -I {} adb pull {} ${HOST_SCRATCH_DIR} &&
+ ls ${HOST_SCRATCH_DIR} &&
+ cat > ${GCOV_TOOL} <<< '
+#!/bin/bash
+exec llvm-cov gcov "$@"
+' &&
+ chmod +x ${GCOV_TOOL} &&
+ lcov --directory ${HOST_SCRATCH_DIR} --base-directory $(gettop) --gcov-tool ${GCOV_TOOL} --capture -o ${HOST_SCRATCH_DIR}/report.cov &&
+ genhtml ${HOST_SCRATCH_DIR}/report.cov -o ${HOST_SCRATCH_DIR}/html &&
+ echo file://$(realpath ${HOST_SCRATCH_DIR}/html/index.html)
+}
+
+# run_snapshot_fuzz -runs=10000
+run_snapshot_fuzz_all() {
+ generate_corpse "$@" &&
+ run_snapshot_fuzz &&
+ show_fuzz_result
+}
diff --git a/fs_mgr/libsnapshot/fuzz_utils.cpp b/fs_mgr/libsnapshot/fuzz_utils.cpp
new file mode 100644
index 0000000..0263f7e
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz_utils.cpp
@@ -0,0 +1,38 @@
+// 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 "fuzz_utils.h"
+
+#include <android-base/logging.h>
+
+namespace android::fuzz {
+
+void CheckInternal(bool value, std::string_view msg) {
+ CHECK(value) << msg;
+}
+
+const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
+ const google::protobuf::Descriptor* action_desc) {
+ CHECK(action_desc);
+ CHECK(action_desc->oneof_decl_count() == 1)
+ << action_desc->oneof_decl_count() << " oneof fields found in " << action_desc->name()
+ << "; only one is expected.";
+ auto* oneof_value_desc = action_desc->oneof_decl(0);
+ CHECK(oneof_value_desc);
+ CHECK(oneof_value_desc->name() == "value")
+ << "oneof field has name " << oneof_value_desc->name();
+ return oneof_value_desc;
+}
+
+} // namespace android::fuzz
diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
new file mode 100644
index 0000000..4dc6cdc
--- /dev/null
+++ b/fs_mgr/libsnapshot/fuzz_utils.h
@@ -0,0 +1,265 @@
+// 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 <map>
+#include <string>
+#include <string_view>
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/repeated_field.h>
+
+// Utilities for using a protobuf definition to fuzz APIs in a class.
+// Terms:
+// The "fuzzed class" is the C++ class definition whose functions are fuzzed.
+// The "fuzzed object" is an instantiated object of the fuzzed class. It is
+// typically created and destroyed for each test run.
+// An "action" is an operation on the fuzzed object that may mutate its state.
+// This typically involves one function call into the fuzzed object.
+
+namespace android::fuzz {
+
+// CHECK(value) << msg
+void CheckInternal(bool value, std::string_view msg);
+
+// Get the oneof descriptor inside Action
+const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
+ const google::protobuf::Descriptor* action_desc);
+
+template <typename Class>
+using FunctionMapImpl =
+ std::map<int, std::function<void(Class*, const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc)>>;
+
+template <typename Class>
+class FunctionMap : public FunctionMapImpl<Class> {
+ public:
+ void CheckEmplace(typename FunctionMapImpl<Class>::key_type key,
+ typename FunctionMapImpl<Class>::mapped_type&& value) {
+ auto [it, inserted] = this->emplace(key, std::move(value));
+ CheckInternal(inserted,
+ "Multiple implementation registered for tag number " + std::to_string(key));
+ }
+};
+
+template <typename Action>
+int CheckConsistency() {
+ const auto* function_map = Action::GetFunctionMap();
+ const auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
+
+ for (int field_index = 0; field_index < action_value_desc->field_count(); ++field_index) {
+ const auto* field_desc = action_value_desc->field(field_index);
+ CheckInternal(function_map->find(field_desc->number()) != function_map->end(),
+ "Missing impl for function " + field_desc->camelcase_name());
+ }
+ return 0;
+}
+
+template <typename Action>
+void ExecuteActionProto(typename Action::Class* module,
+ const typename Action::Proto& action_proto) {
+ static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
+
+ auto* action_refl = Action::Proto::GetReflection();
+ if (!action_refl->HasOneof(action_proto, action_value_desc)) {
+ return;
+ }
+
+ const auto* field_desc = action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
+ auto number = field_desc->number();
+ const auto& map = *Action::GetFunctionMap();
+ auto it = map.find(number);
+ CheckInternal(it != map.end(), "Missing impl for function " + field_desc->camelcase_name());
+ const auto& func = it->second;
+ func(module, action_proto, field_desc);
+}
+
+template <typename Action>
+void ExecuteAllActionProtos(
+ typename Action::Class* module,
+ const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
+ for (const auto& proto : action_protos) {
+ ExecuteActionProto<Action>(module, proto);
+ }
+}
+
+// Safely cast message to T. Returns a pointer to message if cast successfully, otherwise nullptr.
+template <typename T>
+const T* SafeCast(const google::protobuf::Message& message) {
+ if (message.GetDescriptor() != T::GetDescriptor()) {
+ return nullptr;
+ }
+ return static_cast<const T*>(&message);
+}
+
+// Cast message to const T&. Abort if type mismatch.
+template <typename T>
+const T& CheckedCast(const google::protobuf::Message& message) {
+ const auto* ptr = SafeCast<T>(message);
+ CheckInternal(ptr, "Cannot cast " + message.GetDescriptor()->name() + " to " +
+ T::GetDescriptor()->name());
+ return *ptr;
+}
+
+// A templated way to a primitive field from a message using reflection.
+template <typename T>
+struct PrimitiveGetter;
+#define FUZZ_DEFINE_PRIMITIVE_GETTER(type, func_name) \
+ template <> \
+ struct PrimitiveGetter<type> { \
+ static constexpr const auto fp = &google::protobuf::Reflection::func_name; \
+ }
+
+FUZZ_DEFINE_PRIMITIVE_GETTER(bool, GetBool);
+FUZZ_DEFINE_PRIMITIVE_GETTER(uint32_t, GetUInt32);
+FUZZ_DEFINE_PRIMITIVE_GETTER(int32_t, GetInt32);
+FUZZ_DEFINE_PRIMITIVE_GETTER(uint64_t, GetUInt64);
+FUZZ_DEFINE_PRIMITIVE_GETTER(int64_t, GetInt64);
+FUZZ_DEFINE_PRIMITIVE_GETTER(double, GetDouble);
+FUZZ_DEFINE_PRIMITIVE_GETTER(float, GetFloat);
+
+// ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction
+// with these arguments.
+template <typename FuzzFunction, typename Signature, typename Enabled = void>
+struct ActionPerfomer; // undefined
+
+template <typename FuzzFunction, typename MessageProto>
+struct ActionPerfomer<
+ FuzzFunction, void(const MessageProto&),
+ typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
+ static void Invoke(typename FuzzFunction::Class* module,
+ const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
+ const MessageProto& arg = CheckedCast<std::remove_reference_t<MessageProto>>(
+ action_proto.GetReflection()->GetMessage(action_proto, field_desc));
+ FuzzFunction::ImplBody(module, arg);
+ }
+};
+
+template <typename FuzzFunction, typename Primitive>
+struct ActionPerfomer<FuzzFunction, void(Primitive),
+ typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
+ static void Invoke(typename FuzzFunction::Class* module,
+ const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
+ Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
+ action_proto, field_desc);
+ FuzzFunction::ImplBody(module, arg);
+ }
+};
+
+template <typename FuzzFunction>
+struct ActionPerfomer<FuzzFunction, void()> {
+ static void Invoke(typename FuzzFunction::Class* module, const google::protobuf::Message&,
+ const google::protobuf::FieldDescriptor*) {
+ FuzzFunction::ImplBody(module);
+ }
+};
+
+template <typename FuzzFunction>
+struct ActionPerfomer<FuzzFunction, void(const std::string&)> {
+ static void Invoke(typename FuzzFunction::Class* module,
+ const google::protobuf::Message& action_proto,
+ const google::protobuf::FieldDescriptor* field_desc) {
+ std::string scratch;
+ const std::string& arg = action_proto.GetReflection()->GetStringReference(
+ action_proto, field_desc, &scratch);
+ FuzzFunction::ImplBody(module, arg);
+ }
+};
+
+} // namespace android::fuzz
+
+// Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
+//
+// Prerequisite: ActionProto must be defined in Protobuf to describe possible actions:
+// message FooActionProto {
+// message NoArgs {}
+// oneof value {
+// bool do_foo = 1;
+// NoArgs do_bar = 1;
+// }
+// }
+// Use it to fuzz a C++ class Foo by doing the following:
+// FUZZ_CLASS(Foo, FooAction)
+// After linking functions of Foo to FooAction, execute all actions by:
+// FooAction::ExecuteAll(foo_object, action_protos)
+#define FUZZ_CLASS(ClassType, Action) \
+ class Action { \
+ public: \
+ using Proto = Action##Proto; \
+ using Class = ClassType; \
+ using FunctionMap = android::fuzz::FunctionMap<Class>; \
+ static FunctionMap* GetFunctionMap() { \
+ static Action::FunctionMap map; \
+ return ↦ \
+ } \
+ static void ExecuteAll(Class* module, \
+ const google::protobuf::RepeatedPtrField<Proto>& action_protos) { \
+ [[maybe_unused]] static int consistent = android::fuzz::CheckConsistency<Action>(); \
+ android::fuzz::ExecuteAllActionProtos<Action>(module, action_protos); \
+ } \
+ }
+
+#define FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) Action##_##FunctionName
+#define FUZZ_FUNCTION_TAG_NAME(FunctionName) k##FunctionName
+
+// Implement an action defined in protobuf. Example:
+// message FooActionProto {
+// oneof value {
+// bool do_foo = 1;
+// }
+// }
+// class Foo { public: void DoAwesomeFoo(bool arg); };
+// FUZZ_OBJECT(FooAction, Foo);
+// FUZZ_FUNCTION(FooAction, DoFoo, module, bool arg) {
+// module->DoAwesomeFoo(arg);
+// }
+// The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto.
+#define FUZZ_FUNCTION(Action, FunctionName, module, ...) \
+ class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) { \
+ public: \
+ using Class = Action::Class; \
+ static void ImplBody(Action::Class*, ##__VA_ARGS__); \
+ \
+ private: \
+ static bool registered_; \
+ }; \
+ auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] { \
+ auto tag = Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \
+ auto func = \
+ &::android::fuzz::ActionPerfomer<FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName), \
+ void(__VA_ARGS__)>::Invoke; \
+ Action::GetFunctionMap()->CheckEmplace(tag, func); \
+ return true; \
+ })(); \
+ void FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(Action::Class* module, \
+ ##__VA_ARGS__)
+
+// Implement a simple action by linking it to the function with the same name. Example:
+// message FooActionProto {
+// message NoArgs {}
+// oneof value {
+// NoArgs do_bar = 1;
+// }
+// }
+// class Foo { public void DoBar(); };
+// FUZZ_OBJECT(FooAction, Foo);
+// FUZZ_FUNCTION(FooAction, DoBar);
+// The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and
+// also the name of the function of Foo.
+#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \
+ FUZZ_FUNCTION(Action, FunctionName, module) { (void)module->FunctionName(); }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
new file mode 100644
index 0000000..ef9d648
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
@@ -0,0 +1,37 @@
+// 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 <libsnapshot/snapshot.h>
+
+#include <gmock/gmock.h>
+
+namespace android::snapshot {
+
+class MockDeviceInfo : public SnapshotManager::IDeviceInfo {
+ public:
+ MOCK_METHOD(std::string, GetGsidDir, (), (const, override));
+ MOCK_METHOD(std::string, GetMetadataDir, (), (const, override));
+ MOCK_METHOD(std::string, GetSlotSuffix, (), (const, override));
+ MOCK_METHOD(std::string, GetOtherSlotSuffix, (), (const, override));
+ MOCK_METHOD(std::string, GetSuperDevice, (uint32_t slot), (const, override));
+ MOCK_METHOD(const android::fs_mgr::IPartitionOpener&, GetPartitionOpener, (), (const));
+ MOCK_METHOD(bool, IsOverlayfsSetup, (), (const, override));
+ MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (override));
+ MOCK_METHOD(bool, SetSlotAsUnbootable, (unsigned int slot), (override));
+ MOCK_METHOD(bool, IsRecovery, (), (const, override));
+};
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
new file mode 100644
index 0000000..cf0b085
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.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 <libsnapshot/snapshot.h>
+
+#include <gmock/gmock.h>
+
+namespace android::snapshot {
+
+class MockSnapshotManager : public ISnapshotManager {
+ public:
+ MOCK_METHOD(bool, BeginUpdate, (), (override));
+ MOCK_METHOD(bool, CancelUpdate, (), (override));
+ MOCK_METHOD(bool, FinishedSnapshotWrites, (bool wipe), (override));
+ MOCK_METHOD(bool, InitiateMerge, (), (override));
+
+ MOCK_METHOD(UpdateState, ProcessUpdateState,
+ (const std::function<bool()>& callback, const std::function<bool()>& before_cancel),
+ (override));
+ MOCK_METHOD(UpdateState, GetUpdateState, (double* progress), (override));
+ MOCK_METHOD(Return, CreateUpdateSnapshots,
+ (const chromeos_update_engine::DeltaArchiveManifest& manifest), (override));
+ MOCK_METHOD(bool, MapUpdateSnapshot,
+ (const android::fs_mgr::CreateLogicalPartitionParams& params,
+ std::string* snapshot_path),
+ (override));
+ MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
+ MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
+ MOCK_METHOD(bool, CreateLogicalAndSnapshotPartitions,
+ (const std::string& super_device, const std::chrono::milliseconds& timeout_ms),
+ (override));
+ MOCK_METHOD(bool, HandleImminentDataWipe, (const std::function<void()>& callback), (override));
+ MOCK_METHOD(bool, FinishMergeInRecovery, (), (override));
+ MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices, (), (override));
+ MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices,
+ (const std::unique_ptr<AutoDevice>& metadata_device), (override));
+ MOCK_METHOD(bool, Dump, (std::ostream & os), (override));
+ MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));
+ MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));
+};
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 1daa83b..5acd039 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -70,6 +70,8 @@
struct AutoDeleteSnapshot;
struct AutoDeviceList;
struct PartitionCowCreator;
+class ISnapshotMergeStats;
+class SnapshotMergeStats;
class SnapshotStatus;
static constexpr const std::string_view kCowGroupName = "cow";
@@ -83,17 +85,7 @@
NOT_CREATED,
};
-class SnapshotManager final {
- using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
- using IPartitionOpener = android::fs_mgr::IPartitionOpener;
- using LpMetadata = android::fs_mgr::LpMetadata;
- using MetadataBuilder = android::fs_mgr::MetadataBuilder;
- using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
- using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
- using FiemapStatus = android::fiemap::FiemapStatus;
-
- friend class SnapshotMergeStats;
-
+class ISnapshotManager {
public:
// Dependency injection for testing.
class IDeviceInfo {
@@ -104,39 +96,23 @@
virtual std::string GetSlotSuffix() const = 0;
virtual std::string GetOtherSlotSuffix() const = 0;
virtual std::string GetSuperDevice(uint32_t slot) const = 0;
- virtual const IPartitionOpener& GetPartitionOpener() const = 0;
+ virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
virtual bool IsOverlayfsSetup() const = 0;
- virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
+ virtual bool SetBootControlMergeStatus(
+ android::hardware::boot::V1_1::MergeStatus status) = 0;
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0;
};
-
- ~SnapshotManager();
-
- // Return a new SnapshotManager instance, or null on error. The device
- // pointer is owned for the lifetime of SnapshotManager. If null, a default
- // instance will be created.
- static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
-
- // This is similar to New(), except designed specifically for first-stage
- // init or recovery.
- static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
-
- // Helper function for first-stage init to check whether a SnapshotManager
- // might be needed to perform first-stage mounts.
- static bool IsSnapshotManagerNeeded();
-
- // Helper function for second stage init to restorecon on the rollback indicator.
- static std::string GetGlobalRollbackIndicatorPath();
+ virtual ~ISnapshotManager() = default;
// Begin an update. This must be called before creating any snapshots. It
// will fail if GetUpdateState() != None.
- bool BeginUpdate();
+ virtual bool BeginUpdate() = 0;
// Cancel an update; any snapshots will be deleted. This is allowed if the
// state == Initiated, None, or Unverified (before rebooting to the new
// slot).
- bool CancelUpdate();
+ virtual bool CancelUpdate() = 0;
// Mark snapshot writes as having completed. After this, new snapshots cannot
// be created, and the device must either cancel the OTA (either before
@@ -144,11 +120,11 @@
// Before calling this function, all snapshots must be mapped.
// If |wipe| is set to true, wipe is scheduled after reboot, and snapshots
// may need to be merged before wiping.
- bool FinishedSnapshotWrites(bool wipe);
+ virtual bool FinishedSnapshotWrites(bool wipe) = 0;
// Initiate a merge on all snapshot devices. This should only be used after an
// update has been marked successful after booting.
- bool InitiateMerge();
+ virtual bool InitiateMerge() = 0;
// Perform any necessary post-boot actions. This should be run soon after
// /data is mounted.
@@ -178,8 +154,8 @@
//
// The optional callback allows the caller to periodically check the
// progress with GetUpdateState().
- UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
- const std::function<bool()>& before_cancel = {});
+ virtual UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+ const std::function<bool()>& before_cancel = {}) = 0;
// Find the status of the current update, if any.
//
@@ -187,28 +163,31 @@
// Merging: Value in the range [0, 100]
// MergeCompleted: 100
// Other: 0
- UpdateState GetUpdateState(double* progress = nullptr);
+ virtual UpdateState GetUpdateState(double* progress = nullptr) = 0;
// Create necessary COW device / files for OTA clients. New logical partitions will be added to
// group "cow" in target_metadata. Regions of partitions of current_metadata will be
// "write-protected" and snapshotted.
- Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
+ virtual Return CreateUpdateSnapshots(
+ const chromeos_update_engine::DeltaArchiveManifest& manifest) = 0;
// Map a snapshotted partition for OTA clients to write to. Write-protected regions are
// determined previously in CreateSnapshots.
- bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params, std::string* snapshot_path);
+ // |snapshot_path| must not be nullptr.
+ virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
+ std::string* snapshot_path) = 0;
// Unmap a snapshot device that's previously mapped with MapUpdateSnapshot.
- bool UnmapUpdateSnapshot(const std::string& target_partition_name);
+ virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0;
// If this returns true, first-stage mount must call
// CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
- bool NeedSnapshotsInFirstStageMount();
+ virtual bool NeedSnapshotsInFirstStageMount() = 0;
// Perform first-stage mapping of snapshot targets. This replaces init's
// call to CreateLogicalPartitions when snapshots are present.
- bool CreateLogicalAndSnapshotPartitions(const std::string& super_device,
- const std::chrono::milliseconds& timeout_ms = {});
+ virtual bool CreateLogicalAndSnapshotPartitions(
+ const std::string& super_device, const std::chrono::milliseconds& timeout_ms = {}) = 0;
// This method should be called preceding any wipe or flash of metadata or
// userdata. It is only valid in recovery or fastbootd, and it ensures that
@@ -221,7 +200,11 @@
//
// Returns true on success (or nothing to do), false on failure. The
// optional callback fires periodically to query progress via GetUpdateState.
- bool HandleImminentDataWipe(const std::function<void()>& callback = {});
+ virtual bool HandleImminentDataWipe(const std::function<void()>& callback = {}) = 0;
+
+ // Force a merge to complete in recovery. This is similar to HandleImminentDataWipe
+ // but does not expect a data wipe after.
+ virtual bool FinishMergeInRecovery() = 0;
// This method is only allowed in recovery and is used as a helper to
// initialize the snapshot devices as a requirement to mount a snapshotted
@@ -234,14 +217,15 @@
// be aborted.
// This function mounts /metadata when called, and unmounts /metadata upon
// return.
- CreateResult RecoveryCreateSnapshotDevices();
+ virtual CreateResult RecoveryCreateSnapshotDevices() = 0;
// Same as RecoveryCreateSnapshotDevices(), but does not auto mount/umount
// /metadata.
- CreateResult RecoveryCreateSnapshotDevices(const std::unique_ptr<AutoDevice>& metadata_device);
+ virtual CreateResult RecoveryCreateSnapshotDevices(
+ const std::unique_ptr<AutoDevice>& metadata_device) = 0;
// Dump debug information.
- bool Dump(std::ostream& os);
+ virtual bool Dump(std::ostream& os) = 0;
// Ensure metadata directory is mounted in recovery. When the returned
// AutoDevice is destroyed, the metadata directory is automatically
@@ -257,7 +241,66 @@
// auto b = mgr->EnsureMetadataMounted(); // does nothing
// b.reset() // unmounts
// a.reset() // does nothing
- std::unique_ptr<AutoDevice> EnsureMetadataMounted();
+ virtual std::unique_ptr<AutoDevice> EnsureMetadataMounted() = 0;
+
+ // Return the associated ISnapshotMergeStats instance. Never null.
+ virtual ISnapshotMergeStats* GetSnapshotMergeStatsInstance() = 0;
+};
+
+class SnapshotManager final : public ISnapshotManager {
+ using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
+ using IPartitionOpener = android::fs_mgr::IPartitionOpener;
+ using LpMetadata = android::fs_mgr::LpMetadata;
+ using MetadataBuilder = android::fs_mgr::MetadataBuilder;
+ using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
+ using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+ using FiemapStatus = android::fiemap::FiemapStatus;
+
+ friend class SnapshotMergeStats;
+
+ public:
+ ~SnapshotManager();
+
+ // Return a new SnapshotManager instance, or null on error. The device
+ // pointer is owned for the lifetime of SnapshotManager. If null, a default
+ // instance will be created.
+ static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
+
+ // This is similar to New(), except designed specifically for first-stage
+ // init or recovery.
+ static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
+
+ // Helper function for first-stage init to check whether a SnapshotManager
+ // might be needed to perform first-stage mounts.
+ static bool IsSnapshotManagerNeeded();
+
+ // Helper function for second stage init to restorecon on the rollback indicator.
+ static std::string GetGlobalRollbackIndicatorPath();
+
+ // ISnapshotManager overrides.
+ bool BeginUpdate() override;
+ bool CancelUpdate() override;
+ bool FinishedSnapshotWrites(bool wipe) override;
+ bool InitiateMerge() override;
+ UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+ const std::function<bool()>& before_cancel = {}) override;
+ UpdateState GetUpdateState(double* progress = nullptr) override;
+ Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
+ bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
+ std::string* snapshot_path) override;
+ bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
+ bool NeedSnapshotsInFirstStageMount() override;
+ bool CreateLogicalAndSnapshotPartitions(
+ const std::string& super_device,
+ const std::chrono::milliseconds& timeout_ms = {}) override;
+ bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+ bool FinishMergeInRecovery() override;
+ CreateResult RecoveryCreateSnapshotDevices() override;
+ CreateResult RecoveryCreateSnapshotDevices(
+ const std::unique_ptr<AutoDevice>& metadata_device) override;
+ bool Dump(std::ostream& os) override;
+ std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
+ ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
@@ -281,6 +324,7 @@
friend class SnapshotUpdateTest;
friend class FlashAfterUpdateTest;
friend class LockTestConsumer;
+ friend class SnapshotFuzzEnv;
friend struct AutoDeleteCowImage;
friend struct AutoDeleteSnapshot;
friend struct PartitionCowCreator;
@@ -541,6 +585,7 @@
std::unique_ptr<IDeviceInfo> device_;
std::unique_ptr<IImageManager> images_;
bool has_local_image_manager_ = false;
+ bool in_factory_data_reset_ = false;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 91dd34f..4caf632 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -23,14 +23,12 @@
namespace android {
namespace snapshot {
-class SnapshotMergeStats {
+class ISnapshotMergeStats {
public:
- // Not thread safe.
- static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
-
+ virtual ~ISnapshotMergeStats() = default;
// Called when merge starts or resumes.
- bool Start();
- void set_state(android::snapshot::UpdateState state);
+ virtual bool Start() = 0;
+ virtual void set_state(android::snapshot::UpdateState state) = 0;
// Called when merge ends. Properly clean up permanent storage.
class Result {
@@ -40,7 +38,19 @@
// Time between successful Start() / Resume() to Finish().
virtual std::chrono::steady_clock::duration merge_time() const = 0;
};
- std::unique_ptr<Result> Finish();
+ // Return nullptr if any failure.
+ virtual std::unique_ptr<Result> Finish() = 0;
+};
+
+class SnapshotMergeStats : public ISnapshotMergeStats {
+ public:
+ // Not thread safe.
+ static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
+
+ // ISnapshotMergeStats overrides
+ bool Start() override;
+ void set_state(android::snapshot::UpdateState state) override;
+ std::unique_ptr<Result> Finish() override;
private:
bool ReadState();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
new file mode 100644
index 0000000..9c82906
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -0,0 +1,53 @@
+// 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 <libsnapshot/snapshot.h>
+
+namespace android::snapshot {
+
+class SnapshotManagerStub : public ISnapshotManager {
+ public:
+ // Create a stubbed snapshot manager. All calls into the stub fails.
+ static std::unique_ptr<ISnapshotManager> New();
+
+ // ISnapshotManager overrides.
+ bool BeginUpdate() override;
+ bool CancelUpdate() override;
+ bool FinishedSnapshotWrites(bool wipe) override;
+ bool InitiateMerge() override;
+ UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
+ const std::function<bool()>& before_cancel = {}) override;
+ UpdateState GetUpdateState(double* progress = nullptr) override;
+ Return CreateUpdateSnapshots(
+ const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
+ bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
+ std::string* snapshot_path) override;
+ bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
+ bool NeedSnapshotsInFirstStageMount() override;
+ bool CreateLogicalAndSnapshotPartitions(
+ const std::string& super_device,
+ const std::chrono::milliseconds& timeout_ms = {}) override;
+ bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;
+ bool FinishMergeInRecovery() override;
+ CreateResult RecoveryCreateSnapshotDevices() override;
+ CreateResult RecoveryCreateSnapshotDevices(
+ const std::unique_ptr<AutoDevice>& metadata_device) override;
+ bool Dump(std::ostream& os) override;
+ std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
+ ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
+};
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index efdb59f..0df5664 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -181,6 +181,13 @@
ret.snapshot_status.set_device_size(target_partition->size());
ret.snapshot_status.set_snapshot_size(target_partition->size());
+ if (ret.snapshot_status.snapshot_size() == 0) {
+ LOG(INFO) << "Not creating snapshot for partition " << ret.snapshot_status.name();
+ ret.snapshot_status.set_cow_partition_size(0);
+ ret.snapshot_status.set_cow_file_size(0);
+ return ret;
+ }
+
// Being the COW partition virtual, its size doesn't affect the storage
// memory that will be occupied by the target.
// The actual storage space is affected by the COW file, whose size depends
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index 526f874..adfb975 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -46,20 +46,20 @@
};
TEST_F(PartitionCowCreatorTest, IntersectSelf) {
- constexpr uint64_t initial_size = 1_MiB;
- constexpr uint64_t final_size = 40_KiB;
+ constexpr uint64_t super_size = 1_MiB;
+ constexpr uint64_t partition_size = 40_KiB;
- auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_a, nullptr);
auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_a, nullptr);
- ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));
- auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_b, nullptr);
auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));
PartitionCowCreator creator{.target_metadata = builder_b.get(),
.target_suffix = "_b",
@@ -68,8 +68,8 @@
.current_suffix = "_a"};
auto ret = creator.Run();
ASSERT_TRUE(ret.has_value());
- ASSERT_EQ(final_size, ret->snapshot_status.device_size());
- ASSERT_EQ(final_size, ret->snapshot_status.snapshot_size());
+ ASSERT_EQ(partition_size, ret->snapshot_status.device_size());
+ ASSERT_EQ(partition_size, ret->snapshot_status.snapshot_size());
}
TEST_F(PartitionCowCreatorTest, Holes) {
@@ -118,20 +118,20 @@
using RepeatedInstallOperationPtr = google::protobuf::RepeatedPtrField<InstallOperation>;
using Extent = chromeos_update_engine::Extent;
- constexpr uint64_t initial_size = 50_MiB;
- constexpr uint64_t final_size = 40_MiB;
+ constexpr uint64_t super_size = 50_MiB;
+ constexpr uint64_t partition_size = 40_MiB;
- auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_a, nullptr);
auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_a, nullptr);
- ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
+ ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));
- auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
ASSERT_NE(builder_b, nullptr);
auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
ASSERT_NE(system_b, nullptr);
- ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
+ ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));
const uint64_t block_size = builder_b->logical_block_size();
const uint64_t chunk_size = kSnapshotChunkSize * dm::kSectorSize;
@@ -197,6 +197,31 @@
ASSERT_EQ(6 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
}
+TEST_F(PartitionCowCreatorTest, Zero) {
+ constexpr uint64_t super_size = 1_MiB;
+ auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
+ ASSERT_NE(builder_a, nullptr);
+
+ auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
+ ASSERT_NE(builder_b, nullptr);
+ auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system_b, nullptr);
+
+ PartitionCowCreator creator{.target_metadata = builder_b.get(),
+ .target_suffix = "_b",
+ .target_partition = system_b,
+ .current_metadata = builder_a.get(),
+ .current_suffix = "_a",
+ .operations = nullptr};
+
+ auto ret = creator.Run();
+
+ ASSERT_EQ(0u, ret->snapshot_status.device_size());
+ ASSERT_EQ(0u, ret->snapshot_status.snapshot_size());
+ ASSERT_EQ(0u, ret->snapshot_status.cow_file_size());
+ ASSERT_EQ(0u, ret->snapshot_status.cow_partition_size());
+}
+
TEST(DmSnapshotInternals, CowSizeCalculator) {
DmSnapCowSizeCalculator cc(512, 8);
unsigned long int b;
diff --git a/fs_mgr/libsnapshot/power_test.cpp b/fs_mgr/libsnapshot/power_test.cpp
new file mode 100644
index 0000000..4d2548a
--- /dev/null
+++ b/fs_mgr/libsnapshot/power_test.cpp
@@ -0,0 +1,559 @@
+//
+// 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 <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <iostream>
+#include <random>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parsedouble.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+using android::dm::DmTable;
+using android::dm::DmTargetSnapshot;
+using android::dm::SnapshotStorageMode;
+using android::fiemap::ImageManager;
+using android::fs_mgr::Fstab;
+
+namespace android {
+namespace snapshot {
+
+static void usage() {
+ std::cerr << "Usage:\n";
+ std::cerr << " create <orig-payload> <new-payload>\n";
+ std::cerr << "\n";
+ std::cerr << " Create a snapshot device containing the contents of\n";
+ std::cerr << " orig-payload, and then write the contents of new-payload.\n";
+ std::cerr << " The original files are not modified.\n";
+ std::cerr << "\n";
+ std::cerr << " merge <fail-rate>\n";
+ std::cerr << "\n";
+ std::cerr << " Merge the snapshot previously started by create, and wait\n";
+ std::cerr << " for it to complete. Once done, it is compared to the\n";
+ std::cerr << " new-payload for consistency. The original files are not \n";
+ std::cerr << " modified. If a fail-rate is passed (as a fraction between 0\n";
+ std::cerr << " and 100), every 10ms the device has that percent change of\n";
+ std::cerr << " injecting a kernel crash.\n";
+ std::cerr << "\n";
+ std::cerr << " check <new-payload>\n";
+ std::cerr << " Verify that all artifacts are correct after a merge\n";
+ std::cerr << " completes.\n";
+ std::cerr << "\n";
+ std::cerr << " cleanup\n";
+ std::cerr << " Remove all ImageManager artifacts from create/merge.\n";
+}
+
+class PowerTest final {
+ public:
+ PowerTest();
+ bool Run(int argc, char** argv);
+
+ private:
+ bool OpenImageManager();
+ bool Create(int argc, char** argv);
+ bool Merge(int argc, char** argv);
+ bool Check(int argc, char** argv);
+ bool Cleanup();
+ bool CleanupImage(const std::string& name);
+ bool SetupImages(const std::string& first_file, borrowed_fd second_fd);
+ bool MapImages();
+ bool MapSnapshot(SnapshotStorageMode mode);
+ bool GetMergeStatus(DmTargetSnapshot::Status* status);
+
+ static constexpr char kSnapshotName[] = "snapshot-power-test";
+ static constexpr char kSnapshotImageName[] = "snapshot-power-test-image";
+ static constexpr char kSnapshotCowName[] = "snapshot-power-test-cow";
+
+ DeviceMapper& dm_;
+ std::unique_ptr<ImageManager> images_;
+ std::string image_path_;
+ std::string cow_path_;
+ std::string snapshot_path_;
+};
+
+PowerTest::PowerTest() : dm_(DeviceMapper::Instance()) {}
+
+bool PowerTest::Run([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+ if (!OpenImageManager()) {
+ return false;
+ }
+
+ if (argc < 2) {
+ usage();
+ return false;
+ }
+ if (argv[1] == "create"s) {
+ return Create(argc, argv);
+ } else if (argv[1] == "merge"s) {
+ return Merge(argc, argv);
+ } else if (argv[1] == "check"s) {
+ return Check(argc, argv);
+ } else if (argv[1] == "cleanup"s) {
+ return Cleanup();
+ } else {
+ usage();
+ return false;
+ }
+}
+
+bool PowerTest::OpenImageManager() {
+ std::vector<std::string> dirs = {
+ "/data/gsi/test",
+ "/metadata/gsi/test",
+ };
+ for (const auto& dir : dirs) {
+ if (mkdir(dir.c_str(), 0700) && errno != EEXIST) {
+ std::cerr << "mkdir " << dir << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ }
+
+ images_ = ImageManager::Open("/metadata/gsi/test", "/data/gsi/test");
+ if (!images_) {
+ std::cerr << "Could not open ImageManager\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::Create(int argc, char** argv) {
+ if (argc < 4) {
+ usage();
+ return false;
+ }
+
+ std::string first = argv[2];
+ std::string second = argv[3];
+
+ unique_fd second_fd(open(second.c_str(), O_RDONLY));
+ if (second_fd < 0) {
+ std::cerr << "open " << second << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ if (!Cleanup()) {
+ return false;
+ }
+ if (!SetupImages(first, second_fd)) {
+ return false;
+ }
+ if (!MapSnapshot(SnapshotStorageMode::Persistent)) {
+ return false;
+ }
+
+ struct stat s;
+ if (fstat(second_fd, &s)) {
+ std::cerr << "fstat " << second << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ unique_fd snap_fd(open(snapshot_path_.c_str(), O_WRONLY));
+ if (snap_fd < 0) {
+ std::cerr << "open " << snapshot_path_ << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ uint8_t chunk[4096];
+ uint64_t written = 0;
+ while (written < s.st_size) {
+ uint64_t remaining = s.st_size - written;
+ size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
+ if (!android::base::ReadFully(second_fd, chunk, bytes)) {
+ std::cerr << "read " << second << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ if (!android::base::WriteFully(snap_fd, chunk, bytes)) {
+ std::cerr << "write " << snapshot_path_ << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ written += bytes;
+ }
+ if (fsync(snap_fd)) {
+ std::cerr << "fsync: " << strerror(errno) << "\n";
+ return false;
+ }
+
+ sync();
+
+ snap_fd = {};
+ if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
+ std::cerr << "could not delete dm device " << kSnapshotName << "\n";
+ return false;
+ }
+ if (!images_->UnmapImageIfExists(kSnapshotImageName)) {
+ std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
+ return false;
+ }
+ if (!images_->UnmapImageIfExists(kSnapshotCowName)) {
+ std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::Cleanup() {
+ if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
+ std::cerr << "could not delete dm device " << kSnapshotName << "\n";
+ return false;
+ }
+ if (!CleanupImage(kSnapshotImageName) || !CleanupImage(kSnapshotCowName)) {
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::CleanupImage(const std::string& name) {
+ if (!images_->UnmapImageIfExists(name)) {
+ std::cerr << "failed to unmap " << name << "\n";
+ return false;
+ }
+ if (images_->BackingImageExists(name) && !images_->DeleteBackingImage(name)) {
+ std::cerr << "failed to delete " << name << "\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::SetupImages(const std::string& first, borrowed_fd second_fd) {
+ unique_fd first_fd(open(first.c_str(), O_RDONLY));
+ if (first_fd < 0) {
+ std::cerr << "open " << first << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ struct stat s1, s2;
+ if (fstat(first_fd.get(), &s1)) {
+ std::cerr << "first stat: " << strerror(errno) << "\n";
+ return false;
+ }
+ if (fstat(second_fd.get(), &s2)) {
+ std::cerr << "second stat: " << strerror(errno) << "\n";
+ return false;
+ }
+
+ // Pick the bigger size of both images, rounding up to the nearest block.
+ uint64_t s1_size = (s1.st_size + 4095) & ~uint64_t(4095);
+ uint64_t s2_size = (s2.st_size + 4095) & ~uint64_t(4095);
+ uint64_t image_size = std::max(s1_size, s2_size) + (1024 * 1024 * 128);
+ if (!images_->CreateBackingImage(kSnapshotImageName, image_size, 0, nullptr)) {
+ std::cerr << "failed to create " << kSnapshotImageName << "\n";
+ return false;
+ }
+ // Use the same size for the cow.
+ if (!images_->CreateBackingImage(kSnapshotCowName, image_size, 0, nullptr)) {
+ std::cerr << "failed to create " << kSnapshotCowName << "\n";
+ return false;
+ }
+ if (!MapImages()) {
+ return false;
+ }
+
+ unique_fd image_fd(open(image_path_.c_str(), O_WRONLY));
+ if (image_fd < 0) {
+ std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ uint8_t chunk[4096];
+ uint64_t written = 0;
+ while (written < s1.st_size) {
+ uint64_t remaining = s1.st_size - written;
+ size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
+ if (!android::base::ReadFully(first_fd, chunk, bytes)) {
+ std::cerr << "read: " << strerror(errno) << "\n";
+ return false;
+ }
+ if (!android::base::WriteFully(image_fd, chunk, bytes)) {
+ std::cerr << "write: " << strerror(errno) << "\n";
+ return false;
+ }
+ written += bytes;
+ }
+ if (fsync(image_fd)) {
+ std::cerr << "fsync: " << strerror(errno) << "\n";
+ return false;
+ }
+
+ // Zero the first block of the COW.
+ unique_fd cow_fd(open(cow_path_.c_str(), O_WRONLY));
+ if (cow_fd < 0) {
+ std::cerr << "open: " << cow_path_ << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ memset(chunk, 0, sizeof(chunk));
+ if (!android::base::WriteFully(cow_fd, chunk, sizeof(chunk))) {
+ std::cerr << "read: " << strerror(errno) << "\n";
+ return false;
+ }
+ if (fsync(cow_fd)) {
+ std::cerr << "fsync: " << strerror(errno) << "\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::MapImages() {
+ if (!images_->MapImageDevice(kSnapshotImageName, 10s, &image_path_)) {
+ std::cerr << "failed to map " << kSnapshotImageName << "\n";
+ return false;
+ }
+ if (!images_->MapImageDevice(kSnapshotCowName, 10s, &cow_path_)) {
+ std::cerr << "failed to map " << kSnapshotCowName << "\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::MapSnapshot(SnapshotStorageMode mode) {
+ uint64_t sectors;
+ {
+ unique_fd fd(open(image_path_.c_str(), O_RDONLY));
+ if (fd < 0) {
+ std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ sectors = get_block_device_size(fd) / 512;
+ }
+
+ DmTable table;
+ table.Emplace<DmTargetSnapshot>(0, sectors, image_path_, cow_path_, mode, 8);
+ if (!dm_.CreateDevice(kSnapshotName, table, &snapshot_path_, 10s)) {
+ std::cerr << "failed to create snapshot device\n";
+ return false;
+ }
+ return true;
+}
+
+bool PowerTest::GetMergeStatus(DmTargetSnapshot::Status* status) {
+ std::vector<DeviceMapper::TargetInfo> targets;
+ if (!dm_.GetTableStatus(kSnapshotName, &targets)) {
+ std::cerr << "failed to get merge status\n";
+ return false;
+ }
+ if (targets.size() != 1) {
+ std::cerr << "merge device has wrong number of targets\n";
+ return false;
+ }
+ if (!DmTargetSnapshot::ParseStatusText(targets[0].data, status)) {
+ std::cerr << "could not parse merge target status text\n";
+ return false;
+ }
+ return true;
+}
+
+static std::string GetUserdataBlockDeviceName() {
+ Fstab fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ return {};
+ }
+
+ auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
+ if (!entry) {
+ return {};
+ }
+
+ auto prefix = "/dev/block/"s;
+ if (!android::base::StartsWith(entry->blk_device, prefix)) {
+ return {};
+ }
+ return entry->blk_device.substr(prefix.size());
+}
+
+bool PowerTest::Merge(int argc, char** argv) {
+ // Start an f2fs GC to really stress things. :TODO: figure out data device
+ auto userdata_dev = GetUserdataBlockDeviceName();
+ if (userdata_dev.empty()) {
+ std::cerr << "could not locate userdata block device\n";
+ return false;
+ }
+
+ auto cmd =
+ android::base::StringPrintf("echo 1 > /sys/fs/f2fs/%s/gc_urgent", userdata_dev.c_str());
+ system(cmd.c_str());
+
+ if (dm_.GetState(kSnapshotName) == DmDeviceState::INVALID) {
+ if (!MapImages()) {
+ return false;
+ }
+ if (!MapSnapshot(SnapshotStorageMode::Merge)) {
+ return false;
+ }
+ }
+
+ std::random_device r;
+ std::default_random_engine re(r());
+ std::uniform_real_distribution<double> dist(0.0, 100.0);
+
+ std::optional<double> failure_rate;
+ if (argc >= 3) {
+ double d;
+ if (!android::base::ParseDouble(argv[2], &d)) {
+ std::cerr << "Could not parse failure rate as double: " << argv[2] << "\n";
+ return false;
+ }
+ failure_rate = d;
+ }
+
+ while (true) {
+ DmTargetSnapshot::Status status;
+ if (!GetMergeStatus(&status)) {
+ return false;
+ }
+ if (!status.error.empty()) {
+ std::cerr << "merge reported error: " << status.error << "\n";
+ return false;
+ }
+ if (status.sectors_allocated == status.metadata_sectors) {
+ break;
+ }
+
+ std::cerr << status.sectors_allocated << " / " << status.metadata_sectors << "\n";
+
+ if (failure_rate && *failure_rate >= dist(re)) {
+ system("echo 1 > /proc/sys/kernel/sysrq");
+ system("echo c > /proc/sysrq-trigger");
+ }
+
+ std::this_thread::sleep_for(10ms);
+ }
+
+ std::cout << "Merge completed.\n";
+ return true;
+}
+
+bool PowerTest::Check([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+ if (argc < 3) {
+ std::cerr << "Expected argument: <new-image-path>\n";
+ return false;
+ }
+ std::string md_path, image_path;
+ std::string canonical_path = argv[2];
+
+ if (!dm_.GetDmDevicePathByName(kSnapshotName, &md_path)) {
+ std::cerr << "could not get dm-path for merge device\n";
+ return false;
+ }
+ if (!images_->GetMappedImageDevice(kSnapshotImageName, &image_path)) {
+ std::cerr << "could not get image path\n";
+ return false;
+ }
+
+ unique_fd md_fd(open(md_path.c_str(), O_RDONLY));
+ if (md_fd < 0) {
+ std::cerr << "open: " << md_path << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ unique_fd image_fd(open(image_path.c_str(), O_RDONLY));
+ if (image_fd < 0) {
+ std::cerr << "open: " << image_path << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ unique_fd canonical_fd(open(canonical_path.c_str(), O_RDONLY));
+ if (canonical_fd < 0) {
+ std::cerr << "open: " << canonical_path << ": " << strerror(errno) << "\n";
+ return false;
+ }
+
+ struct stat s;
+ if (fstat(canonical_fd, &s)) {
+ std::cerr << "fstat: " << canonical_path << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ uint64_t canonical_size = s.st_size;
+ uint64_t md_size = get_block_device_size(md_fd);
+ uint64_t image_size = get_block_device_size(image_fd);
+ if (image_size != md_size) {
+ std::cerr << "image size does not match merge device size\n";
+ return false;
+ }
+ if (canonical_size > image_size) {
+ std::cerr << "canonical size " << canonical_size << " is greater than image size "
+ << image_size << "\n";
+ return false;
+ }
+
+ constexpr size_t kBlockSize = 4096;
+ uint8_t canonical_buffer[kBlockSize];
+ uint8_t image_buffer[kBlockSize];
+ uint8_t md_buffer[kBlockSize];
+
+ uint64_t remaining = canonical_size;
+ uint64_t blockno = 0;
+ while (remaining) {
+ size_t bytes = (size_t)std::min((uint64_t)kBlockSize, remaining);
+ if (!android::base::ReadFully(canonical_fd, canonical_buffer, bytes)) {
+ std::cerr << "read: " << canonical_buffer << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ if (!android::base::ReadFully(image_fd, image_buffer, bytes)) {
+ std::cerr << "read: " << image_buffer << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ if (!android::base::ReadFully(md_fd, md_buffer, bytes)) {
+ std::cerr << "read: " << md_buffer << ": " << strerror(errno) << "\n";
+ return false;
+ }
+ if (memcmp(canonical_buffer, image_buffer, bytes)) {
+ std::cerr << "canonical and image differ at block " << blockno << "\n";
+ return false;
+ }
+ if (memcmp(canonical_buffer, md_buffer, bytes)) {
+ std::cerr << "canonical and image differ at block " << blockno << "\n";
+ return false;
+ }
+
+ remaining -= bytes;
+ blockno++;
+ }
+
+ std::cout << "Images all match.\n";
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::snapshot::PowerTest test;
+
+ if (!test.Run(argc, argv)) {
+ std::cerr << "Unexpected error running test." << std::endl;
+ return 1;
+ }
+ fflush(stdout);
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/run_power_test.sh b/fs_mgr/libsnapshot/run_power_test.sh
new file mode 100755
index 0000000..dc03dc9
--- /dev/null
+++ b/fs_mgr/libsnapshot/run_power_test.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+set -e
+
+if [ -z "$FAIL_RATE" ]; then
+ FAIL_RATE=5.0
+fi
+if [ ! -z "$ANDROID_SERIAL" ]; then
+ DEVICE_ARGS=-s $ANDROID_SERIAL
+else
+ DEVICE_ARGS=
+fi
+
+TEST_BIN=/data/nativetest64/snapshot_power_test/snapshot_power_test
+
+while :
+do
+ adb $DEVICE_ARGS wait-for-device
+ adb $DEVICE_ARGS root
+ adb $DEVICE_ARGS shell rm $TEST_BIN
+ adb $DEVICE_ARGS sync data
+ set +e
+ output=$(adb $DEVICE_ARGS shell $TEST_BIN merge $FAIL_RATE 2>&1)
+ set -e
+ if [[ "$output" == *"Merge completed"* ]]; then
+ echo "Merge completed."
+ break
+ fi
+ if [[ "$output" == *"Unexpected error"* ]]; then
+ echo "Unexpected error."
+ exit 1
+ fi
+done
+
+adb $DEVICE_ARGS shell $TEST_BIN check $1
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index c9fa28e..03efd68 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -577,8 +577,16 @@
return false;
}
+ auto other_suffix = device_->GetOtherSlotSuffix();
+
auto& dm = DeviceMapper::Instance();
for (const auto& snapshot : snapshots) {
+ if (android::base::EndsWith(snapshot, other_suffix)) {
+ // Allow the merge to continue, but log this unexpected case.
+ LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
+ continue;
+ }
+
// The device has to be mapped, since everything should be merged at
// the same time. This is a fairly serious error. We could forcefully
// map everything here, but it should have been mapped during first-
@@ -1008,6 +1016,15 @@
}
void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
+ // It's not possible to remove update state in recovery, so write an
+ // indicator that cleanup is needed on reboot. If a factory data reset
+ // was requested, it doesn't matter, everything will get wiped anyway.
+ // To make testing easier we consider a /data wipe as cleaned up.
+ if (device_->IsRecovery() && !in_factory_data_reset_) {
+ WriteUpdateState(lock, UpdateState::MergeCompleted);
+ return;
+ }
+
RemoveAllUpdateState(lock);
}
@@ -2528,7 +2545,43 @@
}
return true;
};
- if (!ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback)) {
+
+ in_factory_data_reset_ = true;
+ bool ok = ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
+ in_factory_data_reset_ = false;
+
+ if (!ok) {
+ return false;
+ }
+
+ // Nothing should be depending on partitions now, so unmap them all.
+ if (!UnmapAllPartitions()) {
+ LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
+ }
+ return true;
+}
+
+bool SnapshotManager::FinishMergeInRecovery() {
+ if (!device_->IsRecovery()) {
+ LOG(ERROR) << "Data wipes are only allowed in recovery.";
+ return false;
+ }
+
+ auto mount = EnsureMetadataMounted();
+ if (!mount || !mount->HasDevice()) {
+ return false;
+ }
+
+ auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+ auto super_path = device_->GetSuperDevice(slot_number);
+ if (!CreateLogicalAndSnapshotPartitions(super_path)) {
+ LOG(ERROR) << "Unable to map partitions to complete merge.";
+ return false;
+ }
+
+ UpdateState state = ProcessUpdateState();
+ if (state != UpdateState::MergeCompleted) {
+ LOG(ERROR) << "Merge returned unexpected status: " << state;
return false;
}
@@ -2685,5 +2738,9 @@
return true;
}
+ISnapshotMergeStats* SnapshotManager::GetSnapshotMergeStatsInstance() {
+ return SnapshotMergeStats::GetInstance(*this);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
new file mode 100644
index 0000000..421154d
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -0,0 +1,215 @@
+// 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 <stddef.h>
+#include <stdint.h>
+#include <sysexits.h>
+
+#include <functional>
+#include <sstream>
+#include <tuple>
+
+#include <android-base/logging.h>
+#include <src/libfuzzer/libfuzzer_macro.h>
+#include <storage_literals/storage_literals.h>
+
+#include "fuzz_utils.h"
+#include "snapshot_fuzz_utils.h"
+
+using android::base::LogId;
+using android::base::LogSeverity;
+using android::base::SetLogger;
+using android::base::StderrLogger;
+using android::base::StdioLogger;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fuzz::CheckedCast;
+using android::snapshot::SnapshotFuzzData;
+using android::snapshot::SnapshotFuzzEnv;
+using chromeos_update_engine::DeltaArchiveManifest;
+using google::protobuf::RepeatedPtrField;
+
+// Avoid linking to libgsi since it needs disk I/O.
+namespace android::gsi {
+bool IsGsiRunning() {
+ LOG(FATAL) << "Called IsGsiRunning";
+ __builtin_unreachable();
+}
+std::string GetDsuSlot(const std::string& install_dir) {
+ LOG(FATAL) << "Called GetDsuSlot(" << install_dir << ")";
+ __builtin_unreachable();
+}
+} // namespace android::gsi
+
+namespace android::snapshot {
+
+const SnapshotFuzzData* current_data = nullptr;
+
+SnapshotFuzzEnv* GetSnapshotFuzzEnv();
+
+FUZZ_CLASS(ISnapshotManager, SnapshotManagerAction);
+
+using ProcessUpdateStateArgs = SnapshotManagerAction::Proto::ProcessUpdateStateArgs;
+using CreateLogicalAndSnapshotPartitionsArgs =
+ SnapshotManagerAction::Proto::CreateLogicalAndSnapshotPartitionsArgs;
+using RecoveryCreateSnapshotDevicesArgs =
+ SnapshotManagerAction::Proto::RecoveryCreateSnapshotDevicesArgs;
+
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, BeginUpdate);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, CancelUpdate);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, InitiateMerge);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, NeedSnapshotsInFirstStageMount);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, RecoveryCreateSnapshotDevices);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
+FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
+
+#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ...) \
+ FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, snapshot, ##__VA_ARGS__)
+
+SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool wipe) {
+ (void)snapshot->FinishedSnapshotWrites(wipe);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, const ProcessUpdateStateArgs& args) {
+ std::function<bool()> before_cancel;
+ if (args.has_before_cancel()) {
+ before_cancel = [&]() { return args.fail_before_cancel(); };
+ }
+ (void)snapshot->ProcessUpdateState({}, before_cancel);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, bool has_progress_arg) {
+ double progress;
+ (void)snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool has_callback) {
+ std::function<void()> callback;
+ if (has_callback) {
+ callback = []() {};
+ }
+ (void)snapshot->HandleImminentDataWipe(callback);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(Dump) {
+ std::stringstream ss;
+ (void)snapshot->Dump(ss);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, const DeltaArchiveManifest& manifest) {
+ (void)snapshot->CreateUpdateSnapshots(manifest);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, const std::string& name) {
+ (void)snapshot->UnmapUpdateSnapshot(name);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions,
+ const CreateLogicalAndSnapshotPartitionsArgs& args) {
+ const std::string* super;
+ if (args.use_correct_super()) {
+ super = &GetSnapshotFuzzEnv()->super();
+ } else {
+ super = &args.super();
+ }
+ (void)snapshot->CreateLogicalAndSnapshotPartitions(
+ *super, std::chrono::milliseconds(args.timeout_millis()));
+}
+
+SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata,
+ const RecoveryCreateSnapshotDevicesArgs& args) {
+ std::unique_ptr<AutoDevice> device;
+ if (args.has_metadata_device_object()) {
+ device = std::make_unique<DummyAutoDevice>(args.metadata_mounted());
+ }
+ (void)snapshot->RecoveryCreateSnapshotDevices(device);
+}
+
+SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, const CreateLogicalPartitionParamsProto& params_proto) {
+ auto partition_opener = std::make_unique<TestPartitionOpener>(GetSnapshotFuzzEnv()->super());
+ CreateLogicalPartitionParams params;
+ if (params_proto.use_correct_super()) {
+ params.block_device = GetSnapshotFuzzEnv()->super();
+ } else {
+ params.block_device = params_proto.block_device();
+ }
+ if (params_proto.has_metadata_slot()) {
+ params.metadata_slot = params_proto.metadata_slot();
+ }
+ params.partition_name = params_proto.partition_name();
+ params.force_writable = params_proto.force_writable();
+ params.timeout_ms = std::chrono::milliseconds(params_proto.timeout_millis());
+ params.device_name = params_proto.device_name();
+ params.partition_opener = partition_opener.get();
+ std::string path;
+ (void)snapshot->MapUpdateSnapshot(params, &path);
+}
+
+// During global init, log all messages to stdio. This is only done once.
+int AllowLoggingDuringGlobalInit() {
+ SetLogger(&StdioLogger);
+ return 0;
+}
+
+// Only log fatal messages during tests.
+void FatalOnlyLogger(LogId logid, LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ if (severity == LogSeverity::FATAL) {
+ StderrLogger(logid, severity, tag, file, line, message);
+
+ // If test fails by a LOG(FATAL) or CHECK(), log the corpus. If it abort()'s, there's
+ // nothing else we can do.
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__,
+ "Attempting to dump current corpus:");
+ if (current_data == nullptr) {
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__, "Current corpus is nullptr.");
+ } else {
+ std::string content;
+ if (!google::protobuf::TextFormat::PrintToString(*current_data, &content)) {
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__,
+ "Failed to print corpus to string.");
+ } else {
+ StderrLogger(logid, severity, tag, __FILE__, __LINE__, content.c_str());
+ }
+ }
+ }
+}
+// Stop logging (except fatal messages) after global initialization. This is only done once.
+int StopLoggingAfterGlobalInit() {
+ [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silincer;
+ SetLogger(&FatalOnlyLogger);
+ return 0;
+}
+
+SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
+ [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
+ static SnapshotFuzzEnv env;
+ [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
+ return &env;
+}
+
+} // namespace android::snapshot
+
+DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
+ using namespace android::snapshot;
+
+ current_data = &snapshot_fuzz_data;
+
+ auto env = GetSnapshotFuzzEnv();
+ env->CheckSoftReset();
+
+ auto snapshot_manager = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
+ CHECK(snapshot_manager);
+
+ SnapshotManagerAction::ExecuteAll(snapshot_manager.get(), snapshot_fuzz_data.actions());
+}
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
new file mode 100644
index 0000000..8101d03
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -0,0 +1,314 @@
+// 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 <ftw.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sysexits.h>
+
+#include <chrono>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/snapshot.h>
+#include <storage_literals/storage_literals.h>
+
+#include "snapshot_fuzz_utils.h"
+#include "utility.h"
+
+// Prepends the errno string, but it is good enough.
+#ifndef PCHECK
+#define PCHECK(x) CHECK(x) << strerror(errno) << ": "
+#endif
+
+using namespace android::storage_literals;
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+using android::dm::LoopControl;
+using android::fiemap::IImageManager;
+using android::fiemap::ImageManager;
+using android::fs_mgr::BlockDeviceInfo;
+using android::fs_mgr::IPartitionOpener;
+using chromeos_update_engine::DynamicPartitionMetadata;
+
+// This directory is exempted from pinning in ImageManager.
+static const char MNT_DIR[] = "/data/gsi/ota/test/";
+
+static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
+static const auto SUPER_IMAGE_SIZE = 16_MiB;
+static const auto FAKE_ROOT_SIZE = 64_MiB;
+
+namespace android::snapshot {
+
+bool Mkdir(const std::string& path) {
+ if (mkdir(path.c_str(), 0750) == -1 && errno != EEXIST) {
+ PLOG(ERROR) << "Cannot create " << path;
+ return false;
+ }
+ return true;
+}
+
+bool RmdirRecursive(const std::string& path) {
+ auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
+ switch (file_type) {
+ case FTW_D:
+ case FTW_DP:
+ case FTW_DNR:
+ if (rmdir(child) == -1) {
+ PLOG(ERROR) << "rmdir " << child;
+ return -1;
+ }
+ return 0;
+ case FTW_NS:
+ default:
+ if (rmdir(child) != -1) break;
+ [[fallthrough]];
+ case FTW_F:
+ case FTW_SL:
+ case FTW_SLN:
+ if (unlink(child) == -1) {
+ PLOG(ERROR) << "unlink " << child;
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+ };
+
+ return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0;
+}
+
+class AutoDeleteDir : public AutoDevice {
+ public:
+ static std::unique_ptr<AutoDeleteDir> New(const std::string& path) {
+ if (!Mkdir(path)) {
+ return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(""));
+ }
+ return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(path));
+ }
+ ~AutoDeleteDir() {
+ if (!HasDevice()) return;
+ if (rmdir(name_.c_str()) == -1) {
+ PLOG(ERROR) << "Cannot remove " << name_;
+ }
+ }
+
+ private:
+ AutoDeleteDir(const std::string& path) : AutoDevice(path) {}
+};
+
+class AutoUnmount : public AutoDevice {
+ public:
+ static std::unique_ptr<AutoUnmount> New(const std::string& path, uint64_t size) {
+ if (mount("tmpfs", path.c_str(), "tmpfs", 0,
+ (void*)StringPrintf("size=%" PRIu64, size).data()) == -1) {
+ PLOG(ERROR) << "Cannot mount " << path;
+ return std::unique_ptr<AutoUnmount>(new AutoUnmount(""));
+ }
+ return std::unique_ptr<AutoUnmount>(new AutoUnmount(path));
+ }
+ ~AutoUnmount() {
+ if (!HasDevice()) return;
+ if (umount(name_.c_str()) == -1) {
+ PLOG(ERROR) << "Cannot umount " << name_;
+ }
+ }
+
+ private:
+ AutoUnmount(const std::string& path) : AutoDevice(path) {}
+};
+
+// A directory on tmpfs. Upon destruct, it is unmounted and deleted.
+class AutoMemBasedDir : public AutoDevice {
+ public:
+ static std::unique_ptr<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
+ if (!Mkdir(MNT_DIR)) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
+ ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
+ if (!ret->auto_delete_mount_dir_->HasDevice()) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ ret->auto_umount_mount_point_ = AutoUnmount::New(ret->mount_path(), size);
+ if (!ret->auto_umount_mount_point_->HasDevice()) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ // tmp_path() and persist_path does not need to be deleted upon destruction, hence it is
+ // not wrapped with AutoDeleteDir.
+ if (!Mkdir(ret->tmp_path())) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ if (!Mkdir(ret->persist_path())) {
+ return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+ }
+ return ret;
+ }
+ // Return the temporary scratch directory.
+ std::string tmp_path() const {
+ CHECK(HasDevice());
+ return mount_path() + "/tmp";
+ }
+ // Return the temporary scratch directory.
+ std::string persist_path() const {
+ CHECK(HasDevice());
+ return mount_path() + "/persist";
+ }
+ // Delete all contents in tmp_path() and start over. tmp_path() itself is re-created.
+ void CheckSoftReset() {
+ PCHECK(RmdirRecursive(tmp_path()));
+ PCHECK(Mkdir(tmp_path()));
+ }
+
+ private:
+ AutoMemBasedDir(const std::string& name) : AutoDevice(name) {}
+ std::string mount_path() const {
+ CHECK(HasDevice());
+ return MNT_DIR + "/"s + name_;
+ }
+ std::unique_ptr<AutoDeleteDir> auto_delete_mount_dir_;
+ std::unique_ptr<AutoUnmount> auto_umount_mount_point_;
+};
+
+SnapshotFuzzEnv::SnapshotFuzzEnv() {
+ fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
+ CHECK(fake_root_ != nullptr);
+ CHECK(fake_root_->HasDevice());
+ loop_control_ = std::make_unique<LoopControl>();
+ mapped_super_ = CheckMapSuper(fake_root_->persist_path(), loop_control_.get(), &fake_super_);
+}
+
+SnapshotFuzzEnv::~SnapshotFuzzEnv() = default;
+
+void CheckZeroFill(const std::string& file, size_t size) {
+ std::string zeros(size, '\0');
+ PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file;
+}
+
+void SnapshotFuzzEnv::CheckSoftReset() {
+ fake_root_->CheckSoftReset();
+ CheckZeroFill(super(), SUPER_IMAGE_SIZE);
+}
+
+std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager(
+ const std::string& path) {
+ auto images_dir = path + "/images";
+ auto metadata_dir = images_dir + "/metadata";
+ auto data_dir = images_dir + "/data";
+
+ PCHECK(Mkdir(images_dir));
+ PCHECK(Mkdir(metadata_dir));
+ PCHECK(Mkdir(data_dir));
+ return ImageManager::Open(metadata_dir, data_dir);
+}
+
+// Helper to create a loop device for a file.
+static void CheckCreateLoopDevice(LoopControl* control, const std::string& file,
+ const std::chrono::milliseconds& timeout_ms, std::string* path) {
+ static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
+ android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
+ PCHECK(file_fd >= 0) << "Could not open file: " << file;
+ CHECK(control->Attach(file_fd, timeout_ms, path))
+ << "Could not create loop device for: " << file;
+}
+
+class AutoDetachLoopDevice : public AutoDevice {
+ public:
+ AutoDetachLoopDevice(LoopControl* control, const std::string& device)
+ : AutoDevice(device), control_(control) {}
+ ~AutoDetachLoopDevice() { control_->Detach(name_); }
+
+ private:
+ LoopControl* control_;
+};
+
+std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapSuper(const std::string& fake_persist_path,
+ LoopControl* control,
+ std::string* fake_super) {
+ auto super_img = fake_persist_path + "/super.img";
+ CheckZeroFill(super_img, SUPER_IMAGE_SIZE);
+ CheckCreateLoopDevice(control, super_img, 1s, fake_super);
+
+ return std::make_unique<AutoDetachLoopDevice>(control, *fake_super);
+}
+
+std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CheckCreateSnapshotManager(
+ const SnapshotFuzzData& data) {
+ auto partition_opener = std::make_unique<TestPartitionOpener>(super());
+ CheckWriteSuperMetadata(data, *partition_opener);
+ auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
+ PCHECK(Mkdir(metadata_dir));
+
+ auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data(),
+ std::move(partition_opener), metadata_dir);
+ auto snapshot = SnapshotManager::New(device_info /* takes ownership */);
+ snapshot->images_ = CheckCreateFakeImageManager(fake_root_->tmp_path());
+ snapshot->has_local_image_manager_ = data.manager_data().is_local_image_manager();
+
+ return snapshot;
+}
+
+const std::string& SnapshotFuzzEnv::super() const {
+ return fake_super_;
+}
+
+void SnapshotFuzzEnv::CheckWriteSuperMetadata(const SnapshotFuzzData& data,
+ const IPartitionOpener& opener) {
+ if (!data.is_super_metadata_valid()) {
+ // Leave it zero.
+ return;
+ }
+
+ BlockDeviceInfo super_device("super", SUPER_IMAGE_SIZE, 0, 0, 4096);
+ std::vector<BlockDeviceInfo> devices = {super_device};
+ auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
+ CHECK(builder != nullptr);
+
+ // Attempt to create a super partition metadata using proto. All errors are ignored.
+ for (const auto& group_proto : data.super_data().dynamic_partition_metadata().groups()) {
+ (void)builder->AddGroup(group_proto.name(), group_proto.size());
+ for (const auto& partition_name : group_proto.partition_names()) {
+ (void)builder->AddPartition(partition_name, group_proto.name(),
+ LP_PARTITION_ATTR_READONLY);
+ }
+ }
+
+ for (const auto& partition_proto : data.super_data().partitions()) {
+ auto p = builder->FindPartition(partition_proto.partition_name());
+ if (p == nullptr) continue;
+ (void)builder->ResizePartition(p, partition_proto.new_partition_info().size());
+ }
+
+ auto metadata = builder->Export();
+ // metadata may be nullptr if it is not valid (e.g. partition name too long).
+ // In this case, just use empty super partition data.
+ if (metadata == nullptr) {
+ builder = MetadataBuilder::New(devices, "super", 65536, 2);
+ CHECK(builder != nullptr);
+ metadata = builder->Export();
+ CHECK(metadata != nullptr);
+ }
+ CHECK(FlashPartitionTable(opener, super(), *metadata.get()));
+}
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
new file mode 100644
index 0000000..5533def
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -0,0 +1,119 @@
+// 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 <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android/snapshot/snapshot_fuzz.pb.h>
+#include <libdm/loop_control.h>
+#include <libfiemap/image_manager.h>
+#include <liblp/liblp.h>
+#include <libsnapshot/auto_device.h>
+#include <libsnapshot/test_helpers.h>
+
+// libsnapshot-specific code for fuzzing. Defines fake classes that are depended
+// by SnapshotManager.
+
+#include "android/snapshot/snapshot_fuzz.pb.h"
+
+namespace android::snapshot {
+
+class AutoMemBasedDir;
+
+class DummyAutoDevice : public AutoDevice {
+ public:
+ DummyAutoDevice(bool mounted) : AutoDevice(mounted ? "dummy" : "") {}
+};
+
+// Prepare test environment. This has a heavy overhead and should be done once.
+class SnapshotFuzzEnv {
+ public:
+ // Check if test should run at all.
+ static bool ShouldSkipTest();
+
+ // Initialize the environment.
+ SnapshotFuzzEnv();
+ ~SnapshotFuzzEnv();
+
+ // Soft reset part of the environment before running the next test.
+ // Abort if fails.
+ void CheckSoftReset();
+
+ // Create a snapshot manager for this test run.
+ // Client is responsible for maintaining the lifetime of |data| over the life time of
+ // ISnapshotManager.
+ std::unique_ptr<ISnapshotManager> CheckCreateSnapshotManager(const SnapshotFuzzData& data);
+
+ // Return path to super partition.
+ const std::string& super() const;
+
+ private:
+ std::unique_ptr<AutoMemBasedDir> fake_root_;
+ std::unique_ptr<android::dm::LoopControl> loop_control_;
+ std::unique_ptr<AutoDevice> mapped_super_;
+ std::string fake_super_;
+
+ static std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager(
+ const std::string& fake_tmp_path);
+ static std::unique_ptr<AutoDevice> CheckMapSuper(const std::string& fake_persist_path,
+ android::dm::LoopControl* control,
+ std::string* fake_super);
+
+ void CheckWriteSuperMetadata(const SnapshotFuzzData& proto,
+ const android::fs_mgr::IPartitionOpener& opener);
+};
+
+class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
+ public:
+ // Client is responsible for maintaining the lifetime of |data|.
+ SnapshotFuzzDeviceInfo(const FuzzDeviceInfoData& data,
+ std::unique_ptr<TestPartitionOpener>&& partition_opener,
+ const std::string& metadata_dir)
+ : data_(&data),
+ partition_opener_(std::move(partition_opener)),
+ metadata_dir_(metadata_dir) {}
+
+ // Following APIs are mocked.
+ std::string GetGsidDir() const override { return "fuzz_ota"; }
+ std::string GetMetadataDir() const override { return metadata_dir_; }
+ std::string GetSuperDevice(uint32_t) const override {
+ // TestPartitionOpener can recognize this.
+ return "super";
+ }
+ const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
+ return *partition_opener_;
+ }
+
+ // Following APIs are fuzzed.
+ std::string GetSlotSuffix() const override { return data_->slot_suffix_is_a() ? "_a" : "_b"; }
+ std::string GetOtherSlotSuffix() const override {
+ return data_->slot_suffix_is_a() ? "_b" : "_a";
+ }
+ bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); }
+ bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
+ return data_->allow_set_boot_control_merge_status();
+ }
+ bool SetSlotAsUnbootable(unsigned int) override {
+ return data_->allow_set_slot_as_unbootable();
+ }
+ bool IsRecovery() const override { return data_->is_recovery(); }
+
+ private:
+ const FuzzDeviceInfoData* data_;
+ std::unique_ptr<TestPartitionOpener> partition_opener_;
+ std::string metadata_dir_;
+};
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
new file mode 100644
index 0000000..2aaa78c
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -0,0 +1,130 @@
+// 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 <libsnapshot/snapshot_stub.h>
+
+#include <android-base/logging.h>
+
+#include <libsnapshot/snapshot_stats.h>
+
+using android::fs_mgr::CreateLogicalPartitionParams;
+using chromeos_update_engine::DeltaArchiveManifest;
+
+namespace android::snapshot {
+
+std::unique_ptr<ISnapshotManager> SnapshotManagerStub::New() {
+ return std::make_unique<SnapshotManagerStub>();
+}
+
+bool SnapshotManagerStub::BeginUpdate() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::CancelUpdate() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::FinishedSnapshotWrites(bool) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::InitiateMerge() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+UpdateState SnapshotManagerStub::ProcessUpdateState(const std::function<bool()>&,
+ const std::function<bool()>&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return UpdateState::None;
+}
+
+UpdateState SnapshotManagerStub::GetUpdateState(double*) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return UpdateState::None;
+}
+
+Return SnapshotManagerStub::CreateUpdateSnapshots(const DeltaArchiveManifest&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return Return::Error();
+}
+
+bool SnapshotManagerStub::MapUpdateSnapshot(const CreateLogicalPartitionParams&, std::string*) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::UnmapUpdateSnapshot(const std::string&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::NeedSnapshotsInFirstStageMount() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::CreateLogicalAndSnapshotPartitions(const std::string&,
+ const std::chrono::milliseconds&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::HandleImminentDataWipe(const std::function<void()>&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::FinishMergeInRecovery() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return CreateResult::ERROR;
+}
+
+CreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices(
+ const std::unique_ptr<AutoDevice>&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return CreateResult::ERROR;
+}
+
+bool SnapshotManagerStub::Dump(std::ostream&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+std::unique_ptr<AutoDevice> SnapshotManagerStub::EnsureMetadataMounted() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return nullptr;
+}
+
+class SnapshotMergeStatsStub : public ISnapshotMergeStats {
+ bool Start() override { return false; }
+ void set_state(android::snapshot::UpdateState) override {}
+ std::unique_ptr<Result> Finish() override { return nullptr; }
+};
+
+ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
+ static SnapshotMergeStatsStub snapshot_merge_stats;
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return &snapshot_merge_stats;
+}
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index f82c082..2bd0135 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -41,6 +41,11 @@
#include <libsnapshot/test_helpers.h>
#include "utility.h"
+// Mock classes are not used. Header included to ensure mocked class definition aligns with the
+// class itself.
+#include <libsnapshot/mock_device_info.h>
+#include <libsnapshot/mock_snapshot.h>
+
namespace android {
namespace snapshot {
@@ -1454,6 +1459,52 @@
ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
}
+// Test that a merge does not clear the snapshot state in fastboot.
+TEST_F(SnapshotUpdateTest, MergeInFastboot) {
+ // Execute the first update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(MapUpdateSnapshots());
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ init = nullptr;
+
+ // Initiate the merge and then immediately stop it to simulate a reboot.
+ auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+ ASSERT_TRUE(new_sm->InitiateMerge());
+ ASSERT_TRUE(UnmapAll());
+
+ // Simulate a reboot into recovery.
+ auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ test_device->set_recovery(true);
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+
+ ASSERT_TRUE(new_sm->FinishMergeInRecovery());
+
+ auto mount = new_sm->EnsureMetadataMounted();
+ ASSERT_TRUE(mount && mount->HasDevice());
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+
+ // Finish the merge in a normal boot.
+ test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ init = SnapshotManager::NewForFirstStageMount(test_device.release());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ init = nullptr;
+
+ test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
+ new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
+ ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
+}
+
// Test that after an OTA, before a merge, we can wipe data in recovery.
TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
// Execute the first update.
@@ -1753,7 +1804,6 @@
protected:
void SetUp() override {
if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
- GTEST_SKIP() << "WIP failure b/149738928";
SnapshotTest::SetUp();
userdata_ = std::make_unique<LowSpaceUserdata>();
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index b036606..f82a602 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -212,8 +212,8 @@
return AssertionFailure() << strerror(errno);
}
bsize_ = buf.f_bsize;
- free_space_ = buf.f_bsize * buf.f_bfree;
- available_space_ = buf.f_bsize * buf.f_bavail;
+ free_space_ = bsize_ * buf.f_bfree;
+ available_space_ = bsize_ * buf.f_bavail;
return AssertionSuccess();
}
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
new file mode 100644
index 0000000..be5e1fe
--- /dev/null
+++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
@@ -0,0 +1,75 @@
+//
+// 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.
+//
+
+// A subset of system/update_engine/update_metadata.proto. A separate file is
+// used here because:
+// - The original file is optimized for LITE_RUNTIME, but fuzzing needs
+// reflection.
+// - The definition here has less fields. libsnapshot only uses fields declared
+// here, and all fields declared here are fuzzed by libsnapshot_fuzzer. If
+// libsnapshot uses more fields in system/update_engine/update_metadata.proto
+// in the future, they must be added here too, otherwise it will fail to
+// compile.
+//
+// It is okay that this file is older than
+// system/update_engine/update_metadata.proto as long as the messages defined
+// here can also be parsed by protobuf defined there. However, it is not
+// okay to add fields here without adding them to
+// system/update_engine/update_metadata.proto. Doing so will cause a compiler
+// error when libsnapshot code starts to use these dangling fields.
+
+syntax = "proto2";
+
+package chromeos_update_engine;
+
+message Extent {
+ optional uint64 start_block = 1;
+ optional uint64 num_blocks = 2;
+}
+
+message PartitionInfo {
+ optional uint64 size = 1;
+}
+
+message InstallOperation {
+ enum Type { SOURCE_COPY = 4; }
+ required Type type = 1;
+ repeated Extent src_extents = 4;
+ repeated Extent dst_extents = 6;
+}
+
+message PartitionUpdate {
+ required string partition_name = 1;
+ optional PartitionInfo new_partition_info = 7;
+ repeated InstallOperation operations = 8;
+ optional Extent hash_tree_extent = 11;
+ optional Extent fec_extent = 15;
+}
+
+message DynamicPartitionGroup {
+ required string name = 1;
+ optional uint64 size = 2;
+ repeated string partition_names = 3;
+}
+
+message DynamicPartitionMetadata {
+ repeated DynamicPartitionGroup groups = 1;
+}
+
+message DeltaArchiveManifest {
+ repeated PartitionUpdate partitions = 13;
+ optional DynamicPartitionMetadata dynamic_partition_metadata = 15;
+}
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
index 0ff8995..de835b3 100644
--- a/fs_mgr/tests/AndroidTest.xml
+++ b/fs_mgr/tests/AndroidTest.xml
@@ -21,6 +21,9 @@
<option name="push" value="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
<option name="append-bitness" value="true" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="throw-on-error" value="false" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="CtsFsMgrTestCases" />
diff --git a/init/Android.bp b/init/Android.bp
index 1b3aa18..edf9099 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -41,6 +41,7 @@
"builtins.cpp",
"devices.cpp",
"firmware_handler.cpp",
+ "first_stage_console.cpp",
"first_stage_init.cpp",
"first_stage_mount.cpp",
"fscrypt_init_extensions.cpp",
@@ -130,6 +131,7 @@
"libpropertyinfoparser",
"libsnapshot_init",
"lib_apex_manifest_proto_lite",
+ "update_metadata-protos",
],
shared_libs: [
"libbacktrace",
diff --git a/init/Android.mk b/init/Android.mk
index b49fb3b..da94daf 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -50,6 +50,7 @@
LOCAL_SRC_FILES := \
block_dev_initializer.cpp \
devices.cpp \
+ first_stage_console.cpp \
first_stage_init.cpp \
first_stage_main.cpp \
first_stage_mount.cpp \
@@ -112,6 +113,7 @@
libext2_uuid \
libprotobuf-cpp-lite \
libsnapshot_init \
+ update_metadata-protos \
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 920dc6c..17f509a 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -24,6 +24,9 @@
<option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
<option name="append-bitness" value="true" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="throw-on-error" value="false" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="CtsInitTestCases" />
diff --git a/init/README.md b/init/README.md
index 13f1bac..726c0cc 100644
--- a/init/README.md
+++ b/init/README.md
@@ -322,6 +322,10 @@
This is mutually exclusive with the console option, which additionally connects stdin to the
given console.
+`task_profiles <profile> [ <profile>\* ]`
+> Set task profiles for the process when it forks. This is designed to replace the use of
+ writepid option for moving a process into a cgroup.
+
`timeout_period <seconds>`
> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
here, so oneshot services do not automatically restart, however all other services will.
@@ -356,6 +360,8 @@
cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
'/foreground'), then the pid is written to file /dev/cpuset/_cpuset\_name_/tasks.
+ The use of this option for moving a process into a cgroup is obsolete. Please
+ use task_profiles option instead.
Triggers
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
new file mode 100644
index 0000000..cfa0d99
--- /dev/null
+++ b/init/first_stage_console.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 "first_stage_console.h"
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+static void RunScript() {
+ LOG(INFO) << "Attempting to run /first_stage.sh...";
+ pid_t pid = fork();
+ if (pid != 0) {
+ int status;
+ waitpid(pid, &status, 0);
+ LOG(INFO) << "/first_stage.sh exited with status " << status;
+ return;
+ }
+ const char* path = "/system/bin/sh";
+ const char* args[] = {path, "/first_stage.sh", nullptr};
+ int rv = execv(path, const_cast<char**>(args));
+ LOG(ERROR) << "unable to execv /first_stage.sh, returned " << rv << " errno " << errno;
+}
+
+namespace android {
+namespace init {
+
+void StartConsole() {
+ if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
+ PLOG(ERROR) << "unable to create /dev/console";
+ return;
+ }
+ pid_t pid = fork();
+ if (pid != 0) {
+ int status;
+ waitpid(pid, &status, 0);
+ LOG(ERROR) << "console shell exited with status " << status;
+ return;
+ }
+ int fd = -1;
+ int tries = 50; // should timeout after 5s
+ // The device driver for console may not be ready yet so retry for a while in case of failure.
+ while (tries--) {
+ fd = open("/dev/console", O_RDWR);
+ if (fd != -1) {
+ break;
+ }
+ std::this_thread::sleep_for(100ms);
+ }
+ if (fd == -1) {
+ LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
+ _exit(127);
+ }
+ ioctl(fd, TIOCSCTTY, 0);
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+
+ RunScript();
+ const char* path = "/system/bin/sh";
+ const char* args[] = {path, nullptr};
+ int rv = execv(path, const_cast<char**>(args));
+ LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
+ _exit(127);
+}
+
+int FirstStageConsole(const std::string& cmdline) {
+ auto pos = cmdline.find("androidboot.first_stage_console=");
+ if (pos != std::string::npos) {
+ int val = 0;
+ if (sscanf(cmdline.c_str() + pos, "androidboot.first_stage_console=%d", &val) != 1) {
+ return FirstStageConsoleParam::DISABLED;
+ }
+ if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
+ return val;
+ }
+ }
+ return FirstStageConsoleParam::DISABLED;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
new file mode 100644
index 0000000..8f36a7c
--- /dev/null
+++ b/init/first_stage_console.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 <string>
+
+namespace android {
+namespace init {
+
+enum FirstStageConsoleParam {
+ DISABLED = 0,
+ CONSOLE_ON_FAILURE = 1,
+ IGNORE_FAILURE = 2,
+ MAX_PARAM_VALUE = IGNORE_FAILURE,
+};
+
+void StartConsole();
+int FirstStageConsole(const std::string& cmdline);
+
+} // namespace init
+} // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index ef8ffbe..1a608f6 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -24,12 +24,10 @@
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
-#include <sys/wait.h>
#include <unistd.h>
#include <filesystem>
#include <string>
-#include <thread>
#include <vector>
#include <android-base/chrono_utils.h>
@@ -39,6 +37,7 @@
#include <private/android_filesystem_config.h>
#include "debug_ramdisk.h"
+#include "first_stage_console.h"
#include "first_stage_mount.h"
#include "reboot_utils.h"
#include "switch_root.h"
@@ -94,49 +93,6 @@
}
}
-void StartConsole() {
- if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
- PLOG(ERROR) << "unable to create /dev/console";
- return;
- }
- pid_t pid = fork();
- if (pid != 0) {
- int status;
- waitpid(pid, &status, 0);
- LOG(ERROR) << "console shell exited with status " << status;
- return;
- }
- int fd = -1;
- int tries = 10;
- // The device driver for console may not be ready yet so retry for a while in case of failure.
- while (tries--) {
- fd = open("/dev/console", O_RDWR);
- if (fd != -1) {
- break;
- }
- std::this_thread::sleep_for(100ms);
- }
- if (fd == -1) {
- LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
- _exit(127);
- }
- ioctl(fd, TIOCSCTTY, 0);
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- close(fd);
-
- const char* path = "/system/bin/sh";
- const char* args[] = {path, nullptr};
- int rv = execv(path, const_cast<char**>(args));
- LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
- _exit(127);
-}
-
-bool FirstStageConsole(const std::string& cmdline) {
- return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
-}
-
bool ForceNormalBoot(const std::string& cmdline) {
return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
}
@@ -244,16 +200,16 @@
}
Modprobe m({"/lib/modules"}, module_load_file);
- auto want_console = ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline);
+ auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
if (!m.LoadListedModules(!want_console)) {
- if (want_console) {
+ if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {
LOG(FATAL) << "Failed to load kernel modules";
}
}
- if (want_console) {
+ if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
StartConsole();
}
diff --git a/init/init.cpp b/init/init.cpp
index a9d6301..3f8f628 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -76,6 +76,7 @@
#include "service.h"
#include "service_parser.h"
#include "sigchld_handler.h"
+#include "subcontext.h"
#include "system/core/init/property_service.pb.h"
#include "util.h"
@@ -100,8 +101,6 @@
static int signal_fd = -1;
static int property_fd = -1;
-static std::unique_ptr<Subcontext> subcontext;
-
struct PendingControlMessage {
std::string message;
std::string name;
@@ -216,16 +215,6 @@
prop_waiter_state.ResetWaitForProp();
}
-static void UnwindMainThreadStack() {
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
- if (!backtrace->Unwind(0)) {
- LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
- }
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- LOG(ERROR) << backtrace->FormatFrameData(i);
- }
-}
-
static class ShutdownState {
public:
void TriggerShutdown(const std::string& command) {
@@ -243,13 +232,13 @@
std::optional<std::string> CheckShutdown() {
auto lock = std::lock_guard{shutdown_command_lock_};
if (do_shutdown_ && !IsShuttingDown()) {
- do_shutdown_ = false;
return shutdown_command_;
}
return {};
}
bool do_shutdown() const { return do_shutdown_; }
+ void set_do_shutdown(bool value) { do_shutdown_ = value; }
private:
std::mutex shutdown_command_lock_;
@@ -257,16 +246,28 @@
bool do_shutdown_ = false;
} shutdown_state;
+static void UnwindMainThreadStack() {
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
+ if (!backtrace->Unwind(0)) {
+ LOG(ERROR) << __FUNCTION__ << "sys.powerctl: Failed to unwind callstack.";
+ }
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ LOG(ERROR) << "sys.powerctl: " << backtrace->FormatFrameData(i);
+ }
+}
+
void DebugRebootLogging() {
- LOG(INFO) << "do_shutdown: " << shutdown_state.do_shutdown()
+ LOG(INFO) << "sys.powerctl: do_shutdown: " << shutdown_state.do_shutdown()
<< " IsShuttingDown: " << IsShuttingDown();
if (shutdown_state.do_shutdown()) {
LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
UnwindMainThreadStack();
+ DumpShutdownDebugInformation();
}
if (IsShuttingDown()) {
LOG(ERROR) << "sys.powerctl set while init is already shutting down";
UnwindMainThreadStack();
+ DumpShutdownDebugInformation();
}
}
@@ -279,9 +280,8 @@
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(
- &service_list, subcontext.get(), std::nullopt));
- parser.AddSectionParser("on",
- std::make_unique<ActionParser>(&action_manager, subcontext.get()));
+ &service_list, GetSubcontext(), std::nullopt));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
@@ -291,9 +291,9 @@
Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, subcontext.get(),
- std::nullopt, from_apex));
+ parser.AddSectionParser(
+ "service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
+ from_apex));
return parser;
}
@@ -721,7 +721,7 @@
trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
SetStdioToDevNull(argv);
- InitKernelLogging(argv);
+ InitSecondStageLogging(argv);
LOG(INFO) << "init second stage started!";
// Init should not crash because of a dependence on any other process, therefore we ignore
@@ -809,7 +809,7 @@
PLOG(FATAL) << "SetupMountNamespaces failed";
}
- subcontext = InitializeSubcontext();
+ InitializeSubcontext();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
@@ -874,6 +874,7 @@
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
+ shutdown_state.set_do_shutdown(false);
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
diff --git a/init/init_test.cpp b/init/init_test.cpp
index caf3e03..07b4724 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -239,6 +239,28 @@
EXPECT_EQ(6, num_executed);
}
+TEST(init, RejectsCriticalAndOneshotService) {
+ std::string init_script =
+ R"init(
+service A something
+ class first
+ critical
+ oneshot
+)init";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+
+ ServiceList service_list;
+ Parser parser;
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+ ASSERT_TRUE(parser.ParseConfig(tf.path));
+ ASSERT_EQ(1u, parser.parse_error_count());
+}
+
} // namespace init
} // namespace android
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 0749fe3..f3b584c 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -240,14 +240,20 @@
// slave to the /mnt/user mount, and at the same time /mnt/installer in the
// bootstrap namespace shares a peer group with /mnt/installer in the
// default namespace.
+ // /mnt/androidwritable is similar to /mnt/installer but serves for
+ // MOUNT_EXTERNAL_ANDROID_WRITABLE apps.
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;
- // First, make /mnt/installer a slave bind mount
+ if (!(BindMount("/mnt/user", "/mnt/androidwritable", true))) 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;
// 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;
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 2f91663..e89f74a 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -203,6 +203,7 @@
}
static Result<void> CallVdc(const std::string& system, const std::string& cmd) {
+ LOG(INFO) << "Calling /system/bin/vdc " << system << " " << cmd;
const char* vdc_argv[] = {"/system/bin/vdc", system.c_str(), cmd.c_str()};
int status;
if (logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true,
@@ -456,10 +457,14 @@
#define ZRAM_RESET "/sys/block/zram0/reset"
#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
static Result<void> KillZramBackingDevice() {
+ if (access(ZRAM_BACK_DEV, F_OK) != 0 && errno == ENOENT) {
+ LOG(INFO) << "No zram backing device configured";
+ return {};
+ }
std::string backing_dev;
- if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return {};
-
- if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return {};
+ if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) {
+ return ErrnoError() << "Failed to read " << ZRAM_BACK_DEV;
+ }
// cut the last "\n"
backing_dev.erase(backing_dev.length() - 1);
@@ -478,6 +483,11 @@
<< " failed";
}
+ if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) {
+ LOG(INFO) << backing_dev << " is not a loop device. Exiting early";
+ return {};
+ }
+
// clear loopback device
unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));
if (loop.get() < 0) {
@@ -748,20 +758,24 @@
static Result<void> DoUserspaceReboot() {
LOG(INFO) << "Userspace reboot initiated";
- auto guard = android::base::make_scope_guard([] {
+ // An ugly way to pass a more precise reason on why fallback to hard reboot was triggered.
+ std::string sub_reason = "";
+ auto guard = android::base::make_scope_guard([&sub_reason] {
// Leave shutdown so that we can handle a full reboot.
LeaveShutdown();
- trigger_shutdown("reboot,userspace_failed,shutdown_aborted");
+ trigger_shutdown("reboot,userspace_failed,shutdown_aborted," + sub_reason);
});
// Triggering userspace-reboot-requested will result in a bunch of setprop
// actions. We should make sure, that all of them are propagated before
// proceeding with userspace reboot. Synchronously setting sys.init.userspace_reboot.in_progress
// property is not perfect, but it should do the trick.
if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
+ sub_reason = "setprop";
return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
}
EnterShutdown();
if (!SetProperty("sys.powerctl", "")) {
+ sub_reason = "resetprop";
return Error() << "Failed to reset sys.powerctl property";
}
std::vector<Service*> stop_first;
@@ -785,23 +799,27 @@
}
auto sigterm_timeout = GetMillisProperty("init.userspace_reboot.sigterm.timeoutmillis", 5s);
auto sigkill_timeout = GetMillisProperty("init.userspace_reboot.sigkill.timeoutmillis", 10s);
- LOG(INFO) << "Timeout to terminate services : " << sigterm_timeout.count() << "ms"
+ LOG(INFO) << "Timeout to terminate services: " << sigterm_timeout.count() << "ms "
<< "Timeout to kill services: " << sigkill_timeout.count() << "ms";
StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ sub_reason = "sigkill";
// TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " post-data services are still running";
}
if (auto result = KillZramBackingDevice(); !result.ok()) {
+ sub_reason = "zram";
return result;
}
if (auto result = CallVdc("volume", "reset"); !result.ok()) {
+ sub_reason = "vold_reset";
return result;
}
if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */),
sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ sub_reason = "sigkill_debug";
// TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " debugging services are still running";
}
@@ -812,9 +830,11 @@
LOG(INFO) << "sync() took " << sync_timer;
}
if (auto result = UnmountAllApexes(); !result.ok()) {
+ sub_reason = "apex";
return result;
}
if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+ sub_reason = "ns_switch";
return Error() << "Failed to switch to bootstrap namespace";
}
// Remove services that were defined in an APEX.
diff --git a/init/service.cpp b/init/service.cpp
index 20400a0..165b848 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -513,6 +513,10 @@
LOG(ERROR) << "failed to write pid to files: " << result.error();
}
+ if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
+ LOG(ERROR) << "failed to set task profiles";
+ }
+
// As requested, set our gid, supplemental gids, uid, context, and
// priority. Aborts on failure.
SetProcessAttributesAndCaps();
diff --git a/init/service.h b/init/service.h
index 9f1d697..34ed5ef 100644
--- a/init/service.h
+++ b/init/service.h
@@ -170,6 +170,8 @@
std::vector<std::string> writepid_files_;
+ std::vector<std::string> task_profiles_;
+
std::set<std::string> interfaces_; // e.g. some.package.foo@1.0::IBaz/instance-name
// keycodes for triggering this service via /dev/input/input*
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 560f693..bdac077 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -360,6 +360,12 @@
return Error() << "Invalid shutdown option";
}
+Result<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {
+ args.erase(args.begin());
+ service_->task_profiles_ = std::move(args);
+ return {};
+}
+
Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args) {
int period;
if (!ParseInt(args[1], &period, 1)) {
@@ -529,6 +535,7 @@
{"sigstop", {0, 0, &ServiceParser::ParseSigstop}},
{"socket", {3, 6, &ServiceParser::ParseSocket}},
{"stdio_to_kmsg", {0, 0, &ServiceParser::ParseStdioToKmsg}},
+ {"task_profiles", {1, kMax, &ServiceParser::ParseTaskProfiles}},
{"timeout_period", {1, 1, &ServiceParser::ParseTimeoutPeriod}},
{"updatable", {0, 0, &ServiceParser::ParseUpdatable}},
{"user", {1, 1, &ServiceParser::ParseUser}},
@@ -598,6 +605,13 @@
}
}
+ if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+ if ((service_->flags() & SVC_CRITICAL) != 0 && (service_->flags() & SVC_ONESHOT) != 0) {
+ return Error() << "service '" << service_->name()
+ << "' can't be both critical and oneshot";
+ }
+ }
+
Service* old_service = service_list_->FindService(service_->name());
if (old_service) {
if (!service_->is_override()) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 7bb0cc0..0fd2da5 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -78,6 +78,7 @@
Result<void> ParseSigstop(std::vector<std::string>&& args);
Result<void> ParseSocket(std::vector<std::string>&& args);
Result<void> ParseStdioToKmsg(std::vector<std::string>&& args);
+ Result<void> ParseTaskProfiles(std::vector<std::string>&& args);
Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
Result<void> ParseFile(std::vector<std::string>&& args);
Result<void> ParseUser(std::vector<std::string>&& args);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 5263c14..f3dd538 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -52,6 +52,8 @@
namespace {
std::string shutdown_command;
+static bool subcontext_terminated_by_shutdown;
+static std::unique_ptr<Subcontext> subcontext;
class SubcontextProcess {
public:
@@ -323,34 +325,30 @@
return expanded_args;
}
-static std::vector<Subcontext> subcontexts;
-static bool shutting_down;
-
-std::unique_ptr<Subcontext> InitializeSubcontext() {
+void InitializeSubcontext() {
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
- return std::make_unique<Subcontext>(std::vector<std::string>{"/vendor", "/odm"},
- kVendorContext);
+ subcontext.reset(
+ new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
}
- return nullptr;
+}
+
+Subcontext* GetSubcontext() {
+ return subcontext.get();
}
bool SubcontextChildReap(pid_t pid) {
- for (auto& subcontext : subcontexts) {
- if (subcontext.pid() == pid) {
- if (!shutting_down) {
- subcontext.Restart();
- }
- return true;
+ if (subcontext->pid() == pid) {
+ if (!subcontext_terminated_by_shutdown) {
+ subcontext->Restart();
}
+ return true;
}
return false;
}
void SubcontextTerminate() {
- shutting_down = true;
- for (auto& subcontext : subcontexts) {
- kill(subcontext.pid(), SIGTERM);
- }
+ subcontext_terminated_by_shutdown = true;
+ kill(subcontext->pid(), SIGTERM);
}
} // namespace init
diff --git a/init/subcontext.h b/init/subcontext.h
index 5e1d8a8..788d3be 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -60,7 +60,8 @@
};
int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
-std::unique_ptr<Subcontext> InitializeSubcontext();
+void InitializeSubcontext();
+Subcontext* GetSubcontext();
bool SubcontextChildReap(pid_t pid);
void SubcontextTerminate();
diff --git a/init/util.cpp b/init/util.cpp
index 24f94ec..f9be055 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -30,7 +30,9 @@
#include <time.h>
#include <unistd.h>
+#include <mutex>
#include <thread>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -660,5 +662,50 @@
return access("/system/bin/recovery", F_OK) == 0;
}
+// TODO(b/155203339): remove this
+// Devices in the lab seem to be stuck during shutdown, but the logs don't capture the last actions
+// before shutdown started, so we record those lines, ignoring requests to shutdown, and replay them
+// if we identify that the device is stuck.
+constexpr size_t kRecordedLogsSize = 30;
+std::string recorded_logs[kRecordedLogsSize];
+size_t recorded_log_position = 0;
+std::mutex recorded_logs_lock;
+
+void InitSecondStageLogging(char** argv) {
+ SetFatalRebootTarget();
+ auto second_stage_logger = [](android::base::LogId log_id, android::base::LogSeverity severity,
+ const char* tag, const char* file, unsigned int line,
+ const char* message) {
+ // We only store logs for init, not its children, and only if they're not related to
+ // sys.powerctl.
+ if (getpid() == 1 && strstr(message, "sys.powerctl") == nullptr) {
+ auto lock = std::lock_guard{recorded_logs_lock};
+ recorded_logs[recorded_log_position++] = message;
+ if (recorded_log_position == kRecordedLogsSize) {
+ recorded_log_position = 0;
+ }
+ }
+ android::base::KernelLogger(log_id, severity, tag, file, line, message);
+ };
+ android::base::InitLogging(argv, second_stage_logger, InitAborter);
+}
+
+void DumpShutdownDebugInformation() {
+ auto lock = std::lock_guard{recorded_logs_lock};
+ android::base::KernelLogger(
+ android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ "===================== Dumping previous init lines =====================");
+ for (size_t i = recorded_log_position; i < kRecordedLogsSize; ++i) {
+ android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ recorded_logs[i].c_str());
+ }
+ for (size_t i = 0; i < recorded_log_position; ++i) {
+ android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ recorded_logs[i].c_str());
+ }
+ android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
+ "===================== End of dump =====================");
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index ad322d9..8167b02 100644
--- a/init/util.h
+++ b/init/util.h
@@ -78,6 +78,8 @@
void SetStdioToDevNull(char** argv);
void InitKernelLogging(char** argv);
+void InitSecondStageLogging(char** argv);
+void DumpShutdownDebugInformation();
bool IsRecoveryMode();
} // namespace init
} // namespace android
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 175b2b7..d7c83a2 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -33,6 +33,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
@@ -59,6 +60,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
export_include_dirs: ["include"],
@@ -142,6 +144,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
native_bridge_supported: true,
srcs: [
"config_utils.cpp",
diff --git a/libcutils/arch-x86/android_memset16.S b/libcutils/arch-x86/android_memset16.S
index cb2ff14..f4d497e 100755
--- a/libcutils/arch-x86/android_memset16.S
+++ b/libcutils/arch-x86/android_memset16.S
@@ -105,14 +105,16 @@
/* We loaded the jump table and adjuested EDX. Go. */ \
jmp *%ebx
- .section .gnu.linkonce.t.__x86.get_pc_thunk.bx,"ax",@progbits
+ .section .text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
.globl __x86.get_pc_thunk.bx
.hidden __x86.get_pc_thunk.bx
ALIGN (4)
.type __x86.get_pc_thunk.bx,@function
__x86.get_pc_thunk.bx:
+ cfi_startproc
movl (%esp), %ebx
ret
+ cfi_endproc
#else
# define ENTRANCE
# define RETURN_END ret
diff --git a/libcutils/arch-x86/android_memset32.S b/libcutils/arch-x86/android_memset32.S
index f4326dc..b928f6b 100755
--- a/libcutils/arch-x86/android_memset32.S
+++ b/libcutils/arch-x86/android_memset32.S
@@ -105,14 +105,16 @@
/* We loaded the jump table and adjuested EDX. Go. */ \
jmp *%ebx
- .section .gnu.linkonce.t.__x86.get_pc_thunk.bx,"ax",@progbits
+ .section .text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
.globl __x86.get_pc_thunk.bx
.hidden __x86.get_pc_thunk.bx
ALIGN (4)
.type __x86.get_pc_thunk.bx,@function
__x86.get_pc_thunk.bx:
+ cfi_startproc
movl (%esp), %ebx
ret
+ cfi_endproc
#else
# define ENTRANCE
# define RETURN_END ret
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..1913c1e 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -1,14 +1,20 @@
-/* cutils/ashmem.h
- **
- ** Copyright 2008 The Android Open Source Project
- **
- ** This file is dual licensed. It may be redistributed and/or modified
- ** under the terms of the Apache 2.0 License OR version 2 of the GNU
- ** General Public License.
+/*
+ * Copyright (C) 2008 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 _CUTILS_ASHMEM_H
-#define _CUTILS_ASHMEM_H
+#pragma once
#include <stddef.h>
@@ -30,5 +36,3 @@
#ifdef __cplusplus
}
#endif
-
-#endif /* _CUTILS_ASHMEM_H */
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index ce0c3c8..33ae13d 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -26,4 +26,5 @@
export_include_dirs: ["include"],
shared_libs: ["android.hardware.graphics.allocator@2.0"],
header_libs: ["libhardware_headers"],
+ min_sdk_version: "29",
}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 0b98e1a..6051ac7 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
native_bridge_supported: true,
export_include_dirs: ["include"],
system_shared_libs: [],
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 512c7cd..8a0ebf2 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -185,14 +185,26 @@
* and sending log messages to user defined loggers specified in __android_log_set_logger().
*/
struct __android_log_message {
- size_t
- struct_size; /** Must be set to sizeof(__android_log_message) and is used for versioning. */
- int32_t buffer_id; /** {@link log_id_t} values. */
- int32_t priority; /** {@link android_LogPriority} values. */
- const char* tag; /** The tag for the log message. */
- const char* file; /** Optional file name, may be set to nullptr. */
- uint32_t line; /** Optional line number, ignore if file is nullptr. */
- const char* message; /** The log message itself. */
+ /** Must be set to sizeof(__android_log_message) and is used for versioning. */
+ size_t struct_size;
+
+ /** {@link log_id_t} values. */
+ int32_t buffer_id;
+
+ /** {@link android_LogPriority} values. */
+ int32_t priority;
+
+ /** The tag for the log message. */
+ const char* tag;
+
+ /** Optional file name, may be set to nullptr. */
+ const char* file;
+
+ /** Optional line number, ignore if file is nullptr. */
+ uint32_t line;
+
+ /** The log message itself. */
+ const char* message;
};
/**
@@ -215,7 +227,7 @@
* buffers, then pass the message to liblog via this function, and therefore we do not want to
* duplicate the loggability check here.
*
- * @param log_message the log message itself, see {@link __android_log_message}.
+ * @param log_message the log message itself, see __android_log_message.
*
* Available since API level 30.
*/
@@ -237,7 +249,7 @@
* Writes the log message to logd. This is an __android_logger_function and can be provided to
* __android_log_set_logger(). It is the default logger when running liblog on a device.
*
- * @param log_message the log message to write, see {@link __android_log_message}.
+ * @param log_message the log message to write, see __android_log_message.
*
* Available since API level 30.
*/
@@ -247,7 +259,7 @@
* Writes the log message to stderr. This is an __android_logger_function and can be provided to
* __android_log_set_logger(). It is the default logger when running liblog on host.
*
- * @param log_message the log message to write, see {@link __android_log_message}.
+ * @param log_message the log message to write, see __android_log_message.
*
* Available since API level 30.
*/
@@ -259,7 +271,7 @@
* user defined aborter function is highly recommended to abort and be noreturn, but is not strictly
* required to.
*
- * @param aborter the new aborter function, see {@link __android_aborter_function}.
+ * @param aborter the new aborter function, see __android_aborter_function.
*
* Available since API level 30.
*/
@@ -297,7 +309,26 @@
* minimum priority needed to log. If only one is set, then that value is used to determine the
* minimum priority needed. If none are set, then default_priority is used.
*
- * @param prio the priority to test, takes {@link android_LogPriority} values.
+ * @param prio the priority to test, takes android_LogPriority values.
+ * @param tag the tag to test.
+ * @param default_prio the default priority to use if no properties or minimum priority are set.
+ * @return an integer where 1 indicates that the message is loggable and 0 indicates that it is not.
+ *
+ * Available since API level 30.
+ */
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
+
+/**
+ * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from
+ * __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will
+ * be printed. A non-zero result indicates yes, zero indicates false.
+ *
+ * If both a priority for a tag and a minimum priority are set by
+ * __android_log_set_minimum_priority(), then the lowest of the two values are to determine the
+ * minimum priority needed to log. If only one is set, then that value is used to determine the
+ * minimum priority needed. If none are set, then default_priority is used.
+ *
+ * @param prio the priority to test, takes android_LogPriority values.
* @param tag the tag to test.
* @param len the length of the tag.
* @param default_prio the default priority to use if no properties or minimum priority are set.
@@ -305,15 +336,14 @@
*
* Available since API level 30.
*/
-int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30);
int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio)
__INTRODUCED_IN(30);
/**
* Sets the minimum priority that will be logged for this process.
*
- * @param priority the new minimum priority to set, takes @{link android_LogPriority} values.
- * @return the previous set minimum priority as @{link android_LogPriority} values, or
+ * @param priority the new minimum priority to set, takes android_LogPriority values.
+ * @return the previous set minimum priority as android_LogPriority values, or
* ANDROID_LOG_DEFAULT if none was set.
*
* Available since API level 30.
@@ -324,7 +354,7 @@
* Gets the minimum priority that will be logged for this process. If none has been set by a
* previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
*
- * @return the current minimum priority as @{link android_LogPriority} values, or
+ * @return the current minimum priority as android_LogPriority values, or
* ANDROID_LOG_DEFAULT if none is set.
*
* Available since API level 30.
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 90d1e76..820b7cb 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -29,7 +29,6 @@
#include <log/log_id.h>
#include <log/log_main.h>
#include <log/log_radio.h>
-#include <log/log_read.h>
#include <log/log_safetynet.h>
#include <log/log_system.h>
#include <log/log_time.h>
@@ -65,6 +64,13 @@
#endif
/*
+ * The maximum size of the log entry payload that can be
+ * written to the logger. An attempt to write more than
+ * this amount will result in a truncated log entry.
+ */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4068
+
+/*
* Event logging.
*/
@@ -87,8 +93,6 @@
/*
* Event log entry types.
*/
-#ifndef __AndroidEventLogType_defined
-#define __AndroidEventLogType_defined
typedef enum {
/* Special markers for android_log_list_element type */
EVENT_TYPE_LIST_STOP = '\n', /* declare end of list */
@@ -101,9 +105,6 @@
EVENT_TYPE_LIST = 3,
EVENT_TYPE_FLOAT = 4,
} AndroidEventLogType;
-#endif
-#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
-#define typeof_AndroidEventLogType unsigned char
#ifndef LOG_EVENT_INT
#define LOG_EVENT_INT(_tag, _value) \
@@ -143,8 +144,11 @@
/*
* Release any logger resources (a new log write will immediately re-acquire)
*
- * May be used to clean up File descriptors after a Fork, the resources are
- * all O_CLOEXEC so wil self clean on exec().
+ * This is specifically meant to be used by Zygote to close open file descriptors after fork()
+ * and before specialization. O_CLOEXEC is used on file descriptors, so they will be closed upon
+ * exec() in normal use cases.
+ *
+ * Note that this is not safe to call from a multi-threaded program.
*/
void __android_log_close(void);
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index c8fafe7..8e4faeb 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -23,14 +23,6 @@
#endif
/*
- * Send a simple string to the log.
- */
-int __android_log_buf_write(int bufID, int prio, const char* tag,
- const char* text);
-int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
- __attribute__((__format__(printf, 4, 5)));
-
-/*
* log_id_t helpers
*/
log_id_t android_name_to_log_id(const char* logName);
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 3a8af6d..3497d63 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -1,11 +1,18 @@
/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
+ * Copyright (C) 2017 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
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
index 8b8a362..f5525c1 100644
--- a/liblog/include/log/log_radio.h
+++ b/liblog/include/log/log_radio.h
@@ -17,7 +17,6 @@
#pragma once
#include <android/log.h>
-#include <log/log_id.h>
/*
* Normally we strip the effects of ALOGV (VERBOSE messages),
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index b9a6bc9..24b88d2 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -16,30 +16,18 @@
#pragma once
+#include <stdint.h>
#include <sys/types.h>
-/* deal with possible sys/cdefs.h conflict with fcntl.h */
-#ifdef __unused
-#define __unused_defined __unused
-#undef __unused
-#endif
-
-#include <fcntl.h> /* Pick up O_* macros */
-
-/* restore definitions from above */
-#ifdef __unused_defined
-#define __unused __attribute__((__unused__))
-#endif
-
-#include <stdint.h>
-
-#include <log/log_id.h>
+#include <android/log.h>
#include <log/log_time.h>
#ifdef __cplusplus
extern "C" {
#endif
+#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
+
/*
* Native log reading interface section. See logcat for sample code.
*
@@ -60,13 +48,6 @@
};
/*
- * The maximum size of the log entry payload that can be
- * written to the logger. An attempt to write more than
- * this amount will result in a truncated log entry.
- */
-#define LOGGER_ENTRY_MAX_PAYLOAD 4068
-
-/*
* The maximum size of a log entry which can be read.
* An attempt to read less than this amount may result
* in read() returning EINVAL.
@@ -79,32 +60,9 @@
struct logger_entry entry;
} __attribute__((aligned(4)));
#ifdef __cplusplus
- /* Matching log_time operators */
- bool operator==(const log_msg& T) const {
- return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec);
- }
- bool operator!=(const log_msg& T) const {
- return !(*this == T);
- }
- bool operator<(const log_msg& T) const {
- return (entry.sec < T.entry.sec) ||
- ((entry.sec == T.entry.sec) && (entry.nsec < T.entry.nsec));
- }
- bool operator>=(const log_msg& T) const {
- return !(*this < T);
- }
- bool operator>(const log_msg& T) const {
- return (entry.sec > T.entry.sec) ||
- ((entry.sec == T.entry.sec) && (entry.nsec > T.entry.nsec));
- }
- bool operator<=(const log_msg& T) const {
- return !(*this > T);
- }
uint64_t nsec() const {
return static_cast<uint64_t>(entry.sec) * NS_PER_SEC + entry.nsec;
}
-
- /* packet methods */
log_id_t id() {
return static_cast<log_id_t>(entry.lid);
}
@@ -137,13 +95,10 @@
char* buf, size_t len);
int android_logger_set_prune_list(struct logger_list* logger_list, const char* buf, size_t len);
-#ifndef O_NONBLOCK
+/* The below values are used for the `mode` argument of the below functions. */
+/* Note that 0x00000003 were previously used and should be considered reserved. */
#define ANDROID_LOG_NONBLOCK 0x00000800
-#else
-#define ANDROID_LOG_NONBLOCK O_NONBLOCK
-#endif
#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
-#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
#define ANDROID_LOG_PSTORE 0x80000000
struct logger_list* android_logger_list_alloc(int mode, unsigned int tail,
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index d3e9b19..b2604b5 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -1,11 +1,18 @@
/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
+ * Copyright (C) 2017 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
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
index eaec741..6f40515 100644
--- a/liblog/include/log/log_system.h
+++ b/liblog/include/log/log_system.h
@@ -17,7 +17,6 @@
#pragma once
#include <android/log.h>
-#include <log/log_id.h>
/*
* Normally we strip the effects of ALOGV (VERBOSE messages),
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
index a79beec..ab4adc4 100644
--- a/liblog/include_vndk/log/log.h
+++ b/liblog/include_vndk/log/log.h
@@ -3,6 +3,9 @@
#ifndef _LIBS_LOG_LOG_H
#define _LIBS_LOG_LOG_H
+/* Historically vendors have depended on this header being included. */
+#include <fcntl.h>
+
#include <android/log.h>
#include <log/log_id.h>
#include <log/log_main.h>
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 67376f4..a230749 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -32,58 +32,53 @@
#include <time.h>
#include <unistd.h>
-#include <shared_mutex>
-
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
-static int logd_socket;
-static RwLock logd_socket_lock;
+static atomic_int logd_socket;
-static void OpenSocketLocked() {
- logd_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
- if (logd_socket <= 0) {
- return;
- }
-
+// Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
+// function is used to reconnect to logd without requiring a new socket.
+static void LogdConnect() {
sockaddr_un un = {};
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "/dev/socket/logdw");
-
- if (TEMP_FAILURE_RETRY(
- connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un))) < 0) {
- close(logd_socket);
- logd_socket = 0;
- }
+ TEMP_FAILURE_RETRY(connect(logd_socket, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
}
-static void OpenSocket() {
- auto lock = std::unique_lock{logd_socket_lock};
- if (logd_socket > 0) {
- // Someone raced us and opened the socket already.
+// logd_socket should only be opened once. If we see that logd_socket is uninitialized, we create a
+// new socket and attempt to exchange it into the atomic logd_socket. If the compare/exchange was
+// successful, then that will be the socket used for the duration of the program, otherwise a
+// different thread has already opened and written the socket to the atomic, so close the new socket
+// and return.
+static void GetSocket() {
+ if (logd_socket != 0) {
return;
}
- OpenSocketLocked();
-}
-
-static void ResetSocket(int old_socket) {
- auto lock = std::unique_lock{logd_socket_lock};
- if (old_socket != logd_socket) {
- // Someone raced us and reset the socket already.
+ int new_socket =
+ TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ if (new_socket <= 0) {
return;
}
- close(logd_socket);
- logd_socket = 0;
- OpenSocketLocked();
+
+ int uninitialized_value = 0;
+ if (!logd_socket.compare_exchange_strong(uninitialized_value, new_socket)) {
+ close(new_socket);
+ return;
+ }
+
+ LogdConnect();
}
+// This is the one exception to the above. Zygote uses this to clean up open FD's after fork() and
+// before specialization. It is single threaded at this point and therefore this function is
+// explicitly not thread safe. It sets logd_socket to 0, so future logs will be safely initialized
+// whenever they happen.
void LogdClose() {
- auto lock = std::unique_lock{logd_socket_lock};
if (logd_socket > 0) {
close(logd_socket);
}
@@ -99,12 +94,7 @@
static atomic_int dropped;
static atomic_int droppedSecurity;
- auto lock = std::shared_lock{logd_socket_lock};
- if (logd_socket <= 0) {
- lock.unlock();
- OpenSocket();
- lock.lock();
- }
+ GetSocket();
if (logd_socket <= 0) {
return -EBADF;
@@ -183,10 +173,7 @@
// the connection, so we reset it and try again.
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
if (ret < 0 && errno != EAGAIN) {
- int old_socket = logd_socket;
- lock.unlock();
- ResetSocket(old_socket);
- lock.lock();
+ LogdConnect();
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
}
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index c174b85..3a75fa3 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -28,7 +28,6 @@
#endif
#include <atomic>
-#include <shared_mutex>
#include <android-base/errno_restorer.h>
#include <android-base/macros.h>
@@ -38,7 +37,6 @@
#include "android/log.h"
#include "log/log_read.h"
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
#ifdef __ANDROID__
@@ -142,10 +140,8 @@
static std::string default_tag = getprogname();
return default_tag;
}
-RwLock default_tag_lock;
void __android_log_set_default_tag(const char* tag) {
- auto lock = std::unique_lock{default_tag_lock};
GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD);
}
@@ -163,10 +159,8 @@
#else
static __android_logger_function logger_function = __android_log_stderr_logger;
#endif
-static RwLock logger_function_lock;
void __android_log_set_logger(__android_logger_function logger) {
- auto lock = std::unique_lock{logger_function_lock};
logger_function = logger;
}
@@ -180,15 +174,12 @@
}
static __android_aborter_function aborter_function = __android_log_default_aborter;
-static RwLock aborter_function_lock;
void __android_log_set_aborter(__android_aborter_function aborter) {
- auto lock = std::unique_lock{aborter_function_lock};
aborter_function = aborter;
}
void __android_log_call_aborter(const char* abort_message) {
- auto lock = std::shared_lock{aborter_function_lock};
aborter_function(abort_message);
}
@@ -310,9 +301,7 @@
return;
}
- auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
if (log_message->tag == nullptr) {
- tag_lock.lock();
log_message->tag = GetDefaultTag().c_str();
}
@@ -322,7 +311,6 @@
}
#endif
- auto lock = std::shared_lock{logger_function_lock};
logger_function(log_message);
}
@@ -330,7 +318,7 @@
ErrnoRestorer errno_restorer;
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
- return 0;
+ return -EPERM;
}
__android_log_message log_message = {
@@ -343,7 +331,7 @@
ErrnoRestorer errno_restorer;
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
- return 0;
+ return -EPERM;
}
__attribute__((uninitialized)) char buf[LOG_BUF_SIZE];
@@ -360,7 +348,7 @@
ErrnoRestorer errno_restorer;
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
- return 0;
+ return -EPERM;
}
va_list ap;
@@ -380,7 +368,7 @@
ErrnoRestorer errno_restorer;
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
- return 0;
+ return -EPERM;
}
va_list ap;
diff --git a/liblog/logger_write.h b/liblog/logger_write.h
index 065fd55..eee2778 100644
--- a/liblog/logger_write.h
+++ b/liblog/logger_write.h
@@ -18,7 +18,4 @@
#include <string>
-#include "rwlock.h"
-
-std::string& GetDefaultTag(); // Must read lock default_tag_lock
-extern RwLock default_tag_lock;
\ No newline at end of file
+std::string& GetDefaultTag();
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 5c69bf8..9e8d277 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -19,6 +19,8 @@
#define HAVE_STRSEP
#endif
+#include <log/logprint.h>
+
#include <assert.h>
#include <ctype.h>
#include <errno.h>
@@ -37,7 +39,7 @@
#include <cutils/list.h>
#include <log/log.h>
-#include <log/logprint.h>
+#include <log/log_read.h>
#include <private/android_logger.h>
#define MS_PER_NSEC 1000000
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 06e5e04..0751e2c 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -23,30 +23,36 @@
#include <sys/types.h>
#include <time.h>
-#include <shared_mutex>
-
#include <log/log_properties.h>
#include <private/android_logger.h>
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
-static int pmsg_fd;
-static RwLock pmsg_fd_lock;
+static atomic_int pmsg_fd;
-static void PmsgOpen() {
- auto lock = std::unique_lock{pmsg_fd_lock};
- if (pmsg_fd > 0) {
- // Someone raced us and opened the socket already.
+// pmsg_fd should only beopened once. If we see that pmsg_fd is uninitialized, we open "/dev/pmsg0"
+// then attempt to compare/exchange it into pmsg_fd. If the compare/exchange was successful, then
+// that will be the fd used for the duration of the program, otherwise a different thread has
+// already opened and written the fd to the atomic, so close the new fd and return.
+static void GetPmsgFd() {
+ if (pmsg_fd != 0) {
return;
}
- pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (new_fd <= 0) {
+ return;
+ }
+
+ int uninitialized_value = 0;
+ if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
+ close(new_fd);
+ return;
+ }
}
void PmsgClose() {
- auto lock = std::unique_lock{pmsg_fd_lock};
if (pmsg_fd > 0) {
close(pmsg_fd);
}
@@ -77,13 +83,7 @@
}
}
- auto lock = std::shared_lock{pmsg_fd_lock};
-
- if (pmsg_fd <= 0) {
- lock.unlock();
- PmsgOpen();
- lock.lock();
- }
+ GetPmsgFd();
if (pmsg_fd <= 0) {
return -EBADF;
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index abd48fc..37670ec 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -23,7 +23,6 @@
#include <unistd.h>
#include <algorithm>
-#include <shared_mutex>
#include <private/android_logger.h>
@@ -99,9 +98,7 @@
static const char log_namespace[] = "persist.log.tag.";
static const size_t base_offset = 8; /* skip "persist." */
- auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock};
if (tag == nullptr || len == 0) {
- tag_lock.lock();
auto& tag_string = GetDefaultTag();
tag = tag_string.c_str();
len = tag_string.size();
diff --git a/liblog/rwlock.h b/liblog/rwlock.h
deleted file mode 100644
index 00f1806..0000000
--- a/liblog/rwlock.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <pthread.h>
-
-// As of the end of Dec 2019, std::shared_mutex is *not* simply a pthread_rwlock, but rather a
-// combination of std::mutex and std::condition variable, which is obviously less efficient. This
-// immitates what std::shared_mutex should be doing and is compatible with std::shared_lock and
-// std::unique_lock.
-
-class RwLock {
- public:
- RwLock() {}
- ~RwLock() {}
-
- void lock() { pthread_rwlock_wrlock(&rwlock_); }
- void unlock() { pthread_rwlock_unlock(&rwlock_); }
-
- void lock_shared() { pthread_rwlock_rdlock(&rwlock_); }
- void unlock_shared() { pthread_rwlock_unlock(&rwlock_); }
-
- private:
- pthread_rwlock_t rwlock_ = PTHREAD_RWLOCK_INITIALIZER;
-};
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 385b079..2a6424b 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -63,8 +63,8 @@
"log_system_test.cpp",
"log_time_test.cpp",
"log_wrap_test.cpp",
+ "logd_writer_test.cpp",
"logprint_test.cpp",
- "rwlock_test.cpp",
],
shared_libs: [
"libcutils",
@@ -72,6 +72,7 @@
],
static_libs: ["liblog"],
isolated: true,
+ require_root: true,
}
// Build tests for the device (with .so). Run with:
@@ -108,7 +109,6 @@
"liblog_host_test.cpp",
"liblog_default_tag.cpp",
"liblog_global_state.cpp",
- "rwlock_test.cpp",
],
isolated: true,
}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3a6ed90..f4734b9 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -31,6 +31,7 @@
#include <benchmark/benchmark.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
+#include <log/log_read.h>
#include <private/android_logger.h>
BENCHMARK_MAIN();
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 048bf61..d3d8e91 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -40,6 +40,7 @@
#include <gtest/gtest.h>
#include <log/log_event_list.h>
#include <log/log_properties.h>
+#include <log/log_read.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
diff --git a/liblog/tests/logd_writer_test.cpp b/liblog/tests/logd_writer_test.cpp
new file mode 100644
index 0000000..b8e4726
--- /dev/null
+++ b/liblog/tests/logd_writer_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+// logd_writer takes advantage of the fact that connect() can be called multiple times for a DGRAM
+// socket. This tests for that behavior.
+TEST(liblog, multi_connect_dgram_socket) {
+#ifdef __ANDROID__
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+ auto temp_dir = TemporaryDir();
+ auto socket_path = StringPrintf("%s/test_socket", temp_dir.path);
+
+ unique_fd server_socket;
+
+ auto open_server_socket = [&] {
+ server_socket.reset(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
+ ASSERT_TRUE(server_socket.ok());
+
+ sockaddr_un server_sockaddr = {};
+ server_sockaddr.sun_family = AF_UNIX;
+ strlcpy(server_sockaddr.sun_path, socket_path.c_str(), sizeof(server_sockaddr.sun_path));
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(bind(server_socket, reinterpret_cast<sockaddr*>(&server_sockaddr),
+ sizeof(server_sockaddr))));
+ };
+
+ // Open the server socket.
+ open_server_socket();
+
+ // Open the client socket.
+ auto client_socket =
+ unique_fd{TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0))};
+ ASSERT_TRUE(client_socket.ok());
+ sockaddr_un client_sockaddr = {};
+ client_sockaddr.sun_family = AF_UNIX;
+ strlcpy(client_sockaddr.sun_path, socket_path.c_str(), sizeof(client_sockaddr.sun_path));
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+ sizeof(client_sockaddr))));
+
+ // Ensure that communication works.
+ constexpr static char kSmoke[] = "smoke test";
+ ssize_t smoke_len = sizeof(kSmoke);
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ char read_buf[512];
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+ ASSERT_STREQ(kSmoke, read_buf);
+
+ // Close the server socket.
+ server_socket.reset();
+ ASSERT_EQ(0, unlink(socket_path.c_str())) << strerror(errno);
+
+ // Ensure that write() from the client returns an error since the server is closed.
+ ASSERT_EQ(-1, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ ASSERT_EQ(errno, ECONNREFUSED) << strerror(errno);
+
+ // Open the server socket again.
+ open_server_socket();
+
+ // Reconnect the same client socket.
+ ASSERT_EQ(0,
+ TEMP_FAILURE_RETRY(connect(client_socket, reinterpret_cast<sockaddr*>(&client_sockaddr),
+ sizeof(client_sockaddr))))
+ << strerror(errno);
+
+ // Ensure that communication works.
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(write(client_socket, kSmoke, sizeof(kSmoke))));
+ ASSERT_EQ(smoke_len, TEMP_FAILURE_RETRY(read(server_socket, read_buf, sizeof(read_buf))));
+ ASSERT_STREQ(kSmoke, read_buf);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
\ No newline at end of file
diff --git a/liblog/tests/rwlock_test.cpp b/liblog/tests/rwlock_test.cpp
deleted file mode 100644
index 617d5c4..0000000
--- a/liblog/tests/rwlock_test.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "../rwlock.h"
-
-#include <chrono>
-#include <shared_mutex>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-using namespace std::literals;
-
-TEST(rwlock, reader_then_reader_lock) {
- RwLock lock;
-
- bool thread_ran = false;
- auto read_guard = std::shared_lock{lock};
-
- auto reader_thread = std::thread([&] {
- auto read_guard = std::shared_lock{lock};
- thread_ran = true;
- });
-
- auto end_time = std::chrono::steady_clock::now() + 1s;
-
- while (std::chrono::steady_clock::now() < end_time) {
- if (thread_ran) {
- break;
- }
- }
-
- EXPECT_EQ(true, thread_ran);
-
- // Unlock the lock in case something went wrong, to ensure that we can still join() the thread.
- read_guard.unlock();
- reader_thread.join();
-}
-
-template <template <typename> typename L1, template <typename> typename L2>
-void TestBlockingLocks() {
- RwLock lock;
-
- bool thread_ran = false;
- auto read_guard = L1{lock};
-
- auto reader_thread = std::thread([&] {
- auto read_guard = L2{lock};
- thread_ran = true;
- });
-
- auto end_time = std::chrono::steady_clock::now() + 1s;
-
- while (std::chrono::steady_clock::now() < end_time) {
- if (thread_ran) {
- break;
- }
- }
-
- EXPECT_EQ(false, thread_ran);
-
- read_guard.unlock();
- reader_thread.join();
-
- EXPECT_EQ(true, thread_ran);
-}
-
-TEST(rwlock, reader_then_writer_lock) {
- TestBlockingLocks<std::shared_lock, std::unique_lock>();
-}
-
-TEST(rwlock, writer_then_reader_lock) {
- TestBlockingLocks<std::unique_lock, std::shared_lock>();
-}
-
-TEST(rwlock, writer_then_writer_lock) {
- TestBlockingLocks<std::unique_lock, std::unique_lock>();
-}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 2c1b255..bda11e9 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -17,6 +17,7 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
}
cc_library {
@@ -60,4 +61,5 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ min_sdk_version: "29",
}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 12474f1..766ea0f 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -100,13 +100,12 @@
"libjsoncpp",
"libprotobuf-cpp-full",
],
- target: {
- android: {
- test_config: "vts_processgroup_validate_test.xml",
- },
- },
+ test_suites: [
+ "vts",
+ ],
}
vts_config {
name: "VtsProcessgroupValidateTest",
+ test_config: "vts_processgroup_validate_test.xml",
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 3f08535..a515e58 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -144,6 +144,19 @@
}
]
},
+ {
+ "Name": "CameraServicePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
{
"Name": "CpuPolicySpread",
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index caea048..cbc65ff 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -48,6 +48,7 @@
export_include_dirs: ["include"],
static_libs: ["libgtest_prod"],
apex_available: ["com.android.resolv"],
+ min_sdk_version: "29",
}
cc_test {
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 690dc94..6882ab2 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -55,6 +55,7 @@
export_include_dirs: ["include"],
host_supported: true,
apex_available: ["com.android.resolv"],
+ min_sdk_version: "29",
}
cc_benchmark {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 9afc9a3..f3d3f27 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -57,6 +57,7 @@
"MapInfo.cpp",
"Maps.cpp",
"Memory.cpp",
+ "MemoryMte.cpp",
"LocalUnwinder.cpp",
"Regs.cpp",
"RegsArm.cpp",
@@ -74,13 +75,29 @@
],
target: {
- // Always disable optimizations for host to make it easier to debug.
host: {
+ // Always disable optimizations for host to make it easier to debug.
cflags: [
"-O0",
"-g",
],
},
+ android: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
+ },
+ linux_bionic: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
+ },
},
arch: {
@@ -213,6 +230,7 @@
"tests/MemoryRangesTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/MemoryTest.cpp",
+ "tests/MemoryMteTest.cpp",
"tests/RegsInfoTest.cpp",
"tests/RegsIterateTest.cpp",
"tests/RegsStepIfSignalHandlerTest.cpp",
@@ -268,6 +286,25 @@
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
+
+ target: {
+ android: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
+ },
+ linux_bionic: {
+ header_libs: ["bionic_libc_platform_headers"],
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
+ },
+ },
}
cc_test {
@@ -373,12 +410,28 @@
srcs: [
"benchmarks/unwind_benchmarks.cpp",
+ "benchmarks/ElfBenchmark.cpp",
+ "benchmarks/SymbolBenchmark.cpp",
+ "benchmarks/Utils.cpp",
+ ],
+
+ data: [
+ "benchmarks/files/*",
],
shared_libs: [
"libbase",
"libunwindstack",
],
+
+ target: {
+ android: {
+ static_libs: [
+ "libmeminfo",
+ "libprocinfo",
+ ],
+ },
+ },
}
// Generates the elf data for use in the tests for .gnu_debugdata frames.
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 821e042..17470fd 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -371,7 +371,7 @@
// Look for the .debug_frame and .gnu_debugdata.
if (shdr.sh_name < sec_size) {
std::string name;
- if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+ if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name)) {
if (name == ".debug_frame") {
debug_frame_offset_ = shdr.sh_offset;
debug_frame_size_ = shdr.sh_size;
@@ -405,7 +405,7 @@
} else if (shdr.sh_type == SHT_NOTE) {
if (shdr.sh_name < sec_size) {
std::string name;
- if (memory_->ReadString(sec_offset + shdr.sh_name, &name) &&
+ if (memory_->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
name == ".note.gnu.build-id") {
gnu_build_id_offset_ = shdr.sh_offset;
gnu_build_id_size_ = shdr.sh_size;
@@ -456,10 +456,11 @@
for (const auto& entry : strtabs_) {
if (entry.first == strtab_addr) {
soname_offset = entry.second + soname_offset;
- if (soname_offset >= entry.second + strtab_size) {
+ uint64_t soname_max = entry.second + strtab_size;
+ if (soname_offset >= soname_max) {
return "";
}
- if (!memory_->ReadString(soname_offset, &soname_)) {
+ if (!memory_->ReadString(soname_offset, &soname_, soname_max - soname_offset)) {
return "";
}
soname_type_ = SONAME_VALID;
@@ -608,7 +609,8 @@
}
std::string name;
if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
- memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
+ memory->ReadString(sec_offset + shdr.sh_name, &name, sec_size - shdr.sh_name) &&
+ name == ".note.gnu.build-id") {
*build_id_offset = shdr.sh_offset;
*build_id_size = shdr.sh_size;
return true;
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 8f49ad9..670d904 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -159,6 +159,8 @@
search_map_idx = old_map_idx + 1;
if (new_map_idx + 1 < maps_.size()) {
maps_[new_map_idx + 1]->prev_map = info.get();
+ maps_[new_map_idx + 1]->prev_real_map =
+ info->IsBlank() ? info->prev_real_map : info.get();
}
maps_[new_map_idx] = nullptr;
total_entries--;
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 8de3d98..b4623fa 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -158,20 +158,30 @@
return rc == size;
}
-bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
- string->clear();
- uint64_t bytes_read = 0;
- while (bytes_read < max_read) {
- uint8_t value;
- if (!ReadFully(addr, &value, sizeof(value))) {
- return false;
+bool Memory::ReadString(uint64_t addr, std::string* dst, size_t max_read) {
+ char buffer[256]; // Large enough for 99% of symbol names.
+ size_t size = 0; // Number of bytes which were read into the buffer.
+ for (size_t offset = 0; offset < max_read; offset += size) {
+ // Look for null-terminator first, so we can allocate string of exact size.
+ // If we know the end of valid memory range, do the reads in larger blocks.
+ size_t read = std::min(sizeof(buffer), max_read - offset);
+ size = Read(addr + offset, buffer, read);
+ if (size == 0) {
+ return false; // We have not found end of string yet and we can not read more data.
}
- if (value == '\0') {
- return true;
+ size_t length = strnlen(buffer, size); // Index of the null-terminator.
+ if (length < size) {
+ // We found the null-terminator. Allocate the string and set its content.
+ if (offset == 0) {
+ // We did just single read, so the buffer already contains the whole string.
+ dst->assign(buffer, length);
+ return true;
+ } else {
+ // The buffer contains only the last block. Read the whole string again.
+ dst->assign(offset + length, '\0');
+ return ReadFully(addr, dst->data(), dst->size());
+ }
}
- string->push_back(value);
- addr++;
- bytes_read++;
}
return false;
}
@@ -324,6 +334,16 @@
return ProcessVmRead(getpid(), addr, dst, size);
}
+#if !defined(ANDROID_EXPERIMENTAL_MTE)
+long MemoryRemote::ReadTag(uint64_t) {
+ return -1;
+}
+
+long MemoryLocal::ReadTag(uint64_t) {
+ return -1;
+}
+#endif
+
MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
uint64_t offset)
: memory_(memory), begin_(begin), length_(length), offset_(offset) {}
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
index 769d907..d97640d 100644
--- a/libunwindstack/MemoryCache.h
+++ b/libunwindstack/MemoryCache.h
@@ -33,6 +33,7 @@
virtual ~MemoryCache() = default;
size_t Read(uint64_t addr, void* dst, size_t size) override;
+ long ReadTag(uint64_t addr) override { return impl_->ReadTag(addr); }
void Clear() override { cache_.clear(); }
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
index 7e027cf..741f107 100644
--- a/libunwindstack/MemoryLocal.h
+++ b/libunwindstack/MemoryLocal.h
@@ -31,6 +31,7 @@
bool IsLocal() const override { return true; }
size_t Read(uint64_t addr, void* dst, size_t size) override;
+ long ReadTag(uint64_t addr) override;
};
} // namespace unwindstack
diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp
new file mode 100644
index 0000000..46a546e
--- /dev/null
+++ b/libunwindstack/MemoryMte.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
+
+#include "MemoryLocal.h"
+#include "MemoryRemote.h"
+
+namespace unwindstack {
+
+long MemoryRemote::ReadTag(uint64_t addr) {
+#if defined(__aarch64__)
+ char tag;
+ iovec iov = {&tag, 1};
+ if (ptrace(PTRACE_PEEKMTETAGS, pid_, reinterpret_cast<void*>(addr), &iov) != 0 ||
+ iov.iov_len != 1) {
+ return -1;
+ }
+ return tag;
+#else
+ (void)addr;
+ return -1;
+#endif
+}
+
+long MemoryLocal::ReadTag(uint64_t addr) {
+#if defined(__aarch64__)
+ // Check that the memory is readable first. This is racy with the ldg but there's not much
+ // we can do about it.
+ char data;
+ if (!mte_supported() || !Read(addr, &data, 1)) {
+ return -1;
+ }
+
+ __asm__ __volatile__(".arch_extension mte; ldg %0, [%0]" : "+r"(addr) : : "memory");
+ return (addr >> 56) & 0xf;
+#else
+ (void)addr;
+ return -1;
+#endif
+}
+
+} // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
index db367d6..dd09c88 100644
--- a/libunwindstack/MemoryRemote.h
+++ b/libunwindstack/MemoryRemote.h
@@ -32,6 +32,7 @@
virtual ~MemoryRemote() = default;
size_t Read(uint64_t addr, void* dst, size_t size) override;
+ long ReadTag(uint64_t addr) override;
pid_t pid() { return pid_; }
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index e3c15a2..2117ebd 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -19,6 +19,7 @@
#include <algorithm>
#include <string>
+#include <vector>
#include <unwindstack/Memory.h>
@@ -29,23 +30,55 @@
Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
uint64_t str_size)
- : cur_offset_(offset),
- offset_(offset),
- end_(offset + size),
+ : offset_(offset),
+ count_(entry_size != 0 ? size / entry_size : 0),
entry_size_(entry_size),
str_offset_(str_offset),
str_end_(str_offset_ + str_size) {}
-const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) {
- // Binary search the table.
+template <typename SymType>
+static bool IsFunc(const SymType* entry) {
+ return entry->st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry->st_info) == STT_FUNC;
+}
+
+// Read symbol entry from memory and cache it so we don't have to read it again.
+template <typename SymType>
+inline __attribute__((__always_inline__)) const Symbols::Info* Symbols::ReadFuncInfo(
+ uint32_t symbol_index, Memory* elf_memory) {
+ auto it = symbols_.find(symbol_index);
+ if (it != symbols_.end()) {
+ return &it->second;
+ }
+ SymType sym;
+ if (!elf_memory->ReadFully(offset_ + symbol_index * entry_size_, &sym, sizeof(sym))) {
+ return nullptr;
+ }
+ if (!IsFunc(&sym)) {
+ // We need the address for binary search, but we don't want it to be matched.
+ sym.st_size = 0;
+ }
+ Info info{.addr = sym.st_value, .size = static_cast<uint32_t>(sym.st_size), .name = sym.st_name};
+ return &symbols_.emplace(symbol_index, info).first->second;
+}
+
+// Binary search the symbol table to find function containing the given address.
+// Without remap, the symbol table is assumed to be sorted and accessed directly.
+// If the symbol table is not sorted this method might fail but should not crash.
+// When the indices are remapped, they are guaranteed to be sorted by address.
+template <typename SymType, bool RemapIndices>
+const Symbols::Info* Symbols::BinarySearch(uint64_t addr, Memory* elf_memory) {
size_t first = 0;
- size_t last = symbols_.size();
+ size_t last = RemapIndices ? remap_->size() : count_;
while (first < last) {
size_t current = first + (last - first) / 2;
- const Info* info = &symbols_[current];
- if (addr < info->start_offset) {
+ size_t symbol_index = RemapIndices ? remap_.value()[current] : current;
+ const Info* info = ReadFuncInfo<SymType>(symbol_index, elf_memory);
+ if (info == nullptr) {
+ return nullptr;
+ }
+ if (addr < info->addr) {
last = current;
- } else if (addr < info->end_offset) {
+ } else if (addr < info->addr + info->size) {
return info;
} else {
first = current + 1;
@@ -54,64 +87,72 @@
return nullptr;
}
+// Create remapping table which allows us to access symbols as if they were sorted by address.
template <typename SymType>
-bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
- if (symbols_.size() != 0) {
- const Info* info = GetInfoFromCache(addr);
- if (info) {
- CHECK(addr >= info->start_offset && addr <= info->end_offset);
- *func_offset = addr - info->start_offset;
- return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
+void Symbols::BuildRemapTable(Memory* elf_memory) {
+ std::vector<uint64_t> addrs; // Addresses of all symbols (addrs[i] == symbols[i].st_value).
+ addrs.reserve(count_);
+ remap_.emplace(); // Construct the optional remap table.
+ remap_->reserve(count_);
+ for (size_t symbol_idx = 0; symbol_idx < count_;) {
+ // Read symbols from memory. We intentionally bypass the cache to save memory.
+ // Do the reads in batches so that we minimize the number of memory read calls.
+ uint8_t buffer[1024];
+ size_t read = std::min<size_t>(sizeof(buffer), (count_ - symbol_idx) * entry_size_);
+ size_t size = elf_memory->Read(offset_ + symbol_idx * entry_size_, buffer, read);
+ if (size < sizeof(SymType)) {
+ break; // Stop processing, something looks like it is corrupted.
}
- }
-
- bool symbol_added = false;
- bool return_value = false;
- while (cur_offset_ + entry_size_ <= end_) {
- SymType entry;
- if (!elf_memory->ReadFully(cur_offset_, &entry, sizeof(entry))) {
- // Stop all processing, something looks like it is corrupted.
- cur_offset_ = UINT64_MAX;
- return false;
- }
- cur_offset_ += entry_size_;
-
- if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
- // Treat st_value as virtual address.
- uint64_t start_offset = entry.st_value;
- uint64_t end_offset = start_offset + entry.st_size;
-
- // Cache the value.
- symbols_.emplace_back(start_offset, end_offset, str_offset_ + entry.st_name);
- symbol_added = true;
-
- if (addr >= start_offset && addr < end_offset) {
- *func_offset = addr - start_offset;
- uint64_t offset = str_offset_ + entry.st_name;
- if (offset < str_end_) {
- return_value = elf_memory->ReadString(offset, name, str_end_ - offset);
- }
- break;
+ for (size_t offset = 0; offset + sizeof(SymType) <= size; offset += entry_size_, symbol_idx++) {
+ SymType sym;
+ memcpy(&sym, &buffer[offset], sizeof(SymType)); // Copy to ensure alignment.
+ addrs.push_back(sym.st_value); // Always insert so it is indexable by symbol index.
+ if (IsFunc(&sym)) {
+ remap_->push_back(symbol_idx); // Indices of function symbols only.
}
}
}
+ // Sort by address to make the remap list binary searchable (stable due to the a<b tie break).
+ auto comp = [&addrs](auto a, auto b) { return std::tie(addrs[a], a) < std::tie(addrs[b], b); };
+ std::sort(remap_->begin(), remap_->end(), comp);
+ // Remove duplicate entries (methods de-duplicated by the linker).
+ auto pred = [&addrs](auto a, auto b) { return addrs[a] == addrs[b]; };
+ remap_->erase(std::unique(remap_->begin(), remap_->end(), pred), remap_->end());
+ remap_->shrink_to_fit();
+}
- if (symbol_added) {
- std::sort(symbols_.begin(), symbols_.end(),
- [](const Info& a, const Info& b) { return a.start_offset < b.start_offset; });
+template <typename SymType>
+bool Symbols::GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset) {
+ const Info* info;
+ if (!remap_.has_value()) {
+ // Assume the symbol table is sorted. If it is not, this will gracefully fail.
+ info = BinarySearch<SymType, false>(addr, elf_memory);
+ if (info == nullptr) {
+ // Create the remapping table and retry the search.
+ BuildRemapTable<SymType>(elf_memory);
+ symbols_.clear(); // Remove cached symbols since the access pattern will be different.
+ info = BinarySearch<SymType, true>(addr, elf_memory);
+ }
+ } else {
+ // Fast search using the previously created remap table.
+ info = BinarySearch<SymType, true>(addr, elf_memory);
}
- return return_value;
+ if (info == nullptr) {
+ return false;
+ }
+ // Read the function name from the string table.
+ *func_offset = addr - info->addr;
+ uint64_t str = str_offset_ + info->name;
+ return str < str_end_ && elf_memory->ReadString(str, name, str_end_ - str);
}
template <typename SymType>
bool Symbols::GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address) {
- uint64_t cur_offset = offset_;
- while (cur_offset + entry_size_ <= end_) {
+ for (uint32_t i = 0; i < count_; i++) {
SymType entry;
- if (!elf_memory->ReadFully(cur_offset, &entry, sizeof(entry))) {
+ if (!elf_memory->ReadFully(offset_ + i * entry_size_, &entry, sizeof(entry))) {
return false;
}
- cur_offset += entry_size_;
if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_OBJECT &&
ELF32_ST_BIND(entry.st_info) == STB_GLOBAL) {
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 7fcd067..3b3f20b 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -19,8 +19,9 @@
#include <stdint.h>
+#include <optional>
#include <string>
-#include <vector>
+#include <unordered_map>
namespace unwindstack {
@@ -29,11 +30,9 @@
class Symbols {
struct Info {
- Info(uint64_t start_offset, uint64_t end_offset, uint64_t str_offset)
- : start_offset(start_offset), end_offset(end_offset), str_offset(str_offset) {}
- uint64_t start_offset;
- uint64_t end_offset;
- uint64_t str_offset;
+ uint64_t addr; // Symbol address.
+ uint32_t size; // Symbol size in bytes. Zero if not a function.
+ uint32_t name; // Offset in .strtab.
};
public:
@@ -41,8 +40,6 @@
uint64_t str_size);
virtual ~Symbols() = default;
- const Info* GetInfoFromCache(uint64_t addr);
-
template <typename SymType>
bool GetName(uint64_t addr, Memory* elf_memory, std::string* name, uint64_t* func_offset);
@@ -51,18 +48,27 @@
void ClearCache() {
symbols_.clear();
- cur_offset_ = offset_;
+ remap_.reset();
}
private:
- uint64_t cur_offset_;
- uint64_t offset_;
- uint64_t end_;
- uint64_t entry_size_;
- uint64_t str_offset_;
- uint64_t str_end_;
+ template <typename SymType>
+ const Info* ReadFuncInfo(uint32_t symbol_index, Memory* elf_memory);
- std::vector<Info> symbols_;
+ template <typename SymType, bool RemapIndices>
+ const Info* BinarySearch(uint64_t addr, Memory* elf_memory);
+
+ template <typename SymType>
+ void BuildRemapTable(Memory* elf_memory);
+
+ const uint64_t offset_;
+ const uint64_t count_;
+ const uint64_t entry_size_;
+ const uint64_t str_offset_;
+ const uint64_t str_end_;
+
+ std::unordered_map<uint32_t, Info> symbols_; // Cache of read symbols (keyed by symbol index).
+ std::optional<std::vector<uint32_t>> remap_; // Indices of function symbols sorted by address.
};
} // namespace unwindstack
diff --git a/libunwindstack/benchmarks/ElfBenchmark.cpp b/libunwindstack/benchmarks/ElfBenchmark.cpp
new file mode 100644
index 0000000..c108a2a
--- /dev/null
+++ b/libunwindstack/benchmarks/ElfBenchmark.cpp
@@ -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.
+ */
+
+#include <err.h>
+#include <malloc.h>
+#include <stdint.h>
+
+#include <string>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+#include "Utils.h"
+
+static void BenchmarkElfCreate(benchmark::State& state, const std::string& elf_file) {
+#if defined(__BIONIC__)
+ uint64_t rss_bytes = 0;
+#endif
+ uint64_t alloc_bytes = 0;
+ for (auto _ : state) {
+ state.PauseTiming();
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+ uint64_t rss_bytes_before = 0;
+ GatherRss(&rss_bytes_before);
+#endif
+ uint64_t alloc_bytes_before = mallinfo().uordblks;
+ auto file_memory = unwindstack::Memory::CreateFileMemory(elf_file, 0);
+ state.ResumeTiming();
+
+ unwindstack::Elf elf(file_memory.release());
+ if (!elf.Init() || !elf.valid()) {
+ errx(1, "Internal Error: Cannot open elf.");
+ }
+
+ state.PauseTiming();
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+#endif
+ alloc_bytes += mallinfo().uordblks - alloc_bytes_before;
+#if defined(__BIONIC__)
+ GatherRss(&rss_bytes);
+ rss_bytes -= rss_bytes_before;
+#endif
+ state.ResumeTiming();
+ }
+
+#if defined(__BIONIC__)
+ state.counters["RSS_BYTES"] = rss_bytes / static_cast<double>(state.iterations());
+#endif
+ state.counters["ALLOCATED_BYTES"] = alloc_bytes / static_cast<double>(state.iterations());
+}
+
+void BM_elf_create(benchmark::State& state) {
+ BenchmarkElfCreate(state, GetElfFile());
+}
+BENCHMARK(BM_elf_create);
+
+void BM_elf_create_compressed(benchmark::State& state) {
+ BenchmarkElfCreate(state, GetCompressedElfFile());
+}
+BENCHMARK(BM_elf_create_compressed);
diff --git a/libunwindstack/benchmarks/SymbolBenchmark.cpp b/libunwindstack/benchmarks/SymbolBenchmark.cpp
new file mode 100644
index 0000000..73088da
--- /dev/null
+++ b/libunwindstack/benchmarks/SymbolBenchmark.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 <err.h>
+#include <inttypes.h>
+#include <malloc.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+#include "Utils.h"
+
+static void BenchmarkSymbolLookup(benchmark::State& state, std::vector<uint64_t> offsets,
+ std::string elf_file, bool expect_found) {
+#if defined(__BIONIC__)
+ uint64_t rss_bytes = 0;
+#endif
+ uint64_t alloc_bytes = 0;
+ for (auto _ : state) {
+ state.PauseTiming();
+ unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(elf_file, 0).release());
+ if (!elf.Init() || !elf.valid()) {
+ errx(1, "Internal Error: Cannot open elf.");
+ }
+
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+ uint64_t rss_bytes_before = 0;
+ GatherRss(&rss_bytes_before);
+#endif
+ uint64_t alloc_bytes_before = mallinfo().uordblks;
+ state.ResumeTiming();
+
+ for (auto pc : offsets) {
+ std::string name;
+ uint64_t offset;
+ bool found = elf.GetFunctionName(pc, &name, &offset);
+ if (expect_found && !found) {
+ errx(1, "expected pc 0x%" PRIx64 " present, but not found.", pc);
+ } else if (!expect_found && found) {
+ errx(1, "expected pc 0x%" PRIx64 " not present, but found.", pc);
+ }
+ }
+
+ state.PauseTiming();
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+#endif
+ alloc_bytes += mallinfo().uordblks - alloc_bytes_before;
+#if defined(__BIONIC__)
+ GatherRss(&rss_bytes);
+ rss_bytes -= rss_bytes_before;
+#endif
+ state.ResumeTiming();
+ }
+
+#if defined(__BIONIC__)
+ state.counters["RSS_BYTES"] = rss_bytes / static_cast<double>(state.iterations());
+#endif
+ state.counters["ALLOCATED_BYTES"] = alloc_bytes / static_cast<double>(state.iterations());
+}
+
+static void BenchmarkSymbolLookup(benchmark::State& state, uint64_t pc, std::string elf_file,
+ bool expect_found) {
+ BenchmarkSymbolLookup(state, std::vector<uint64_t>{pc}, elf_file, expect_found);
+}
+
+void BM_symbol_not_present(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0, GetElfFile(), false);
+}
+BENCHMARK(BM_symbol_not_present);
+
+void BM_symbol_find_single(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0x22b2bc, GetElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single);
+
+void BM_symbol_find_single_many_times(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, std::vector<uint64_t>(15, 0x22b2bc), GetElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single_many_times);
+
+void BM_symbol_find_multiple(benchmark::State& state) {
+ BenchmarkSymbolLookup(state,
+ std::vector<uint64_t>{0x22b2bc, 0xd5d30, 0x1312e8, 0x13582e, 0x1389c8},
+ GetElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_multiple);
+
+void BM_symbol_not_present_from_sorted(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0, GetSymbolSortedElfFile(), false);
+}
+BENCHMARK(BM_symbol_not_present_from_sorted);
+
+void BM_symbol_find_single_from_sorted(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0x138638, GetSymbolSortedElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single_from_sorted);
+
+void BM_symbol_find_single_many_times_from_sorted(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, std::vector<uint64_t>(15, 0x138638), GetSymbolSortedElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_single_many_times_from_sorted);
+
+void BM_symbol_find_multiple_from_sorted(benchmark::State& state) {
+ BenchmarkSymbolLookup(state,
+ std::vector<uint64_t>{0x138638, 0x84350, 0x14df18, 0x1f3a38, 0x1f3ca8},
+ GetSymbolSortedElfFile(), true);
+}
+BENCHMARK(BM_symbol_find_multiple_from_sorted);
diff --git a/libunwindstack/benchmarks/Utils.cpp b/libunwindstack/benchmarks/Utils.cpp
new file mode 100644
index 0000000..c92f109
--- /dev/null
+++ b/libunwindstack/benchmarks/Utils.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 <err.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
+
+std::string GetElfFile() {
+ return android::base::GetExecutableDirectory() + "/benchmarks/files/libart_arm.so";
+}
+
+std::string GetSymbolSortedElfFile() {
+ return android::base::GetExecutableDirectory() + "/benchmarks/files/boot_arm.oat";
+}
+
+std::string GetCompressedElfFile() {
+ // Both are the same right now.
+ return GetSymbolSortedElfFile();
+}
+
+#if defined(__BIONIC__)
+
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process_map.h>
+
+void GatherRss(uint64_t* rss_bytes) {
+ android::meminfo::ProcMemInfo proc_mem(getpid());
+ const std::vector<android::meminfo::Vma>& maps = proc_mem.MapsWithoutUsageStats();
+ for (auto& vma : maps) {
+ if (vma.name == "[anon:libc_malloc]" || android::base::StartsWith(vma.name, "[anon:scudo:") ||
+ android::base::StartsWith(vma.name, "[anon:GWP-ASan")) {
+ android::meminfo::Vma update_vma(vma);
+ if (!proc_mem.FillInVmaStats(update_vma)) {
+ err(1, "FillInVmaStats failed\n");
+ }
+ *rss_bytes += update_vma.usage.rss;
+ }
+ }
+}
+#endif
diff --git a/libunwindstack/benchmarks/Utils.h b/libunwindstack/benchmarks/Utils.h
new file mode 100644
index 0000000..bee6efc
--- /dev/null
+++ b/libunwindstack/benchmarks/Utils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_UTILS_H
+#define _LIBUNWINDSTACK_UTILS_H
+
+#include <stdint.h>
+
+#include <string>
+
+std::string GetElfFile();
+
+std::string GetSymbolSortedElfFile();
+
+std::string GetCompressedElfFile();
+
+#if defined(__BIONIC__)
+
+#include <meminfo/procmeminfo.h>
+#include <procinfo/process_map.h>
+
+void GatherRss(uint64_t* rss_bytes);
+
+#endif
+
+#endif // _LIBUNWINDSTACK_UTILS_h
diff --git a/libunwindstack/benchmarks/files/boot_arm.oat b/libunwindstack/benchmarks/files/boot_arm.oat
new file mode 100644
index 0000000..51188eb
--- /dev/null
+++ b/libunwindstack/benchmarks/files/boot_arm.oat
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libart_arm.so b/libunwindstack/benchmarks/files/libart_arm.so
new file mode 100644
index 0000000..2201faf
--- /dev/null
+++ b/libunwindstack/benchmarks/files/libart_arm.so
Binary files differ
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index ecd908a..3d81878 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -37,13 +37,14 @@
uint64_t end);
static std::unique_ptr<Memory> CreateFileMemory(const std::string& path, uint64_t offset);
- virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
+ virtual bool ReadString(uint64_t addr, std::string* dst, size_t max_read);
virtual void Clear() {}
virtual bool IsLocal() const { return false; }
virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
+ virtual long ReadTag(uint64_t) { return -1; }
bool ReadFully(uint64_t addr, void* dst, size_t size);
diff --git a/libunwindstack/tests/LocalUpdatableMapsTest.cpp b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
index b816b9a..99afb0b 100644
--- a/libunwindstack/tests/LocalUpdatableMapsTest.cpp
+++ b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
@@ -271,4 +271,103 @@
EXPECT_TRUE(map_info->name.empty());
}
+TEST_F(LocalUpdatableMapsTest, add_map_prev_name_updated) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 rwxp 00000 00:00 0\n"
+ "8000-9000 r-xp 00000 00:00 0\n"
+ "9000-a000 r-xp 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(3U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x9000U, map_info->start);
+ EXPECT_EQ(0xA000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_TRUE(map_info->name.empty());
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+}
+
+TEST_F(LocalUpdatableMapsTest, add_map_prev_real_name_updated) {
+ TemporaryFile tf;
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+ "4000-5000 ---p 00000 00:00 0\n"
+ "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+ "8000-9000 ---p 00000 00:00 0\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(4U, maps_.Total());
+
+ MapInfo* map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x7000U, map_info->start);
+ EXPECT_EQ(0x8000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ(maps_.Get(0), map_info->prev_real_map);
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+ EXPECT_EQ("/fake/lib1.so", map_info->name);
+
+ map_info = maps_.Get(3);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x8000U, map_info->start);
+ EXPECT_EQ(0x9000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_TRUE(map_info->IsBlank());
+ EXPECT_EQ(maps_.Get(2), map_info->prev_real_map);
+ EXPECT_EQ(maps_.Get(2), map_info->prev_map);
+ EXPECT_TRUE(map_info->name.empty());
+
+ ASSERT_TRUE(
+ android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
+ "4000-5000 ---p 00000 00:00 0\n"
+ "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+ "8000-9000 ---p 00000 00:00 0\n"
+ "9000-a000 r-xp 00000 00:00 0 /fake/lib2.so\n"
+ "a000-b000 r-xp 00000 00:00 0 /fake/lib3.so\n",
+ tf.path));
+
+ maps_.TestSetMapsFile(tf.path);
+ ASSERT_TRUE(maps_.Reparse());
+ ASSERT_EQ(6U, maps_.Total());
+
+ map_info = maps_.Get(2);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x7000U, map_info->start);
+ EXPECT_EQ(0x8000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib1.so", map_info->name);
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(0), map_info->prev_real_map);
+
+ map_info = maps_.Get(4);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0x9000U, map_info->start);
+ EXPECT_EQ(0xA000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib2.so", map_info->name);
+ EXPECT_EQ(maps_.Get(3), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(2), map_info->prev_real_map);
+
+ map_info = maps_.Get(5);
+ ASSERT_TRUE(map_info != nullptr);
+ EXPECT_EQ(0xA000U, map_info->start);
+ EXPECT_EQ(0xB000U, map_info->end);
+ EXPECT_EQ(0U, map_info->offset);
+ EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
+ EXPECT_EQ("/fake/lib3.so", map_info->name);
+ EXPECT_EQ(maps_.Get(4), map_info->prev_map);
+ EXPECT_EQ(maps_.Get(4), map_info->prev_real_map);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryMteTest.cpp b/libunwindstack/tests/MemoryMteTest.cpp
new file mode 100644
index 0000000..3ae322e
--- /dev/null
+++ b/libunwindstack/tests/MemoryMteTest.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <gtest/gtest.h>
+
+#include <bionic/mte.h>
+
+#include "MemoryLocal.h"
+#include "MemoryRemote.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+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;
+ }
+#if defined(__aarch64__)
+ __asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
+ :
+ : "r"(mapping + (1ULL << 56))
+ : "memory");
+#endif
+ return mapping;
+}
+
+TEST(MemoryMteTest, remote_read_tag) {
+#if !defined(__aarch64__)
+ GTEST_SKIP() << "Requires aarch64";
+#else
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+#endif
+
+ uintptr_t mapping = CreateTagMapping();
+ ASSERT_NE(0U, mapping);
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true)
+ ;
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(TestAttach(pid));
+
+ MemoryRemote remote(pid);
+
+ EXPECT_EQ(1, remote.ReadTag(mapping));
+ EXPECT_EQ(0, remote.ReadTag(mapping + 16));
+
+ ASSERT_TRUE(TestDetach(pid));
+}
+
+TEST(MemoryMteTest, local_read_tag) {
+#if !defined(__aarch64__)
+ GTEST_SKIP() << "Requires aarch64";
+#else
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+#endif
+
+ uintptr_t mapping = CreateTagMapping();
+ ASSERT_NE(0U, mapping);
+
+ MemoryLocal local;
+
+ EXPECT_EQ(1, local.ReadTag(mapping));
+ EXPECT_EQ(0, local.ReadTag(mapping + 16));
+}
+
+} // namespace unwindstack
+
+#endif
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index c90dedc..621893b 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -26,8 +26,8 @@
#include <vector>
-#include <android-base/test_utils.h>
#include <android-base/file.h>
+#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "MemoryRemote.h"
@@ -37,24 +37,7 @@
namespace unwindstack {
-class MemoryRemoteTest : public ::testing::Test {
- protected:
- static bool Attach(pid_t pid) {
- if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
- return false;
- }
-
- return TestQuiescePid(pid);
- }
-
- static bool Detach(pid_t pid) {
- return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
- }
-
- static constexpr size_t NS_PER_SEC = 1000000000ULL;
-};
-
-TEST_F(MemoryRemoteTest, read) {
+TEST(MemoryRemoteTest, read) {
std::vector<uint8_t> src(1024);
memset(src.data(), 0x4c, 1024);
@@ -66,7 +49,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -76,10 +59,10 @@
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_large) {
+TEST(MemoryRemoteTest, read_large) {
static constexpr size_t kTotalPages = 245;
std::vector<uint8_t> src(kTotalPages * getpagesize());
for (size_t i = 0; i < kTotalPages; i++) {
@@ -95,7 +78,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -105,10 +88,10 @@
ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
}
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_partial) {
+TEST(MemoryRemoteTest, read_partial) {
char* mapping = static_cast<char*>(
mmap(nullptr, 4 * getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_NE(MAP_FAILED, mapping);
@@ -128,7 +111,7 @@
// Unmap from our process.
ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -149,10 +132,10 @@
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_fail) {
+TEST(MemoryRemoteTest, read_fail) {
int pagesize = getpagesize();
void* src = mmap(nullptr, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,-1, 0);
memset(src, 0x4c, pagesize * 2);
@@ -169,7 +152,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -188,10 +171,10 @@
ASSERT_EQ(0, munmap(src, pagesize));
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_overflow) {
+TEST(MemoryRemoteTest, read_overflow) {
pid_t pid;
if ((pid = fork()) == 0) {
while (true)
@@ -201,7 +184,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -209,10 +192,10 @@
std::vector<uint8_t> dst(200);
ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_illegal) {
+TEST(MemoryRemoteTest, read_illegal) {
pid_t pid;
if ((pid = fork()) == 0) {
while (true);
@@ -221,7 +204,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -229,10 +212,10 @@
ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
- ASSERT_TRUE(Detach(pid));
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_mprotect_hole) {
+TEST(MemoryRemoteTest, read_mprotect_hole) {
size_t page_size = getpagesize();
void* mapping =
mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -250,7 +233,7 @@
ASSERT_EQ(0, munmap(mapping, 3 * page_size));
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -263,9 +246,11 @@
for (size_t i = read_size; i < dst.size(); ++i) {
ASSERT_EQ(0xCC, dst[i]);
}
+
+ ASSERT_TRUE(TestDetach(pid));
}
-TEST_F(MemoryRemoteTest, read_munmap_hole) {
+TEST(MemoryRemoteTest, read_munmap_hole) {
size_t page_size = getpagesize();
void* mapping =
mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -285,7 +270,7 @@
ASSERT_EQ(0, munmap(mapping, page_size));
ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -297,11 +282,13 @@
for (size_t i = read_size; i < dst.size(); ++i) {
ASSERT_EQ(0xCC, dst[i]);
}
+
+ ASSERT_TRUE(TestDetach(pid));
}
// Verify that the memory remote object chooses a memory read function
// properly. Either process_vm_readv or ptrace.
-TEST_F(MemoryRemoteTest, read_choose_correctly) {
+TEST(MemoryRemoteTest, read_choose_correctly) {
size_t page_size = getpagesize();
void* mapping =
mmap(nullptr, 2 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
@@ -320,7 +307,7 @@
ASSERT_EQ(0, munmap(mapping, 2 * page_size));
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
// We know that process_vm_readv of a mprotect'd PROT_NONE region will fail.
// Read from the PROT_NONE area first to force the choice of ptrace.
@@ -348,6 +335,8 @@
bytes = remote_readv.Read(reinterpret_cast<uint64_t>(mapping) + page_size, &value, sizeof(value));
ASSERT_EQ(sizeof(value), bytes);
ASSERT_EQ(0xfcfcfcfcU, value);
+
+ ASSERT_TRUE(TestDetach(pid));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 3655984..8a8eb24 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -59,10 +59,10 @@
memory.SetMemory(100, name.c_str(), name.size() + 1);
std::string dst_name;
- ASSERT_TRUE(memory.ReadString(100, &dst_name));
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 100));
ASSERT_EQ("string_in_memory", dst_name);
- ASSERT_TRUE(memory.ReadString(107, &dst_name));
+ ASSERT_TRUE(memory.ReadString(107, &dst_name, 100));
ASSERT_EQ("in_memory", dst_name);
// Set size greater than string.
@@ -82,15 +82,56 @@
std::string dst_name;
// Read from a non-existant address.
- ASSERT_FALSE(memory.ReadString(100, &dst_name));
+ ASSERT_FALSE(memory.ReadString(100, &dst_name, 100));
// This should fail because there is no terminating '\0'.
- ASSERT_FALSE(memory.ReadString(0, &dst_name));
+ ASSERT_FALSE(memory.ReadString(0, &dst_name, 100));
// This should pass because there is a terminating '\0'.
memory.SetData8(name.size(), '\0');
- ASSERT_TRUE(memory.ReadString(0, &dst_name));
+ ASSERT_TRUE(memory.ReadString(0, &dst_name, 100));
ASSERT_EQ("short", dst_name);
}
+TEST(MemoryTest, read_string_long) {
+ // This string should be greater than 768 characters long (greater than 3 times
+ // the buffer in the ReadString function) to read multiple blocks.
+ static constexpr char kLongString[] =
+ "one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen "
+ "sixteen seventeen eightteen nineteen twenty twenty-one twenty-two twenty-three twenty-four "
+ "twenty-five twenty-six twenty-seven twenty-eight twenty-nine thirty thirty-one thirty-two "
+ "thirty-three thirty-four thirty-five thirty-six thirty-seven thirty-eight thirty-nine forty "
+ "forty-one forty-two forty-three forty-four forty-five forty-size forty-seven forty-eight "
+ "forty-nine fifty fifty-one fifty-two fifty-three fifty-four fifty-five fifty-six "
+ "fifty-seven fifty-eight fifty-nine sixty sixty-one sixty-two sixty-three sixty-four "
+ "sixty-five sixty-six sixty-seven sixty-eight sixty-nine seventy seventy-one seventy-two "
+ "seventy-three seventy-four seventy-five seventy-six seventy-seven seventy-eight "
+ "seventy-nine eighty";
+
+ MemoryFake memory;
+
+ memory.SetMemory(100, kLongString, sizeof(kLongString));
+
+ std::string dst_name;
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, sizeof(kLongString)));
+ ASSERT_EQ(kLongString, dst_name);
+
+ std::string expected_str(kLongString, 255);
+ memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 256));
+ ASSERT_EQ(expected_str, dst_name);
+ ASSERT_FALSE(memory.ReadString(100, &dst_name, 255));
+
+ expected_str = std::string(kLongString, 256);
+ memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 257));
+ ASSERT_EQ(expected_str, dst_name);
+ ASSERT_FALSE(memory.ReadString(100, &dst_name, 256));
+
+ expected_str = std::string(kLongString, 299);
+ memory.SetMemory(100, expected_str.data(), expected_str.length() + 1);
+ ASSERT_TRUE(memory.ReadString(100, &dst_name, 300));
+ ASSERT_EQ(expected_str, dst_name);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index c58aeff..9afbeec 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -185,18 +185,21 @@
std::string fake_name;
this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemoryBlock(offset, entry_size, 0);
this->memory_.SetMemory(offset, &sym, sizeof(sym));
fake_name = "function_one";
this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
offset += entry_size;
this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemoryBlock(offset, entry_size, 0);
this->memory_.SetMemory(offset, &sym, sizeof(sym));
fake_name = "function_two";
this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
offset += entry_size;
this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemoryBlock(offset, entry_size, 0);
this->memory_.SetMemory(offset, &sym, sizeof(sym));
fake_name = "function_three";
this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
@@ -274,7 +277,9 @@
// Do call that should cache all of the entries (except the string data).
std::string name;
uint64_t func_offset;
- ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x2000, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x1000, &this->memory_, &name, &func_offset));
this->memory_.Clear();
ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, &this->memory_, &name, &func_offset));
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index a4d7b9b..0685006 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -21,6 +21,7 @@
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
namespace unwindstack {
@@ -50,6 +51,18 @@
return ready;
}
+inline bool TestAttach(pid_t pid) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ return TestQuiescePid(pid);
+}
+
+inline bool TestDetach(pid_t pid) {
+ return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+}
+
void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
} // namespace unwindstack
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index c439c5c..466fbb7 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -26,6 +26,8 @@
#include <android-base/unique_fd.h>
+#include <utility>
+
namespace android {
/*
@@ -438,9 +440,8 @@
struct MessageEnvelope {
MessageEnvelope() : uptime(0) { }
- MessageEnvelope(nsecs_t u, const sp<MessageHandler> h,
- const Message& m) : uptime(u), handler(h), message(m) {
- }
+ MessageEnvelope(nsecs_t u, sp<MessageHandler> h, const Message& m)
+ : uptime(u), handler(std::move(h)), message(m) {}
nsecs_t uptime;
sp<MessageHandler> handler;
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 3d51de9..005d697 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -48,9 +48,10 @@
// Modification time. The zipfile format specifies
// that the first two little endian bytes contain the time
// and the last two little endian bytes contain the date.
- // See `GetModificationTime`.
+ // See `GetModificationTime`. Use signed integer to avoid the
+ // sub-overflow.
// TODO: should be overridden by extra time field, if present.
- uint32_t mod_time;
+ int32_t mod_time;
// Returns `mod_time` as a broken-down struct tm.
struct tm GetModificationTime() const;
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 19f95d4..014f881 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -137,6 +137,19 @@
uint64_t cd_start_offset;
};
+// Reads |T| at |readPtr| and increments |readPtr|. Returns std::nullopt if the boundary check
+// fails.
+template <typename T>
+static std::optional<T> TryConsumeUnaligned(uint8_t** readPtr, const uint8_t* bufStart,
+ size_t bufSize) {
+ if (bufSize < sizeof(T) || *readPtr - bufStart > bufSize - sizeof(T)) {
+ ALOGW("Zip: %zu byte read exceeds the boundary of allocated buf, offset %zu, bufSize %zu",
+ sizeof(T), *readPtr - bufStart, bufSize);
+ return std::nullopt;
+ }
+ return ConsumeUnaligned<T>(readPtr);
+}
+
static ZipError FindCentralDirectoryInfoForZip64(const char* debugFileName, ZipArchive* archive,
off64_t eocdOffset, CentralDirectoryInfo* cdInfo) {
if (eocdOffset <= sizeof(Zip64EocdLocator)) {
@@ -379,13 +392,19 @@
std::optional<uint64_t> compressedFileSize;
std::optional<uint64_t> localHeaderOffset;
if (zip32UncompressedSize == UINT32_MAX) {
- uncompressedFileSize = ConsumeUnaligned<uint64_t>(&readPtr);
+ uncompressedFileSize =
+ TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
+ if (!uncompressedFileSize.has_value()) return kInvalidOffset;
}
if (zip32CompressedSize == UINT32_MAX) {
- compressedFileSize = ConsumeUnaligned<uint64_t>(&readPtr);
+ compressedFileSize =
+ TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
+ if (!compressedFileSize.has_value()) return kInvalidOffset;
}
if (zip32LocalFileHeaderOffset == UINT32_MAX) {
- localHeaderOffset = ConsumeUnaligned<uint64_t>(&readPtr);
+ localHeaderOffset =
+ TryConsumeUnaligned<uint64_t>(&readPtr, extraFieldStart, extraFieldLength);
+ if (!localHeaderOffset.has_value()) return kInvalidOffset;
}
// calculate how many bytes we read after the data size field.
@@ -606,6 +625,10 @@
static int32_t ValidateDataDescriptor(MappedZipFile& mapped_zip, const ZipEntry64* entry) {
// Maximum possible size for data descriptor: 2 * 4 + 2 * 8 = 24 bytes
+ // The zip format doesn't specify the size of data descriptor. But we won't read OOB here even
+ // if the descriptor isn't present. Because the size cd + eocd in the end of the zipfile is
+ // larger than 24 bytes. And if the descriptor contains invalid data, we'll abort due to
+ // kInconsistentInformation.
uint8_t ddBuf[24];
off64_t offset = entry->offset;
if (entry->method != kCompressStored) {
@@ -1499,8 +1522,9 @@
return false;
}
} else {
- if (off < 0 || off > data_length_) {
- ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64, off, data_length_);
+ if (off < 0 || data_length_ < len || off > data_length_ - len) {
+ ALOGE("Zip: invalid offset: %" PRId64 ", read length: %zu, data length: %" PRId64, off, len,
+ data_length_);
return false;
}
memcpy(buf, static_cast<const uint8_t*>(base_ptr_) + off, len);
@@ -1546,6 +1570,7 @@
return true;
}
+// This function returns the embedded timestamp as is; and doesn't perform validations.
tm ZipEntryCommon::GetModificationTime() const {
tm t = {};
diff --git a/libziparchive/ziptool.cpp b/libziparchive/ziptool.cpp
index 17d4833..a261535 100644
--- a/libziparchive/ziptool.cpp
+++ b/libziparchive/ziptool.cpp
@@ -263,12 +263,12 @@
snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
t.tm_mday, t.tm_hour, t.tm_min);
if (flag_v) {
- printf("%8" PRIu64 " %s %8" PRIu64 " %3.0f%% %s %08x %s\n", entry.uncompressed_length,
+ printf("%8" PRIu64 " %s %8" PRIu64 " %3.0f%% %s %08x %s\n", entry.uncompressed_length,
(entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length,
CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32,
name.c_str());
} else {
- printf("%9" PRIu64 " %s %s\n", entry.uncompressed_length, time, name.c_str());
+ printf("%9" PRIu64 " %s %s\n", entry.uncompressed_length, time, name.c_str());
}
}
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 1c3acb8..8ad9900 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -41,6 +41,7 @@
#include <string>
#include <unordered_map>
#include <unordered_set>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -1204,9 +1205,19 @@
}
}
// We are here because we have confirmed kernel live-lock
+ std::vector<std::string> threads;
+ auto taskdir = procdir + std::to_string(tid) + "/task/";
+ dir taskDirectory(taskdir);
+ for (auto tp = taskDirectory.read(); tp != nullptr; tp = taskDirectory.read()) {
+ std::string piddir;
+ if (getValidTidDir(tp, &piddir))
+ threads.push_back(android::base::Basename(piddir));
+ }
const auto message = state + " "s + llkFormat(procp->count) + " " +
std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
- std::to_string(tid) + " " + process_comm + " [panic]";
+ std::to_string(tid) + " " + process_comm + " [panic]\n" +
+ " thread group: {" + android::base::Join(threads, ",") +
+ "}";
llkPanicKernel(dump, tid,
(state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
message);
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 3a1d36f..56a670a 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -145,6 +145,8 @@
# libcore failure logging
90100 exp_det_cert_pin_failure (certs|4)
+# 150000 - 160000 reserved for Android Automotive builds
+
1397638484 snet_event_log (subtag|3) (uid|1) (message|3)
# for events that go to stats log buffer
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index b065855..8185f01 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -50,6 +50,7 @@
#include <android/log.h>
#include <log/event_tag_map.h>
#include <log/log_id.h>
+#include <log/log_read.h>
#include <log/logprint.h>
#include <private/android_logger.h>
#include <processgroup/sched_policy.h>
@@ -122,6 +123,18 @@
return fd;
}
+static void closeLogFile(const char* pathname) {
+ int fd = open(pathname, O_WRONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return;
+ }
+
+ // no need to check errors
+ __u32 set = 0;
+ ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
+ close(fd);
+}
+
void Logcat::RotateLogs() {
// Can't rotate logs if we're not outputting to a file
if (!output_file_name_) return;
@@ -152,6 +165,8 @@
break;
}
+ closeLogFile(file0.c_str());
+
int err = rename(file0.c_str(), file1.c_str());
if (err < 0 && errno != ENOENT) {
diff --git a/logd/Android.bp b/logd/Android.bp
index b337b7c..2663271 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -36,7 +36,6 @@
"CommandListener.cpp",
"LogListener.cpp",
"LogReader.cpp",
- "FlushCommand.cpp",
"LogBuffer.cpp",
"LogBufferElement.cpp",
"LogTimes.cpp",
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 694b5fa..4044dc9 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -31,6 +31,7 @@
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
+#include <log/log_properties.h>
#include <private/android_filesystem_config.h>
#include <sysutils/SocketClient.h>
@@ -38,37 +39,20 @@
#include "LogCommand.h"
#include "LogUtils.h"
-CommandListener::CommandListener(LogBuffer* buf, LogReader* /*reader*/,
- LogListener* /*swl*/)
- : FrameworkListener(getLogSocket()) {
- // registerCmd(new ShutdownCmd(buf, writer, swl));
- registerCmd(new ClearCmd(buf));
- registerCmd(new GetBufSizeCmd(buf));
- registerCmd(new SetBufSizeCmd(buf));
- registerCmd(new GetBufSizeUsedCmd(buf));
- registerCmd(new GetStatisticsCmd(buf));
- registerCmd(new SetPruneListCmd(buf));
- registerCmd(new GetPruneListCmd(buf));
- registerCmd(new GetEventTagCmd(buf));
- registerCmd(new ReinitCmd());
+CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune)
+ : FrameworkListener(getLogSocket()), buf_(buf), tags_(tags), prune_(prune) {
+ registerCmd(new ClearCmd(this));
+ registerCmd(new GetBufSizeCmd(this));
+ registerCmd(new SetBufSizeCmd(this));
+ registerCmd(new GetBufSizeUsedCmd(this));
+ registerCmd(new GetStatisticsCmd(this));
+ registerCmd(new SetPruneListCmd(this));
+ registerCmd(new GetPruneListCmd(this));
+ registerCmd(new GetEventTagCmd(this));
+ registerCmd(new ReinitCmd(this));
registerCmd(new ExitCmd(this));
}
-CommandListener::ShutdownCmd::ShutdownCmd(LogReader* reader, LogListener* swl)
- : LogCommand("shutdown"), mReader(*reader), mSwl(*swl) {
-}
-
-int CommandListener::ShutdownCmd::runCommand(SocketClient* /*cli*/,
- int /*argc*/, char** /*argv*/) {
- mSwl.stopListener();
- mReader.stopListener();
- exit(0);
-}
-
-CommandListener::ClearCmd::ClearCmd(LogBuffer* buf)
- : LogCommand("clear"), mBuf(*buf) {
-}
-
static void setname() {
static bool name_set;
if (!name_set) {
@@ -96,14 +80,10 @@
return 0;
}
- cli->sendMsg(mBuf.clear((log_id_t)id, uid) ? "busy" : "success");
+ cli->sendMsg(buf()->clear((log_id_t)id, uid) ? "busy" : "success");
return 0;
}
-CommandListener::GetBufSizeCmd::GetBufSizeCmd(LogBuffer* buf)
- : LogCommand("getLogSize"), mBuf(*buf) {
-}
-
int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -118,17 +98,13 @@
return 0;
}
- unsigned long size = mBuf.getSize((log_id_t)id);
+ unsigned long size = buf()->getSize((log_id_t)id);
char buf[512];
snprintf(buf, sizeof(buf), "%lu", size);
cli->sendMsg(buf);
return 0;
}
-CommandListener::SetBufSizeCmd::SetBufSizeCmd(LogBuffer* buf)
- : LogCommand("setLogSize"), mBuf(*buf) {
-}
-
int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -149,7 +125,7 @@
}
unsigned long size = atol(argv[2]);
- if (mBuf.setSize((log_id_t)id, size)) {
+ if (buf()->setSize((log_id_t)id, size)) {
cli->sendMsg("Range Error");
return 0;
}
@@ -158,10 +134,6 @@
return 0;
}
-CommandListener::GetBufSizeUsedCmd::GetBufSizeUsedCmd(LogBuffer* buf)
- : LogCommand("getLogSizeUsed"), mBuf(*buf) {
-}
-
int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -176,17 +148,13 @@
return 0;
}
- unsigned long size = mBuf.getSizeUsed((log_id_t)id);
+ unsigned long size = buf()->getSizeUsed((log_id_t)id);
char buf[512];
snprintf(buf, sizeof(buf), "%lu", size);
cli->sendMsg(buf);
return 0;
}
-CommandListener::GetStatisticsCmd::GetStatisticsCmd(LogBuffer* buf)
- : LogCommand("getStatistics"), mBuf(*buf) {
-}
-
// This returns a string with a length prefix with the format <length>\n<data>\n\f. The length
// prefix includes the length of the prefix itself.
static std::string PackageString(const std::string& str) {
@@ -241,25 +209,17 @@
}
}
- cli->sendMsg(PackageString(mBuf.formatStatistics(uid, pid, logMask)).c_str());
+ cli->sendMsg(PackageString(buf()->formatStatistics(uid, pid, logMask)).c_str());
return 0;
}
-CommandListener::GetPruneListCmd::GetPruneListCmd(LogBuffer* buf)
- : LogCommand("getPruneList"), mBuf(*buf) {
-}
-
int CommandListener::GetPruneListCmd::runCommand(SocketClient* cli,
int /*argc*/, char** /*argv*/) {
setname();
- cli->sendMsg(PackageString(mBuf.formatPrune()).c_str());
+ cli->sendMsg(PackageString(prune()->format()).c_str());
return 0;
}
-CommandListener::SetPruneListCmd::SetPruneListCmd(LogBuffer* buf)
- : LogCommand("setPruneList"), mBuf(*buf) {
-}
-
int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -276,7 +236,7 @@
str += argv[i];
}
- int ret = mBuf.initPrune(str.c_str());
+ int ret = prune()->init(str.c_str());
if (ret) {
cli->sendMsg("Invalid");
@@ -288,10 +248,6 @@
return 0;
}
-CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer* buf)
- : LogCommand("getEventTag"), mBuf(*buf) {
-}
-
int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
setname();
@@ -328,39 +284,45 @@
cli->sendMsg("can not mix id= with either format= or name=");
return 0;
}
- cli->sendMsg(PackageString(mBuf.formatEntry(atoi(id), uid)).c_str());
+ cli->sendMsg(PackageString(tags()->formatEntry(atoi(id), uid)).c_str());
return 0;
}
- cli->sendMsg(PackageString(mBuf.formatGetEventTag(uid, name, format)).c_str());
+ cli->sendMsg(PackageString(tags()->formatGetEventTag(uid, name, format)).c_str());
return 0;
}
-CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
-}
-
int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
char** /*argv*/) {
setname();
- reinit_signal_handler(SIGHUP);
+ android::prdebug("logd reinit");
+ buf()->init();
+ 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.
+ // The operation is near to boot and should only happen once. There
+ // are races associated with its use since it can trigger a Rebuild
+ // of the file, but that is a can-not-happen since the file was not
+ // read yet. More dangerous if called later, but if all is well it
+ // should just skip over everything and not write any new entries.
+ if (__android_log_is_debuggable()) {
+ tags()->ReadFileEventLogTags(tags()->debug_event_log_tags);
+ }
cli->sendMsg("success");
return 0;
}
-CommandListener::ExitCmd::ExitCmd(CommandListener* parent)
- : LogCommand("EXIT"), mParent(*parent) {
-}
-
int CommandListener::ExitCmd::runCommand(SocketClient* cli, int /*argc*/,
char** /*argv*/) {
setname();
cli->sendMsg("success");
- release(cli);
+ parent_->release(cli);
return 0;
}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index ed99419..c90c247 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -14,85 +14,53 @@
* limitations under the License.
*/
-#ifndef _COMMANDLISTENER_H__
-#define _COMMANDLISTENER_H__
+#pragma once
#include <sysutils/FrameworkListener.h>
+
#include "LogBuffer.h"
#include "LogCommand.h"
#include "LogListener.h"
#include "LogReader.h"
-
-// See main.cpp for implementation
-void reinit_signal_handler(int /*signal*/);
+#include "LogTags.h"
+#include "LogWhiteBlackList.h"
class CommandListener : public FrameworkListener {
- public:
- CommandListener(LogBuffer* buf, LogReader* reader, LogListener* swl);
- virtual ~CommandListener() {
- }
+ public:
+ CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune);
+ virtual ~CommandListener() {}
- private:
+ private:
static int getLogSocket();
- class ShutdownCmd : public LogCommand {
- LogReader& mReader;
- LogListener& mSwl;
+ LogBuffer* buf_;
+ LogTags* tags_;
+ PruneList* prune_;
- public:
- ShutdownCmd(LogReader* reader, LogListener* swl);
- virtual ~ShutdownCmd() {
- }
- int runCommand(SocketClient* c, int argc, char** argv);
- };
-
-#define LogBufferCmd(name) \
+#define LogCmd(name, command_string) \
class name##Cmd : public LogCommand { \
- LogBuffer& mBuf; \
+ public: \
+ explicit name##Cmd(CommandListener* parent) \
+ : LogCommand(#command_string), parent_(parent) {} \
+ virtual ~name##Cmd() {} \
+ int runCommand(SocketClient* c, int argc, char** argv); \
\
- public: \
- explicit name##Cmd(LogBuffer* buf); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
+ private: \
+ LogBuffer* buf() const { return parent_->buf_; } \
+ LogTags* tags() const { return parent_->tags_; } \
+ PruneList* prune() const { return parent_->prune_; } \
+ CommandListener* parent_; \
}
- LogBufferCmd(Clear);
- LogBufferCmd(GetBufSize);
- LogBufferCmd(SetBufSize);
- LogBufferCmd(GetBufSizeUsed);
- LogBufferCmd(GetStatistics);
- LogBufferCmd(GetPruneList);
- LogBufferCmd(SetPruneList);
- LogBufferCmd(GetEventTag);
-
-#define LogCmd(name) \
- class name##Cmd : public LogCommand { \
- public: \
- name##Cmd(); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
- }
-
- LogCmd(Reinit);
-
-#define LogParentCmd(name) \
- class name##Cmd : public LogCommand { \
- CommandListener& mParent; \
- \
- public: \
- name##Cmd(); \
- explicit name##Cmd(CommandListener* parent); \
- virtual ~name##Cmd() { \
- } \
- int runCommand(SocketClient* c, int argc, char** argv); \
- void release(SocketClient* c) { \
- mParent.release(c); \
- } \
- }
-
- LogParentCmd(Exit);
+ LogCmd(Clear, clear);
+ LogCmd(GetBufSize, getLogSize);
+ LogCmd(SetBufSize, setLogSize);
+ LogCmd(GetBufSizeUsed, getLogSizeUsed);
+ LogCmd(GetStatistics, getStatistics);
+ LogCmd(GetPruneList, getPruneList);
+ LogCmd(SetPruneList, setPruneList);
+ LogCmd(GetEventTag, getEventTag);
+ LogCmd(Reinit, reinit);
+ LogCmd(Exit, EXIT);
+#undef LogCmd
};
-
-#endif
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
deleted file mode 100644
index 0845504..0000000
--- a/logd/FlushCommand.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2012-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 <stdlib.h>
-
-#include <private/android_filesystem_config.h>
-
-#include "FlushCommand.h"
-#include "LogBuffer.h"
-#include "LogBufferElement.h"
-#include "LogCommand.h"
-#include "LogReader.h"
-#include "LogTimes.h"
-#include "LogUtils.h"
-
-// runSocketCommand is called once for every open client on the
-// log reader socket. Here we manage and associated the reader
-// client tracking and log region locks LastLogTimes list of
-// LogTimeEntrys, and spawn a transitory per-client thread to
-// work at filing data to the socket.
-//
-// global LogTimeEntry::wrlock() is used to protect access,
-// reference counts are used to ensure that individual
-// LogTimeEntry lifetime is managed when not protected.
-void FlushCommand::runSocketCommand(SocketClient* client) {
- LogTimeEntry* entry = nullptr;
- LastLogTimes& times = mReader.logbuf().mTimes;
-
- LogTimeEntry::wrlock();
- LastLogTimes::iterator it = times.begin();
- while (it != times.end()) {
- entry = it->get();
- if (entry->mClient == client) {
- if (!entry->isWatchingMultiple(mLogMask)) {
- LogTimeEntry::unlock();
- return;
- }
- if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
- LogTimeEntry::unlock();
- return;
- }
- entry->triggerReader_Locked();
- LogTimeEntry::unlock();
- return;
- }
- it++;
- }
-
- LogTimeEntry::unlock();
-}
-
-bool FlushCommand::hasReadLogs(SocketClient* client) {
- return clientHasLogCredentials(client);
-}
-
-static bool clientHasSecurityCredentials(SocketClient* client) {
- return (client->getUid() == AID_SYSTEM) || (client->getGid() == AID_SYSTEM);
-}
-
-bool FlushCommand::hasSecurityLogs(SocketClient* client) {
- return clientHasSecurityCredentials(client);
-}
diff --git a/logd/FlushCommand.h b/logd/FlushCommand.h
deleted file mode 100644
index a69d439..0000000
--- a/logd/FlushCommand.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012-2013 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 _FLUSH_COMMAND_H
-#define _FLUSH_COMMAND_H
-
-#include <android/log.h>
-#include <sysutils/SocketClientCommand.h>
-
-class LogBufferElement;
-
-#include "LogTimes.h"
-
-class LogReader;
-
-class FlushCommand : public SocketClientCommand {
- LogReader& mReader;
- log_mask_t mLogMask;
-
- public:
- explicit FlushCommand(LogReader& reader, log_mask_t logMask)
- : mReader(reader), mLogMask(logMask) {
- }
-
- virtual void runSocketCommand(SocketClient* client);
-
- static bool hasReadLogs(SocketClient* client);
- static bool hasSecurityLogs(SocketClient* client);
-};
-
-#endif
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1f8ad05..a3e4e09 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -46,9 +46,6 @@
void LogBuffer::init() {
log_id_for_each(i) {
- mLastSet[i] = false;
- mLast[i] = mLogElements.begin();
-
if (setSize(i, __android_logger_get_buffer_size(i))) {
setSize(i, LOG_BUFFER_MIN_SIZE);
}
@@ -95,11 +92,7 @@
unlock();
}
- // We may have been triggered by a SIGHUP. Release any sleeping reader
- // threads to dump their current content.
- //
- // NB: this is _not_ performed in the context of a SIGHUP, it is
- // performed during startup, and in context of reinit administrative thread
+ // Release any sleeping reader threads to dump their current content.
LogTimeEntry::wrlock();
LastLogTimes::iterator times = mTimes.begin();
@@ -112,8 +105,11 @@
LogTimeEntry::unlock();
}
-LogBuffer::LogBuffer(LastLogTimes* times)
- : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
+LogBuffer::LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune)
+ : monotonic(android_log_clockid() == CLOCK_MONOTONIC),
+ mTimes(*times),
+ tags_(tags),
+ prune_(prune) {
pthread_rwlock_init(&mLogElementsLock, nullptr);
log_id_for_each(i) {
@@ -131,6 +127,20 @@
}
}
+LogBufferElementCollection::iterator LogBuffer::GetOldest(log_id_t log_id) {
+ auto it = mLogElements.begin();
+ if (oldest_[log_id]) {
+ it = *oldest_[log_id];
+ }
+ while (it != mLogElements.end() && (*it)->getLogId() != log_id) {
+ it++;
+ }
+ if (it != mLogElements.end()) {
+ oldest_[log_id] = it;
+ }
+ return it;
+}
+
enum match_type { DIFFERENT, SAME, SAME_LIBLOG };
static enum match_type identical(LogBufferElement* elem,
@@ -221,7 +231,7 @@
const char* tag = nullptr;
size_t tag_len = 0;
if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
- tag = tagToName(elem->getTag());
+ tag = tags_->tagToName(elem->getTag());
if (tag) {
tag_len = strlen(tag);
}
@@ -450,9 +460,7 @@
bool setLast[LOG_ID_MAX];
bool doSetLast = false;
- log_id_for_each(i) {
- doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
- }
+ log_id_for_each(i) { doSetLast |= setLast[i] = oldest_[i] && it == *oldest_[i]; }
#ifdef DEBUG_CHECK_FOR_STALE_ENTRIES
LogBufferElementCollection::iterator bad = it;
int key = ((id == LOG_ID_EVENTS) || (id == LOG_ID_SECURITY))
@@ -463,11 +471,11 @@
if (doSetLast) {
log_id_for_each(i) {
if (setLast[i]) {
- if (__predict_false(it == mLogElements.end())) { // impossible
- mLastSet[i] = false;
- mLast[i] = mLogElements.begin();
+ if (__predict_false(it == mLogElements.end())) {
+ oldest_[i] = std::nullopt;
} else {
- mLast[i] = it; // push down the road as next-best-watermark
+ oldest_[i] = it; // Store the next iterator even if it does not correspond to
+ // the same log_id, as a starting point for GetOldest().
}
}
}
@@ -486,11 +494,6 @@
b.first);
}
}
- if (mLastSet[i] && (bad == mLast[i])) {
- android::prdebug("stale mLast[%d]\n", i);
- mLastSet[i] = false;
- mLast[i] = mLogElements.begin();
- }
}
#endif
if (coalesce) {
@@ -668,7 +671,7 @@
if (__predict_false(caller_uid != AID_ROOT)) { // unlikely
// Only here if clear all request from non system source, so chatty
// filter logistics is not required.
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ it = GetOldest(id);
while (it != mLogElements.end()) {
LogBufferElement* element = *it;
@@ -678,11 +681,6 @@
continue;
}
- if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
if (oldest && oldest->mStart <= element->getSequence()) {
busy = true;
kickMe(oldest, id, pruneRows);
@@ -699,7 +697,7 @@
}
// prune by worst offenders; by blacklist, UID, and by PID of system UID
- bool hasBlacklist = (id != LOG_ID_SECURITY) && mPrune.naughty();
+ bool hasBlacklist = (id != LOG_ID_SECURITY) && prune_->naughty();
while (!clearAll && (pruneRows > 0)) {
// recalculate the worst offender on every batched pass
int worst = -1; // not valid for getUid() or getKey()
@@ -707,7 +705,7 @@
size_t second_worst_sizes = 0;
pid_t worstPid = 0; // POSIX guarantees PID != 0
- if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
+ if (worstUidEnabledForLogid(id) && prune_->worstUidEnabled()) {
// Calculate threshold as 12.5% of available storage
size_t threshold = log_buffer_size(id) / 8;
@@ -721,7 +719,7 @@
.findWorst(worst, worst_sizes, second_worst_sizes,
threshold);
- if ((worst == AID_SYSTEM) && mPrune.worstPidOfSystemEnabled()) {
+ if ((worst == AID_SYSTEM) && prune_->worstPidOfSystemEnabled()) {
stats.sortPids(worst, (pid_t)0, 2, id)
.findWorst(worstPid, worst_sizes, second_worst_sizes);
}
@@ -734,8 +732,8 @@
}
bool kick = false;
- bool leading = true;
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ bool leading = true; // true if starting from the oldest log entry, false if starting from
+ // a specific chatty entry.
// Perform at least one mandatory garbage collection cycle in following
// - clear leading chatty tags
// - coalesce chatty tags
@@ -763,6 +761,9 @@
}
}
}
+ if (leading) {
+ it = GetOldest(id);
+ }
static const timespec too_old = { EXPIRE_HOUR_THRESHOLD * 60 * 60, 0 };
LogBufferElementCollection::iterator lastt;
lastt = mLogElements.end();
@@ -783,11 +784,6 @@
}
// below this point element->getLogId() == id
- if (leading && (!mLastSet[id] || ((*mLast[id])->getLogId() != id))) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
uint16_t dropped = element->getDropped();
// remove any leading drops
@@ -805,7 +801,7 @@
? element->getTag()
: element->getUid();
- if (hasBlacklist && mPrune.naughty(element)) {
+ if (hasBlacklist && prune_->naughty(element)) {
last.clear(element);
it = erase(it);
if (dropped) {
@@ -902,14 +898,14 @@
}
last.clear();
- if (!kick || !mPrune.worstUidEnabled()) {
+ if (!kick || !prune_->worstUidEnabled()) {
break; // the following loop will ask bad clients to skip/drop
}
}
bool whitelist = false;
- bool hasWhitelist = (id != LOG_ID_SECURITY) && mPrune.nice() && !clearAll;
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ bool hasWhitelist = (id != LOG_ID_SECURITY) && prune_->nice() && !clearAll;
+ it = GetOldest(id);
while ((pruneRows > 0) && (it != mLogElements.end())) {
LogBufferElement* element = *it;
@@ -918,18 +914,13 @@
continue;
}
- if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
if (oldest && oldest->mStart <= element->getSequence()) {
busy = true;
if (!whitelist) kickMe(oldest, id, pruneRows);
break;
}
- if (hasWhitelist && !element->getDropped() && mPrune.nice(element)) {
+ if (hasWhitelist && !element->getDropped() && prune_->nice(element)) {
// WhiteListed
whitelist = true;
it++;
@@ -942,7 +933,7 @@
// Do not save the whitelist if we are reader range limited
if (whitelist && (pruneRows > 0)) {
- it = mLastSet[id] ? mLast[id] : mLogElements.begin();
+ it = GetOldest(id);
while ((it != mLogElements.end()) && (pruneRows > 0)) {
LogBufferElement* element = *it;
@@ -951,11 +942,6 @@
continue;
}
- if (!mLastSet[id] || ((*mLast[id])->getLogId() != id)) {
- mLast[id] = it;
- mLastSet[id] = true;
- }
-
if (oldest && oldest->mStart <= element->getSequence()) {
busy = true;
kickMe(oldest, id, pruneRows);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 16225a5..9a36712 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_BUFFER_H__
-#define _LOGD_LOG_BUFFER_H__
+#pragma once
#include <sys/types.h>
#include <list>
+#include <optional>
#include <string>
#include <android/log.h>
@@ -80,10 +80,6 @@
LogStatistics stats;
- PruneList mPrune;
- // watermark for last per log id
- LogBufferElementCollection::iterator mLast[LOG_ID_MAX];
- bool mLastSet[LOG_ID_MAX];
// watermark of any worst/chatty uid processing
typedef std::unordered_map<uid_t, LogBufferElementCollection::iterator>
LogBufferIteratorMap;
@@ -97,8 +93,6 @@
bool monotonic;
- LogTags tags;
-
LogBufferElement* lastLoggedElements[LOG_ID_MAX];
LogBufferElement* droppedElements[LOG_ID_MAX];
void log(LogBufferElement* elem);
@@ -106,7 +100,7 @@
public:
LastLogTimes& mTimes;
- explicit LogBuffer(LastLogTimes* times);
+ LogBuffer(LastLogTimes* times, LogTags* tags, PruneList* prune);
~LogBuffer();
void init();
bool isMonotonic() {
@@ -135,24 +129,6 @@
stats.enableStatistics();
}
- int initPrune(const char* cp) {
- return mPrune.init(cp);
- }
- std::string formatPrune() {
- return mPrune.format();
- }
-
- std::string formatGetEventTag(uid_t uid, const char* name,
- const char* format) {
- return tags.formatGetEventTag(uid, name, format);
- }
- std::string formatEntry(uint32_t tag, uid_t uid) {
- return tags.formatEntry(tag, uid);
- }
- const char* tagToName(uint32_t tag) {
- return tags.tagToName(tag);
- }
-
// helper must be protected directly or implicitly by wrlock()/unlock()
const char* pidToName(pid_t pid) {
return stats.pidToName(pid);
@@ -181,6 +157,15 @@
bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
LogBufferElementCollection::iterator erase(
LogBufferElementCollection::iterator it, bool coalesce = false);
-};
-#endif // _LOGD_LOG_BUFFER_H__
+ // Returns an iterator to the oldest element for a given log type, or mLogElements.end() if
+ // there are no logs for the given log type. Requires mLogElementsLock to be held.
+ LogBufferElementCollection::iterator GetOldest(log_id_t log_id);
+
+ LogTags* tags_;
+ PruneList* prune_;
+
+ // 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.
+ std::optional<LogBufferElementCollection::iterator> oldest_[LOG_ID_MAX];
+};
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 3714800..916ed42 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -22,6 +22,7 @@
#include <time.h>
#include <unistd.h>
+#include <log/log_read.h>
#include <private/android_logger.h>
#include "LogBuffer.h"
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index f79d39c..c6dea69 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -24,21 +24,35 @@
#include <cutils/sockets.h>
#include <private/android_logger.h>
-#include "FlushCommand.h"
#include "LogBuffer.h"
#include "LogBufferElement.h"
#include "LogReader.h"
#include "LogUtils.h"
+static bool CanReadSecurityLogs(SocketClient* client) {
+ return client->getUid() == AID_SYSTEM || client->getGid() == AID_SYSTEM;
+}
+
LogReader::LogReader(LogBuffer* logbuf)
: SocketListener(getLogSocket(), true), mLogbuf(*logbuf) {
}
// When we are notified a new log entry is available, inform
// listening sockets who are watching this entry's log id.
-void LogReader::notifyNewLog(log_mask_t logMask) {
- FlushCommand command(*this, logMask);
- runOnEachSocket(&command);
+void LogReader::notifyNewLog(log_mask_t log_mask) {
+ LastLogTimes& times = mLogbuf.mTimes;
+
+ LogTimeEntry::wrlock();
+ for (const auto& entry : times) {
+ if (!entry->isWatchingMultiple(log_mask)) {
+ continue;
+ }
+ if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) {
+ continue;
+ }
+ entry->triggerReader_Locked();
+ }
+ LogTimeEntry::unlock();
}
// Note returning false will release the SocketClient instance.
@@ -129,6 +143,9 @@
nonBlock = true;
}
+ bool privileged = clientHasLogCredentials(cli);
+ bool can_read_security = CanReadSecurityLogs(cli);
+
uint64_t sequence = 1;
// Convert realtime to sequence number
if (start != log_time::EPOCH) {
@@ -178,8 +195,7 @@
} logFindStart(logMask, pid, start, sequence,
logbuf().isMonotonic() && android::isMonotonic(start));
- logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
- FlushCommand::hasSecurityLogs(cli),
+ logbuf().flushTo(cli, sequence, nullptr, privileged, can_read_security,
logFindStart.callback, &logFindStart);
if (!logFindStart.found()) {
@@ -203,7 +219,7 @@
LogTimeEntry::wrlock();
auto entry = std::make_unique<LogTimeEntry>(*this, cli, nonBlock, tail, logMask, pid, start,
- sequence, timeout);
+ sequence, timeout, privileged, can_read_security);
if (!entry->startReader_Locked()) {
LogTimeEntry::unlock();
return false;
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 8299e66..3e52b38 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -34,6 +34,7 @@
#include <android-base/stringprintf.h>
#include <log/log_event_list.h>
#include <log/log_properties.h>
+#include <log/log_read.h>
#include <private/android_filesystem_config.h>
#include "LogTags.h"
@@ -390,23 +391,6 @@
return me->tagToName(tag);
}
-// Prototype in LogUtils.h allowing external access to our database.
-//
-// This only works on userdebug and eng devices to re-read the
-// /data/misc/logd/event-log-tags file right after /data is mounted.
-// The operation is near to boot and should only happen once. There
-// are races associated with its use since it can trigger a Rebuild
-// of the file, but that is a can-not-happen since the file was not
-// read yet. More dangerous if called later, but if all is well it
-// should just skip over everything and not write any new entries.
-void android::ReReadEventLogTags() {
- LogTags* me = logtags;
-
- if (me && __android_log_is_debuggable()) {
- me->ReadFileEventLogTags(me->debug_event_log_tags);
- }
-}
-
// converts an event tag into a format
const char* LogTags::tagToFormat(uint32_t tag) const {
tag2format_const_iterator iform;
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index ed8d2f5..ad150bd 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -18,7 +18,6 @@
#include <string.h>
#include <sys/prctl.h>
-#include "FlushCommand.h"
#include "LogBuffer.h"
#include "LogReader.h"
#include "LogTimes.h"
@@ -27,7 +26,8 @@
LogTimeEntry::LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock,
unsigned long tail, log_mask_t logMask, pid_t pid, log_time start_time,
- uint64_t start, uint64_t timeout)
+ uint64_t start, uint64_t timeout, bool privileged,
+ bool can_read_security_logs)
: leadingDropped(false),
mReader(reader),
mLogMask(logMask),
@@ -38,7 +38,9 @@
mClient(client),
mStartTime(start_time),
mStart(start),
- mNonBlock(nonBlock) {
+ mNonBlock(nonBlock),
+ privileged_(privileged),
+ can_read_security_logs_(can_read_security_logs) {
mTimeout.tv_sec = timeout / NS_PER_SEC;
mTimeout.tv_nsec = timeout % NS_PER_SEC;
memset(mLastTid, 0, sizeof(mLastTid));
@@ -72,9 +74,6 @@
LogBuffer& logbuf = me->mReader.logbuf();
- bool privileged = FlushCommand::hasReadLogs(client);
- bool security = FlushCommand::hasSecurityLogs(client);
-
me->leadingDropped = true;
wrlock();
@@ -96,12 +95,12 @@
unlock();
if (me->mTail) {
- logbuf.flushTo(client, start, nullptr, privileged, security,
+ logbuf.flushTo(client, start, nullptr, me->privileged_, me->can_read_security_logs_,
FilterFirstPass, me);
me->leadingDropped = true;
}
- start = logbuf.flushTo(client, start, me->mLastTid, privileged,
- security, FilterSecondPass, me);
+ start = logbuf.flushTo(client, start, me->mLastTid, me->privileged_,
+ me->can_read_security_logs_, FilterSecondPass, me);
// We only ignore entries before the original start time for the first flushTo(), if we
// get entries after this first flush before the original start time, then the client
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index a99c73b..56c930a 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -52,7 +52,7 @@
public:
LogTimeEntry(LogReader& reader, SocketClient* client, bool nonBlock, unsigned long tail,
log_mask_t logMask, pid_t pid, log_time start_time, uint64_t sequence,
- uint64_t timeout);
+ uint64_t timeout, bool privileged, bool can_read_security_logs);
SocketClient* mClient;
log_time mStartTime;
@@ -98,6 +98,10 @@
// flushTo filter callbacks
static int FilterFirstPass(const LogBufferElement* element, void* me);
static int FilterSecondPass(const LogBufferElement* element, void* me);
+
+ private:
+ bool privileged_;
+ bool can_read_security_logs_;
};
typedef std::list<std::unique_ptr<LogTimeEntry>> LastLogTimes;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fa9f398..f9cd42d 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGD_LOG_UTILS_H__
-#define _LOGD_LOG_UTILS_H__
+#pragma once
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -41,7 +40,6 @@
// Furnished in LogTags.cpp. Thread safe.
const char* tagToName(uint32_t tag);
-void ReReadEventLogTags();
// Furnished by LogKlog.cpp
char* log_strntok_r(char* s, ssize_t& len, char*& saveptr, ssize_t& sublen);
@@ -72,5 +70,3 @@
return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) ||
(id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
}
-
-#endif // _LOGD_LOG_UTILS_H__
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 4d1589b..14c5163 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -94,12 +94,14 @@
}
LastLogTimes times;
- LogBuffer log_buffer(×);
+ LogTags tags;
+ PruneList prune_list;
+ LogBuffer log_buffer(×, &tags, &prune_list);
size_t data_left = size;
const uint8_t** pdata = &data;
log_buffer.enableStatistics();
- log_buffer.initPrune(nullptr);
+ prune_list.init(nullptr);
// We want to get pruning code to get called.
log_id_for_each(i) { log_buffer.setSize(i, 10000); }
diff --git a/logd/main.cpp b/logd/main.cpp
index 23bbf86..cc45eb3 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -52,6 +52,7 @@
#include "LogBuffer.h"
#include "LogKlog.h"
#include "LogListener.h"
+#include "LogTags.h"
#include "LogUtils.h"
#define KMSG_PRIORITY(PRI) \
@@ -150,50 +151,6 @@
}
}
-static sem_t reinit;
-static bool reinit_running = false;
-static LogBuffer* logBuf = nullptr;
-
-static void* reinit_thread_start(void* /*obj*/) {
- prctl(PR_SET_NAME, "logd.daemon");
-
- while (reinit_running && !sem_wait(&reinit) && reinit_running) {
- if (fdDmesg >= 0) {
- static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
- 'l',
- 'o',
- 'g',
- 'd',
- '.',
- 'd',
- 'a',
- 'e',
- 'm',
- 'o',
- 'n',
- ':',
- ' ',
- 'r',
- 'e',
- 'i',
- 'n',
- 'i',
- 't',
- '\n' };
- write(fdDmesg, reinit_message, sizeof(reinit_message));
- }
-
- // Anything that reads persist.<property>
- if (logBuf) {
- logBuf->init();
- logBuf->initPrune(nullptr);
- }
- android::ReReadEventLogTags();
- }
-
- return nullptr;
-}
-
char* android::uidToName(uid_t u) {
struct Userdata {
uid_t uid;
@@ -220,12 +177,6 @@
return userdata.name;
}
-// Serves as a global method to trigger reinitialization
-// and as a function that can be provided to signal().
-void reinit_signal_handler(int /*signal*/) {
- sem_post(&reinit);
-}
-
static void readDmesg(LogAudit* al, LogKlog* kl) {
if (!al && !kl) {
return;
@@ -336,24 +287,10 @@
return EXIT_FAILURE;
}
- // Reinit Thread
- sem_init(&reinit, 0, 0);
- pthread_attr_t attr;
- if (!pthread_attr_init(&attr)) {
- struct sched_param param;
-
- memset(¶m, 0, sizeof(param));
- pthread_attr_setschedparam(&attr, ¶m);
- pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
- if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
- pthread_t thread;
- reinit_running = true;
- if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
- reinit_running = false;
- }
- }
- pthread_attr_destroy(&attr);
- }
+ // A cache of event log tags
+ LogTags log_tags;
+ // Pruning configuration.
+ PruneList prune_list;
// Serves the purpose of managing the last logs times read on a
// socket connection, and as a reader lock on a range of log
@@ -364,9 +301,7 @@
// LogBuffer is the object which is responsible for holding all
// log entries.
- logBuf = new LogBuffer(times);
-
- signal(SIGHUP, reinit_signal_handler);
+ LogBuffer* logBuf = new LogBuffer(times, &log_tags, &prune_list);
if (__android_logger_property_get_bool(
"logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
@@ -396,7 +331,7 @@
// Command listener listens on /dev/socket/logd for incoming logd
// administrative commands.
- CommandListener* cl = new CommandListener(logBuf, reader, swl);
+ CommandListener* cl = new CommandListener(logBuf, &log_tags, &prune_list);
if (cl->startListener()) {
return EXIT_FAILURE;
}
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index d57b79e..1dd5c86 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -32,6 +32,7 @@
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include <gtest/gtest.h>
+#include <log/log_read.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#ifdef __ANDROID__
@@ -582,6 +583,7 @@
"dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=0.000000000");
}
+#ifdef ENABLE_FLAKY_TESTS
// b/26447386 refined behavior
TEST(logd, timeout) {
#ifdef __ANDROID__
@@ -716,6 +718,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
// b/27242723 confirmed fixed
TEST(logd, SNDTIMEO) {
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index 405f5a9..5de422f 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -16,6 +16,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so nopreload
libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index b565340..77f8bb8 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -17,6 +17,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so
libOpenMAXAL.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 7cbda08..82196e4 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -16,6 +16,7 @@
liblog.so
libmediandk.so
libm.so
+libnativehelper.so
libnativewindow.so
libneuralnetworks.so
libOpenMAXAL.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6564e8f..a380ebb 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -78,6 +78,9 @@
mkdir /dev/boringssl 0755 root root
mkdir /dev/boringssl/selftest 0755 root root
+ # Mount tracefs
+ mount tracefs tracefs /sys/kernel/tracing
+
# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
on early-init && property:ro.product.cpu.abilist32=*
exec_start boringssl_self_test32
diff --git a/toolbox/start.cpp b/toolbox/start.cpp
index b87ed15..46314cf 100644
--- a/toolbox/start.cpp
+++ b/toolbox/start.cpp
@@ -36,7 +36,12 @@
}
static void ControlDefaultServices(bool start) {
- std::vector<std::string> services = {"netd", "surfaceflinger", "zygote"};
+ std::vector<std::string> services = {
+ "netd",
+ "surfaceflinger",
+ "audioserver",
+ "zygote",
+ };
// Only start zygote_secondary if not single arch.
std::string zygote_configuration = GetProperty("ro.zygote", "");
@@ -86,4 +91,4 @@
extern "C" int stop_main(int argc, char** argv) {
return StartStop(argc, argv, false);
-}
\ No newline at end of file
+}
diff --git a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
index aa30707..65eb854 100644
--- a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
+++ b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
@@ -2,10 +2,6 @@
<hal format="hidl">
<name>android.hardware.keymaster</name>
<transport>hwbinder</transport>
- <version>4.0</version>
- <interface>
- <name>IKeymasterDevice</name>
- <instance>default</instance>
- </interface>
+ <fqname>@4.0::IKeymasterDevice/default</fqname>
</hal>
</manifest>