Merge "ueventd: create a /dev/block/by-name/ symlink without a partition name"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 9c0eeca..06e4c50 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -357,9 +357,14 @@
case A_OPEN: /* OPEN(local-id, 0, "destination") */
if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
- // TODO: Switch to string_view.
- std::string address(p->payload.begin(), p->payload.end());
- asocket* s = create_local_service_socket(address.c_str(), t);
+ std::string_view address(p->payload.begin(), p->payload.size());
+
+ // Historically, we received service names as a char*, and stopped at the first NUL
+ // byte. The client sent strings with null termination, which post-string_view, start
+ // being interpreted as part of the string, unless we explicitly strip them.
+ address = StripTrailingNulls(address);
+
+ asocket* s = create_local_service_socket(address, t);
if (s == nullptr) {
send_close(0, p->msg.arg0, t);
} else {
@@ -600,7 +605,7 @@
fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
fprintf(stderr, "Server had pid: %d\n", pid);
- android::base::unique_fd fd(unix_open(GetLogFilePath().c_str(), O_RDONLY));
+ android::base::unique_fd fd(unix_open(GetLogFilePath(), O_RDONLY));
if (fd == -1) return;
// Let's not show more than 128KiB of log...
diff --git a/adb/adb.h b/adb/adb.h
index cdd6346..47ea0e8 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -139,9 +139,9 @@
atransport* find_emulator_transport_by_console_port(int console_port);
#endif
-int service_to_fd(const char* name, atransport* transport);
+int service_to_fd(std::string_view name, atransport* transport);
#if !ADB_HOST
-unique_fd daemon_service_to_fd(const char* name, atransport* transport);
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport);
#endif
#if ADB_HOST
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 6cc274b..e6fefda 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -38,7 +38,8 @@
// The cost of sending two strings outweighs the cost of formatting.
// "adb sync" performance is affected by this.
- return WriteFdFmt(fd, "%04x%.*s", length, length, s.c_str());
+ auto str = android::base::StringPrintf("%04x", length).append(s);
+ return WriteFdExactly(fd, str);
}
bool ReadProtocolString(int fd, std::string* s, std::string* error) {
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 051ab73..be457a6 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -105,7 +105,7 @@
s = create_local_socket(fd);
if (s) {
s->transport = listener->transport;
- connect_to_remote(s, listener->connect_to.c_str());
+ connect_to_remote(s, listener->connect_to);
return;
}
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index a024a89..2bd6a3e 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -72,8 +72,7 @@
}
void start_device_log(void) {
- int fd = unix_open(get_log_file_name().c_str(),
- O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+ int fd = unix_open(get_log_file_name(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
if (fd == -1) {
return;
}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 6d12225..8253487 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -19,6 +19,8 @@
#include <condition_variable>
#include <mutex>
#include <string>
+#include <string_view>
+#include <type_traits>
#include <vector>
#include <android-base/macros.h>
@@ -94,3 +96,47 @@
};
std::string GetLogFilePath();
+
+inline std::string_view StripTrailingNulls(std::string_view str) {
+ size_t n = 0;
+ for (auto it = str.rbegin(); it != str.rend(); ++it) {
+ if (*it != '\0') {
+ break;
+ }
+ ++n;
+ }
+
+ str.remove_suffix(n);
+ return str;
+}
+
+// Base-10 stroll on a string_view.
+template <typename T>
+inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining) {
+ if (str.empty() || !isdigit(str[0])) {
+ return false;
+ }
+
+ T value = 0;
+ std::string_view::iterator it;
+ constexpr T max = std::numeric_limits<T>::max();
+ for (it = str.begin(); it != str.end() && isdigit(*it); ++it) {
+ if (value > max / 10) {
+ return false;
+ }
+
+ value *= 10;
+
+ T digit = *it - '0';
+ if (value > max - digit) {
+ return false;
+ }
+
+ value += digit;
+ }
+ *result = value;
+ if (remaining) {
+ *remaining = str.substr(it - str.begin());
+ }
+ return true;
+}
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 870f6f0..bb09425 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -181,3 +181,48 @@
EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:a", &error));
EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:22x", &error));
}
+
+void TestParseUint(std::string_view string, bool expected_success, uint32_t expected_value = 0) {
+ // Standalone.
+ {
+ uint32_t value;
+ std::string_view remaining;
+ bool success = ParseUint(&value, string, &remaining);
+ EXPECT_EQ(success, expected_success);
+ if (expected_success) {
+ EXPECT_EQ(value, expected_value);
+ }
+ EXPECT_TRUE(remaining.empty());
+ }
+
+ // With trailing text.
+ {
+ std::string text = std::string(string) + "foo";
+ uint32_t value;
+ std::string_view remaining;
+ bool success = ParseUint(&value, text, &remaining);
+ EXPECT_EQ(success, expected_success);
+ if (expected_success) {
+ EXPECT_EQ(value, expected_value);
+ EXPECT_EQ(remaining, "foo");
+ }
+ }
+}
+
+TEST(adb_utils, ParseUint) {
+ TestParseUint("", false);
+ TestParseUint("foo", false);
+ TestParseUint("foo123", false);
+ TestParseUint("-1", false);
+
+ TestParseUint("123", true, 123);
+ TestParseUint("9999999999999999999999999", false);
+ TestParseUint(std::to_string(UINT32_MAX), true, UINT32_MAX);
+ TestParseUint("0" + std::to_string(UINT32_MAX), true, UINT32_MAX);
+ TestParseUint(std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
+ TestParseUint("0" + std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
+
+ std::string x = std::to_string(UINT32_MAX) + "123";
+ std::string_view substr = std::string_view(x).substr(0, std::to_string(UINT32_MAX).size());
+ TestParseUint(substr, true, UINT32_MAX);
+}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index fb581a6..2ee81a9 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -40,7 +40,7 @@
static void setup_daemon_logging() {
const std::string log_file_path(GetLogFilePath());
- int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
+ int fd = unix_open(log_file_path, O_WRONLY | O_CREAT | O_APPEND, 0640);
if (fd == -1) {
PLOG(FATAL) << "cannot open " << log_file_path;
}
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index f1bf559..1168958 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -39,6 +39,7 @@
#include <list>
#include <mutex>
#include <string>
+#include <string_view>
#include <thread>
#include <android-base/file.h>
@@ -90,7 +91,7 @@
static auto& g_usb_handles_mutex = *new std::mutex();
static auto& g_usb_handles = *new std::list<usb_handle*>();
-static int is_known_device(const char* dev_name) {
+static int is_known_device(std::string_view dev_name) {
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
for (usb_handle* usb : g_usb_handles) {
if (usb->path == dev_name) {
@@ -152,11 +153,11 @@
if (contains_non_digit(de->d_name)) continue;
std::string dev_name = bus_name + "/" + de->d_name;
- if (is_known_device(dev_name.c_str())) {
+ if (is_known_device(dev_name)) {
continue;
}
- int fd = unix_open(dev_name.c_str(), O_RDONLY | O_CLOEXEC);
+ int fd = unix_open(dev_name, O_RDONLY | O_CLOEXEC);
if (fd == -1) {
continue;
}
@@ -535,10 +536,10 @@
// Initialize mark so we don't get garbage collected after the device scan.
usb->mark = true;
- usb->fd = unix_open(usb->path.c_str(), O_RDWR | O_CLOEXEC);
+ usb->fd = unix_open(usb->path, O_RDWR | O_CLOEXEC);
if (usb->fd == -1) {
// Opening RW failed, so see if we have RO access.
- usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
+ usb->fd = unix_open(usb->path, O_RDONLY | O_CLOEXEC);
if (usb->fd == -1) {
D("[ usb open %s failed: %s]", usb->path.c_str(), strerror(errno));
return;
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 1a92317..80b3e06 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -103,7 +103,7 @@
bool make_block_device_writable(const std::string& dev) {
if (dev_is_overlayfs(dev)) return true;
- int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
+ int fd = unix_open(dev, O_RDONLY | O_CLOEXEC);
if (fd == -1) {
return false;
}
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 3182ddd..3693997 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -151,14 +151,17 @@
kick_transport(t);
}
-unique_fd reverse_service(const char* command, atransport* transport) {
+unique_fd reverse_service(std::string_view command, atransport* transport) {
+ // TODO: Switch handle_forward_request to std::string_view.
+ std::string str(command);
+
int s[2];
if (adb_socketpair(s)) {
PLOG(ERROR) << "cannot create service socket pair.";
return unique_fd{};
}
VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
- if (!handle_forward_request(command, transport, s[1])) {
+ if (!handle_forward_request(str.c_str(), transport, s[1])) {
SendFail(s[1], "not a reverse forwarding command");
}
adb_close(s[1]);
@@ -167,15 +170,16 @@
// Shell service string can look like:
// shell[,arg1,arg2,...]:[command]
-unique_fd ShellService(const std::string& args, const atransport* transport) {
+unique_fd ShellService(std::string_view args, const atransport* transport) {
size_t delimiter_index = args.find(':');
if (delimiter_index == std::string::npos) {
LOG(ERROR) << "No ':' found in shell service arguments: " << args;
return unique_fd{};
}
- const std::string service_args = args.substr(0, delimiter_index);
- const std::string command = args.substr(delimiter_index + 1);
+ // TODO: android::base::Split(const std::string_view&, ...)
+ std::string service_args(args.substr(0, delimiter_index));
+ std::string command(args.substr(delimiter_index + 1));
// Defaults:
// PTY for interactive, raw for non-interactive.
@@ -192,15 +196,15 @@
type = SubprocessType::kPty;
} else if (arg == kShellServiceArgShellProtocol) {
protocol = SubprocessProtocol::kShell;
- } else if (android::base::StartsWith(arg, "TERM=")) {
- terminal_type = arg.substr(5);
+ } else if (arg.starts_with("TERM=")) {
+ terminal_type = arg.substr(strlen("TERM="));
} else if (!arg.empty()) {
// This is not an error to allow for future expansion.
LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
}
}
- return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
+ return StartSubprocess(command, terminal_type.c_str(), type, protocol);
}
static void spin_service(unique_fd fd) {
@@ -323,59 +327,70 @@
return nullptr;
}
-unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
- if (!strncmp("dev:", name, 4)) {
- return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
- } else if (!strncmp(name, "framebuffer:", 12)) {
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
+ if (name.starts_with("dev:")) {
+ name.remove_prefix(strlen("dev:"));
+ return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
+ } else if (name.starts_with("framebuffer:")) {
return create_service_thread("fb", framebuffer_service);
- } else if (!strncmp(name, "jdwp:", 5)) {
- return create_jdwp_connection_fd(atoi(name + 5));
- } else if (!strncmp(name, "shell", 5)) {
- return ShellService(name + 5, transport);
- } else if (!strncmp(name, "exec:", 5)) {
- return StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
- } else if (!strncmp(name, "sync:", 5)) {
+ } else if (name.starts_with("jdwp:")) {
+ name.remove_prefix(strlen("jdwp:"));
+ std::string str(name);
+ return create_jdwp_connection_fd(atoi(str.c_str()));
+ } else if (name.starts_with("shell")) {
+ name.remove_prefix(strlen("shell"));
+ return ShellService(name, transport);
+ } else if (name.starts_with("exec:")) {
+ name.remove_prefix(strlen("exec:"));
+ return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
+ SubprocessProtocol::kNone);
+ } else if (name.starts_with("sync:")) {
return create_service_thread("sync", file_sync_service);
- } else if (!strncmp(name, "remount:", 8)) {
- std::string options(name + strlen("remount:"));
+ } else if (name.starts_with("remount:")) {
+ std::string arg(name.begin() + strlen("remount:"), name.end());
return create_service_thread("remount",
- std::bind(remount_service, std::placeholders::_1, options));
- } else if (!strncmp(name, "reboot:", 7)) {
- std::string arg(name + strlen("reboot:"));
+ std::bind(remount_service, std::placeholders::_1, arg));
+ } else if (name.starts_with("reboot:")) {
+ std::string arg(name.begin() + strlen("reboot:"), name.end());
return create_service_thread("reboot",
std::bind(reboot_service, std::placeholders::_1, arg));
- } else if (!strncmp(name, "root:", 5)) {
+ } else if (name.starts_with("root:")) {
return create_service_thread("root", restart_root_service);
- } else if (!strncmp(name, "unroot:", 7)) {
+ } else if (name.starts_with("unroot:")) {
return create_service_thread("unroot", restart_unroot_service);
- } else if (!strncmp(name, "backup:", 7)) {
- return StartSubprocess(
- android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
- nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
- } else if (!strncmp(name, "restore:", 8)) {
+ } else if (name.starts_with("backup:")) {
+ name.remove_prefix(strlen("backup:"));
+ std::string cmd = "/system/bin/bu backup ";
+ cmd += name;
+ return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+ } else if (name.starts_with("restore:")) {
return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
SubprocessProtocol::kNone);
- } else if (!strncmp(name, "tcpip:", 6)) {
+ } else if (name.starts_with("tcpip:")) {
+ name.remove_prefix(strlen("tcpip:"));
+ std::string str(name);
+
int port;
- if (sscanf(name + 6, "%d", &port) != 1) {
+ if (sscanf(str.c_str(), "%d", &port) != 1) {
return unique_fd{};
}
return create_service_thread("tcp",
std::bind(restart_tcp_service, std::placeholders::_1, port));
- } else if (!strncmp(name, "usb:", 4)) {
+ } else if (name.starts_with("usb:")) {
return create_service_thread("usb", restart_usb_service);
- } else if (!strncmp(name, "reverse:", 8)) {
- return reverse_service(name + 8, transport);
- } else if (!strncmp(name, "disable-verity:", 15)) {
+ } else if (name.starts_with("reverse:")) {
+ name.remove_prefix(strlen("reverse:"));
+ return reverse_service(name, transport);
+ } else if (name.starts_with("disable-verity:")) {
return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
std::placeholders::_1, false));
- } else if (!strncmp(name, "enable-verity:", 15)) {
+ } else if (name.starts_with("enable-verity:")) {
return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
std::placeholders::_1, true));
- } else if (!strcmp(name, "reconnect")) {
+ } else if (name == "reconnect") {
return create_service_thread(
"reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
- } else if (!strcmp(name, "spin")) {
+ } else if (name == "spin") {
return create_service_thread("spin", spin_service);
}
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 8805fc1..595d5c6 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -140,8 +140,8 @@
class Subprocess {
public:
- Subprocess(const std::string& command, const char* terminal_type,
- SubprocessType type, SubprocessProtocol protocol);
+ Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol);
~Subprocess();
const std::string& command() const { return command_; }
@@ -191,9 +191,9 @@
DISALLOW_COPY_AND_ASSIGN(Subprocess);
};
-Subprocess::Subprocess(const std::string& command, const char* terminal_type,
- SubprocessType type, SubprocessProtocol protocol)
- : command_(command),
+Subprocess::Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol)
+ : command_(std::move(command)),
terminal_type_(terminal_type ? terminal_type : ""),
type_(type),
protocol_(protocol) {
@@ -745,14 +745,13 @@
return read;
}
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
SubprocessProtocol protocol) {
D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
type == SubprocessType::kRaw ? "raw" : "PTY",
- protocol == SubprocessProtocol::kNone ? "none" : "shell",
- terminal_type, name);
+ protocol == SubprocessProtocol::kNone ? "none" : "shell", terminal_type, name.c_str());
- auto subprocess = std::make_unique<Subprocess>(name, terminal_type, type, protocol);
+ auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol);
if (!subprocess) {
LOG(ERROR) << "failed to allocate new subprocess";
return ReportError(protocol, "failed to allocate new subprocess");
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index 2a48923..421d61f 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -16,6 +16,8 @@
#pragma once
+#include <string>
+
#include "adb_unique_fd.h"
enum class SubprocessType {
@@ -32,5 +34,5 @@
// shell is started, otherwise |name| is executed non-interactively.
//
// Returns an open FD connected to the subprocess or -1 on failure.
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
SubprocessProtocol protocol);
diff --git a/adb/services.cpp b/adb/services.cpp
index 4b033bd..8636657 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -71,7 +71,7 @@
return unique_fd(s[0]);
}
-int service_to_fd(const char* name, atransport* transport) {
+int service_to_fd(std::string_view name, atransport* transport) {
int ret = -1;
if (is_socket_spec(name)) {
diff --git a/adb/socket.h b/adb/socket.h
index 0905aab..e7df991 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -103,17 +103,18 @@
void close_all_sockets(atransport *t);
asocket *create_local_socket(int fd);
-asocket* create_local_service_socket(const char* destination, atransport* transport);
+asocket* create_local_service_socket(std::string_view destination, atransport* transport);
asocket *create_remote_socket(unsigned id, atransport *t);
-void connect_to_remote(asocket *s, const char *destination);
+void connect_to_remote(asocket* s, std::string_view destination);
void connect_to_smartsocket(asocket *s);
// Internal functions that are only made available here for testing purposes.
namespace internal {
#if ADB_HOST
-char* skip_host_serial(char* service);
+bool parse_host_service(std::string_view* out_serial, std::string_view* out_command,
+ std::string_view service);
#endif
} // namespace internal
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index eb4df97..4cddc84 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -17,6 +17,7 @@
#include "socket_spec.h"
#include <string>
+#include <string_view>
#include <unordered_map>
#include <vector>
@@ -29,7 +30,8 @@
#include "adb.h"
#include "sysdeps.h"
-using android::base::StartsWith;
+using namespace std::string_literals;
+
using android::base::StringPrintf;
#if defined(__linux__)
@@ -64,10 +66,11 @@
{ "localfilesystem", { ANDROID_SOCKET_NAMESPACE_FILESYSTEM, !ADB_WINDOWS } },
});
-bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
+bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
std::string* error) {
- if (!StartsWith(spec, "tcp:")) {
- *error = StringPrintf("specification is not tcp: '%s'", spec.c_str());
+ if (!spec.starts_with("tcp:")) {
+ *error = "specification is not tcp: ";
+ *error += spec;
return false;
}
@@ -84,7 +87,7 @@
return false;
}
} else {
- std::string addr = spec.substr(4);
+ std::string addr(spec.substr(4));
port_value = -1;
// FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
@@ -94,7 +97,8 @@
}
if (port_value == -1) {
- *error = StringPrintf("missing port in specification: '%s'", spec.c_str());
+ *error = "missing port in specification: ";
+ *error += spec;
return false;
}
}
@@ -110,25 +114,25 @@
return true;
}
-static bool tcp_host_is_local(const std::string& hostname) {
+static bool tcp_host_is_local(std::string_view hostname) {
// FIXME
return hostname.empty() || hostname == "localhost";
}
-bool is_socket_spec(const std::string& spec) {
+bool is_socket_spec(std::string_view spec) {
for (const auto& it : kLocalSocketTypes) {
std::string prefix = it.first + ":";
- if (StartsWith(spec, prefix)) {
+ if (spec.starts_with(prefix)) {
return true;
}
}
- return StartsWith(spec, "tcp:");
+ return spec.starts_with("tcp:");
}
-bool is_local_socket_spec(const std::string& spec) {
+bool is_local_socket_spec(std::string_view spec) {
for (const auto& it : kLocalSocketTypes) {
std::string prefix = it.first + ":";
- if (StartsWith(spec, prefix)) {
+ if (spec.starts_with(prefix)) {
return true;
}
}
@@ -141,8 +145,8 @@
return tcp_host_is_local(hostname);
}
-int socket_spec_connect(const std::string& spec, std::string* error) {
- if (StartsWith(spec, "tcp:")) {
+int socket_spec_connect(std::string_view spec, std::string* error) {
+ if (spec.starts_with("tcp:")) {
std::string hostname;
int port;
if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
@@ -170,7 +174,7 @@
for (const auto& it : kLocalSocketTypes) {
std::string prefix = it.first + ":";
- if (StartsWith(spec, prefix)) {
+ if (spec.starts_with(prefix)) {
if (!it.second.available) {
*error = StringPrintf("socket type %s is unavailable on this platform",
it.first.c_str());
@@ -182,12 +186,13 @@
}
}
- *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+ *error = "unknown socket specification: ";
+ *error += spec;
return -1;
}
-int socket_spec_listen(const std::string& spec, std::string* error, int* resolved_tcp_port) {
- if (StartsWith(spec, "tcp:")) {
+int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port) {
+ if (spec.starts_with("tcp:")) {
std::string hostname;
int port;
if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
@@ -213,10 +218,10 @@
for (const auto& it : kLocalSocketTypes) {
std::string prefix = it.first + ":";
- if (StartsWith(spec, prefix)) {
+ if (spec.starts_with(prefix)) {
if (!it.second.available) {
- *error = StringPrintf("attempted to listen on unavailable socket type: '%s'",
- spec.c_str());
+ *error = "attempted to listen on unavailable socket type: ";
+ *error += spec;
return -1;
}
@@ -225,6 +230,7 @@
}
}
- *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+ *error = "unknown socket specification:";
+ *error += spec;
return -1;
}
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 6920e91..5b06973 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -19,13 +19,12 @@
#include <string>
// Returns true if the argument starts with a plausible socket prefix.
-bool is_socket_spec(const std::string& spec);
-bool is_local_socket_spec(const std::string& spec);
+bool is_socket_spec(std::string_view spec);
+bool is_local_socket_spec(std::string_view spec);
-int socket_spec_connect(const std::string& spec, std::string* error);
-int socket_spec_listen(const std::string& spec, std::string* error,
- int* resolved_tcp_port = nullptr);
+int socket_spec_connect(std::string_view spec, std::string* error);
+int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port = nullptr);
// Exposed for testing.
-bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
+bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
std::string* error);
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 04214a2..80f9430 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -34,6 +34,9 @@
#include "sysdeps.h"
#include "sysdeps/chrono.h"
+using namespace std::string_literals;
+using namespace std::string_view_literals;
+
struct ThreadArg {
int first_read_fd;
int last_write_fd;
@@ -303,56 +306,78 @@
#if ADB_HOST
-// Checks that skip_host_serial(serial) returns a pointer to the part of |serial| which matches
-// |expected|, otherwise logs the failure to gtest.
-void VerifySkipHostSerial(std::string serial, const char* expected) {
- char* result = internal::skip_host_serial(&serial[0]);
- if (expected == nullptr) {
- EXPECT_EQ(nullptr, result);
- } else {
- EXPECT_STREQ(expected, result);
- }
-}
+#define VerifyParseHostServiceFailed(s) \
+ do { \
+ std::string service(s); \
+ std::string_view serial, command; \
+ bool result = internal::parse_host_service(&serial, &command, service); \
+ EXPECT_FALSE(result); \
+ } while (0)
+
+#define VerifyParseHostService(s, expected_serial, expected_command) \
+ do { \
+ std::string service(s); \
+ std::string_view serial, command; \
+ bool result = internal::parse_host_service(&serial, &command, service); \
+ EXPECT_TRUE(result); \
+ EXPECT_EQ(std::string(expected_serial), std::string(serial)); \
+ EXPECT_EQ(std::string(expected_command), std::string(command)); \
+ } while (0);
// Check [tcp:|udp:]<serial>[:<port>]:<command> format.
-TEST(socket_test, test_skip_host_serial) {
+TEST(socket_test, test_parse_host_service) {
for (const std::string& protocol : {"", "tcp:", "udp:"}) {
- VerifySkipHostSerial(protocol, nullptr);
- VerifySkipHostSerial(protocol + "foo", nullptr);
+ VerifyParseHostServiceFailed(protocol);
+ VerifyParseHostServiceFailed(protocol + "foo");
- VerifySkipHostSerial(protocol + "foo:bar", ":bar");
- VerifySkipHostSerial(protocol + "foo:bar:baz", ":bar:baz");
+ {
+ std::string serial = protocol + "foo";
+ VerifyParseHostService(serial + ":bar", serial, "bar");
+ VerifyParseHostService(serial + " :bar:baz", serial, "bar:baz");
+ }
- VerifySkipHostSerial(protocol + "foo:123:bar", ":bar");
- VerifySkipHostSerial(protocol + "foo:123:456", ":456");
- VerifySkipHostSerial(protocol + "foo:123:bar:baz", ":bar:baz");
+ {
+ // With port.
+ std::string serial = protocol + "foo:123";
+ VerifyParseHostService(serial + ":bar", serial, "bar");
+ VerifyParseHostService(serial + ":456", serial, "456");
+ VerifyParseHostService(serial + ":bar:baz", serial, "bar:baz");
+ }
// Don't register a port unless it's all numbers and ends with ':'.
- VerifySkipHostSerial(protocol + "foo:123", ":123");
- VerifySkipHostSerial(protocol + "foo:123bar:baz", ":123bar:baz");
+ VerifyParseHostService(protocol + "foo:123", protocol + "foo", "123");
+ VerifyParseHostService(protocol + "foo:123bar:baz", protocol + "foo", "123bar:baz");
- VerifySkipHostSerial(protocol + "100.100.100.100:5555:foo", ":foo");
- VerifySkipHostSerial(protocol + "[0123:4567:89ab:CDEF:0:9:a:f]:5555:foo", ":foo");
- VerifySkipHostSerial(protocol + "[::1]:5555:foo", ":foo");
+ std::string addresses[] = {"100.100.100.100", "[0123:4567:89ab:CDEF:0:9:a:f]", "[::1]"};
+ for (const std::string& address : addresses) {
+ std::string serial = protocol + address;
+ std::string serial_with_port = protocol + address + ":5555";
+ VerifyParseHostService(serial + ":foo", serial, "foo");
+ VerifyParseHostService(serial_with_port + ":foo", serial_with_port, "foo");
+ }
// If we can't find both [] then treat it as a normal serial with [ in it.
- VerifySkipHostSerial(protocol + "[0123:foo", ":foo");
+ VerifyParseHostService(protocol + "[0123:foo", protocol + "[0123", "foo");
// Don't be fooled by random IPv6 addresses in the command string.
- VerifySkipHostSerial(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
- ":ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+ VerifyParseHostService(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
+ protocol + "foo", "ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+
+ // Handle embedded NULs properly.
+ VerifyParseHostService(protocol + "foo:echo foo\0bar"s, protocol + "foo",
+ "echo foo\0bar"sv);
}
}
// Check <prefix>:<serial>:<command> format.
-TEST(socket_test, test_skip_host_serial_prefix) {
+TEST(socket_test, test_parse_host_service_prefix) {
for (const std::string& prefix : {"usb:", "product:", "model:", "device:"}) {
- VerifySkipHostSerial(prefix, nullptr);
- VerifySkipHostSerial(prefix + "foo", nullptr);
+ VerifyParseHostServiceFailed(prefix);
+ VerifyParseHostServiceFailed(prefix + "foo");
- VerifySkipHostSerial(prefix + "foo:bar", ":bar");
- VerifySkipHostSerial(prefix + "foo:bar:baz", ":bar:baz");
- VerifySkipHostSerial(prefix + "foo:123:bar", ":123:bar");
+ VerifyParseHostService(prefix + "foo:bar", prefix + "foo", "bar");
+ VerifyParseHostService(prefix + "foo:bar:baz", prefix + "foo", "bar:baz");
+ VerifyParseHostService(prefix + "foo:123:bar", prefix + "foo", "123:bar");
}
}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 1bd57c1..47ae883 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -37,6 +37,7 @@
#include "adb.h"
#include "adb_io.h"
+#include "adb_utils.h"
#include "transport.h"
#include "types.h"
@@ -346,7 +347,7 @@
return s;
}
-asocket* create_local_service_socket(const char* name, atransport* transport) {
+asocket* create_local_service_socket(std::string_view name, atransport* transport) {
#if !ADB_HOST
if (asocket* s = daemon_service_to_socket(name); s) {
return s;
@@ -358,13 +359,12 @@
}
asocket* s = create_local_socket(fd);
- D("LS(%d): bound to '%s' via %d", s->id, name, fd);
+ LOG(VERBOSE) << "LS(" << s->id << "): bound to '" << name << "' via " << fd;
#if !ADB_HOST
- if ((!strncmp(name, "root:", 5) && getuid() != 0 && __android_log_is_debuggable()) ||
- (!strncmp(name, "unroot:", 7) && getuid() == 0) ||
- !strncmp(name, "usb:", 4) ||
- !strncmp(name, "tcpip:", 6)) {
+ if ((name.starts_with("root:") && getuid() != 0 && __android_log_is_debuggable()) ||
+ (name.starts_with("unroot:") && getuid() == 0) || name.starts_with("usb:") ||
+ name.starts_with("tcpip:")) {
D("LS(%d): enabling exit_on_close", s->id);
s->exit_on_close = 1;
}
@@ -462,16 +462,19 @@
return s;
}
-void connect_to_remote(asocket* s, const char* destination) {
+void connect_to_remote(asocket* s, std::string_view destination) {
D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
apacket* p = get_apacket();
- D("LS(%d): connect('%s')", s->id, destination);
+ LOG(VERBOSE) << "LS(" << s->id << ": connect(" << destination << ")";
p->msg.command = A_OPEN;
p->msg.arg0 = s->id;
- // adbd expects a null-terminated string.
- p->payload.assign(destination, destination + strlen(destination) + 1);
+ // adbd used to expect a null-terminated string.
+ // Keep doing so to maintain backward compatibility.
+ p->payload.resize(destination.size() + 1);
+ memcpy(p->payload.data(), destination.data(), destination.size());
+ p->payload[destination.size()] = '\0';
p->msg.data_length = p->payload.size();
CHECK_LE(p->msg.data_length, s->get_max_payload());
@@ -547,57 +550,119 @@
namespace internal {
-// Returns the position in |service| following the target serial parameter. Serial format can be
-// any of:
+// Parses a host service string of the following format:
// * [tcp:|udp:]<serial>[:<port>]:<command>
// * <prefix>:<serial>:<command>
// Where <port> must be a base-10 number and <prefix> may be any of {usb,product,model,device}.
-//
-// The returned pointer will point to the ':' just before <command>, or nullptr if not found.
-char* skip_host_serial(char* service) {
- static const std::vector<std::string>& prefixes =
- *(new std::vector<std::string>{"usb:", "product:", "model:", "device:"});
+bool parse_host_service(std::string_view* out_serial, std::string_view* out_command,
+ std::string_view full_service) {
+ if (full_service.empty()) {
+ return false;
+ }
- for (const std::string& prefix : prefixes) {
- if (!strncmp(service, prefix.c_str(), prefix.length())) {
- return strchr(service + prefix.length(), ':');
+ std::string_view serial;
+ std::string_view command = full_service;
+ // Remove |count| bytes from the beginning of command and add them to |serial|.
+ auto consume = [&full_service, &serial, &command](size_t count) {
+ CHECK_LE(count, command.size());
+ if (!serial.empty()) {
+ CHECK_EQ(serial.data() + serial.size(), command.data());
+ }
+
+ serial = full_service.substr(0, serial.size() + count);
+ command.remove_prefix(count);
+ };
+
+ // Remove the trailing : from serial, and assign the values to the output parameters.
+ auto finish = [out_serial, out_command, &serial, &command] {
+ if (serial.empty() || command.empty()) {
+ return false;
+ }
+
+ CHECK_EQ(':', serial.back());
+ serial.remove_suffix(1);
+
+ *out_serial = serial;
+ *out_command = command;
+ return true;
+ };
+
+ static constexpr std::string_view prefixes[] = {"usb:", "product:", "model:", "device:"};
+ for (std::string_view prefix : prefixes) {
+ if (command.starts_with(prefix)) {
+ consume(prefix.size());
+
+ size_t offset = command.find_first_of(':');
+ if (offset == std::string::npos) {
+ return false;
+ }
+ consume(offset + 1);
+ return finish();
}
}
// For fastboot compatibility, ignore protocol prefixes.
- if (!strncmp(service, "tcp:", 4) || !strncmp(service, "udp:", 4)) {
- service += 4;
- }
-
- // Check for an IPv6 address. `adb connect` creates the serial number from the canonical
- // network address so it will always have the [] delimiters.
- if (service[0] == '[') {
- char* ipv6_end = strchr(service, ']');
- if (ipv6_end != nullptr) {
- service = ipv6_end;
+ if (command.starts_with("tcp:") || command.starts_with("udp:")) {
+ consume(4);
+ if (command.empty()) {
+ return false;
}
}
- // The next colon we find must either begin the port field or the command field.
- char* colon_ptr = strchr(service, ':');
- if (!colon_ptr) {
- // No colon in service string.
- return nullptr;
+ bool found_address = false;
+ if (command[0] == '[') {
+ // Read an IPv6 address. `adb connect` creates the serial number from the canonical
+ // network address so it will always have the [] delimiters.
+ size_t ipv6_end = command.find_first_of(']');
+ if (ipv6_end != std::string::npos) {
+ consume(ipv6_end + 1);
+ if (command.empty()) {
+ // Nothing after the IPv6 address.
+ return false;
+ } else if (command[0] != ':') {
+ // Garbage after the IPv6 address.
+ return false;
+ }
+ consume(1);
+ found_address = true;
+ }
}
- // If the next field is only decimal digits and ends with another colon, it's a port.
- char* serial_end = colon_ptr;
- if (isdigit(serial_end[1])) {
- serial_end++;
- while (*serial_end && isdigit(*serial_end)) {
- serial_end++;
+ if (!found_address) {
+ // Scan ahead to the next colon.
+ size_t offset = command.find_first_of(':');
+ if (offset == std::string::npos) {
+ return false;
}
- if (*serial_end != ':') {
- // Something other than "<port>:" was found, this must be the command field instead.
- serial_end = colon_ptr;
+ consume(offset + 1);
+ }
+
+ // We're either at the beginning of a port, or the command itself.
+ // Look for a port in between colons.
+ size_t next_colon = command.find_first_of(':');
+ if (next_colon == std::string::npos) {
+ // No colon, we must be at the command.
+ return finish();
+ }
+
+ bool port_valid = true;
+ if (command.size() <= next_colon) {
+ return false;
+ }
+
+ std::string_view port = command.substr(0, next_colon);
+ for (auto digit : port) {
+ if (!isdigit(digit)) {
+ // Port isn't a number.
+ port_valid = false;
+ break;
}
}
- return serial_end;
+
+ if (port_valid) {
+ consume(next_colon + 1);
+ }
+ return finish();
}
} // namespace internal
@@ -606,8 +671,8 @@
static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
#if ADB_HOST
- char* service = nullptr;
- char* serial = nullptr;
+ std::string_view service;
+ std::string_view serial;
TransportId transport_id = 0;
TransportType type = kTransportAny;
#endif
@@ -644,49 +709,52 @@
D("SS(%d): '%s'", s->id, (char*)(s->smart_socket_data.data() + 4));
#if ADB_HOST
- service = &s->smart_socket_data[4];
- if (!strncmp(service, "host-serial:", strlen("host-serial:"))) {
- char* serial_end;
- service += strlen("host-serial:");
+ service = std::string_view(s->smart_socket_data).substr(4);
+ if (service.starts_with("host-serial:")) {
+ service.remove_prefix(strlen("host-serial:"));
// serial number should follow "host:" and could be a host:port string.
- serial_end = internal::skip_host_serial(service);
- if (serial_end) {
- *serial_end = 0; // terminate string
- serial = service;
- service = serial_end + 1;
+ if (!internal::parse_host_service(&serial, &service, service)) {
+ LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
+ goto fail;
}
- } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) {
- service += strlen("host-transport-id:");
- transport_id = strtoll(service, &service, 10);
-
- if (*service != ':') {
+ } else if (service.starts_with("host-transport-id:")) {
+ service.remove_prefix(strlen("host-transport-id:"));
+ if (!ParseUint(&transport_id, service, &service)) {
+ LOG(ERROR) << "SS(" << s->id << "): failed to parse host transport id: " << service;
return -1;
}
- service++;
- } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
+ if (!service.starts_with(":")) {
+ LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
+ return -1;
+ }
+ service.remove_prefix(1);
+ } else if (service.starts_with("host-usb:")) {
type = kTransportUsb;
- service += strlen("host-usb:");
- } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
+ service.remove_prefix(strlen("host-usb:"));
+ } else if (service.starts_with("host-local:")) {
type = kTransportLocal;
- service += strlen("host-local:");
- } else if (!strncmp(service, "host:", strlen("host:"))) {
+ service.remove_prefix(strlen("host-local:"));
+ } else if (service.starts_with("host:")) {
type = kTransportAny;
- service += strlen("host:");
+ service.remove_prefix(strlen("host:"));
} else {
- service = nullptr;
+ service = std::string_view{};
}
- if (service) {
+ if (!service.empty()) {
asocket* s2;
// Some requests are handled immediately -- in that case the handle_host_request() routine
// has sent the OKAY or FAIL message and all we have to do is clean up.
- if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s)) {
- D("SS(%d): handled host service '%s'", s->id, service);
+ // TODO: Convert to string_view.
+ if (handle_host_request(std::string(service).c_str(), type,
+ serial.empty() ? nullptr : std::string(serial).c_str(),
+ transport_id, s->peer->fd, s)) {
+ LOG(VERBOSE) << "SS(" << s->id << "): handled host service '" << service << "'";
goto fail;
}
- if (!strncmp(service, "transport", strlen("transport"))) {
+ if (service.starts_with("transport")) {
D("SS(%d): okay transport", s->id);
s->smart_socket_data.clear();
return 0;
@@ -696,9 +764,11 @@
** if no such service exists, we'll fail out
** and tear down here.
*/
- s2 = create_host_service_socket(service, serial, transport_id);
+ // TODO: Convert to string_view.
+ s2 = create_host_service_socket(std::string(service).c_str(), std::string(serial).c_str(),
+ transport_id);
if (s2 == nullptr) {
- D("SS(%d): couldn't create host service '%s'", s->id, service);
+ LOG(VERBOSE) << "SS(" << s->id << "): couldn't create host service '" << service << "'";
SendFail(s->peer->fd, "unknown host service");
goto fail;
}
@@ -759,7 +829,7 @@
/* give him our transport and upref it */
s->peer->transport = s->transport;
- connect_to_remote(s->peer, s->smart_socket_data.data() + 4);
+ connect_to_remote(s->peer, std::string_view(s->smart_socket_data).substr(4));
s->peer = nullptr;
s->close(s);
return 1;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index b8d7e06..15247e7 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -27,6 +27,7 @@
#include <errno.h>
#include <string>
+#include <string_view>
#include <vector>
// Include this before open/close/unlink are defined as macros below.
@@ -139,7 +140,7 @@
}
// See the comments for the !defined(_WIN32) version of unix_open().
-extern int unix_open(const char* path, int options, ...);
+extern int unix_open(std::string_view path, int options, ...);
#define open ___xxx_unix_open
// Checks if |fd| corresponds to a console.
@@ -357,20 +358,17 @@
// by unix_read(), unix_write(), unix_close()). Also, the C Runtime has
// configurable CR/LF translation which defaults to text mode, but is settable
// with _setmode().
-static __inline__ int unix_open(const char* path, int options,...)
-{
- if ((options & O_CREAT) == 0)
- {
- return TEMP_FAILURE_RETRY( open(path, options) );
- }
- else
- {
- int mode;
- va_list args;
- va_start( args, options );
- mode = va_arg( args, int );
- va_end( args );
- return TEMP_FAILURE_RETRY( open( path, options, mode ) );
+static __inline__ int unix_open(std::string_view path, int options, ...) {
+ std::string zero_terminated(path.begin(), path.end());
+ if ((options & O_CREAT) == 0) {
+ return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options));
+ } else {
+ int mode;
+ va_list args;
+ va_start(args, options);
+ mode = va_arg(args, int);
+ va_end(args);
+ return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options, mode));
}
}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 8a6541d..dbc8920 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -29,6 +29,7 @@
#include <memory>
#include <mutex>
#include <string>
+#include <string_view>
#include <unordered_map>
#include <vector>
@@ -2203,15 +2204,15 @@
}
}
-int unix_open(const char* path, int options, ...) {
+int unix_open(std::string_view path, int options, ...) {
std::wstring path_wide;
- if (!android::base::UTF8ToWide(path, &path_wide)) {
+ if (!android::base::UTF8ToWide(path.data(), path.size(), &path_wide)) {
return -1;
}
if ((options & O_CREAT) == 0) {
return _wopen(path_wide.c_str(), options);
} else {
- int mode;
+ int mode;
va_list args;
va_start(args, options);
mode = va_arg(args, int);
diff --git a/base/Android.bp b/base/Android.bp
index 741664b..b0181aa 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,6 +20,7 @@
"-Wall",
"-Werror",
"-Wextra",
+ "-D_FILE_OFFSET_BITS=64",
],
}
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 71d2a1d..e91598d 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -329,12 +329,14 @@
MetadataBuilder* operator->() const { return builder_.get(); }
private:
+ FastbootDevice* device_;
std::string super_device_;
uint32_t slot_number_;
std::unique_ptr<MetadataBuilder> builder_;
};
-PartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name) {
+PartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name)
+ : device_(device) {
std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
slot_number_ = SlotNumberForSlotSuffix(slot_suffix);
auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number_));
@@ -350,7 +352,7 @@
if (!metadata) {
return false;
}
- return UpdateAllPartitionMetadata(super_device_, *metadata.get());
+ return UpdateAllPartitionMetadata(device_, super_device_, *metadata.get());
}
bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index fbba631..f737405 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -143,6 +143,11 @@
return device->WriteFail("Data is not a valid logical partition metadata image");
}
+ if (!FindPhysicalPartition(super_name)) {
+ return device->WriteFail("Cannot find " + super_name +
+ ", build may be missing broken or missing boot_devices");
+ }
+
// If we are unable to read the existing metadata, then the super partition
// is corrupt. In this case we reflash the whole thing using the provided
// image.
@@ -184,7 +189,7 @@
}
// Write the new table to every metadata slot.
- if (!UpdateAllPartitionMetadata(super_name, *new_metadata.get())) {
+ if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
return device->WriteFail("Unable to write new partition table");
}
fs_mgr_overlayfs_teardown();
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 2ae9ac5..2ebd57d 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -200,10 +200,16 @@
return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
}
-bool UpdateAllPartitionMetadata(const std::string& super_name,
+bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
const android::fs_mgr::LpMetadata& metadata) {
+ size_t num_slots = 1;
+ auto boot_control_hal = device->boot_control_hal();
+ if (boot_control_hal) {
+ num_slots = boot_control_hal->getNumberSlots();
+ }
+
bool ok = true;
- for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
+ for (size_t i = 0; i < num_slots; i++) {
ok &= UpdatePartitionTable(super_name, metadata, i);
}
return ok;
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 4c6aa07..bfeeb74 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -68,5 +68,5 @@
bool GetDeviceLockStatus();
// Update all copies of metadata.
-bool UpdateAllPartitionMetadata(const std::string& super_name,
+bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
const android::fs_mgr::LpMetadata& metadata);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index ac138a3..824511e 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -40,6 +40,7 @@
"fs_mgr_verity.cpp",
"fs_mgr_dm_linear.cpp",
"fs_mgr_overlayfs.cpp",
+ "fs_mgr_roots.cpp",
"fs_mgr_vendor_overlay.cpp",
],
shared_libs: [
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index fbb5f5d..960410c 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -83,18 +83,19 @@
-------
- Space used in the backing storage is on a file by file basis
- and will require more space than if updated in place.
+ and will require more space than if updated in place. As such
+ it is important to be mindful of any wasted space, for instance
+ **BOARD_<partition>IMAGE_PARTITION_RESERVED_SIZE** being defined
+ will have a negative impact on the overall right-sizing of images
+ and thus free dynamic partition space.
- Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
with "*overlayfs: override_creds=off option bypass creator_cred*"
if higher than 4.6.
- *adb enable-verity* will free up overlayfs and as a bonus the
device will be reverted pristine to before any content was updated.
Update engine does not take advantage of this, will perform a full OTA.
-- Update engine will not run if *fs_mgr_overlayfs_is_setup*() reports
- true as adb remount overrides are incompatable with an OTA for
- multiple reasons.
- NB: This is not a problem for fastbootd or recovery as overrides are
- disabled for those special boot scenarios.
+- Update engine may not run if *fs_mgr_overlayfs_is_setup*() reports
+ true as adb remount overrides are incompatable with an OTA resources.
- For implementation simplicity on retrofit dynamic partition devices,
take the whole alternate super (eg: if "*a*" slot, then the whole of
"*system_b*").
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 88f7a2c..943fe10 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1179,11 +1179,12 @@
// wrapper to __mount() and expects a fully prepared fstab_rec,
// unlike fs_mgr_do_mount which does more things with avb / verity etc.
-int fs_mgr_do_mount_one(const FstabEntry& entry) {
+int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point) {
// Run fsck if needed
prepare_fs_for_mount(entry.blk_device, entry);
- int ret = __mount(entry.blk_device, entry.mount_point, entry);
+ int ret =
+ __mount(entry.blk_device, mount_point.empty() ? entry.mount_point : mount_point, entry);
if (ret) {
ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
}
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 733ad55..abece4d 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -81,6 +81,9 @@
bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
std::string cmdline;
if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
+ if (!cmdline.empty() && cmdline.back() == '\n') {
+ cmdline.pop_back();
+ }
return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
}
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
new file mode 100644
index 0000000..f44ff41
--- /dev/null
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fs_mgr/roots.h"
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "fs_mgr.h"
+#include "fs_mgr_dm_linear.h"
+#include "fs_mgr_priv.h"
+
+namespace android {
+namespace fs_mgr {
+
+static constexpr const char* kSystemRoot = "/system";
+
+static bool gDidMapLogicalPartitions = false;
+
+FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path) {
+ if (path.empty()) return nullptr;
+ std::string str(path);
+ while (true) {
+ auto it = std::find_if(fstab->begin(), fstab->end(),
+ [&str](const auto& entry) { return entry.mount_point == str; });
+ if (it != fstab->end()) return &*it;
+ if (str == "/") break;
+ auto slash = str.find_last_of('/');
+ if (slash == std::string::npos) break;
+ if (slash == 0) {
+ str = "/";
+ } else {
+ str = str.substr(0, slash);
+ }
+ }
+ return nullptr;
+}
+
+enum class MountState {
+ ERROR = -1,
+ NOT_MOUNTED = 0,
+ MOUNTED = 1,
+};
+
+static MountState GetMountState(const std::string& mount_point) {
+ Fstab mounted_fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+ LERROR << "Failed to scan mounted volumes";
+ return MountState::ERROR;
+ }
+
+ auto mv = std::find_if(
+ mounted_fstab.begin(), mounted_fstab.end(),
+ [&mount_point](const auto& entry) { return entry.mount_point == mount_point; });
+ if (mv != mounted_fstab.end()) {
+ return MountState::MOUNTED;
+ }
+ return MountState::NOT_MOUNTED;
+}
+
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_pt) {
+ auto rec = GetEntryForPath(fstab, path);
+ if (rec == nullptr) {
+ LERROR << "unknown volume for path [" << path << "]";
+ return false;
+ }
+ if (rec->fs_type == "ramdisk") {
+ // The ramdisk is always mounted.
+ return true;
+ }
+
+ // If we can't acquire the block device for a logical partition, it likely
+ // was never created. In that case we try to create it.
+ if (rec->fs_mgr_flags.logical && !fs_mgr_update_logical_partition(rec)) {
+ if (gDidMapLogicalPartitions) {
+ LERROR << "Failed to find block device for partition";
+ return false;
+ }
+ std::string super_name = fs_mgr_get_super_partition_name();
+ if (!android::fs_mgr::CreateLogicalPartitions("/dev/block/by-name/" + super_name)) {
+ LERROR << "Failed to create logical partitions";
+ return false;
+ }
+ gDidMapLogicalPartitions = true;
+ if (!fs_mgr_update_logical_partition(rec)) {
+ LERROR << "Failed to find block device for partition";
+ return false;
+ }
+ }
+
+ auto mounted = GetMountState(rec->mount_point);
+ if (mounted == MountState::ERROR) {
+ return false;
+ }
+ if (mounted == MountState::MOUNTED) {
+ return true;
+ }
+
+ const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;
+
+ static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs"};
+ if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
+ LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
+ return false;
+ }
+
+ int result = fs_mgr_do_mount_one(*rec, mount_point);
+ if (result == -1 && rec->fs_mgr_flags.formattable) {
+ PERROR << "Failed to mount " << mount_point << "; formatting";
+ bool crypt_footer = rec->is_encryptable() && rec->key_loc == "footer";
+ if (fs_mgr_do_format(*rec, crypt_footer) != 0) {
+ PERROR << "Failed to format " << mount_point;
+ return false;
+ }
+ result = fs_mgr_do_mount_one(*rec, mount_point);
+ }
+
+ if (result == -1) {
+ PERROR << "Failed to mount " << mount_point;
+ return false;
+ }
+ return true;
+}
+
+bool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {
+ auto rec = GetEntryForPath(fstab, path);
+ if (rec == nullptr) {
+ LERROR << "unknown volume for path [" << path << "]";
+ return false;
+ }
+ if (rec->fs_type == "ramdisk") {
+ // The ramdisk is always mounted; you can't unmount it.
+ return false;
+ }
+
+ Fstab mounted_fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+ LERROR << "Failed to scan mounted volumes";
+ return false;
+ }
+
+ auto mounted = GetMountState(rec->mount_point);
+ if (mounted == MountState::ERROR) {
+ return false;
+ }
+ if (mounted == MountState::NOT_MOUNTED) {
+ return true;
+ }
+
+ int result = umount(rec->mount_point.c_str());
+ if (result == -1) {
+ PWARNING << "Failed to umount " << rec->mount_point;
+ return false;
+ }
+ return true;
+}
+
+std::string GetSystemRoot() {
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ LERROR << "Failed to read default fstab";
+ return "";
+ }
+
+ auto it = std::find_if(fstab.begin(), fstab.end(),
+ [](const auto& entry) { return entry.mount_point == kSystemRoot; });
+ if (it == fstab.end()) {
+ return "/";
+ }
+
+ return kSystemRoot;
+}
+
+bool LogicalPartitionsMapped() {
+ return gDidMapLogicalPartitions;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 814ba46..e87332f 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -69,7 +69,7 @@
int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point);
int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
bool need_cp);
-int fs_mgr_do_mount_one(const FstabEntry& entry);
+int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point = "");
int fs_mgr_do_mount_one(fstab_rec* rec);
int fs_mgr_do_tmpfs_mount(const char *n_name);
fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab);
diff --git a/fs_mgr/include/fs_mgr/roots.h b/fs_mgr/include/fs_mgr/roots.h
new file mode 100644
index 0000000..65c59cf
--- /dev/null
+++ b/fs_mgr/include/fs_mgr/roots.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <fs_mgr.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Finds the volume specified by the given path. fs_mgr_get_entry_for_mount_point() does exact match
+// only, so it attempts the prefixes recursively (e.g. "/cache/recovery/last_log",
+// "/cache/recovery", "/cache", "/" for a given path of "/cache/recovery/last_log") and returns the
+// first match or nullptr.
+FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path);
+
+// Make sure that the volume 'path' is on is mounted.
+// * If 'mount_point' is nullptr, use mount point in fstab. Caller can call
+// fs_mgr_ensure_path_unmounted() with the same 'path' argument to unmount.
+// * If 'mount_point' is not nullptr, the mount point is overridden. Caller can
+// call umount(mount_point) to unmount.
+// Returns true on success (volume is mounted).
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point = "");
+
+// Make sure that the volume 'path' is on is unmounted. Returns true on
+// success (volume is unmounted).
+bool EnsurePathUnmounted(Fstab* fstab, const std::string& path);
+
+// Return "/system" if it is in default fstab, otherwise "/".
+std::string GetSystemRoot();
+
+// Return true iff logical partitions are mapped when partitions are mounted via ensure_path_mounted
+// functions.
+bool LogicalPartitionsMapped();
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 22af123..90bce51 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -20,10 +20,6 @@
recovery_available: true,
export_include_dirs: ["include"],
- cflags: [
- // TODO(b/110035986): Allows us to create a skeleton of required classes
- "-Wno-unused-private-field",
- ],
srcs: [
"dm_table.cpp",
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index c4b57c7..d9786ad 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -203,7 +203,8 @@
}
next += vers->next;
data_size -= vers->next;
- vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+ vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) +
+ next);
}
return true;
@@ -288,12 +289,23 @@
}
bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
+ return GetTable(name, 0, table);
+}
+
+bool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
+ return GetTable(name, DM_STATUS_TABLE_FLAG, table);
+}
+
+// private methods of DeviceMapper
+bool DeviceMapper::GetTable(const std::string& name, uint32_t flags,
+ std::vector<TargetInfo>* table) {
char buffer[4096];
struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer);
InitIo(io, name);
io->data_size = sizeof(buffer);
io->data_start = sizeof(*io);
+ io->flags = flags;
if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
return false;
@@ -327,7 +339,6 @@
return true;
}
-// private methods of DeviceMapper
void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
memset(io, 0, sizeof(*io));
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 91f8bb4..28e6e01 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -128,6 +128,10 @@
};
bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
+ // Identical to GetTableStatus, except also retrives the active table for the device
+ // mapper device from the kernel.
+ bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table);
+
private:
// Maximum possible device mapper targets registered in the kernel.
// This is only used to read the list of targets from kernel so we allocate
@@ -140,6 +144,8 @@
// limit we are imposing here of 256.
static constexpr uint32_t kMaxPossibleDmDevices = 256;
+ bool GetTable(const std::string& name, uint32_t flags, std::vector<TargetInfo>* table);
+
void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
DeviceMapper();
diff --git a/fs_mgr/libfiemap_writer/.clang-format b/fs_mgr/libfiemap_writer/.clang-format
new file mode 120000
index 0000000..8b770a1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-4
\ No newline at end of file
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
new file mode 100644
index 0000000..33c3cad
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+ name: "libfiemap_writer",
+ defaults: ["fs_mgr_defaults"],
+ recovery_available: true,
+ export_include_dirs: ["include"],
+ cflags: [
+ // TODO(b/121211685): Allows us to create a skeleton of required classes
+ "-Wno-unused-private-field",
+ "-Wno-unused-parameter",
+ ],
+
+ srcs: [
+ "fiemap_writer.cpp",
+ ],
+
+ header_libs: [
+ "libbase_headers",
+ "liblog_headers",
+ ],
+}
+
+cc_test {
+ name: "fiemap_writer_test",
+ static_libs: [
+ "libbase",
+ "libdm",
+ "libfiemap_writer",
+ "liblog",
+ ],
+
+ data: [
+ "testdata/unaligned_file",
+ "testdata/file_4k",
+ "testdata/file_32k",
+ ],
+
+ srcs: [
+ "fiemap_writer_test.cpp",
+ ],
+}
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
new file mode 100644
index 0000000..097e07f
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <libfiemap_writer/fiemap_writer.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fiemap_writer {
+
+// We are expecting no more than 512 extents in a fiemap of the file we create.
+// If we find more, then it is treated as error for now.
+static constexpr const uint32_t kMaxExtents = 512;
+
+// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
+static constexpr const uint32_t kUnsupportedExtentFlags =
+ FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
+ FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
+ FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
+
+static inline void cleanup(const std::string& file_path, bool created) {
+ if (created) {
+ unlink(file_path.c_str());
+ }
+}
+
+static bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
+ // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
+ // The directory name in the target corresponds to the name of the block device. We use
+ // that to extract the block device name.
+ // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
+ // follows.
+ // 1:0 -> ../../devices/virtual/block/ram0
+ std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
+ std::string sysfs_bdev;
+
+ if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
+ PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
+ return false;
+ }
+
+ *bdev_name = ::android::base::Basename(sysfs_bdev);
+ // Paranoid sanity check to make sure we just didn't get the
+ // input in return as-is.
+ if (sysfs_bdev == *bdev_name) {
+ LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
+ return false;
+ }
+
+ return true;
+}
+
+static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
+ // TODO: Stop popping the device mapper stack if dm-linear target is found
+ if (!::android::base::StartsWith(bdev, "dm-")) {
+ // We are at the bottom of the device mapper stack.
+ *bdev_raw = bdev;
+ return true;
+ }
+
+ std::string dm_leaf_dir = ::android::base::StringPrintf("/sys/block/%s/slaves", bdev.c_str());
+ auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
+ if (d == nullptr) {
+ PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
+ return false;
+ }
+
+ struct dirent* de;
+ uint32_t num_leaves = 0;
+ std::string bdev_next = "";
+ while ((de = readdir(d.get())) != nullptr) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+ continue;
+ }
+
+ // We set the first name we find here
+ if (bdev_next.empty()) {
+ bdev_next = de->d_name;
+ }
+ num_leaves++;
+ }
+
+ // if we have more than one leaves, we return immediately. We can't continue to create the
+ // file since we don't know how to write it out using fiemap, so it will be readable via the
+ // underlying block devices later. The reader will also have to construct the same device mapper
+ // target in order read the file out.
+ if (num_leaves > 1) {
+ LOG(ERROR) << "Found " << num_leaves << " leaf block devices under device mapper device "
+ << bdev;
+ return false;
+ }
+
+ // recursively call with the block device we found in order to pop the device mapper stack.
+ return DeviceMapperStackPop(bdev_next, bdev_raw);
+}
+
+static bool FileToBlockDevicePath(const std::string& file_path, std::string* bdev_path) {
+ struct stat sb;
+ if (stat(file_path.c_str(), &sb)) {
+ PLOG(ERROR) << "Failed to get stat for: " << file_path;
+ return false;
+ }
+
+ std::string bdev;
+ if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {
+ LOG(ERROR) << "Failed to get block device name for " << major(sb.st_dev) << ":"
+ << minor(sb.st_dev);
+ return false;
+ }
+
+ std::string bdev_raw;
+ if (!DeviceMapperStackPop(bdev, &bdev_raw)) {
+ LOG(ERROR) << "Failed to get the bottom of the device mapper stack for device: " << bdev;
+ return false;
+ }
+
+ LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
+ << bdev << ")";
+
+ *bdev_path = ::android::base::StringPrintf("/dev/block/%s", bdev_raw.c_str());
+
+ // Make sure we are talking to a block device before calling it a success.
+ if (stat(bdev_path->c_str(), &sb)) {
+ PLOG(ERROR) << "Failed to get stat for block device: " << *bdev_path;
+ return false;
+ }
+
+ if ((sb.st_mode & S_IFMT) != S_IFBLK) {
+ PLOG(ERROR) << "File: " << *bdev_path << " is not a block device";
+ return false;
+ }
+
+ return true;
+}
+
+static bool GetBlockDeviceParams(int bdev_fd, const std::string& bdev_path, uint64_t* blocksz,
+ uint64_t* bdev_size) {
+ // TODO: For some reason, the block device ioctl require the argument to be initialized
+ // to zero even if its the out parameter for the given ioctl cmd.
+ uint64_t blksz = 0;
+ if (ioctl(bdev_fd, BLKBSZGET, &blksz)) {
+ PLOG(ERROR) << "Failed to get block size for: " << bdev_path;
+ return false;
+ }
+
+ uint64_t size_in_bytes = 0;
+ if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
+ PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
+ return false;
+ }
+
+ *blocksz = blksz;
+ *bdev_size = size_in_bytes;
+
+ return true;
+}
+
+static uint64_t GetFileSize(const std::string& file_path) {
+ struct stat sb;
+ if (stat(file_path.c_str(), &sb)) {
+ PLOG(ERROR) << "Failed to get size for file: " << file_path;
+ return 0;
+ }
+
+ return sb.st_size;
+}
+
+static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t blocksz,
+ uint32_t* fs_type) {
+ // Check if the size aligned to the block size of the block device.
+ // We need this to be true in order to be able to write the file using FIEMAP.
+ if (file_size % blocksz) {
+ LOG(ERROR) << "File size " << file_size << " is not aligned to block size " << blocksz
+ << " for file " << file_path;
+ return false;
+ }
+
+ struct statfs64 sfs;
+ if (statfs64(file_path.c_str(), &sfs)) {
+ PLOG(ERROR) << "Failed to read file system status at: " << file_path;
+ return false;
+ }
+
+ // Check if the filesystem is of supported types.
+ // Only ext4 and f2fs are tested and supported.
+ if ((sfs.f_type != EXT4_SUPER_MAGIC) && (sfs.f_type != F2FS_SUPER_MAGIC)) {
+ LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
+ return false;
+ }
+
+ uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
+ if (available_bytes <= file_size) {
+ LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
+ return false;
+ }
+
+ *fs_type = sfs.f_type;
+ return true;
+}
+
+static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
+ uint64_t file_size) {
+ // Reserve space for the file on the file system and write it out to make sure the extents
+ // don't come back unwritten. Return from this function with the kernel file offset set to 0.
+ // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
+ // aren't moved around.
+ if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
+ PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
+ return false;
+ }
+
+ // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
+ // blocks are actually written to by the file system and thus getting rid of the holes in the
+ // file.
+ auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
+ if (buffer == nullptr) {
+ LOG(ERROR) << "failed to allocate memory for writing file";
+ return false;
+ }
+
+ off64_t offset = lseek64(file_fd, 0, SEEK_SET);
+ if (offset < 0) {
+ PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
+ return false;
+ }
+
+ for (; offset < file_size; offset += blocksz) {
+ if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
+ PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
+ << " in file " << file_path;
+ return false;
+ }
+ }
+
+ if (lseek64(file_fd, 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
+ return false;
+ }
+
+ // flush all writes here ..
+ if (fsync(file_fd)) {
+ PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
+ return false;
+ }
+
+ return true;
+}
+
+static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
+ if (fs_type == EXT4_SUPER_MAGIC) {
+ // No pinning necessary for ext4. The blocks, once allocated, are expected
+ // to be fixed.
+ return true;
+ }
+
+// F2FS-specific ioctl
+// It requires the below kernel commit merged in v4.16-rc1.
+// 1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
+// In android-4.4,
+// 56ee1e817908 ("f2fs: updates on v4.16-rc1")
+// In android-4.9,
+// 2f17e34672a8 ("f2fs: updates on v4.16-rc1")
+// In android-4.14,
+// ce767d9a55bc ("f2fs: updates on v4.16-rc1")
+#ifndef F2FS_IOC_SET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#endif
+
+ uint32_t pin_status = 1;
+ int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
+ if (error) {
+ if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+ PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
+ } else {
+ PLOG(ERROR) << "Failed to pin file: " << file_path;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+#if 0
+static bool PinFileStatus(int file_fd, const std::string& file_path, uint32_t fs_type) {
+ if (fs_type == EXT4_SUPER_MAGIC) {
+ // No pinning necessary for ext4. The blocks, once allocated, are expected
+ // to be fixed.
+ return true;
+ }
+
+// F2FS-specific ioctl
+// It requires the below kernel commit merged in v4.16-rc1.
+// 1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
+// In android-4.4,
+// 56ee1e817908 ("f2fs: updates on v4.16-rc1")
+// In android-4.9,
+// 2f17e34672a8 ("f2fs: updates on v4.16-rc1")
+// In android-4.14,
+// ce767d9a55bc ("f2fs: updates on v4.16-rc1")
+#ifndef F2FS_IOC_GET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
+#endif
+
+ uint32_t pin_status;
+ int error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &pin_status);
+ if (error) {
+ if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+ PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
+ } else {
+ PLOG(ERROR) << "Failed to get file pin status: " << file_path;
+ }
+ return false;
+ }
+
+ return !!pin_status;
+}
+#endif
+
+static void LogExtent(uint32_t num, const struct fiemap_extent& ext) {
+ LOG(INFO) << "Extent #" << num;
+ LOG(INFO) << " fe_logical: " << ext.fe_logical;
+ LOG(INFO) << " fe_physical: " << ext.fe_physical;
+ LOG(INFO) << " fe_length: " << ext.fe_length;
+ LOG(INFO) << " fe_flags: 0x" << std::hex << ext.fe_flags;
+}
+
+static bool ReadFiemap(int file_fd, const std::string& file_path,
+ std::vector<struct fiemap_extent>* extents) {
+ uint64_t fiemap_size =
+ sizeof(struct fiemap_extent) + kMaxExtents * sizeof(struct fiemap_extent);
+ auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
+ if (buffer == nullptr) {
+ LOG(ERROR) << "Failed to allocate memory for fiemap";
+ return false;
+ }
+
+ struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());
+ fiemap->fm_start = 0;
+ fiemap->fm_length = UINT64_MAX;
+ // make sure file is synced to disk before we read the fiemap
+ fiemap->fm_flags = FIEMAP_FLAG_SYNC;
+ fiemap->fm_extent_count = kMaxExtents;
+
+ if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
+ PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
+ return false;
+ }
+
+ if (fiemap->fm_mapped_extents == 0) {
+ LOG(ERROR) << "File " << file_path << " has zero extents";
+ return false;
+ }
+
+ // Iterate through each extent read and make sure its valid before adding it to the vector
+ bool last_extent_seen = false;
+ struct fiemap_extent* extent = &fiemap->fm_extents[0];
+ for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++, extent++) {
+ // LogExtent(i + 1, *extent);
+ if (extent->fe_flags & kUnsupportedExtentFlags) {
+ LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
+ << " has unsupported flags";
+ extents->clear();
+ return false;
+ }
+
+ if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
+ last_extent_seen = true;
+ if (i != (fiemap->fm_mapped_extents - 1)) {
+ LOG(WARNING) << "Extents are being received out-of-order";
+ }
+ }
+ extents->emplace_back(std::move(*extent));
+ }
+
+ if (!last_extent_seen) {
+ // The file is possibly too fragmented.
+ if (fiemap->fm_mapped_extents == kMaxExtents) {
+ LOG(ERROR) << "File is too fragmented, needs more than " << kMaxExtents << " extents.";
+ }
+ extents->clear();
+ }
+
+ return last_extent_seen;
+}
+
+FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create) {
+ // if 'create' is false, open an existing file and do not truncate.
+ int open_flags = O_RDWR | O_CLOEXEC;
+ if (create) {
+ if (access(file_path.c_str(), F_OK) == 0) {
+ LOG(WARNING) << "File " << file_path << " already exists, truncating";
+ }
+ open_flags |= O_CREAT | O_TRUNC;
+ }
+ ::android::base::unique_fd file_fd(
+ TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
+ if (file_fd < 0) {
+ PLOG(ERROR) << "Failed to create file at: " << file_path;
+ return nullptr;
+ }
+
+ std::string abs_path;
+ if (!::android::base::Realpath(file_path, &abs_path)) {
+ PLOG(ERROR) << "Invalid file path: " << file_path;
+ cleanup(file_path, create);
+ return nullptr;
+ }
+
+ std::string bdev_path;
+ if (!FileToBlockDevicePath(abs_path, &bdev_path)) {
+ LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
+ cleanup(file_path, create);
+ return nullptr;
+ }
+
+ ::android::base::unique_fd bdev_fd(
+ TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDWR | O_CLOEXEC)));
+ if (bdev_fd < 0) {
+ PLOG(ERROR) << "Failed to open block device: " << bdev_path;
+ cleanup(file_path, create);
+ return nullptr;
+ }
+
+ uint64_t blocksz, bdevsz;
+ if (!GetBlockDeviceParams(bdev_fd, bdev_path, &blocksz, &bdevsz)) {
+ LOG(ERROR) << "Failed to get block device params for: " << bdev_path;
+ cleanup(file_path, create);
+ return nullptr;
+ }
+
+ if (!create) {
+ file_size = GetFileSize(abs_path);
+ if (file_size == 0) {
+ LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
+ return nullptr;
+ }
+ }
+
+ uint32_t fs_type;
+ if (!PerformFileChecks(abs_path, file_size, blocksz, &fs_type)) {
+ LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
+ cleanup(file_path, create);
+ return nullptr;
+ }
+
+ if (create) {
+ if (!AllocateFile(file_fd, abs_path, blocksz, file_size)) {
+ unlink(abs_path.c_str());
+ return nullptr;
+ }
+ }
+
+ // f2fs may move the file blocks around.
+ if (!PinFile(file_fd, file_path, fs_type)) {
+ cleanup(file_path, create);
+ LOG(ERROR) << "Failed to pin the file in storage";
+ return nullptr;
+ }
+
+ // now allocate the FiemapWriter and start setting it up
+ FiemapUniquePtr fmap(new FiemapWriter());
+ if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
+ LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
+ cleanup(file_path, create);
+ return nullptr;
+ }
+
+ fmap->file_path_ = abs_path;
+ fmap->bdev_path_ = bdev_path;
+ fmap->file_fd_ = std::move(file_fd);
+ fmap->bdev_fd_ = std::move(bdev_fd);
+ fmap->file_size_ = file_size;
+ fmap->bdev_size_ = bdevsz;
+ fmap->fs_type_ = fs_type;
+ fmap->block_size_ = blocksz;
+
+ LOG(INFO) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
+ << bdev_path;
+ return fmap;
+}
+
+bool FiemapWriter::Flush() const {
+ if (fsync(bdev_fd_)) {
+ PLOG(ERROR) << "Failed to flush " << bdev_path_ << " with fsync";
+ return false;
+ }
+ return true;
+}
+
+// TODO: Test with fs block_size > bdev block_size
+bool FiemapWriter::Write(off64_t off, uint8_t* buffer, uint64_t size) {
+ if (!size || size > file_size_) {
+ LOG(ERROR) << "Failed write: size " << size << " is invalid for file's size " << file_size_;
+ return false;
+ }
+
+ if (off + size > file_size_) {
+ LOG(ERROR) << "Failed write: Invalid offset " << off << " or size " << size
+ << " for file size " << file_size_;
+ return false;
+ }
+
+ if ((off & (block_size_ - 1)) || (size & (block_size_ - 1))) {
+ LOG(ERROR) << "Failed write: Unaligned offset " << off << " or size " << size
+ << " for block size " << block_size_;
+ return false;
+ }
+#if 0
+ // TODO(b/122138114): check why this fails.
+ if (!PinFileStatus(file_fd_, file_path_, fs_type_)) {
+ LOG(ERROR) << "Failed write: file " << file_path_ << " is not pinned";
+ return false;
+ }
+#endif
+ // find extents that must be written to and then write one at a time.
+ uint32_t num_extent = 1;
+ uint32_t buffer_offset = 0;
+ for (auto& extent : extents_) {
+ uint64_t e_start = extent.fe_logical;
+ uint64_t e_end = extent.fe_logical + extent.fe_length;
+ // Do we write in this extent ?
+ if (off >= e_start && off < e_end) {
+ uint64_t written = WriteExtent(extent, buffer + buffer_offset, off, size);
+ if (written == 0) {
+ return false;
+ }
+
+ buffer_offset += written;
+ off += written;
+ size -= written;
+
+ // Paranoid check to make sure we are done with this extent now
+ if (size && (off >= e_start && off < e_end)) {
+ LOG(ERROR) << "Failed to write extent fully";
+ LogExtent(num_extent, extent);
+ return false;
+ }
+
+ if (size == 0) {
+ // done
+ break;
+ }
+ }
+ num_extent++;
+ }
+
+ return true;
+}
+
+bool FiemapWriter::Read(off64_t off, uint8_t* buffer, uint64_t size) {
+ return false;
+}
+
+// private helpers
+
+// WriteExtent() Returns the total number of bytes written. It will always be multiple of
+// block_size_. 0 is returned in one of the two cases.
+// 1. Any write failed between logical_off & logical_off + length.
+// 2. The logical_offset + length doesn't overlap with the extent passed.
+// The function can either partially for fully write the extent depending on the
+// logical_off + length. It is expected that alignment checks for size and offset are
+// performed before calling into this function.
+uint64_t FiemapWriter::WriteExtent(const struct fiemap_extent& ext, uint8_t* buffer,
+ off64_t logical_off, uint64_t length) {
+ uint64_t e_start = ext.fe_logical;
+ uint64_t e_end = ext.fe_logical + ext.fe_length;
+ if (logical_off < e_start || logical_off >= e_end) {
+ LOG(ERROR) << "Failed write extent, invalid offset " << logical_off << " and size "
+ << length;
+ LogExtent(0, ext);
+ return 0;
+ }
+
+ off64_t bdev_offset = ext.fe_physical + (logical_off - e_start);
+ if (bdev_offset >= bdev_size_) {
+ LOG(ERROR) << "Failed write extent, invalid block # " << bdev_offset << " for block device "
+ << bdev_path_ << " of size " << bdev_size_ << " bytes";
+ return 0;
+ }
+ if (TEMP_FAILURE_RETRY(lseek64(bdev_fd_, bdev_offset, SEEK_SET)) == -1) {
+ PLOG(ERROR) << "Failed write extent, seek offset for " << bdev_path_ << " offset "
+ << bdev_offset;
+ return 0;
+ }
+
+ // Determine how much we want to write at once.
+ uint64_t logical_end = logical_off + length;
+ uint64_t write_size = (e_end <= logical_end) ? (e_end - logical_off) : length;
+ if (!android::base::WriteFully(bdev_fd_, buffer, write_size)) {
+ PLOG(ERROR) << "Failed write extent, write " << bdev_path_ << " at " << bdev_offset
+ << " size " << write_size;
+ return 0;
+ }
+
+ return write_size;
+}
+
+} // namespace fiemap_writer
+} // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
new file mode 100644
index 0000000..6653ada
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <libdm/loop_control.h>
+
+#include <libfiemap_writer/fiemap_writer.h>
+
+using namespace std;
+using namespace android::fiemap_writer;
+using unique_fd = android::base::unique_fd;
+using LoopDevice = android::dm::LoopDevice;
+
+std::string testfile = "";
+std::string testbdev = "";
+uint64_t testfile_size = 536870912; // default of 512MiB
+
+TEST(FiemapWriter, CreateImpossiblyLargeFile) {
+ // Try creating a file of size ~100TB but aligned to
+ // 512 byte to make sure block alignment tests don't
+ // fail.
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);
+ EXPECT_EQ(fptr, nullptr);
+ EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
+ EXPECT_EQ(errno, ENOENT);
+}
+
+TEST(FiemapWriter, CreateUnalignedFile) {
+ // Try creating a file of size 4097 bytes which is guaranteed
+ // to be unaligned to all known block sizes. The creation must
+ // fail.
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4097);
+ EXPECT_EQ(fptr, nullptr);
+ EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
+ EXPECT_EQ(errno, ENOENT);
+}
+
+TEST(FiemapWriter, CheckFilePath) {
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
+ ASSERT_NE(fptr, nullptr);
+ EXPECT_EQ(fptr->size(), 4096);
+ EXPECT_EQ(fptr->file_path(), testfile);
+ EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
+}
+
+TEST(FiemapWriter, CheckBlockDevicePath) {
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 4096);
+ EXPECT_EQ(fptr->size(), 4096);
+ EXPECT_EQ(fptr->bdev_path(), testbdev);
+}
+
+TEST(FiemapWriter, CheckFileCreated) {
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
+ ASSERT_NE(fptr, nullptr);
+ unique_fd fd(open(testfile.c_str(), O_RDONLY));
+ EXPECT_GT(fd, -1);
+}
+
+TEST(FiemapWriter, CheckFileSizeActual) {
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+ ASSERT_NE(fptr, nullptr);
+
+ struct stat sb;
+ ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
+ EXPECT_EQ(sb.st_size, testfile_size);
+}
+
+TEST(FiemapWriter, CheckFileExtents) {
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+ ASSERT_NE(fptr, nullptr);
+ EXPECT_GT(fptr->extents().size(), 0);
+}
+
+TEST(FiemapWriter, CheckWriteError) {
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
+ ASSERT_NE(fptr, nullptr);
+
+ // prepare buffer for writing the pattern - 0xa0
+ uint64_t blocksize = fptr->block_size();
+ auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
+ ASSERT_NE(buffer, nullptr);
+ memset(buffer.get(), 0xa0, blocksize);
+
+ uint8_t* p = static_cast<uint8_t*>(buffer.get());
+ for (off64_t off = 0; off < testfile_size; off += blocksize) {
+ ASSERT_TRUE(fptr->Write(off, p, blocksize));
+ }
+
+ EXPECT_TRUE(fptr->Flush());
+}
+
+class TestExistingFile : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string unaligned_file = exec_dir + "/testdata/unaligned_file";
+ std::string file_4k = exec_dir + "/testdata/file_4k";
+ std::string file_32k = exec_dir + "/testdata/file_32k";
+ fptr_unaligned = FiemapWriter::Open(unaligned_file, 4097, false);
+ fptr_4k = FiemapWriter::Open(file_4k, 4096, false);
+ fptr_32k = FiemapWriter::Open(file_32k, 32768, false);
+ }
+
+ FiemapUniquePtr fptr_unaligned;
+ FiemapUniquePtr fptr_4k;
+ FiemapUniquePtr fptr_32k;
+};
+
+TEST_F(TestExistingFile, ErrorChecks) {
+ EXPECT_EQ(fptr_unaligned, nullptr);
+ EXPECT_NE(fptr_4k, nullptr);
+ EXPECT_NE(fptr_32k, nullptr);
+
+ EXPECT_EQ(fptr_4k->size(), 4096);
+ EXPECT_EQ(fptr_32k->size(), 32768);
+ EXPECT_GT(fptr_4k->extents().size(), 0);
+ EXPECT_GT(fptr_32k->extents().size(), 0);
+}
+
+TEST_F(TestExistingFile, CheckWriteError) {
+ ASSERT_NE(fptr_4k, nullptr);
+ // prepare buffer for writing the pattern - 0xa0
+ uint64_t blocksize = fptr_4k->block_size();
+ auto buff_4k = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
+ ASSERT_NE(buff_4k, nullptr);
+ memset(buff_4k.get(), 0xa0, blocksize);
+
+ uint8_t* p = static_cast<uint8_t*>(buff_4k.get());
+ for (off64_t off = 0; off < 4096; off += blocksize) {
+ ASSERT_TRUE(fptr_4k->Write(off, p, blocksize));
+ }
+ EXPECT_TRUE(fptr_4k->Flush());
+
+ ASSERT_NE(fptr_32k, nullptr);
+ // prepare buffer for writing the pattern - 0xa0
+ blocksize = fptr_32k->block_size();
+ auto buff_32k = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksize), free);
+ ASSERT_NE(buff_32k, nullptr);
+ memset(buff_32k.get(), 0xa0, blocksize);
+ p = static_cast<uint8_t*>(buff_32k.get());
+ for (off64_t off = 0; off < 4096; off += blocksize) {
+ ASSERT_TRUE(fptr_32k->Write(off, p, blocksize));
+ }
+ EXPECT_TRUE(fptr_32k->Flush());
+}
+
+class VerifyBlockWritesExt4 : public ::testing::Test {
+ // 2GB Filesystem and 4k block size by default
+ static constexpr uint64_t block_size = 4096;
+ static constexpr uint64_t fs_size = 2147483648;
+
+ protected:
+ void SetUp() override {
+ fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
+ uint64_t count = fs_size / block_size;
+ std::string dd_cmd =
+ ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+ " count=%" PRIu64 " > /dev/null 2>&1",
+ fs_path.c_str(), block_size, count);
+ std::string mkfs_cmd =
+ ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
+ // create mount point
+ mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+ ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+ // create file for the file system
+ int ret = system(dd_cmd.c_str());
+ ASSERT_EQ(ret, 0);
+ // Get and attach a loop device to the filesystem we created
+ LoopDevice loop_dev(fs_path);
+ ASSERT_TRUE(loop_dev.valid());
+ // create file system
+ ret = system(mkfs_cmd.c_str());
+ ASSERT_EQ(ret, 0);
+
+ // mount the file system
+ ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
+ }
+
+ void TearDown() override {
+ umount(mntpoint.c_str());
+ rmdir(mntpoint.c_str());
+ unlink(fs_path.c_str());
+ }
+
+ std::string mntpoint;
+ std::string fs_path;
+};
+
+TEST_F(VerifyBlockWritesExt4, CheckWrites) {
+ EXPECT_EQ(access(fs_path.c_str(), F_OK), 0);
+
+ std::string file_path = mntpoint + "/testfile";
+ uint64_t file_size = 100 * 1024 * 1024;
+ auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+ ASSERT_NE(buffer, nullptr);
+ memset(buffer.get(), 0xa0, getpagesize());
+ {
+ // scoped fiemap writer
+ FiemapUniquePtr fptr = FiemapWriter::Open(file_path, file_size);
+ ASSERT_NE(fptr, nullptr);
+ uint8_t* p = static_cast<uint8_t*>(buffer.get());
+ for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+ ASSERT_TRUE(fptr->Write(off, p, getpagesize()));
+ }
+ EXPECT_TRUE(fptr->Flush());
+ }
+ // unmount file system here to make sure we invalidated all page cache and
+ // remount the filesystem again for verification
+ ASSERT_EQ(umount(mntpoint.c_str()), 0);
+
+ LoopDevice loop_dev(fs_path);
+ ASSERT_TRUE(loop_dev.valid());
+ ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0)
+ << "failed to mount: " << loop_dev.device() << " on " << mntpoint << ": "
+ << strerror(errno);
+
+ ::android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_SYNC));
+ ASSERT_NE(fd, -1);
+ auto filebuf = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+ ASSERT_NE(filebuf, nullptr);
+ for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+ memset(filebuf.get(), 0x00, getpagesize());
+ ASSERT_EQ(pread64(fd, filebuf.get(), getpagesize(), off), getpagesize());
+ ASSERT_EQ(memcmp(filebuf.get(), buffer.get(), getpagesize()), 0)
+ << "Invalid pattern at offset: " << off << " size " << getpagesize();
+ }
+}
+
+class VerifyBlockWritesF2fs : public ::testing::Test {
+ // 2GB Filesystem and 4k block size by default
+ static constexpr uint64_t block_size = 4096;
+ static constexpr uint64_t fs_size = 2147483648;
+
+ protected:
+ void SetUp() override {
+ fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
+ uint64_t count = fs_size / block_size;
+ std::string dd_cmd =
+ ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+ " count=%" PRIu64 " > /dev/null 2>&1",
+ fs_path.c_str(), block_size, count);
+ std::string mkfs_cmd =
+ ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
+ // create mount point
+ mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+ ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+ // create file for the file system
+ int ret = system(dd_cmd.c_str());
+ ASSERT_EQ(ret, 0);
+ // Get and attach a loop device to the filesystem we created
+ LoopDevice loop_dev(fs_path);
+ ASSERT_TRUE(loop_dev.valid());
+ // create file system
+ ret = system(mkfs_cmd.c_str());
+ ASSERT_EQ(ret, 0);
+
+ // mount the file system
+ ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
+ }
+
+ void TearDown() override {
+ umount(mntpoint.c_str());
+ rmdir(mntpoint.c_str());
+ unlink(fs_path.c_str());
+ }
+
+ std::string mntpoint;
+ std::string fs_path;
+};
+
+TEST_F(VerifyBlockWritesF2fs, CheckWrites) {
+ EXPECT_EQ(access(fs_path.c_str(), F_OK), 0);
+
+ std::string file_path = mntpoint + "/testfile";
+ uint64_t file_size = 100 * 1024 * 1024;
+ auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+ ASSERT_NE(buffer, nullptr);
+ memset(buffer.get(), 0xa0, getpagesize());
+ {
+ // scoped fiemap writer
+ FiemapUniquePtr fptr = FiemapWriter::Open(file_path, file_size);
+ ASSERT_NE(fptr, nullptr);
+ uint8_t* p = static_cast<uint8_t*>(buffer.get());
+ for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+ ASSERT_TRUE(fptr->Write(off, p, getpagesize()));
+ }
+ EXPECT_TRUE(fptr->Flush());
+ }
+ // unmount file system here to make sure we invalidated all page cache and
+ // remount the filesystem again for verification
+ ASSERT_EQ(umount(mntpoint.c_str()), 0);
+
+ LoopDevice loop_dev(fs_path);
+ ASSERT_TRUE(loop_dev.valid());
+ ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0)
+ << "failed to mount: " << loop_dev.device() << " on " << mntpoint << ": "
+ << strerror(errno);
+
+ ::android::base::unique_fd fd(open(file_path.c_str(), O_RDONLY | O_SYNC));
+ ASSERT_NE(fd, -1);
+ auto filebuf = std::unique_ptr<void, decltype(&free)>(calloc(1, getpagesize()), free);
+ ASSERT_NE(filebuf, nullptr);
+ for (off64_t off = 0; off < file_size / getpagesize(); off += getpagesize()) {
+ memset(filebuf.get(), 0x00, getpagesize());
+ ASSERT_EQ(pread64(fd, filebuf.get(), getpagesize(), off), getpagesize());
+ ASSERT_EQ(memcmp(filebuf.get(), buffer.get(), getpagesize()), 0)
+ << "Invalid pattern at offset: " << off << " size " << getpagesize();
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ if (argc <= 2) {
+ cerr << "Filepath with its bdev path must be provided as follows:" << endl;
+ cerr << " $ fiemap_writer_test <path to file> </dev/block/XXXX" << endl;
+ cerr << " where, /dev/block/XXX is the block device where the file resides" << endl;
+ exit(EXIT_FAILURE);
+ }
+ ::android::base::InitLogging(argv, ::android::base::StderrLogger);
+
+ testfile = argv[1];
+ testbdev = argv[2];
+ if (argc > 3) {
+ testfile_size = strtoull(argv[3], NULL, 0);
+ if (testfile_size == ULLONG_MAX) {
+ testfile_size = 512 * 1024 * 1024;
+ }
+ }
+
+ return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
new file mode 100644
index 0000000..ae61344
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <linux/fiemap.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fiemap_writer {
+
+class FiemapWriter;
+using FiemapUniquePtr = std::unique_ptr<FiemapWriter>;
+
+class FiemapWriter final {
+ public:
+ // Factory method for FiemapWriter.
+ // The method returns FiemapUniquePtr that contains all the data necessary to be able to write
+ // to the given file directly using raw block i/o.
+ static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
+ bool create = true);
+
+ // Syncs block device writes.
+ bool Flush() const;
+
+ // Writes the file by using its FIEMAP and performing i/o on the raw block device.
+ // The return value is success / failure. This will happen in particular if the
+ // kernel write returns errors, extents are not writeable or more importantly, if the 'size' is
+ // not aligned to the block device's block size.
+ bool Write(off64_t off, uint8_t* buffer, uint64_t size);
+
+ // The counter part of Write(). It is an error for the offset to be unaligned with
+ // the block device's block size.
+ // In case of error, the contents of buffer MUST be discarded.
+ bool Read(off64_t off, uint8_t* buffer, uint64_t size);
+
+ ~FiemapWriter() = default;
+
+ const std::string& file_path() const { return file_path_; };
+ uint64_t size() const { return file_size_; };
+ const std::string& bdev_path() const { return bdev_path_; };
+ uint64_t block_size() const { return block_size_; };
+ const std::vector<struct fiemap_extent>& extents() { return extents_; };
+
+ // Non-copyable & Non-movable
+ FiemapWriter(const FiemapWriter&) = delete;
+ FiemapWriter& operator=(const FiemapWriter&) = delete;
+ FiemapWriter& operator=(FiemapWriter&&) = delete;
+ FiemapWriter(FiemapWriter&&) = delete;
+
+ private:
+ // Name of the file managed by this class.
+ std::string file_path_;
+ // Block device on which we have created the file.
+ std::string bdev_path_;
+
+ // File descriptors for the file and block device
+ ::android::base::unique_fd file_fd_;
+ ::android::base::unique_fd bdev_fd_;
+
+ // Size in bytes of the file this class is writing
+ uint64_t file_size_;
+
+ // total size in bytes of the block device
+ uint64_t bdev_size_;
+
+ // Filesystem type where the file is being created.
+ // See: <uapi/linux/magic.h> for filesystem magic numbers
+ uint32_t fs_type_;
+
+ // block size as reported by the kernel of the underlying block device;
+ uint64_t block_size_;
+
+ // This file's fiemap
+ std::vector<struct fiemap_extent> extents_;
+
+ FiemapWriter() = default;
+
+ uint64_t WriteExtent(const struct fiemap_extent& ext, uint8_t* buffer, off64_t logical_off,
+ uint64_t length);
+};
+
+} // namespace fiemap_writer
+} // namespace android
diff --git a/fs_mgr/libfiemap_writer/testdata/file_32k b/fs_mgr/libfiemap_writer/testdata/file_32k
new file mode 100644
index 0000000..12f3be4
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/file_32k
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/file_4k b/fs_mgr/libfiemap_writer/testdata/file_4k
new file mode 100644
index 0000000..08e7df1
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/file_4k
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/unaligned_file b/fs_mgr/libfiemap_writer/testdata/unaligned_file
new file mode 100644
index 0000000..c107c26
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/testdata/unaligned_file
Binary files differ
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 07f9d66..07e3c8a 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -212,7 +212,7 @@
sABOverrideValue = ab_device;
}
-MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
+MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false), ignore_slot_suffixing_(false) {
memset(&geometry_, 0, sizeof(geometry_));
geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
geometry_.struct_size = sizeof(geometry_);
@@ -436,7 +436,7 @@
LERROR << "Could not find partition group: " << group_name;
return nullptr;
}
- if (IsABDevice() && !auto_slot_suffixing_ && name != "scratch" &&
+ if (IsABDevice() && !auto_slot_suffixing_ && name != "scratch" && !ignore_slot_suffixing_ &&
GetPartitionSlotSuffix(name).empty()) {
LERROR << "Unsuffixed partition not allowed on A/B device: " << name;
return nullptr;
@@ -972,6 +972,10 @@
auto_slot_suffixing_ = true;
}
+void MetadataBuilder::IgnoreSlotSuffixing() {
+ ignore_slot_suffixing_ = true;
+}
+
bool MetadataBuilder::IsABDevice() const {
if (sABOverrideSet) {
return sABOverrideValue;
@@ -983,5 +987,18 @@
return GetBlockDevicePartitionName(block_devices_[0]) != LP_METADATA_DEFAULT_PARTITION_NAME;
}
+bool MetadataBuilder::AddLinearExtent(Partition* partition, const std::string& block_device,
+ uint64_t num_sectors, uint64_t physical_sector) {
+ uint32_t device_index;
+ if (!FindBlockDeviceByName(block_device, &device_index)) {
+ LERROR << "Could not find backing block device for extent: " << block_device;
+ return false;
+ }
+
+ auto extent = std::make_unique<LinearExtent>(num_sectors, device_index, physical_sector);
+ partition->AddExtent(std::move(extent));
+ return true;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f477b4b..57cce21 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -219,6 +219,10 @@
// Find a group by name. If no group is found, nullptr is returned.
PartitionGroup* FindGroup(const std::string& name);
+ // Add a predetermined extent to a partition.
+ bool AddLinearExtent(Partition* partition, const std::string& block_device,
+ uint64_t num_sectors, uint64_t physical_sector);
+
// Grow or shrink a partition to the requested size. This size will be
// rounded UP to the nearest block (512 bytes).
//
@@ -244,6 +248,9 @@
// Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
void SetAutoSlotSuffixing();
+ // If set, checks for slot suffixes will be ignored internally.
+ void IgnoreSlotSuffixing();
+
bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
@@ -306,6 +313,7 @@
std::vector<std::unique_ptr<PartitionGroup>> groups_;
std::vector<LpMetadataBlockDevice> block_devices_;
bool auto_slot_suffixing_;
+ bool ignore_slot_suffixing_;
};
// Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 5957e30..9e211e3 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -92,7 +92,7 @@
Returns: true if device is (likely) a debug build" ]
isDebuggable() {
- if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
+ if inAdb && [ 1 -ne "`get_property ro.debuggable`" ]; then
false
fi
}
@@ -389,16 +389,16 @@
die -t "${T}" "disable-verity"
fi
rebooted=false
-if [ X"${D}" != X"${H}" -a X"${D}" = X"${D##*using overlayfs}" ]; then
+if [ X"${D}" != X"${H}" ]; then
echo "${H}"
if [ X"${D}" != X"${D##*setup failed}" ]; then
echo "${ORANGE}[ WARNING ]${NORMAL} overlayfs setup whined" >&2
fi
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | grep "^overlay "` &&
- [ -n "${D}" ] &&
- ( echo "${H}" && echo "${D}" ) &&
+ D=`echo "${D}" | grep "^overlay " || true` &&
+ [ -z "${D}" ] ||
+ ( echo "${H}" && echo "${D}" && false ) ||
die -t ${T} "overlay takeover unexpected at this phase"
echo "${GREEN}[ INFO ]${NORMAL} rebooting as requested" >&2
L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
@@ -426,12 +426,12 @@
die -t "${T}" "setup for overlay"
fi
if [ X"${D}" != X"${D##*Successfully disabled verity}" ]; then
- echo "${D}"
+ echo "${H}"
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | grep "^overlay " | true` &&
- [ -n "${D}" ] &&
- ( echo "${H}" && echo "${D}" ) &&
+ D=`echo "${D}" | grep "^overlay " || true` &&
+ [ -z "${D}" ] ||
+ ( echo "${H}" && echo "${D}" && false ) ||
( [ -n "${L}" ] && echo "${L}" && false ) ||
die -t "${T}" "overlay takeover unexpected"
[ -n "${L}" ] && echo "${L}"
@@ -504,17 +504,17 @@
# Check something
-echo "${GREEN}[ RUN ]${NORMAL} push content to system and vendor" >&2
+echo "${GREEN}[ RUN ]${NORMAL} push content to /system and /vendor" >&2
A="Hello World! $(date)"
echo "${A}" | adb_sh "cat - > /system/hello"
echo "${A}" | adb_sh "cat - > /vendor/hello"
B="`adb_cat /system/hello`" ||
die "sytem hello"
-check_eq "${A}" "${B}" system before reboot
+check_eq "${A}" "${B}" /system before reboot
B="`adb_cat /vendor/hello`" ||
die "vendor hello"
-check_eq "${A}" "${B}" vendor before reboot
+check_eq "${A}" "${B}" /vendor before reboot
echo "${GREEN}[ RUN ]${NORMAL} reboot to confirm content persistent" >&2
@@ -537,18 +537,21 @@
fi
B="`adb_cat /system/hello`" ||
- die "re-read system hello after reboot"
-check_eq "${A}" "${B}" system after reboot
+ die "re-read /system/hello after reboot"
+check_eq "${A}" "${B}" /system after reboot
+echo "${GREEN}[ OK ]${NORMAL} /system content remains after reboot" >&2
# Only root can read vendor if sepolicy permissions are as expected
if ${enforcing}; then
B="`adb_cat /vendor/hello`" &&
- die "re-read vendor hello after reboot w/o root"
+ die "re-read /vendor/hello after reboot w/o root"
check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
+ echo "${GREEN}[ OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
fi
adb_root &&
B="`adb_cat /vendor/hello`" ||
- die "re-read vendor hello after reboot"
+ die "re-read /vendor/hello after reboot"
check_eq "${A}" "${B}" vendor after reboot
+echo "${GREEN}[ OK ]${NORMAL} /vendor content remains after reboot" >&2
echo "${GREEN}[ RUN ]${NORMAL} flash vendor, confirm its content disappears" >&2
@@ -608,17 +611,17 @@
echo "${H}" &&
echo "${D}" &&
echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
- die "overlay system takeover after flash vendor"
+ die "overlay /system takeover after flash vendor"
echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
- die "overlay minus vendor takeover after flash vendor"
+ die "overlay supposed to be minus /vendor takeover after flash vendor"
fi
B="`adb_cat /system/hello`" ||
- die "re-read system hello after flash vendor"
+ die "re-read /system/hello after flash vendor"
check_eq "${A}" "${B}" system after flash vendor
adb_root ||
die "adb root"
B="`adb_cat /vendor/hello`" &&
- die "re-read vendor hello after flash vendor"
+ die "re-read /vendor/hello after flash vendor"
check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
fi
@@ -630,10 +633,10 @@
adb_sh rm /system/hello </dev/null ||
die -t ${T} "cleanup hello"
B="`adb_cat /system/hello`" &&
- die "re-read system hello after rm"
+ die "re-read /system/hello after rm"
check_eq "cat: /system/hello: No such file or directory" "${B}" after flash rm
B="`adb_cat /vendor/hello`" &&
- die "re-read vendor hello after rm"
+ die "re-read /vendor/hello after rm"
check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
if [ -n "${scratch_partition}" ]; then
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index f78093b..3b6ff9b 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -36,6 +36,8 @@
#include <string>
#include <vector>
+using namespace std::literals::string_literals;
+
using DeviceMapper = ::android::dm::DeviceMapper;
using DmTable = ::android::dm::DmTable;
using DmTarget = ::android::dm::DmTarget;
@@ -51,7 +53,7 @@
std::cerr << "commands:" << std::endl;
std::cerr << " create <dm-name> [-ro] <targets...>" << std::endl;
std::cerr << " delete <dm-name>" << std::endl;
- std::cerr << " list <devices | targets>" << std::endl;
+ std::cerr << " list <devices | targets> [-v]" << std::endl;
std::cerr << " getpath <dm-name>" << std::endl;
std::cerr << " table <dm-name>" << std::endl;
std::cerr << " help" << std::endl;
@@ -197,7 +199,8 @@
return 0;
}
-static int DmListTargets(DeviceMapper& dm) {
+static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
+ [[maybe_unused]] char** argv) {
std::vector<DmTargetTypeInfo> targets;
if (!dm.GetAvailableTargets(&targets)) {
std::cerr << "Failed to read available device mapper targets" << std::endl;
@@ -218,7 +221,7 @@
return 0;
}
-static int DmListDevices(DeviceMapper& dm) {
+static int DmListDevices(DeviceMapper& dm, int argc, char** argv) {
std::vector<DmBlockDevice> devices;
if (!dm.GetAvailableDevices(&devices)) {
std::cerr << "Failed to read available device mapper devices" << std::endl;
@@ -230,15 +233,37 @@
return 0;
}
+ bool verbose = (argc && (argv[0] == "-v"s));
for (const auto& dev : devices) {
std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
<< dev.Minor() << std::endl;
+ if (verbose) {
+ std::vector<DeviceMapper::TargetInfo> table;
+ if (!dm.GetTableInfo(dev.name(), &table)) {
+ std::cerr << "Could not query table status for device \"" << dev.name() << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
+
+ uint32_t target_num = 1;
+ for (const auto& target : table) {
+ std::cout << " target#" << target_num << ": ";
+ std::cout << target.spec.sector_start << "-"
+ << (target.spec.sector_start + target.spec.length) << ": "
+ << target.spec.target_type;
+ if (!target.data.empty()) {
+ std::cout << ", " << target.data;
+ }
+ std::cout << std::endl;
+ target_num++;
+ }
+ }
}
return 0;
}
-static const std::map<std::string, std::function<int(DeviceMapper&)>> listmap = {
+static const std::map<std::string, std::function<int(DeviceMapper&, int, char**)>> listmap = {
{"targets", DmListTargets},
{"devices", DmListDevices},
};
@@ -251,7 +276,7 @@
DeviceMapper& dm = DeviceMapper::Instance();
for (const auto& l : listmap) {
- if (l.first == argv[0]) return l.second(dm);
+ if (l.first == argv[0]) return l.second(dm, argc - 1, argv + 1);
}
std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
diff --git a/init/README.md b/init/README.md
index 5c07ac1..8d47e0a 100644
--- a/init/README.md
+++ b/init/README.md
@@ -813,3 +813,42 @@
SELinux would permit the operation, or if the UIDs and GIDs resolve.
2) No checking if a service exists or has a valid SELinux domain defined
3) No checking if a service has not been previously defined in a different init script.
+
+Early Init Boot Sequence
+------------------------
+The early init boot sequence is broken up into three stages: first stage init, SELinux setup, and
+second stage init.
+
+First stage init is responsible for setting up the bare minimum requirements to load the rest of the
+system. Specifically this includes mounting /dev, /proc, mounting 'early mount' partitions (which
+needs to include all partitions that contain system code, for example system and vendor), and moving
+the system.img mount to / for devices with a ramdisk.
+
+Note that in Android Q, system.img always contains TARGET_ROOT_OUT and always is mounted at / by the
+time first stage init finishes. Android Q will also require dynamic partitions and therefore will
+require using a ramdisk to boot Android. The recovery ramdisk can be used to boot to Android instead
+of a dedicated ramdisk as well.
+
+First stage init has three variations depending on the device configuration:
+1) For system-as-root devices, first stage init is part of /system/bin/init and a symlink at /init
+points to /system/bin/init for backwards compatibility. These devices do not need to do anything to
+mount system.img, since it is by definition already mounted as the rootfs by the kernel.
+
+2) For devices with a ramdisk, first stage init is a static executable located at /init. These
+devices mount system.img as /system then perform a switch root operation to move the mount at
+/system to /. The contents of the ramdisk are freed after mounting has completed.
+
+3) For devices that use recovery as a ramdisk, first stage init it contained within the shared init
+located at /init within the recovery ramdisk. These devices first switch root to
+/first_stage_ramdisk to remove the recovery components from the environment, then proceed the same
+as 2). Note that the decision to boot normally into Android instead of booting
+into recovery mode is made if androidboot.force_normal_boot=1 is present in the
+kernel commandline.
+
+Once first stage init finishes it execs /system/bin/init with the "selinux_setup" argument. This
+phase is where SELinux is optionally compiled and loaded onto the system. selinux.cpp contains more
+information on the specifics of this process.
+
+Lastly once that phase finishes, it execs /system/bin/init again with the "second_stage"
+argument. At this point the main phase of init runs and continues the boot process via the init.rc
+scripts.
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index acefdf0..b5ff658 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -395,6 +395,11 @@
return false;
}
if (fs_mgr_do_mount_one(*fstab_entry)) {
+ if (fstab_entry->fs_mgr_flags.formattable) {
+ PLOG(INFO) << "Failed to mount '" << fstab_entry->mount_point << "', "
+ << "ignoring mount for formattable partition";
+ return true;
+ }
PLOG(ERROR) << "Failed to mount '" << fstab_entry->mount_point << "'";
return false;
}
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 3a09096..04ca207 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -306,6 +306,11 @@
}
std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+ std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
+ if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
+ product_policy_cil_file.clear();
+ }
+
// vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
// nonplat_sepolicy.cil.
std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");
@@ -342,6 +347,9 @@
};
// clang-format on
+ if (!product_policy_cil_file.empty()) {
+ compile_args.push_back(product_policy_cil_file.c_str());
+ }
if (!plat_pub_versioned_cil_file.empty()) {
compile_args.push_back(plat_pub_versioned_cil_file.c_str());
}
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 8b0c53e..ac94e69 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -311,7 +311,7 @@
};
FuseBridgeLoop::FuseBridgeLoop() : opened_(true) {
- base::unique_fd epoll_fd(epoll_create1(/* no flag */ 0));
+ base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
if (epoll_fd.get() == -1) {
PLOG(ERROR) << "Failed to open FD for epoll";
opened_ = false;
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 39cb995..038b59e 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -76,7 +76,7 @@
return UnwindFromContext(num_ignore_frames, ucontext);
}
- if (Tid() != android::base::GetThreadId()) {
+ if (Tid() != static_cast<pid_t>(android::base::GetThreadId())) {
return UnwindThread(num_ignore_frames);
}
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index fe28eba..f5f9b2a 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -117,7 +117,7 @@
back_frame->map.name = frame->map_name;
back_frame->map.start = frame->map_start;
back_frame->map.end = frame->map_end;
- back_frame->map.offset = frame->map_offset;
+ back_frame->map.offset = frame->map_elf_start_offset;
back_frame->map.load_bias = frame->map_load_bias;
back_frame->map.flags = frame->map_flags;
}
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 68bf898..0723612 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -133,6 +133,7 @@
#define AID_LLKD 1070 /* live lock daemon */
#define AID_IORAPD 1071 /* input/output readahead and pin daemon */
#define AID_GPU_SERVICE 1072 /* GPU service daemon */
+#define AID_NETWORK_STACK 1073 /* network stack service */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
index 715d63d..1f93494 100644
--- a/libmeminfo/tools/Android.bp
+++ b/libmeminfo/tools/Android.bp
@@ -13,11 +13,24 @@
// limitations under the License.
cc_binary {
+ name: "librank2",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: ["librank.cpp"],
+ shared_libs: [
+ "libbase",
+ "libmeminfo",
+ ],
+}
+
+cc_binary {
name: "procmem2",
cflags: [
"-Wall",
"-Werror",
- "-Wno-unused-parameter",
],
srcs: ["procmem.cpp"],
@@ -32,7 +45,6 @@
cflags: [
"-Wall",
"-Werror",
- "-Wno-unused-parameter",
],
srcs: ["procrank.cpp"],
diff --git a/libmeminfo/tools/librank.cpp b/libmeminfo/tools/librank.cpp
new file mode 100644
index 0000000..2c2583d
--- /dev/null
+++ b/libmeminfo/tools/librank.cpp
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <meminfo/procmeminfo.h>
+
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::Vma;
+
+[[noreturn]] static void usage(int exit_status) {
+ fprintf(stderr,
+ "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n"
+ "\n"
+ "Sort options:\n"
+ " -v Sort processes by VSS.\n"
+ " -r Sort processes by RSS.\n"
+ " -p Sort processes by PSS.\n"
+ " -u Sort processes by USS.\n"
+ " -s Sort processes by swap.\n"
+ " (Default sort order is PSS.)\n"
+ " -a Show all mappings, including stack, heap and anon.\n"
+ " -P /path Limit libraries displayed to those in path.\n"
+ " -R Reverse sort order (default is descending).\n"
+ " -m [r][w][x] Only list pages that exactly match permissions\n"
+ " -c Only show cached (storage backed) pages\n"
+ " -C Only show non-cached (ram/swap backed) pages\n"
+ " -k Only show pages collapsed by KSM\n"
+ " -h Display this help screen.\n",
+ getprogname());
+ exit(exit_status);
+}
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+ to->vss += from.vss;
+ to->rss += from.rss;
+ to->pss += from.pss;
+ to->uss += from.uss;
+
+ to->swap += from.swap;
+
+ to->private_clean += from.private_clean;
+ to->private_dirty += from.private_dirty;
+
+ to->shared_clean += from.shared_clean;
+ to->shared_dirty += from.shared_dirty;
+}
+
+struct ProcessRecord {
+ public:
+ ProcessRecord(pid_t pid) : pid_(-1), cmdline_("") {
+ std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+ std::string cmdline;
+ if (!::android::base::ReadFileToString(fname, &cmdline)) {
+ fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
+ return;
+ }
+ // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
+ // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
+ // e.g. xtra-daemon, lowi-server
+ // The .c_str() assignment below then takes care of trimming the cmdline at the first
+ // 0x00. This is how original procrank worked (luckily)
+ cmdline_ = cmdline.c_str();
+ pid_ = pid;
+ usage_.clear();
+ }
+
+ ~ProcessRecord() = default;
+
+ bool valid() const { return pid_ != -1; }
+
+ // Getters
+ pid_t pid() const { return pid_; }
+ const std::string& cmdline() const { return cmdline_; }
+ const MemUsage& usage() const { return usage_; }
+
+ // Add to the usage
+ void AddUsage(const MemUsage& mem_usage) { add_mem_usage(&usage_, mem_usage); }
+
+ private:
+ pid_t pid_;
+ std::string cmdline_;
+ MemUsage usage_;
+};
+
+struct LibRecord {
+ public:
+ LibRecord(const std::string& name) : name_(name) {}
+ ~LibRecord() = default;
+
+ const std::string& name() const { return name_; }
+ const MemUsage& usage() const { return usage_; }
+ const std::vector<ProcessRecord>& processes() const { return procs_; }
+ uint64_t pss() const { return usage_.pss; }
+ void AddUsage(const ProcessRecord& proc, const MemUsage& mem_usage) {
+ auto process = std::find_if(procs_.begin(), procs_.end(),
+ [&](auto p) -> bool { return p.pid() == proc.pid(); });
+ if (process == procs_.end()) {
+ process = procs_.emplace(procs_.end(), proc.pid());
+ }
+ process->AddUsage(mem_usage);
+ add_mem_usage(&usage_, mem_usage);
+ }
+
+ void Sort(std::function<bool(const ProcessRecord&, const ProcessRecord&)>& sorter) {
+ std::sort(procs_.begin(), procs_.end(), sorter);
+ }
+
+ private:
+ std::string name_;
+ MemUsage usage_;
+ std::vector<ProcessRecord> procs_;
+};
+
+// List of every library / map
+static std::vector<LibRecord> g_libs;
+
+// List of library/map names that we don't want to show by default
+static const std::vector<std::string> g_blacklisted_libs = {"[heap]", "[stack]"};
+
+// Global flags affected by command line
+static uint64_t g_pgflags = 0;
+static uint64_t g_pgflags_mask = 0;
+static uint16_t g_mapflags_mask = 0;
+static bool g_all_libs = false;
+static bool g_has_swap = false;
+static bool g_reverse_sort = false;
+static std::string g_prefix_filter = "";
+
+static bool read_all_pids(std::function<bool(pid_t pid)> for_each_pid) {
+ std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
+ if (!procdir) return false;
+
+ struct dirent* dir;
+ pid_t pid;
+ while ((dir = readdir(procdir.get()))) {
+ if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
+ if (!for_each_pid(pid)) return false;
+ }
+
+ return true;
+}
+
+static bool scan_libs_per_process(pid_t pid) {
+ ProcMemInfo pmem(pid, false, g_pgflags, g_pgflags_mask);
+ const std::vector<Vma> maps = pmem.Maps();
+ if (maps.size() == 0) {
+ // nothing to do here, continue
+ return true;
+ }
+
+ ProcessRecord proc(pid);
+ if (!proc.valid()) {
+ fprintf(stderr, "Failed to create process record for process: %d\n", pid);
+ return false;
+ }
+
+ for (auto& map : maps) {
+ // skip library / map if prefix for the path doesn't match
+ if (!g_prefix_filter.empty() && !::android::base::StartsWith(map.name, g_prefix_filter)) {
+ continue;
+ }
+ // Skip maps based on map permissions
+ if (g_mapflags_mask &&
+ ((map.flags & (PROT_READ | PROT_WRITE | PROT_EXEC)) != g_mapflags_mask)) {
+ continue;
+ }
+
+ // skip blacklisted library / map names
+ if (!g_all_libs && (std::find(g_blacklisted_libs.begin(), g_blacklisted_libs.end(),
+ map.name) != g_blacklisted_libs.end())) {
+ continue;
+ }
+
+ auto lib = std::find_if(g_libs.begin(), g_libs.end(),
+ [&](auto l) -> bool { return map.name == l.name(); });
+ if (lib == g_libs.end()) {
+ lib = g_libs.emplace(g_libs.end(), map.name);
+ }
+
+ lib->AddUsage(proc, map.usage);
+ if (!g_has_swap && map.usage.swap) {
+ g_has_swap = true;
+ }
+ }
+
+ return true;
+}
+
+static uint16_t parse_mapflags(const char* mapflags) {
+ uint16_t ret = 0;
+ for (const char* p = mapflags; *p; p++) {
+ switch (*p) {
+ case 'r':
+ ret |= PROT_READ;
+ break;
+ case 'w':
+ ret |= PROT_WRITE;
+ break;
+ case 'x':
+ ret |= PROT_EXEC;
+ break;
+ default:
+ error(EXIT_FAILURE, 0, "Invalid permissions string: %s, %s", mapflags, p);
+ }
+ }
+
+ return ret;
+}
+
+int main(int argc, char* argv[]) {
+ int opt;
+
+ auto pss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+ return g_reverse_sort ? a.usage().pss < b.usage().pss : a.usage().pss > b.usage().pss;
+ };
+
+ auto uss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+ return g_reverse_sort ? a.usage().uss < b.usage().uss : a.usage().uss > b.usage().uss;
+ };
+
+ auto vss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+ return g_reverse_sort ? a.usage().vss < b.usage().vss : a.usage().vss > b.usage().vss;
+ };
+
+ auto rss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+ return g_reverse_sort ? a.usage().rss < b.usage().rss : a.usage().rss > b.usage().rss;
+ };
+
+ auto swap_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+ return g_reverse_sort ? a.usage().swap < b.usage().swap : a.usage().swap > b.usage().swap;
+ };
+
+ std::function<bool(const ProcessRecord&, const ProcessRecord&)> sort_func = pss_sort;
+
+ while ((opt = getopt(argc, argv, "acChkm:pP:uvrsR")) != -1) {
+ switch (opt) {
+ case 'a':
+ g_all_libs = true;
+ break;
+ case 'c':
+ g_pgflags = 0;
+ g_pgflags_mask = (1 << KPF_SWAPBACKED);
+ break;
+ case 'C':
+ g_pgflags = g_pgflags_mask = (1 << KPF_SWAPBACKED);
+ break;
+ case 'h':
+ usage(EXIT_SUCCESS);
+ case 'k':
+ g_pgflags = g_pgflags_mask = (1 << KPF_KSM);
+ break;
+ case 'm':
+ g_mapflags_mask = parse_mapflags(optarg);
+ break;
+ case 'p':
+ sort_func = pss_sort;
+ break;
+ case 'P':
+ g_prefix_filter = optarg;
+ break;
+ case 'u':
+ sort_func = uss_sort;
+ break;
+ case 'v':
+ sort_func = vss_sort;
+ break;
+ case 'r':
+ sort_func = rss_sort;
+ break;
+ case 's':
+ sort_func = swap_sort;
+ break;
+ case 'R':
+ g_reverse_sort = true;
+ break;
+ default:
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ if (!read_all_pids(scan_libs_per_process)) {
+ error(EXIT_FAILURE, 0, "Failed to read all pids from the system");
+ }
+
+ printf(" %6s %7s %6s %6s %6s ", "RSStot", "VSS", "RSS", "PSS", "USS");
+ if (g_has_swap) {
+ printf(" %6s ", "Swap");
+ }
+ printf("Name/PID\n");
+
+ // sort the libraries by their pss
+ std::sort(g_libs.begin(), g_libs.end(),
+ [](const LibRecord& l1, const LibRecord& l2) { return l1.pss() > l2.pss(); });
+
+ for (auto& lib : g_libs) {
+ printf("%6" PRIu64 "K %7s %6s %6s %6s ", lib.pss() / 1024, "", "", "", "");
+ if (g_has_swap) {
+ printf(" %6s ", "");
+ }
+ printf("%s\n", lib.name().c_str());
+
+ // sort all mappings first
+ lib.Sort(sort_func);
+
+ for (auto& p : lib.processes()) {
+ const MemUsage& usage = p.usage();
+ printf(" %6s %7" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K ", "",
+ usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
+ if (g_has_swap) {
+ printf("%6" PRIu64 "K ", usage.swap / 1024);
+ }
+ printf(" %s [%d]\n", p.cmdline().c_str(), p.pid());
+ }
+ }
+
+ return 0;
+}
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
index 56de12d..b9b174d 100644
--- a/libmeminfo/tools/procmem.cpp
+++ b/libmeminfo/tools/procmem.cpp
@@ -42,7 +42,7 @@
// Show working set, mutually exclusive with reset_wss;
bool show_wss = false;
-static void usage(const char* cmd) {
+[[noreturn]] static void usage(int exit_status) {
fprintf(stderr,
"Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
" -i Uses idle page tracking for working set statistics.\n"
@@ -52,7 +52,9 @@
" -u Sort by USS.\n"
" -m Sort by mapping order (as read from /proc).\n"
" -h Hide maps with no RSS.\n",
- cmd);
+ getprogname());
+
+ exit(exit_status);
}
static void print_separator(std::stringstream& ss) {
@@ -154,17 +156,15 @@
show_wss = true;
break;
case '?':
- usage(argv[0]);
- return 0;
+ usage(EXIT_SUCCESS);
default:
- abort();
+ usage(EXIT_FAILURE);
}
}
if (optind != (argc - 1)) {
fprintf(stderr, "Need exactly one pid at the end\n");
- usage(argv[0]);
- exit(EXIT_FAILURE);
+ usage(EXIT_FAILURE);
}
pid_t pid = atoi(argv[optind]);
diff --git a/libmeminfo/tools/procrank.cpp b/libmeminfo/tools/procrank.cpp
index e39e7fa..a751722 100644
--- a/libmeminfo/tools/procrank.cpp
+++ b/libmeminfo/tools/procrank.cpp
@@ -142,8 +142,9 @@
uint64_t total_uswap = 0;
uint64_t total_zswap = 0;
-static void usage(const char* myname) {
- std::cerr << "Usage: " << myname << " [ -W ] [ -v | -r | -p | -u | -s | -h ]" << std::endl
+[[noreturn]] static void usage(int exit_status) {
+ std::cerr << "Usage: " << getprogname() << " [ -W ] [ -v | -r | -p | -u | -s | -h ]"
+ << std::endl
<< " -v Sort by VSS." << std::endl
<< " -r Sort by RSS." << std::endl
<< " -p Sort by PSS." << std::endl
@@ -159,6 +160,7 @@
<< " -o Show and sort by oom score against lowmemorykiller thresholds."
<< std::endl
<< " -h Display this help screen." << std::endl;
+ exit(exit_status);
}
static bool read_all_pids(std::vector<pid_t>* pids, std::function<bool(pid_t pid)> for_each_pid) {
@@ -386,9 +388,7 @@
pgflags_mask = (1 << KPF_SWAPBACKED);
break;
case 'h':
- usage(argv[0]);
- return 0;
- break;
+ usage(EXIT_SUCCESS);
case 'k':
pgflags = (1 << KPF_KSM);
pgflags_mask = (1 << KPF_KSM);
@@ -422,7 +422,7 @@
reset_wss = true;
break;
default:
- abort();
+ usage(EXIT_FAILURE);
}
}
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index b3e2b97..de7ea08 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -639,7 +639,11 @@
UNUSED(target_sdk_version);
if (class_loader == nullptr) {
*needs_native_bridge = false;
- return dlopen(path, RTLD_NOW);
+ void* handle = dlopen(path, RTLD_NOW);
+ if (handle == nullptr) {
+ *error_msg = dlerror();
+ }
+ return handle;
}
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index 41ca79b..162d1cf 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -24,7 +24,7 @@
#endif
void reset_log_context(android_log_context ctx);
int write_to_logger(android_log_context context, log_id_t id);
-void note_log_drop();
+void note_log_drop(int error);
void stats_log_close();
int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
#ifdef __cplusplus
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index f4a7e94..5b90361 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -120,8 +120,8 @@
return retValue;
}
-void note_log_drop() {
- statsdLoggerWrite.noteDrop();
+void note_log_drop(int error) {
+ statsdLoggerWrite.noteDrop(error);
}
void stats_log_close() {
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index 88f7d44..f00fc2d 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -48,6 +48,7 @@
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
static atomic_int dropped = 0;
+static atomic_int log_error = 0;
void statsd_writer_init_lock() {
/*
@@ -150,8 +151,9 @@
return 1;
}
-static void statsdNoteDrop() {
+static void statsdNoteDrop(int error) {
atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+ atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
}
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
@@ -202,7 +204,8 @@
if (snapshot) {
android_log_event_int_t buffer;
header.id = LOG_ID_STATS;
- buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+ // store the last log error in the tag field. This tag field is not used by statsd.
+ buffer.header.tag = htole32(atomic_load(&log_error));
buffer.payload.type = EVENT_TYPE_INT;
buffer.payload.data = htole32(snapshot);
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
index 7289441..4fc3f8b 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -39,7 +39,7 @@
/* write log to transport, returns number of bytes propagated, or -errno */
int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
/* note one log drop */
- void (*noteDrop)();
+ void (*noteDrop)(int error);
};
#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 4ba8a4b..c128b9b 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -204,7 +204,7 @@
bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
uint64_t* cur_pc) {
const auto* cfa = &DwarfCfaInfo::kTable[op];
- if (cfa->name == nullptr) {
+ if (cfa->name[0] == '\0') {
log(indent, "Illegal");
log(indent, "Raw Data: 0x%02x", op);
return true;
@@ -677,29 +677,29 @@
{DW_EH_PE_uleb128, DW_EH_PE_block},
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
},
- {nullptr, 0, 0, {}, {}}, // 0x17 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x18 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x19 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x1a illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x1b illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x1c DW_CFA_lo_user (Treat as illegal)
- {nullptr, 0, 0, {}, {}}, // 0x1d illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x1e illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x1f illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x20 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x21 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x22 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x23 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x24 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x25 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x26 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x27 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x28 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x29 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x2a illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x2b illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x2c illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+ {"", 0, 0, {}, {}}, // 0x17 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x18 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x19 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1a illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1b illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1c DW_CFA_lo_user (Treat as illegal)
+ {"", 0, 0, {}, {}}, // 0x1d illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1e illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1f illegal cfa
+ {"", 0, 0, {}, {}}, // 0x20 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x21 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x22 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x23 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x24 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x25 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x26 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x27 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x28 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x29 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2a illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2b illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2c illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
{
"DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
2,
@@ -714,21 +714,21 @@
{DW_EH_PE_uleb128, DW_EH_PE_uleb128},
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
},
- {nullptr, 0, 0, {}, {}}, // 0x31 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x32 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x33 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x34 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x35 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x36 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x37 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x38 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x39 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3a illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3b illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3c illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3d illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3e illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3f DW_CFA_hi_user (Treat as illegal)
+ {"", 0, 0, {}, {}}, // 0x31 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x32 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x33 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x34 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x35 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x36 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x37 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x38 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x39 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3a illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3b illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3c illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3d illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3e illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3f DW_CFA_hi_user (Treat as illegal)
};
// Explicitly instantiate DwarfCfa.
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index c5ffb8e..569c17c 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -49,7 +49,14 @@
};
struct Info {
- const char* name;
+ // It may seem cleaner to just change the type of 'name' to 'const char *'.
+ // However, having a pointer here would require relocation at runtime,
+ // causing 'kTable' to be placed in data.rel.ro section instead of rodata
+ // section, adding memory pressure to the system. Note that this is only
+ // safe because this is only used in C++ code. C++ standard, unlike C
+ // standard, mandates the array size to be large enough to hold the NULL
+ // terminator when initialized with a string literal.
+ const char name[36];
uint8_t supported_version;
uint8_t num_operands;
uint8_t operands[2];
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 44ec5c1..39a09cf 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -32,33 +32,27 @@
bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
// One last attempt, see if the previous map is read-only with the
// same name and stretches across this map.
- for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
- if (*iter == this) {
- if (iter == maps_->begin()) {
- return false;
- }
- --iter;
- MapInfo* prev_map = *iter;
- // Make sure this is a read-only map.
- if (prev_map->flags != PROT_READ) {
- return false;
- }
- uint64_t map_size = end - prev_map->end;
- if (!memory->Init(name, prev_map->offset, map_size)) {
- return false;
- }
- uint64_t max_size;
- if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
- return false;
- }
- if (!memory->Init(name, prev_map->offset, max_size)) {
- return false;
- }
- elf_offset = offset - prev_map->offset;
- return true;
- }
+ if (prev_map == nullptr || prev_map->flags != PROT_READ) {
+ return false;
}
- return false;
+
+ uint64_t map_size = end - prev_map->end;
+ if (!memory->Init(name, prev_map->offset, map_size)) {
+ return false;
+ }
+
+ uint64_t max_size;
+ if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
+ return false;
+ }
+
+ if (!memory->Init(name, prev_map->offset, max_size)) {
+ return false;
+ }
+
+ elf_offset = offset - prev_map->offset;
+ elf_start_offset = prev_map->offset;
+ return true;
}
Memory* MapInfo::GetFileMemory() {
@@ -91,14 +85,13 @@
// Check if the start of this map is an embedded elf.
uint64_t max_size = 0;
- uint64_t file_offset = offset;
if (Elf::GetInfo(memory.get(), &max_size)) {
if (max_size > map_size) {
- if (memory->Init(name, file_offset, max_size)) {
+ if (memory->Init(name, offset, max_size)) {
return memory.release();
}
// Try to reinit using the default map_size.
- if (memory->Init(name, file_offset, map_size)) {
+ if (memory->Init(name, offset, map_size)) {
return memory.release();
}
return nullptr;
@@ -109,6 +102,13 @@
// No elf at offset, try to init as if the whole file is an elf.
if (memory->Init(name, 0) && Elf::IsValidElf(memory.get())) {
elf_offset = offset;
+ // Need to check how to set the elf start offset. If this map is not
+ // the r-x map of a r-- map, then use the real offset value. Otherwise,
+ // use 0.
+ if (prev_map == nullptr || prev_map->offset != 0 || prev_map->flags != PROT_READ ||
+ prev_map->name != name) {
+ elf_start_offset = offset;
+ }
return memory.release();
}
@@ -156,35 +156,24 @@
return memory.release();
}
- if (name.empty() || maps_ == nullptr) {
- return nullptr;
- }
-
// Find the read-only map by looking at the previous map. The linker
// doesn't guarantee that this invariant will always be true. However,
// if that changes, there is likely something else that will change and
// break something.
- MapInfo* ro_map_info = nullptr;
- for (auto iter = maps_->begin(); iter != maps_->end(); ++iter) {
- if (*iter == this) {
- if (iter != maps_->begin()) {
- --iter;
- ro_map_info = *iter;
- }
- break;
- }
- }
-
- if (ro_map_info == nullptr || ro_map_info->name != name || ro_map_info->offset >= offset) {
+ if (offset == 0 || name.empty() || prev_map == nullptr || prev_map->name != name ||
+ prev_map->offset >= offset) {
return nullptr;
}
// Make sure that relative pc values are corrected properly.
- elf_offset = offset - ro_map_info->offset;
+ elf_offset = offset - prev_map->offset;
+ // Use this as the elf start offset, otherwise, you always get offsets into
+ // the r-x section, which is not quite the right information.
+ elf_start_offset = prev_map->offset;
MemoryRanges* ranges = new MemoryRanges;
- ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
- ro_map_info->end - ro_map_info->start, 0));
+ ranges->Insert(
+ new MemoryRange(process_memory, prev_map->start, prev_map->end - prev_map->start, 0));
ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
return ranges;
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index a9fb859..c90e383 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -67,13 +67,15 @@
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
+ maps_.push_back(
+ new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
});
}
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name, uint64_t load_bias) {
- MapInfo* map_info = new MapInfo(this, start, end, offset, flags, name);
+ MapInfo* map_info =
+ new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, offset, flags, name);
map_info->load_bias = load_bias;
maps_.push_back(map_info);
}
@@ -81,6 +83,13 @@
void Maps::Sort() {
std::sort(maps_.begin(), maps_.end(),
[](const MapInfo* a, const MapInfo* b) { return a->start < b->start; });
+
+ // Set the prev_map values on the info objects.
+ MapInfo* prev_map = nullptr;
+ for (MapInfo* map_info : maps_) {
+ map_info->prev_map = prev_map;
+ prev_map = map_info;
+ }
}
Maps::~Maps() {
@@ -98,7 +107,8 @@
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
+ maps_.push_back(
+ new MapInfo(maps_.empty() ? nullptr : maps_.back(), start, end, pgoff, flags, name));
});
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 792cd0b..8133639 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -59,7 +59,8 @@
if (info != nullptr) {
frame->map_start = info->start;
frame->map_end = info->end;
- frame->map_offset = info->offset;
+ frame->map_elf_start_offset = info->elf_start_offset;
+ frame->map_exact_offset = info->offset;
frame->map_load_bias = info->load_bias;
frame->map_flags = info->flags;
if (resolve_names_) {
@@ -102,7 +103,8 @@
if (resolve_names_) {
frame->map_name = map_info->name;
}
- frame->map_offset = map_info->offset;
+ frame->map_elf_start_offset = map_info->elf_start_offset;
+ frame->map_exact_offset = map_info->offset;
frame->map_start = map_info->start;
frame->map_end = map_info->end;
frame->map_flags = map_info->flags;
@@ -290,10 +292,6 @@
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
}
- if (frame.map_offset != 0) {
- data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_offset);
- }
-
if (frame.map_start == frame.map_end) {
// No valid map associated with this frame.
data += " <unknown>";
@@ -302,6 +300,11 @@
} else {
data += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", frame.map_start);
}
+
+ if (frame.map_elf_start_offset != 0) {
+ data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_elf_start_offset);
+ }
+
if (!frame.function_name.empty()) {
data += " (" + frame.function_name;
if (frame.function_offset != 0) {
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index cfdefd0..5e3d6f6 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -25,38 +25,31 @@
#include <string>
#include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
namespace unwindstack {
-// Forward declarations.
-class Maps;
-class Memory;
-
struct MapInfo {
- MapInfo(Maps* maps) : maps_(maps) {}
- MapInfo(Maps* maps, uint64_t start, uint64_t end) : maps_(maps), start(start), end(end) {}
- MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const char* name)
- : maps_(maps),
- start(start),
+ : start(start),
end(end),
offset(offset),
flags(flags),
name(name),
+ prev_map(map_info),
load_bias(static_cast<uint64_t>(-1)) {}
- MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name)
- : maps_(maps),
- start(start),
+ : start(start),
end(end),
offset(offset),
flags(flags),
name(name),
+ prev_map(map_info),
load_bias(static_cast<uint64_t>(-1)) {}
~MapInfo() = default;
- Maps* maps_ = nullptr;
-
uint64_t start = 0;
uint64_t end = 0;
uint64_t offset = 0;
@@ -64,10 +57,14 @@
std::string name;
std::shared_ptr<Elf> elf;
// This value is only non-zero if the offset is non-zero but there is
- // no elf signature found at that offset. This indicates that the
- // entire file is represented by the Memory object returned by CreateMemory,
- // instead of a portion of the file.
+ // no elf signature found at that offset.
uint64_t elf_offset = 0;
+ // This value is the offset from the map in memory that is the start
+ // of the elf. This is not equal to offset when the linker splits
+ // shared libraries into a read-only and read-execute map.
+ uint64_t elf_start_offset = 0;
+
+ MapInfo* prev_map = nullptr;
std::atomic_uint64_t load_bias;
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 56b0581..d7bbd9d 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -48,7 +48,13 @@
uint64_t function_offset = 0;
std::string map_name;
- uint64_t map_offset = 0;
+ // The offset from the first map representing the frame. When there are
+ // two maps (read-only and read-execute) this will be the offset from
+ // the read-only map. When there is only one map, this will be the
+ // same as the actual offset of the map and match map_exact_offset.
+ uint64_t map_elf_start_offset = 0;
+ // The actual offset from the map where the pc lies.
+ uint64_t map_exact_offset = 0;
uint64_t map_start = 0;
uint64_t map_end = 0;
uint64_t map_load_bias = 0;
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 7766218..f7689ce 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -269,7 +269,7 @@
elf.FakeSetInterface(interface);
elf.FakeSetValid(true);
- MapInfo map_info(nullptr, 0x1000, 0x2000);
+ MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 943b3c9..a66685a 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -118,6 +118,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
// Read the entire file.
std::vector<uint8_t> buffer(1024);
@@ -129,6 +130,44 @@
}
ASSERT_FALSE(memory->ReadFully(1024, buffer.data(), 1));
+
+ // Now verify the elf start offset is set correctly based on the previous
+ // info.
+ MapInfo prev_info(nullptr, 0, 0x100, 0x10, 0, "");
+ info.prev_map = &prev_info;
+
+ // No preconditions met, change each one until it should set the elf start
+ // offset to zero.
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
+
+ prev_info.offset = 0;
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
+
+ prev_info.flags = PROT_READ;
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
+
+ prev_info.name = info.name;
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0U, info.elf_start_offset);
}
// Verify that if the offset is non-zero and there is an elf at that
@@ -139,6 +178,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
+ EXPECT_EQ(0U, info.elf_start_offset);
// Read the valid part of the file.
std::vector<uint8_t> buffer(0x100);
@@ -162,6 +202,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
+ EXPECT_EQ(0U, info.elf_start_offset);
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
@@ -178,6 +219,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
+ EXPECT_EQ(0U, info.elf_start_offset);
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
@@ -250,6 +292,7 @@
ASSERT_TRUE(mem.get() != nullptr);
EXPECT_EQ(0x4000UL, map_info->elf_offset);
EXPECT_EQ(0x4000UL, map_info->offset);
+ EXPECT_EQ(0U, map_info->elf_start_offset);
// Verify that reading values from this memory works properly.
std::vector<uint8_t> buffer(0x4000);
@@ -295,6 +338,7 @@
ASSERT_TRUE(mem.get() != nullptr);
EXPECT_EQ(0x1000UL, map_info->elf_offset);
EXPECT_EQ(0xb000UL, map_info->offset);
+ EXPECT_EQ(0xa000UL, map_info->elf_start_offset);
// Verify that reading values from this memory works properly.
std::vector<uint8_t> buffer(0x4000);
@@ -333,6 +377,7 @@
std::vector<uint8_t> buffer(0x100);
EXPECT_EQ(0x2000U, map_info->offset);
EXPECT_EQ(0U, map_info->elf_offset);
+ EXPECT_EQ(0U, map_info->elf_start_offset);
ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
EXPECT_EQ(0xffU, buffer[0]);
@@ -346,6 +391,7 @@
memory.reset(map_info->CreateMemory(process_memory_));
EXPECT_EQ(0x2000U, map_info->offset);
EXPECT_EQ(0x1000U, map_info->elf_offset);
+ EXPECT_EQ(0x1000U, map_info->elf_start_offset);
Elf64_Ehdr ehdr_mem;
ASSERT_TRUE(memory->ReadFully(0, &ehdr_mem, sizeof(ehdr_mem)));
EXPECT_TRUE(memcmp(&ehdr, &ehdr_mem, sizeof(ehdr)) == 0);
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 80e292a..b4197f2 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -62,7 +62,7 @@
}
TEST(MapsTest, verify_parse_line) {
- MapInfo info(nullptr);
+ MapInfo info(nullptr, 0, 0, 0, 0, "");
VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
EXPECT_EQ(1U, info.start);
@@ -135,7 +135,7 @@
}
TEST(MapsTest, verify_large_values) {
- MapInfo info(nullptr);
+ MapInfo info(nullptr, 0, 0, 0, 0, "");
#if defined(__LP64__)
VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 00264c2..472d1cf 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
RegsX86_64 regs_x86_64;
RegsMips regs_mips;
RegsMips64 regs_mips64;
- MapInfo map_info(nullptr, 0x1000, 0x2000);
+ MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
Elf* invalid_elf = new Elf(nullptr);
map_info.elf.reset(invalid_elf);
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index dc015b4..aab9ec2 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -298,7 +298,7 @@
EXPECT_EQ(
" #00 pc 00068fb8 libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n"
" #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
- " #02 pc 000021a8 (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+ " #02 pc 000021a8 137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
"boolean)+136)\n"
" #03 pc 0000fe80 anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
" #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
@@ -591,7 +591,7 @@
ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)\n"
- " #01 pc 0000212d (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+ " #01 pc 0000212d 137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
"boolean)+92)\n"
" #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
" #03 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
@@ -1135,29 +1135,29 @@
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
- " #00 pc 0032bfa0 (offset 0x42000) libunwindstack_test (SignalInnerFunction+40)\n"
- " #01 pc 0032bfeb (offset 0x42000) libunwindstack_test (SignalMiddleFunction+2)\n"
- " #02 pc 0032bff3 (offset 0x42000) libunwindstack_test (SignalOuterFunction+2)\n"
- " #03 pc 0032fed3 (offset 0x42000) libunwindstack_test "
+ " #00 pc 0032bfa0 libunwindstack_test (SignalInnerFunction+40)\n"
+ " #01 pc 0032bfeb libunwindstack_test (SignalMiddleFunction+2)\n"
+ " #02 pc 0032bff3 libunwindstack_test (SignalOuterFunction+2)\n"
+ " #03 pc 0032fed3 libunwindstack_test "
"(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n"
- " #04 pc 00026528 (offset 0x25000) libc.so\n"
+ " #04 pc 00026528 libc.so\n"
" #05 pc 00000000 <unknown>\n"
- " #06 pc 0032c2d9 (offset 0x42000) libunwindstack_test (InnerFunction+736)\n"
- " #07 pc 0032cc4f (offset 0x42000) libunwindstack_test (MiddleFunction+42)\n"
- " #08 pc 0032cc81 (offset 0x42000) libunwindstack_test (OuterFunction+42)\n"
- " #09 pc 0032e547 (offset 0x42000) libunwindstack_test "
+ " #06 pc 0032c2d9 libunwindstack_test (InnerFunction+736)\n"
+ " #07 pc 0032cc4f libunwindstack_test (MiddleFunction+42)\n"
+ " #08 pc 0032cc81 libunwindstack_test (OuterFunction+42)\n"
+ " #09 pc 0032e547 libunwindstack_test "
"(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n"
- " #10 pc 0032ed99 (offset 0x42000) libunwindstack_test "
+ " #10 pc 0032ed99 libunwindstack_test "
"(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n"
- " #11 pc 00354453 (offset 0x42000) libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
- " #12 pc 00354de7 (offset 0x42000) libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
- " #13 pc 00355105 (offset 0x42000) libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
- " #14 pc 0035a215 (offset 0x42000) libunwindstack_test "
+ " #11 pc 00354453 libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
+ " #12 pc 00354de7 libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
+ " #13 pc 00355105 libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
+ " #14 pc 0035a215 libunwindstack_test "
"(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n"
- " #15 pc 00359f4f (offset 0x42000) libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
- " #16 pc 0034d3db (offset 0x42000) libunwindstack_test (main+38)\n"
- " #17 pc 00092c0d (offset 0x25000) libc.so (__libc_init+48)\n"
- " #18 pc 0004202f (offset 0x42000) libunwindstack_test (_start_main+38)\n",
+ " #15 pc 00359f4f libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
+ " #16 pc 0034d3db libunwindstack_test (main+38)\n"
+ " #17 pc 00092c0d libc.so (__libc_init+48)\n"
+ " #18 pc 0004202f libunwindstack_test (_start_main+38)\n",
frame_info);
EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
@@ -1248,14 +1248,14 @@
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
- " #00 pc 000000000014ccbc (offset 0x39000) linker64 (__dl_syscall+28)\n"
- " #01 pc 000000000005426c (offset 0x39000) linker64 "
+ " #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
+ " #01 pc 000000000005426c linker64 "
"(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
" #02 pc 00000000000008bc vdso.so\n"
- " #03 pc 00000000000846f4 (offset 0x40000) libc.so (abort+172)\n"
- " #04 pc 0000000000084ad4 (offset 0x40000) libc.so (__assert2+36)\n"
- " #05 pc 000000000003d5b4 (offset 0x40000) ANGLEPrebuilt.apk (ANGLEGetUtilityAPI+56)\n"
- " #06 pc 000000000007fe68 (offset 0x40000) libc.so (__libc_init)\n",
+ " #03 pc 00000000000846f4 libc.so (abort+172)\n"
+ " #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
+ " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x4000) (ANGLEGetUtilityAPI+56)\n"
+ " #06 pc 000000000007fe68 libc.so (__libc_init)\n",
frame_info);
EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
@@ -1287,14 +1287,14 @@
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
- " #00 pc 000000000014ccbc (offset 0x39000) linker64 (__dl_syscall+28)\n"
- " #01 pc 000000000005426c (offset 0x39000) linker64 "
+ " #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
+ " #01 pc 000000000005426c linker64 "
"(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
" #02 pc 00000000000008bc vdso.so\n"
- " #03 pc 00000000000846f4 (offset 0x40000) libc.so (abort+172)\n"
- " #04 pc 0000000000084ad4 (offset 0x40000) libc.so (__assert2+36)\n"
- " #05 pc 000000000003d5b4 (offset 0x2211000) ANGLEPrebuilt.apk\n"
- " #06 pc 000000000007fe68 (offset 0x40000) libc.so (__libc_init)\n",
+ " #03 pc 00000000000846f4 libc.so (abort+172)\n"
+ " #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
+ " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x21d5000)\n"
+ " #06 pc 000000000007fe68 libc.so (__libc_init)\n",
frame_info);
EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index c4b8763..1fdeee5 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -42,84 +42,64 @@
namespace unwindstack {
-class MapsFake : public Maps {
- public:
- MapsFake() = default;
- virtual ~MapsFake() = default;
-
- bool Parse() { return true; }
-
- void FakeClear() { maps_.clear(); }
-
- void FakeAddMapInfo(MapInfo* map_info) { maps_.push_back(map_info); }
-};
-
class UnwinderTest : public ::testing::Test {
protected:
+ static void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const char* name, Elf* elf = nullptr) {
+ std::string str_name(name);
+ maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
+ if (elf != nullptr) {
+ MapInfo* map_info = *--maps_->end();
+ map_info->elf.reset(elf);
+ }
+ }
+
static void SetUpTestCase() {
- maps_.FakeClear();
- MapInfo* info =
- new MapInfo(&maps_, 0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
+ maps_.reset(new Maps);
+
ElfFake* elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf);
- info = new MapInfo(&maps_, 0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
- info = new MapInfo(&maps_, 0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
- "/dev/fake_device");
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+ "/dev/fake_device");
- info = new MapInfo(&maps_, 0x20000, 0x22000, 0, PROT_READ | PROT_WRITE,
- "/system/fake/libunwind.so");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so", elf);
- info = new MapInfo(&maps_, 0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so", elf);
- info = new MapInfo(&maps_, 0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
- info = new MapInfo(&maps_, 0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
- info = new MapInfo(&maps_, 0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
- info = new MapInfo(&maps_, 0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
- "/fake/fake.vdex");
+ AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+ MapInfo* info = *--maps_->end();
info->load_bias = 0;
- maps_.FakeAddMapInfo(info);
- info = new MapInfo(&maps_, 0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
- "/fake/fake_load_bias.so");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
elf->FakeSetLoadBias(0x5000);
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_load_bias.so",
+ elf);
- info = new MapInfo(&maps_, 0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
- "/fake/fake_offset.oat");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
+ elf);
+ info = *--maps_->end();
info->elf_offset = 0x8000;
- maps_.FakeAddMapInfo(info);
process_memory_.reset(new MemoryFake);
}
@@ -130,12 +110,12 @@
regs_.FakeSetReturnAddressValid(false);
}
- static MapsFake maps_;
+ static std::unique_ptr<Maps> maps_;
static RegsFake regs_;
static std::shared_ptr<Memory> process_memory_;
};
-MapsFake UnwinderTest::maps_;
+std::unique_ptr<Maps> UnwinderTest::maps_;
RegsFake UnwinderTest::regs_(5);
std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
@@ -150,7 +130,7 @@
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -164,7 +144,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -178,7 +159,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -192,7 +174,8 @@
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -210,7 +193,7 @@
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.SetResolveNames(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -225,7 +208,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -239,7 +223,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -253,7 +238,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -267,7 +253,7 @@
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -281,7 +267,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake_load_bias.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa5000U, frame->map_start);
EXPECT_EQ(0xa6000U, frame->map_end);
EXPECT_EQ(0x5000U, frame->map_load_bias);
@@ -295,7 +282,7 @@
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -309,7 +296,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake_offset.oat", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa7000U, frame->map_start);
EXPECT_EQ(0xa8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -323,7 +311,7 @@
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -337,7 +325,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.apk", frame->map_name);
- EXPECT_EQ(0x1d000U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -358,7 +347,7 @@
ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -372,7 +361,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -389,7 +379,7 @@
regs_.set_pc(0x1000);
regs_.set_sp(0x10000);
- Unwinder unwinder(20, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(20, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
@@ -404,7 +394,8 @@
EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
- EXPECT_EQ(0U, frame->map_offset) << "Failed at frame " << i;
+ EXPECT_EQ(0U, frame->map_elf_start_offset) << "Failed at frame " << i;
+ EXPECT_EQ(0U, frame->map_exact_offset) << "Failed at frame " << i;
EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
@@ -429,7 +420,7 @@
ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
unwinder.Unwind(&skip_libs);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -444,7 +435,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -458,7 +450,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x20000U, frame->map_start);
EXPECT_EQ(0x22000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -472,7 +465,7 @@
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
EXPECT_EQ("/fake/libanother.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x23000U, frame->map_start);
EXPECT_EQ(0x24000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -489,7 +482,7 @@
ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -503,7 +496,7 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -517,7 +510,7 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x20000U, frame->map_start);
EXPECT_EQ(0x22000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -536,7 +529,7 @@
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -555,7 +548,7 @@
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -569,7 +562,7 @@
regs_.set_pc(0x41000);
regs_.set_sp(0x13000);
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
@@ -583,7 +576,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -604,7 +598,7 @@
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -618,7 +612,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -632,7 +627,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -646,7 +642,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/libanother.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x23000U, frame->map_start);
EXPECT_EQ(0x24000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -666,7 +663,7 @@
regs_.FakeSetReturnAddress(0x12);
regs_.FakeSetReturnAddressValid(true);
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
@@ -680,7 +677,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x20000U, frame->map_start);
EXPECT_EQ(0x22000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -694,7 +692,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -713,7 +712,7 @@
regs_.FakeSetReturnAddress(0x1202);
regs_.FakeSetReturnAddressValid(true);
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -727,7 +726,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -741,7 +741,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -762,14 +763,14 @@
ElfInterfaceFake::FakePushStepData(StepData(0x53502, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
std::vector<std::string> suffixes{"oat"};
unwinder.Unwind(nullptr, &suffixes);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
ASSERT_EQ(2U, unwinder.NumFrames());
// Make sure the elf was not initialized.
- MapInfo* map_info = maps_.Find(0x53000);
+ MapInfo* map_info = maps_->Find(0x53000);
ASSERT_TRUE(map_info != nullptr);
EXPECT_TRUE(map_info->elf == nullptr);
@@ -781,7 +782,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -795,7 +797,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/fake.apk", frame->map_name);
- EXPECT_EQ(0x1d000U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -819,7 +822,7 @@
ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
@@ -833,7 +836,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -847,7 +851,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/compressed.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x33000U, frame->map_start);
EXPECT_EQ(0x34000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -861,7 +866,8 @@
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
EXPECT_EQ("/fake/compressed.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x33000U, frame->map_start);
EXPECT_EQ(0x34000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -874,7 +880,7 @@
regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0xa3400);
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -888,7 +894,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.vdex", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa3000U, frame->map_start);
EXPECT_EQ(0xa4000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -902,7 +909,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -915,7 +923,7 @@
regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0x50000);
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -929,7 +937,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -943,7 +952,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -959,7 +969,7 @@
ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
@@ -973,7 +983,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.vdex", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa3000U, frame->map_start);
EXPECT_EQ(0xa4000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -987,7 +998,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -1001,7 +1013,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/compressed.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x33000U, frame->map_start);
EXPECT_EQ(0x34000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -1014,7 +1027,7 @@
regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0xa3400);
- Unwinder unwinder(1, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(1, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
@@ -1028,7 +1041,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.vdex", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa3000U, frame->map_start);
EXPECT_EQ(0xa4000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -1045,17 +1059,17 @@
frame.function_name = "function";
frame.function_offset = 100;
frame.map_name = "/fake/libfake.so";
- frame.map_offset = 0x2000;
+ frame.map_elf_start_offset = 0x2000;
frame.map_start = 0x3000;
frame.map_end = 0x6000;
frame.map_flags = PROT_READ;
- EXPECT_EQ(" #01 pc 0000000000001000 (offset 0x2000) /fake/libfake.so (function+100)",
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (offset 0x2000) (function+100)",
Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 (offset 0x2000) /fake/libfake.so (function+100)",
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (offset 0x2000) (function+100)",
Unwinder::FormatFrame(frame, true));
- frame.map_offset = 0;
+ frame.map_elf_start_offset = 0;
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function+100)",
Unwinder::FormatFrame(frame, false));
EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)",
@@ -1130,7 +1144,7 @@
for (auto regs : reg_list) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
- Unwinder unwinder(64, &maps_, regs, process_memory_);
+ Unwinder unwinder(64, maps_.get(), regs, process_memory_);
unwinder.Unwind();
ASSERT_EQ(1U, unwinder.NumFrames());
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt
index 6224464..768dd9f 100644
--- a/libunwindstack/tests/files/offline/offset_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt
@@ -1,2 +1,4 @@
+2b2a000-2b6c000 r--p 0 00:00 0 libunwindstack_test
2b6c000-2e92000 r-xp 42000 00:00 0 libunwindstack_test
+f4110000-f4135000 r--p 0 00:00 0 libc.so
f4135000-f41a9000 r-xp 25000 00:00 0 libc.so
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 652dbd1..5ae8874 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <unistd.h>
@@ -46,6 +47,7 @@
uint64_t start;
uint64_t end;
uint64_t offset;
+ uint64_t flags;
std::string name;
};
@@ -163,14 +165,19 @@
return true;
}
-bool CopyElfFromFile(map_info_t* info) {
+bool CopyElfFromFile(map_info_t* info, bool* file_copied) {
+ std::string cur_name = basename(info->name.c_str());
+ if (*file_copied) {
+ info->name = cur_name;
+ return true;
+ }
+
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(info->name.c_str(), "r"), &fclose);
if (fp == nullptr) {
perror((std::string("Cannot open ") + info->name).c_str());
return false;
}
- std::string cur_name = basename(info->name.c_str());
std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
if (output == nullptr) {
perror((std::string("Cannot create file " + cur_name)).c_str());
@@ -193,6 +200,39 @@
return true;
}
+map_info_t* FillInAndGetMapInfo(std::unordered_map<uint64_t, map_info_t>& maps_by_start,
+ unwindstack::MapInfo* map_info) {
+ auto info = &maps_by_start[map_info->start];
+ info->start = map_info->start;
+ info->end = map_info->end;
+ info->offset = map_info->offset;
+ info->name = map_info->name;
+ info->flags = map_info->flags;
+
+ return info;
+}
+
+void SaveMapInformation(std::shared_ptr<unwindstack::Memory>& process_memory, map_info_t* info,
+ bool* file_copied) {
+ if (CopyElfFromFile(info, file_copied)) {
+ return;
+ }
+ *file_copied = false;
+
+ // Try to create the elf from memory, this will handle cases where
+ // the data only exists in memory such as vdso data on x86.
+ if (CreateElfFromMemory(process_memory, info)) {
+ return;
+ }
+
+ printf("Cannot save memory or file for map ");
+ if (!info->name.empty()) {
+ printf("%s\n", info->name.c_str());
+ } else {
+ printf("anonymous:%" PRIx64 "\n", info->start);
+ }
+}
+
int SaveData(pid_t pid) {
unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
if (regs == nullptr) {
@@ -237,22 +277,21 @@
}
if (maps_by_start.count(frame.map_start) == 0) {
- auto info = &maps_by_start[frame.map_start];
- info->start = frame.map_start;
- info->end = frame.map_end;
- info->offset = frame.map_offset;
- info->name = frame.map_name;
- if (!CopyElfFromFile(info)) {
- // Try to create the elf from memory, this will handle cases where
- // the data only exists in memory such as vdso data on x86.
- if (!CreateElfFromMemory(process_memory, info)) {
- printf("Ignoring map ");
- if (!info->name.empty()) {
- printf("%s\n", info->name.c_str());
- } else {
- printf("anonymous:%" PRIx64 "\n", info->start);
- }
- }
+ map_info = maps.Find(frame.map_start);
+
+ auto info = FillInAndGetMapInfo(maps_by_start, map_info);
+ bool file_copied = false;
+ SaveMapInformation(process_memory, info, &file_copied);
+
+ // If you are using a a linker that creates two maps (one read-only, one
+ // read-executable), it's necessary to capture the previous map
+ // information if needed.
+ unwindstack::MapInfo* prev_map = map_info->prev_map;
+ if (prev_map != nullptr && map_info->offset != 0 && prev_map->offset == 0 &&
+ prev_map->flags == PROT_READ && map_info->name == prev_map->name &&
+ maps_by_start.count(prev_map->start) == 0) {
+ info = FillInAndGetMapInfo(maps_by_start, prev_map);
+ SaveMapInformation(process_memory, info, &file_copied);
}
}
}
@@ -277,8 +316,18 @@
}
for (auto& element : sorted_maps) {
+ char perms[5] = {"---p"};
map_info_t& map = element.second;
- fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " r-xp %" PRIx64 " 00:00 0", map.start, map.end,
+ if (map.flags & PROT_READ) {
+ perms[0] = 'r';
+ }
+ if (map.flags & PROT_WRITE) {
+ perms[1] = 'w';
+ }
+ if (map.flags & PROT_EXEC) {
+ perms[2] = 'x';
+ }
+ fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map.start, map.end, perms,
map.offset);
if (!map.name.empty()) {
fprintf(fp.get(), " %s", map.name.c_str());
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 102fdf0..b3f943d 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -51,9 +51,6 @@
// --- Looper ---
-// Hint for number of file descriptors to be associated with the epoll instance.
-static const int EPOLL_SIZE_HINT = 8;
-
// Maximum number of file descriptors for which to retrieve poll events each iteration.
static const int EPOLL_MAX_EVENTS = 16;
@@ -139,7 +136,7 @@
}
// Allocate the new epoll instance and register the wake pipe.
- mEpollFd.reset(epoll_create(EPOLL_SIZE_HINT));
+ mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
struct epoll_event eventItem;
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 56004fe..7a4a345 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -123,13 +123,14 @@
if (reinterpret_cast<uintptr_t>(logStackInternal) != 0 && stack != nullptr) {
logStackInternal(logtag, stack, priority);
} else {
- ALOGW("CallStack::logStackInternal not linked");
+ ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
}
}
#else
- static void ALWAYS_INLINE logStack(const char*, CallStack* = getCurrent().get(),
+ static void ALWAYS_INLINE logStack(const char* logtag, CallStack* = getCurrent().get(),
android_LogPriority = ANDROID_LOG_DEBUG) {
+ ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
}
#endif // !WEAKS_AVAILABLE
@@ -139,13 +140,13 @@
if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
return stackToStringInternal(prefix, stack);
} else {
- return String8("<CallStack package not linked>");
+ return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
}
}
#else // !WEAKS_AVAILABLE
- static String8 ALWAYS_INLINE stackToString(const char* = nullptr,
+ static String8 ALWAYS_INLINE stackToString(const char* prefix = nullptr,
const CallStack* = getCurrent().get()) {
- return String8("<CallStack package not linked>");
+ return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
}
#endif // !WEAKS_AVAILABLE
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 9eb7f2c..6b9f6e1 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -491,7 +491,7 @@
}
int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
- const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY, 0);
+ const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
ZipArchive* archive = new ZipArchive(fd, true);
*handle = archive;
diff --git a/llkd/README.md b/llkd/README.md
index 3da7a2f..7cc7e1f 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -135,8 +135,9 @@
default 2 minutes samples of threads for D or Z.
#### ro.llk.stack
-default cma_alloc,__get_user_pages,bit_wait_io comma separated list of kernel
-symbols. The string "*false*" is the equivalent to an *empty* list.
+default cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
+comma separated list of kernel symbols.
+The string "*false*" is the equivalent to an *empty* list.
Look for kernel stack symbols that if ever persistently present can
indicate a subsystem is locked up.
Beware, check does not on purpose do forward scheduling ABA except by polling
@@ -160,7 +161,7 @@
NB: false is a very very very unlikely process to want to blacklist.
#### ro.llk.blacklist.parent
-default 0,2 (kernel and [kthreadd]).
+default 0,2,adbd (kernel, [kthreadd] and adbd).
The string "*false*" is the equivalent to an *empty* list.
Do not watch processes that have this parent.
A parent process can be comm, cmdline or pid reference.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index b16b1d8..7b7dbf9 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -50,12 +50,17 @@
/* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
#define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
#define LLK_CHECK_STACK_PROPERTY "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT "cma_alloc,__get_user_pages,bit_wait_io"
+#define LLK_CHECK_STACK_DEFAULT \
+ "cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable"
#define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
#define LLK_BLACKLIST_PROCESS_DEFAULT \
"0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
#define LLK_BLACKLIST_PARENT_PROPERTY "ro.llk.blacklist.parent"
+#ifdef __PTRACE_ENABLED__ // defined if userdebug build
+#define LLK_BLACKLIST_PARENT_DEFAULT "0,2,[kthreadd],adbd"
+#else
#define LLK_BLACKLIST_PARENT_DEFAULT "0,2,[kthreadd]"
+#endif
#define LLK_BLACKLIST_UID_PROPERTY "ro.llk.blacklist.uid"
#define LLK_BLACKLIST_UID_DEFAULT ""
#define LLK_BLACKLIST_STACK_PROPERTY "ro.llk.blacklist.process.stack"
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 267da4a..7f47fc9 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -737,19 +737,21 @@
auto kernel_stack = ReadFile(piddir + "/stack");
if (kernel_stack.empty()) {
- LOG(INFO) << piddir << "/stack empty comm=" << procp->getComm()
- << " cmdline=" << procp->getCmdline();
+ LOG(VERBOSE) << piddir << "/stack empty comm=" << procp->getComm()
+ << " cmdline=" << procp->getCmdline();
return false;
}
// A scheduling incident that should not reset count_stack
if (kernel_stack.find(" cpu_worker_pools+0x") != std::string::npos) return false;
char idx = -1;
char match = -1;
+ std::string matched_stack_symbol = "<unknown>";
for (const auto& stack : llkCheckStackSymbols) {
if (++idx < 0) break;
if ((kernel_stack.find(" "s + stack + "+0x") != std::string::npos) ||
(kernel_stack.find(" "s + stack + ".cfi+0x") != std::string::npos)) {
match = idx;
+ matched_stack_symbol = stack;
break;
}
}
@@ -760,7 +762,9 @@
}
if (match == char(-1)) return false;
procp->count_stack += llkCycle;
- return procp->count_stack >= llkStateTimeoutMs[llkStateStack];
+ if (procp->count_stack < llkStateTimeoutMs[llkStateStack]) return false;
+ LOG(WARNING) << "Found " << matched_stack_symbol << " in stack for pid " << procp->pid;
+ return true;
}
#endif
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 19e6d68..2fd9f95 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -55,8 +55,19 @@
mMsgLen(elem.mMsgLen),
mLogId(elem.mLogId),
mDropped(elem.mDropped) {
- mMsg = new char[mMsgLen];
- memcpy(mMsg, elem.mMsg, mMsgLen);
+ if (mDropped) {
+ if (elem.isBinary() && elem.mMsg != nullptr) {
+ // for the following "len" value, refer to : setDropped(uint16_t value), getTag()
+ const int len = sizeof(android_event_header_t);
+ mMsg = new char[len];
+ memcpy(mMsg, elem.mMsg, len);
+ } else {
+ mMsg = nullptr;
+ }
+ } else {
+ mMsg = new char[mMsgLen];
+ memcpy(mMsg, elem.mMsg, mMsgLen);
+ }
}
LogBufferElement::~LogBufferElement() {
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index f4e165f..9f6f203 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -18,6 +18,7 @@
#define _LOGD_LOG_TIMES_H__
#include <pthread.h>
+#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
@@ -82,6 +83,8 @@
void cleanSkip_Locked(void);
void release_Locked(void) {
+ // gracefully shut down the socket.
+ shutdown(mClient->getSocket(), SHUT_RDWR);
mRelease = true;
pthread_cond_signal(&threadTriggeredCondition);
}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 264c612..49ae960 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -73,6 +73,7 @@
namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.permitted.paths += /data
namespace.default.permitted.paths += /mnt/expand
+namespace.default.permitted.paths += /apex/com.android.resolv/${LIB}
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
@@ -104,6 +105,7 @@
namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.asan.permitted.paths += /mnt/expand
+namespace.default.asan.permitted.paths += /apex/com.android.resolv/${LIB}
# Keep in sync with ld.config.txt in the com.android.runtime APEX.
namespace.default.links = runtime
@@ -332,7 +334,9 @@
###############################################################################
namespace.runtime.isolated = true
namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
+namespace.runtime.links = system,default
+namespace.runtime.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.runtime.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
# TODO(b/119867084): Restrict to Bionic dlopen dependencies.
namespace.runtime.link.default.allow_all_shared_libs = true
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6fb1a8b..483fc51 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -545,6 +545,7 @@
mkdir /data/anr 0775 system system
mkdir /data/apex 0770 root root
+ mkdir /data/staging 0750 system system
# NFC: create data/nfc for nv storage
mkdir /data/nfc 0770 nfc nfc
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index a9658a4..35f469a 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,9 +1,6 @@
firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
uevent_socket_rcvbuf_size 16M
-subsystem adf
- devname uevent_devname
-
subsystem graphics
devname uevent_devpath
dirname /dev/graphics
@@ -12,26 +9,10 @@
devname uevent_devpath
dirname /dev/dri
-subsystem oncrpc
- devname uevent_devpath
- dirname /dev/oncrpc
-
-subsystem adsp
- devname uevent_devpath
- dirname /dev/adsp
-
-subsystem msm_camera
- devname uevent_devpath
- dirname /dev/msm_camera
-
subsystem input
devname uevent_devpath
dirname /dev/input
-subsystem mtd
- devname uevent_devpath
- dirname /dev/mtd
-
subsystem sound
devname uevent_devpath
dirname /dev/snd
@@ -59,73 +40,27 @@
/dev/pmsg0 0222 root log
-# the msm hw3d client device node is world writable/readable.
-/dev/msm_hw3dc 0666 root root
-
-# gpu driver for adreno200 is globally accessible
-/dev/kgsl 0666 root root
-
# kms driver for drm based gpu
/dev/dri/* 0666 root graphics
# these should not be world writable
/dev/diag 0660 radio radio
-/dev/diag_arm9 0660 radio radio
/dev/ttyMSM0 0600 bluetooth bluetooth
/dev/uhid 0660 uhid uhid
/dev/uinput 0660 uhid uhid
-/dev/alarm 0664 system radio
/dev/rtc0 0640 system system
/dev/tty0 0660 root system
/dev/graphics/* 0660 root graphics
-/dev/msm_hw3dm 0660 system graphics
/dev/input/* 0660 root input
/dev/v4l-touch* 0660 root input
-/dev/eac 0660 root audio
-/dev/cam 0660 root camera
-/dev/pmem 0660 system graphics
-/dev/pmem_adsp* 0660 system audio
-/dev/pmem_camera* 0660 system camera
-/dev/oncrpc/* 0660 root system
-/dev/adsp/* 0660 system audio
/dev/snd/* 0660 system audio
-/dev/mt9t013 0660 system system
-/dev/msm_camera/* 0660 system system
-/dev/akm8976_daemon 0640 compass system
-/dev/akm8976_aot 0640 compass system
-/dev/akm8973_daemon 0640 compass system
-/dev/akm8973_aot 0640 compass system
-/dev/bma150 0640 compass system
-/dev/cm3602 0640 compass system
-/dev/akm8976_pffd 0640 compass system
-/dev/lightsensor 0640 system system
-/dev/msm_pcm_out* 0660 system audio
-/dev/msm_pcm_in* 0660 system audio
-/dev/msm_pcm_ctl* 0660 system audio
-/dev/msm_snd* 0660 system audio
/dev/msm_mp3* 0660 system audio
-/dev/audience_a1026* 0660 system audio
-/dev/tpa2018d1* 0660 system audio
-/dev/msm_audpre 0660 system audio
-/dev/msm_audio_ctl 0660 system audio
-/dev/htc-acoustic 0660 system audio
-/dev/vdec 0660 system audio
-/dev/q6venc 0660 system audio
-/dev/snd/dsp 0660 system audio
-/dev/snd/dsp1 0660 system audio
-/dev/snd/mixer 0660 system audio
-/dev/smd0 0640 radio radio
-/dev/qmi 0640 radio radio
-/dev/qmi0 0640 radio radio
-/dev/qmi1 0640 radio radio
-/dev/qmi2 0640 radio radio
/dev/bus/usb/* 0660 root usb
/dev/mtp_usb 0660 root mtp
/dev/usb_accessory 0660 root usb
/dev/tun 0660 system vpn
# CDMA radio interface MUX
-/dev/ts0710mux* 0640 radio radio
/dev/ppp 0660 radio vpn
# sysfs properties
@@ -135,6 +70,3 @@
/sys/devices/virtual/usb_composite/* enable 0664 root system
/sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
/sys/devices/system/cpu/cpu* cpufreq/scaling_min_freq 0664 system system
-
-# DVB API device nodes
-/dev/dvb* 0660 root system
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 56a30b2..79bed7b 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -38,9 +38,11 @@
sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
$(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+ $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
$(UBSAN_RUNTIME_LIBRARY) \
$(TSAN_RUNTIME_LIBRARY) \
$(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+ $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
$(2ND_UBSAN_RUNTIME_LIBRARY) \
$(2ND_TSAN_RUNTIME_LIBRARY)))
# If BOARD_VNDK_VERSION is not defined, VNDK version suffix will not be used.