Merge "Don't include log/log_read.h in log/log.h"
diff --git a/adb/Android.bp b/adb/Android.bp
index 6386a78..8779c0a 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -165,7 +165,6 @@
"adb_unique_fd.cpp",
"adb_utils.cpp",
"fdevent/fdevent.cpp",
- "fdevent/fdevent_poll.cpp",
"services.cpp",
"sockets.cpp",
"socket_spec.cpp",
@@ -176,6 +175,17 @@
"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",
@@ -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"],
},
},
@@ -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/client/adb_client.h b/adb/client/adb_client.h
index 27be28f..caf4e86 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -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/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 29f9dc1..d565e01 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"
@@ -1910,6 +1912,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")) {
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/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 22b9b18..2bf062f 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -216,6 +216,9 @@
int adbSecureServiceType = serviceIndex();
switch (adbSecureServiceType) {
+ case kADBTransportServiceRefIndex:
+ sAdbTransportServices->push_back(this);
+ break;
case kADBSecurePairingServiceRefIndex:
sAdbSecurePairingServices->push_back(this);
break;
@@ -233,16 +236,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 +272,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 +297,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 +316,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 +413,9 @@
int index = adb_DNSServiceIndexByName(regType);
ResolvedService::ServiceRegistry* services;
switch (index) {
+ case kADBTransportServiceRefIndex:
+ services = ResolvedService::sAdbTransportServices;
+ break;
case kADBSecurePairingServiceRefIndex:
services = ResolvedService::sAdbSecurePairingServices;
break;
@@ -542,6 +560,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..7ea7949
--- /dev/null
+++ b/adb/coverage/show.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -euxo pipefail
+
+OUTPUT_DIR=$(realpath $(dirname "$0"))
+. "$OUTPUT_DIR"/include.sh
+
+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
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/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/adbconnection_client.cpp b/adb/libs/adbconnection/adbconnection_client.cpp
index b569421..7e16148 100644
--- a/adb/libs/adbconnection/adbconnection_client.cpp
+++ b/adb/libs/adbconnection/adbconnection_client.cpp
@@ -122,16 +122,7 @@
return nullptr;
}
-#if defined(__APPLE__)
- ctx->control_socket_.reset(socket(AF_UNIX, SOCK_SEQPACKET, 0));
- // TODO: find a better home for all these copies of the mac CLOEXEC workaround.
- if (int fd = ctx->control_socket_.get(), flags = fcntl(fd, F_GETFD);
- flags != -1 && (flags & FD_CLOEXEC) == 0) {
- fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
- }
-#else
ctx->control_socket_.reset(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0));
-#endif
if (ctx->control_socket_ < 0) {
PLOG(ERROR) << "failed to create Unix domain socket";
return nullptr;
@@ -149,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/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/logging.cpp b/base/logging.cpp
index cd460eb..3c73fea 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -195,7 +195,6 @@
return logging_lock;
}
-// Only used for Q fallback.
static LogFunction& Logger() {
#ifdef __ANDROID__
static auto& logger = *new LogFunction(LogdLogger());
@@ -205,7 +204,6 @@
return logger;
}
-// Only used for Q fallback.
static AbortFunction& Aborter() {
static auto& aborter = *new AbortFunction(DefaultAborter);
return aborter;
@@ -416,45 +414,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); });
}
}
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 0c2569d..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;
@@ -1908,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) {
@@ -1932,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/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index d496466..2f516fa 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -40,6 +40,20 @@
return true;
}
+bool LinearExtent::OverlapsWith(const LinearExtent& other) const {
+ if (device_index_ != other.device_index()) {
+ return false;
+ }
+ return physical_sector() < other.end_sector() && other.physical_sector() < end_sector();
+}
+
+bool LinearExtent::OverlapsWith(const Interval& interval) const {
+ if (device_index_ != interval.device_index) {
+ return false;
+ }
+ return physical_sector() < interval.end && interval.start < end_sector();
+}
+
Interval LinearExtent::AsInterval() const {
return Interval(device_index(), physical_sector(), end_sector());
}
@@ -774,8 +788,7 @@
bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
const LinearExtent& candidate) const {
for (const auto& region : regions) {
- if (region.device_index == candidate.device_index() &&
- (candidate.OwnsSector(region.start) || candidate.OwnsSector(region.end))) {
+ if (candidate.OverlapsWith(region)) {
return true;
}
}
@@ -786,11 +799,10 @@
for (const auto& partition : partitions_) {
for (const auto& extent : partition->extents()) {
LinearExtent* linear = extent->AsLinearExtent();
- if (!linear || linear->device_index() != candidate.device_index()) {
+ if (!linear) {
continue;
}
- if (linear->OwnsSector(candidate.physical_sector()) ||
- linear->OwnsSector(candidate.end_sector() - 1)) {
+ if (linear->OverlapsWith(candidate)) {
return true;
}
}
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index ca8df61..977ebe3 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -937,3 +937,85 @@
EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2));
EXPECT_EQ(exported->header.flags, 0x5e5e5e5e);
}
+
+static Interval ToInterval(const std::unique_ptr<Extent>& extent) {
+ if (LinearExtent* le = extent->AsLinearExtent()) {
+ return le->AsInterval();
+ }
+ return {0, 0, 0};
+}
+
+static void AddPartition(const std::unique_ptr<MetadataBuilder>& builder,
+ const std::string& partition_name, uint64_t num_sectors,
+ uint64_t start_sector, std::vector<Interval>* intervals) {
+ Partition* p = builder->AddPartition(partition_name, "group", 0);
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(p, "super", num_sectors, start_sector));
+ ASSERT_EQ(p->extents().size(), 1);
+
+ if (!intervals) {
+ return;
+ }
+
+ auto new_interval = ToInterval(p->extents().back());
+ std::vector<Interval> new_intervals = {new_interval};
+
+ auto overlap = Interval::Intersect(*intervals, new_intervals);
+ ASSERT_TRUE(overlap.empty());
+
+ intervals->push_back(new_interval);
+}
+
+TEST_F(BuilderTest, CollidedExtents) {
+ BlockDeviceInfo super("super", 8_GiB, 786432, 229376, 4096);
+ std::vector<BlockDeviceInfo> block_devices = {super};
+
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("group", 0));
+
+ std::vector<Interval> old_intervals;
+ AddPartition(builder, "system", 10229008, 2048, &old_intervals);
+ AddPartition(builder, "test_a", 648, 12709888, &old_intervals);
+ AddPartition(builder, "test_b", 625184, 12711936, &old_intervals);
+ AddPartition(builder, "test_c", 130912, 13338624, &old_intervals);
+ AddPartition(builder, "test_d", 888, 13469696, &old_intervals);
+ AddPartition(builder, "test_e", 888, 13471744, &old_intervals);
+ AddPartition(builder, "test_f", 888, 13475840, &old_intervals);
+ AddPartition(builder, "test_g", 888, 13477888, &old_intervals);
+
+ // Don't track the first vendor interval, since it will get extended.
+ AddPartition(builder, "vendor", 2477920, 10231808, nullptr);
+
+ std::vector<Interval> new_intervals;
+
+ Partition* p = builder->FindPartition("vendor");
+ ASSERT_NE(p, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(p, 1282031616));
+ ASSERT_GE(p->extents().size(), 1);
+ for (const auto& extent : p->extents()) {
+ new_intervals.push_back(ToInterval(extent));
+ }
+
+ std::vector<Interval> overlap = Interval::Intersect(old_intervals, new_intervals);
+ ASSERT_TRUE(overlap.empty());
+}
+
+TEST_F(BuilderTest, LinearExtentOverlap) {
+ LinearExtent extent(20, 0, 10);
+
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 10}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{50, 0, 10}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 0, 30}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{10, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{40, 0, 0}));
+ EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 15}));
+
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 0}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{50, 1, 10}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{40, 1, 0}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 15}));
+ EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 10}));
+}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
index 382d53d..99bff6e 100644
--- a/fs_mgr/liblp/device_test.cpp
+++ b/fs_mgr/liblp/device_test.cpp
@@ -56,6 +56,10 @@
// Having an alignment offset > alignment doesn't really make sense.
EXPECT_LT(device_info.alignment_offset, device_info.alignment);
+
+ if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+ EXPECT_EQ(device_info.alignment_offset, 0);
+ }
}
TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f7738fb..bd39150 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -71,9 +71,8 @@
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
uint32_t device_index() const { return device_index_; }
- bool OwnsSector(uint64_t sector) const {
- return sector >= physical_sector_ && sector < end_sector();
- }
+ bool OverlapsWith(const LinearExtent& other) const;
+ bool OverlapsWith(const Interval& interval) const;
Interval AsInterval() const;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index bd788f5..384595d 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",
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..758d66c
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -0,0 +1,54 @@
+// 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(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..fff667e 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,30 @@
// 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);
+ 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 +199,7 @@
//
// 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;
// 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 +212,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 +236,65 @@
// 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;
+ 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);
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..9b2590c
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -0,0 +1,52 @@
+// 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;
+ 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/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index c9fa28e..eafeea2 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -2685,5 +2685,9 @@
return true;
}
+ISnapshotMergeStats* SnapshotManager::GetSnapshotMergeStatsInstance() {
+ return SnapshotMergeStats::GetInstance(*this);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
new file mode 100644
index 0000000..2747b03
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -0,0 +1,125 @@
+// 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;
+}
+
+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 c662838..53a37be 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 {
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..72f0450 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) {
@@ -785,7 +795,7 @@
}
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 */);
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/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 7a144df..f27b8e6 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -93,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 */
@@ -107,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) \
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index d15b367..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);
}
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/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/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ab59a4b..bf7d69e 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",
@@ -101,6 +102,16 @@
"liblog",
"liblzma",
],
+
+ header_libs: [
+ "bionic_libc_platform_headers",
+ ],
+
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
}
cc_library {
@@ -213,6 +224,7 @@
"tests/MemoryRangesTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/MemoryTest.cpp",
+ "tests/MemoryMteTest.cpp",
"tests/RegsInfoTest.cpp",
"tests/RegsIterateTest.cpp",
"tests/RegsStepIfSignalHandlerTest.cpp",
@@ -268,6 +280,16 @@
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
+
+ header_libs: [
+ "bionic_libc_platform_headers",
+ ],
+
+ product_variables: {
+ experimental_mte: {
+ cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+ },
+ },
}
cc_test {
@@ -373,7 +395,9 @@
srcs: [
"benchmarks/unwind_benchmarks.cpp",
+ "benchmarks/ElfBenchmark.cpp",
"benchmarks/SymbolBenchmark.cpp",
+ "benchmarks/Utils.cpp",
],
data: [
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/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..d1d0ebc
--- /dev/null
+++ b/libunwindstack/MemoryMte.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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 <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__)
+ return ptrace(PTRACE_PEEKTAG, pid_, (void*)addr, nullptr);
+#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
index a850ff0..73088da 100644
--- a/libunwindstack/benchmarks/SymbolBenchmark.cpp
+++ b/libunwindstack/benchmarks/SymbolBenchmark.cpp
@@ -22,33 +22,12 @@
#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>
-#if defined(__BIONIC__)
-
-#include <meminfo/procmeminfo.h>
-#include <procinfo/process_map.h>
-
-static void Gather(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
+#include "Utils.h"
static void BenchmarkSymbolLookup(benchmark::State& state, std::vector<uint64_t> offsets,
std::string elf_file, bool expect_found) {
@@ -66,7 +45,7 @@
#if defined(__BIONIC__)
mallopt(M_PURGE, 0);
uint64_t rss_bytes_before = 0;
- Gather(&rss_bytes_before);
+ GatherRss(&rss_bytes_before);
#endif
uint64_t alloc_bytes_before = mallinfo().uordblks;
state.ResumeTiming();
@@ -88,7 +67,7 @@
#endif
alloc_bytes += mallinfo().uordblks - alloc_bytes_before;
#if defined(__BIONIC__)
- Gather(&rss_bytes);
+ GatherRss(&rss_bytes);
rss_bytes -= rss_bytes_before;
#endif
state.ResumeTiming();
@@ -105,14 +84,6 @@
BenchmarkSymbolLookup(state, std::vector<uint64_t>{pc}, elf_file, expect_found);
}
-std::string GetElfFile() {
- return android::base::GetExecutableDirectory() + "/benchmarks/files/libart_arm.so";
-}
-
-std::string GetSortedElfFile() {
- return android::base::GetExecutableDirectory() + "/benchmarks/files/boot_arm.oat";
-}
-
void BM_symbol_not_present(benchmark::State& state) {
BenchmarkSymbolLookup(state, 0, GetElfFile(), false);
}
@@ -136,23 +107,23 @@
BENCHMARK(BM_symbol_find_multiple);
void BM_symbol_not_present_from_sorted(benchmark::State& state) {
- BenchmarkSymbolLookup(state, 0, GetSortedElfFile(), false);
+ 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, GetSortedElfFile(), true);
+ 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), GetSortedElfFile(), true);
+ 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},
- GetSortedElfFile(), true);
+ 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/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/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..385078d 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -26,8 +26,10 @@
#include <vector>
-#include <android-base/test_utils.h>
#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
#include <gtest/gtest.h>
#include "MemoryRemote.h"
@@ -37,24 +39,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 +51,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -76,10 +61,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 +80,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -105,10 +90,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 +113,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 +134,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 +154,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -188,10 +173,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 +186,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -209,10 +194,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 +206,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(Attach(pid));
+ ASSERT_TRUE(TestAttach(pid));
MemoryRemote remote(pid);
@@ -229,10 +214,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 +235,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 +248,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 +272,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 +284,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 +309,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 +337,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/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());
}
}