diff options
author | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-04-27 21:57:49 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2020-04-27 21:57:49 +0000 |
commit | d457c1a694a8200816b69b67fba115e6b38cbcae (patch) | |
tree | 60fce6c77726689eccfb06f1cca01f074f8afabe | |
parent | d796f803235cdefc93dc20d2e7283d62a21997ba (diff) | |
parent | 1fbbfbf1fc546c10529745cafe755f88fa72293e (diff) | |
download | core-d457c1a694a8200816b69b67fba115e6b38cbcae.tar.gz |
Merge "Snap for 6435660 from 91fd50c78f862bdb5c0ea08b08c60c1aa67b71de to sdk-release" into sdk-releaseplatform-tools-30.0.1
84 files changed, 2614 insertions, 582 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index c8dbf77fa..dcf92be1e 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -3,3 +3,6 @@ clang_format = true [Builtin Hooks Options] clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp + +[Hook Scripts] +aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "." diff --git a/adb/Android.bp b/adb/Android.bp index 6386a78ed..432770cd4 100644 --- a/adb/Android.bp +++ b/adb/Android.bp @@ -165,17 +165,26 @@ libadb_srcs = [ "adb_unique_fd.cpp", "adb_utils.cpp", "fdevent/fdevent.cpp", - "fdevent/fdevent_poll.cpp", "services.cpp", "sockets.cpp", "socket_spec.cpp", "sysdeps/errno.cpp", "transport.cpp", "transport_fd.cpp", - "transport_local.cpp", "types.cpp", ] +libadb_darwin_srcs = [ + "fdevent/fdevent_poll.cpp", +] + +libadb_windows_srcs = [ + "fdevent/fdevent_poll.cpp", + "sysdeps_win32.cpp", + "sysdeps/win32/errno.cpp", + "sysdeps/win32/stat.cpp", +] + libadb_posix_srcs = [ "sysdeps_unix.cpp", "sysdeps/posix/network.cpp", @@ -207,6 +216,7 @@ cc_library_host_static { "client/adb_wifi.cpp", "client/usb_libusb.cpp", "client/usb_dispatch.cpp", + "client/transport_local.cpp", "client/transport_mdns.cpp", "client/transport_usb.cpp", "client/pairing/pairing_client.cpp", @@ -219,7 +229,7 @@ cc_library_host_static { 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 @@ cc_library_host_static { enabled: true, srcs: [ "client/usb_windows.cpp", - "sysdeps_win32.cpp", - "sysdeps/win32/errno.cpp", - "sysdeps/win32/stat.cpp", - ], + ] + libadb_windows_srcs, shared_libs: ["AdbWinApi"], }, }, @@ -383,10 +390,11 @@ cc_library_static { compile_multilib: "both", srcs: libadb_srcs + libadb_linux_srcs + libadb_posix_srcs + [ + "daemon/adb_wifi.cpp", "daemon/auth.cpp", "daemon/jdwp_service.cpp", "daemon/logging.cpp", - "daemon/adb_wifi.cpp", + "daemon/transport_local.cpp", ], generated_headers: ["platform_tools_version"], @@ -585,7 +593,6 @@ cc_library { cc_binary { name: "adbd", defaults: ["adbd_defaults", "host_adbd_supported", "libadbd_binary_dependencies"], - stl: "libc++_static", recovery_available: true, apex_available: ["com.android.adbd"], @@ -695,6 +702,7 @@ cc_test { "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 08d3904c7..dcec0ba11 100644 --- a/adb/adb.cpp +++ b/adb/adb.cpp @@ -149,7 +149,7 @@ void print_packet(const char *label, apacket *p) 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 @@ void adb_set_reject_kill_server(bool value) { 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 @@ HostRequestResult handle_host_request(std::string_view service, TransportType ty 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 6b373553a..31112485b 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* kADBSecurePairingServiceTxtRecord = 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 cea24fe2f..c579dde92 100644 --- a/adb/adb_trace.cpp +++ b/adb/adb_trace.cpp @@ -89,18 +89,13 @@ void start_device_log(void) { 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 585748c91..3a6b0b133 100644 --- a/adb/adb_wifi.h +++ b/adb/adb_wifi.h @@ -27,6 +27,9 @@ void adb_wifi_pair_device(const std::string& host, const std::string& password, std::string& response); bool adb_wifi_is_known_host(const std::string& host); +std::string mdns_check(); +std::string mdns_list_discovered_services(); + #else // !ADB_HOST struct AdbdAuthContext; diff --git a/adb/apex/Android.bp b/adb/apex/Android.bp index 4346f6778..ddb17dac1 100644 --- a/adb/apex/Android.bp +++ b/adb/apex/Android.bp @@ -1,6 +1,7 @@ apex_defaults { name: "com.android.adbd-defaults", updatable: true, + min_sdk_version: "R", binaries: ["adbd"], compile_multilib: "both", diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h index 27be28f35..caf4e86ac 100644 --- a/adb/client/adb_client.h +++ b/adb/client/adb_client.h @@ -90,8 +90,9 @@ extern const char* _Nullable * _Nullable __adb_envp; // ADB Secure DNS service interface. Used to query what ADB Secure DNS services have been // resolved, and to run some kind of callback for each one. -using adb_secure_foreach_service_callback = std::function<void( - const char* _Nonnull service_name, const char* _Nonnull ip_address, uint16_t port)>; +using adb_secure_foreach_service_callback = + std::function<void(const char* _Nonnull service_name, const char* _Nonnull reg_type, + const char* _Nonnull ip_address, uint16_t port)>; // Queries pairing/connect services that have been discovered and resolved. // If |host_name| is not null, run |cb| only for services diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp index 59c856382..e562f8b01 100644 --- a/adb/client/adb_install.cpp +++ b/adb/client/adb_install.cpp @@ -154,6 +154,14 @@ static void read_status_line(int fd, char* buf, size_t count) { *buf = '\0'; } +static unique_fd send_command(const std::vector<std::string>& cmd_args, std::string* error) { + if (is_abb_exec_supported()) { + return send_abb_exec_command(cmd_args, error); + } else { + return unique_fd(adb_connect(android::base::Join(cmd_args, " "), error)); + } +} + static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) { printf("Performing Streamed Install\n"); @@ -226,12 +234,7 @@ static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy cmd_args.push_back("--apex"); } - unique_fd remote_fd; - if (use_abb_exec) { - remote_fd = send_abb_exec_command(cmd_args, &error); - } else { - remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error)); - } + unique_fd remote_fd = send_command(cmd_args, &error); if (remote_fd < 0) { fprintf(stderr, "adb: connect error for write: %s\n", error.c_str()); return 1; @@ -547,24 +550,28 @@ int install_multiple_app(int argc, const char** argv) { if (first_apk == -1) error_exit("need APK file on command line"); - std::string install_cmd; - if (best_install_mode() == INSTALL_PUSH) { - install_cmd = "exec:pm"; - } else { - install_cmd = "exec:cmd package"; - } + const bool use_abb_exec = is_abb_exec_supported(); + + const std::string install_cmd = + use_abb_exec ? "package" + : best_install_mode() == INSTALL_PUSH ? "exec:pm" : "exec:cmd package"; - std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, - install_cmd.c_str(), total_size); + std::vector<std::string> cmd_args = {install_cmd, "install-create", "-S", + std::to_string(total_size)}; + cmd_args.reserve(first_apk + 4); for (int i = 1; i < first_apk; i++) { - cmd += " " + escape_arg(argv[i]); + if (use_abb_exec) { + cmd_args.push_back(argv[i]); + } else { + cmd_args.push_back(escape_arg(argv[i])); + } } // Create install session std::string error; char buf[BUFSIZ]; { - unique_fd fd(adb_connect(cmd, &error)); + unique_fd fd = send_command(cmd_args, &error); if (fd < 0) { fprintf(stderr, "adb: connect error for create: %s\n", error.c_str()); return EXIT_FAILURE; @@ -586,6 +593,7 @@ int install_multiple_app(int argc, const char** argv) { fputs(buf, stderr); return EXIT_FAILURE; } + const auto session_id_str = std::to_string(session_id); // Valid session, now stream the APKs bool success = true; @@ -598,10 +606,15 @@ int install_multiple_app(int argc, const char** argv) { goto finalize_session; } - std::string cmd = - android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -", - install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), - session_id, android::base::Basename(file).c_str()); + std::vector<std::string> cmd_args = { + install_cmd, + "install-write", + "-S", + std::to_string(sb.st_size), + session_id_str, + android::base::Basename(file), + "-", + }; unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC)); if (local_fd < 0) { @@ -611,7 +624,7 @@ int install_multiple_app(int argc, const char** argv) { } std::string error; - unique_fd remote_fd(adb_connect(cmd, &error)); + unique_fd remote_fd = send_command(cmd_args, &error); if (remote_fd < 0) { fprintf(stderr, "adb: connect error for write: %s\n", error.c_str()); success = false; @@ -636,10 +649,13 @@ int install_multiple_app(int argc, const char** argv) { finalize_session: // Commit session if we streamed everything okay; otherwise abandon. - std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(), - success ? "commit" : "abandon", session_id); + std::vector<std::string> service_args = { + install_cmd, + success ? "install-commit" : "install-abandon", + session_id_str, + }; { - unique_fd fd(adb_connect(service, &error)); + unique_fd fd = send_command(service_args, &error); if (fd < 0) { fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str()); return EXIT_FAILURE; diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp index 4b2fa0423..b674a8161 100644 --- a/adb/client/auth.cpp +++ b/adb/client/auth.cpp @@ -504,6 +504,12 @@ void adb_auth_tls_handshake(atransport* t) { }).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 29f9dc14c..f0a287d47 100644 --- a/adb/client/commandline.cpp +++ b/adb/client/commandline.cpp @@ -127,6 +127,8 @@ static void help() { " localfilesystem:<unix domain socket name>\n" " reverse --remove REMOTE remove specific reverse socket connection\n" " reverse --remove-all remove all reverse socket connections from device\n" + " mdns check check if mdns discovery is available\n" + " mdns services list all discovered services\n" "\n" "file transfer:\n" " push [--sync] [-z ALGORITHM] [-Z] LOCAL... REMOTE\n" @@ -238,6 +240,7 @@ static void help() { " $ANDROID_SERIAL serial number to connect to (see -s)\n" " $ANDROID_LOG_TAGS tags to be used by logcat (see logcat --help)\n" " $ADB_LOCAL_TRANSPORT_MAX_PORT max emulator scan port (default 5585, 16 emus)\n" + " $ADB_MDNS_AUTO_CONNECT comma-separated list of mdns services to allow auto-connect (default adb-tls-connect)\n" ); // clang-format on } @@ -1910,6 +1913,29 @@ int adb_commandline(int argc, const char** argv) { 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 1a1361c6d..bfe18c0b2 100644 --- a/adb/client/incremental_server.cpp +++ b/adb/client/incremental_server.cpp @@ -264,7 +264,7 @@ class IncrementalServer { 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/transport_local.cpp b/adb/client/transport_local.cpp index 5ec8e1665..15a072466 100644 --- a/adb/transport_local.cpp +++ b/adb/client/transport_local.cpp @@ -38,10 +38,6 @@ #include <android-base/thread_annotations.h> #include <cutils/sockets.h> -#if !ADB_HOST -#include <android-base/properties.h> -#endif - #include "adb.h" #include "adb_io.h" #include "adb_unique_fd.h" @@ -49,8 +45,6 @@ #include "socket_spec.h" #include "sysdeps/chrono.h" -#if ADB_HOST - // Android Wear has been using port 5601 in all of its documentation/tooling, // but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX]. // Avoid stomping on their port by restricting the active scanning range. @@ -76,9 +70,8 @@ static void adb_local_transport_max_port_env_override() { // We keep a map from emulator port to transport. // TODO: weak_ptr? -static auto& local_transports GUARDED_BY(local_transports_lock) = - *new std::unordered_map<int, atransport*>(); -#endif /* ADB_HOST */ +static std::unordered_map<int, atransport*> local_transports + [[clang::no_destroy]] GUARDED_BY(local_transports_lock); bool local_connect(int port) { std::string dummy; @@ -140,21 +133,19 @@ void connect_device(const std::string& address, std::string* response) { } } - int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error) { unique_fd fd; -#if ADB_HOST if (find_emulator_transport_by_adb_port(adb_port) != nullptr || find_emulator_transport_by_console_port(console_port) != nullptr) { return -1; } - const char *host = getenv("ADBHOST"); + const char* host = getenv("ADBHOST"); if (host) { fd.reset(network_connect(host, adb_port, SOCK_STREAM, 0, error)); } -#endif + if (fd < 0) { fd.reset(network_loopback_client(adb_port, SOCK_STREAM, error)); } @@ -173,8 +164,6 @@ int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* e return -1; } -#if ADB_HOST - static void PollAllLocalPortsForEmulator() { // Try to connect to any number of running emulator instances. for (int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; port <= adb_local_transport_max_port; @@ -194,8 +183,8 @@ struct RetryPort { // Retry emulators just kicked. static std::vector<RetryPort>& retry_ports = *new std::vector<RetryPort>; -std::mutex &retry_ports_lock = *new std::mutex; -std::condition_variable &retry_ports_cond = *new std::condition_variable; +std::mutex& retry_ports_lock = *new std::mutex; +std::condition_variable& retry_ports_cond = *new std::condition_variable; static void client_socket_thread(std::string_view) { adb_thread_setname("client_socket_thread"); @@ -220,7 +209,7 @@ static void client_socket_thread(std::string_view) { std::vector<RetryPort> next_ports; for (auto& port : ports) { VLOG(TRANSPORT) << "retry port " << port.port << ", last retry_count " - << port.retry_count; + << port.retry_count; if (local_connect(port.port)) { VLOG(TRANSPORT) << "retry port " << port.port << " successfully"; continue; @@ -240,77 +229,12 @@ static void client_socket_thread(std::string_view) { } } -#else // !ADB_HOST - -void server_socket_thread(std::function<unique_fd(std::string_view, std::string*)> listen_func, - std::string_view addr) { - adb_thread_setname("server socket"); - - unique_fd serverfd; - std::string error; - - while (serverfd == -1) { - errno = 0; - serverfd = listen_func(addr, &error); - if (errno == EAFNOSUPPORT || errno == EINVAL || errno == EPROTONOSUPPORT) { - D("unrecoverable error: '%s'", error.c_str()); - return; - } else if (serverfd < 0) { - D("server: cannot bind socket yet: %s", error.c_str()); - std::this_thread::sleep_for(1s); - continue; - } - close_on_exec(serverfd.get()); - } - - while (true) { - D("server: trying to get new connection from fd %d", serverfd.get()); - unique_fd fd(adb_socket_accept(serverfd, nullptr, nullptr)); - if (fd >= 0) { - D("server: new connection on fd %d", fd.get()); - close_on_exec(fd.get()); - disable_tcp_nagle(fd.get()); - std::string serial = android::base::StringPrintf("host-%d", fd.get()); - // We don't care about port value in "register_socket_transport" as it is used - // only from ADB_HOST. "server_socket_thread" is never called from ADB_HOST. - register_socket_transport( - std::move(fd), std::move(serial), 0, 1, - [](atransport*) { return ReconnectResult::Abort; }, false); - } - } - D("transport: server_socket_thread() exiting"); -} - -#endif - -#if !ADB_HOST -unique_fd adb_listen(std::string_view addr, std::string* error) { - return unique_fd{socket_spec_listen(addr, error, nullptr)}; -} -#endif - void local_init(const std::string& addr) { -#if ADB_HOST D("transport: local client init"); std::thread(client_socket_thread, addr).detach(); adb_local_transport_max_port_env_override(); -#elif !defined(__ANDROID__) - // Host adbd. - D("transport: local server init"); - std::thread(server_socket_thread, adb_listen, addr).detach(); -#else - D("transport: local server init"); - // For the adbd daemon in the system image we need to distinguish - // between the device, and the emulator. - if (addr.starts_with("tcp:") && use_qemu_goldfish()) { - std::thread(qemu_socket_thread, addr).detach(); - } else { - std::thread(server_socket_thread, adb_listen, addr).detach(); - } -#endif // !ADB_HOST } -#if ADB_HOST struct EmulatorConnection : public FdConnection { EmulatorConnection(unique_fd fd, int local_port) : FdConnection(std::move(fd)), local_port_(local_port) {} @@ -336,7 +260,7 @@ struct EmulatorConnection : public FdConnection { /* Only call this function if you already hold local_transports_lock. */ static atransport* find_emulator_transport_by_adb_port_locked(int adb_port) - REQUIRES(local_transports_lock) { + REQUIRES(local_transports_lock) { auto it = local_transports.find(adb_port); if (it == local_transports.end()) { return nullptr; @@ -352,7 +276,6 @@ atransport* find_emulator_transport_by_adb_port(int adb_port) { atransport* find_emulator_transport_by_console_port(int console_port) { return find_transport(getEmulatorSerialString(console_port).c_str()); } -#endif std::string getEmulatorSerialString(int console_port) { return android::base::StringPrintf("emulator-%d", console_port); @@ -363,7 +286,6 @@ int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) t->type = kTransportLocal; -#if ADB_HOST // Emulator connection. if (local) { auto emulator_connection = std::make_unique<EmulatorConnection>(std::move(fd), adb_port); @@ -380,7 +302,6 @@ int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) return fail; } -#endif // Regular tcp connection. auto fd_connection = std::make_unique<FdConnection>(std::move(fd)); diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp index 22b9b1808..2b6aa7cbb 100644 --- a/adb/client/transport_mdns.cpp +++ b/adb/client/transport_mdns.cpp @@ -26,6 +26,7 @@ #include <memory> #include <thread> +#include <unordered_set> #include <vector> #include <android-base/stringprintf.h> @@ -42,27 +43,75 @@ static DNSServiceRef service_refs[kNumADBDNSServices]; static fdevent* service_ref_fdes[kNumADBDNSServices]; +static auto& g_autoconn_whitelist = *new std::unordered_set<int>(); -static int adb_DNSServiceIndexByName(const char* regType) { +static int adb_DNSServiceIndexByName(std::string_view regType) { for (int i = 0; i < kNumADBDNSServices; ++i) { - if (!strncmp(regType, kADBDNSServices[i], strlen(kADBDNSServices[i]))) { + if (!strncmp(regType.data(), kADBDNSServices[i], strlen(kADBDNSServices[i]))) { return i; } } return -1; } -static bool adb_DNSServiceShouldConnect(const char* regType, const char* serviceName) { - int index = adb_DNSServiceIndexByName(regType); - if (index == kADBTransportServiceRefIndex) { - // Ignore adb-EMULATOR* service names, as it interferes with the - // emulator ports that are already connected. - if (android::base::StartsWith(serviceName, "adb-EMULATOR")) { - LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]"; - return false; +static void config_auto_connect_services() { + // ADB_MDNS_AUTO_CONNECT is a comma-delimited list of mdns services + // that are allowed to auto-connect. By default, only allow "adb-tls-connect" + // to auto-connect, since this is filtered down to auto-connect only to paired + // devices. + g_autoconn_whitelist.insert(kADBSecureConnectServiceRefIndex); + const char* srvs = getenv("ADB_MDNS_AUTO_CONNECT"); + if (!srvs) { + return; + } + + if (strcmp(srvs, "0") == 0) { + D("Disabling all auto-connecting"); + g_autoconn_whitelist.clear(); + return; + } + + if (strcmp(srvs, "1") == 0) { + D("Allow all auto-connecting"); + g_autoconn_whitelist.insert(kADBTransportServiceRefIndex); + return; + } + + // Selectively choose which services to allow auto-connect. + // E.g. ADB_MDNS_AUTO_CONNECT=adb,adb-tls-connect would allow + // _adb._tcp and _adb-tls-connnect._tcp services to auto-connect. + auto srvs_list = android::base::Split(srvs, ","); + std::unordered_set<int> new_whitelist; + for (const auto& item : srvs_list) { + auto full_srv = android::base::StringPrintf("_%s._tcp", item.data()); + int idx = adb_DNSServiceIndexByName(full_srv); + if (idx >= 0) { + new_whitelist.insert(idx); } } - return (index == kADBTransportServiceRefIndex || index == kADBSecureConnectServiceRefIndex); + + if (!new_whitelist.empty()) { + g_autoconn_whitelist = std::move(new_whitelist); + } +} + +static bool adb_DNSServiceShouldAutoConnect(const char* regType, const char* serviceName) { + // Try to auto-connect to any "_adb" or "_adb-tls-connect" services excluding emulator services. + int index = adb_DNSServiceIndexByName(regType); + if (index != kADBTransportServiceRefIndex && index != kADBSecureConnectServiceRefIndex) { + return false; + } + if (g_autoconn_whitelist.find(index) == g_autoconn_whitelist.end()) { + D("Auto-connect for regType '%s' disabled", regType); + return false; + } + // Ignore adb-EMULATOR* service names, as it interferes with the + // emulator ports that are already connected. + if (android::base::StartsWith(serviceName, "adb-EMULATOR")) { + LOG(INFO) << "Ignoring emulator transport service [" << serviceName << "]"; + return false; + } + return true; } // Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD() @@ -196,7 +245,7 @@ class ResolvedService : public AsyncServiceRef { // adb secure service needs to do something different from just // connecting here. - if (adb_DNSServiceShouldConnect(regType_.c_str(), serviceName_.c_str())) { + if (adb_DNSServiceShouldAutoConnect(regType_.c_str(), serviceName_.c_str())) { std::string response; D("Attempting to serviceName=[%s], regtype=[%s] ipaddr=(%s:%hu)", serviceName_.c_str(), regType_.c_str(), ip_addr_, port_); @@ -216,6 +265,9 @@ class ResolvedService : public AsyncServiceRef { int adbSecureServiceType = serviceIndex(); switch (adbSecureServiceType) { + case kADBTransportServiceRefIndex: + sAdbTransportServices->push_back(this); + break; case kADBSecurePairingServiceRefIndex: sAdbSecurePairingServices->push_back(this); break; @@ -233,16 +285,21 @@ class ResolvedService : public AsyncServiceRef { std::string serviceName() const { return serviceName_; } + std::string regType() const { return regType_; } + std::string ipAddress() const { return ip_addr_; } uint16_t port() const { return port_; } using ServiceRegistry = std::vector<ResolvedService*>; + // unencrypted tcp connections + static ServiceRegistry* sAdbTransportServices; + static ServiceRegistry* sAdbSecurePairingServices; static ServiceRegistry* sAdbSecureConnectServices; - static void initAdbSecure(); + static void initAdbServiceRegistries(); static void forEachService(const ServiceRegistry& services, const std::string& hostname, adb_secure_foreach_service_callback cb); @@ -264,13 +321,19 @@ class ResolvedService : public AsyncServiceRef { }; // static +std::vector<ResolvedService*>* ResolvedService::sAdbTransportServices = NULL; + +// static std::vector<ResolvedService*>* ResolvedService::sAdbSecurePairingServices = NULL; // static std::vector<ResolvedService*>* ResolvedService::sAdbSecureConnectServices = NULL; // static -void ResolvedService::initAdbSecure() { +void ResolvedService::initAdbServiceRegistries() { + if (!sAdbTransportServices) { + sAdbTransportServices = new ServiceRegistry; + } if (!sAdbSecurePairingServices) { sAdbSecurePairingServices = new ServiceRegistry; } @@ -283,17 +346,18 @@ void ResolvedService::initAdbSecure() { void ResolvedService::forEachService(const ServiceRegistry& services, const std::string& wanted_service_name, adb_secure_foreach_service_callback cb) { - initAdbSecure(); + initAdbServiceRegistries(); for (auto service : services) { auto service_name = service->serviceName(); + auto reg_type = service->regType(); auto ip = service->ipAddress(); auto port = service->port(); if (wanted_service_name == "") { - cb(service_name.c_str(), ip.c_str(), port); + cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port); } else if (service_name == wanted_service_name) { - cb(service_name.c_str(), ip.c_str(), port); + cb(service_name.c_str(), reg_type.c_str(), ip.c_str(), port); } } } @@ -301,7 +365,7 @@ void ResolvedService::forEachService(const ServiceRegistry& services, // static bool ResolvedService::connectByServiceName(const ServiceRegistry& services, const std::string& service_name) { - initAdbSecure(); + initAdbServiceRegistries(); for (auto service : services) { if (service_name == service->serviceName()) { D("Got service_name match [%s]", service->serviceName().c_str()); @@ -398,6 +462,9 @@ static void adb_RemoveDNSService(const char* regType, const char* serviceName) { int index = adb_DNSServiceIndexByName(regType); ResolvedService::ServiceRegistry* services; switch (index) { + case kADBTransportServiceRefIndex: + services = ResolvedService::sAdbTransportServices; + break; case kADBSecurePairingServiceRefIndex: services = ResolvedService::sAdbSecurePairingServices; break; @@ -521,8 +588,15 @@ static void DNSSD_API on_service_browsed(DNSServiceRef sdRef, DNSServiceFlags fl } void init_mdns_transport_discovery_thread(void) { - int errorCodes[kNumADBDNSServices]; + config_auto_connect_services(); + std::string res; + std::for_each(g_autoconn_whitelist.begin(), g_autoconn_whitelist.end(), [&](const int& i) { + res += kADBDNSServices[i]; + res += ","; + }); + D("mdns auto-connect whitelist: [%s]", res.data()); + int errorCodes[kNumADBDNSServices]; for (int i = 0; i < kNumADBDNSServices; ++i) { errorCodes[i] = DNSServiceBrowse(&service_refs[i], 0, 0, kADBDNSServices[i], nullptr, on_service_browsed, nullptr); @@ -542,6 +616,34 @@ void init_mdns_transport_discovery_thread(void) { } 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 000000000..b6a258204 --- /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 000000000..cced62a29 --- /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 000000000..45ebc3475 --- /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 000000000..257310c6a --- /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 000000000..3b2faa304 --- /dev/null +++ b/adb/coverage/show.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -euxo pipefail + +OUTPUT_DIR=$(realpath $(dirname "$0")) +. "$OUTPUT_DIR"/include.sh + +BASE_PATH=/proc/self/cwd/system/core/adb +PATHS="" +if [[ $# == 0 ]]; then + PATHS=$BASE_PATH +else + for arg in "$@"; do + PATHS="$PATHS $BASE_PATH/$arg" + done +fi + +cd $ANDROID_BUILD_TOP +llvm-cov show --instr-profile="$OUTPUT_DIR"/adbd.profdata \ + $ANDROID_PRODUCT_OUT/apex/com.android.adbd/bin/adbd \ + $PATHS \ + $ADB_TEST_BINARIES diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp index 9e02e89ab..55b77835d 100644 --- a/adb/daemon/main.cpp +++ b/adb/daemon/main.cpp @@ -301,6 +301,8 @@ int adbd_main(int server_port) { setup_adb(addrs); } + LOG(INFO) << "adbd started"; + D("adbd_main(): pre init_jdwp()"); init_jdwp(); D("adbd_main(): post init_jdwp()"); diff --git a/adb/daemon/transport_local.cpp b/adb/daemon/transport_local.cpp new file mode 100644 index 000000000..9e0b88737 --- /dev/null +++ b/adb/daemon/transport_local.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TRACE_TAG TRANSPORT + +#include "sysdeps.h" +#include "transport.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include <condition_variable> +#include <functional> +#include <memory> +#include <mutex> +#include <thread> +#include <unordered_map> +#include <vector> + +#include <android-base/parsenetaddress.h> +#include <android-base/stringprintf.h> +#include <android-base/thread_annotations.h> +#include <cutils/sockets.h> + +#if !ADB_HOST +#include <android-base/properties.h> +#endif + +#include "adb.h" +#include "adb_io.h" +#include "adb_unique_fd.h" +#include "adb_utils.h" +#include "socket_spec.h" +#include "sysdeps/chrono.h" + +void server_socket_thread(std::function<unique_fd(std::string_view, std::string*)> listen_func, + std::string_view addr) { + adb_thread_setname("server socket"); + + unique_fd serverfd; + std::string error; + + while (serverfd == -1) { + errno = 0; + serverfd = listen_func(addr, &error); + if (errno == EAFNOSUPPORT || errno == EINVAL || errno == EPROTONOSUPPORT) { + D("unrecoverable error: '%s'", error.c_str()); + return; + } else if (serverfd < 0) { + D("server: cannot bind socket yet: %s", error.c_str()); + std::this_thread::sleep_for(1s); + continue; + } + close_on_exec(serverfd.get()); + } + + while (true) { + D("server: trying to get new connection from fd %d", serverfd.get()); + unique_fd fd(adb_socket_accept(serverfd, nullptr, nullptr)); + if (fd >= 0) { + D("server: new connection on fd %d", fd.get()); + close_on_exec(fd.get()); + disable_tcp_nagle(fd.get()); + std::string serial = android::base::StringPrintf("host-%d", fd.get()); + // We don't care about port value in "register_socket_transport" as it is used + // only from ADB_HOST. "server_socket_thread" is never called from ADB_HOST. + register_socket_transport( + std::move(fd), std::move(serial), 0, 1, + [](atransport*) { return ReconnectResult::Abort; }, false); + } + } + D("transport: server_socket_thread() exiting"); +} + +unique_fd adb_listen(std::string_view addr, std::string* error) { + return unique_fd{socket_spec_listen(addr, error, nullptr)}; +} + +void local_init(const std::string& addr) { +#if !defined(__ANDROID__) + // Host adbd. + D("transport: local server init"); + std::thread(server_socket_thread, adb_listen, addr).detach(); +#else + D("transport: local server init"); + // For the adbd daemon in the system image we need to distinguish + // between the device, and the emulator. + if (addr.starts_with("tcp:") && use_qemu_goldfish()) { + std::thread(qemu_socket_thread, addr).detach(); + } else { + std::thread(server_socket_thread, adb_listen, addr).detach(); + } +#endif // !ADB_HOST +} + +int init_socket_transport(atransport* t, unique_fd fd, int adb_port, int local) { + t->type = kTransportLocal; + auto fd_connection = std::make_unique<FdConnection>(std::move(fd)); + t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection))); + return 0; +} diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp index fd550200f..70cb9b3a6 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 b569421db..7e1614835 100644 --- a/adb/libs/adbconnection/adbconnection_client.cpp +++ b/adb/libs/adbconnection/adbconnection_client.cpp @@ -122,16 +122,7 @@ AdbConnectionClientContext* adbconnection_client_new( 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 @@ AdbConnectionClientContext* adbconnection_client_new( 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 000000000..1f662c1ca --- /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 d87948c84..19a9030f2 100644 --- a/adb/services.cpp +++ b/adb/services.cpp @@ -202,11 +202,22 @@ static void wait_service(unique_fd fd, std::string serial, TransportId transport 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 c872fb0f7..03bdcbd8d 100755 --- a/adb/test_adb.py +++ b/adb/test_adb.py @@ -32,6 +32,8 @@ import threading 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 @@ class PowerTest(unittest.TestCase): # 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 71e32b78f..a7af53cee 100644 --- a/adb/tools/Android.bp +++ b/adb/tools/Android.bp @@ -34,3 +34,26 @@ cc_binary_host { ], }, } + +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 000000000..6f141bd92 --- /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 963c3c194..25ed36654 100644 --- a/adb/transport.cpp +++ b/adb/transport.cpp @@ -505,11 +505,10 @@ bool FdConnection::DoTlsHandshake(RSA* key, std::string* auth_key) { int osh = cast_handle_to_int(adb_get_os_handle(fd_)); #if ADB_HOST - tls_ = TlsConnection::Create(TlsConnection::Role::Client, + tls_ = TlsConnection::Create(TlsConnection::Role::Client, x509_str, evp_str, osh); #else - tls_ = TlsConnection::Create(TlsConnection::Role::Server, + tls_ = TlsConnection::Create(TlsConnection::Role::Server, x509_str, evp_str, osh); #endif - x509_str, evp_str, osh); CHECK(tls_); #if ADB_HOST // TLS 1.3 gives the client no message if the server rejected the diff --git a/base/Android.bp b/base/Android.bp index 894ad6c50..58af2eb8f 100644 --- a/base/Android.bp +++ b/base/Android.bp @@ -157,6 +157,7 @@ cc_test { "errors_test.cpp", "expected_test.cpp", "file_test.cpp", + "logging_splitters_test.cpp", "logging_test.cpp", "macros_test.cpp", "mapped_file_test.cpp", diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h index 9603bb1cf..9470344e8 100644 --- a/base/include/android-base/expected.h +++ b/base/include/android-base/expected.h @@ -182,7 +182,7 @@ class _NODISCARD_ expected { !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> && std::is_convertible_v<U&&, T> /* non-explicit */ )> - // NOLINTNEXTLINE(google-explicit-constructor) + // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload) constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {} template <class U = T _ENABLE_IF( @@ -192,6 +192,7 @@ class _NODISCARD_ expected { !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> && !std::is_convertible_v<U&&, T> /* explicit */ )> + // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {} template<class G = E _ENABLE_IF( @@ -387,13 +388,9 @@ class _NODISCARD_ expected { template<class T1, class E1, class T2, class E2> constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) { - if (x.has_value() != y.has_value()) { - return false; - } else if (!x.has_value()) { - return x.error() == y.error(); - } else { - return *x == *y; - } + if (x.has_value() != y.has_value()) return false; + if (!x.has_value()) return x.error() == y.error(); + return *x == *y; } template<class T1, class E1, class T2, class E2> @@ -581,35 +578,23 @@ class _NODISCARD_ expected<void, E> { template<class E1, class E2> constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) { - if (x.has_value() != y.has_value()) { - return false; - } else if (!x.has_value()) { - return x.error() == y.error(); - } else { - return true; - } + if (x.has_value() != y.has_value()) return false; + if (!x.has_value()) return x.error() == y.error(); + return true; } template<class T1, class E1, class E2> constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) { - if (x.has_value() != y.has_value()) { - return false; - } else if (!x.has_value()) { - return x.error() == y.error(); - } else { - return false; - } + if (x.has_value() != y.has_value()) return false; + if (!x.has_value()) return x.error() == y.error(); + return false; } template<class E1, class T2, class E2> constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) { - if (x.has_value() != y.has_value()) { - return false; - } else if (!x.has_value()) { - return x.error() == y.error(); - } else { - return false; - } + if (x.has_value() != y.has_value()) return false; + if (!x.has_value()) return x.error() == y.error(); + return false; } template<class E> @@ -623,7 +608,7 @@ class unexpected { std::is_constructible_v<E, Err> && !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> && !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)> - // NOLINTNEXTLINE(google-explicit-constructor) + // NOLINTNEXTLINE(google-explicit-constructor,bugprone-forwarding-reference-overload) constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {} template<class U, class... Args _ENABLE_IF( diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h index accc225db..26827fb83 100644 --- a/base/include/android-base/logging.h +++ b/base/include/android-base/logging.h @@ -118,8 +118,10 @@ void DefaultAborter(const char* abort_message); void SetDefaultTag(const std::string& tag); -// We expose this even though it is the default because a user that wants to -// override the default log buffer will have to construct this themselves. +// The LogdLogger sends chunks of up to ~4000 bytes at a time to logd. It does not prevent other +// threads from writing to logd between sending each chunk, so other threads may interleave their +// messages. If preventing interleaving is required, then a custom logger that takes a lock before +// calling this logger should be provided. class LogdLogger { public: explicit LogdLogger(LogId default_log_id = android::base::MAIN); diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h index 5e65876c5..56a4f3e80 100644 --- a/base/include/android-base/result.h +++ b/base/include/android-base/result.h @@ -130,6 +130,7 @@ class Error { template <typename T> Error& operator<<(T&& t) { + // NOLINTNEXTLINE(bugprone-suspicious-semicolon) if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) { errno_ = t.code(); return (*this) << t.message(); diff --git a/base/logging.cpp b/base/logging.cpp index cd460eb46..6e9c67fc4 100644 --- a/base/logging.cpp +++ b/base/logging.cpp @@ -61,6 +61,7 @@ #include <android-base/threads.h> #include "liblog_symbols.h" +#include "logging_splitters.h" namespace android { namespace base { @@ -190,12 +191,6 @@ static int32_t LogSeverityToPriority(LogSeverity severity) { } } -static std::mutex& LoggingLock() { - static auto& logging_lock = *new std::mutex(); - return logging_lock; -} - -// Only used for Q fallback. static LogFunction& Logger() { #ifdef __ANDROID__ static auto& logger = *new LogFunction(LogdLogger()); @@ -205,7 +200,6 @@ static LogFunction& Logger() { return logger; } -// Only used for Q fallback. static AbortFunction& Aborter() { static auto& aborter = *new AbortFunction(DefaultAborter); return aborter; @@ -241,8 +235,8 @@ static bool gInitialized = false; static LogSeverity gMinimumLogSeverity = INFO; #if defined(__linux__) -void KernelLogger(android::base::LogId, android::base::LogSeverity severity, - const char* tag, const char*, unsigned int, const char* msg) { +static void KernelLogLine(const char* msg, int length, android::base::LogSeverity severity, + const char* tag) { // clang-format off static constexpr int kLogSeverityToKernelLogLevel[] = { [android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log @@ -267,7 +261,7 @@ void KernelLogger(android::base::LogId, android::base::LogSeverity severity, // TODO: should we automatically break up long lines into multiple lines? // Or we could log but with something like "..." at the end? char buf[1024]; - size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg); + size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg); if (size > sizeof(buf)) { size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n", level, tag, size); @@ -278,6 +272,11 @@ void KernelLogger(android::base::LogId, android::base::LogSeverity severity, iov[0].iov_len = size; TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1)); } + +void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag, + const char*, unsigned int, const char* full_message) { + SplitByLines(full_message, KernelLogLine, severity, tag); +} #endif void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line, @@ -290,21 +289,10 @@ void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file #else localtime_r(&t, &now); #endif + auto output_string = + StderrOutputGenerator(now, getpid(), GetThreadId(), severity, tag, file, line, message); - char timestamp[32]; - strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now); - - static const char log_characters[] = "VDIWEFF"; - static_assert(arraysize(log_characters) - 1 == FATAL + 1, - "Mismatch in size of log_characters and values in LogSeverity"); - char severity_char = log_characters[severity]; - if (file != nullptr) { - fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char, - timestamp, getpid(), GetThreadId(), file, line, message); - } else { - fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n", tag ? tag : "nullptr", severity_char, - timestamp, getpid(), GetThreadId(), message); - } + fputs(output_string.c_str(), stderr); } void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/, @@ -326,26 +314,9 @@ void DefaultAborter(const char* abort_message) { abort(); } - -LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) { -} - -void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, - const char* file, unsigned int line, - const char* message) { - int32_t priority = LogSeverityToPriority(severity); - if (id == DEFAULT) { - id = default_log_id_; - } - +static void LogdLogChunk(LogId id, LogSeverity severity, const char* tag, const char* message) { int32_t lg_id = LogIdTolog_id_t(id); - - char log_message_with_file[4068]; // LOGGER_ENTRY_MAX_PAYLOAD, not available in the NDK. - if (priority == ANDROID_LOG_FATAL && file != nullptr) { - snprintf(log_message_with_file, sizeof(log_message_with_file), "%s:%u] %s", file, line, - message); - message = log_message_with_file; - } + int32_t priority = LogSeverityToPriority(severity); static auto& liblog_functions = GetLibLogFunctions(); if (liblog_functions) { @@ -357,6 +328,17 @@ void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, } } +LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {} + +void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file, + unsigned int line, const char* message) { + if (id == DEFAULT) { + id = default_log_id_; + } + + SplitByLogdChunks(id, severity, tag, file, line, message, LogdLogChunk); +} + void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) { SetLogger(std::forward<LogFunction>(logger)); SetAborter(std::forward<AbortFunction>(aborter)); @@ -416,45 +398,27 @@ void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) { } void SetLogger(LogFunction&& logger) { + Logger() = std::move(logger); + static auto& liblog_functions = GetLibLogFunctions(); if (liblog_functions) { - // We need to atomically swap the old and new pointers since other threads may be logging. - // We know all threads will be using the new logger after __android_log_set_logger() returns, - // so we can delete it then. - // This leaks one std::function<> per instance of libbase if multiple copies of libbase within a - // single process call SetLogger(). That is the same cost as having a static - // std::function<>, which is the not-thread-safe alternative. - static std::atomic<LogFunction*> logger_function(nullptr); - auto* old_logger_function = logger_function.exchange(new LogFunction(logger)); liblog_functions->__android_log_set_logger([](const struct __android_log_message* log_message) { auto log_id = log_id_tToLogId(log_message->buffer_id); auto severity = PriorityToLogSeverity(log_message->priority); - auto& function = *logger_function.load(std::memory_order_acquire); - function(log_id, severity, log_message->tag, log_message->file, log_message->line, + Logger()(log_id, severity, log_message->tag, log_message->file, log_message->line, log_message->message); }); - delete old_logger_function; - } else { - std::lock_guard<std::mutex> lock(LoggingLock()); - Logger() = std::move(logger); } } void SetAborter(AbortFunction&& aborter) { + Aborter() = std::move(aborter); + static auto& liblog_functions = GetLibLogFunctions(); if (liblog_functions) { - // See the comment in SetLogger(). - static std::atomic<AbortFunction*> abort_function(nullptr); - auto* old_abort_function = abort_function.exchange(new AbortFunction(aborter)); - liblog_functions->__android_log_set_aborter([](const char* abort_message) { - auto& function = *abort_function.load(std::memory_order_acquire); - function(abort_message); - }); - delete old_abort_function; - } else { - std::lock_guard<std::mutex> lock(LoggingLock()); - Aborter() = std::move(aborter); + liblog_functions->__android_log_set_aborter( + [](const char* abort_message) { Aborter()(abort_message); }); } } @@ -535,26 +499,8 @@ LogMessage::~LogMessage() { #endif } - { - // Do the actual logging with the lock held. - std::lock_guard<std::mutex> lock(LoggingLock()); - if (msg.find('\n') == std::string::npos) { - LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(), - msg.c_str()); - } else { - msg += '\n'; - size_t i = 0; - while (i < msg.size()) { - size_t nl = msg.find('\n', i); - msg[nl] = '\0'; - LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(), - &msg[i]); - // Undo the zero-termination so we can give the complete message to the aborter. - msg[nl] = '\n'; - i = nl + 1; - } - } - } + LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(), + msg.c_str()); // Abort if necessary. if (data_->GetSeverity() == FATAL) { diff --git a/base/logging_splitters.h b/base/logging_splitters.h new file mode 100644 index 000000000..2ec2b205d --- /dev/null +++ b/base/logging_splitters.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <inttypes.h> + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> + +#define LOGGER_ENTRY_MAX_PAYLOAD 4068 // This constant is not in the NDK. + +namespace android { +namespace base { + +// This splits the message up line by line, by calling log_function with a pointer to the start of +// each line and the size up to the newline character. It sends size = -1 for the final line. +template <typename F, typename... Args> +static void SplitByLines(const char* msg, const F& log_function, Args&&... args) { + const char* newline = strchr(msg, '\n'); + while (newline != nullptr) { + log_function(msg, newline - msg, args...); + msg = newline + 1; + newline = strchr(msg, '\n'); + } + + log_function(msg, -1, args...); +} + +// This splits the message up into chunks that logs can process delimited by new lines. It calls +// log_function with the exact null terminated message that should be sent to logd. +// Note, despite the loops and snprintf's, if severity is not fatal and there are no new lines, +// this function simply calls log_function with msg without any extra overhead. +template <typename F> +static void SplitByLogdChunks(LogId log_id, LogSeverity severity, const char* tag, const char* file, + unsigned int line, const char* msg, const F& log_function) { + // The maximum size of a payload, after the log header that logd will accept is + // LOGGER_ENTRY_MAX_PAYLOAD, so subtract the other elements in the payload to find the size of + // the string that we can log in each pass. + // The protocol is documented in liblog/README.protocol.md. + // Specifically we subtract a byte for the priority, the length of the tag + its null terminator, + // and an additional byte for the null terminator on the payload. We subtract an additional 32 + // bytes for slack, similar to java/android/util/Log.java. + ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35; + if (max_size <= 0) { + abort(); + } + // If we're logging a fatal message, we'll append the file and line numbers. + bool add_file = file != nullptr && (severity == FATAL || severity == FATAL_WITHOUT_ABORT); + + std::string file_header; + if (add_file) { + file_header = StringPrintf("%s:%u] ", file, line); + } + int file_header_size = file_header.size(); + + __attribute__((uninitialized)) char logd_chunk[max_size + 1]; + ptrdiff_t chunk_position = 0; + + auto call_log_function = [&]() { + log_function(log_id, severity, tag, logd_chunk); + chunk_position = 0; + }; + + auto write_to_logd_chunk = [&](const char* message, int length) { + int size_written = 0; + const char* new_line = chunk_position > 0 ? "\n" : ""; + if (add_file) { + size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position, + "%s%s%.*s", new_line, file_header.c_str(), length, message); + } else { + size_written = snprintf(logd_chunk + chunk_position, sizeof(logd_chunk) - chunk_position, + "%s%.*s", new_line, length, message); + } + + // This should never fail, if it does and we set size_written to 0, which will skip this line + // and move to the next one. + if (size_written < 0) { + size_written = 0; + } + chunk_position += size_written; + }; + + const char* newline = strchr(msg, '\n'); + while (newline != nullptr) { + // If we have data in the buffer and this next line doesn't fit, write the buffer. + if (chunk_position != 0 && chunk_position + (newline - msg) + 1 + file_header_size > max_size) { + call_log_function(); + } + + // Otherwise, either the next line fits or we have any empty buffer and too large of a line to + // ever fit, in both cases, we add it to the buffer and continue. + write_to_logd_chunk(msg, newline - msg); + + msg = newline + 1; + newline = strchr(msg, '\n'); + } + + // If we have left over data in the buffer and we can fit the rest of msg, add it to the buffer + // then write the buffer. + if (chunk_position != 0 && + chunk_position + static_cast<int>(strlen(msg)) + 1 + file_header_size <= max_size) { + write_to_logd_chunk(msg, -1); + call_log_function(); + } else { + // If the buffer is not empty and we can't fit the rest of msg into it, write its contents. + if (chunk_position != 0) { + call_log_function(); + } + // Then write the rest of the msg. + if (add_file) { + snprintf(logd_chunk, sizeof(logd_chunk), "%s%s", file_header.c_str(), msg); + log_function(log_id, severity, tag, logd_chunk); + } else { + log_function(log_id, severity, tag, msg); + } + } +} + +static std::pair<int, int> CountSizeAndNewLines(const char* message) { + int size = 0; + int new_lines = 0; + while (*message != '\0') { + size++; + if (*message == '\n') { + ++new_lines; + } + ++message; + } + return {size, new_lines}; +} + +// This adds the log header to each line of message and returns it as a string intended to be +// written to stderr. +static std::string StderrOutputGenerator(const struct tm& now, int pid, uint64_t tid, + LogSeverity severity, const char* tag, const char* file, + unsigned int line, const char* message) { + char timestamp[32]; + strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now); + + static const char log_characters[] = "VDIWEFF"; + static_assert(arraysize(log_characters) - 1 == FATAL + 1, + "Mismatch in size of log_characters and values in LogSeverity"); + char severity_char = log_characters[severity]; + std::string line_prefix; + if (file != nullptr) { + line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " %s:%u] ", tag ? tag : "nullptr", + severity_char, timestamp, pid, tid, file, line); + } else { + line_prefix = StringPrintf("%s %c %s %5d %5" PRIu64 " ", tag ? tag : "nullptr", severity_char, + timestamp, pid, tid); + } + + auto [size, new_lines] = CountSizeAndNewLines(message); + std::string output_string; + output_string.reserve(size + new_lines * line_prefix.size() + 1); + + auto concat_lines = [&](const char* message, int size) { + output_string.append(line_prefix); + if (size == -1) { + output_string.append(message); + } else { + output_string.append(message, size); + } + output_string.append("\n"); + }; + SplitByLines(message, concat_lines); + return output_string; +} + +} // namespace base +} // namespace android diff --git a/base/logging_splitters_test.cpp b/base/logging_splitters_test.cpp new file mode 100644 index 000000000..679d19ed1 --- /dev/null +++ b/base/logging_splitters_test.cpp @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "logging_splitters.h" + +#include <string> +#include <vector> + +#include <android-base/strings.h> +#include <gtest/gtest.h> + +namespace android { +namespace base { + +void TestNewlineSplitter(const std::string& input, + const std::vector<std::string>& expected_output) { + std::vector<std::string> output; + auto logger_function = [&](const char* msg, int length) { + if (length == -1) { + output.push_back(msg); + } else { + output.push_back(std::string(msg, length)); + } + }; + SplitByLines(input.c_str(), logger_function); + + EXPECT_EQ(expected_output, output); +} + +TEST(logging_splitters, NewlineSplitter_EmptyString) { + TestNewlineSplitter("", std::vector<std::string>{""}); +} + +TEST(logging_splitters, NewlineSplitter_BasicString) { + TestNewlineSplitter("normal string", std::vector<std::string>{"normal string"}); +} + +TEST(logging_splitters, NewlineSplitter_ormalBasicStringTrailingNewline) { + TestNewlineSplitter("normal string\n", std::vector<std::string>{"normal string", ""}); +} + +TEST(logging_splitters, NewlineSplitter_MultilineTrailing) { + TestNewlineSplitter("normal string\nsecond string\nthirdstring", + std::vector<std::string>{"normal string", "second string", "thirdstring"}); +} + +TEST(logging_splitters, NewlineSplitter_MultilineTrailingNewline) { + TestNewlineSplitter( + "normal string\nsecond string\nthirdstring\n", + std::vector<std::string>{"normal string", "second string", "thirdstring", ""}); +} + +TEST(logging_splitters, NewlineSplitter_MultilineEmbeddedNewlines) { + TestNewlineSplitter( + "normal string\n\n\nsecond string\n\nthirdstring\n", + std::vector<std::string>{"normal string", "", "", "second string", "", "thirdstring", ""}); +} + +void TestLogdChunkSplitter(const std::string& tag, const std::string& file, + const std::string& input, + const std::vector<std::string>& expected_output) { + std::vector<std::string> output; + auto logger_function = [&](LogId, LogSeverity, const char*, const char* msg) { + output.push_back(msg); + }; + + SplitByLogdChunks(MAIN, FATAL, tag.c_str(), file.empty() ? nullptr : file.c_str(), 1000, + input.c_str(), logger_function); + + auto return_lengths = [&] { + std::string sizes; + sizes += "expected_output sizes:"; + for (const auto& string : expected_output) { + sizes += " " + std::to_string(string.size()); + } + sizes += "\noutput sizes:"; + for (const auto& string : output) { + sizes += " " + std::to_string(string.size()); + } + return sizes; + }; + + EXPECT_EQ(expected_output, output) << return_lengths(); +} + +TEST(logging_splitters, LogdChunkSplitter_EmptyString) { + TestLogdChunkSplitter("tag", "", "", std::vector<std::string>{""}); +} + +TEST(logging_splitters, LogdChunkSplitter_BasicString) { + TestLogdChunkSplitter("tag", "", "normal string", std::vector<std::string>{"normal string"}); +} + +TEST(logging_splitters, LogdChunkSplitter_NormalBasicStringTrailingNewline) { + TestLogdChunkSplitter("tag", "", "normal string\n", std::vector<std::string>{"normal string\n"}); +} + +TEST(logging_splitters, LogdChunkSplitter_MultilineTrailing) { + TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring", + std::vector<std::string>{"normal string\nsecond string\nthirdstring"}); +} + +TEST(logging_splitters, LogdChunkSplitter_MultilineTrailingNewline) { + TestLogdChunkSplitter("tag", "", "normal string\nsecond string\nthirdstring\n", + std::vector<std::string>{"normal string\nsecond string\nthirdstring\n"}); +} + +TEST(logging_splitters, LogdChunkSplitter_MultilineEmbeddedNewlines) { + TestLogdChunkSplitter( + "tag", "", "normal string\n\n\nsecond string\n\nthirdstring\n", + std::vector<std::string>{"normal string\n\n\nsecond string\n\nthirdstring\n"}); +} + +// This test should return the same string, the logd logger itself will truncate down to size. +// This has historically been the behavior both in libbase and liblog. +TEST(logging_splitters, LogdChunkSplitter_HugeLineNoNewline) { + auto long_string = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x'); + ASSERT_EQ(LOGGER_ENTRY_MAX_PAYLOAD, static_cast<int>(long_string.size())); + + TestLogdChunkSplitter("tag", "", long_string, std::vector{long_string}); +} + +std::string ReduceToMaxSize(const std::string& tag, const std::string& string) { + return string.substr(0, LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35); +} + +TEST(logging_splitters, LogdChunkSplitter_MultipleHugeLineNoNewline) { + auto long_string_x = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'x'); + auto long_string_y = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'y'); + auto long_string_z = std::string(LOGGER_ENTRY_MAX_PAYLOAD, 'z'); + + auto long_strings = long_string_x + '\n' + long_string_y + '\n' + long_string_z; + + std::string tag = "tag"; + std::vector expected = {ReduceToMaxSize(tag, long_string_x), ReduceToMaxSize(tag, long_string_y), + long_string_z}; + + TestLogdChunkSplitter(tag, "", long_strings, expected); +} + +// With a ~4k buffer, we should print 2 long strings per logger call. +TEST(logging_splitters, LogdChunkSplitter_Multiple2kLines) { + std::vector expected = { + std::string(2000, 'a') + '\n' + std::string(2000, 'b'), + std::string(2000, 'c') + '\n' + std::string(2000, 'd'), + std::string(2000, 'e') + '\n' + std::string(2000, 'f'), + }; + + auto long_strings = Join(expected, '\n'); + + TestLogdChunkSplitter("tag", "", long_strings, expected); +} + +TEST(logging_splitters, LogdChunkSplitter_ExactSizedLines) { + const char* tag = "tag"; + ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - strlen(tag) - 35; + auto long_string_a = std::string(max_size, 'a'); + auto long_string_b = std::string(max_size, 'b'); + auto long_string_c = std::string(max_size, 'c'); + + auto long_strings = long_string_a + '\n' + long_string_b + '\n' + long_string_c; + + TestLogdChunkSplitter(tag, "", long_strings, + std::vector{long_string_a, long_string_b, long_string_c}); +} + +TEST(logging_splitters, LogdChunkSplitter_UnderEqualOver) { + std::string tag = "tag"; + ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35; + + auto first_string_size = 1000; + auto first_string = std::string(first_string_size, 'a'); + auto second_string_size = max_size - first_string_size - 1; + auto second_string = std::string(second_string_size, 'b'); + + auto exact_string = std::string(max_size, 'c'); + + auto large_string = std::string(max_size + 50, 'd'); + + auto final_string = std::string("final string!\n\nfinal \n \n final \n"); + + std::vector expected = {first_string + '\n' + second_string, exact_string, + ReduceToMaxSize(tag, large_string), final_string}; + + std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string, + final_string}; + auto long_strings = Join(input_strings, '\n'); + + TestLogdChunkSplitter(tag, "", long_strings, expected); +} + +TEST(logging_splitters, LogdChunkSplitter_WithFile) { + std::string tag = "tag"; + std::string file = "/path/to/myfile.cpp"; + int line = 1000; + auto file_header = StringPrintf("%s:%d] ", file.c_str(), line); + ptrdiff_t max_size = LOGGER_ENTRY_MAX_PAYLOAD - tag.size() - 35; + + auto first_string_size = 1000; + auto first_string = std::string(first_string_size, 'a'); + auto second_string_size = max_size - first_string_size - 1 - 2 * file_header.size(); + auto second_string = std::string(second_string_size, 'b'); + + auto exact_string = std::string(max_size - file_header.size(), 'c'); + + auto large_string = std::string(max_size + 50, 'd'); + + auto final_string = std::string("final string!"); + + std::vector expected = { + file_header + first_string + '\n' + file_header + second_string, file_header + exact_string, + file_header + ReduceToMaxSize(file_header + tag, large_string), file_header + final_string}; + + std::vector input_strings = {first_string + '\n' + second_string, exact_string, large_string, + final_string}; + auto long_strings = Join(input_strings, '\n'); + + TestLogdChunkSplitter(tag, file, long_strings, expected); +} + +// We set max_size based off of tag, so if it's too large, the buffer will be sized wrong. +// We could recover from this, but it's certainly an error for someone to attempt to use a tag this +// large, so we abort instead. +TEST(logging_splitters, LogdChunkSplitter_TooLongTag) { + auto long_tag = std::string(5000, 'x'); + auto logger_function = [](LogId, LogSeverity, const char*, const char*) {}; + ASSERT_DEATH( + SplitByLogdChunks(MAIN, ERROR, long_tag.c_str(), nullptr, 0, "message", logger_function), ""); +} + +// We do handle excessively large file names correctly however. +TEST(logging_splitters, LogdChunkSplitter_TooLongFile) { + auto long_file = std::string(5000, 'x'); + std::string tag = "tag"; + + std::vector expected = {ReduceToMaxSize(tag, long_file), ReduceToMaxSize(tag, long_file)}; + + TestLogdChunkSplitter(tag, long_file, "can't see me\nor me", expected); +} + +void TestStderrOutputGenerator(const char* tag, const char* file, int line, const char* message, + const std::string& expected) { + // All log messages will show "01-01 00:00:00" + struct tm now = { + .tm_sec = 0, + .tm_min = 0, + .tm_hour = 0, + .tm_mday = 1, + .tm_mon = 0, + .tm_year = 1970, + }; + + int pid = 1234; // All log messages will have 1234 for their PID. + uint64_t tid = 4321; // All log messages will have 4321 for their TID. + + auto result = StderrOutputGenerator(now, pid, tid, ERROR, tag, file, line, message); + EXPECT_EQ(expected, result); +} + +TEST(logging_splitters, StderrOutputGenerator_Basic) { + TestStderrOutputGenerator(nullptr, nullptr, 0, "simple message", + "nullptr E 01-01 00:00:00 1234 4321 simple message\n"); + TestStderrOutputGenerator("tag", nullptr, 0, "simple message", + "tag E 01-01 00:00:00 1234 4321 simple message\n"); + TestStderrOutputGenerator( + "tag", "/path/to/some/file", 0, "simple message", + "tag E 01-01 00:00:00 1234 4321 /path/to/some/file:0] simple message\n"); +} + +TEST(logging_splitters, StderrOutputGenerator_NewlineTagAndFile) { + TestStderrOutputGenerator("tag\n\n", nullptr, 0, "simple message", + "tag\n\n E 01-01 00:00:00 1234 4321 simple message\n"); + TestStderrOutputGenerator( + "tag", "/path/to/some/file\n\n", 0, "simple message", + "tag E 01-01 00:00:00 1234 4321 /path/to/some/file\n\n:0] simple message\n"); +} + +TEST(logging_splitters, StderrOutputGenerator_TrailingNewLine) { + TestStderrOutputGenerator( + "tag", nullptr, 0, "simple message\n", + "tag E 01-01 00:00:00 1234 4321 simple message\ntag E 01-01 00:00:00 1234 4321 \n"); +} + +TEST(logging_splitters, StderrOutputGenerator_MultiLine) { + const char* expected_result = + "tag E 01-01 00:00:00 1234 4321 simple message\n" + "tag E 01-01 00:00:00 1234 4321 \n" + "tag E 01-01 00:00:00 1234 4321 \n" + "tag E 01-01 00:00:00 1234 4321 another message \n" + "tag E 01-01 00:00:00 1234 4321 \n" + "tag E 01-01 00:00:00 1234 4321 final message \n" + "tag E 01-01 00:00:00 1234 4321 \n" + "tag E 01-01 00:00:00 1234 4321 \n" + "tag E 01-01 00:00:00 1234 4321 \n"; + + TestStderrOutputGenerator("tag", nullptr, 0, + "simple message\n\n\nanother message \n\n final message \n\n\n", + expected_result); +} + +TEST(logging_splitters, StderrOutputGenerator_MultiLineLong) { + auto long_string_a = std::string(4000, 'a'); + auto long_string_b = std::string(4000, 'b'); + + auto message = long_string_a + '\n' + long_string_b; + auto expected_result = "tag E 01-01 00:00:00 1234 4321 " + long_string_a + '\n' + + "tag E 01-01 00:00:00 1234 4321 " + long_string_b + '\n'; + TestStderrOutputGenerator("tag", nullptr, 0, message.c_str(), expected_result); +} + +} // namespace base +} // namespace android diff --git a/base/logging_test.cpp b/base/logging_test.cpp index 3a453e66c..593e2c100 100644 --- a/base/logging_test.cpp +++ b/base/logging_test.cpp @@ -24,8 +24,10 @@ #include <regex> #include <string> +#include <thread> #include "android-base/file.h" +#include "android-base/scopeguard.h" #include "android-base/stringprintf.h" #include "android-base/test_utils.h" @@ -596,7 +598,7 @@ TEST(logging, LOG_FATAL_ABORTER_MESSAGE) { CapturedStderr cap; LOG(FATAL) << "foo\nbar"; - EXPECT_EQ(CountLineAborter::newline_count, 1U + 1U); // +1 for final '\n'. + EXPECT_EQ(CountLineAborter::newline_count, 1U); } __attribute__((constructor)) void TestLoggingInConstructor() { @@ -617,3 +619,55 @@ TEST(logging, StdioLogger) { // Whereas ERROR logging includes the program name. ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str()); } + +TEST(logging, ForkSafe) { +#if !defined(_WIN32) + using namespace android::base; + SetLogger( + [&](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) { sleep(3); }); + + auto guard = make_scope_guard([&] { +#ifdef __ANDROID__ + SetLogger(LogdLogger()); +#else + SetLogger(StderrLogger); +#endif + }); + + auto thread = std::thread([] { + LOG(ERROR) << "This should sleep for 3 seconds, long enough to fork another process, if there " + "is no intervention"; + }); + thread.detach(); + + auto pid = fork(); + ASSERT_NE(-1, pid); + + if (pid == 0) { + // Reset the logger, so the next message doesn't sleep(). + SetLogger([](LogId, LogSeverity, const char*, const char*, unsigned int, const char*) {}); + LOG(ERROR) << "This should succeed in the child, only if libbase is forksafe."; + _exit(EXIT_SUCCESS); + } + + // Wait for up to 3 seconds for the child to exit. + int tries = 3; + bool found_child = false; + while (tries-- > 0) { + auto result = waitpid(pid, nullptr, WNOHANG); + EXPECT_NE(-1, result); + if (result == pid) { + found_child = true; + break; + } + sleep(1); + } + + EXPECT_TRUE(found_child); + + // Kill the child if it did not exit. + if (!found_child) { + kill(pid, SIGKILL); + } +#endif +} diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp index e0168d539..d6b2e2537 100644 --- a/debuggerd/libdebuggerd/tombstone.cpp +++ b/debuggerd/libdebuggerd/tombstone.cpp @@ -43,6 +43,7 @@ #include <android-base/unique_fd.h> #include <android/log.h> #include <log/log.h> +#include <log/log_read.h> #include <log/logprint.h> #include <private/android_filesystem_config.h> #include <unwindstack/DexFiles.h> diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp index 0c2569d6a..d2daaa197 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::base::unique_fd; 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 @@ static bool PrepareZramDevice(const std::string& loop, off64_t size, const std:: 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 @@ static bool PrepareZramDevice(const std::string& loop, off64_t size, const std:: 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 d4964669a..2f516fa9b 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -40,6 +40,20 @@ bool LinearExtent::AddTo(LpMetadata* out) const { 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 @@ std::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent( 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 @@ bool MetadataBuilder::IsAnyRegionAllocated(const LinearExtent& candidate) const 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 ca8df61fd..977ebe3c0 100644 --- a/fs_mgr/liblp/builder_test.cpp +++ b/fs_mgr/liblp/builder_test.cpp @@ -937,3 +937,85 @@ TEST_F(BuilderTest, ExpandedHeader) { 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 382d53d52..99bff6e03 100644 --- a/fs_mgr/liblp/device_test.cpp +++ b/fs_mgr/liblp/device_test.cpp @@ -56,6 +56,10 @@ TEST_F(DeviceTest, BlockDeviceInfo) { // 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 f7738fb88..bd3915061 100644 --- a/fs_mgr/liblp/include/liblp/builder.h +++ b/fs_mgr/liblp/include/liblp/builder.h @@ -71,9 +71,8 @@ class LinearExtent final : public Extent { 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 bd788f5a9..384595db6 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -26,7 +26,6 @@ cc_defaults { "libbase", "libcutils", "liblog", - "liblp", ], static_libs: [ "libdm", @@ -73,6 +72,7 @@ filegroup { "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 000000000..ef9d64849 --- /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 000000000..758d66cc2 --- /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 1daa83b8a..fff667ee5 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -70,6 +70,8 @@ struct AutoDeleteCowImage; struct AutoDeleteSnapshot; struct AutoDeviceList; struct PartitionCowCreator; +class ISnapshotMergeStats; +class SnapshotMergeStats; class SnapshotStatus; static constexpr const std::string_view kCowGroupName = "cow"; @@ -83,17 +85,7 @@ enum class CreateResult : unsigned int { 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 @@ class SnapshotManager final { 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 @@ class SnapshotManager final { // 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 @@ class SnapshotManager final { // // 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 @@ class SnapshotManager final { // 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 @@ class SnapshotManager final { // // 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 @@ class SnapshotManager final { // 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 @@ class SnapshotManager final { // 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 91dd34f80..4caf63231 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 @@ class SnapshotMergeStats { // 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 000000000..9b2590ccd --- /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 c9fa28e9f..eafeea215 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -2685,5 +2685,9 @@ bool SnapshotManager::UpdateForwardMergeIndicator(bool wipe) { 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 000000000..2747b037f --- /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 c662838fd..53a37bebd 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 0749fe3b8..f3b584c4c 100644 --- a/init/mount_namespace.cpp +++ b/init/mount_namespace.cpp @@ -240,14 +240,20 @@ bool SetupMountNamespaces() { // 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/libcutils/arch-x86/android_memset16.S b/libcutils/arch-x86/android_memset16.S index cb2ff1412..f4d497e6f 100755 --- a/libcutils/arch-x86/android_memset16.S +++ b/libcutils/arch-x86/android_memset16.S @@ -105,14 +105,16 @@ name: \ /* 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 f4326dc90..b928f6b11 100755 --- a/libcutils/arch-x86/android_memset32.S +++ b/libcutils/arch-x86/android_memset32.S @@ -105,14 +105,16 @@ name: \ /* 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 512c7cd7b..8a0ebf22f 100644 --- a/liblog/include/android/log.h +++ b/liblog/include/android/log.h @@ -185,14 +185,26 @@ int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fm * 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 @@ typedef void (*__android_aborter_function)(const char* abort_message); * 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 @@ void __android_log_set_logger(__android_logger_function logger) __INTRODUCED_IN( * 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 @@ void __android_log_logd_logger(const struct __android_log_message* log_message) * 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 @@ void __android_log_stderr_logger(const struct __android_log_message* log_message * 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,23 +309,41 @@ __INTRODUCED_IN(30); * 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 len the length of the tag. * @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. + * @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_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 @@ int32_t __android_log_set_minimum_priority(int32_t priority) __INTRODUCED_IN(30) * 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 90d1e760e..f27b8e654 100644 --- a/liblog/include/log/log.h +++ b/liblog/include/log/log.h @@ -29,7 +29,6 @@ #include <log/log_id.h> #include <log/log_main.h> #include <log/log_radio.h> -#include <log/log_read.h> #include <log/log_safetynet.h> #include <log/log_system.h> #include <log/log_time.h> @@ -65,6 +64,13 @@ extern "C" { #endif /* + * The maximum size of the log entry payload that can be + * written to the logger. An attempt to write more than + * this amount will result in a truncated log entry. + */ +#define LOGGER_ENTRY_MAX_PAYLOAD 4068 + +/* * Event logging. */ @@ -87,8 +93,6 @@ int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len); /* * Event log entry types. */ -#ifndef __AndroidEventLogType_defined -#define __AndroidEventLogType_defined typedef enum { /* Special markers for android_log_list_element type */ EVENT_TYPE_LIST_STOP = '\n', /* declare end of list */ @@ -101,9 +105,6 @@ typedef enum { 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/include/log/log_read.h b/liblog/include/log/log_read.h index e2bc29721..ffd3b5230 100644 --- a/liblog/include/log/log_read.h +++ b/liblog/include/log/log_read.h @@ -48,13 +48,6 @@ struct logger_entry { }; /* - * The maximum size of the log entry payload that can be - * written to the logger. An attempt to write more than - * this amount will result in a truncated log entry. - */ -#define LOGGER_ENTRY_MAX_PAYLOAD 4068 - -/* * The maximum size of a log entry which can be read. * An attempt to read less than this amount may result * in read() returning EINVAL. diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp index d15b3679b..3a75fa3e9 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 @@ std::string& GetDefaultTag() { 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 @@ static __android_logger_function logger_function = __android_log_logd_logger; #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 @@ void __android_log_default_aborter(const char* abort_message) { } 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 @@ void __android_log_write_log_message(__android_log_message* log_message) { 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 @@ void __android_log_write_log_message(__android_log_message* log_message) { } #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 065fd55fd..eee277821 100644 --- a/liblog/logger_write.h +++ b/liblog/logger_write.h @@ -18,7 +18,4 @@ #include <string> -#include "rwlock.h" - -std::string& GetDefaultTag(); // Must read lock default_tag_lock -extern RwLock default_tag_lock;
\ No newline at end of file +std::string& GetDefaultTag(); diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp index 5c69bf863..9e8d277e0 100644 --- a/liblog/logprint.cpp +++ b/liblog/logprint.cpp @@ -19,6 +19,8 @@ #define HAVE_STRSEP #endif +#include <log/logprint.h> + #include <assert.h> #include <ctype.h> #include <errno.h> @@ -37,7 +39,7 @@ #include <cutils/list.h> #include <log/log.h> -#include <log/logprint.h> +#include <log/log_read.h> #include <private/android_logger.h> #define MS_PER_NSEC 1000000 diff --git a/liblog/properties.cpp b/liblog/properties.cpp index abd48fcd4..37670ecd6 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 int __android_log_level(const char* tag, size_t len) { static const char log_namespace[] = "persist.log.tag."; static const size_t base_offset = 8; /* skip "persist." */ - auto tag_lock = std::shared_lock{default_tag_lock, std::defer_lock}; if (tag == nullptr || len == 0) { - tag_lock.lock(); auto& tag_string = GetDefaultTag(); tag = tag_string.c_str(); len = tag_string.size(); diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp index 3a6ed90d4..f4734b930 100644 --- a/liblog/tests/liblog_benchmark.cpp +++ b/liblog/tests/liblog_benchmark.cpp @@ -31,6 +31,7 @@ #include <benchmark/benchmark.h> #include <cutils/sockets.h> #include <log/event_tag_map.h> +#include <log/log_read.h> #include <private/android_logger.h> BENCHMARK_MAIN(); diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index 048bf610f..d3d8e914e 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -40,6 +40,7 @@ #include <gtest/gtest.h> #include <log/log_event_list.h> #include <log/log_properties.h> +#include <log/log_read.h> #include <log/logprint.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 57c4c2efe..bf7d69e5d 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -395,7 +395,9 @@ cc_benchmark { 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 821e042b0..17470fd3e 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp @@ -371,7 +371,7 @@ void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { // 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 @@ void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) { } 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 @@ std::string ElfInterface::GetSonameWithTemplate() { 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 @@ bool GetBuildIDInfo(Memory* memory, uint64_t* build_id_offset, uint64_t* build_i } 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 fac9085b4..b4623faac 100644 --- a/libunwindstack/Memory.cpp +++ b/libunwindstack/Memory.cpp @@ -158,20 +158,30 @@ bool Memory::ReadFully(uint64_t addr, void* dst, size_t size) { 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; } diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp index e3c15a298..2117ebd3e 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 @@ namespace unwindstack { 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 @@ const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) { 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); + } + if (info == nullptr) { + return false; } - return return_value; + // 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 7fcd067d3..3b3f20b1c 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 Memory; 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 @@ class Symbols { 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 @@ class Symbols { 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_; - - std::vector<Info> symbols_; + template <typename SymType> + const Info* ReadFuncInfo(uint32_t symbol_index, Memory* elf_memory); + + 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 000000000..c108a2aa7 --- /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 a850ff047..73088dab0 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 @@ static void BenchmarkSymbolLookup(benchmark::State& state, std::vector<uint64_t> #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 @@ static void BenchmarkSymbolLookup(benchmark::State& state, std::vector<uint64_t> #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 @@ static void BenchmarkSymbolLookup(benchmark::State& state, uint64_t pc, std::str 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 @@ void BM_symbol_find_multiple(benchmark::State& state) { 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 000000000..c92f1099d --- /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 000000000..bee6efcad --- /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 26252b6b0..3d8187856 100644 --- a/libunwindstack/include/unwindstack/Memory.h +++ b/libunwindstack/include/unwindstack/Memory.h @@ -37,7 +37,7 @@ class Memory { 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() {} diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp index 365598499..8a8eb24f5 100644 --- a/libunwindstack/tests/MemoryTest.cpp +++ b/libunwindstack/tests/MemoryTest.cpp @@ -59,10 +59,10 @@ TEST(MemoryTest, read_string) { 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 @@ TEST(MemoryTest, read_string_error) { 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 c58aeff71..9afbeec55 100644 --- a/libunwindstack/tests/SymbolsTest.cpp +++ b/libunwindstack/tests/SymbolsTest.cpp @@ -185,18 +185,21 @@ TYPED_TEST_P(SymbolsTest, multiple_entries_nonstandard_size) { 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 @@ TYPED_TEST_P(SymbolsTest, symtab_read_cached) { // 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/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h index c439c5ce0..466fbb726 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 @@ private: 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 17d4833a5..a2615359a 100644 --- a/libziparchive/ziptool.cpp +++ b/libziparchive/ziptool.cpp @@ -263,12 +263,12 @@ static void ListOne(const ZipEntry64& entry, const std::string& name) { snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min); if (flag_v) { - printf("%8" PRIu64 " %s %8" PRIu64 " %3.0f%% %s %08x %s\n", entry.uncompressed_length, + printf("%8" PRIu64 " %s %8" PRIu64 " %3.0f%% %s %08x %s\n", entry.uncompressed_length, (entry.method == kCompressStored) ? "Stored" : "Defl:N", entry.compressed_length, CompressionRatio(entry.uncompressed_length, entry.compressed_length), time, entry.crc32, name.c_str()); } else { - printf("%9" PRIu64 " %s %s\n", entry.uncompressed_length, time, name.c_str()); + printf("%9" PRIu64 " %s %s\n", entry.uncompressed_length, time, name.c_str()); } } diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp index 1c3acb821..8ad9900ec 100644 --- a/llkd/libllkd.cpp +++ b/llkd/libllkd.cpp @@ -41,6 +41,7 @@ #include <string> #include <unordered_map> #include <unordered_set> +#include <vector> #include <android-base/file.h> #include <android-base/logging.h> @@ -1204,9 +1205,19 @@ milliseconds llkCheck(bool checkRunning) { } } // We are here because we have confirmed kernel live-lock + std::vector<std::string> threads; + auto taskdir = procdir + std::to_string(tid) + "/task/"; + dir taskDirectory(taskdir); + for (auto tp = taskDirectory.read(); tp != nullptr; tp = taskDirectory.read()) { + std::string piddir; + if (getValidTidDir(tp, &piddir)) + threads.push_back(android::base::Basename(piddir)); + } const auto message = state + " "s + llkFormat(procp->count) + " " + std::to_string(ppid) + "->" + std::to_string(pid) + "->" + - std::to_string(tid) + " " + process_comm + " [panic]"; + std::to_string(tid) + " " + process_comm + " [panic]\n" + + " thread group: {" + android::base::Join(threads, ",") + + "}"; llkPanicKernel(dump, tid, (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping", message); diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index b065855c5..8185f0165 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -50,6 +50,7 @@ #include <android/log.h> #include <log/event_tag_map.h> #include <log/log_id.h> +#include <log/log_read.h> #include <log/logprint.h> #include <private/android_logger.h> #include <processgroup/sched_policy.h> @@ -122,6 +123,18 @@ static int openLogFile(const char* pathname, size_t sizeKB) { return fd; } +static void closeLogFile(const char* pathname) { + int fd = open(pathname, O_WRONLY | O_CLOEXEC); + if (fd == -1) { + return; + } + + // no need to check errors + __u32 set = 0; + ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set); + close(fd); +} + void Logcat::RotateLogs() { // Can't rotate logs if we're not outputting to a file if (!output_file_name_) return; @@ -152,6 +165,8 @@ void Logcat::RotateLogs() { break; } + closeLogFile(file0.c_str()); + int err = rename(file0.c_str(), file1.c_str()); if (err < 0 && errno != ENOENT) { diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp index 37148002a..916ed42cc 100644 --- a/logd/LogBufferElement.cpp +++ b/logd/LogBufferElement.cpp @@ -22,6 +22,7 @@ #include <time.h> #include <unistd.h> +#include <log/log_read.h> #include <private/android_logger.h> #include "LogBuffer.h" diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp index 8299e667a..e45cc8ae6 100644 --- a/logd/LogTags.cpp +++ b/logd/LogTags.cpp @@ -34,6 +34,7 @@ #include <android-base/stringprintf.h> #include <log/log_event_list.h> #include <log/log_properties.h> +#include <log/log_read.h> #include <private/android_filesystem_config.h> #include "LogTags.h" diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp index d57b79efc..b09248985 100644 --- a/logd/tests/logd_test.cpp +++ b/logd/tests/logd_test.cpp @@ -32,6 +32,7 @@ #include <android-base/stringprintf.h> #include <cutils/sockets.h> #include <gtest/gtest.h> +#include <log/log_read.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> #ifdef __ANDROID__ diff --git a/toolbox/start.cpp b/toolbox/start.cpp index b87ed15a9..46314cfcf 100644 --- a/toolbox/start.cpp +++ b/toolbox/start.cpp @@ -36,7 +36,12 @@ static void ControlService(bool start, const std::string& service) { } static void ControlDefaultServices(bool start) { - std::vector<std::string> services = {"netd", "surfaceflinger", "zygote"}; + std::vector<std::string> services = { + "netd", + "surfaceflinger", + "audioserver", + "zygote", + }; // Only start zygote_secondary if not single arch. std::string zygote_configuration = GetProperty("ro.zygote", ""); @@ -86,4 +91,4 @@ extern "C" int start_main(int argc, char** argv) { extern "C" int stop_main(int argc, char** argv) { return StartStop(argc, argv, false); -}
\ No newline at end of file +} |