Merge "Add Mips64 Runtime ISA type."
diff --git a/adb/.clang-format b/adb/.clang-format
index 6737535..fc4eb1b 100644
--- a/adb/.clang-format
+++ b/adb/.clang-format
@@ -2,6 +2,7 @@
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
+AccessModifierOffset: -2
ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
diff --git a/adb/Android.mk b/adb/Android.mk
index d629223..f1d3bee 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -50,6 +50,7 @@
adb_listeners.cpp \
adb_trace.cpp \
adb_utils.cpp \
+ fdevent.cpp \
sockets.cpp \
transport.cpp \
transport_local.cpp \
@@ -58,6 +59,9 @@
LIBADB_TEST_SRCS := \
adb_io_test.cpp \
adb_utils_test.cpp \
+ fdevent_test.cpp \
+ socket_test.cpp \
+ sysdeps_test.cpp \
transport_test.cpp \
LIBADB_CFLAGS := \
@@ -74,27 +78,19 @@
$(ADB_COMMON_windows_CFLAGS) \
LIBADB_darwin_SRC_FILES := \
- fdevent.cpp \
get_my_path_darwin.cpp \
+ sysdeps_unix.cpp \
usb_osx.cpp \
LIBADB_linux_SRC_FILES := \
- fdevent.cpp \
get_my_path_linux.cpp \
+ sysdeps_unix.cpp \
usb_linux.cpp \
LIBADB_windows_SRC_FILES := \
sysdeps_win32.cpp \
usb_windows.cpp \
-LIBADB_TEST_linux_SRCS := \
- fdevent_test.cpp \
- socket_test.cpp \
-
-LIBADB_TEST_darwin_SRCS := \
- fdevent_test.cpp \
- socket_test.cpp \
-
LIBADB_TEST_windows_SRCS := \
sysdeps_win32_test.cpp \
@@ -105,7 +101,6 @@
LOCAL_SRC_FILES := \
$(LIBADB_SRC_FILES) \
adb_auth_client.cpp \
- fdevent.cpp \
jdwp_service.cpp \
usb_linux_client.cpp \
@@ -157,7 +152,7 @@
LOCAL_SANITIZE := $(adb_target_sanitize)
LOCAL_STATIC_LIBRARIES := libadbd
-LOCAL_SHARED_LIBRARIES := libbase libcutils
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
include $(BUILD_NATIVE_TEST)
# libdiagnose_usb
@@ -293,7 +288,11 @@
include $(BUILD_HOST_EXECUTABLE)
-$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE))
+$(call dist-for-goals,dist_files sdk win_sdk,$(LOCAL_BUILT_MODULE))
+ifdef HOST_CROSS_OS
+# Archive adb.exe for win_sdk build.
+$(call dist-for-goals,win_sdk,$(ALL_MODULES.host_cross_adb.BUILT))
+endif
# adbd device daemon
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 58ccd0a..cb54d04 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -883,8 +883,6 @@
fprintf(stderr, "ADB server didn't ACK\n" );
return -1;
}
-
- setsid();
}
#endif /* !defined(_WIN32) */
return 0;
diff --git a/adb/adb_auth_client.cpp b/adb/adb_auth_client.cpp
index c4ffc85..128c3df 100644
--- a/adb/adb_auth_client.cpp
+++ b/adb/adb_auth_client.cpp
@@ -44,6 +44,7 @@
};
static fdevent listener_fde;
+static fdevent framework_fde;
static int framework_fd = -1;
static void usb_disconnected(void* unused, atransport* t);
@@ -161,29 +162,30 @@
return ret;
}
-static void usb_disconnected(void* unused, atransport* t)
-{
+static void usb_disconnected(void* unused, atransport* t) {
D("USB disconnect");
usb_transport = NULL;
needs_retry = false;
}
-static void adb_auth_event(int fd, unsigned events, void *data)
-{
+static void framework_disconnected() {
+ D("Framework disconnect");
+ fdevent_remove(&framework_fde);
+ framework_fd = -1;
+}
+
+static void adb_auth_event(int fd, unsigned events, void*) {
char response[2];
int ret;
if (events & FDE_READ) {
ret = unix_read(fd, response, sizeof(response));
if (ret <= 0) {
- D("Framework disconnect");
- if (usb_transport)
- fdevent_remove(&usb_transport->auth_fde);
- framework_fd = -1;
- }
- else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
- if (usb_transport)
+ framework_disconnected();
+ } else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
+ if (usb_transport) {
adb_auth_verified(usb_transport);
+ }
}
}
}
@@ -221,13 +223,9 @@
D("Failed to write PK, errno=%d", errno);
return;
}
-
- fdevent_install(&t->auth_fde, framework_fd, adb_auth_event, t);
- fdevent_add(&t->auth_fde, FDE_READ);
}
-static void adb_auth_listener(int fd, unsigned events, void *data)
-{
+static void adb_auth_listener(int fd, unsigned events, void* data) {
sockaddr_storage addr;
socklen_t alen;
int s;
@@ -240,7 +238,14 @@
return;
}
+ if (framework_fd >= 0) {
+ LOG(WARNING) << "adb received framework auth socket connection again";
+ framework_disconnected();
+ }
+
framework_fd = s;
+ fdevent_install(&framework_fde, framework_fd, adb_auth_event, nullptr);
+ fdevent_add(&framework_fde, FDE_READ);
if (needs_retry) {
needs_retry = false;
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index db9b710..d29c08e 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -50,6 +50,15 @@
__adb_serial = serial;
}
+void adb_get_transport(TransportType* type, const char** serial) {
+ if (type) {
+ *type = __adb_transport;
+ }
+ if (serial) {
+ *serial = __adb_serial;
+ }
+}
+
void adb_set_tcp_specifics(int server_port)
{
__adb_server_port = server_port;
diff --git a/adb/adb_client.h b/adb/adb_client.h
index a9df4d7..d5cd922 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -39,6 +39,9 @@
// Set the preferred transport to connect to.
void adb_set_transport(TransportType type, const char* _Nullable serial);
+// Get the preferred transport to connect to.
+void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial);
+
// Set TCP specifics of the transport to use.
void adb_set_tcp_specifics(int server_port);
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 474d1b4..3333fc6 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -134,7 +134,7 @@
return result;
}
-// Given a relative or absolute filepath, create the parent directory hierarchy
+// Given a relative or absolute filepath, create the directory hierarchy
// as needed. Returns true if the hierarchy is/was setup.
bool mkdirs(const std::string& path) {
// TODO: all the callers do unlink && mkdirs && adb_creat ---
@@ -153,16 +153,18 @@
// - Recursive, so it uses stack space relative to number of directory
// components.
- if (directory_exists(path)) {
+ // If path points to a symlink to a directory, that's fine.
+ struct stat sb;
+ if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
return true;
}
+ const std::string parent(adb_dirname(path));
+
// If dirname returned the same path as what we passed in, don't go recursive.
// This can happen on Windows when walking up the directory hierarchy and not
// finding anything that already exists (unlike POSIX that will eventually
// find . or /).
- const std::string parent(adb_dirname(path));
-
if (parent == path) {
errno = ENOENT;
return false;
@@ -174,14 +176,14 @@
}
// Now that the parent directory hierarchy of 'path' has been ensured,
- // create parent itself.
+ // create path itself.
if (adb_mkdir(path, 0775) == -1) {
- // Can't just check for errno == EEXIST because it might be a file that
- // exists.
const int saved_errno = errno;
- if (directory_exists(parent)) {
+ // If someone else created the directory, that is ok.
+ if (directory_exists(path)) {
return true;
}
+ // There might be a pre-existing file at 'path', or there might have been some other error.
errno = saved_errno;
return false;
}
@@ -213,6 +215,7 @@
}
#if !defined(_WIN32)
+// Windows version provided in sysdeps_win32.cpp
bool set_file_block_mode(int fd, bool block) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f1149b3..89fcd66 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -19,6 +19,8 @@
#include <string>
+#include <android-base/macros.h>
+
void close_stdin();
bool getcwd(std::string* cwd);
@@ -39,4 +41,45 @@
bool set_file_block_mode(int fd, bool block);
+extern int adb_close(int fd);
+
+// Helper to automatically close an FD when it goes out of scope.
+class ScopedFd {
+ public:
+ ScopedFd() {
+ }
+
+ ~ScopedFd() {
+ Reset();
+ }
+
+ void Reset(int fd = -1) {
+ if (fd != fd_) {
+ if (valid()) {
+ adb_close(fd_);
+ }
+ fd_ = fd;
+ }
+ }
+
+ int Release() {
+ int temp = fd_;
+ fd_ = -1;
+ return temp;
+ }
+
+ bool valid() const {
+ return fd_ >= 0;
+ }
+
+ int fd() const {
+ return fd_;
+ }
+
+ private:
+ int fd_ = -1;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFd);
+};
+
#endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 794dce6..f1ebaa1 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -112,20 +112,26 @@
}
void test_mkdirs(const std::string basepath) {
+ // Test creating a directory hierarchy.
EXPECT_TRUE(mkdirs(basepath));
- EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600));
- EXPECT_FALSE(mkdirs(basepath + "/subdir/"));
+ // Test finding an existing directory hierarchy.
+ EXPECT_TRUE(mkdirs(basepath));
+ const std::string filepath = basepath + "/file";
+ // Verify that the hierarchy was created by trying to create a file in it.
+ EXPECT_NE(-1, adb_creat(filepath.c_str(), 0600));
+ // If a file exists where we want a directory, the operation should fail.
+ EXPECT_FALSE(mkdirs(filepath));
}
TEST(adb_utils, mkdirs) {
TemporaryDir td;
// Absolute paths.
- test_mkdirs(std::string(td.path) + "/dir/subdir/file");
+ test_mkdirs(std::string(td.path) + "/dir/subdir");
// Relative paths.
ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
- test_mkdirs(std::string("relative/subrel/file"));
+ test_mkdirs(std::string("relative/subrel"));
}
#if !defined(_WIN32)
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index b7b30c5..27b7109 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -21,6 +21,7 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
// We only build the affinity WAR code for Linux.
#if defined(__linux__)
@@ -125,6 +126,15 @@
close_stdin();
setup_daemon_logging();
+#if !defined(_WIN32)
+ // Start a new session for the daemon. Do this here instead of after the fork so
+ // that a ctrl-c between the "starting server" and "done starting server" messages
+ // gets a chance to terminate the server.
+ if (setsid() == -1) {
+ fatal("setsid() failed: %s", strerror(errno));
+ }
+#endif
+
// Any error output written to stderr now goes to adb.log. We could
// keep around a copy of the stderr fd and use that to write any errors
// encountered by the following code, but that is probably overkill.
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 85ab4d1..30a38e3 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -482,7 +482,7 @@
// Loops to read from stdin and push the data to the given FD.
// The argument should be a pointer to a StdinReadArgs object. This function
// will take ownership of the object and delete it when finished.
-static void* stdin_read_thread_loop(void* x) {
+static void stdin_read_thread_loop(void* x) {
std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
#if !defined(_WIN32)
@@ -586,8 +586,6 @@
}
}
}
-
- return nullptr;
}
// Returns a shell service string with the indicated arguments and command.
@@ -1074,6 +1072,51 @@
return adb_command(cmd);
}
+static bool adb_root(const char* command) {
+ std::string error;
+ ScopedFd fd;
+
+ fd.Reset(adb_connect(android::base::StringPrintf("%s:", command), &error));
+ if (!fd.valid()) {
+ fprintf(stderr, "adb: unable to connect for %s: %s\n", command, error.c_str());
+ return false;
+ }
+
+ // Figure out whether we actually did anything.
+ char buf[256];
+ char* cur = buf;
+ ssize_t bytes_left = sizeof(buf);
+ while (bytes_left > 0) {
+ ssize_t bytes_read = adb_read(fd.fd(), cur, bytes_left);
+ if (bytes_read == 0) {
+ break;
+ } else if (bytes_read < 0) {
+ fprintf(stderr, "adb: error while reading for %s: %s\n", command, strerror(errno));
+ return false;
+ }
+ cur += bytes_read;
+ bytes_left -= bytes_read;
+ }
+
+ if (bytes_left == 0) {
+ fprintf(stderr, "adb: unexpected output length for %s\n", command);
+ return false;
+ }
+
+ fflush(stdout);
+ WriteFdExactly(STDOUT_FILENO, buf, sizeof(buf) - bytes_left);
+ if (cur != buf && strstr(buf, "restarting") == nullptr) {
+ return true;
+ }
+
+ // Give adbd 500ms to kill itself, then wait-for-device for it to come back up.
+ adb_sleep_ms(500);
+ TransportType type;
+ const char* serial;
+ adb_get_transport(&type, &serial);
+ return wait_for_device("wait-for-device", type, serial);
+}
+
// Connects to the device "shell" service with |command| and prints the
// resulting output.
static int send_shell_command(TransportType transport_type, const char* serial,
@@ -1108,8 +1151,9 @@
}
fprintf(stderr,"- waiting for device -\n");
- adb_sleep_ms(1000);
- wait_for_device("wait-for-device", transport_type, serial);
+ if (!wait_for_device("wait-for-device", transport_type, serial)) {
+ return 1;
+ }
}
int exit_code = read_and_dump(fd, use_shell_protocol);
@@ -1221,6 +1265,9 @@
printf("Now unlock your device and confirm the restore operation.\n");
copy_to_file(tarFd, fd);
+ // Wait until the other side finishes, or it'll get sent SIGHUP.
+ copy_to_file(fd, STDOUT_FILENO);
+
adb_close(fd);
adb_close(tarFd);
return 0;
@@ -1633,8 +1680,6 @@
!strcmp(argv[0], "reboot") ||
!strcmp(argv[0], "reboot-bootloader") ||
!strcmp(argv[0], "usb") ||
- !strcmp(argv[0], "root") ||
- !strcmp(argv[0], "unroot") ||
!strcmp(argv[0], "disable-verity") ||
!strcmp(argv[0], "enable-verity")) {
std::string command;
@@ -1646,8 +1691,9 @@
command = android::base::StringPrintf("%s:", argv[0]);
}
return adb_connect_command(command);
- }
- else if (!strcmp(argv[0], "bugreport")) {
+ } else if (!strcmp(argv[0], "root") || !strcmp(argv[0], "unroot")) {
+ return adb_root(argv[0]) ? 0 : 1;
+ } else if (!strcmp(argv[0], "bugreport")) {
if (argc != 1) return usage();
// No need for shell protocol with bugreport, always disable for
// simplicity.
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 4721e2f..7f40b96 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -43,24 +43,15 @@
static const char* root_seclabel = nullptr;
-static void drop_capabilities_bounding_set_if_needed() {
-#ifdef ALLOW_ADBD_ROOT
+static void drop_capabilities_bounding_set_if_needed(struct minijail *j) {
+#if defined(ALLOW_ADBD_ROOT)
char value[PROPERTY_VALUE_MAX];
property_get("ro.debuggable", value, "");
if (strcmp(value, "1") == 0) {
return;
}
#endif
- for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
- if (i == CAP_SETUID || i == CAP_SETGID) {
- // CAP_SETUID CAP_SETGID needed by /system/bin/run-as
- continue;
- }
-
- if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) {
- PLOG(FATAL) << "Could not drop capabilities";
- }
- }
+ minijail_capbset_drop(j, CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
}
static bool should_drop_privileges() {
@@ -131,7 +122,7 @@
// Don't listen on a port (default 5037) if running in secure mode.
// Don't run as root if running in secure mode.
if (should_drop_privileges()) {
- drop_capabilities_bounding_set_if_needed();
+ drop_capabilities_bounding_set_if_needed(jail.get());
minijail_change_gid(jail.get(), AID_SHELL);
minijail_change_uid(jail.get(), AID_SHELL);
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 386f221..902548e 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -21,12 +21,11 @@
#include "fdevent.h"
#include <fcntl.h>
-#include <poll.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ioctl.h>
#include <unistd.h>
+#include <atomic>
#include <list>
#include <unordered_map>
#include <vector>
@@ -54,7 +53,7 @@
struct PollNode {
fdevent* fde;
- ::pollfd pollfd;
+ adb_pollfd pollfd;
PollNode(fdevent* fde) : fde(fde) {
memset(&pollfd, 0, sizeof(pollfd));
@@ -72,18 +71,19 @@
// That's why we don't need a lock for fdevent.
static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
static auto& g_pending_list = *new std::list<fdevent*>();
+static std::atomic<bool> terminate_loop(false);
static bool main_thread_valid;
-static pthread_t main_thread;
+static unsigned long main_thread_id;
static void check_main_thread() {
if (main_thread_valid) {
- CHECK_NE(0, pthread_equal(main_thread, pthread_self()));
+ CHECK_EQ(main_thread_id, adb_thread_id());
}
}
static void set_main_thread() {
main_thread_valid = true;
- main_thread = pthread_self();
+ main_thread_id = adb_thread_id();
}
static std::string dump_fde(const fdevent* fde) {
@@ -217,7 +217,7 @@
fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
}
-static std::string dump_pollfds(const std::vector<pollfd>& pollfds) {
+static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
std::string result;
for (const auto& pollfd : pollfds) {
std::string op;
@@ -233,13 +233,13 @@
}
static void fdevent_process() {
- std::vector<pollfd> pollfds;
+ std::vector<adb_pollfd> pollfds;
for (const auto& pair : g_poll_node_map) {
pollfds.push_back(pair.second.pollfd);
}
CHECK_GT(pollfds.size(), 0u);
D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
- int ret = TEMP_FAILURE_RETRY(poll(&pollfds[0], pollfds.size(), -1));
+ int ret = adb_poll(&pollfds[0], pollfds.size(), -1);
if (ret == -1) {
PLOG(ERROR) << "poll(), ret = " << ret;
return;
@@ -289,6 +289,9 @@
}
#if !ADB_HOST
+
+#include <sys/ioctl.h>
+
static void fdevent_subproc_event_func(int fd, unsigned ev,
void* /* userdata */)
{
@@ -363,6 +366,10 @@
#endif // !ADB_HOST
while (true) {
+ if (terminate_loop) {
+ return;
+ }
+
D("--- --- waiting for events");
fdevent_process();
@@ -375,6 +382,10 @@
}
}
+void fdevent_terminate_loop() {
+ terminate_loop = true;
+}
+
size_t fdevent_installed_count() {
return g_poll_node_map.size();
}
@@ -383,4 +394,5 @@
g_poll_node_map.clear();
g_pending_list.clear();
main_thread_valid = false;
+ terminate_loop = false;
}
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 657fde5..207f9b7 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -76,9 +76,9 @@
*/
void fdevent_loop();
-// For debugging only.
+// The following functions are used only for tests.
+void fdevent_terminate_loop();
size_t fdevent_installed_count();
-// For debugging only.
void fdevent_reset();
#endif
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 7fe3d37..c933ed5 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -18,15 +18,13 @@
#include <gtest/gtest.h>
-#include <pthread.h>
-#include <signal.h>
-
#include <limits>
#include <queue>
#include <string>
#include <vector>
#include "adb_io.h"
+#include "fdevent_test.h"
class FdHandler {
public:
@@ -48,7 +46,7 @@
if (events & FDE_READ) {
ASSERT_EQ(fd, handler->read_fd_);
char c;
- ASSERT_EQ(1, read(fd, &c, 1));
+ ASSERT_EQ(1, adb_read(fd, &c, 1));
handler->queue_.push(c);
fdevent_add(&handler->write_fde_, FDE_WRITE);
}
@@ -57,7 +55,7 @@
ASSERT_FALSE(handler->queue_.empty());
char c = handler->queue_.front();
handler->queue_.pop();
- ASSERT_EQ(1, write(fd, &c, 1));
+ ASSERT_EQ(1, adb_write(fd, &c, 1));
if (handler->queue_.empty()) {
fdevent_del(&handler->write_fde_, FDE_WRITE);
}
@@ -72,29 +70,19 @@
std::queue<char> queue_;
};
-static void signal_handler(int) {
- pthread_exit(nullptr);
-}
-
-class FdeventTest : public ::testing::Test {
- protected:
- static void SetUpTestCase() {
- ASSERT_NE(SIG_ERR, signal(SIGUSR1, signal_handler));
- ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
- }
-
- virtual void SetUp() {
- fdevent_reset();
- ASSERT_EQ(0u, fdevent_installed_count());
- }
-};
-
struct ThreadArg {
int first_read_fd;
int last_write_fd;
size_t middle_pipe_count;
};
+TEST_F(FdeventTest, fdevent_terminate) {
+ adb_thread_t thread;
+ PrepareThread();
+ ASSERT_TRUE(adb_thread_create([](void*) { fdevent_loop(); }, nullptr, &thread));
+ TerminateThread(thread);
+}
+
static void FdEventThreadFunc(ThreadArg* arg) {
std::vector<int> read_fds;
std::vector<int> write_fds;
@@ -102,7 +90,7 @@
read_fds.push_back(arg->first_read_fd);
for (size_t i = 0; i < arg->middle_pipe_count; ++i) {
int fds[2];
- ASSERT_EQ(0, pipe(fds));
+ ASSERT_EQ(0, adb_socketpair(fds));
read_fds.push_back(fds[0]);
write_fds.push_back(fds[1]);
}
@@ -122,9 +110,9 @@
const std::string MESSAGE = "fdevent_test";
int fd_pair1[2];
int fd_pair2[2];
- ASSERT_EQ(0, pipe(fd_pair1));
- ASSERT_EQ(0, pipe(fd_pair2));
- pthread_t thread;
+ ASSERT_EQ(0, adb_socketpair(fd_pair1));
+ ASSERT_EQ(0, adb_socketpair(fd_pair2));
+ adb_thread_t thread;
ThreadArg thread_arg;
thread_arg.first_read_fd = fd_pair1[0];
thread_arg.last_write_fd = fd_pair2[1];
@@ -132,9 +120,9 @@
int writer = fd_pair1[1];
int reader = fd_pair2[0];
- ASSERT_EQ(0, pthread_create(&thread, nullptr,
- reinterpret_cast<void* (*)(void*)>(FdEventThreadFunc),
- &thread_arg));
+ PrepareThread();
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(FdEventThreadFunc), &thread_arg,
+ &thread));
for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
std::string read_buffer = MESSAGE;
@@ -144,10 +132,9 @@
ASSERT_EQ(read_buffer, write_buffer);
}
- ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
- ASSERT_EQ(0, pthread_join(thread, nullptr));
- ASSERT_EQ(0, close(writer));
- ASSERT_EQ(0, close(reader));
+ TerminateThread(thread);
+ ASSERT_EQ(0, adb_close(writer));
+ ASSERT_EQ(0, adb_close(reader));
}
struct InvalidFdArg {
@@ -161,7 +148,7 @@
ASSERT_EQ(arg->expected_events, events);
fdevent_remove(&arg->fde);
if (++*(arg->happened_event_count) == 2) {
- pthread_exit(nullptr);
+ fdevent_terminate_loop();
}
}
@@ -184,9 +171,7 @@
}
TEST_F(FdeventTest, invalid_fd) {
- pthread_t thread;
- ASSERT_EQ(0, pthread_create(&thread, nullptr,
- reinterpret_cast<void* (*)(void*)>(InvalidFdThreadFunc),
- nullptr));
- ASSERT_EQ(0, pthread_join(thread, nullptr));
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(InvalidFdThreadFunc, nullptr, &thread));
+ ASSERT_TRUE(adb_thread_join(thread));
}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
new file mode 100644
index 0000000..c853bce
--- /dev/null
+++ b/adb/fdevent_test.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "socket.h"
+#include "sysdeps.h"
+
+class FdeventTest : public ::testing::Test {
+ protected:
+ int dummy = -1;
+
+ static void SetUpTestCase() {
+#if !defined(_WIN32)
+ ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
+#endif
+ }
+
+ void SetUp() override {
+ fdevent_reset();
+ ASSERT_EQ(0u, fdevent_installed_count());
+ }
+
+ // Register a dummy socket used to wake up the fdevent loop to tell it to die.
+ void PrepareThread() {
+ int dummy_fds[2];
+ if (adb_socketpair(dummy_fds) != 0) {
+ FAIL() << "failed to create socketpair: " << strerror(errno);
+ }
+
+ asocket* dummy_socket = create_local_socket(dummy_fds[1]);
+ if (!dummy_socket) {
+ FAIL() << "failed to create local socket: " << strerror(errno);
+ }
+ dummy_socket->ready(dummy_socket);
+ dummy = dummy_fds[0];
+ }
+
+ void TerminateThread(adb_thread_t thread) {
+ fdevent_terminate_loop();
+ ASSERT_TRUE(WriteFdExactly(dummy, "", 1));
+ ASSERT_TRUE(adb_thread_join(thread));
+ ASSERT_EQ(0, adb_close(dummy));
+ }
+};
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 51fc143..6a9e163 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -60,6 +60,18 @@
}
}
+static bool should_pull_file(mode_t mode) {
+ return mode & (S_IFREG | S_IFBLK | S_IFCHR);
+}
+
+static bool should_push_file(mode_t mode) {
+ mode_t mask = S_IFREG;
+#if !defined(_WIN32)
+ mask |= S_IFLNK;
+#endif
+ return mode & mask;
+}
+
struct copyinfo {
std::string lpath;
std::string rpath;
@@ -88,7 +100,8 @@
: total_bytes_(0),
start_time_ms_(CurrentTimeMs()),
expected_total_bytes_(0),
- expect_multiple_files_(false) {
+ expect_multiple_files_(false),
+ expect_done_(false) {
max = SYNC_DATA_MAX; // TODO: decide at runtime.
std::string error;
@@ -117,6 +130,16 @@
bool IsValid() { return fd >= 0; }
+ bool ReceivedError(const char* from, const char* to) {
+ adb_pollfd pfd = {.fd = fd, .events = POLLIN};
+ int rc = adb_poll(&pfd, 1, 0);
+ if (rc < 0) {
+ Error("failed to poll: %s", strerror(errno));
+ return true;
+ }
+ return rc != 0;
+ }
+
bool SendRequest(int id, const char* path_and_mode) {
size_t path_length = strlen(path_and_mode);
if (path_length > 1024) {
@@ -175,7 +198,9 @@
p += sizeof(SyncRequest);
WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
+ expect_done_ = true;
total_bytes_ += data_length;
+ ReportProgress(rpath, data_length, data_length);
return true;
}
@@ -220,6 +245,11 @@
total_bytes_ += bytes_read;
bytes_copied += bytes_read;
+ // Check to see if we've received an error from the other side.
+ if (ReceivedError(lpath, rpath)) {
+ break;
+ }
+
ReportProgress(rpath, bytes_copied, total_size);
}
@@ -228,17 +258,24 @@
syncmsg msg;
msg.data.id = ID_DONE;
msg.data.size = mtime;
+ expect_done_ = true;
return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
}
bool CopyDone(const char* from, const char* to) {
syncmsg msg;
if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
- Error("failed to copy '%s' to '%s': no ID_DONE: %s", from, to, strerror(errno));
+ Error("failed to copy '%s' to '%s': couldn't read from device", from, to);
return false;
}
if (msg.status.id == ID_OKAY) {
- return true;
+ if (expect_done_) {
+ expect_done_ = false;
+ return true;
+ } else {
+ Error("failed to copy '%s' to '%s': received premature success", from, to);
+ return true;
+ }
}
if (msg.status.id != ID_FAIL) {
Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
@@ -357,6 +394,7 @@
uint64_t expected_total_bytes_;
bool expect_multiple_files_;
+ bool expect_done_;
LinePrinter line_printer_;
@@ -457,11 +495,6 @@
#endif
}
- if (!S_ISREG(mode)) {
- sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode);
- return false;
- }
-
struct stat st;
if (stat(lpath, &st) == -1) {
sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
@@ -492,12 +525,6 @@
if (!sc.SendRequest(ID_RECV, rpath)) return false;
adb_unlink(lpath);
- const std::string dirpath = adb_dirname(lpath);
- if (!mkdirs(dirpath.c_str())) {
- sc.Error("failed to create parent directory '%s': %s", dirpath.c_str(), strerror(errno));
- return false;
- }
-
int lfd = adb_creat(lpath, 0644);
if (lfd < 0) {
sc.Error("cannot create '%s': %s", lpath, strerror(errno));
@@ -599,13 +626,12 @@
if (S_ISDIR(st.st_mode)) {
dirlist.push_back(ci);
} else {
- if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
- sc.Error("skipping special file '%s'", lpath.c_str());
+ if (!should_push_file(st.st_mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", lpath.c_str(), st.st_mode);
ci.skip = true;
- } else {
- ci.time = st.st_mtime;
- ci.size = st.st_size;
}
+ ci.time = st.st_mtime;
+ ci.size = st.st_size;
file_list->push_back(ci);
}
}
@@ -747,6 +773,9 @@
success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(),
false, false);
continue;
+ } else if (!should_push_file(st.st_mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
+ continue;
}
std::string path_holder;
@@ -778,13 +807,14 @@
return S_ISDIR(mode);
}
-static bool remote_build_list(SyncConnection& sc,
- std::vector<copyinfo>* file_list,
- const std::string& rpath,
- const std::string& lpath) {
+static bool remote_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
+ const std::string& rpath, const std::string& lpath) {
std::vector<copyinfo> dirlist;
std::vector<copyinfo> linklist;
- bool empty_dir = true;
+
+ // Add an entry for the current directory to ensure it gets created before pulling its contents.
+ copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
+ file_list->push_back(ci);
// Put the files/dirs in rpath on the lists.
auto callback = [&](unsigned mode, unsigned size, unsigned time, const char* name) {
@@ -792,15 +822,16 @@
return;
}
- // We found a child that isn't '.' or '..'.
- empty_dir = false;
-
copyinfo ci(lpath, rpath, name, mode);
if (S_ISDIR(mode)) {
dirlist.push_back(ci);
} else if (S_ISLNK(mode)) {
linklist.push_back(ci);
} else {
+ if (!should_pull_file(ci.mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", ci.rpath.c_str(), ci.mode);
+ ci.skip = true;
+ }
ci.time = time;
ci.size = size;
file_list->push_back(ci);
@@ -811,13 +842,6 @@
return false;
}
- // Add the current directory to the list if it was empty, to ensure that it gets created.
- if (empty_dir) {
- copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(rpath), S_IFDIR);
- file_list->push_back(ci);
- return true;
- }
-
// Check each symlink we found to see whether it's a file or directory.
for (copyinfo& link_ci : linklist) {
if (remote_symlink_isdir(sc, link_ci.rpath)) {
@@ -964,11 +988,6 @@
src_isdir = remote_symlink_isdir(sc, src_path);
}
- if ((src_mode & (S_IFREG | S_IFDIR | S_IFBLK | S_IFCHR)) == 0) {
- sc.Error("skipping remote object '%s' (mode = 0o%o)", src_path, src_mode);
- continue;
- }
-
if (src_isdir) {
std::string dst_dir = dst;
@@ -987,27 +1006,30 @@
success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
continue;
- } else {
- std::string path_holder;
- if (dst_isdir) {
- // If we're copying a remote file to a local directory, we
- // really want to copy to local_dir + OS_PATH_SEPARATOR +
- // basename(remote).
- path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
- adb_basename(src_path).c_str());
- dst_path = path_holder.c_str();
- }
+ } else if (!should_pull_file(src_mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_mode);
+ continue;
+ }
- sc.SetExpectedTotalBytes(src_size);
- if (!sync_recv(sc, src_path, dst_path)) {
- success = false;
- continue;
- }
+ std::string path_holder;
+ if (dst_isdir) {
+ // If we're copying a remote file to a local directory, we
+ // really want to copy to local_dir + OS_PATH_SEPARATOR +
+ // basename(remote).
+ path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
+ adb_basename(src_path).c_str());
+ dst_path = path_holder.c_str();
+ }
- if (copy_attrs && set_time_and_mode(dst_path, src_time, src_mode) != 0) {
- success = false;
- continue;
- }
+ sc.SetExpectedTotalBytes(src_size);
+ if (!sync_recv(sc, src_path, dst_path)) {
+ success = false;
+ continue;
+ }
+
+ if (copy_attrs && set_time_and_mode(dst_path, src_time, src_mode) != 0) {
+ success = false;
+ continue;
}
}
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 29c6629..926dbcf 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -183,8 +183,6 @@
}
while (true) {
- unsigned int len;
-
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
if (msg.data.id != ID_DATA) {
@@ -193,17 +191,17 @@
break;
}
SendSyncFail(s, "invalid data message");
- goto fail;
+ goto abort;
}
- len = msg.data.size;
- if (len > buffer.size()) { // TODO: resize buffer?
+
+ if (msg.data.size > buffer.size()) { // TODO: resize buffer?
SendSyncFail(s, "oversize data message");
- goto fail;
+ goto abort;
}
- if (!ReadFdExactly(s, &buffer[0], len)) goto fail;
+ if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
- if (!WriteFdExactly(fd, &buffer[0], len)) {
+ if (!WriteFdExactly(fd, &buffer[0], msg.data.size)) {
SendSyncFailErrno(s, "write failed");
goto fail;
}
@@ -221,6 +219,35 @@
return WriteFdExactly(s, &msg.status, sizeof(msg.status));
fail:
+ // If there's a problem on the device, we'll send an ID_FAIL message and
+ // close the socket. Unfortunately the kernel will sometimes throw that
+ // data away if the other end keeps writing without reading (which is
+ // the case with old versions of adb). To maintain compatibility, keep
+ // reading and throwing away ID_DATA packets until the other side notices
+ // that we've reported an error.
+ while (true) {
+ if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
+
+ if (msg.data.id == ID_DONE) {
+ goto abort;
+ } else if (msg.data.id != ID_DATA) {
+ char id[5];
+ memcpy(id, &msg.data.id, sizeof(msg.data.id));
+ id[4] = '\0';
+ D("handle_send_fail received unexpected id '%s' during failure", id);
+ goto abort;
+ }
+
+ if (msg.data.size > buffer.size()) {
+ D("handle_send_fail received oversized packet of length '%u' during failure",
+ msg.data.size);
+ goto abort;
+ }
+
+ if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
+ }
+
+abort:
if (fd >= 0) adb_close(fd);
if (do_unlink) adb_unlink(path);
return false;
@@ -403,18 +430,6 @@
void file_sync_service(int fd, void* cookie) {
std::vector<char> buffer(SYNC_DATA_MAX);
- // If there's a problem on the device, we'll send an ID_FAIL message and
- // close the socket. Unfortunately the kernel will sometimes throw that
- // data away if the other end keeps writing without reading (which is
- // the normal case with our protocol --- they won't read until the end).
- // So set SO_LINGER to give the client 20s to get around to reading our
- // failure response. Without this, the other side's ability to report
- // useful errors is reduced.
- struct linger l;
- l.l_onoff = 1;
- l.l_linger = 20;
- adb_setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
-
while (handle_sync_command(fd, buffer)) {
}
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 38382c1..460e9dc 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -41,25 +41,25 @@
union syncmsg {
struct __attribute__((packed)) {
- unsigned id;
- unsigned mode;
- unsigned size;
- unsigned time;
+ uint32_t id;
+ uint32_t mode;
+ uint32_t size;
+ uint32_t time;
} stat;
struct __attribute__((packed)) {
- unsigned id;
- unsigned mode;
- unsigned size;
- unsigned time;
- unsigned namelen;
+ uint32_t id;
+ uint32_t mode;
+ uint32_t size;
+ uint32_t time;
+ uint32_t namelen;
} dent;
struct __attribute__((packed)) {
- unsigned id;
- unsigned size;
+ uint32_t id;
+ uint32_t size;
} data;
struct __attribute__((packed)) {
- unsigned id;
- unsigned msglen;
+ uint32_t id;
+ uint32_t msglen;
} status;
};
diff --git a/adb/services.cpp b/adb/services.cpp
index 9cbf787..d5e963b 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -49,6 +49,7 @@
#include "remount_service.h"
#include "services.h"
#include "shell_service.h"
+#include "sysdeps.h"
#include "transport.h"
struct stinfo {
@@ -57,13 +58,11 @@
void *cookie;
};
-void *service_bootstrap_func(void *x)
-{
+static void service_bootstrap_func(void* x) {
stinfo* sti = reinterpret_cast<stinfo*>(x);
adb_thread_setname(android::base::StringPrintf("service %d", sti->fd));
sti->func(sti->fd, sti->cookie);
free(sti);
- return 0;
}
#if !ADB_HOST
@@ -371,12 +370,21 @@
std::string error = "unknown error";
const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
-
if (t != nullptr && t->connection_state == sinfo->state) {
SendOkay(fd);
break;
} else if (!is_ambiguous) {
- adb_sleep_ms(1000);
+ adb_pollfd pfd = {.fd = fd, .events = POLLIN };
+ int rc = adb_poll(&pfd, 1, 1000);
+ if (rc < 0) {
+ SendFail(fd, error);
+ break;
+ } else if (rc > 0 && (pfd.revents & POLLHUP) != 0) {
+ // The other end of the socket is closed, probably because the other side was
+ // terminated, bail out.
+ break;
+ }
+
// Try again...
} else {
SendFail(fd, error);
@@ -413,6 +421,11 @@
close_on_exec(fd);
disable_tcp_nagle(fd);
+ // Send a TCP keepalive ping to the device every second so we can detect disconnects.
+ if (!set_tcp_keepalive(fd, 1)) {
+ D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
+ }
+
int ret = register_socket_transport(fd, serial.c_str(), port, 0);
if (ret < 0) {
adb_close(fd);
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index d080e09..ce10708 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -135,37 +135,6 @@
return received;
}
-// Helper to automatically close an FD when it goes out of scope.
-class ScopedFd {
- public:
- ScopedFd() {}
- ~ScopedFd() { Reset(); }
-
- void Reset(int fd=-1) {
- if (fd != fd_) {
- if (valid()) {
- adb_close(fd_);
- }
- fd_ = fd;
- }
- }
-
- int Release() {
- int temp = fd_;
- fd_ = -1;
- return temp;
- }
-
- bool valid() const { return fd_ >= 0; }
-
- int fd() const { return fd_; }
-
- private:
- int fd_ = -1;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedFd);
-};
-
// Creates a socketpair and saves the endpoints to |fd1| and |fd2|.
bool CreateSocketpair(ScopedFd* fd1, ScopedFd* fd2) {
int sockets[2];
@@ -198,7 +167,7 @@
// Opens the file at |pts_name|.
int OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd);
- static void* ThreadHandler(void* userdata);
+ static void ThreadHandler(void* userdata);
void PassDataStreams();
void WaitForExit();
@@ -315,7 +284,9 @@
if (type_ == SubprocessType::kPty) {
int fd;
pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
- stdinout_sfd_.Reset(fd);
+ if (pid_ > 0) {
+ stdinout_sfd_.Reset(fd);
+ }
} else {
if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
*error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
@@ -465,7 +436,7 @@
return child_fd;
}
-void* Subprocess::ThreadHandler(void* userdata) {
+void Subprocess::ThreadHandler(void* userdata) {
Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
adb_thread_setname(android::base::StringPrintf(
@@ -475,8 +446,6 @@
D("deleting Subprocess for PID %d", subprocess->pid());
delete subprocess;
-
- return nullptr;
}
void Subprocess::PassDataStreams() {
diff --git a/adb/socket.h b/adb/socket.h
index 4083036..9eb1b19 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -114,4 +114,13 @@
void connect_to_remote(asocket *s, const char *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(const char* service);
+#endif
+
+} // namespace internal
+
#endif // __ADB_SOCKET_H
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 03cab64..5cbef6d 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -18,119 +18,89 @@
#include <gtest/gtest.h>
+#include <array>
#include <limits>
#include <queue>
#include <string>
#include <vector>
-#include <pthread.h>
-#include <signal.h>
#include <unistd.h>
#include "adb.h"
#include "adb_io.h"
+#include "fdevent_test.h"
#include "socket.h"
#include "sysdeps.h"
-static void signal_handler(int) {
- ASSERT_EQ(1u, fdevent_installed_count());
- pthread_exit(nullptr);
-}
-
-// On host, register a dummy socket, so fdevet_loop() will not abort when previously
-// registered local sockets are all closed. On device, fdevent_subproc_setup() installs
-// one fdevent which can be considered as dummy socket.
-static void InstallDummySocket() {
-#if ADB_HOST
- int dummy_fds[2];
- ASSERT_EQ(0, pipe(dummy_fds));
- asocket* dummy_socket = create_local_socket(dummy_fds[0]);
- ASSERT_TRUE(dummy_socket != nullptr);
- dummy_socket->ready(dummy_socket);
-#endif
-}
-
struct ThreadArg {
int first_read_fd;
int last_write_fd;
size_t middle_pipe_count;
};
-static void FdEventThreadFunc(ThreadArg* arg) {
- std::vector<int> read_fds;
- std::vector<int> write_fds;
+class LocalSocketTest : public FdeventTest {};
- read_fds.push_back(arg->first_read_fd);
- for (size_t i = 0; i < arg->middle_pipe_count; ++i) {
- int fds[2];
- ASSERT_EQ(0, adb_socketpair(fds));
- read_fds.push_back(fds[0]);
- write_fds.push_back(fds[1]);
- }
- write_fds.push_back(arg->last_write_fd);
-
- for (size_t i = 0; i < read_fds.size(); ++i) {
- asocket* reader = create_local_socket(read_fds[i]);
- ASSERT_TRUE(reader != nullptr);
- asocket* writer = create_local_socket(write_fds[i]);
- ASSERT_TRUE(writer != nullptr);
- reader->peer = writer;
- writer->peer = reader;
- reader->ready(reader);
- }
-
- InstallDummySocket();
+static void FdEventThreadFunc(void*) {
fdevent_loop();
}
-class LocalSocketTest : public ::testing::Test {
- protected:
- static void SetUpTestCase() {
- ASSERT_NE(SIG_ERR, signal(SIGUSR1, signal_handler));
- ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
- }
-
- virtual void SetUp() {
- fdevent_reset();
- ASSERT_EQ(0u, fdevent_installed_count());
- }
-};
-
TEST_F(LocalSocketTest, smoke) {
- const size_t PIPE_COUNT = 100;
- const size_t MESSAGE_LOOP_COUNT = 100;
+ // Join two socketpairs with a chain of intermediate socketpairs.
+ int first[2];
+ std::vector<std::array<int, 2>> intermediates;
+ int last[2];
+
+ constexpr size_t INTERMEDIATE_COUNT = 50;
+ constexpr size_t MESSAGE_LOOP_COUNT = 100;
const std::string MESSAGE = "socket_test";
- int fd_pair1[2];
- int fd_pair2[2];
- ASSERT_EQ(0, adb_socketpair(fd_pair1));
- ASSERT_EQ(0, adb_socketpair(fd_pair2));
- pthread_t thread;
- ThreadArg thread_arg;
- thread_arg.first_read_fd = fd_pair1[0];
- thread_arg.last_write_fd = fd_pair2[1];
- thread_arg.middle_pipe_count = PIPE_COUNT;
- int writer = fd_pair1[1];
- int reader = fd_pair2[0];
- ASSERT_EQ(0, pthread_create(&thread, nullptr,
- reinterpret_cast<void* (*)(void*)>(FdEventThreadFunc),
- &thread_arg));
+ intermediates.resize(INTERMEDIATE_COUNT);
+ ASSERT_EQ(0, adb_socketpair(first)) << strerror(errno);
+ ASSERT_EQ(0, adb_socketpair(last)) << strerror(errno);
+ asocket* prev_tail = create_local_socket(first[1]);
+ ASSERT_NE(nullptr, prev_tail);
- usleep(1000);
+ auto connect = [](asocket* tail, asocket* head) {
+ tail->peer = head;
+ head->peer = tail;
+ tail->ready(tail);
+ };
+
+ for (auto& intermediate : intermediates) {
+ ASSERT_EQ(0, adb_socketpair(intermediate.data())) << strerror(errno);
+
+ asocket* head = create_local_socket(intermediate[0]);
+ ASSERT_NE(nullptr, head);
+
+ asocket* tail = create_local_socket(intermediate[1]);
+ ASSERT_NE(nullptr, tail);
+
+ connect(prev_tail, head);
+ prev_tail = tail;
+ }
+
+ asocket* end = create_local_socket(last[0]);
+ ASSERT_NE(nullptr, end);
+ connect(prev_tail, end);
+
+ PrepareThread();
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(FdEventThreadFunc, nullptr, &thread));
+
for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
std::string read_buffer = MESSAGE;
std::string write_buffer(MESSAGE.size(), 'a');
- ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
- ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
+ ASSERT_TRUE(WriteFdExactly(first[0], &read_buffer[0], read_buffer.size()));
+ ASSERT_TRUE(ReadFdExactly(last[1], &write_buffer[0], write_buffer.size()));
ASSERT_EQ(read_buffer, write_buffer);
}
- ASSERT_EQ(0, adb_close(writer));
- ASSERT_EQ(0, adb_close(reader));
- // Wait until the local sockets are closed.
- sleep(1);
- ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
- ASSERT_EQ(0, pthread_join(thread, nullptr));
+ ASSERT_EQ(0, adb_close(first[0]));
+ ASSERT_EQ(0, adb_close(last[1]));
+
+ // Wait until the local sockets are closed.
+ adb_sleep_ms(100);
+ TerminateThread(thread);
}
struct CloseWithPacketArg {
@@ -160,7 +130,6 @@
s->peer = cause_close_s;
cause_close_s->ready(cause_close_s);
- InstallDummySocket();
fdevent_loop();
}
@@ -176,21 +145,19 @@
CloseWithPacketArg arg;
arg.socket_fd = socket_fd[1];
arg.cause_close_fd = cause_close_fd[1];
- pthread_t thread;
- ASSERT_EQ(0, pthread_create(&thread, nullptr,
- reinterpret_cast<void* (*)(void*)>(CloseWithPacketThreadFunc),
- &arg));
- // Wait until the fdevent_loop() starts.
- sleep(1);
- ASSERT_EQ(0, adb_close(cause_close_fd[0]));
- sleep(1);
- ASSERT_EQ(2u, fdevent_installed_count());
- ASSERT_EQ(0, adb_close(socket_fd[0]));
- // Wait until the socket is closed.
- sleep(1);
- ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
- ASSERT_EQ(0, pthread_join(thread, nullptr));
+ PrepareThread();
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
+ &arg, &thread));
+ // Wait until the fdevent_loop() starts.
+ adb_sleep_ms(100);
+ ASSERT_EQ(0, adb_close(cause_close_fd[0]));
+ adb_sleep_ms(100);
+ EXPECT_EQ(2u, fdevent_installed_count());
+ ASSERT_EQ(0, adb_close(socket_fd[0]));
+
+ TerminateThread(thread);
}
// This test checks if we can read packets from a closing local socket.
@@ -203,26 +170,23 @@
arg.socket_fd = socket_fd[1];
arg.cause_close_fd = cause_close_fd[1];
- pthread_t thread;
- ASSERT_EQ(0, pthread_create(&thread, nullptr,
- reinterpret_cast<void* (*)(void*)>(CloseWithPacketThreadFunc),
- &arg));
+ PrepareThread();
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
+ &arg, &thread));
// Wait until the fdevent_loop() starts.
- sleep(1);
+ adb_sleep_ms(100);
ASSERT_EQ(0, adb_close(cause_close_fd[0]));
- sleep(1);
- ASSERT_EQ(2u, fdevent_installed_count());
+ adb_sleep_ms(100);
+ EXPECT_EQ(2u, fdevent_installed_count());
// Verify if we can read successfully.
std::vector<char> buf(arg.bytes_written);
+ ASSERT_NE(0u, arg.bytes_written);
ASSERT_EQ(true, ReadFdExactly(socket_fd[0], buf.data(), buf.size()));
ASSERT_EQ(0, adb_close(socket_fd[0]));
- // Wait until the socket is closed.
- sleep(1);
-
- ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
- ASSERT_EQ(0, pthread_join(thread, nullptr));
+ TerminateThread(thread);
}
// This test checks if we can close local socket in the following situation:
@@ -238,20 +202,17 @@
arg.socket_fd = socket_fd[1];
arg.cause_close_fd = cause_close_fd[1];
- pthread_t thread;
- ASSERT_EQ(0, pthread_create(&thread, nullptr,
- reinterpret_cast<void* (*)(void*)>(CloseWithPacketThreadFunc),
- &arg));
+ PrepareThread();
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
+ &arg, &thread));
+
// Wait until the fdevent_loop() starts.
- sleep(1);
- ASSERT_EQ(3u, fdevent_installed_count());
+ adb_sleep_ms(100);
+ EXPECT_EQ(3u, fdevent_installed_count());
ASSERT_EQ(0, adb_close(socket_fd[0]));
- // Wait until the socket is closed.
- sleep(1);
-
- ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
- ASSERT_EQ(0, pthread_join(thread, nullptr));
+ TerminateThread(thread);
}
#if defined(__linux__)
@@ -260,50 +221,98 @@
std::string error;
int fd = network_loopback_client(5038, SOCK_STREAM, &error);
ASSERT_GE(fd, 0) << error;
- sleep(2);
+ adb_sleep_ms(200);
ASSERT_EQ(0, adb_close(fd));
}
struct CloseRdHupSocketArg {
- int socket_fd;
+ int socket_fd;
};
static void CloseRdHupSocketThreadFunc(CloseRdHupSocketArg* arg) {
- asocket* s = create_local_socket(arg->socket_fd);
- ASSERT_TRUE(s != nullptr);
+ asocket* s = create_local_socket(arg->socket_fd);
+ ASSERT_TRUE(s != nullptr);
- InstallDummySocket();
- fdevent_loop();
+ fdevent_loop();
}
// This test checks if we can close sockets in CLOSE_WAIT state.
TEST_F(LocalSocketTest, close_socket_in_CLOSE_WAIT_state) {
- std::string error;
- int listen_fd = network_inaddr_any_server(5038, SOCK_STREAM, &error);
- ASSERT_GE(listen_fd, 0);
- pthread_t client_thread;
- ASSERT_EQ(0, pthread_create(&client_thread, nullptr,
- reinterpret_cast<void* (*)(void*)>(ClientThreadFunc), nullptr));
+ std::string error;
+ int listen_fd = network_inaddr_any_server(5038, SOCK_STREAM, &error);
+ ASSERT_GE(listen_fd, 0);
- struct sockaddr addr;
- socklen_t alen;
- alen = sizeof(addr);
- int accept_fd = adb_socket_accept(listen_fd, &addr, &alen);
- ASSERT_GE(accept_fd, 0);
- CloseRdHupSocketArg arg;
- arg.socket_fd = accept_fd;
- pthread_t thread;
- ASSERT_EQ(0, pthread_create(&thread, nullptr,
- reinterpret_cast<void* (*)(void*)>(CloseRdHupSocketThreadFunc),
- &arg));
- // Wait until the fdevent_loop() starts.
- sleep(1);
- ASSERT_EQ(2u, fdevent_installed_count());
- // Wait until the client closes its socket.
- ASSERT_EQ(0, pthread_join(client_thread, nullptr));
- sleep(2);
- ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
- ASSERT_EQ(0, pthread_join(thread, nullptr));
+ adb_thread_t client_thread;
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(ClientThreadFunc), nullptr,
+ &client_thread));
+
+ struct sockaddr addr;
+ socklen_t alen;
+ alen = sizeof(addr);
+ int accept_fd = adb_socket_accept(listen_fd, &addr, &alen);
+ ASSERT_GE(accept_fd, 0);
+ CloseRdHupSocketArg arg;
+ arg.socket_fd = accept_fd;
+
+ PrepareThread();
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseRdHupSocketThreadFunc),
+ &arg, &thread));
+
+ // Wait until the fdevent_loop() starts.
+ adb_sleep_ms(100);
+ EXPECT_EQ(2u, fdevent_installed_count());
+
+ // Wait until the client closes its socket.
+ ASSERT_TRUE(adb_thread_join(client_thread));
+
+ TerminateThread(thread);
}
#endif // defined(__linux__)
+
+#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(const std::string& serial, const char* expected) {
+ const char* result = internal::skip_host_serial(serial.c_str());
+ if (expected == nullptr) {
+ EXPECT_EQ(nullptr, result);
+ } else {
+ EXPECT_STREQ(expected, result);
+ }
+}
+
+// Check [tcp:|udp:]<serial>[:<port>]:<command> format.
+TEST(socket_test, test_skip_host_serial) {
+ for (const std::string& protocol : {"", "tcp:", "udp:"}) {
+ VerifySkipHostSerial(protocol, nullptr);
+ VerifySkipHostSerial(protocol + "foo", nullptr);
+
+ VerifySkipHostSerial(protocol + "foo:bar", ":bar");
+ VerifySkipHostSerial(protocol + "foo:bar:baz", ":bar:baz");
+
+ VerifySkipHostSerial(protocol + "foo:123:bar", ":bar");
+ VerifySkipHostSerial(protocol + "foo:123:456", ":456");
+ VerifySkipHostSerial(protocol + "foo:123:bar:baz", ":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");
+ }
+}
+
+// Check <prefix>:<serial>:<command> format.
+TEST(socket_test, test_skip_host_serial_prefix) {
+ for (const std::string& prefix : {"usb:", "product:", "model:", "device:"}) {
+ VerifySkipHostSerial(prefix, nullptr);
+ VerifySkipHostSerial(prefix + "foo", nullptr);
+
+ VerifySkipHostSerial(prefix + "foo:bar", ":bar");
+ VerifySkipHostSerial(prefix + "foo:bar:baz", ":bar:baz");
+ VerifySkipHostSerial(prefix + "foo:123:bar", ":123:bar");
+ }
+}
+
+#endif // ADB_HOST
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index d8e4e93..c083ee1 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -26,6 +26,8 @@
#include <unistd.h>
#include <algorithm>
+#include <string>
+#include <vector>
#if !ADB_HOST
#include "cutils/properties.h"
@@ -623,43 +625,43 @@
#if ADB_HOST
-#define PREFIX(str) { str, sizeof(str) - 1 }
-static const struct prefix_struct {
- const char *str;
- const size_t len;
-} prefixes[] = {
- PREFIX("usb:"),
- PREFIX("product:"),
- PREFIX("model:"),
- PREFIX("device:"),
-};
-static const int num_prefixes = (sizeof(prefixes) / sizeof(prefixes[0]));
+namespace internal {
-/* skip_host_serial return the position in a string
- skipping over the 'serial' parameter in the ADB protocol,
- where parameter string may be a host:port string containing
- the protocol delimiter (colon). */
-static char *skip_host_serial(char *service) {
- char *first_colon, *serial_end;
- int i;
+// Returns the position in |service| following the target serial parameter. Serial format can be
+// any of:
+// * [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(const char* service) {
+ static const std::vector<std::string>& prefixes =
+ *(new std::vector<std::string>{"usb:", "product:", "model:", "device:"});
- for (i = 0; i < num_prefixes; i++) {
- if (!strncmp(service, prefixes[i].str, prefixes[i].len))
- return strchr(service + prefixes[i].len, ':');
+ for (const std::string& prefix : prefixes) {
+ if (!strncmp(service, prefix.c_str(), prefix.length())) {
+ return strchr(service + prefix.length(), ':');
+ }
}
- first_colon = strchr(service, ':');
+ // For fastboot compatibility, ignore protocol prefixes.
+ if (!strncmp(service, "tcp:", 4) || !strncmp(service, "udp:", 4)) {
+ service += 4;
+ }
+
+ char* first_colon = strchr(service, ':');
if (!first_colon) {
- /* No colon in service string. */
- return NULL;
+ // No colon in service string.
+ return nullptr;
}
- serial_end = first_colon;
+
+ char* serial_end = first_colon;
if (isdigit(serial_end[1])) {
serial_end++;
- while ((*serial_end) && isdigit(*serial_end)) {
+ while (*serial_end && isdigit(*serial_end)) {
serial_end++;
}
- if ((*serial_end) != ':') {
+ if (*serial_end != ':') {
// Something other than numbers was found, reset the end.
serial_end = first_colon;
}
@@ -667,6 +669,8 @@
return serial_end;
}
+} // namespace internal
+
#endif // ADB_HOST
static int smart_socket_enqueue(asocket *s, apacket *p)
@@ -725,7 +729,7 @@
service += strlen("host-serial:");
// serial number should follow "host:" and could be a host:port string.
- serial_end = skip_host_serial(service);
+ serial_end = internal::skip_host_serial(service);
if (serial_end) {
*serial_end = 0; // terminate string
serial = service;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 16796cd..81d201e 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -30,6 +30,7 @@
#include <vector>
// Include this before open/unlink are defined as macros below.
+#include <android-base/errors.h>
#include <android-base/utf8.h>
/*
@@ -114,13 +115,62 @@
LeaveCriticalSection( lock );
}
-typedef void* (*adb_thread_func_t)(void* arg);
+typedef void (*adb_thread_func_t)(void* arg);
+typedef HANDLE adb_thread_t;
-typedef void (*win_thread_func_t)(void* arg);
+struct adb_winthread_args {
+ adb_thread_func_t func;
+ void* arg;
+};
-static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg) {
- uintptr_t tid = _beginthread((win_thread_func_t)func, 0, arg);
- return (tid != static_cast<uintptr_t>(-1L));
+static unsigned __stdcall adb_winthread_wrapper(void* heap_args) {
+ // Move the arguments from the heap onto the thread's stack.
+ adb_winthread_args thread_args = *static_cast<adb_winthread_args*>(heap_args);
+ delete static_cast<adb_winthread_args*>(heap_args);
+ thread_args.func(thread_args.arg);
+ return 0;
+}
+
+static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg,
+ adb_thread_t* thread = nullptr) {
+ adb_winthread_args* args = new adb_winthread_args{.func = func, .arg = arg};
+ uintptr_t handle = _beginthreadex(nullptr, 0, adb_winthread_wrapper, args, 0, nullptr);
+ if (handle != static_cast<uintptr_t>(0)) {
+ if (thread) {
+ *thread = reinterpret_cast<HANDLE>(handle);
+ } else {
+ CloseHandle(thread);
+ }
+ return true;
+ }
+ return false;
+}
+
+static __inline__ bool adb_thread_join(adb_thread_t thread) {
+ switch (WaitForSingleObject(thread, INFINITE)) {
+ case WAIT_OBJECT_0:
+ CloseHandle(thread);
+ return true;
+
+ case WAIT_FAILED:
+ fprintf(stderr, "adb_thread_join failed: %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ break;
+
+ default:
+ abort();
+ }
+
+ return false;
+}
+
+static __inline__ bool adb_thread_detach(adb_thread_t thread) {
+ CloseHandle(thread);
+ return true;
+}
+
+static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
+ _endthreadex(0);
}
static __inline__ int adb_thread_setname(const std::string& name) {
@@ -130,6 +180,14 @@
return 0;
}
+static __inline__ adb_thread_t adb_thread_self() {
+ return GetCurrentThread();
+}
+
+static __inline__ bool adb_thread_equal(adb_thread_t lhs, adb_thread_t rhs) {
+ return GetThreadId(lhs) == GetThreadId(rhs);
+}
+
static __inline__ unsigned long adb_thread_id()
{
return GetCurrentThreadId();
@@ -213,24 +271,6 @@
/* normally provided by <cutils/misc.h> */
extern void* load_file(const char* pathname, unsigned* psize);
-/* normally provided by "fdevent.h" */
-
-#define FDE_READ 0x0001
-#define FDE_WRITE 0x0002
-#define FDE_ERROR 0x0004
-#define FDE_DONT_CLOSE 0x0080
-
-typedef void (*fd_func)(int fd, unsigned events, void *userdata);
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg);
-void fdevent_destroy(fdevent *fde);
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
-void fdevent_remove(fdevent *item);
-void fdevent_set(fdevent *fde, unsigned events);
-void fdevent_add(fdevent *fde, unsigned events);
-void fdevent_del(fdevent *fde, unsigned events);
-void fdevent_loop();
-
static __inline__ void adb_sleep_ms( int mseconds )
{
Sleep( mseconds );
@@ -254,6 +294,14 @@
extern int adb_socketpair( int sv[2] );
+struct adb_pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+extern int adb_poll(adb_pollfd* fds, size_t nfds, int timeout);
+#define poll ___xxx_poll
+
static __inline__ int adb_is_absolute_host_path(const char* path) {
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
}
@@ -406,14 +454,14 @@
#else /* !_WIN32 a.k.a. Unix */
-#include "fdevent.h"
#include <cutils/misc.h>
#include <cutils/sockets.h>
#include <cutils/threads.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
#include <pthread.h>
#include <unistd.h>
@@ -528,8 +576,7 @@
// Closes a file descriptor that came from adb_open() or adb_open_mode(), but
// not designed to take a file descriptor from unix_open(). See the comments
// for adb_open() for more info.
-static __inline__ int adb_close(int fd)
-{
+__inline__ int adb_close(int fd) {
return close(fd);
}
#undef close
@@ -656,16 +703,53 @@
#define unix_write adb_write
#define unix_close adb_close
-typedef void* (*adb_thread_func_t)( void* arg );
+// Win32 is limited to DWORDs for thread return values; limit the POSIX systems to this as well to
+// ensure compatibility.
+typedef void (*adb_thread_func_t)(void* arg);
+typedef pthread_t adb_thread_t;
-static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg) {
+struct adb_pthread_args {
+ adb_thread_func_t func;
+ void* arg;
+};
+
+static void* adb_pthread_wrapper(void* heap_args) {
+ // Move the arguments from the heap onto the thread's stack.
+ adb_pthread_args thread_args = *reinterpret_cast<adb_pthread_args*>(heap_args);
+ delete static_cast<adb_pthread_args*>(heap_args);
+ thread_args.func(thread_args.arg);
+ return nullptr;
+}
+
+static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg,
+ adb_thread_t* thread = nullptr) {
+ pthread_t temp;
pthread_attr_t attr;
pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setdetachstate(&attr, thread ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED);
+ auto* pthread_args = new adb_pthread_args{.func = start, .arg = arg};
+ errno = pthread_create(&temp, &attr, adb_pthread_wrapper, pthread_args);
+ if (errno == 0) {
+ if (thread) {
+ *thread = temp;
+ }
+ return true;
+ }
+ return false;
+}
- pthread_t thread;
- errno = pthread_create(&thread, &attr, start, arg);
- return (errno == 0);
+static __inline__ bool adb_thread_join(adb_thread_t thread) {
+ errno = pthread_join(thread, nullptr);
+ return errno == 0;
+}
+
+static __inline__ bool adb_thread_detach(adb_thread_t thread) {
+ errno = pthread_detach(thread);
+ return errno == 0;
+}
+
+static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
+ pthread_exit(nullptr);
}
static __inline__ int adb_thread_setname(const std::string& name) {
@@ -716,6 +800,13 @@
#undef socketpair
#define socketpair ___xxx_socketpair
+typedef struct pollfd adb_pollfd;
+static __inline__ int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
+ return TEMP_FAILURE_RETRY(poll(fds, nfds, timeout));
+}
+
+#define poll ___xxx_poll
+
static __inline__ void adb_sleep_ms( int mseconds )
{
usleep( mseconds*1000 );
@@ -749,4 +840,9 @@
adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
}
+// Sets TCP socket |fd| to send a keepalive TCP message every |interval_sec| seconds. Set
+// |interval_sec| to 0 to disable keepalives. If keepalives are enabled, the connection will be
+// configured to drop after 10 missed keepalives. Returns true on success.
+bool set_tcp_keepalive(int fd, int interval_sec);
+
#endif /* _ADB_SYSDEPS_H */
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
new file mode 100644
index 0000000..78efea8
--- /dev/null
+++ b/adb/sysdeps_test.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <atomic>
+
+#include "adb_io.h"
+#include "sysdeps.h"
+
+static void increment_atomic_int(void* c) {
+ sleep(1);
+ reinterpret_cast<std::atomic<int>*>(c)->fetch_add(1);
+}
+
+TEST(sysdeps_thread, smoke) {
+ std::atomic<int> counter(0);
+
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter));
+ }
+
+ sleep(2);
+ ASSERT_EQ(100, counter.load());
+}
+
+TEST(sysdeps_thread, join) {
+ std::atomic<int> counter(0);
+ std::vector<adb_thread_t> threads(500);
+ for (size_t i = 0; i < threads.size(); ++i) {
+ ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter, &threads[i]));
+ }
+
+ int current = counter.load();
+ ASSERT_GE(current, 0);
+ // Make sure that adb_thread_create actually creates threads, and doesn't do something silly
+ // like synchronously run the function passed in. The sleep in increment_atomic_int should be
+ // enough to keep this from being flakey.
+ ASSERT_LT(current, 500);
+
+ for (const auto& thread : threads) {
+ ASSERT_TRUE(adb_thread_join(thread));
+ }
+
+ ASSERT_EQ(500, counter.load());
+}
+
+TEST(sysdeps_thread, exit) {
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(
+ [](void*) {
+ adb_thread_exit();
+ for (;;) continue;
+ },
+ nullptr, &thread));
+ ASSERT_TRUE(adb_thread_join(thread));
+}
+
+TEST(sysdeps_socketpair, smoke) {
+ int fds[2];
+ ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
+ ASSERT_TRUE(WriteFdExactly(fds[0], "foo", 4));
+ ASSERT_TRUE(WriteFdExactly(fds[1], "bar", 4));
+
+ char buf[4];
+ ASSERT_TRUE(ReadFdExactly(fds[1], buf, 4));
+ ASSERT_STREQ(buf, "foo");
+ ASSERT_TRUE(ReadFdExactly(fds[0], buf, 4));
+ ASSERT_STREQ(buf, "bar");
+ ASSERT_EQ(0, adb_close(fds[0]));
+ ASSERT_EQ(0, adb_close(fds[1]));
+}
+
+TEST(sysdeps_fd, exhaustion) {
+ std::vector<int> fds;
+ int socketpair[2];
+
+ while (adb_socketpair(socketpair) == 0) {
+ fds.push_back(socketpair[0]);
+ fds.push_back(socketpair[1]);
+ }
+
+ ASSERT_EQ(EMFILE, errno) << strerror(errno);
+ for (int fd : fds) {
+ ASSERT_EQ(0, adb_close(fd));
+ }
+ ASSERT_EQ(0, adb_socketpair(socketpair));
+ ASSERT_EQ(socketpair[0], fds[0]);
+ ASSERT_EQ(socketpair[1], fds[1]);
+ ASSERT_EQ(0, adb_close(socketpair[0]));
+ ASSERT_EQ(0, adb_close(socketpair[1]));
+}
+
+class sysdeps_poll : public ::testing::Test {
+ protected:
+ int fds[2];
+ void SetUp() override {
+ ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
+ }
+
+ void TearDown() override {
+ if (fds[0] >= 0) {
+ ASSERT_EQ(0, adb_close(fds[0]));
+ }
+ if (fds[1] >= 0) {
+ ASSERT_EQ(0, adb_close(fds[1]));
+ }
+ }
+};
+
+TEST_F(sysdeps_poll, smoke) {
+ adb_pollfd pfd[2] = {};
+ pfd[0].fd = fds[0];
+ pfd[0].events = POLLRDNORM;
+ pfd[1].fd = fds[1];
+ pfd[1].events = POLLWRNORM;
+
+ pfd[0].revents = -1;
+ pfd[1].revents = -1;
+ EXPECT_EQ(1, adb_poll(pfd, 2, 0));
+ EXPECT_EQ(0, pfd[0].revents);
+ EXPECT_EQ(POLLWRNORM, pfd[1].revents);
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ // Wait for the socketpair to be flushed.
+ pfd[0].revents = -1;
+ EXPECT_EQ(1, adb_poll(pfd, 1, 100));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ pfd[0].revents = -1;
+ pfd[1].revents = -1;
+ EXPECT_EQ(2, adb_poll(pfd, 2, 0));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ EXPECT_EQ(POLLWRNORM, pfd[1].revents);
+}
+
+TEST_F(sysdeps_poll, timeout) {
+ adb_pollfd pfd = {};
+ pfd.fd = fds[0];
+ pfd.events = POLLRDNORM;
+
+ EXPECT_EQ(0, adb_poll(&pfd, 1, 100));
+ EXPECT_EQ(0, pfd.revents);
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
+ EXPECT_EQ(POLLRDNORM, pfd.revents);
+}
+
+TEST_F(sysdeps_poll, invalid_fd) {
+ adb_pollfd pfd[3] = {};
+ pfd[0].fd = fds[0];
+ pfd[0].events = POLLRDNORM;
+ pfd[1].fd = INT_MAX;
+ pfd[1].events = POLLRDNORM;
+ pfd[2].fd = fds[1];
+ pfd[2].events = POLLWRNORM;
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ // Wait for the socketpair to be flushed.
+ EXPECT_EQ(1, adb_poll(pfd, 1, 100));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+
+ EXPECT_EQ(3, adb_poll(pfd, 3, 0));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ EXPECT_EQ(POLLNVAL, pfd[1].revents);
+ EXPECT_EQ(POLLWRNORM, pfd[2].revents);
+}
+
+TEST_F(sysdeps_poll, duplicate_fd) {
+ adb_pollfd pfd[2] = {};
+ pfd[0].fd = fds[0];
+ pfd[0].events = POLLRDNORM;
+ pfd[1] = pfd[0];
+
+ EXPECT_EQ(0, adb_poll(pfd, 2, 0));
+ EXPECT_EQ(0, pfd[0].revents);
+ EXPECT_EQ(0, pfd[1].revents);
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ EXPECT_EQ(2, adb_poll(pfd, 2, 100));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ EXPECT_EQ(POLLRDNORM, pfd[1].revents);
+}
+
+TEST_F(sysdeps_poll, disconnect) {
+ adb_pollfd pfd = {};
+ pfd.fd = fds[0];
+ pfd.events = POLLIN;
+
+ EXPECT_EQ(0, adb_poll(&pfd, 1, 0));
+ EXPECT_EQ(0, pfd.revents);
+
+ EXPECT_EQ(0, adb_close(fds[1]));
+ fds[1] = -1;
+
+ EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
+
+ // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
+ EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
+}
diff --git a/adb/sysdeps_unix.cpp b/adb/sysdeps_unix.cpp
new file mode 100644
index 0000000..4445a44
--- /dev/null
+++ b/adb/sysdeps_unix.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps.h"
+
+bool set_tcp_keepalive(int fd, int interval_sec) {
+ int enable = (interval_sec > 0);
+ if (adb_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable))) {
+ return false;
+ }
+
+ if (!enable) {
+ return true;
+ }
+
+ // Idle time before sending the first keepalive is TCP_KEEPIDLE on Linux, TCP_KEEPALIVE on Mac.
+#if defined(TCP_KEEPIDLE)
+ if (adb_setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &interval_sec, sizeof(interval_sec))) {
+ return false;
+ }
+#elif defined(TCP_KEEPALIVE)
+ if (adb_setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &interval_sec, sizeof(interval_sec))) {
+ return false;
+ }
+#endif
+
+ // TCP_KEEPINTVL and TCP_KEEPCNT are available on Linux 2.4+ and OS X 10.8+ (Mountain Lion).
+#if defined(TCP_KEEPINTVL)
+ if (adb_setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval_sec, sizeof(interval_sec))) {
+ return false;
+ }
+#endif
+
+#if defined(TCP_KEEPCNT)
+ // On Windows this value is hardcoded to 10. This is a reasonable value, so we do the same here
+ // to match behavior. See SO_KEEPALIVE documentation at
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ee470551(v=vs.85).aspx.
+ const int keepcnt = 10;
+ if (adb_setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt))) {
+ return false;
+ }
+#endif
+
+ return true;
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 0b08981..a2f34fb 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -29,6 +29,7 @@
#include <memory>
#include <string>
#include <unordered_map>
+#include <vector>
#include <cutils/sockets.h>
@@ -39,6 +40,7 @@
#include <android-base/utf8.h>
#include "adb.h"
+#include "adb_utils.h"
extern void fatal(const char *fmt, ...);
@@ -54,7 +56,6 @@
int (*_fh_lseek)(FH, int, int);
int (*_fh_read)(FH, void*, int);
int (*_fh_write)(FH, const void*, int);
- void (*_fh_hook)(FH, int, EventHook);
} FHClassRec;
static void _fh_file_init(FH);
@@ -62,7 +63,6 @@
static int _fh_file_lseek(FH, int, int);
static int _fh_file_read(FH, void*, int);
static int _fh_file_write(FH, const void*, int);
-static void _fh_file_hook(FH, int, EventHook);
static const FHClassRec _fh_file_class = {
_fh_file_init,
@@ -70,7 +70,6 @@
_fh_file_lseek,
_fh_file_read,
_fh_file_write,
- _fh_file_hook
};
static void _fh_socket_init(FH);
@@ -78,7 +77,6 @@
static int _fh_socket_lseek(FH, int, int);
static int _fh_socket_read(FH, void*, int);
static int _fh_socket_write(FH, const void*, int);
-static void _fh_socket_hook(FH, int, EventHook);
static const FHClassRec _fh_socket_class = {
_fh_socket_init,
@@ -86,7 +84,6 @@
_fh_socket_lseek,
_fh_socket_read,
_fh_socket_write,
- _fh_socket_hook
};
#define assert(cond) \
@@ -174,9 +171,6 @@
/**************************************************************************/
/**************************************************************************/
-/* used to emulate unix-domain socket pairs */
-typedef struct SocketPairRec_* SocketPair;
-
typedef struct FHRec_
{
FHClass clazz;
@@ -185,10 +179,8 @@
union {
HANDLE handle;
SOCKET socket;
- SocketPair pair;
} u;
- HANDLE event;
int mask;
char name[32];
@@ -197,10 +189,8 @@
#define fh_handle u.handle
#define fh_socket u.socket
-#define fh_pair u.pair
-#define WIN32_FH_BASE 100
-
+#define WIN32_FH_BASE 2048
#define WIN32_MAX_FHS 128
static adb_mutex_t _win32_lock;
@@ -250,17 +240,10 @@
adb_mutex_lock( &_win32_lock );
- // Search entire array, starting from _win32_fh_next.
- for (int nn = 0; nn < WIN32_MAX_FHS; nn++) {
- // Keep incrementing _win32_fh_next to avoid giving out an index that
- // was recently closed, to try to avoid use-after-free.
- const int index = _win32_fh_next++;
- // Handle wrap-around of _win32_fh_next.
- if (_win32_fh_next == WIN32_MAX_FHS) {
- _win32_fh_next = 0;
- }
- if (_win32_fhs[index].clazz == NULL) {
- f = &_win32_fhs[index];
+ for (int i = _win32_fh_next; i < WIN32_MAX_FHS; ++i) {
+ if (_win32_fhs[i].clazz == NULL) {
+ f = &_win32_fhs[i];
+ _win32_fh_next = i + 1;
goto Exit;
}
}
@@ -285,6 +268,12 @@
// Use lock so that closing only happens once and so that _fh_alloc can't
// allocate a FH that we're in the middle of closing.
adb_mutex_lock(&_win32_lock);
+
+ int offset = f - _win32_fhs;
+ if (_win32_fh_next > offset) {
+ _win32_fh_next = offset;
+ }
+
if (f->used) {
f->clazz->_fh_close( f );
f->name[0] = '\0';
@@ -672,19 +661,56 @@
}
}
-static void _fh_socket_init( FH f ) {
- f->fh_socket = INVALID_SOCKET;
- f->event = WSACreateEvent();
- if (f->event == WSA_INVALID_EVENT) {
- D("WSACreateEvent failed: %s",
- android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
-
- // _event_socket_start assumes that this field is INVALID_HANDLE_VALUE
- // on failure, instead of NULL which is what Windows really returns on
- // error. It might be better to change all the other code to look for
- // NULL, but that is a much riskier change.
- f->event = INVALID_HANDLE_VALUE;
+extern int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
+ // WSAPoll doesn't handle invalid/non-socket handles, so we need to handle them ourselves.
+ int skipped = 0;
+ std::vector<WSAPOLLFD> sockets;
+ std::vector<adb_pollfd*> original;
+ for (size_t i = 0; i < nfds; ++i) {
+ FH fh = _fh_from_int(fds[i].fd, __func__);
+ if (!fh || !fh->used || fh->clazz != &_fh_socket_class) {
+ D("adb_poll received bad FD %d", fds[i].fd);
+ fds[i].revents = POLLNVAL;
+ ++skipped;
+ } else {
+ WSAPOLLFD wsapollfd = {
+ .fd = fh->u.socket,
+ .events = static_cast<short>(fds[i].events)
+ };
+ sockets.push_back(wsapollfd);
+ original.push_back(&fds[i]);
+ }
}
+
+ if (sockets.empty()) {
+ return skipped;
+ }
+
+ int result = WSAPoll(sockets.data(), sockets.size(), timeout);
+ if (result == SOCKET_ERROR) {
+ _socket_set_errno(WSAGetLastError());
+ return -1;
+ }
+
+ // Map the results back onto the original set.
+ for (size_t i = 0; i < sockets.size(); ++i) {
+ original[i]->revents = sockets[i].revents;
+ }
+
+ // WSAPoll appears to return the number of unique FDs with avaiable events, instead of how many
+ // of the pollfd elements have a non-zero revents field, which is what it and poll are specified
+ // to do. Ignore its result and calculate the proper return value.
+ result = 0;
+ for (size_t i = 0; i < nfds; ++i) {
+ if (fds[i].revents != 0) {
+ ++result;
+ }
+ }
+ return result;
+}
+
+static void _fh_socket_init(FH f) {
+ f->fh_socket = INVALID_SOCKET;
f->mask = 0;
}
@@ -700,18 +726,12 @@
#endif
}
if (closesocket(f->fh_socket) == SOCKET_ERROR) {
- D("closesocket failed: %s",
- android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+ // Don't set errno here, since adb_close will ignore it.
+ const DWORD err = WSAGetLastError();
+ D("closesocket failed: %s", android::base::SystemErrorCodeToString(err).c_str());
}
f->fh_socket = INVALID_SOCKET;
}
- if (f->event != NULL) {
- if (!CloseHandle(f->event)) {
- D("CloseHandle failed: %s",
- android::base::SystemErrorCodeToString(GetLastError()).c_str());
- }
- f->event = NULL;
- }
f->mask = 0;
return 0;
}
@@ -820,16 +840,15 @@
int network_loopback_client(int port, int type, std::string* error) {
struct sockaddr_in addr;
- SOCKET s;
+ SOCKET s;
- unique_fh f(_fh_alloc(&_fh_socket_class));
+ unique_fh f(_fh_alloc(&_fh_socket_class));
if (!f) {
*error = strerror(errno);
return -1;
}
- if (!_winsock_init)
- _init_winsock();
+ if (!_winsock_init) _init_winsock();
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
@@ -837,30 +856,32 @@
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
s = socket(AF_INET, type, GetSocketProtocolFromSocketType(type));
- if(s == INVALID_SOCKET) {
+ if (s == INVALID_SOCKET) {
+ const DWORD err = WSAGetLastError();
*error = android::base::StringPrintf("cannot create socket: %s",
- android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+ android::base::SystemErrorCodeToString(err).c_str());
D("%s", error->c_str());
+ _socket_set_errno(err);
return -1;
}
f->fh_socket = s;
- if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+ if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
// Save err just in case inet_ntoa() or ntohs() changes the last error.
const DWORD err = WSAGetLastError();
*error = android::base::StringPrintf("cannot connect to %s:%u: %s",
- inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
- android::base::SystemErrorCodeToString(err).c_str());
- D("could not connect to %s:%d: %s",
- type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+ android::base::SystemErrorCodeToString(err).c_str());
+ D("could not connect to %s:%d: %s", type != SOCK_STREAM ? "udp" : "tcp", port,
+ error->c_str());
+ _socket_set_errno(err);
return -1;
}
const int fd = _fh_to_int(f.get());
- snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", fd,
- type != SOCK_STREAM ? "udp:" : "", port );
- D( "port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp",
- fd );
+ snprintf(f->name, sizeof(f->name), "%d(lo-client:%s%d)", fd, type != SOCK_STREAM ? "udp:" : "",
+ port);
+ D("port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp", fd);
f.release();
return fd;
}
@@ -868,20 +889,18 @@
#define LISTEN_BACKLOG 4
// interface_address is INADDR_LOOPBACK or INADDR_ANY.
-static int _network_server(int port, int type, u_long interface_address,
- std::string* error) {
+static int _network_server(int port, int type, u_long interface_address, std::string* error) {
struct sockaddr_in addr;
- SOCKET s;
- int n;
+ SOCKET s;
+ int n;
- unique_fh f(_fh_alloc(&_fh_socket_class));
+ unique_fh f(_fh_alloc(&_fh_socket_class));
if (!f) {
*error = strerror(errno);
return -1;
}
- if (!_winsock_init)
- _init_winsock();
+ if (!_winsock_init) _init_winsock();
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
@@ -892,9 +911,11 @@
// IPv4 and IPv6.
s = socket(AF_INET, type, GetSocketProtocolFromSocketType(type));
if (s == INVALID_SOCKET) {
+ const DWORD err = WSAGetLastError();
*error = android::base::StringPrintf("cannot create socket: %s",
- android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+ android::base::SystemErrorCodeToString(err).c_str());
D("%s", error->c_str());
+ _socket_set_errno(err);
return -1;
}
@@ -903,40 +924,41 @@
// Note: SO_REUSEADDR on Windows allows multiple processes to bind to the
// same port, so instead use SO_EXCLUSIVEADDRUSE.
n = 1;
- if (setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n,
- sizeof(n)) == SOCKET_ERROR) {
- *error = android::base::StringPrintf(
- "cannot set socket option SO_EXCLUSIVEADDRUSE: %s",
- android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+ if (setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n)) == SOCKET_ERROR) {
+ const DWORD err = WSAGetLastError();
+ *error = android::base::StringPrintf("cannot set socket option SO_EXCLUSIVEADDRUSE: %s",
+ android::base::SystemErrorCodeToString(err).c_str());
D("%s", error->c_str());
+ _socket_set_errno(err);
return -1;
}
- if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+ if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
// Save err just in case inet_ntoa() or ntohs() changes the last error.
const DWORD err = WSAGetLastError();
- *error = android::base::StringPrintf("cannot bind to %s:%u: %s",
- inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
- android::base::SystemErrorCodeToString(err).c_str());
- D("could not bind to %s:%d: %s",
- type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+ *error = android::base::StringPrintf("cannot bind to %s:%u: %s", inet_ntoa(addr.sin_addr),
+ ntohs(addr.sin_port),
+ android::base::SystemErrorCodeToString(err).c_str());
+ D("could not bind to %s:%d: %s", type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+ _socket_set_errno(err);
return -1;
}
if (type == SOCK_STREAM) {
if (listen(s, LISTEN_BACKLOG) == SOCKET_ERROR) {
- *error = android::base::StringPrintf("cannot listen on socket: %s",
- android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
- D("could not listen on %s:%d: %s",
- type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+ const DWORD err = WSAGetLastError();
+ *error = android::base::StringPrintf(
+ "cannot listen on socket: %s", android::base::SystemErrorCodeToString(err).c_str());
+ D("could not listen on %s:%d: %s", type != SOCK_STREAM ? "udp" : "tcp", port,
+ error->c_str());
+ _socket_set_errno(err);
return -1;
}
}
const int fd = _fh_to_int(f.get());
- snprintf( f->name, sizeof(f->name), "%d(%s-server:%s%d)", fd,
- interface_address == INADDR_LOOPBACK ? "lo" : "any",
- type != SOCK_STREAM ? "udp:" : "", port );
- D( "port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp",
- fd );
+ snprintf(f->name, sizeof(f->name), "%d(%s-server:%s%d)", fd,
+ interface_address == INADDR_LOOPBACK ? "lo" : "any", type != SOCK_STREAM ? "udp:" : "",
+ port);
+ D("port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp", fd);
f.release();
return fd;
}
@@ -970,54 +992,57 @@
struct addrinfo* addrinfo_ptr = nullptr;
#if (NTDDI_VERSION >= NTDDI_WINXPSP2) || (_WIN32_WINNT >= _WIN32_WINNT_WS03)
- // TODO: When the Android SDK tools increases the Windows system
- // requirements >= WinXP SP2, switch to android::base::UTF8ToWide() + GetAddrInfoW().
+// TODO: When the Android SDK tools increases the Windows system
+// requirements >= WinXP SP2, switch to android::base::UTF8ToWide() + GetAddrInfoW().
#else
- // Otherwise, keep using getaddrinfo(), or do runtime API detection
- // with GetProcAddress("GetAddrInfoW").
+// Otherwise, keep using getaddrinfo(), or do runtime API detection
+// with GetProcAddress("GetAddrInfoW").
#endif
if (getaddrinfo(host.c_str(), port_str, &hints, &addrinfo_ptr) != 0) {
- *error = android::base::StringPrintf(
- "cannot resolve host '%s' and port %s: %s", host.c_str(),
- port_str, android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+ const DWORD err = WSAGetLastError();
+ *error = android::base::StringPrintf("cannot resolve host '%s' and port %s: %s",
+ host.c_str(), port_str,
+ android::base::SystemErrorCodeToString(err).c_str());
+
D("%s", error->c_str());
+ _socket_set_errno(err);
return -1;
}
- std::unique_ptr<struct addrinfo, decltype(freeaddrinfo)*>
- addrinfo(addrinfo_ptr, freeaddrinfo);
+ std::unique_ptr<struct addrinfo, decltype(freeaddrinfo)*> addrinfo(addrinfo_ptr, freeaddrinfo);
addrinfo_ptr = nullptr;
// TODO: Try all the addresses if there's more than one? This just uses
// the first. Or, could call WSAConnectByName() (Windows Vista and newer)
// which tries all addresses, takes a timeout and more.
- SOCKET s = socket(addrinfo->ai_family, addrinfo->ai_socktype,
- addrinfo->ai_protocol);
- if(s == INVALID_SOCKET) {
+ SOCKET s = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
+ if (s == INVALID_SOCKET) {
+ const DWORD err = WSAGetLastError();
*error = android::base::StringPrintf("cannot create socket: %s",
- android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+ android::base::SystemErrorCodeToString(err).c_str());
D("%s", error->c_str());
+ _socket_set_errno(err);
return -1;
}
f->fh_socket = s;
// TODO: Implement timeouts for Windows. Seems like the default in theory
// (according to http://serverfault.com/a/671453) and in practice is 21 sec.
- if(connect(s, addrinfo->ai_addr, addrinfo->ai_addrlen) == SOCKET_ERROR) {
+ if (connect(s, addrinfo->ai_addr, addrinfo->ai_addrlen) == SOCKET_ERROR) {
// TODO: Use WSAAddressToString or inet_ntop on address.
- *error = android::base::StringPrintf("cannot connect to %s:%s: %s",
- host.c_str(), port_str,
- android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
- D("could not connect to %s:%s:%s: %s",
- type != SOCK_STREAM ? "udp" : "tcp", host.c_str(), port_str,
- error->c_str());
+ const DWORD err = WSAGetLastError();
+ *error = android::base::StringPrintf("cannot connect to %s:%s: %s", host.c_str(), port_str,
+ android::base::SystemErrorCodeToString(err).c_str());
+ D("could not connect to %s:%s:%s: %s", type != SOCK_STREAM ? "udp" : "tcp", host.c_str(),
+ port_str, error->c_str());
+ _socket_set_errno(err);
return -1;
}
const int fd = _fh_to_int(f.get());
- snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", fd,
- type != SOCK_STREAM ? "udp:" : "", port );
- D( "host '%s' port %d type %s => fd %d", host.c_str(), port,
- type != SOCK_STREAM ? "udp" : "tcp", fd );
+ snprintf(f->name, sizeof(f->name), "%d(net-client:%s%d)", fd, type != SOCK_STREAM ? "udp:" : "",
+ port);
+ D("host '%s' port %d type %s => fd %d", host.c_str(), port, type != SOCK_STREAM ? "udp" : "tcp",
+ fd);
f.release();
return fd;
}
@@ -1083,6 +1108,25 @@
return result;
}
+int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen) {
+ FH fh = _fh_from_int(fd, __func__);
+
+ if (!fh || fh->clazz != &_fh_socket_class) {
+ D("adb_getsockname: invalid fd %d", fd);
+ errno = EBADF;
+ return -1;
+ }
+
+ int result = getsockname(fh->fh_socket, sockaddr, optlen);
+ if (result == SOCKET_ERROR) {
+ const DWORD err = WSAGetLastError();
+ D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
+ android::base::SystemErrorCodeToString(err).c_str());
+ _socket_set_errno(err);
+ result = -1;
+ }
+ return result;
+}
int adb_shutdown(int fd)
{
@@ -1105,1352 +1149,112 @@
return 0;
}
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** emulated socketpairs *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
+// Emulate socketpair(2) by binding and connecting to a socket.
+int adb_socketpair(int sv[2]) {
+ int server = -1;
+ int client = -1;
+ int accepted = -1;
+ sockaddr_storage addr_storage;
+ socklen_t addr_len = sizeof(addr_storage);
+ sockaddr_in* addr = nullptr;
+ std::string error;
-/* we implement socketpairs directly in use space for the following reasons:
- * - it avoids copying data from/to the Nt kernel
- * - it allows us to implement fdevent hooks easily and cheaply, something
- * that is not possible with standard Win32 pipes !!
- *
- * basically, we use two circular buffers, each one corresponding to a given
- * direction.
- *
- * each buffer is implemented as two regions:
- *
- * region A which is (a_start,a_end)
- * region B which is (0, b_end) with b_end <= a_start
- *
- * an empty buffer has: a_start = a_end = b_end = 0
- *
- * a_start is the pointer where we start reading data
- * a_end is the pointer where we start writing data, unless it is BUFFER_SIZE,
- * then you start writing at b_end
- *
- * the buffer is full when b_end == a_start && a_end == BUFFER_SIZE
- *
- * there is room when b_end < a_start || a_end < BUFER_SIZE
- *
- * when reading, a_start is incremented, it a_start meets a_end, then
- * we do: a_start = 0, a_end = b_end, b_end = 0, and keep going on..
- */
-
-#define BIP_BUFFER_SIZE 4096
-
-#if 0
-#include <stdio.h>
-# define BIPD(x) D x
-# define BIPDUMP bip_dump_hex
-
-static void bip_dump_hex( const unsigned char* ptr, size_t len )
-{
- int nn, len2 = len;
-
- if (len2 > 8) len2 = 8;
-
- for (nn = 0; nn < len2; nn++)
- printf("%02x", ptr[nn]);
- printf(" ");
-
- for (nn = 0; nn < len2; nn++) {
- int c = ptr[nn];
- if (c < 32 || c > 127)
- c = '.';
- printf("%c", c);
- }
- printf("\n");
- fflush(stdout);
-}
-
-#else
-# define BIPD(x) do {} while (0)
-# define BIPDUMP(p,l) BIPD(p)
-#endif
-
-typedef struct BipBufferRec_
-{
- int a_start;
- int a_end;
- int b_end;
- int fdin;
- int fdout;
- int closed;
- int can_write; /* boolean */
- HANDLE evt_write; /* event signaled when one can write to a buffer */
- int can_read; /* boolean */
- HANDLE evt_read; /* event signaled when one can read from a buffer */
- CRITICAL_SECTION lock;
- unsigned char buff[ BIP_BUFFER_SIZE ];
-
-} BipBufferRec, *BipBuffer;
-
-static void
-bip_buffer_init( BipBuffer buffer )
-{
- D( "bit_buffer_init %p", buffer );
- buffer->a_start = 0;
- buffer->a_end = 0;
- buffer->b_end = 0;
- buffer->can_write = 1;
- buffer->can_read = 0;
- buffer->fdin = 0;
- buffer->fdout = 0;
- buffer->closed = 0;
- buffer->evt_write = CreateEvent( NULL, TRUE, TRUE, NULL );
- buffer->evt_read = CreateEvent( NULL, TRUE, FALSE, NULL );
- InitializeCriticalSection( &buffer->lock );
-}
-
-static void
-bip_buffer_close( BipBuffer bip )
-{
- bip->closed = 1;
-
- if (!bip->can_read) {
- SetEvent( bip->evt_read );
- }
- if (!bip->can_write) {
- SetEvent( bip->evt_write );
- }
-}
-
-static void
-bip_buffer_done( BipBuffer bip )
-{
- BIPD(( "bip_buffer_done: %d->%d", bip->fdin, bip->fdout ));
- CloseHandle( bip->evt_read );
- CloseHandle( bip->evt_write );
- DeleteCriticalSection( &bip->lock );
-}
-
-static int
-bip_buffer_write( BipBuffer bip, const void* src, int len )
-{
- int avail, count = 0;
-
- if (len <= 0)
- return 0;
-
- BIPD(( "bip_buffer_write: enter %d->%d len %d", bip->fdin, bip->fdout, len ));
- BIPDUMP( src, len );
-
- if (bip->closed) {
- errno = EPIPE;
- return -1;
+ server = network_loopback_server(0, SOCK_STREAM, &error);
+ if (server < 0) {
+ D("adb_socketpair: failed to create server: %s", error.c_str());
+ goto fail;
}
- EnterCriticalSection( &bip->lock );
-
- while (!bip->can_write) {
- int ret;
- LeaveCriticalSection( &bip->lock );
-
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
- /* spinlocking here is probably unfair, but let's live with it */
- ret = WaitForSingleObject( bip->evt_write, INFINITE );
- if (ret != WAIT_OBJECT_0) { /* buffer probably closed */
- D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld", bip->fdin, bip->fdout, ret, GetLastError() );
- return 0;
- }
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
- EnterCriticalSection( &bip->lock );
+ if (adb_getsockname(server, reinterpret_cast<sockaddr*>(&addr_storage), &addr_len) < 0) {
+ D("adb_socketpair: adb_getsockname failed: %s", strerror(errno));
+ goto fail;
}
- BIPD(( "bip_buffer_write: exec %d->%d len %d", bip->fdin, bip->fdout, len ));
-
- avail = BIP_BUFFER_SIZE - bip->a_end;
- if (avail > 0)
- {
- /* we can append to region A */
- if (avail > len)
- avail = len;
-
- memcpy( bip->buff + bip->a_end, src, avail );
- src = (const char *)src + avail;
- count += avail;
- len -= avail;
-
- bip->a_end += avail;
- if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) {
- bip->can_write = 0;
- ResetEvent( bip->evt_write );
- goto Exit;
- }
+ if (addr_storage.ss_family != AF_INET) {
+ D("adb_socketpair: unknown address family received: %d", addr_storage.ss_family);
+ errno = ECONNABORTED;
+ goto fail;
}
- if (len == 0)
- goto Exit;
-
- avail = bip->a_start - bip->b_end;
- assert( avail > 0 ); /* since can_write is TRUE */
-
- if (avail > len)
- avail = len;
-
- memcpy( bip->buff + bip->b_end, src, avail );
- count += avail;
- bip->b_end += avail;
-
- if (bip->b_end == bip->a_start) {
- bip->can_write = 0;
- ResetEvent( bip->evt_write );
+ addr = reinterpret_cast<sockaddr_in*>(&addr_storage);
+ D("adb_socketpair: bound on port %d", ntohs(addr->sin_port));
+ client = network_loopback_client(ntohs(addr->sin_port), SOCK_STREAM, &error);
+ if (client < 0) {
+ D("adb_socketpair: failed to connect client: %s", error.c_str());
+ goto fail;
}
-Exit:
- assert( count > 0 );
-
- if ( !bip->can_read ) {
- bip->can_read = 1;
- SetEvent( bip->evt_read );
+ accepted = adb_socket_accept(server, nullptr, nullptr);
+ if (accepted < 0) {
+ D("adb_socketpair: failed to accept: %s", strerror(errno));
+ goto fail;
}
-
- BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d",
- bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
- LeaveCriticalSection( &bip->lock );
-
- return count;
- }
-
-static int
-bip_buffer_read( BipBuffer bip, void* dst, int len )
-{
- int avail, count = 0;
-
- if (len <= 0)
- return 0;
-
- BIPD(( "bip_buffer_read: enter %d->%d len %d", bip->fdin, bip->fdout, len ));
-
- EnterCriticalSection( &bip->lock );
- while ( !bip->can_read )
- {
-#if 0
- LeaveCriticalSection( &bip->lock );
- errno = EAGAIN;
- return -1;
-#else
- int ret;
- LeaveCriticalSection( &bip->lock );
-
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
-
- ret = WaitForSingleObject( bip->evt_read, INFINITE );
- if (ret != WAIT_OBJECT_0) { /* probably closed buffer */
- D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld", bip->fdin, bip->fdout, ret, GetLastError());
- return 0;
- }
- if (bip->closed) {
- errno = EPIPE;
- return -1;
- }
- EnterCriticalSection( &bip->lock );
-#endif
- }
-
- BIPD(( "bip_buffer_read: exec %d->%d len %d", bip->fdin, bip->fdout, len ));
-
- avail = bip->a_end - bip->a_start;
- assert( avail > 0 ); /* since can_read is TRUE */
-
- if (avail > len)
- avail = len;
-
- memcpy( dst, bip->buff + bip->a_start, avail );
- dst = (char *)dst + avail;
- count += avail;
- len -= avail;
-
- bip->a_start += avail;
- if (bip->a_start < bip->a_end)
- goto Exit;
-
- bip->a_start = 0;
- bip->a_end = bip->b_end;
- bip->b_end = 0;
-
- avail = bip->a_end;
- if (avail > 0) {
- if (avail > len)
- avail = len;
- memcpy( dst, bip->buff, avail );
- count += avail;
- bip->a_start += avail;
-
- if ( bip->a_start < bip->a_end )
- goto Exit;
-
- bip->a_start = bip->a_end = 0;
- }
-
- bip->can_read = 0;
- ResetEvent( bip->evt_read );
-
-Exit:
- assert( count > 0 );
-
- if (!bip->can_write ) {
- bip->can_write = 1;
- SetEvent( bip->evt_write );
- }
-
- BIPDUMP( (const unsigned char*)dst - count, count );
- BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d",
- bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
- LeaveCriticalSection( &bip->lock );
-
- return count;
-}
-
-typedef struct SocketPairRec_
-{
- BipBufferRec a2b_bip;
- BipBufferRec b2a_bip;
- FH a_fd;
- int used;
-
-} SocketPairRec;
-
-void _fh_socketpair_init( FH f )
-{
- f->fh_pair = NULL;
-}
-
-static int
-_fh_socketpair_close( FH f )
-{
- if ( f->fh_pair ) {
- SocketPair pair = f->fh_pair;
-
- if ( f == pair->a_fd ) {
- pair->a_fd = NULL;
- }
-
- bip_buffer_close( &pair->b2a_bip );
- bip_buffer_close( &pair->a2b_bip );
-
- if ( --pair->used == 0 ) {
- bip_buffer_done( &pair->b2a_bip );
- bip_buffer_done( &pair->a2b_bip );
- free( pair );
- }
- f->fh_pair = NULL;
- }
+ adb_close(server);
+ sv[0] = client;
+ sv[1] = accepted;
return 0;
-}
-static int
-_fh_socketpair_lseek( FH f, int pos, int origin )
-{
- errno = ESPIPE;
+fail:
+ if (server >= 0) {
+ adb_close(server);
+ }
+ if (client >= 0) {
+ adb_close(client);
+ }
+ if (accepted >= 0) {
+ adb_close(accepted);
+ }
return -1;
}
-static int
-_fh_socketpair_read( FH f, void* buf, int len )
-{
- SocketPair pair = f->fh_pair;
- BipBuffer bip;
+bool set_file_block_mode(int fd, bool block) {
+ FH fh = _fh_from_int(fd, __func__);
- if (!pair)
- return -1;
-
- if ( f == pair->a_fd )
- bip = &pair->b2a_bip;
- else
- bip = &pair->a2b_bip;
-
- return bip_buffer_read( bip, buf, len );
-}
-
-static int
-_fh_socketpair_write( FH f, const void* buf, int len )
-{
- SocketPair pair = f->fh_pair;
- BipBuffer bip;
-
- if (!pair)
- return -1;
-
- if ( f == pair->a_fd )
- bip = &pair->a2b_bip;
- else
- bip = &pair->b2a_bip;
-
- return bip_buffer_write( bip, buf, len );
-}
-
-
-static void _fh_socketpair_hook( FH f, int event, EventHook hook ); /* forward */
-
-static const FHClassRec _fh_socketpair_class =
-{
- _fh_socketpair_init,
- _fh_socketpair_close,
- _fh_socketpair_lseek,
- _fh_socketpair_read,
- _fh_socketpair_write,
- _fh_socketpair_hook
-};
-
-
-int adb_socketpair(int sv[2]) {
- SocketPair pair;
-
- unique_fh fa(_fh_alloc(&_fh_socketpair_class));
- if (!fa) {
- return -1;
- }
- unique_fh fb(_fh_alloc(&_fh_socketpair_class));
- if (!fb) {
- return -1;
+ if (!fh || !fh->used) {
+ errno = EBADF;
+ return false;
}
- pair = reinterpret_cast<SocketPair>(malloc(sizeof(*pair)));
- if (pair == NULL) {
- D("adb_socketpair: not enough memory to allocate pipes" );
- return -1;
- }
-
- bip_buffer_init( &pair->a2b_bip );
- bip_buffer_init( &pair->b2a_bip );
-
- fa->fh_pair = pair;
- fb->fh_pair = pair;
- pair->used = 2;
- pair->a_fd = fa.get();
-
- sv[0] = _fh_to_int(fa.get());
- sv[1] = _fh_to_int(fb.get());
-
- pair->a2b_bip.fdin = sv[0];
- pair->a2b_bip.fdout = sv[1];
- pair->b2a_bip.fdin = sv[1];
- pair->b2a_bip.fdout = sv[0];
-
- snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
- snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
- D( "adb_socketpair: returns (%d, %d)", sv[0], sv[1] );
- fa.release();
- fb.release();
- return 0;
-}
-
-/**************************************************************************/
-/**************************************************************************/
-/***** *****/
-/***** fdevents emulation *****/
-/***** *****/
-/***** this is a very simple implementation, we rely on the fact *****/
-/***** that ADB doesn't use FDE_ERROR. *****/
-/***** *****/
-/**************************************************************************/
-/**************************************************************************/
-
-#define FATAL(fmt, ...) fatal("%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
-
-#if DEBUG
-static void dump_fde(fdevent *fde, const char *info)
-{
- fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
- fde->state & FDE_READ ? 'R' : ' ',
- fde->state & FDE_WRITE ? 'W' : ' ',
- fde->state & FDE_ERROR ? 'E' : ' ',
- info);
-}
-#else
-#define dump_fde(fde, info) do { } while(0)
-#endif
-
-#define FDE_EVENTMASK 0x00ff
-#define FDE_STATEMASK 0xff00
-
-#define FDE_ACTIVE 0x0100
-#define FDE_PENDING 0x0200
-#define FDE_CREATED 0x0400
-
-static void fdevent_plist_enqueue(fdevent *node);
-static void fdevent_plist_remove(fdevent *node);
-static fdevent *fdevent_plist_dequeue(void);
-
-static fdevent list_pending = {
- .next = &list_pending,
- .prev = &list_pending,
-};
-
-static fdevent **fd_table = 0;
-static int fd_table_max = 0;
-
-typedef struct EventLooperRec_* EventLooper;
-
-typedef struct EventHookRec_
-{
- EventHook next;
- FH fh;
- HANDLE h;
- int wanted; /* wanted event flags */
- int ready; /* ready event flags */
- void* aux;
- void (*prepare)( EventHook hook );
- int (*start) ( EventHook hook );
- void (*stop) ( EventHook hook );
- int (*check) ( EventHook hook );
- int (*peek) ( EventHook hook );
-} EventHookRec;
-
-static EventHook _free_hooks;
-
-static EventHook
-event_hook_alloc(FH fh) {
- EventHook hook = _free_hooks;
- if (hook != NULL) {
- _free_hooks = hook->next;
+ if (fh->clazz == &_fh_socket_class) {
+ u_long x = !block;
+ if (ioctlsocket(fh->u.socket, FIONBIO, &x) != 0) {
+ _socket_set_errno(WSAGetLastError());
+ return false;
+ }
+ return true;
} else {
- hook = reinterpret_cast<EventHook>(malloc(sizeof(*hook)));
- if (hook == NULL)
- fatal( "could not allocate event hook\n" );
- }
- hook->next = NULL;
- hook->fh = fh;
- hook->wanted = 0;
- hook->ready = 0;
- hook->h = INVALID_HANDLE_VALUE;
- hook->aux = NULL;
-
- hook->prepare = NULL;
- hook->start = NULL;
- hook->stop = NULL;
- hook->check = NULL;
- hook->peek = NULL;
-
- return hook;
-}
-
-static void
-event_hook_free( EventHook hook )
-{
- hook->fh = NULL;
- hook->wanted = 0;
- hook->ready = 0;
- hook->next = _free_hooks;
- _free_hooks = hook;
-}
-
-
-static void
-event_hook_signal( EventHook hook )
-{
- FH f = hook->fh;
- int fd = _fh_to_int(f);
- fdevent* fde = fd_table[ fd - WIN32_FH_BASE ];
-
- if (fde != NULL && fde->fd == fd) {
- if ((fde->state & FDE_PENDING) == 0) {
- fde->state |= FDE_PENDING;
- fdevent_plist_enqueue( fde );
- }
- fde->events |= hook->wanted;
+ errno = ENOTSOCK;
+ return false;
}
}
+bool set_tcp_keepalive(int fd, int interval_sec) {
+ FH fh = _fh_from_int(fd, __func__);
-#define MAX_LOOPER_HANDLES WIN32_MAX_FHS
-
-typedef struct EventLooperRec_
-{
- EventHook hooks;
- HANDLE htab[ MAX_LOOPER_HANDLES ];
- int htab_count;
-
-} EventLooperRec;
-
-static EventHook*
-event_looper_find_p( EventLooper looper, FH fh )
-{
- EventHook *pnode = &looper->hooks;
- EventHook node = *pnode;
- for (;;) {
- if ( node == NULL || node->fh == fh )
- break;
- pnode = &node->next;
- node = *pnode;
- }
- return pnode;
-}
-
-static void
-event_looper_hook( EventLooper looper, int fd, int events )
-{
- FH f = _fh_from_int(fd, __func__);
- EventHook *pnode;
- EventHook node;
-
- if (f == NULL) /* invalid arg */ {
- D("event_looper_hook: invalid fd=%d", fd);
- return;
+ if (!fh || fh->clazz != &_fh_socket_class) {
+ D("set_tcp_keepalive(%d) failed: invalid fd", fd);
+ errno = EBADF;
+ return false;
}
- pnode = event_looper_find_p( looper, f );
- node = *pnode;
- if ( node == NULL ) {
- node = event_hook_alloc( f );
- node->next = *pnode;
- *pnode = node;
+ tcp_keepalive keepalive;
+ keepalive.onoff = (interval_sec > 0);
+ keepalive.keepalivetime = interval_sec * 1000;
+ keepalive.keepaliveinterval = interval_sec * 1000;
+
+ DWORD bytes_returned = 0;
+ if (WSAIoctl(fh->fh_socket, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive), nullptr, 0,
+ &bytes_returned, nullptr, nullptr) != 0) {
+ const DWORD err = WSAGetLastError();
+ D("set_tcp_keepalive(%d) failed: %s", fd,
+ android::base::SystemErrorCodeToString(err).c_str());
+ _socket_set_errno(err);
+ return false;
}
- if ( (node->wanted & events) != events ) {
- /* this should update start/stop/check/peek */
- D("event_looper_hook: call hook for %d (new=%x, old=%x)",
- fd, node->wanted, events);
- f->clazz->_fh_hook( f, events & ~node->wanted, node );
- node->wanted |= events;
- } else {
- D("event_looper_hook: ignoring events %x for %d wanted=%x)",
- events, fd, node->wanted);
- }
+ return true;
}
-static void
-event_looper_unhook( EventLooper looper, int fd, int events )
-{
- FH fh = _fh_from_int(fd, __func__);
- EventHook *pnode = event_looper_find_p( looper, fh );
- EventHook node = *pnode;
-
- if (node != NULL) {
- int events2 = events & node->wanted;
- if ( events2 == 0 ) {
- D( "event_looper_unhook: events %x not registered for fd %d", events, fd );
- return;
- }
- node->wanted &= ~events2;
- if (!node->wanted) {
- *pnode = node->next;
- event_hook_free( node );
- }
- }
-}
-
-/*
- * A fixer for WaitForMultipleObjects on condition that there are more than 64
- * handles to wait on.
- *
- * In cetain cases DDMS may establish more than 64 connections with ADB. For
- * instance, this may happen if there are more than 64 processes running on a
- * device, or there are multiple devices connected (including the emulator) with
- * the combined number of running processes greater than 64. In this case using
- * WaitForMultipleObjects to wait on connection events simply wouldn't cut,
- * because of the API limitations (64 handles max). So, we need to provide a way
- * to scale WaitForMultipleObjects to accept an arbitrary number of handles. The
- * easiest (and "Microsoft recommended") way to do that would be dividing the
- * handle array into chunks with the chunk size less than 64, and fire up as many
- * waiting threads as there are chunks. Then each thread would wait on a chunk of
- * handles, and will report back to the caller which handle has been set.
- * Here is the implementation of that algorithm.
- */
-
-/* Number of handles to wait on in each wating thread. */
-#define WAIT_ALL_CHUNK_SIZE 63
-
-/* Descriptor for a wating thread */
-typedef struct WaitForAllParam {
- /* A handle to an event to signal when waiting is over. This handle is shared
- * accross all the waiting threads, so each waiting thread knows when any
- * other thread has exited, so it can exit too. */
- HANDLE main_event;
- /* Upon exit from a waiting thread contains the index of the handle that has
- * been signaled. The index is an absolute index of the signaled handle in
- * the original array. This pointer is shared accross all the waiting threads
- * and it's not guaranteed (due to a race condition) that when all the
- * waiting threads exit, the value contained here would indicate the first
- * handle that was signaled. This is fine, because the caller cares only
- * about any handle being signaled. It doesn't care about the order, nor
- * about the whole list of handles that were signaled. */
- LONG volatile *signaled_index;
- /* Array of handles to wait on in a waiting thread. */
- HANDLE* handles;
- /* Number of handles in 'handles' array to wait on. */
- int handles_count;
- /* Index inside the main array of the first handle in the 'handles' array. */
- int first_handle_index;
- /* Waiting thread handle. */
- HANDLE thread;
-} WaitForAllParam;
-
-/* Waiting thread routine. */
-static unsigned __stdcall
-_in_waiter_thread(void* arg)
-{
- HANDLE wait_on[WAIT_ALL_CHUNK_SIZE + 1];
- int res;
- WaitForAllParam* const param = (WaitForAllParam*)arg;
-
- /* We have to wait on the main_event in order to be notified when any of the
- * sibling threads is exiting. */
- wait_on[0] = param->main_event;
- /* The rest of the handles go behind the main event handle. */
- memcpy(wait_on + 1, param->handles, param->handles_count * sizeof(HANDLE));
-
- res = WaitForMultipleObjects(param->handles_count + 1, wait_on, FALSE, INFINITE);
- if (res > 0 && res < (param->handles_count + 1)) {
- /* One of the original handles got signaled. Save its absolute index into
- * the output variable. */
- InterlockedCompareExchange(param->signaled_index,
- res - 1L + param->first_handle_index, -1L);
- }
-
- /* Notify the caller (and the siblings) that the wait is over. */
- SetEvent(param->main_event);
-
- _endthreadex(0);
- return 0;
-}
-
-/* WaitForMultipeObjects fixer routine.
- * Param:
- * handles Array of handles to wait on.
- * handles_count Number of handles in the array.
- * Return:
- * (>= 0 && < handles_count) - Index of the signaled handle in the array, or
- * WAIT_FAILED on an error.
- */
-static int
-_wait_for_all(HANDLE* handles, int handles_count)
-{
- WaitForAllParam* threads;
- HANDLE main_event;
- int chunks, chunk, remains;
-
- /* This variable is going to be accessed by several threads at the same time,
- * this is bound to fail randomly when the core is run on multi-core machines.
- * To solve this, we need to do the following (1 _and_ 2):
- * 1. Use the "volatile" qualifier to ensure the compiler doesn't optimize
- * out the reads/writes in this function unexpectedly.
- * 2. Ensure correct memory ordering. The "simple" way to do that is to wrap
- * all accesses inside a critical section. But we can also use
- * InterlockedCompareExchange() which always provide a full memory barrier
- * on Win32.
- */
- volatile LONG sig_index = -1;
-
- /* Calculate number of chunks, and allocate thread param array. */
- chunks = handles_count / WAIT_ALL_CHUNK_SIZE;
- remains = handles_count % WAIT_ALL_CHUNK_SIZE;
- threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) *
- sizeof(WaitForAllParam));
- if (threads == NULL) {
- D("Unable to allocate thread array for %d handles.", handles_count);
- return (int)WAIT_FAILED;
- }
-
- /* Create main event to wait on for all waiting threads. This is a "manualy
- * reset" event that will remain set once it was set. */
- main_event = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (main_event == NULL) {
- D("Unable to create main event. Error: %ld", GetLastError());
- free(threads);
- return (int)WAIT_FAILED;
- }
-
- /*
- * Initialize waiting thread parameters.
- */
-
- for (chunk = 0; chunk < chunks; chunk++) {
- threads[chunk].main_event = main_event;
- threads[chunk].signaled_index = &sig_index;
- threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
- threads[chunk].handles = handles + threads[chunk].first_handle_index;
- threads[chunk].handles_count = WAIT_ALL_CHUNK_SIZE;
- }
- if (remains) {
- threads[chunk].main_event = main_event;
- threads[chunk].signaled_index = &sig_index;
- threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
- threads[chunk].handles = handles + threads[chunk].first_handle_index;
- threads[chunk].handles_count = remains;
- chunks++;
- }
-
- /* Start the waiting threads. */
- for (chunk = 0; chunk < chunks; chunk++) {
- /* Note that using adb_thread_create is not appropriate here, since we
- * need a handle to wait on for thread termination. */
- threads[chunk].thread = (HANDLE)_beginthreadex(NULL, 0, _in_waiter_thread,
- &threads[chunk], 0, NULL);
- if (threads[chunk].thread == NULL) {
- /* Unable to create a waiter thread. Collapse. */
- D("Unable to create a waiting thread %d of %d. errno=%d",
- chunk, chunks, errno);
- chunks = chunk;
- SetEvent(main_event);
- break;
- }
- }
-
- /* Wait on any of the threads to get signaled. */
- WaitForSingleObject(main_event, INFINITE);
-
- /* Wait on all the waiting threads to exit. */
- for (chunk = 0; chunk < chunks; chunk++) {
- WaitForSingleObject(threads[chunk].thread, INFINITE);
- CloseHandle(threads[chunk].thread);
- }
-
- CloseHandle(main_event);
- free(threads);
-
-
- const int ret = (int)InterlockedCompareExchange(&sig_index, -1, -1);
- return (ret >= 0) ? ret : (int)WAIT_FAILED;
-}
-
-static EventLooperRec win32_looper;
-
-static void fdevent_init(void)
-{
- win32_looper.htab_count = 0;
- win32_looper.hooks = NULL;
-}
-
-static void fdevent_connect(fdevent *fde)
-{
- EventLooper looper = &win32_looper;
- int events = fde->state & FDE_EVENTMASK;
-
- if (events != 0)
- event_looper_hook( looper, fde->fd, events );
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
- EventLooper looper = &win32_looper;
- int events = fde->state & FDE_EVENTMASK;
-
- if (events != 0)
- event_looper_unhook( looper, fde->fd, events );
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
- EventLooper looper = &win32_looper;
- unsigned events0 = fde->state & FDE_EVENTMASK;
-
- if (events != events0) {
- int removes = events0 & ~events;
- int adds = events & ~events0;
- if (removes) {
- D("fdevent_update: remove %x from %d", removes, fde->fd);
- event_looper_unhook( looper, fde->fd, removes );
- }
- if (adds) {
- D("fdevent_update: add %x to %d", adds, fde->fd);
- event_looper_hook ( looper, fde->fd, adds );
- }
- }
-}
-
-static void fdevent_process()
-{
- EventLooper looper = &win32_looper;
- EventHook hook;
- int gotone = 0;
-
- /* if we have at least one ready hook, execute it/them */
- for (hook = looper->hooks; hook; hook = hook->next) {
- hook->ready = 0;
- if (hook->prepare) {
- hook->prepare(hook);
- if (hook->ready != 0) {
- event_hook_signal( hook );
- gotone = 1;
- }
- }
- }
-
- /* nothing's ready yet, so wait for something to happen */
- if (!gotone)
- {
- looper->htab_count = 0;
-
- for (hook = looper->hooks; hook; hook = hook->next)
- {
- if (hook->start && !hook->start(hook)) {
- D( "fdevent_process: error when starting a hook" );
- return;
- }
- if (hook->h != INVALID_HANDLE_VALUE) {
- int nn;
-
- for (nn = 0; nn < looper->htab_count; nn++)
- {
- if ( looper->htab[nn] == hook->h )
- goto DontAdd;
- }
- looper->htab[ looper->htab_count++ ] = hook->h;
- DontAdd:
- ;
- }
- }
-
- if (looper->htab_count == 0) {
- D( "fdevent_process: nothing to wait for !!" );
- return;
- }
-
- do
- {
- int wait_ret;
-
- D( "adb_win32: waiting for %d events", looper->htab_count );
- if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
- D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.", looper->htab_count);
- wait_ret = _wait_for_all(looper->htab, looper->htab_count);
- } else {
- wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE );
- }
- if (wait_ret == (int)WAIT_FAILED) {
- D( "adb_win32: wait failed, error %ld", GetLastError() );
- } else {
- D( "adb_win32: got one (index %d)", wait_ret );
-
- /* according to Cygwin, some objects like consoles wake up on "inappropriate" events
- * like mouse movements. we need to filter these with the "check" function
- */
- if ((unsigned)wait_ret < (unsigned)looper->htab_count)
- {
- for (hook = looper->hooks; hook; hook = hook->next)
- {
- if ( looper->htab[wait_ret] == hook->h &&
- (!hook->check || hook->check(hook)) )
- {
- D( "adb_win32: signaling %s for %x", hook->fh->name, hook->ready );
- event_hook_signal( hook );
- gotone = 1;
- break;
- }
- }
- }
- }
- }
- while (!gotone);
-
- for (hook = looper->hooks; hook; hook = hook->next) {
- if (hook->stop)
- hook->stop( hook );
- }
- }
-
- for (hook = looper->hooks; hook; hook = hook->next) {
- if (hook->peek && hook->peek(hook))
- event_hook_signal( hook );
- }
-}
-
-
-static void fdevent_register(fdevent *fde)
-{
- int fd = fde->fd - WIN32_FH_BASE;
-
- if(fd < 0) {
- FATAL("bogus negative fd (%d)\n", fde->fd);
- }
-
- if(fd >= fd_table_max) {
- int oldmax = fd_table_max;
- if(fde->fd > 32000) {
- FATAL("bogus huuuuge fd (%d)\n", fde->fd);
- }
- if(fd_table_max == 0) {
- fdevent_init();
- fd_table_max = 256;
- }
- while(fd_table_max <= fd) {
- fd_table_max *= 2;
- }
- fd_table = reinterpret_cast<fdevent**>(realloc(fd_table, sizeof(fdevent*) * fd_table_max));
- if(fd_table == 0) {
- FATAL("could not expand fd_table to %d entries\n", fd_table_max);
- }
- memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
- }
-
- fd_table[fd] = fde;
-}
-
-static void fdevent_unregister(fdevent *fde)
-{
- int fd = fde->fd - WIN32_FH_BASE;
-
- if((fd < 0) || (fd >= fd_table_max)) {
- FATAL("fd out of range (%d)\n", fde->fd);
- }
-
- if(fd_table[fd] != fde) {
- FATAL("fd_table out of sync");
- }
-
- fd_table[fd] = 0;
-
- if(!(fde->state & FDE_DONT_CLOSE)) {
- dump_fde(fde, "close");
- adb_close(fde->fd);
- }
-}
-
-static void fdevent_plist_enqueue(fdevent *node)
-{
- fdevent *list = &list_pending;
-
- node->next = list;
- node->prev = list->prev;
- node->prev->next = node;
- list->prev = node;
-}
-
-static void fdevent_plist_remove(fdevent *node)
-{
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->next = 0;
- node->prev = 0;
-}
-
-static fdevent *fdevent_plist_dequeue(void)
-{
- fdevent *list = &list_pending;
- fdevent *node = list->next;
-
- if(node == list) return 0;
-
- list->next = node->next;
- list->next->prev = list;
- node->next = 0;
- node->prev = 0;
-
- return node;
-}
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
- fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
- if(fde == 0) return 0;
- fdevent_install(fde, fd, func, arg);
- fde->state |= FDE_CREATED;
- return fde;
-}
-
-void fdevent_destroy(fdevent *fde)
-{
- if(fde == 0) return;
- if(!(fde->state & FDE_CREATED)) {
- FATAL("fde %p not created by fdevent_create()\n", fde);
- }
- fdevent_remove(fde);
-}
-
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
-{
- memset(fde, 0, sizeof(fdevent));
- fde->state = FDE_ACTIVE;
- fde->fd = fd;
- fde->func = func;
- fde->arg = arg;
-
- fdevent_register(fde);
- dump_fde(fde, "connect");
- fdevent_connect(fde);
- fde->state |= FDE_ACTIVE;
-}
-
-void fdevent_remove(fdevent *fde)
-{
- if(fde->state & FDE_PENDING) {
- fdevent_plist_remove(fde);
- }
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_disconnect(fde);
- dump_fde(fde, "disconnect");
- fdevent_unregister(fde);
- }
-
- fde->state = 0;
- fde->events = 0;
-}
-
-
-void fdevent_set(fdevent *fde, unsigned events)
-{
- events &= FDE_EVENTMASK;
-
- if((fde->state & FDE_EVENTMASK) == (int)events) return;
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_update(fde, events);
- dump_fde(fde, "update");
- }
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-
- if(fde->state & FDE_PENDING) {
- /* if we're pending, make sure
- ** we don't signal an event that
- ** is no longer wanted.
- */
- fde->events &= (~events);
- if(fde->events == 0) {
- fdevent_plist_remove(fde);
- fde->state &= (~FDE_PENDING);
- }
- }
-}
-
-void fdevent_add(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
-}
-
-void fdevent_del(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
-}
-
-void fdevent_loop()
-{
- fdevent *fde;
-
- for(;;) {
-#if DEBUG
- fprintf(stderr,"--- ---- waiting for events\n");
-#endif
- fdevent_process();
-
- while((fde = fdevent_plist_dequeue())) {
- unsigned events = fde->events;
- fde->events = 0;
- fde->state &= (~FDE_PENDING);
- dump_fde(fde, "callback");
- fde->func(fde->fd, events, fde->arg);
- }
- }
-}
-
-/** FILE EVENT HOOKS
- **/
-
-static void _event_file_prepare( EventHook hook )
-{
- if (hook->wanted & (FDE_READ|FDE_WRITE)) {
- /* we can always read/write */
- hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE);
- }
-}
-
-static int _event_file_peek( EventHook hook )
-{
- return (hook->wanted & (FDE_READ|FDE_WRITE));
-}
-
-static void _fh_file_hook( FH f, int events, EventHook hook )
-{
- hook->h = f->fh_handle;
- hook->prepare = _event_file_prepare;
- hook->peek = _event_file_peek;
-}
-
-/** SOCKET EVENT HOOKS
- **/
-
-static void _event_socket_verify( EventHook hook, WSANETWORKEVENTS* evts )
-{
- if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) {
- if (hook->wanted & FDE_READ)
- hook->ready |= FDE_READ;
- if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
- if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) {
- if (hook->wanted & FDE_WRITE)
- hook->ready |= FDE_WRITE;
- if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
- if ( evts->lNetworkEvents & FD_OOB ) {
- if (hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
-}
-
-static void _event_socket_prepare( EventHook hook )
-{
- WSANETWORKEVENTS evts;
-
- /* look if some of the events we want already happened ? */
- if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts ))
- _event_socket_verify( hook, &evts );
-}
-
-static int _socket_wanted_to_flags( int wanted )
-{
- int flags = 0;
- if (wanted & FDE_READ)
- flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
-
- if (wanted & FDE_WRITE)
- flags |= FD_WRITE | FD_CONNECT | FD_CLOSE;
-
- if (wanted & FDE_ERROR)
- flags |= FD_OOB;
-
- return flags;
-}
-
-static int _event_socket_start( EventHook hook )
-{
- /* create an event which we're going to wait for */
- FH fh = hook->fh;
- long flags = _socket_wanted_to_flags( hook->wanted );
-
- hook->h = fh->event;
- if (hook->h == INVALID_HANDLE_VALUE) {
- D( "_event_socket_start: no event for %s", fh->name );
- return 0;
- }
-
- if ( flags != fh->mask ) {
- D( "_event_socket_start: hooking %s for %x (flags %ld)", hook->fh->name, hook->wanted, flags );
- if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
- D( "_event_socket_start: WSAEventSelect() for %s failed, error %d", hook->fh->name, WSAGetLastError() );
- CloseHandle( hook->h );
- hook->h = INVALID_HANDLE_VALUE;
- exit(1);
- return 0;
- }
- fh->mask = flags;
- }
- return 1;
-}
-
-static void _event_socket_stop( EventHook hook )
-{
- hook->h = INVALID_HANDLE_VALUE;
-}
-
-static int _event_socket_check( EventHook hook )
-{
- int result = 0;
- FH fh = hook->fh;
- WSANETWORKEVENTS evts;
-
- if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) {
- _event_socket_verify( hook, &evts );
- result = (hook->ready != 0);
- if (result) {
- ResetEvent( hook->h );
- }
- }
- D( "_event_socket_check %s returns %d", fh->name, result );
- return result;
-}
-
-static int _event_socket_peek( EventHook hook )
-{
- WSANETWORKEVENTS evts;
- FH fh = hook->fh;
-
- /* look if some of the events we want already happened ? */
- if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) {
- _event_socket_verify( hook, &evts );
- if (hook->ready)
- ResetEvent( hook->h );
- }
-
- return hook->ready != 0;
-}
-
-
-
-static void _fh_socket_hook( FH f, int events, EventHook hook )
-{
- hook->prepare = _event_socket_prepare;
- hook->start = _event_socket_start;
- hook->stop = _event_socket_stop;
- hook->check = _event_socket_check;
- hook->peek = _event_socket_peek;
-
- // TODO: check return value?
- _event_socket_start( hook );
-}
-
-/** SOCKETPAIR EVENT HOOKS
- **/
-
-static void _event_socketpair_prepare( EventHook hook )
-{
- FH fh = hook->fh;
- SocketPair pair = fh->fh_pair;
- BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
- BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
-
- if (hook->wanted & FDE_READ && rbip->can_read)
- hook->ready |= FDE_READ;
-
- if (hook->wanted & FDE_WRITE && wbip->can_write)
- hook->ready |= FDE_WRITE;
- }
-
- static int _event_socketpair_start( EventHook hook )
- {
- FH fh = hook->fh;
- SocketPair pair = fh->fh_pair;
- BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
- BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
-
- if (hook->wanted == FDE_READ)
- hook->h = rbip->evt_read;
-
- else if (hook->wanted == FDE_WRITE)
- hook->h = wbip->evt_write;
-
- else {
- D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE" );
- return 0;
- }
- D( "_event_socketpair_start: hook %s for %x wanted=%x",
- hook->fh->name, _fh_to_int(fh), hook->wanted);
- return 1;
-}
-
-static int _event_socketpair_peek( EventHook hook )
-{
- _event_socketpair_prepare( hook );
- return hook->ready != 0;
-}
-
-static void _fh_socketpair_hook( FH fh, int events, EventHook hook )
-{
- hook->prepare = _event_socketpair_prepare;
- hook->start = _event_socketpair_start;
- hook->peek = _event_socketpair_peek;
-}
-
-
static adb_mutex_t g_console_output_buffer_lock;
void
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 8f610cf..c3a3fd7 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -87,8 +87,12 @@
TestAdbStrError(-1, "Unknown error");
// Test very big, positive unknown error.
TestAdbStrError(1000000, "Unknown error");
+
// Test success case.
- TestAdbStrError(0, "No error");
+ // Wine returns "Success" for strerror(0), Windows returns "No error", so accept both.
+ std::string success = adb_strerror(0);
+ EXPECT_TRUE(success == "Success" || success == "No error") << "strerror(0) = " << success;
+
// Test error that regular strerror() should have a string for.
TestAdbStrError(EPERM, "Operation not permitted");
// Test error that regular strerror() doesn't have a string for, but that
diff --git a/adb/test_device.py b/adb/test_device.py
index afc061a..9dab3ae 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -216,8 +216,8 @@
"""Send data through adb forward and read it back via adb reverse"""
forward_port = 12345
reverse_port = forward_port + 1
- forward_spec = "tcp:" + str(forward_port)
- reverse_spec = "tcp:" + str(reverse_port)
+ forward_spec = 'tcp:' + str(forward_port)
+ reverse_spec = 'tcp:' + str(reverse_port)
forward_setup = False
reverse_setup = False
@@ -739,7 +739,7 @@
# Create some random files and a subdirectory containing more files.
temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
- subdir = os.path.join(host_dir, "subdir")
+ subdir = os.path.join(host_dir, 'subdir')
os.mkdir(subdir)
subdir_temp_files = make_random_host_files(in_dir=subdir,
num_files=4)
@@ -756,7 +756,7 @@
for subdir_temp_file in subdir_temp_files:
remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
# BROKEN: http://b/25394682
- # "subdir",
+ # 'subdir';
temp_file.base_name)
self._verify_remote(temp_file.checksum, remote_path)
@@ -766,6 +766,22 @@
if host_dir is not None:
shutil.rmtree(host_dir)
+ @requires_non_root
+ def test_push_error_reporting(self):
+ """Make sure that errors that occur while pushing a file get reported
+
+ Bug: http://b/26816782
+ """
+ with tempfile.NamedTemporaryFile() as tmp_file:
+ tmp_file.write('\0' * 1024 * 1024)
+ tmp_file.flush()
+ try:
+ self.device.push(local=tmp_file.name, remote='/system/')
+ self.fail('push should not have succeeded')
+ except subprocess.CalledProcessError as e:
+ output = e.output
+
+ self.assertIn('Permission denied', output)
def _test_pull(self, remote_file, checksum):
tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
@@ -829,6 +845,96 @@
if host_dir is not None:
shutil.rmtree(host_dir)
+ def test_pull_dir_symlink(self):
+ """Pull a directory into a symlink to a directory.
+
+ Bug: http://b/27362811
+ """
+ if os.name != 'posix':
+ raise unittest.SkipTest('requires POSIX')
+
+ try:
+ host_dir = tempfile.mkdtemp()
+ real_dir = os.path.join(host_dir, 'dir')
+ symlink = os.path.join(host_dir, 'symlink')
+ os.mkdir(real_dir)
+ os.symlink(real_dir, symlink)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=symlink)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(
+ real_dir, posixpath.basename(self.DEVICE_TEMP_DIR),
+ temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_dir_symlink_collision(self):
+ """Pull a directory into a colliding symlink to directory."""
+ if os.name != 'posix':
+ raise unittest.SkipTest('requires POSIX')
+
+ try:
+ host_dir = tempfile.mkdtemp()
+ real_dir = os.path.join(host_dir, 'real')
+ tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
+ symlink = os.path.join(host_dir, tmp_dirname)
+ os.mkdir(real_dir)
+ os.symlink(real_dir, symlink)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(real_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
+ def test_pull_dir_nonexistent(self):
+ """Pull a directory of files from the device to a nonexistent path."""
+ try:
+ host_dir = tempfile.mkdtemp()
+ dest_dir = os.path.join(host_dir, 'dest')
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(dest_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
def test_pull_symlink_dir(self):
"""Pull a symlink to a directory of symlinks to files."""
try:
@@ -884,7 +990,7 @@
try:
host_dir = tempfile.mkdtemp()
- subdir = posixpath.join(self.DEVICE_TEMP_DIR, "subdir")
+ subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
self.device.shell(['mkdir', '-p', subdir])
@@ -905,7 +1011,7 @@
for subdir_temp_file in subdir_temp_files:
local_path = os.path.join(host_dir,
- "subdir",
+ 'subdir',
subdir_temp_file.base_name)
self._verify_local(subdir_temp_file.checksum, local_path)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 6020ad5..e3340af 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -30,6 +30,7 @@
#include <list>
#include <android-base/logging.h>
+#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -190,8 +191,7 @@
//
// read_transport thread reads data from a transport (representing a usb/tcp connection),
// and makes the main thread call handle_packet().
-static void *read_transport_thread(void *_t)
-{
+static void read_transport_thread(void* _t) {
atransport *t = reinterpret_cast<atransport*>(_t);
apacket *p;
@@ -244,13 +244,11 @@
D("%s: read_transport thread is exiting", t->serial);
kick_transport(t);
transport_unref(t);
- return 0;
}
// write_transport thread gets packets sent by the main thread (through send_packet()),
// and writes to a transport (representing a usb/tcp connection).
-static void *write_transport_thread(void *_t)
-{
+static void write_transport_thread(void* _t) {
atransport *t = reinterpret_cast<atransport*>(_t);
apacket *p;
int active = 0;
@@ -295,7 +293,6 @@
D("%s: write_transport thread is exiting, fd %d", t->serial, t->fd);
kick_transport(t);
transport_unref(t);
- return 0;
}
static void kick_transport_locked(atransport* t) {
@@ -683,11 +680,7 @@
// Check for matching serial number.
if (serial) {
- if ((t->serial && !strcmp(serial, t->serial)) ||
- (t->devpath && !strcmp(serial, t->devpath)) ||
- qual_match(serial, "product:", t->product, false) ||
- qual_match(serial, "model:", t->model, true) ||
- qual_match(serial, "device:", t->device, false)) {
+ if (t->MatchesTarget(serial)) {
if (result) {
*error_out = "more than one device";
if (is_ambiguous) *is_ambiguous = true;
@@ -841,6 +834,43 @@
disconnects_.clear();
}
+bool atransport::MatchesTarget(const std::string& target) const {
+ if (serial) {
+ if (target == serial) {
+ return true;
+ } else if (type == kTransportLocal) {
+ // Local transports can match [tcp:|udp:]<hostname>[:port].
+ const char* local_target_ptr = target.c_str();
+
+ // For fastboot compatibility, ignore protocol prefixes.
+ if (android::base::StartsWith(target, "tcp:") ||
+ android::base::StartsWith(target, "udp:")) {
+ local_target_ptr += 4;
+ }
+
+ // Parse our |serial| and the given |target| to check if the hostnames and ports match.
+ std::string serial_host, error;
+ int serial_port = -1;
+ if (android::base::ParseNetAddress(serial, &serial_host, &serial_port, nullptr,
+ &error)) {
+ // |target| may omit the port to default to ours.
+ std::string target_host;
+ int target_port = serial_port;
+ if (android::base::ParseNetAddress(local_target_ptr, &target_host, &target_port,
+ nullptr, &error) &&
+ serial_host == target_host && serial_port == target_port) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return (devpath && target == devpath) ||
+ qual_match(target.c_str(), "product:", product, false) ||
+ qual_match(target.c_str(), "model:", model, true) ||
+ qual_match(target.c_str(), "device:", device, false);
+}
+
#if ADB_HOST
static void append_transport_info(std::string* result, const char* key,
diff --git a/adb/transport.h b/adb/transport.h
index 76d6afa..5857249 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -50,7 +50,6 @@
// it's better to do this piece by piece.
atransport() {
- auth_fde = {};
transport_fde = {};
protocol_version = A_VERSION;
max_payload = MAX_PAYLOAD;
@@ -87,7 +86,6 @@
void* key = nullptr;
unsigned char token[TOKEN_SIZE] = {};
- fdevent auth_fde;
size_t failed_auth_attempts = 0;
const std::string connection_state_name() const;
@@ -109,6 +107,21 @@
void RemoveDisconnect(adisconnect* disconnect);
void RunDisconnects();
+ // Returns true if |target| matches this transport. A matching |target| can be any of:
+ // * <serial>
+ // * <devpath>
+ // * product:<product>
+ // * model:<model>
+ // * device:<device>
+ //
+ // If this is a local transport, serial will also match [tcp:|udp:]<hostname>[:port] targets.
+ // For example, serial "100.100.100.100:5555" would match any of:
+ // * 100.100.100.100
+ // * tcp:100.100.100.100
+ // * udp:100.100.100.100:5555
+ // This is to make it easier to use the same network target for both fastboot and adb.
+ bool MatchesTarget(const std::string& target) const;
+
private:
// A set of features transmitted in the banner with the initial connection.
// This is stored in the banner as 'features=feature0,feature1,etc'.
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index d2a375a..372bedf 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -121,8 +121,7 @@
}
#if ADB_HOST
-static void *client_socket_thread(void *x)
-{
+static void client_socket_thread(void* x) {
adb_thread_setname("client_socket_thread");
D("transport: client_socket_thread() starting");
while (true) {
@@ -135,13 +134,11 @@
}
sleep(1);
}
- return 0;
}
#else // ADB_HOST
-static void *server_socket_thread(void * arg)
-{
+static void server_socket_thread(void* arg) {
int serverfd, fd;
sockaddr_storage ss;
sockaddr *addrp = reinterpret_cast<sockaddr*>(&ss);
@@ -174,7 +171,6 @@
}
}
D("transport: server_socket_thread() exiting");
- return 0;
}
/* This is relevant only for ADB daemon running inside the emulator. */
@@ -220,17 +216,16 @@
* the transport registration is completed. That's why we need to send the
* 'start' request after the transport is registered.
*/
-static void *qemu_socket_thread(void * arg)
-{
-/* 'accept' request to the adb QEMUD service. */
-static const char _accept_req[] = "accept";
-/* 'start' request to the adb QEMUD service. */
-static const char _start_req[] = "start";
-/* 'ok' reply from the adb QEMUD service. */
-static const char _ok_resp[] = "ok";
+static void qemu_socket_thread(void* arg) {
+ /* 'accept' request to the adb QEMUD service. */
+ static const char _accept_req[] = "accept";
+ /* 'start' request to the adb QEMUD service. */
+ static const char _start_req[] = "start";
+ /* 'ok' reply from the adb QEMUD service. */
+ static const char _ok_resp[] = "ok";
const int port = (int) (uintptr_t) arg;
- int res, fd;
+ int fd;
char tmp[256];
char con_name[32];
@@ -247,7 +242,7 @@
* implement adb QEMUD service. Fall back to the old TCP way. */
D("adb service is not available. Falling back to TCP socket.");
adb_thread_create(server_socket_thread, arg);
- return 0;
+ return;
}
for(;;) {
@@ -256,40 +251,40 @@
*/
/* Send the 'accept' request. */
- res = adb_write(fd, _accept_req, strlen(_accept_req));
- if ((size_t)res == strlen(_accept_req)) {
+ if (WriteFdExactly(fd, _accept_req, strlen(_accept_req))) {
/* Wait for the response. In the response we expect 'ok' on success,
* or 'ko' on failure. */
- res = adb_read(fd, tmp, sizeof(tmp));
- if (res != 2 || memcmp(tmp, _ok_resp, 2)) {
+ if (!ReadFdExactly(fd, tmp, 2) || memcmp(tmp, _ok_resp, 2)) {
D("Accepting ADB host connection has failed.");
adb_close(fd);
} else {
/* Host is connected. Register the transport, and start the
* exchange. */
register_socket_transport(fd, "host", port, 1);
- adb_write(fd, _start_req, strlen(_start_req));
+ if (!WriteFdExactly(fd, _start_req, strlen(_start_req))) {
+ adb_close(fd);
+ }
}
/* Prepare for accepting of the next ADB host connection. */
fd = qemu_pipe_open(con_name);
if (fd < 0) {
D("adb service become unavailable.");
- return 0;
+ return;
}
} else {
D("Unable to send the '%s' request to ADB service.", _accept_req);
- return 0;
+ return;
}
}
D("transport: qemu_socket_thread() exiting");
- return 0;
+ return;
}
#endif // !ADB_HOST
void local_init(int port)
{
- void* (*func)(void *);
+ adb_thread_func_t func;
const char* debug_name = "";
#if ADB_HOST
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 97fc069..2028ecc 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -53,7 +53,6 @@
EXPECT_EQ(key, rhs.key);
EXPECT_EQ(0, memcmp(token, rhs.token, TOKEN_SIZE));
- EXPECT_EQ(0, memcmp(&auth_fde, &rhs.auth_fde, sizeof(fdevent)));
EXPECT_EQ(failed_auth_attempts, rhs.failed_auth_attempts);
EXPECT_EQ(features(), rhs.features());
@@ -219,3 +218,60 @@
ASSERT_EQ(std::string("bar"), t.model);
ASSERT_EQ(std::string("baz"), t.device);
}
+
+TEST(transport, test_matches_target) {
+ std::string serial = "foo";
+ std::string devpath = "/path/to/bar";
+ std::string product = "test_product";
+ std::string model = "test_model";
+ std::string device = "test_device";
+
+ atransport t;
+ t.serial = &serial[0];
+ t.devpath = &devpath[0];
+ t.product = &product[0];
+ t.model = &model[0];
+ t.device = &device[0];
+
+ // These tests should not be affected by the transport type.
+ for (TransportType type : {kTransportAny, kTransportLocal}) {
+ t.type = type;
+
+ EXPECT_TRUE(t.MatchesTarget(serial));
+ EXPECT_TRUE(t.MatchesTarget(devpath));
+ EXPECT_TRUE(t.MatchesTarget("product:" + product));
+ EXPECT_TRUE(t.MatchesTarget("model:" + model));
+ EXPECT_TRUE(t.MatchesTarget("device:" + device));
+
+ // Product, model, and device don't match without the prefix.
+ EXPECT_FALSE(t.MatchesTarget(product));
+ EXPECT_FALSE(t.MatchesTarget(model));
+ EXPECT_FALSE(t.MatchesTarget(device));
+ }
+}
+
+TEST(transport, test_matches_target_local) {
+ std::string serial = "100.100.100.100:5555";
+
+ atransport t;
+ t.serial = &serial[0];
+
+ // Network address matching should only be used for local transports.
+ for (TransportType type : {kTransportAny, kTransportLocal}) {
+ t.type = type;
+ bool should_match = (type == kTransportLocal);
+
+ EXPECT_EQ(should_match, t.MatchesTarget("100.100.100.100"));
+ EXPECT_EQ(should_match, t.MatchesTarget("tcp:100.100.100.100"));
+ EXPECT_EQ(should_match, t.MatchesTarget("tcp:100.100.100.100:5555"));
+ EXPECT_EQ(should_match, t.MatchesTarget("udp:100.100.100.100"));
+ EXPECT_EQ(should_match, t.MatchesTarget("udp:100.100.100.100:5555"));
+
+ // Wrong protocol, hostname, or port should never match.
+ EXPECT_FALSE(t.MatchesTarget("100.100.100"));
+ EXPECT_FALSE(t.MatchesTarget("100.100.100.100:"));
+ EXPECT_FALSE(t.MatchesTarget("100.100.100.100:-1"));
+ EXPECT_FALSE(t.MatchesTarget("100.100.100.100:5554"));
+ EXPECT_FALSE(t.MatchesTarget("abc:100.100.100.100"));
+ }
+}
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index ed5d2d67..500898a 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -571,7 +571,7 @@
register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
}
-static void* device_poll_thread(void* unused) {
+static void device_poll_thread(void*) {
adb_thread_setname("device poll");
D("Created device thread");
while (true) {
@@ -580,7 +580,6 @@
kick_disconnected_devices();
sleep(1);
}
- return nullptr;
}
void usb_init() {
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index a4f1a70..c863ed2 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -232,10 +232,7 @@
},
};
-
-
-static void *usb_adb_open_thread(void *x)
-{
+static void usb_adb_open_thread(void* x) {
struct usb_handle *usb = (struct usb_handle *)x;
int fd;
@@ -270,7 +267,7 @@
}
// never gets here
- return 0;
+ abort();
}
static int usb_adb_write(usb_handle *h, const void *data, int len)
@@ -434,8 +431,7 @@
return;
}
-static void *usb_ffs_open_thread(void *x)
-{
+static void usb_ffs_open_thread(void* x) {
struct usb_handle *usb = (struct usb_handle *)x;
adb_thread_setname("usb ffs open");
@@ -462,7 +458,7 @@
}
// never gets here
- return 0;
+ abort();
}
static int usb_ffs_write(usb_handle* h, const void* data, int len) {
diff --git a/adb/usb_osx.cpp b/adb/usb_osx.cpp
index 148be1d..54d4c6c 100644
--- a/adb/usb_osx.cpp
+++ b/adb/usb_osx.cpp
@@ -400,9 +400,7 @@
return NULL;
}
-
-void* RunLoopThread(void* unused)
-{
+static void RunLoopThread(void* unused) {
adb_thread_setname("RunLoop");
InitUSB();
@@ -420,7 +418,6 @@
IONotificationPortDestroy(notificationPort);
LOG(DEBUG) << "RunLoopThread done";
- return NULL;
}
static void usb_cleanup() {
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index e79008f..8ecca37 100644
--- a/adb/usb_windows.cpp
+++ b/adb/usb_windows.cpp
@@ -97,7 +97,7 @@
/// Entry point for thread that polls (every second) for new usb interfaces.
/// This routine calls find_devices in infinite loop.
-void* device_poll_thread(void* unused);
+static void device_poll_thread(void*);
/// Initializes this module
void usb_init();
@@ -172,7 +172,7 @@
return 1;
}
-void* device_poll_thread(void* unused) {
+void device_poll_thread(void*) {
adb_thread_setname("Device Poll");
D("Created device thread");
@@ -180,8 +180,6 @@
find_devices();
adb_sleep_ms(1000);
}
-
- return NULL;
}
static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
@@ -203,7 +201,7 @@
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
-static void* _power_notification_thread(void* unused) {
+static void _power_notification_thread(void*) {
// This uses a thread with its own window message pump to get power
// notifications. If adb runs from a non-interactive service account, this
// might not work (not sure). If that happens to not work, we could use
@@ -255,8 +253,6 @@
// shutting down. Not a big deal since the whole process will be going away
// soon anyway.
D("Power notification thread exiting");
-
- return NULL;
}
void usb_init() {
diff --git a/base/Android.mk b/base/Android.mk
index d20a81f..1693e74 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -67,7 +67,7 @@
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CPPFLAGS := $(libbase_cppflags) $(libbase_linux_cppflags)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libcutils
+LOCAL_STATIC_LIBRARIES := liblog
LOCAL_MULTILIB := both
include $(BUILD_STATIC_LIBRARY)
@@ -77,7 +77,6 @@
LOCAL_WHOLE_STATIC_LIBRARIES := libbase
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := libcutils
LOCAL_MULTILIB := both
include $(BUILD_SHARED_LIBRARY)
@@ -94,7 +93,7 @@
LOCAL_CPPFLAGS_darwin := $(libbase_darwin_cppflags)
LOCAL_CPPFLAGS_linux := $(libbase_linux_cppflags)
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libcutils
+LOCAL_STATIC_LIBRARIES := liblog
LOCAL_MULTILIB := both
LOCAL_MODULE_HOST_OS := darwin linux windows
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -104,7 +103,6 @@
LOCAL_WHOLE_STATIC_LIBRARIES := libbase
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libcutils
LOCAL_MULTILIB := both
LOCAL_MODULE_HOST_OS := darwin linux windows
include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/base/file.cpp b/base/file.cpp
index bcdfc5e..da1adba 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -29,10 +29,6 @@
#include "cutils/log.h"
#include "utils/Compat.h"
-#if !defined(_WIN32)
-#define O_BINARY 0
-#endif
-
namespace android {
namespace base {
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 486befc..5342d98 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -20,6 +20,10 @@
#include <sys/stat.h>
#include <string>
+#if !defined(_WIN32) && !defined(O_BINARY)
+#define O_BINARY 0
+#endif
+
namespace android {
namespace base {
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index cd526d0..e5babed 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -150,6 +150,14 @@
#define UNIMPLEMENTED(level) \
LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+#ifdef __clang_analyzer__
+// ClangL static analyzer does not see the conditional statement inside
+// LogMessage's destructor that will abort on FATAL severity.
+#define ABORT_AFTER_LOG_FATAL for (;;abort())
+#else
+#define ABORT_AFTER_LOG_FATAL
+#endif
+
// Check whether condition x holds and LOG(FATAL) if not. The value of the
// expression x is only evaluated once. Extra logging can be appended using <<
// after. For example:
@@ -160,6 +168,7 @@
if (LIKELY((x))) \
; \
else \
+ ABORT_AFTER_LOG_FATAL \
::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
::android::base::FATAL, -1).stream() \
<< "Check failed: " #x << " "
@@ -169,6 +178,7 @@
for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
UNLIKELY(!(_values.lhs OP _values.rhs)); \
/* empty */) \
+ ABORT_AFTER_LOG_FATAL \
::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
::android::base::FATAL, -1).stream() \
<< "Check failed: " << #LHS << " " << #OP << " " << #RHS \
@@ -192,6 +202,7 @@
if (LIKELY((strcmp(s1, s2) == 0) == sense)) \
; \
else \
+ ABORT_AFTER_LOG_FATAL \
LOG(FATAL) << "Check failed: " \
<< "\"" << s1 << "\"" \
<< (sense ? " == " : " != ") << "\"" << s2 << "\""
@@ -206,6 +217,7 @@
int rc = call args; \
if (rc != 0) { \
errno = rc; \
+ ABORT_AFTER_LOG_FATAL \
PLOG(FATAL) << #call << " failed for " << what; \
} \
} while (false)
diff --git a/base/logging.cpp b/base/logging.cpp
index a385902..1741871 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -68,7 +68,13 @@
#include <windows.h>
#endif
-static pid_t GetThreadId() {
+#if defined(_WIN32)
+typedef uint32_t thread_id;
+#else
+typedef pid_t thread_id;
+#endif
+
+static thread_id GetThreadId() {
#if defined(__BIONIC__)
return gettid();
#elif defined(__APPLE__)
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 337ba7c..635af6c 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -16,7 +16,6 @@
#include "android-base/logging.h"
#include "android-base/test_utils.h"
-#include "utils/Compat.h" // For OS_PATH_SEPARATOR.
#include <fcntl.h>
#include <stdio.h>
@@ -27,6 +26,9 @@
#if defined(_WIN32)
#include <windows.h>
#include <direct.h>
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
#endif
#include <string>
diff --git a/bootstat/Android.mk b/bootstat/Android.mk
index bbb903d..c6349c1 100644
--- a/bootstat/Android.mk
+++ b/bootstat/Android.mk
@@ -32,6 +32,7 @@
bootstat_shared_libs := \
libbase \
+ libcutils \
liblog
bootstat_cflags := \
diff --git a/bootstat/README.md b/bootstat/README.md
index 1b4bf7f..76ea6c1 100644
--- a/bootstat/README.md
+++ b/bootstat/README.md
@@ -7,10 +7,12 @@
Usage: bootstat [options]
options include:
- -d Dump the boot event records to the console.
- -h Show this help.
- -l Log all metrics to logstorage.
- -r Record the relative time of a named boot event.
+ -h, --help Show this help
+ -l, --log Log all metrics to logstorage
+ -p, --print Dump the boot event records to the console
+ -r, --record Record the timestamp of a named boot event
+ --record_boot_reason Record the reason why the device booted
+ --record_time_since_factory_reset Record the time since the device was reset
## Relative time ##
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 1bdcf2b..8282b40 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -50,30 +50,58 @@
SetStorePath(BOOTSTAT_DATA_DIR);
}
-void BootEventRecordStore::AddBootEvent(const std::string& name) {
+void BootEventRecordStore::AddBootEvent(const std::string& event) {
std::string uptime_str;
if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
LOG(ERROR) << "Failed to read /proc/uptime";
}
- std::string record_path = GetBootEventPath(name);
+ // Cast intentionally rounds down.
+ int32_t uptime = static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
+ AddBootEventWithValue(event, uptime);
+}
+
+// The implementation of AddBootEventValue makes use of the mtime file
+// attribute to store the value associated with a boot event in order to
+// optimize on-disk size requirements and small-file thrashing.
+void BootEventRecordStore::AddBootEventWithValue(
+ const std::string& event, int32_t value) {
+ std::string record_path = GetBootEventPath(event);
if (creat(record_path.c_str(), S_IRUSR | S_IWUSR) == -1) {
PLOG(ERROR) << "Failed to create " << record_path;
}
+ // Fill out the stat structure for |record_path| in order to get the atime to
+ // set in the utime() call.
struct stat file_stat;
if (stat(record_path.c_str(), &file_stat) == -1) {
PLOG(ERROR) << "Failed to read " << record_path;
}
- // Cast intentionally rounds down.
- time_t uptime = static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
- struct utimbuf times = {file_stat.st_atime, uptime};
+ // Set the |modtime| of the file to store the value of the boot event while
+ // preserving the |actime| (as read by stat).
+ struct utimbuf times = {/* actime */ file_stat.st_atime, /* modtime */ value};
if (utime(record_path.c_str(), ×) == -1) {
PLOG(ERROR) << "Failed to set mtime for " << record_path;
}
}
+bool BootEventRecordStore::GetBootEvent(
+ const std::string& event, BootEventRecord* record) const {
+ CHECK_NE(static_cast<BootEventRecord*>(nullptr), record);
+ CHECK(!event.empty());
+
+ const std::string record_path = GetBootEventPath(event);
+ int32_t uptime;
+ if (!ParseRecordEventTime(record_path, &uptime)) {
+ LOG(ERROR) << "Failed to parse boot time record: " << record_path;
+ return false;
+ }
+
+ *record = std::make_pair(event, uptime);
+ return true;
+}
+
std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::
GetAllBootEvents() const {
std::vector<BootEventRecord> events;
@@ -92,14 +120,13 @@
}
const std::string event = entry->d_name;
- const std::string record_path = GetBootEventPath(event);
- int32_t uptime;
- if (!ParseRecordEventTime(record_path, &uptime)) {
- LOG(ERROR) << "Failed to parse boot time record: " << record_path;
+ BootEventRecord record;
+ if (!GetBootEvent(event, &record)) {
+ LOG(ERROR) << "Failed to parse boot time event: " << event;
continue;
}
- events.push_back(std::make_pair(event, uptime));
+ events.push_back(record);
}
return events;
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index 77978ef..4d5deee 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -34,8 +34,16 @@
BootEventRecordStore();
- // Persists the boot event named |name| in the record store.
- void AddBootEvent(const std::string& name);
+ // Persists the boot |event| in the record store.
+ void AddBootEvent(const std::string& event);
+
+ // Persists the boot |event| with the associated |value| in the record store.
+ void AddBootEventWithValue(const std::string& event, int32_t value);
+
+ // Queries the named boot |event|. |record| must be non-null. |record|
+ // contains the boot event data on success. Returns true iff the query is
+ // successful.
+ bool GetBootEvent(const std::string& event, BootEventRecord* record) const;
// Returns a list of all of the boot events persisted in the record store.
std::vector<BootEventRecord> GetAllBootEvents() const;
@@ -45,6 +53,8 @@
// more test-friendly path.
FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent);
FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
+ FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);
+ FRIEND_TEST(BootEventRecordStoreTest, GetBootEvent);
// Sets the filesystem path of the record store.
void SetStorePath(const std::string& path);
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 384f84d..0d7bbb0 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -154,3 +154,38 @@
EXPECT_TRUE(FuzzUptimeEquals(uptime, *i));
}
}
+
+TEST_F(BootEventRecordStoreTest, AddBootEventWithValue) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ store.AddBootEventWithValue("permian", 42);
+
+ auto events = store.GetAllBootEvents();
+ ASSERT_EQ(1U, events.size());
+ EXPECT_EQ("permian", events[0].first);
+ EXPECT_EQ(42, events[0].second);
+}
+
+TEST_F(BootEventRecordStoreTest, GetBootEvent) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ // Event does not exist.
+ BootEventRecordStore::BootEventRecord record;
+ bool result = store.GetBootEvent("nonexistent", &record);
+ EXPECT_EQ(false, result);
+
+ // Empty path.
+ EXPECT_DEATH(store.GetBootEvent(std::string(), &record), std::string());
+
+ // Success case.
+ store.AddBootEventWithValue("carboniferous", 314);
+ result = store.GetBootEvent("carboniferous", &record);
+ EXPECT_EQ(true, result);
+ EXPECT_EQ("carboniferous", record.first);
+ EXPECT_EQ(314, record.second);
+
+ // Null |record|.
+ EXPECT_DEATH(store.GetBootEvent("carboniferous", nullptr), std::string());
+}
\ No newline at end of file
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a83f8ad..bcc8abd 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -18,14 +18,16 @@
// timestamp, dump the persisted events, and log all events to EventLog to be
// uploaded to Android log storage via Tron.
-//#define LOG_TAG "bootstat"
-
+#include <getopt.h>
#include <unistd.h>
#include <cstddef>
#include <cstdio>
+#include <ctime>
+#include <map>
#include <memory>
#include <string>
#include <android-base/logging.h>
+#include <cutils/properties.h>
#include <log/log.h>
#include "boot_event_record_store.h"
#include "event_log_list_builder.h"
@@ -35,7 +37,7 @@
// Builds an EventLog buffer named |event| containing |data| and writes
// the log into the Tron histogram logs.
void LogBootEvent(const std::string& event, int32_t data) {
- LOG(INFO) << "Logging boot time: " << event << " " << data;
+ LOG(INFO) << "Logging boot metric: " << event << " " << data;
EventLogListBuilder log_builder;
log_builder.Append(event);
@@ -74,10 +76,12 @@
fprintf(stderr, "Usage: %s [options]\n", cmd);
fprintf(stderr,
"options include:\n"
- " -d Dump the boot event records to the console.\n"
- " -h Show this help.\n"
- " -l Log all metrics to logstorage.\n"
- " -r Record the timestamp of a named boot event.\n");
+ " -h, --help Show this help\n"
+ " -l, --log Log all metrics to logstorage\n"
+ " -p, --print Dump the boot event records to the console\n"
+ " -r, --record Record the timestamp of a named boot event\n"
+ " --record_boot_reason Record the reason why the device booted\n"
+ " --record_time_since_factory_reset Record the time since the device was reset\n");
}
// Constructs a readable, printable string from the givencommand line
@@ -92,6 +96,95 @@
return cmd;
}
+// Convenience wrapper over the property API that returns an
+// std::string.
+std::string GetProperty(const char* key) {
+ std::vector<char> temp(PROPERTY_VALUE_MAX);
+ const int len = property_get(key, &temp[0], nullptr);
+ if (len < 0) {
+ return "";
+ }
+ return std::string(&temp[0], len);
+}
+
+constexpr int32_t kUnknownBootReason = 1;
+
+// A mapping from boot reason string, as read from the ro.boot.bootreason
+// system property, to a unique integer ID. Viewers of log data dashboards for
+// the boot_reason metric may refer to this mapping to discern the histogram
+// values.
+const std::map<std::string, int32_t> kBootReasonMap = {
+ {"unknown", kUnknownBootReason},
+ {"normal", 2},
+ {"recovery", 3},
+ {"reboot", 4},
+ {"PowerKey", 5},
+ {"hard_reset", 6},
+ {"kernel_panic", 7},
+ {"rpm_err", 8},
+ {"hw_reset", 9},
+ {"tz_err", 10},
+ {"adsp_err", 11},
+ {"modem_err", 12},
+ {"mba_err", 13},
+ {"Watchdog", 14},
+ {"Panic", 15},
+ {"power_key", 16},
+ {"power_on", 17},
+ {"Reboot", 18},
+ {"rtc", 19},
+ {"edl", 20},
+};
+
+// Converts a string value representing the reason the system booted to an
+// integer representation. This is necessary for logging the boot_reason metric
+// via Tron, which does not accept non-integer buckets in histograms.
+int32_t BootReasonStrToEnum(const std::string& boot_reason) {
+ auto mapping = kBootReasonMap.find(boot_reason);
+ if (mapping != kBootReasonMap.end()) {
+ return mapping->second;
+ }
+
+ LOG(INFO) << "Unknown boot reason: " << boot_reason;
+ return kUnknownBootReason;
+}
+
+// Records the boot_reason metric by querying the ro.boot.bootreason system
+// property.
+void RecordBootReason() {
+ int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason"));
+ BootEventRecordStore boot_event_store;
+ boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
+}
+
+// Records two metrics related to the user resetting a device: the time at
+// which the device is reset, and the time since the user last reset the
+// device. The former is only set once per-factory reset.
+void RecordFactoryReset() {
+ BootEventRecordStore boot_event_store;
+ BootEventRecordStore::BootEventRecord record;
+
+ time_t current_time_utc = time(nullptr);
+
+ // The factory_reset boot event does not exist after the device is reset, so
+ // use this signal to mark the time of the factory reset.
+ if (!boot_event_store.GetBootEvent("factory_reset", &record)) {
+ boot_event_store.AddBootEventWithValue("factory_reset", current_time_utc);
+
+ // Don't log the time_since_factory_reset until some time has elapsed.
+ // The data is not meaningful yet and skews the histogram buckets.
+ return;
+ }
+
+ // Calculate and record the difference in time between now and the
+ // factory_reset time.
+ time_t factory_reset_utc = record.second;
+ time_t time_since_factory_reset = difftime(current_time_utc,
+ factory_reset_utc);
+ boot_event_store.AddBootEventWithValue("time_since_factory_reset",
+ time_since_factory_reset);
+}
+
} // namespace
int main(int argc, char **argv) {
@@ -100,9 +193,35 @@
const std::string cmd_line = GetCommandLine(argc, argv);
LOG(INFO) << "Service started: " << cmd_line;
+ int option_index = 0;
+ static const char boot_reason_str[] = "record_boot_reason";
+ static const char factory_reset_str[] = "record_time_since_factory_reset";
+ static const struct option long_options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "log", no_argument, NULL, 'l' },
+ { "print", no_argument, NULL, 'p' },
+ { "record", required_argument, NULL, 'r' },
+ { boot_reason_str, no_argument, NULL, 0 },
+ { factory_reset_str, no_argument, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+ };
+
int opt = 0;
- while ((opt = getopt(argc, argv, "hlpr:")) != -1) {
+ while ((opt = getopt_long(argc, argv, "hlpr:", long_options, &option_index)) != -1) {
switch (opt) {
+ // This case handles long options which have no single-character mapping.
+ case 0: {
+ const std::string option_name = long_options[option_index].name;
+ if (option_name == boot_reason_str) {
+ RecordBootReason();
+ } else if (option_name == factory_reset_str) {
+ RecordFactoryReset();
+ } else {
+ LOG(ERROR) << "Invalid option: " << option_name;
+ }
+ break;
+ }
+
case 'h': {
ShowHelp(argv[0]);
break;
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 2c37dd2..3c20fc8 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -3,12 +3,21 @@
on post-fs-data
mkdir /data/misc/bootstat 0700 root root
-# This marker, boot animation stopped, is considered the point at which the
+# The first marker, boot animation stopped, is considered the point at which
# the user may interact with the device, so it is a good proxy for the boot
# complete signal.
-on property:init.svc.bootanim=stopped
+#
+# The second marker ensures an encrypted device is decrypted before logging
+# boot time data.
+on property:init.svc.bootanim=stopped && property:vold.decrypt=trigger_restart_framework
# Record boot_complete timing event.
exec - root root -- /system/bin/bootstat -r boot_complete
+ # Record the boot reason.
+ exec - root root -- /system/bin/bootstat --record_boot_reason
+
+ # Record time since factory reset.
+ exec - root root -- /system/bin/bootstat --record_time_since_factory_reset
+
# Log all boot events.
exec - root root -- /system/bin/bootstat -l
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
index fa564b2..ce9dc73 100644
--- a/crash_reporter/Android.mk
+++ b/crash_reporter/Android.mk
@@ -136,7 +136,7 @@
LOCAL_MODULE := crash_reporter_tests
LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
ifdef BRILLO
-LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_TAGS := eng
endif
LOCAL_SHARED_LIBRARIES := libchrome \
libbrillo \
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
index b3fdcb4..d993576 100644
--- a/crash_reporter/crash_collector.cc
+++ b/crash_reporter/crash_collector.cc
@@ -36,6 +36,7 @@
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <brillo/key_value_store.h>
+#include <brillo/osrelease_reader.h>
#include <brillo/process.h>
namespace {
@@ -52,6 +53,11 @@
const char kUploadVarPrefix[] = "upload_var_";
const char kUploadFilePrefix[] = "upload_file_";
+// Product information keys in the /etc/os-release.d folder.
+static const char kBdkVersionKey[] = "bdk_version";
+static const char kProductIDKey[] = "product_id";
+static const char kProductVersionKey[] = "product_version";
+
// Normally this path is not used. Unfortunately, there are a few edge cases
// where we need this. Any process that runs as kDefaultUserName that crashes
// is consider a "user crash". That includes the initial Chrome browser that
@@ -183,7 +189,7 @@
return true;
}
-bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
+bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid __unused,
FilePath *crash_directory,
bool *out_of_capacity) {
if (out_of_capacity) *out_of_capacity = false;
@@ -384,14 +390,49 @@
const std::string &payload_path) {
int64_t payload_size = -1;
base::GetFileSize(FilePath(payload_path), &payload_size);
+
+ brillo::OsReleaseReader reader;
+ if (!forced_osreleased_directory_.empty()) {
+ reader.LoadTestingOnly(forced_osreleased_directory_);
+ } else {
+ reader.Load();
+ }
+ std::string bdk_version = "undefined";
+ std::string product_id = "undefined";
+ std::string product_version = "undefined";
+
+ if (!reader.GetString(kBdkVersionKey, &bdk_version)) {
+ LOG(ERROR) << "Could not read " << kBdkVersionKey
+ << " from /etc/os-release.d/";
+ }
+
+ if (!reader.GetString(kProductIDKey, &product_id)) {
+ LOG(ERROR) << "Could not read " << kProductIDKey
+ << " from /etc/os-release.d/";
+ }
+
+ if (!reader.GetString(kProductVersionKey, &product_version)) {
+ LOG(ERROR) << "Could not read " << kProductVersionKey
+ << " from /etc/os-release.d/";
+ }
+
std::string meta_data = StringPrintf("%sexec_name=%s\n"
"payload=%s\n"
"payload_size=%" PRId64 "\n"
+ "%s=%s\n"
+ "%s=%s\n"
+ "%s=%s\n"
"done=1\n",
extra_metadata_.c_str(),
exec_name.c_str(),
payload_path.c_str(),
- payload_size);
+ payload_size,
+ kBdkVersionKey,
+ bdk_version.c_str(),
+ kProductIDKey,
+ product_id.c_str(),
+ kProductVersionKey,
+ product_version.c_str());
// We must use WriteNewFile instead of base::WriteFile as we
// do not want to write with root access to a symlink that an attacker
// might have created.
diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h
index 24cbfb3..21b9198 100644
--- a/crash_reporter/crash_collector.h
+++ b/crash_reporter/crash_collector.h
@@ -84,6 +84,12 @@
forced_crash_directory_ = forced_directory;
}
+ // For testing, set the root directory to read etc/os-release.d properties
+ // from.
+ void ForceOsReleaseDDirectory(const base::FilePath &forced_directory) {
+ forced_osreleased_directory_ = forced_directory;
+ }
+
base::FilePath GetCrashDirectoryInfo(mode_t *mode,
uid_t *directory_owner,
gid_t *directory_group);
@@ -158,6 +164,7 @@
IsFeedbackAllowedFunction is_feedback_allowed_function_;
std::string extra_metadata_;
base::FilePath forced_crash_directory_;
+ base::FilePath forced_osreleased_directory_;
base::FilePath log_config_path_;
private:
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index 11c8c0d..a386cd1 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -74,10 +74,11 @@
TEST_F(CrashCollectorTest, WriteNewFile) {
FilePath test_file = test_dir_.path().Append("test_new");
const char kBuffer[] = "buffer";
- EXPECT_EQ(strlen(kBuffer),
- collector_.WriteNewFile(test_file,
- kBuffer,
- strlen(kBuffer)));
+ unsigned int numBytesWritten = collector_.WriteNewFile(
+ test_file,
+ kBuffer,
+ strlen(kBuffer));
+ EXPECT_EQ(strlen(kBuffer), numBytesWritten);
EXPECT_LT(collector_.WriteNewFile(test_file,
kBuffer,
strlen(kBuffer)), 0);
@@ -94,7 +95,7 @@
}
TEST_F(CrashCollectorTest, FormatDumpBasename) {
- struct tm tm = {0};
+ struct tm tm = {};
tm.tm_sec = 15;
tm.tm_min = 50;
tm.tm_hour = 13;
@@ -182,9 +183,26 @@
const char kMetaFileBasename[] = "generated.meta";
FilePath meta_file = test_dir_.path().Append(kMetaFileBasename);
FilePath payload_file = test_dir_.path().Append("payload-file");
+ FilePath osreleased_directory =
+ test_dir_.path().Append("etc").Append("os-release.d");
+ ASSERT_TRUE(base::CreateDirectory(osreleased_directory));
+ collector_.ForceOsReleaseDDirectory(test_dir_.path());
+
std::string contents;
const char kPayload[] = "foo";
ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
+ const char kBdkVersion[] = "1";
+ ASSERT_TRUE(base::WriteFile(osreleased_directory.Append("bdk_version"),
+ kBdkVersion,
+ strlen(kBdkVersion)));
+ const char kProductId[] = "baz";
+ ASSERT_TRUE(base::WriteFile(osreleased_directory.Append("product_id"),
+ kProductId,
+ strlen(kProductId)));
+ const char kProductVersion[] = "1.2.3.4";
+ ASSERT_TRUE(base::WriteFile(osreleased_directory.Append("product_version"),
+ kProductVersion,
+ strlen(kProductVersion)));
collector_.AddCrashMetaData("foo", "bar");
collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
@@ -193,6 +211,9 @@
"exec_name=kernel\n"
"payload=%s\n"
"payload_size=3\n"
+ "bdk_version=1\n"
+ "product_id=baz\n"
+ "product_version=1.2.3.4\n"
"done=1\n",
test_dir_.path().Append("payload-file").value().c_str());
EXPECT_EQ(kExpectedMeta, contents);
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
index 015f624..60fd832 100644
--- a/crash_reporter/kernel_collector_test.cc
+++ b/crash_reporter/kernel_collector_test.cc
@@ -49,7 +49,9 @@
protected:
void WriteStringToFile(const FilePath &file_path,
const char *data) {
- ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
+ unsigned int numBytesWritten =
+ base::WriteFile(file_path, data, strlen(data));
+ ASSERT_EQ(strlen(data), numBytesWritten);
}
void SetUpSuccessfulCollect();
@@ -284,7 +286,7 @@
size_t end_pos = filename.find_first_of("\n");
ASSERT_NE(std::string::npos, end_pos);
filename = filename.substr(0, end_pos);
- ASSERT_EQ(0, filename.find(test_crash_directory().value()));
+ ASSERT_EQ(0U, filename.find(test_crash_directory().value()));
ASSERT_TRUE(base::PathExists(FilePath(filename)));
std::string contents;
ASSERT_TRUE(base::ReadFileToString(FilePath(filename), &contents));
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
index 56d2704..36372ae 100644
--- a/crash_reporter/unclean_shutdown_collector_test.cc
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -73,7 +73,9 @@
protected:
void WriteStringToFile(const FilePath &file_path,
const char *data) {
- ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
+ unsigned int numBytesWritten =
+ base::WriteFile(file_path, data, strlen(data));
+ ASSERT_EQ(strlen(data), numBytesWritten);
}
UncleanShutdownCollectorMock collector_;
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index 98d7448..48b64e9 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -37,7 +37,6 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <brillo/osrelease_reader.h>
#include <brillo/process.h>
#include <brillo/syslog_logging.h>
#include <cutils/properties.h>
@@ -59,11 +58,6 @@
const char *UserCollector::kUserId = "Uid:\t";
const char *UserCollector::kGroupId = "Gid:\t";
-// Product information keys in the /etc/os-release.d folder.
-static const char kBdkVersionKey[] = "bdk_version";
-static const char kProductIDKey[] = "product_id";
-static const char kProductVersionKey[] = "product_version";
-
using base::FilePath;
using base::StringPrintf;
@@ -505,29 +499,6 @@
if (GetLogContents(FilePath(log_config_path_), exec, log_path))
AddCrashMetaData("log", log_path.value());
- brillo::OsReleaseReader reader;
- reader.Load();
- std::string value = "undefined";
- if (!reader.GetString(kBdkVersionKey, &value)) {
- LOG(ERROR) << "Could not read " << kBdkVersionKey
- << " from /etc/os-release.d/";
- }
- AddCrashMetaData(kBdkVersionKey, value);
-
- value = "undefined";
- if (!reader.GetString(kProductIDKey, &value)) {
- LOG(ERROR) << "Could not read " << kProductIDKey
- << " from /etc/os-release.d/";
- }
- AddCrashMetaData(kProductIDKey, value);
-
- value = "undefined";
- if (!reader.GetString(kProductVersionKey, &value)) {
- LOG(ERROR) << "Could not read " << kProductVersionKey
- << " from /etc/os-release.d/";
- }
- AddCrashMetaData(kProductVersionKey, value);
-
ErrorType error_type =
ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
if (error_type != kErrorNone) {
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index d9c9a5b..16a5cd5 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -101,15 +101,15 @@
&pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ(123456, pid);
EXPECT_EQ(11, signal);
- EXPECT_EQ(1000, uid);
- EXPECT_EQ(2000, gid);
+ EXPECT_EQ(1000U, uid);
+ EXPECT_EQ(2000U, gid);
EXPECT_EQ("foobar", exec_name);
EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo",
&pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ(4321, pid);
EXPECT_EQ(6, signal);
- EXPECT_EQ(-1, uid);
- EXPECT_EQ(-1, gid);
+ EXPECT_EQ(-1U, uid);
+ EXPECT_EQ(-1U, gid);
EXPECT_EQ("barfoo", exec_name);
EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11",
@@ -186,10 +186,10 @@
test_dir_.path().Append("test/this_link").value().c_str();
this_link.assign(long_link.c_str(), len);
ASSERT_EQ(len, this_link.size());
- unlink(kLink);
ASSERT_EQ(0, symlink(this_link.c_str(), kLink));
ASSERT_TRUE(collector_.GetSymlinkTarget(FilePath(kLink), &result));
ASSERT_EQ(this_link, result.value());
+ unlink(kLink);
}
}
@@ -333,8 +333,8 @@
gid_t gid = 100;
uid_t uid = 100;
EXPECT_TRUE(collector_.GetUserInfoFromName("root", &uid, &gid));
- EXPECT_EQ(0, uid);
- EXPECT_EQ(0, gid);
+ EXPECT_EQ(0U, uid);
+ EXPECT_EQ(0U, gid);
}
TEST_F(UserCollectorTest, CopyOffProcFilesBadPath) {
@@ -387,7 +387,9 @@
// maps file is not empty
const char data[] = "test data";
- ASSERT_EQ(sizeof(data), base::WriteFile(maps_file, data, sizeof(data)));
+ unsigned int numBytesWritten =
+ base::WriteFile(maps_file, data, sizeof(data));
+ ASSERT_EQ(sizeof(data), numBytesWritten);
ASSERT_TRUE(base::PathExists(maps_file));
EXPECT_TRUE(collector_.ValidateProcFiles(container_dir));
}
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 8efbacc..ac1c58c 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -678,13 +678,6 @@
// Ignore failed writes to closed sockets
signal(SIGPIPE, SIG_IGN);
- int logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);
- if (logsocket < 0) {
- logsocket = -1;
- } else {
- fcntl(logsocket, F_SETFD, FD_CLOEXEC);
- }
-
struct sigaction act;
act.sa_handler = SIG_DFL;
sigemptyset(&act.sa_mask);
@@ -692,10 +685,9 @@
act.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &act, 0);
- int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
- if (s < 0)
- return 1;
- fcntl(s, F_SETFD, FD_CLOEXEC);
+ int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
+ SOCK_STREAM | SOCK_CLOEXEC);
+ if (s == -1) return 1;
ALOGI("debuggerd: starting\n");
@@ -705,14 +697,12 @@
socklen_t alen = sizeof(ss);
ALOGV("waiting for connection\n");
- int fd = accept(s, addrp, &alen);
- if (fd < 0) {
- ALOGV("accept failed: %s\n", strerror(errno));
+ int fd = accept4(s, addrp, &alen, SOCK_CLOEXEC);
+ if (fd == -1) {
+ ALOGE("accept failed: %s\n", strerror(errno));
continue;
}
- fcntl(fd, F_SETFD, FD_CLOEXEC);
-
handle_request(fd);
}
return 0;
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/elf_utils.cpp
index 3f0dbde..9959f2e 100644
--- a/debuggerd/elf_utils.cpp
+++ b/debuggerd/elf_utils.cpp
@@ -63,10 +63,10 @@
if (nhdr.n_type == NT_GNU_BUILD_ID) {
// Skip the name (which is the owner and should be "GNU").
addr += NOTE_ALIGN(nhdr.n_namesz);
- uint8_t build_id_data[128];
- if (nhdr.n_namesz > sizeof(build_id_data)) {
- ALOGE("Possible corrupted note, name size value is too large: %u",
- nhdr.n_namesz);
+ uint8_t build_id_data[160];
+ if (nhdr.n_descsz > sizeof(build_id_data)) {
+ ALOGE("Possible corrupted note, desc size value is too large: %u",
+ nhdr.n_descsz);
return false;
}
if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
diff --git a/debuggerd/getevent.cpp b/debuggerd/getevent.cpp
index 751c4fb..e5acd17 100644
--- a/debuggerd/getevent.cpp
+++ b/debuggerd/getevent.cpp
@@ -26,6 +26,7 @@
#include <sys/poll.h>
#include <linux/input.h>
#include <errno.h>
+#include <memory>
#include <cutils/log.h>
static struct pollfd* ufds;
@@ -143,22 +144,20 @@
static int scan_dir(const char* dirname) {
char devname[PATH_MAX];
char* filename;
- DIR* dir;
struct dirent* de;
- dir = opendir(dirname);
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
if (dir == NULL)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
- while ((de = readdir(dir))) {
+ while ((de = readdir(dir.get()))) {
if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
(de->d_name[1] == '.' && de->d_name[2] == '\0'))
continue;
strcpy(filename, de->d_name);
open_device(devname);
}
- closedir(dir);
return 0;
}
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index dda6677..7cf2ffc 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -134,8 +134,15 @@
switch (code) {
case SEGV_MAPERR: return "SEGV_MAPERR";
case SEGV_ACCERR: return "SEGV_ACCERR";
+#if defined(SEGV_BNDERR)
+ case SEGV_BNDERR: return "SEGV_BNDERR";
+#endif
}
+#if defined(SEGV_BNDERR)
+ static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code");
+#else
static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code");
+#endif
break;
case SIGTRAP:
switch (code) {
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index e0f7c73..1b4ecbe 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -32,6 +32,7 @@
protocol.cpp \
socket.cpp \
tcp.cpp \
+ udp.cpp \
util.cpp \
LOCAL_MODULE := fastboot
@@ -89,7 +90,11 @@
ifeq ($(HOST_OS),linux)
my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX)
endif
-$(call dist-for-goals,dist_files sdk,$(my_dist_files))
+$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
+ifdef HOST_CROSS_OS
+# Archive fastboot.exe for win_sdk build.
+$(call dist-for-goals,win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
+endif
my_dist_files :=
ifeq ($(HOST_OS),linux)
@@ -114,6 +119,8 @@
socket_test.cpp \
tcp.cpp \
tcp_test.cpp \
+ udp.cpp \
+ udp_test.cpp \
LOCAL_STATIC_LIBRARIES := libbase libcutils
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index b219564..7e60a72 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -59,6 +59,7 @@
#include "fs.h"
#include "tcp.h"
#include "transport.h"
+#include "udp.h"
#include "usb.h"
#ifndef O_BINARY
@@ -243,22 +244,41 @@
return transport;
}
+ Socket::Protocol protocol = Socket::Protocol::kTcp;
std::string host;
- int port = tcp::kDefaultPort;
- if (serial != nullptr && android::base::StartsWith(serial, "tcp:")) {
- std::string error;
- const char* address = serial + strlen("tcp:");
+ int port = 0;
+ if (serial != nullptr) {
+ const char* net_address = nullptr;
- if (!android::base::ParseNetAddress(address, &host, &port, nullptr, &error)) {
- fprintf(stderr, "error: Invalid network address '%s': %s\n", address, error.c_str());
- return nullptr;
+ if (android::base::StartsWith(serial, "tcp:")) {
+ protocol = Socket::Protocol::kTcp;
+ port = tcp::kDefaultPort;
+ net_address = serial + strlen("tcp:");
+ } else if (android::base::StartsWith(serial, "udp:")) {
+ protocol = Socket::Protocol::kUdp;
+ port = udp::kDefaultPort;
+ net_address = serial + strlen("udp:");
+ }
+
+ if (net_address != nullptr) {
+ std::string error;
+ if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {
+ fprintf(stderr, "error: Invalid network address '%s': %s\n", net_address,
+ error.c_str());
+ return nullptr;
+ }
}
}
while (true) {
if (!host.empty()) {
std::string error;
- transport = tcp::Connect(host, port, &error).release();
+ if (protocol == Socket::Protocol::kTcp) {
+ transport = tcp::Connect(host, port, &error).release();
+ } else if (protocol == Socket::Protocol::kUdp) {
+ transport = udp::Connect(host, port, &error).release();
+ }
+
if (transport == nullptr && announce) {
fprintf(stderr, "error: %s\n", error.c_str());
}
@@ -335,8 +355,9 @@
" formatting.\n"
" -s <specific device> Specify a device. For USB, provide either\n"
" a serial number or path to device port.\n"
- " For TCP, provide an address in the form\n"
- " tcp:<hostname>[:port].\n"
+ " For ethernet, provide an address in the"
+ " form <protocol>:<hostname>[:port] where"
+ " <protocol> is either tcp or udp.\n"
" -p <product> Specify product name.\n"
" -c <cmdline> Override kernel commandline.\n"
" -i <vendor id> Specify a custom USB vendor id.\n"
diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt
index 4aa48b1..2801703 100644
--- a/fastboot/fastboot_protocol.txt
+++ b/fastboot/fastboot_protocol.txt
@@ -17,9 +17,9 @@
* The protocol is entirely host-driven and synchronous (unlike the
multi-channel, bi-directional, asynchronous ADB protocol)
-* TCP
+* TCP or UDP
* Device must be reachable via IP.
- * Device will act as the TCP server, fastboot will be the client.
+ * Device will act as the server, fastboot will be the client.
* Fastboot data is wrapped in a simple protocol; see below for details.
@@ -217,3 +217,226 @@
Host [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
Device [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x04]OKAY
Host <disconnect>
+
+
+UDP Protocol v1
+---------------
+
+The UDP protocol is more complex than TCP since we must implement reliability
+to ensure no packets are lost, but the general concept of wrapping the fastboot
+protocol is the same.
+
+Overview:
+ 1. As with TCP, the device will listen on UDP port 5554.
+ 2. Maximum UDP packet size is negotiated during initialization.
+ 3. The host drives all communication; the device may only send a packet as a
+ response to a host packet.
+ 4. If the host does not receive a response in 500ms it will re-transmit.
+
+-- UDP Packet format --
+ +----------+----+-------+-------+--------------------+
+ | Byte # | 0 | 1 | 2 - 3 | 4+ |
+ +----------+----+-------+-------+--------------------+
+ | Contents | ID | Flags | Seq # | Data |
+ +----------+----+-------+-------+--------------------+
+
+ ID Packet ID:
+ 0x00: Error.
+ 0x01: Query.
+ 0x02: Initialization.
+ 0x03: Fastboot.
+
+ Packet types are described in more detail below.
+
+ Flags Packet flags: 0 0 0 0 0 0 0 C
+ C=1 indicates a continuation packet; the data is too large and will
+ continue in the next packet.
+
+ Remaining bits are reserved for future use and must be set to 0.
+
+ Seq # 2-byte packet sequence number (big-endian). The host will increment
+ this by 1 with each new packet, and the device must provide the
+ corresponding sequence number in the response packets.
+
+ Data Packet data, not present in all packets.
+
+-- Packet Types --
+Query The host sends a query packet once on startup to sync with the device.
+ The host will not know the current sequence number, so the device must
+ respond to all query packets regardless of sequence number.
+
+ The response data field should contain a 2-byte big-endian value
+ giving the next expected sequence number.
+
+Init The host sends an init packet once the query response is returned. The
+ device must abort any in-progress operation and prepare for a new
+ fastboot session. This message is meant to allow recovery if a
+ previous session failed, e.g. due to network error or user Ctrl+C.
+
+ The data field contains two big-endian 2-byte values, a protocol
+ version and the max UDP packet size (including the 4-byte header).
+ Both the host and device will send these values, and in each case
+ the minimum of the sent values must be used.
+
+Fastboot These packets wrap the fastboot protocol. To write, the host will
+ send a packet with fastboot data, and the device will reply with an
+ empty packet as an ACK. To read, the host will send an empty packet,
+ and the device will reply with fastboot data. The device may not give
+ any data in the ACK packet.
+
+Error The device may respond to any packet with an error packet to indicate
+ a UDP protocol error. The data field should contain an ASCII string
+ describing the error. This is the only case where a device is allowed
+ to return a packet ID other than the one sent by the host.
+
+-- Packet Size --
+The maximum packet size is negotiated by the host and device in the Init packet.
+Devices must support at least 512-byte packets, but packet size has a direct
+correlation with download speed, so devices are strongly suggested to support at
+least 1024-byte packets. On a local network with 0.5ms round-trip time this will
+provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly
+less.
+
+Query and Initialization packets, which are sent before size negotiation is
+complete, must always be 512 bytes or less.
+
+-- Packet Re-Transmission --
+The host will re-transmit any packet that does not receive a response. The
+requirement of exactly one device response packet per host packet is how we
+achieve reliability and in-order delivery of packets.
+
+For simplicity of implementation, there is no windowing of multiple
+unacknowledged packets in this version of the protocol. The host will continue
+to send the same packet until a response is received. Windowing functionality
+may be implemented in future versions if necessary to increase performance.
+
+The first Query packet will only be attempted a small number of times, but
+subsequent packets will attempt to retransmit for at least 1 minute before
+giving up. This means a device may safely ignore host UDP packets for up to 1
+minute during long operations, e.g. writing to flash.
+
+-- Continuation Packets --
+Any packet may set the continuation flag to indicate that the data is
+incomplete. Large data such as downloading an image may require many
+continuation packets. The receiver should respond to a continuation packet with
+an empty packet to acknowledge receipt. See examples below.
+
+-- Summary --
+The host starts with a Query packet, then an Initialization packet, after
+which only Fastboot packets are sent. Fastboot packets may contain data from
+the host for writes, or from the device for reads, but not both.
+
+Given a next expected sequence number S and a received packet P, the device
+behavior should be:
+ if P is a Query packet:
+ * respond with a Query packet with S in the data field
+ else if P has sequence == S:
+ * process P and take any required action
+ * create a response packet R with the same ID and sequence as P, containing
+ any response data required.
+ * transmit R and save it in case of re-transmission
+ * increment S
+ else if P has sequence == S - 1:
+ * re-transmit the saved response packet R from above
+ else:
+ * ignore the packet
+
+-- Examples --
+In the examples below, S indicates the starting client sequence number.
+
+Host Client
+======================================================================
+[Initialization, S = 0x55AA]
+[Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
+[Resulting values to use: version = 1, max packet size = 1024]
+ID Flag SeqH SeqL Data ID Flag SeqH SeqL Data
+----------------------------------------------------------------------
+0x01 0x00 0x00 0x00
+ 0x01 0x00 0x00 0x00 0x55 0xAA
+0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
+ 0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00
+
+----------------------------------------------------------------------
+[fastboot "getvar" commands, S = 0x0001]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0x00 0x01 getvar:version
+ 0x03 0x00 0x00 0x01
+0x03 0x00 0x00 0x02
+ 0x03 0x00 0x00 0x02 OKAY0.4
+0x03 0x00 0x00 0x03 getvar:foo
+ 0x03 0x00 0x00 0x03
+0x03 0x00 0x00 0x04
+ 0x03 0x00 0x00 0x04 OKAY
+
+----------------------------------------------------------------------
+[fastboot "INFO" responses, S = 0x0000]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0x00 0x00 <command>
+ 0x03 0x00 0x00 0x00
+0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 INFOWait1
+0x03 0x00 0x00 0x02
+ 0x03 0x00 0x00 0x02 INFOWait2
+0x03 0x00 0x00 0x03
+ 0x03 0x00 0x00 0x03 OKAY
+
+----------------------------------------------------------------------
+[Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
+ID Flag SeqH SeqL Data ID Flag SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0xFF 0xFF download:0000834
+ 0x03 0x00 0xFF 0xFF
+0x03 0x00 0x00 0x00
+ 0x03 0x00 0x00 0x00 DATA0000834
+0x03 0x01 0x00 0x01 <1020 bytes>
+ 0x03 0x00 0x00 0x01
+0x03 0x01 0x00 0x02 <1020 bytes>
+ 0x03 0x00 0x00 0x02
+0x03 0x00 0x00 0x03 <60 bytes>
+ 0x03 0x00 0x00 0x03
+0x03 0x00 0x00 0x04
+ 0x03 0x00 0x00 0x04 OKAY
+
+----------------------------------------------------------------------
+[Unknown ID error, S = 0x0000]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x10 0x00 0x00 0x00
+ 0x00 0x00 0x00 0x00 <error message>
+
+----------------------------------------------------------------------
+[Host packet loss and retransmission, S = 0x0000]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0x00 0x00 getvar:version [lost]
+0x03 0x00 0x00 0x00 getvar:version [lost]
+0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+
+----------------------------------------------------------------------
+[Client packet loss and retransmission, S = 0x0000]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00 [lost]
+0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00 [lost]
+0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+
+----------------------------------------------------------------------
+[Host packet delayed, S = 0x0000]
+ID Flags SeqH SeqL Data ID Flags SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0x00 0x00 getvar:version [delayed]
+0x03 0x00 0x00 0x00 getvar:version
+ 0x03 0x00 0x00 0x00
+0x03 0x00 0x00 0x01
+ 0x03 0x00 0x00 0x01 OKAY0.4
+0x03 0x00 0x00 0x00 getvar:version [arrives late with old seq#, is ignored]
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index d49f47f..14ecd93 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -48,18 +48,6 @@
return ret;
}
-bool Socket::SetReceiveTimeout(int timeout_ms) {
- if (timeout_ms != receive_timeout_ms_) {
- if (socket_set_receive_timeout(sock_, timeout_ms) == 0) {
- receive_timeout_ms_ = timeout_ms;
- return true;
- }
- return false;
- }
-
- return true;
-}
-
ssize_t Socket::ReceiveAll(void* data, size_t length, int timeout_ms) {
size_t total = 0;
@@ -82,6 +70,40 @@
return socket_get_local_port(sock_);
}
+// According to Windows setsockopt() documentation, if a Windows socket times out during send() or
+// recv() the state is indeterminate and should not be used. Our UDP protocol relies on being able
+// to re-send after a timeout, so we must use select() rather than SO_RCVTIMEO.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx.
+bool Socket::WaitForRecv(int timeout_ms) {
+ receive_timed_out_ = false;
+
+ // In our usage |timeout_ms| <= 0 means block forever, so just return true immediately and let
+ // the subsequent recv() do the blocking.
+ if (timeout_ms <= 0) {
+ return true;
+ }
+
+ // select() doesn't always check this case and will block for |timeout_ms| if we let it.
+ if (sock_ == INVALID_SOCKET) {
+ return false;
+ }
+
+ fd_set read_set;
+ FD_ZERO(&read_set);
+ FD_SET(sock_, &read_set);
+
+ timeval timeout;
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+
+ int result = TEMP_FAILURE_RETRY(select(sock_ + 1, &read_set, nullptr, nullptr, &timeout));
+
+ if (result == 0) {
+ receive_timed_out_ = true;
+ }
+ return result == 1;
+}
+
// Implements the Socket interface for UDP.
class UdpSocket : public Socket {
public:
@@ -127,7 +149,7 @@
}
ssize_t UdpSocket::Receive(void* data, size_t length, int timeout_ms) {
- if (!SetReceiveTimeout(timeout_ms)) {
+ if (!WaitForRecv(timeout_ms)) {
return -1;
}
@@ -206,7 +228,7 @@
}
ssize_t TcpSocket::Receive(void* data, size_t length, int timeout_ms) {
- if (!SetReceiveTimeout(timeout_ms)) {
+ if (!WaitForRecv(timeout_ms)) {
return -1;
}
diff --git a/fastboot/socket.h b/fastboot/socket.h
index c0bd7c9..de543db 100644
--- a/fastboot/socket.h
+++ b/fastboot/socket.h
@@ -81,13 +81,17 @@
virtual bool Send(std::vector<cutils_socket_buffer_t> buffers) = 0;
// Waits up to |timeout_ms| to receive up to |length| bytes of data. |timout_ms| of 0 will
- // block forever. Returns the number of bytes received or -1 on error/timeout. On timeout
- // errno will be set to EAGAIN or EWOULDBLOCK.
+ // block forever. Returns the number of bytes received or -1 on error/timeout; see
+ // ReceiveTimedOut() to distinguish between the two.
virtual ssize_t Receive(void* data, size_t length, int timeout_ms) = 0;
// Calls Receive() until exactly |length| bytes have been received or an error occurs.
virtual ssize_t ReceiveAll(void* data, size_t length, int timeout_ms);
+ // Returns true if the last Receive() call timed out normally and can be retried; fatal errors
+ // or successful reads will return false.
+ bool ReceiveTimedOut() { return receive_timed_out_; }
+
// Closes the socket. Returns 0 on success, -1 on error.
virtual int Close();
@@ -102,10 +106,13 @@
// Protected constructor to force factory function use.
Socket(cutils_socket_t sock);
- // Update the socket receive timeout if necessary.
- bool SetReceiveTimeout(int timeout_ms);
+ // Blocks up to |timeout_ms| until a read is possible on |sock_|, and sets |receive_timed_out_|
+ // as appropriate to help distinguish between normal timeouts and fatal errors. Returns true if
+ // a subsequent recv() on |sock_| will complete without blocking or if |timeout_ms| <= 0.
+ bool WaitForRecv(int timeout_ms);
cutils_socket_t sock_ = INVALID_SOCKET;
+ bool receive_timed_out_ = false;
// Non-class functions we want to override during tests to verify functionality. Implementation
// should call this rather than using socket_send_buffers() directly.
@@ -113,8 +120,6 @@
socket_send_buffers_function_ = &socket_send_buffers;
private:
- int receive_timeout_ms_ = 0;
-
FRIEND_TEST(SocketTest, TestTcpSendBuffers);
FRIEND_TEST(SocketTest, TestUdpSendBuffers);
diff --git a/fastboot/socket_mock.cpp b/fastboot/socket_mock.cpp
index c962f30..2531b53 100644
--- a/fastboot/socket_mock.cpp
+++ b/fastboot/socket_mock.cpp
@@ -55,7 +55,7 @@
return false;
}
- bool return_value = events_.front().return_value;
+ bool return_value = events_.front().status;
events_.pop();
return return_value;
}
@@ -76,21 +76,28 @@
return -1;
}
- if (events_.front().type != EventType::kReceive) {
+ const Event& event = events_.front();
+ if (event.type != EventType::kReceive) {
ADD_FAILURE() << "Receive() was called out-of-order";
return -1;
}
- if (events_.front().return_value > static_cast<ssize_t>(length)) {
- ADD_FAILURE() << "Receive(): not enough bytes (" << length << ") for "
- << events_.front().message;
+ const std::string& message = event.message;
+ if (message.length() > length) {
+ ADD_FAILURE() << "Receive(): not enough bytes (" << length << ") for " << message;
return -1;
}
- ssize_t return_value = events_.front().return_value;
- if (return_value > 0) {
- memcpy(data, events_.front().message.data(), return_value);
+ receive_timed_out_ = event.status;
+ ssize_t return_value = message.length();
+
+ // Empty message indicates failure.
+ if (message.empty()) {
+ return_value = -1;
+ } else {
+ memcpy(data, message.data(), message.length());
}
+
events_.pop();
return return_value;
}
@@ -124,18 +131,21 @@
}
void SocketMock::AddReceive(std::string message) {
- ssize_t return_value = message.length();
- events_.push(Event(EventType::kReceive, std::move(message), return_value, nullptr));
+ events_.push(Event(EventType::kReceive, std::move(message), false, nullptr));
+}
+
+void SocketMock::AddReceiveTimeout() {
+ events_.push(Event(EventType::kReceive, "", true, nullptr));
}
void SocketMock::AddReceiveFailure() {
- events_.push(Event(EventType::kReceive, "", -1, nullptr));
+ events_.push(Event(EventType::kReceive, "", false, nullptr));
}
void SocketMock::AddAccept(std::unique_ptr<Socket> sock) {
- events_.push(Event(EventType::kAccept, "", 0, std::move(sock)));
+ events_.push(Event(EventType::kAccept, "", false, std::move(sock)));
}
-SocketMock::Event::Event(EventType _type, std::string _message, ssize_t _return_value,
+SocketMock::Event::Event(EventType _type, std::string _message, ssize_t _status,
std::unique_ptr<Socket> _sock)
- : type(_type), message(_message), return_value(_return_value), sock(std::move(_sock)) {}
+ : type(_type), message(_message), status(_status), sock(std::move(_sock)) {}
diff --git a/fastboot/socket_mock.h b/fastboot/socket_mock.h
index 41fe06d..eacd6bb 100644
--- a/fastboot/socket_mock.h
+++ b/fastboot/socket_mock.h
@@ -71,7 +71,10 @@
// Adds data to provide for Receive().
void AddReceive(std::string message);
- // Adds a Receive() failure.
+ // Adds a Receive() timeout after which ReceiveTimedOut() will return true.
+ void AddReceiveTimeout();
+
+ // Adds a Receive() failure after which ReceiveTimedOut() will return false.
void AddReceiveFailure();
// Adds a Socket to return from Accept().
@@ -81,12 +84,12 @@
enum class EventType { kSend, kReceive, kAccept };
struct Event {
- Event(EventType _type, std::string _message, ssize_t _return_value,
+ Event(EventType _type, std::string _message, ssize_t _status,
std::unique_ptr<Socket> _sock);
EventType type;
std::string message;
- ssize_t return_value;
+ bool status; // Return value for Send() or timeout status for Receive().
std::unique_ptr<Socket> sock;
};
diff --git a/fastboot/socket_test.cpp b/fastboot/socket_test.cpp
index cc71075..affbdfd 100644
--- a/fastboot/socket_test.cpp
+++ b/fastboot/socket_test.cpp
@@ -28,7 +28,8 @@
#include <gtest/gtest-spi.h>
#include <gtest/gtest.h>
-enum { kTestTimeoutMs = 3000 };
+static constexpr int kShortTimeoutMs = 10;
+static constexpr int kTestTimeoutMs = 3000;
// Creates connected sockets |server| and |client|. Returns true on success.
bool MakeConnectedSockets(Socket::Protocol protocol, std::unique_ptr<Socket>* server,
@@ -87,6 +88,50 @@
}
}
+TEST(SocketTest, TestReceiveTimeout) {
+ std::unique_ptr<Socket> server, client;
+ char buffer[16];
+
+ for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+ ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
+
+ EXPECT_EQ(-1, server->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+ EXPECT_TRUE(server->ReceiveTimedOut());
+
+ EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+ EXPECT_TRUE(client->ReceiveTimedOut());
+ }
+
+ // UDP will wait for timeout if the other side closes.
+ ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kUdp, &server, &client));
+ EXPECT_EQ(0, server->Close());
+ EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+ EXPECT_TRUE(client->ReceiveTimedOut());
+}
+
+TEST(SocketTest, TestReceiveFailure) {
+ std::unique_ptr<Socket> server, client;
+ char buffer[16];
+
+ for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+ ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
+
+ EXPECT_EQ(0, server->Close());
+ EXPECT_EQ(-1, server->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+ EXPECT_FALSE(server->ReceiveTimedOut());
+
+ EXPECT_EQ(0, client->Close());
+ EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+ EXPECT_FALSE(client->ReceiveTimedOut());
+ }
+
+ // TCP knows right away when the other side closes and returns 0 to indicate EOF.
+ ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kTcp, &server, &client));
+ EXPECT_EQ(0, server->Close());
+ EXPECT_EQ(0, client->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+ EXPECT_FALSE(client->ReceiveTimedOut());
+}
+
// Tests sending and receiving large packets.
TEST(SocketTest, TestLargePackets) {
std::string message(1024, '\0');
@@ -290,6 +335,11 @@
mock->AddReceiveFailure();
EXPECT_FALSE(ReceiveString(mock, "foo"));
+ EXPECT_FALSE(mock->ReceiveTimedOut());
+
+ mock->AddReceiveTimeout();
+ EXPECT_FALSE(ReceiveString(mock, "foo"));
+ EXPECT_TRUE(mock->ReceiveTimedOut());
mock->AddReceive("foo");
mock->AddReceiveFailure();
diff --git a/fastboot/tcp.cpp b/fastboot/tcp.cpp
index da2880a..e42c4e1 100644
--- a/fastboot/tcp.cpp
+++ b/fastboot/tcp.cpp
@@ -28,6 +28,7 @@
#include "tcp.h"
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
namespace tcp {
@@ -98,7 +99,8 @@
return false;
}
- char buffer[kHandshakeLength];
+ char buffer[kHandshakeLength + 1];
+ buffer[kHandshakeLength] = '\0';
if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) != kHandshakeLength) {
*error = android::base::StringPrintf(
"No initialization message received (%s). Target may not support TCP fastboot",
@@ -111,9 +113,10 @@
return false;
}
- if (memcmp(buffer + 2, "01", 2) != 0) {
+ int version = 0;
+ if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {
*error = android::base::StringPrintf("Unknown TCP protocol version %s (host version %02d)",
- std::string(buffer + 2, 2).c_str(), kProtocolVersion);
+ buffer + 2, kProtocolVersion);
return false;
}
diff --git a/fastboot/tcp_test.cpp b/fastboot/tcp_test.cpp
index 7d80d76..6e867ae 100644
--- a/fastboot/tcp_test.cpp
+++ b/fastboot/tcp_test.cpp
@@ -42,6 +42,16 @@
EXPECT_EQ("", error);
}
+TEST(TcpConnectTest, TestNewerVersionSuccess) {
+ std::unique_ptr<SocketMock> mock(new SocketMock);
+ mock->ExpectSend("FB01");
+ mock->AddReceive("FB99");
+
+ std::string error;
+ EXPECT_NE(nullptr, tcp::internal::Connect(std::move(mock), &error));
+ EXPECT_EQ("", error);
+}
+
TEST(TcpConnectTest, TestSendFailure) {
std::unique_ptr<SocketMock> mock(new SocketMock);
mock->ExpectSendFailure("FB01");
@@ -74,11 +84,11 @@
TEST(TcpConnectTest, TestUnknownVersionFailure) {
std::unique_ptr<SocketMock> mock(new SocketMock);
mock->ExpectSend("FB01");
- mock->AddReceive("FB02");
+ mock->AddReceive("FB00");
std::string error;
EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));
- EXPECT_EQ("Unknown TCP protocol version 02 (host version 01)", error);
+ EXPECT_EQ("Unknown TCP protocol version 00 (host version 01)", error);
}
// Fixture to configure a SocketMock for a successful TCP connection.
diff --git a/fastboot/udp.cpp b/fastboot/udp.cpp
new file mode 100644
index 0000000..b36bd60
--- /dev/null
+++ b/fastboot/udp.cpp
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+// This file implements the fastboot UDP protocol; see fastboot_protocol.txt for documentation.
+
+#include "udp.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+
+#include "socket.h"
+
+namespace udp {
+
+using namespace internal;
+
+constexpr size_t kMinPacketSize = 512;
+constexpr size_t kHeaderSize = 4;
+
+enum Index {
+ kIndexId = 0,
+ kIndexFlags = 1,
+ kIndexSeqH = 2,
+ kIndexSeqL = 3,
+};
+
+// Extracts a big-endian uint16_t from a byte array.
+static uint16_t ExtractUint16(const uint8_t* bytes) {
+ return (static_cast<uint16_t>(bytes[0]) << 8) | bytes[1];
+}
+
+// Packet header handling.
+class Header {
+ public:
+ Header();
+ ~Header() = default;
+
+ uint8_t id() const { return bytes_[kIndexId]; }
+ const uint8_t* bytes() const { return bytes_; }
+
+ void Set(uint8_t id, uint16_t sequence, Flag flag);
+
+ // Checks whether |response| is a match for this header.
+ bool Matches(const uint8_t* response);
+
+ private:
+ uint8_t bytes_[kHeaderSize];
+};
+
+Header::Header() {
+ Set(kIdError, 0, kFlagNone);
+}
+
+void Header::Set(uint8_t id, uint16_t sequence, Flag flag) {
+ bytes_[kIndexId] = id;
+ bytes_[kIndexFlags] = flag;
+ bytes_[kIndexSeqH] = sequence >> 8;
+ bytes_[kIndexSeqL] = sequence;
+}
+
+bool Header::Matches(const uint8_t* response) {
+ // Sequence numbers must be the same to match, but the response ID can either be the same
+ // or an error response which is always accepted.
+ return bytes_[kIndexSeqH] == response[kIndexSeqH] &&
+ bytes_[kIndexSeqL] == response[kIndexSeqL] &&
+ (bytes_[kIndexId] == response[kIndexId] || response[kIndexId] == kIdError);
+}
+
+// Implements the Transport interface to work with the fastboot engine.
+class UdpTransport : public Transport {
+ public:
+ // Factory function so we can return nullptr if initialization fails.
+ static std::unique_ptr<UdpTransport> NewTransport(std::unique_ptr<Socket> socket,
+ std::string* error);
+ ~UdpTransport() override = default;
+
+ ssize_t Read(void* data, size_t length) override;
+ ssize_t Write(const void* data, size_t length) override;
+ int Close() override;
+
+ private:
+ UdpTransport(std::unique_ptr<Socket> socket) : socket_(std::move(socket)) {}
+
+ // Performs the UDP initialization procedure. Returns true on success.
+ bool InitializeProtocol(std::string* error);
+
+ // Sends |length| bytes from |data| and waits for the response packet up to |attempts| times.
+ // Continuation packets are handled automatically and any return data is written to |rx_data|.
+ // Excess bytes that cannot fit in |rx_data| are dropped.
+ // On success, returns the number of response data bytes received, which may be greater than
+ // |rx_length|. On failure, returns -1 and fills |error| on failure.
+ ssize_t SendData(Id id, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+ size_t rx_length, int attempts, std::string* error);
+
+ // Helper for SendData(); sends a single packet and handles the response. |header| specifies
+ // the initial outgoing packet information but may be modified by this function.
+ ssize_t SendSinglePacketHelper(Header* header, const uint8_t* tx_data, size_t tx_length,
+ uint8_t* rx_data, size_t rx_length, int attempts,
+ std::string* error);
+
+ std::unique_ptr<Socket> socket_;
+ int sequence_ = -1;
+ size_t max_data_length_ = kMinPacketSize - kHeaderSize;
+ std::vector<uint8_t> rx_packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(UdpTransport);
+};
+
+std::unique_ptr<UdpTransport> UdpTransport::NewTransport(std::unique_ptr<Socket> socket,
+ std::string* error) {
+ std::unique_ptr<UdpTransport> transport(new UdpTransport(std::move(socket)));
+
+ if (!transport->InitializeProtocol(error)) {
+ return nullptr;
+ }
+
+ return transport;
+}
+
+bool UdpTransport::InitializeProtocol(std::string* error) {
+ uint8_t rx_data[4];
+
+ sequence_ = 0;
+ rx_packet_.resize(kMinPacketSize);
+
+ // First send the query packet to sync with the target. Only attempt this a small number of
+ // times so we can fail out quickly if the target isn't available.
+ ssize_t rx_bytes = SendData(kIdDeviceQuery, nullptr, 0, rx_data, sizeof(rx_data),
+ kMaxConnectAttempts, error);
+ if (rx_bytes == -1) {
+ return false;
+ } else if (rx_bytes < 2) {
+ *error = "invalid query response from target";
+ return false;
+ }
+ // The first two bytes contain the next expected sequence number.
+ sequence_ = ExtractUint16(rx_data);
+
+ // Now send the initialization packet with our version and maximum packet size.
+ uint8_t init_data[] = {kProtocolVersion >> 8, kProtocolVersion & 0xFF,
+ kHostMaxPacketSize >> 8, kHostMaxPacketSize & 0xFF};
+ rx_bytes = SendData(kIdInitialization, init_data, sizeof(init_data), rx_data, sizeof(rx_data),
+ kMaxTransmissionAttempts, error);
+ if (rx_bytes == -1) {
+ return false;
+ } else if (rx_bytes < 4) {
+ *error = "invalid initialization response from target";
+ return false;
+ }
+
+ // The first two data bytes contain the version, the second two bytes contain the target max
+ // supported packet size, which must be at least 512 bytes.
+ uint16_t version = ExtractUint16(rx_data);
+ if (version < kProtocolVersion) {
+ *error = android::base::StringPrintf("target reported invalid protocol version %d",
+ version);
+ return false;
+ }
+ uint16_t packet_size = ExtractUint16(rx_data + 2);
+ if (packet_size < kMinPacketSize) {
+ *error = android::base::StringPrintf("target reported invalid packet size %d", packet_size);
+ return false;
+ }
+
+ packet_size = std::min(kHostMaxPacketSize, packet_size);
+ max_data_length_ = packet_size - kHeaderSize;
+ rx_packet_.resize(packet_size);
+
+ return true;
+}
+
+// SendData() is just responsible for chunking |data| into packets until it's all been sent.
+// Per-packet timeout/retransmission logic is done in SendSinglePacketHelper().
+ssize_t UdpTransport::SendData(Id id, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+ size_t rx_length, int attempts, std::string* error) {
+ if (socket_ == nullptr) {
+ *error = "socket is closed";
+ return -1;
+ }
+
+ Header header;
+ size_t packet_data_length;
+ ssize_t ret = 0;
+ // We often send header-only packets with no data as part of the protocol, so always send at
+ // least once even if |length| == 0, then repeat until we've sent all of |data|.
+ do {
+ // Set the continuation flag and truncate packet data if needed.
+ if (tx_length > max_data_length_) {
+ packet_data_length = max_data_length_;
+ header.Set(id, sequence_, kFlagContinuation);
+ } else {
+ packet_data_length = tx_length;
+ header.Set(id, sequence_, kFlagNone);
+ }
+
+ ssize_t bytes = SendSinglePacketHelper(&header, tx_data, packet_data_length, rx_data,
+ rx_length, attempts, error);
+
+ // Advance our read and write buffers for the next packet. Keep going even if we run out
+ // of receive buffer space so we can detect overflows.
+ if (bytes == -1) {
+ return -1;
+ } else if (static_cast<size_t>(bytes) < rx_length) {
+ rx_data += bytes;
+ rx_length -= bytes;
+ } else {
+ rx_data = nullptr;
+ rx_length = 0;
+ }
+
+ tx_length -= packet_data_length;
+ tx_data += packet_data_length;
+
+ ret += bytes;
+ } while (tx_length > 0);
+
+ return ret;
+}
+
+ssize_t UdpTransport::SendSinglePacketHelper(
+ Header* header, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+ size_t rx_length, const int attempts, std::string* error) {
+ ssize_t total_data_bytes = 0;
+ error->clear();
+
+ int attempts_left = attempts;
+ while (attempts_left > 0) {
+ if (!socket_->Send({{header->bytes(), kHeaderSize}, {tx_data, tx_length}})) {
+ *error = Socket::GetErrorMessage();
+ return -1;
+ }
+
+ // Keep receiving until we get a matching response or we timeout.
+ ssize_t bytes = 0;
+ do {
+ bytes = socket_->Receive(rx_packet_.data(), rx_packet_.size(), kResponseTimeoutMs);
+ if (bytes == -1) {
+ if (socket_->ReceiveTimedOut()) {
+ break;
+ }
+ *error = Socket::GetErrorMessage();
+ return -1;
+ } else if (bytes < static_cast<ssize_t>(kHeaderSize)) {
+ *error = "protocol error: incomplete header";
+ return -1;
+ }
+ } while (!header->Matches(rx_packet_.data()));
+
+ if (socket_->ReceiveTimedOut()) {
+ --attempts_left;
+ continue;
+ }
+ ++sequence_;
+
+ // Save to |error| or |rx_data| as appropriate.
+ if (rx_packet_[kIndexId] == kIdError) {
+ error->append(rx_packet_.data() + kHeaderSize, rx_packet_.data() + bytes);
+ } else {
+ total_data_bytes += bytes - kHeaderSize;
+ size_t rx_data_bytes = std::min<size_t>(bytes - kHeaderSize, rx_length);
+ if (rx_data_bytes > 0) {
+ memcpy(rx_data, rx_packet_.data() + kHeaderSize, rx_data_bytes);
+ rx_data += rx_data_bytes;
+ rx_length -= rx_data_bytes;
+ }
+ }
+
+ // If the response has a continuation flag we need to prompt for more data by sending
+ // an empty packet.
+ if (rx_packet_[kIndexFlags] & kFlagContinuation) {
+ // We got a valid response so reset our attempt counter.
+ attempts_left = attempts;
+ header->Set(rx_packet_[kIndexId], sequence_, kFlagNone);
+ tx_data = nullptr;
+ tx_length = 0;
+ continue;
+ }
+
+ break;
+ }
+
+ if (attempts_left <= 0) {
+ *error = "no response from target";
+ return -1;
+ }
+
+ if (rx_packet_[kIndexId] == kIdError) {
+ *error = "target reported error: " + *error;
+ return -1;
+ }
+
+ return total_data_bytes;
+}
+
+ssize_t UdpTransport::Read(void* data, size_t length) {
+ // Read from the target by sending an empty packet.
+ std::string error;
+ ssize_t bytes = SendData(kIdFastboot, nullptr, 0, reinterpret_cast<uint8_t*>(data), length,
+ kMaxTransmissionAttempts, &error);
+
+ if (bytes == -1) {
+ fprintf(stderr, "UDP error: %s\n", error.c_str());
+ return -1;
+ } else if (static_cast<size_t>(bytes) > length) {
+ // Fastboot protocol error: the target sent more data than our fastboot engine was prepared
+ // to receive.
+ fprintf(stderr, "UDP error: receive overflow, target sent too much fastboot data\n");
+ return -1;
+ }
+
+ return bytes;
+}
+
+ssize_t UdpTransport::Write(const void* data, size_t length) {
+ std::string error;
+ ssize_t bytes = SendData(kIdFastboot, reinterpret_cast<const uint8_t*>(data), length, nullptr,
+ 0, kMaxTransmissionAttempts, &error);
+
+ if (bytes == -1) {
+ fprintf(stderr, "UDP error: %s\n", error.c_str());
+ return -1;
+ } else if (bytes > 0) {
+ // UDP protocol error: only empty ACK packets are allowed when writing to a device.
+ fprintf(stderr, "UDP error: target sent fastboot data out-of-turn\n");
+ return -1;
+ }
+
+ return length;
+}
+
+int UdpTransport::Close() {
+ if (socket_ == nullptr) {
+ return 0;
+ }
+
+ int result = socket_->Close();
+ socket_.reset();
+ return result;
+}
+
+std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
+ return internal::Connect(Socket::NewClient(Socket::Protocol::kUdp, hostname, port, error),
+ error);
+}
+
+namespace internal {
+
+std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error) {
+ if (sock == nullptr) {
+ // If Socket creation failed |error| is already set.
+ return nullptr;
+ }
+
+ return UdpTransport::NewTransport(std::move(sock), error);
+}
+
+} // namespace internal
+
+} // namespace udp
diff --git a/fastboot/udp.h b/fastboot/udp.h
new file mode 100644
index 0000000..14f5b35
--- /dev/null
+++ b/fastboot/udp.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef UDP_H_
+#define UDP_H_
+
+#include <memory>
+#include <string>
+
+#include "socket.h"
+#include "transport.h"
+
+namespace udp {
+
+constexpr int kDefaultPort = 5554;
+
+// Returns a newly allocated Transport object connected to |hostname|:|port|. On failure, |error| is
+// filled and nullptr is returned.
+std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error);
+
+// Internal namespace for test use only.
+namespace internal {
+
+constexpr uint16_t kProtocolVersion = 1;
+
+// This will be negotiated with the device so may end up being smaller.
+constexpr uint16_t kHostMaxPacketSize = 8192;
+
+// Retransmission constants. Retransmission timeout must be at least 500ms, and the host must
+// attempt to send packets for at least 1 minute once the device has connected. See
+// fastboot_protocol.txt for more information.
+constexpr int kResponseTimeoutMs = 500;
+constexpr int kMaxConnectAttempts = 4;
+constexpr int kMaxTransmissionAttempts = 60 * 1000 / kResponseTimeoutMs;
+
+enum Id : uint8_t {
+ kIdError = 0x00,
+ kIdDeviceQuery = 0x01,
+ kIdInitialization = 0x02,
+ kIdFastboot = 0x03
+};
+
+enum Flag : uint8_t {
+ kFlagNone = 0x00,
+ kFlagContinuation = 0x01
+};
+
+// Creates a UDP Transport object using a given Socket. Used for unit tests to create a Transport
+// object that uses a SocketMock.
+std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error);
+
+} // namespace internal
+
+} // namespace udp
+
+#endif // UDP_H_
diff --git a/fastboot/udp_test.cpp b/fastboot/udp_test.cpp
new file mode 100644
index 0000000..ff8cf0f
--- /dev/null
+++ b/fastboot/udp_test.cpp
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2015 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 "udp.h"
+
+#include <gtest/gtest.h>
+
+#include "socket.h"
+#include "socket_mock.h"
+
+using namespace udp;
+using namespace udp::internal;
+
+// Some possible corner case sequence numbers we want to check.
+static const uint16_t kTestSequenceNumbers[] = {0x0000, 0x0001, 0x00FF, 0x0100,
+ 0x7FFF, 0x8000, 0xFFFF};
+
+// Converts |value| to a binary big-endian string.
+static std::string PacketValue(uint16_t value) {
+ return std::string{static_cast<char>(value >> 8), static_cast<char>(value)};
+}
+
+// Returns an Error packet.
+static std::string ErrorPacket(uint16_t sequence, const std::string& message = "",
+ char flags = kFlagNone) {
+ return std::string{kIdError, flags} + PacketValue(sequence) + message;
+}
+
+// Returns a Query packet with no data.
+static std::string QueryPacket(uint16_t sequence) {
+ return std::string{kIdDeviceQuery, kFlagNone} + PacketValue(sequence);
+}
+
+// Returns a Query packet with a 2-byte |new_sequence|.
+static std::string QueryPacket(uint16_t sequence, uint16_t new_sequence) {
+ return std::string{kIdDeviceQuery, kFlagNone} + PacketValue(sequence) +
+ PacketValue(new_sequence);
+}
+
+// Returns an Init packet with a 2-byte |version| and |max_packet_size|.
+static std::string InitPacket(uint16_t sequence, uint16_t version, uint16_t max_packet_size) {
+ return std::string{kIdInitialization, kFlagNone} + PacketValue(sequence) +
+ PacketValue(version) + PacketValue(max_packet_size);
+}
+
+// Returns a Fastboot packet with |data|.
+static std::string FastbootPacket(uint16_t sequence, const std::string& data = "",
+ char flags = kFlagNone) {
+ return std::string{kIdFastboot, flags} + PacketValue(sequence) + data;
+}
+
+// Fixture class to test protocol initialization. Usage is to set up the expected calls to the
+// SocketMock object then call UdpConnect() and check the result.
+class UdpConnectTest : public ::testing::Test {
+ public:
+ UdpConnectTest() : mock_socket_(new SocketMock) {}
+
+ // Run the initialization, return whether it was successful or not. This passes ownership of
+ // the current |mock_socket_| but allocates a new one for re-use.
+ bool UdpConnect(std::string* error = nullptr) {
+ std::string local_error;
+ if (error == nullptr) {
+ error = &local_error;
+ }
+ std::unique_ptr<Transport> transport(Connect(std::move(mock_socket_), error));
+ mock_socket_.reset(new SocketMock);
+ return transport != nullptr && error->empty();
+ }
+
+ protected:
+ std::unique_ptr<SocketMock> mock_socket_;
+};
+
+// Tests a successful protocol initialization with various starting sequence numbers.
+TEST_F(UdpConnectTest, InitializationSuccess) {
+ for (uint16_t seq : kTestSequenceNumbers) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, seq));
+ mock_socket_->ExpectSend(InitPacket(seq, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(seq, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+ }
+}
+
+// Tests continuation packets during initialization.
+TEST_F(UdpConnectTest, InitializationContinuationSuccess) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(std::string{kIdDeviceQuery, kFlagContinuation, 0, 0, 0x44});
+ mock_socket_->ExpectSend(std::string{kIdDeviceQuery, kFlagNone, 0, 1});
+ mock_socket_->AddReceive(std::string{kIdDeviceQuery, kFlagNone, 0, 1, 0x55});
+
+ mock_socket_->ExpectSend(InitPacket(0x4455, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x55, 0});
+ mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x56});
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x56, 1});
+ mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x57});
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x57, 2});
+ mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x58});
+ mock_socket_->AddReceive(std::string{kIdInitialization, kFlagNone, 0x44, 0x58, 0});
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+
+// Tests a mismatched version number; as long as the minimum of the two versions is supported
+// we should allow the connection.
+TEST_F(UdpConnectTest, InitializationVersionMismatch) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, 2, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, 0, 1024));
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, QueryResponseTimeoutFailure) {
+ for (int i = 0; i < kMaxConnectAttempts; ++i) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceiveTimeout();
+ }
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, QueryResponseReceiveFailure) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceiveFailure();
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, InitResponseTimeoutFailure) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ for (int i = 0; i < kMaxTransmissionAttempts; ++i) {
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceiveTimeout();
+ }
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, InitResponseReceiveFailure) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceiveFailure();
+
+ EXPECT_FALSE(UdpConnect());
+}
+
+// Tests that we can recover up to the maximum number of allowed retries.
+TEST_F(UdpConnectTest, ResponseRecovery) {
+ // The device query packet can recover from up to (kMaxConnectAttempts - 1) timeouts.
+ for (int i = 0; i < kMaxConnectAttempts - 1; ++i) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceiveTimeout();
+ }
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+
+ // Subsequent packets try up to (kMaxTransmissionAttempts - 1) times.
+ for (int i = 0; i < kMaxTransmissionAttempts - 1; ++i) {
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceiveTimeout();
+ }
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests that the host can handle receiving additional bytes for forward compatibility.
+TEST_F(UdpConnectTest, ExtraResponseDataSuccess) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0) + "foo");
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024) + "bar");
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests mismatched response sequence numbers. A wrong sequence number is interpreted as a previous
+// retransmission and just ignored so we should be able to recover.
+TEST_F(UdpConnectTest, WrongSequenceRecovery) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(1, 0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(1, kProtocolVersion, 1024));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests mismatched response IDs. This should also be interpreted as a retransmission and ignored.
+TEST_F(UdpConnectTest, WrongIdRecovery) {
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(FastbootPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(FastbootPacket(0));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+ EXPECT_TRUE(UdpConnect());
+}
+
+// Tests an invalid query response. Query responses must have at least 2 bytes of data.
+TEST_F(UdpConnectTest, InvalidQueryResponseFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("invalid query response from target", error);
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0) + std::string{0x00});
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("invalid query response from target", error);
+}
+
+// Tests an invalid initialization response. Max packet size must be at least 512 bytes.
+TEST_F(UdpConnectTest, InvalidInitResponseFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 511));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("target reported invalid packet size 511", error);
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(InitPacket(0, 0, 1024));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_EQ("target reported invalid protocol version 0", error);
+}
+
+TEST_F(UdpConnectTest, ErrorResponseFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(ErrorPacket(0, "error1"));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_NE(std::string::npos, error.find("error1"));
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, 0));
+ mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(ErrorPacket(0, "error2"));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_NE(std::string::npos, error.find("error2"));
+}
+
+// Tests an error response with continuation flag.
+TEST_F(UdpConnectTest, ErrorContinuationFailure) {
+ std::string error;
+
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(ErrorPacket(0, "error1", kFlagContinuation));
+ mock_socket_->ExpectSend(ErrorPacket(1));
+ mock_socket_->AddReceive(ErrorPacket(1, " ", kFlagContinuation));
+ mock_socket_->ExpectSend(ErrorPacket(2));
+ mock_socket_->AddReceive(ErrorPacket(2, "error2"));
+
+ EXPECT_FALSE(UdpConnect(&error));
+ EXPECT_NE(std::string::npos, error.find("error1 error2"));
+}
+
+// Fixture class to test UDP Transport read/write functionality.
+class UdpTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ // Create |transport_| starting at sequence 0 with 512 byte max packet size. Tests can call
+ // InitializeTransport() again to change settings.
+ ASSERT_TRUE(InitializeTransport(0, 512));
+ }
+
+ // Sets up |mock_socket_| to correctly initialize the protocol and creates |transport_|. This
+ // can be called multiple times in a test if needed.
+ bool InitializeTransport(uint16_t starting_sequence, int device_max_packet_size = 512) {
+ mock_socket_ = new SocketMock;
+ mock_socket_->ExpectSend(QueryPacket(0));
+ mock_socket_->AddReceive(QueryPacket(0, starting_sequence));
+ mock_socket_->ExpectSend(
+ InitPacket(starting_sequence, kProtocolVersion, kHostMaxPacketSize));
+ mock_socket_->AddReceive(
+ InitPacket(starting_sequence, kProtocolVersion, device_max_packet_size));
+
+ std::string error;
+ transport_ = Connect(std::unique_ptr<Socket>(mock_socket_), &error);
+ return transport_ != nullptr && error.empty();
+ }
+
+ // Writes |message| to |transport_|, returns true on success.
+ bool Write(const std::string& message) {
+ return transport_->Write(message.data(), message.length()) ==
+ static_cast<ssize_t>(message.length());
+ }
+
+ // Reads from |transport_|, returns true if it matches |message|.
+ bool Read(const std::string& message) {
+ std::string buffer(message.length(), '\0');
+ return transport_->Read(&buffer[0], buffer.length()) ==
+ static_cast<ssize_t>(message.length()) && buffer == message;
+ }
+
+ protected:
+ // |mock_socket_| is a raw pointer here because we transfer ownership to |transport_| but we
+ // need to retain a pointer to set send and receive expectations.
+ SocketMock* mock_socket_ = nullptr;
+ std::unique_ptr<Transport> transport_;
+};
+
+// Tests sequence behavior with various starting sequence numbers.
+TEST_F(UdpTest, SequenceIncrementCheck) {
+ for (uint16_t seq : kTestSequenceNumbers) {
+ ASSERT_TRUE(InitializeTransport(seq));
+
+ for (int i = 0; i < 10; ++i) {
+ mock_socket_->ExpectSend(FastbootPacket(++seq, "foo"));
+ mock_socket_->AddReceive(FastbootPacket(seq, ""));
+ mock_socket_->ExpectSend(FastbootPacket(++seq, ""));
+ mock_socket_->AddReceive(FastbootPacket(seq, "bar"));
+
+ EXPECT_TRUE(Write("foo"));
+ EXPECT_TRUE(Read("bar"));
+ }
+ }
+}
+
+// Tests sending and receiving a few small packets.
+TEST_F(UdpTest, ReadAndWriteSmallPackets) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceive(FastbootPacket(1, ""));
+ mock_socket_->ExpectSend(FastbootPacket(2, ""));
+ mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+ EXPECT_TRUE(Write("foo"));
+ EXPECT_TRUE(Read("bar"));
+
+ mock_socket_->ExpectSend(FastbootPacket(3, "12345 67890"));
+ mock_socket_->AddReceive(FastbootPacket(3));
+ mock_socket_->ExpectSend(FastbootPacket(4, "\x01\x02\x03\x04\x05"));
+ mock_socket_->AddReceive(FastbootPacket(4));
+
+ EXPECT_TRUE(Write("12345 67890"));
+ EXPECT_TRUE(Write("\x01\x02\x03\x04\x05"));
+
+ // Reads are done by sending empty packets.
+ mock_socket_->ExpectSend(FastbootPacket(5));
+ mock_socket_->AddReceive(FastbootPacket(5, "foo bar baz"));
+ mock_socket_->ExpectSend(FastbootPacket(6));
+ mock_socket_->AddReceive(FastbootPacket(6, "\x01\x02\x03\x04\x05"));
+
+ EXPECT_TRUE(Read("foo bar baz"));
+ EXPECT_TRUE(Read("\x01\x02\x03\x04\x05"));
+}
+
+TEST_F(UdpTest, ResponseTimeoutFailure) {
+ for (int i = 0; i < kMaxTransmissionAttempts; ++i) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceiveTimeout();
+ }
+
+ EXPECT_FALSE(Write("foo"));
+}
+
+TEST_F(UdpTest, ResponseReceiveFailure) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceiveFailure();
+
+ EXPECT_FALSE(Write("foo"));
+}
+
+TEST_F(UdpTest, ResponseTimeoutRecovery) {
+ for (int i = 0; i < kMaxTransmissionAttempts - 1; ++i) {
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceiveTimeout();
+ }
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceive(FastbootPacket(1, ""));
+
+ EXPECT_TRUE(Write("foo"));
+}
+
+// Tests continuation packets for various max packet sizes.
+// The important part of this test is that regardless of what kind of packet fragmentation happens
+// at the socket layer, a single call to Transport::Read() and Transport::Write() is all the
+// fastboot code needs to do.
+TEST_F(UdpTest, ContinuationPackets) {
+ for (uint16_t max_packet_size : {512, 1024, 1200}) {
+ ASSERT_TRUE(InitializeTransport(0, max_packet_size));
+
+ // Initialize the data we want to send. Use (size - 4) to leave room for the header.
+ size_t max_data_size = max_packet_size - 4;
+ std::string data(max_data_size * 3, '\0');
+ for (size_t i = 0; i < data.length(); ++i) {
+ data[i] = i;
+ }
+ std::string chunks[] = {data.substr(0, max_data_size),
+ data.substr(max_data_size, max_data_size),
+ data.substr(max_data_size * 2, max_data_size)};
+
+ // Write data: split into 3 UDP packets, each of which will be ACKed.
+ mock_socket_->ExpectSend(FastbootPacket(1, chunks[0], kFlagContinuation));
+ mock_socket_->AddReceive(FastbootPacket(1));
+ mock_socket_->ExpectSend(FastbootPacket(2, chunks[1], kFlagContinuation));
+ mock_socket_->AddReceive(FastbootPacket(2));
+ mock_socket_->ExpectSend(FastbootPacket(3, chunks[2]));
+ mock_socket_->AddReceive(FastbootPacket(3));
+ EXPECT_TRUE(Write(data));
+
+ // Same thing for reading the data.
+ mock_socket_->ExpectSend(FastbootPacket(4));
+ mock_socket_->AddReceive(FastbootPacket(4, chunks[0], kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(5));
+ mock_socket_->AddReceive(FastbootPacket(5, chunks[1], kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(6));
+ mock_socket_->AddReceive(FastbootPacket(6, chunks[2]));
+ EXPECT_TRUE(Read(data));
+ }
+}
+
+// Tests that the continuation bit is respected even if the packet isn't max size.
+TEST_F(UdpTest, SmallContinuationPackets) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+ EXPECT_TRUE(Read("foobar"));
+}
+
+// Tests receiving an error packet mid-continuation.
+TEST_F(UdpTest, ContinuationPacketError) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceive(ErrorPacket(2, "test error"));
+
+ EXPECT_FALSE(Read("foo"));
+}
+
+// Tests timeout during a continuation sequence.
+TEST_F(UdpTest, ContinuationTimeoutRecovery) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceiveTimeout();
+ mock_socket_->ExpectSend(FastbootPacket(2));
+ mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+ EXPECT_TRUE(Read("foobar"));
+}
+
+// Tests read overflow returns -1 to indicate the failure.
+TEST_F(UdpTest, MultipleReadPacket) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "foobarbaz"));
+
+ char buffer[3];
+ EXPECT_EQ(-1, transport_->Read(buffer, 3));
+}
+
+// Tests that packets arriving out-of-order are ignored.
+TEST_F(UdpTest, IgnoreOutOfOrderPackets) {
+ mock_socket_->ExpectSend(FastbootPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(0, "sequence too low"));
+ mock_socket_->AddReceive(FastbootPacket(2, "sequence too high"));
+ mock_socket_->AddReceive(QueryPacket(1));
+ mock_socket_->AddReceive(FastbootPacket(1, "correct"));
+
+ EXPECT_TRUE(Read("correct"));
+}
+
+// Tests that an error response with the correct sequence number causes immediate failure.
+TEST_F(UdpTest, ErrorResponse) {
+ // Error packets with the wrong sequence number should be ignored like any other packet.
+ mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+ mock_socket_->AddReceive(ErrorPacket(0, "ignored error"));
+ mock_socket_->AddReceive(FastbootPacket(1));
+
+ EXPECT_TRUE(Write("foo"));
+
+ // Error packets with the correct sequence should abort immediately without retransmission.
+ mock_socket_->ExpectSend(FastbootPacket(2, "foo"));
+ mock_socket_->AddReceive(ErrorPacket(2, "test error"));
+
+ EXPECT_FALSE(Write("foo"));
+}
+
+// Tests that attempting to use a closed transport returns -1 without making any socket calls.
+TEST_F(UdpTest, CloseTransport) {
+ char buffer[32];
+ EXPECT_EQ(0, transport_->Close());
+ EXPECT_EQ(-1, transport_->Write("foo", 3));
+ EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));
+}
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 02ffcd9..d4824fb 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -145,7 +145,7 @@
int in, out;
unsigned i;
unsigned e;
-
+
if (check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
return -1;
dev = (struct usb_device_descriptor *)ptr;
@@ -333,15 +333,14 @@
char desc[1024];
int n, in, out, ifc;
- DIR *busdir;
struct dirent *de;
int fd;
int writable;
- busdir = opendir(base);
+ std::unique_ptr<DIR, decltype(&closedir)> busdir(opendir(base), closedir);
if (busdir == 0) return 0;
- while ((de = readdir(busdir)) && (usb == nullptr)) {
+ while ((de = readdir(busdir.get())) && (usb == nullptr)) {
if (badname(de->d_name)) continue;
if (!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
@@ -377,7 +376,6 @@
}
}
}
- closedir(busdir);
return usb;
}
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index c47a585..02aff55 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -600,7 +600,7 @@
fstab->recs[top_idx].fs_type);
if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
- int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY, 0644);
+ int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY);
if (fd >= 0) {
INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc);
wipe_block_device(fd, get_file_size(fd));
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
index c73045d..c63ff67 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -33,15 +33,15 @@
static int format_ext4(char *fs_blkdev, char *fs_mnt_point)
{
- unsigned int nr_sec;
+ uint64_t dev_sz;
int fd, rc = 0;
- if ((fd = open(fs_blkdev, O_WRONLY, 0644)) < 0) {
+ if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
ERROR("Cannot open block device. %s\n", strerror(errno));
return -1;
}
- if ((ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+ if ((ioctl(fd, BLKGETSIZE64, &dev_sz)) == -1) {
ERROR("Cannot get block device size. %s\n", strerror(errno));
close(fd);
return -1;
@@ -49,7 +49,7 @@
/* Format the partition using the calculated length */
reset_ext4fs_info();
- info.len = ((off64_t)nr_sec * 512);
+ info.len = (off64_t)dev_sz;
/* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index b4fdab0..7254cf2 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -76,7 +76,7 @@
void store_sid(uint32_t uid, uint64_t sid) {
char filename[21];
- sprintf(filename, "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", uid);
int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0) {
ALOGE("could not open file: %s: %s", filename, strerror(errno));
@@ -102,7 +102,7 @@
void maybe_store_sid(uint32_t uid, uint64_t sid) {
char filename[21];
- sprintf(filename, "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", uid);
if (access(filename, F_OK) == -1) {
store_sid(uid, sid);
}
@@ -111,7 +111,7 @@
uint64_t read_sid(uint32_t uid) {
char filename[21];
uint64_t sid;
- sprintf(filename, "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", uid);
int fd = open(filename, O_RDONLY);
if (fd < 0) return 0;
read(fd, &sid, sizeof(sid));
@@ -121,7 +121,7 @@
void clear_sid(uint32_t uid) {
char filename[21];
- sprintf(filename, "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", uid);
if (remove(filename) < 0) {
ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
store_sid(uid, 0);
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 27c985c..ddd9f1f 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -6,17 +6,36 @@
LOCAL_SRC_FILES := healthd_board_default.cpp
LOCAL_MODULE := libhealthd.default
LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := BatteryMonitor.cpp
+LOCAL_MODULE := libbatterymonitor
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libutils
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
+ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
+LOCAL_CHARGER_NO_UI := true
+endif
+ifdef BRILLO
+LOCAL_CHARGER_NO_UI := true
+endif
+
LOCAL_SRC_FILES := \
healthd.cpp \
healthd_mode_android.cpp \
- healthd_mode_charger.cpp \
- BatteryMonitor.cpp \
BatteryPropertiesRegistrar.cpp
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_SRC_FILES += healthd_mode_charger.cpp
+endif
+
LOCAL_MODULE := healthd
LOCAL_MODULE_TAGS := optional
LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -33,9 +52,19 @@
LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
endif
-LOCAL_C_INCLUDES := bootable/recovery
+ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_CFLAGS += -DCHARGER_NO_UI
+endif
-LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libcutils liblog libm libc
+LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+
+LOCAL_STATIC_LIBRARIES := libbatterymonitor libbatteryservice libbinder
+
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_STATIC_LIBRARIES += libminui libpng libz
+endif
+
+LOCAL_STATIC_LIBRARIES += libutils libcutils liblog libm libc
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
LOCAL_STATIC_LIBRARIES += libsuspend
@@ -50,6 +79,7 @@
include $(BUILD_EXECUTABLE)
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
define _add-charger-image
include $$(CLEAR_VARS)
LOCAL_MODULE := system_core_charger_$(notdir $(1))
@@ -75,3 +105,4 @@
_add-charger-image :=
_img_modules :=
+endif # LOCAL_CHARGER_NO_UI
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 8f98f17..69647de 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -16,8 +16,8 @@
#define LOG_TAG "healthd"
-#include "healthd.h"
-#include "BatteryMonitor.h"
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor.h>
#include <dirent.h>
#include <errno.h>
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
+#include <memory>
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
@@ -56,6 +57,28 @@
return -1;
}
+static void initBatteryProperties(BatteryProperties* props) {
+ props->chargerAcOnline = false;
+ props->chargerUsbOnline = false;
+ props->chargerWirelessOnline = false;
+ props->maxChargingCurrent = 0;
+ props->batteryStatus = BATTERY_STATUS_UNKNOWN;
+ props->batteryHealth = BATTERY_HEALTH_UNKNOWN;
+ props->batteryPresent = false;
+ props->batteryLevel = 0;
+ props->batteryVoltage = 0;
+ props->batteryTemperature = 0;
+ props->batteryCurrent = 0;
+ props->batteryCycleCount = 0;
+ props->batteryFullCharge = 0;
+ props->batteryTechnology.clear();
+}
+
+BatteryMonitor::BatteryMonitor() : mHealthdConfig(nullptr), mBatteryDevicePresent(false),
+ mAlwaysPluggedDevice(false), mBatteryFixedCapacity(0), mBatteryFixedTemperature(0) {
+ initBatteryProperties(&props);
+}
+
int BatteryMonitor::getBatteryStatus(const char* status) {
int ret;
struct sysfsStringEnumMap batteryStatusMap[] = {
@@ -184,12 +207,7 @@
bool BatteryMonitor::update(void) {
bool logthis;
- props.chargerAcOnline = false;
- props.chargerUsbOnline = false;
- props.chargerWirelessOnline = false;
- props.batteryStatus = BATTERY_STATUS_UNKNOWN;
- props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
- props.maxChargingCurrent = 0;
+ initBatteryProperties(&props);
if (!mHealthdConfig->batteryPresentPath.isEmpty())
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -305,11 +323,10 @@
" cc=%d", props.batteryCycleCount);
}
} else {
- snprintf(dmesgline, sizeof(dmesgline),
+ len = snprintf(dmesgline, sizeof(dmesgline),
"battery none");
}
- len = strlen(dmesgline);
snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
props.chargerAcOnline ? "a" : "",
props.chargerUsbOnline ? "u" : "",
@@ -323,6 +340,17 @@
props.chargerWirelessOnline;
}
+int BatteryMonitor::getChargeStatus() {
+ int result = BATTERY_STATUS_UNKNOWN;
+ if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+ char buf[128];
+ if (readFromFile(mHealthdConfig->batteryStatusPath, buf, sizeof(buf)) > 0) {
+ result = getBatteryStatus(buf);
+ }
+ }
+ return result;
+}
+
status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
status_t ret = BAD_VALUE;
@@ -439,13 +467,13 @@
char pval[PROPERTY_VALUE_MAX];
mHealthdConfig = hc;
- DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
if (dir == NULL) {
KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
} else {
struct dirent* entry;
- while ((entry = readdir(dir))) {
+ while ((entry = readdir(dir.get()))) {
const char* name = entry->d_name;
if (!strcmp(name, ".") || !strcmp(name, ".."))
@@ -583,22 +611,17 @@
break;
}
}
- closedir(dir);
}
- // This indicates that there is no charger driver registered.
// Typically the case for devices which do not have a battery and
// and are always plugged into AC mains.
- if (!mChargerNames.size()) {
- KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
- mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
- mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
- mAlwaysPluggedDevice = true;
- }
if (!mBatteryDevicePresent) {
KLOG_WARNING(LOG_TAG, "No battery devices found\n");
hc->periodic_chores_interval_fast = -1;
hc->periodic_chores_interval_slow = -1;
+ mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
+ mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
+ mAlwaysPluggedDevice = true;
} else {
if (mHealthdConfig->batteryStatusPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index d3a89d7..5d1fa52 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -26,7 +26,7 @@
#include <utils/Mutex.h>
#include <utils/String16.h>
-#include "healthd.h"
+#include <healthd/healthd.h>
namespace android {
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 85888c3..20a6bf6 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -17,8 +17,8 @@
#define LOG_TAG "healthd"
#define KLOG_LEVEL 6
-#include "healthd.h"
-#include "BatteryMonitor.h"
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor.h>
#include <errno.h>
#include <libgen.h>
@@ -109,10 +109,17 @@
};
static struct healthd_mode_ops charger_ops = {
+#ifdef CHARGER_NO_UI
+ .init = healthd_mode_nop_init,
+ .preparetowait = healthd_mode_nop_preparetowait,
+ .heartbeat = healthd_mode_nop_heartbeat,
+ .battery_update = healthd_mode_nop_battery_update,
+#else
.init = healthd_mode_charger_init,
.preparetowait = healthd_mode_charger_preparetowait,
.heartbeat = healthd_mode_charger_heartbeat,
.battery_update = healthd_mode_charger_battery_update,
+#endif
};
static struct healthd_mode_ops recovery_ops = {
diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp
index ed4ddb4..eb55773 100644
--- a/healthd/healthd_board_default.cpp
+++ b/healthd/healthd_board_default.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <healthd.h>
+#include <healthd/healthd.h>
void healthd_board_init(struct healthd_config*)
{
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
index 0a64099..323ef52 100644
--- a/healthd/healthd_mode_android.cpp
+++ b/healthd/healthd_mode_android.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "healthd-android"
-#include "healthd.h"
+#include <healthd/healthd.h>
#include "BatteryPropertiesRegistrar.h"
#include <binder/IPCThreadState.h>
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 46bad4e..5846626 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -46,7 +46,7 @@
#include "minui/minui.h"
-#include "healthd.h"
+#include <healthd/healthd.h>
char *locale;
diff --git a/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
similarity index 95%
rename from healthd/BatteryMonitor.h
rename to healthd/include/healthd/BatteryMonitor.h
index a61171f..440f2e4 100644
--- a/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -22,7 +22,7 @@
#include <utils/String8.h>
#include <utils/Vector.h>
-#include "healthd.h"
+#include <healthd/healthd.h>
namespace android {
@@ -37,8 +37,10 @@
ANDROID_POWER_SUPPLY_TYPE_BATTERY
};
+ BatteryMonitor();
void init(struct healthd_config *hc);
bool update(void);
+ int getChargeStatus();
status_t getProperty(int id, struct BatteryProperty *val);
void dumpState(int fd);
diff --git a/healthd/healthd.h b/healthd/include/healthd/healthd.h
similarity index 100%
rename from healthd/healthd.h
rename to healthd/include/healthd/healthd.h
diff --git a/include/log/log.h b/include/log/log.h
index 1bd9165..e606a84 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -484,15 +484,19 @@
*/
/*
- * Event log entry types. These must match up with the declarations in
- * java/android/android/util/EventLog.java.
+ * Event log entry types.
*/
typedef enum {
- EVENT_TYPE_INT = 0,
- EVENT_TYPE_LONG = 1,
- EVENT_TYPE_STRING = 2,
- EVENT_TYPE_LIST = 3,
- EVENT_TYPE_FLOAT = 4,
+ /* Special markers for android_log_list_element type */
+ EVENT_TYPE_LIST_STOP = '\n', /* declare end of list */
+ EVENT_TYPE_UNKNOWN = '?', /* protocol error */
+
+ /* must match with declaration in java/android/android/util/EventLog.java */
+ EVENT_TYPE_INT = 0, /* uint32_t */
+ EVENT_TYPE_LONG = 1, /* uint64_t */
+ EVENT_TYPE_STRING = 2,
+ EVENT_TYPE_LIST = 3,
+ EVENT_TYPE_FLOAT = 4,
} AndroidEventLogType;
#define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
#define typeof_AndroidEventLogType unsigned char
@@ -522,7 +526,87 @@
#define LOG_EVENT_STRING(_tag, _value) \
(void) __android_log_bswrite(_tag, _value);
#endif
-/* TODO: something for LIST */
+
+typedef enum log_id {
+ LOG_ID_MIN = 0,
+
+#ifndef LINT_RLOG
+ LOG_ID_MAIN = 0,
+#endif
+ LOG_ID_RADIO = 1,
+#ifndef LINT_RLOG
+ LOG_ID_EVENTS = 2,
+ LOG_ID_SYSTEM = 3,
+ LOG_ID_CRASH = 4,
+ LOG_ID_SECURITY = 5,
+ LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
+#endif
+
+ LOG_ID_MAX
+} log_id_t;
+#define sizeof_log_id_t sizeof(typeof_log_id_t)
+#define typeof_log_id_t unsigned char
+
+/* For manipulating lists of events. */
+
+#define ANDROID_MAX_LIST_NEST_DEPTH 8
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+typedef struct android_log_context_internal *android_log_context;
+
+/*
+ * Elements returned when reading a list of events.
+ */
+typedef struct {
+ AndroidEventLogType type;
+ uint16_t complete;
+ uint16_t len;
+ union {
+ int32_t int32;
+ int64_t int64;
+ char *string;
+ float float32;
+ } data;
+} android_log_list_element;
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ * elements, we will manufacturer a list to embrace it for your API
+ * convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char *value);
+int android_log_write_string8_len(android_log_context ctx,
+ const char *value, size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/*
+ * Creates a context from a raw buffer representing a list of events to be read.
+ */
+android_log_context create_android_log_parser(const char *msg, size_t len);
+
+android_log_list_element android_log_read_next(android_log_context ctx);
+android_log_list_element android_log_peek_next(android_log_context ctx);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context *ctx);
/*
* ===========================================================================
@@ -585,26 +669,6 @@
(__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
#endif
-typedef enum log_id {
- LOG_ID_MIN = 0,
-
-#ifndef LINT_RLOG
- LOG_ID_MAIN = 0,
-#endif
- LOG_ID_RADIO = 1,
-#ifndef LINT_RLOG
- LOG_ID_EVENTS = 2,
- LOG_ID_SYSTEM = 3,
- LOG_ID_CRASH = 4,
- LOG_ID_SECURITY = 5,
- LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
-#endif
-
- LOG_ID_MAX
-} log_id_t;
-#define sizeof_log_id_t sizeof(typeof_log_id_t)
-#define typeof_log_id_t unsigned char
-
/*
* Use the per-tag properties "log.tag.<tagname>" to generate a runtime
* result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 85d6c19..53966d5 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -88,6 +88,10 @@
#define AID_WEBSERV 1044 /* webservd process */
#define AID_DEBUGGERD 1045 /* debuggerd unprivileged user */
#define AID_MEDIA_CODEC 1046 /* mediacodec process */
+#define AID_CAMERASERVER 1047 /* cameraserver process */
+#define AID_FIREWALL 1048 /* firewalld process */
+#define AID_TRUNKS 1049 /* trunksd process (TPM daemon) */
+/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
@@ -194,6 +198,9 @@
{ "webserv", AID_WEBSERV },
{ "debuggerd", AID_DEBUGGERD, },
{ "mediacodec", AID_MEDIA_CODEC, },
+ { "cameraserver", AID_CAMERASERVER, },
+ { "firewall", AID_FIREWALL, },
+ { "trunks", AID_TRUNKS, },
{ "shell", AID_SHELL, },
{ "cache", AID_CACHE, },
diff --git a/include/utils/String16.h b/include/utils/String16.h
index 9a67c7a..4a5874a 100644
--- a/include/utils/String16.h
+++ b/include/utils/String16.h
@@ -17,8 +17,10 @@
#ifndef ANDROID_STRING16_H
#define ANDROID_STRING16_H
+#include <string> // for std::string
+
#include <utils/Errors.h>
-#include <utils/Unicode.h>
+#include <utils/String8.h>
#include <utils/TypeHelpers.h>
// ---------------------------------------------------------------------------
@@ -62,9 +64,10 @@
explicit String16(const char* o, size_t len);
~String16();
-
+
inline const char16_t* string() const;
-
+
+ static inline std::string std_string(const String16& str);
size_t size() const;
void setTo(const String16& other);
status_t setTo(const char16_t* other);
@@ -72,12 +75,12 @@
status_t setTo(const String16& other,
size_t len,
size_t begin=0);
-
+
status_t append(const String16& other);
status_t append(const char16_t* other, size_t len);
-
+
inline String16& operator=(const String16& other);
-
+
inline String16& operator+=(const String16& other);
inline String16 operator+(const String16& other) const;
@@ -90,7 +93,7 @@
bool startsWith(const String16& prefix) const;
bool startsWith(const char16_t* prefix) const;
-
+
status_t makeLower();
status_t replaceAll(char16_t replaceThis,
@@ -106,16 +109,16 @@
inline bool operator!=(const String16& other) const;
inline bool operator>=(const String16& other) const;
inline bool operator>(const String16& other) const;
-
+
inline bool operator<(const char16_t* other) const;
inline bool operator<=(const char16_t* other) const;
inline bool operator==(const char16_t* other) const;
inline bool operator!=(const char16_t* other) const;
inline bool operator>=(const char16_t* other) const;
inline bool operator>(const char16_t* other) const;
-
+
inline operator const char16_t*() const;
-
+
private:
const char16_t* mString;
};
@@ -142,6 +145,11 @@
return mString;
}
+inline std::string String16::std_string(const String16& str)
+{
+ return std::string(String8(str).string());
+}
+
inline String16& String16::operator=(const String16& other)
{
setTo(other);
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 2a75b98..1d12994 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_STRING8_H
#define ANDROID_STRING8_H
+#include <string> // for std::string
+
#include <utils/Errors.h>
#include <utils/Unicode.h>
#include <utils/TypeHelpers.h>
@@ -49,7 +51,7 @@
String8(const String8& o);
explicit String8(const char* o);
explicit String8(const char* o, size_t numChars);
-
+
explicit String8(const String16& o);
explicit String8(const char16_t* o);
explicit String8(const char16_t* o, size_t numChars);
@@ -63,12 +65,13 @@
static String8 formatV(const char* fmt, va_list args);
inline const char* string() const;
+ static inline std::string std_string(const String8& str);
inline size_t size() const;
inline size_t bytes() const;
inline bool isEmpty() const;
-
+
size_t length() const;
-
+
void clear();
void setTo(const String8& other);
@@ -95,10 +98,10 @@
inline String8& operator=(const String8& other);
inline String8& operator=(const char* other);
-
+
inline String8& operator+=(const String8& other);
inline String8 operator+(const String8& other) const;
-
+
inline String8& operator+=(const char* other);
inline String8 operator+(const char* other) const;
@@ -110,20 +113,20 @@
inline bool operator!=(const String8& other) const;
inline bool operator>=(const String8& other) const;
inline bool operator>(const String8& other) const;
-
+
inline bool operator<(const char* other) const;
inline bool operator<=(const char* other) const;
inline bool operator==(const char* other) const;
inline bool operator!=(const char* other) const;
inline bool operator>=(const char* other) const;
inline bool operator>(const char* other) const;
-
+
inline operator const char*() const;
-
+
char* lockBuffer(size_t size);
void unlockBuffer();
status_t unlockBuffer(size_t size);
-
+
// return the index of the first byte of other in this at or after
// start, or -1 if not found
ssize_t find(const char* other, size_t start = 0) const;
@@ -261,6 +264,11 @@
return mString;
}
+inline std::string String8::std_string(const String8& str)
+{
+ return std::string(str.string());
+}
+
inline size_t String8::size() const
{
return length();
diff --git a/include/utils/ThreadDefs.h b/include/utils/ThreadDefs.h
index 9711c13..ae091e4 100644
--- a/include/utils/ThreadDefs.h
+++ b/include/utils/ThreadDefs.h
@@ -29,7 +29,11 @@
extern "C" {
#endif
+#ifdef _WIN32
+typedef uint32_t android_thread_id_t;
+#else
typedef void* android_thread_id_t;
+#endif
typedef int (*android_thread_func_t)(void*);
diff --git a/init/devices.cpp b/init/devices.cpp
index 39cd706..557a6ac 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -31,6 +31,8 @@
#include <sys/un.h>
#include <linux/netlink.h>
+#include <memory>
+
#include <selinux/selinux.h>
#include <selinux/label.h>
#include <selinux/android.h>
@@ -957,10 +959,9 @@
static void coldboot(const char *path)
{
- DIR *d = opendir(path);
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
if(d) {
- do_coldboot(d);
- closedir(d);
+ do_coldboot(d.get());
}
}
diff --git a/init/init.cpp b/init/init.cpp
index 4aef823..9e6143b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -561,6 +561,7 @@
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
mount("sysfs", "/sys", "sysfs", 0, NULL);
+ mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
}
// We must have some place other than / to create the device nodes for
diff --git a/init/parser.cpp b/init/parser.cpp
index 8193729..ae103ec 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -12,7 +12,7 @@
char buf[128];
int off;
- snprintf(buf, 128, "%s: %d: ", state->filename, state->line);
+ snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
buf[127] = 0;
off = strlen(buf);
diff --git a/init/perfboot.py b/init/perfboot.py
index 91e6c2b..713290b 100755
--- a/init/perfboot.py
+++ b/init/perfboot.py
@@ -191,9 +191,9 @@
def init_perf(device, output, record_list, tags):
device.wait()
- build_type = device.get_prop('ro.build.type')
+ debuggable = device.get_prop('ro.debuggable')
original_dropbox_max_files = None
- if build_type != 'user':
+ if debuggable == '1':
# Workaround for Dropbox issue (http://b/20890386).
original_dropbox_max_files = disable_dropbox(device)
diff --git a/init/service.cpp b/init/service.cpp
index 0ddc484..bdecc32 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -416,7 +416,7 @@
}
}
- std::string pid_str = StringPrintf("%d", pid);
+ std::string pid_str = StringPrintf("%d", getpid());
for (const auto& file : writepid_files_) {
if (!WriteStringToFile(pid_str, file)) {
ERROR("couldn't write %s to %s: %s\n",
diff --git a/init/util.cpp b/init/util.cpp
index aefdf8f..bddc3b2 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -102,7 +102,7 @@
gid_t gid, const char *socketcon)
{
struct sockaddr_un addr;
- int fd, ret;
+ int fd, ret, savederrno;
char *filecon;
if (socketcon) {
@@ -140,16 +140,26 @@
}
ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
- if (ret) {
- ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));
- goto out_unlink;
- }
+ savederrno = errno;
setfscreatecon(NULL);
freecon(filecon);
- chown(addr.sun_path, uid, gid);
- chmod(addr.sun_path, perm);
+ if (ret) {
+ ERROR("Failed to bind socket '%s': %s\n", name, strerror(savederrno));
+ goto out_unlink;
+ }
+
+ ret = lchown(addr.sun_path, uid, gid);
+ if (ret) {
+ ERROR("Failed to lchown socket '%s': %s\n", addr.sun_path, strerror(errno));
+ goto out_unlink;
+ }
+ ret = fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW);
+ if (ret) {
+ ERROR("Failed to fchmodat socket '%s': %s\n", addr.sun_path, strerror(errno));
+ goto out_unlink;
+ }
INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",
addr.sun_path, perm, uid, gid);
@@ -391,20 +401,18 @@
void open_devnull_stdio(void)
{
- // Try to avoid the mknod() call if we can. Since SELinux makes
- // a /dev/null replacement available for free, let's use it.
int fd = open("/sys/fs/selinux/null", O_RDWR);
if (fd == -1) {
- // OOPS, /sys/fs/selinux/null isn't available, likely because
- // /sys/fs/selinux isn't mounted. Fall back to mknod.
- static const char *name = "/dev/__null__";
- if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
- fd = open(name, O_RDWR);
- unlink(name);
- }
- if (fd == -1) {
- exit(1);
- }
+ /* Fail silently.
+ * stdout/stderr isn't available, and because
+ * klog_init() is called after open_devnull_stdio(), we can't
+ * log to dmesg. Reordering klog_init() to be called before
+ * open_devnull_stdio() isn't an option either, as then klog_fd
+ * will be assigned 0 or 1, which will end up getting clobbered
+ * by the code below. There's nowhere good to log.
+ */
+
+ exit(1);
}
dup2(fd, 0);
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index ee56a5e..397dfda 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -71,14 +71,22 @@
build_type := host
libbacktrace_multilib := both
include $(LOCAL_PATH)/Android.build.mk
+
+libbacktrace_shared_libraries :=
+
libbacktrace_static_libraries := \
libbase \
liblog \
libunwind \
+ liblzma \
+module := libbacktrace
+build_type := target
build_target := STATIC_LIBRARY
include $(LOCAL_PATH)/Android.build.mk
-libbacktrace_static_libraries :=
+build_type := host
+libbacktrace_multilib := both
+include $(LOCAL_PATH)/Android.build.mk
#-------------------------------------------------------------------------
# The libbacktrace_offline shared library.
@@ -106,7 +114,6 @@
libLLVMSupport \
module := libbacktrace_offline
-module_tag := optional
build_type := target
build_target := SHARED_LIBRARY
include $(LOCAL_PATH)/Android.build.mk
@@ -114,6 +121,26 @@
libbacktrace_multilib := both
include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_offline_shared_libraries :=
+libbacktrace_offline_static_libraries := \
+ libbacktrace \
+ libbase \
+ libcutils \
+ liblog \
+ libunwind \
+ liblzma \
+ libLLVMObject \
+ libLLVMBitReader \
+ libLLVMMC \
+ libLLVMMCParser \
+ libLLVMCore \
+ libLLVMSupport \
+
+module := libbacktrace_offline
+build_type := target
+build_target := STATIC_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+
#-------------------------------------------------------------------------
# The libbacktrace_test library needed by backtrace_test.
#-------------------------------------------------------------------------
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index 879fea5..34d79f9 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -33,14 +33,18 @@
// of maps using the same map cursor.
//-------------------------------------------------------------------------
UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
+ unw_map_cursor_clear(&map_cursor_);
}
-UnwindMap::~UnwindMap() {
+UnwindMapRemote::UnwindMapRemote(pid_t pid) : UnwindMap(pid) {
+}
+
+UnwindMapRemote::~UnwindMapRemote() {
unw_map_cursor_destroy(&map_cursor_);
unw_map_cursor_clear(&map_cursor_);
}
-bool UnwindMap::GenerateMap() {
+bool UnwindMapRemote::GenerateMap() {
// Use the map_cursor information to construct the BacktraceMap data
// rather than reparsing /proc/self/maps.
unw_map_cursor_reset(&map_cursor_);
@@ -63,7 +67,7 @@
return true;
}
-bool UnwindMap::Build() {
+bool UnwindMapRemote::Build() {
return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap();
}
@@ -84,6 +88,7 @@
for (int i = 0; i < 3; i++) {
maps_.clear();
+ // Save the map data retrieved so we can tell if it changes.
unw_map_local_cursor_get(&map_cursor_);
unw_map_t unw_map;
@@ -142,7 +147,7 @@
} else if (pid == getpid()) {
map = new UnwindMapLocal();
} else {
- map = new UnwindMap(pid);
+ map = new UnwindMapRemote(pid);
}
if (!map->Build()) {
delete map;
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
index e292016..111401f 100644
--- a/libbacktrace/UnwindMap.h
+++ b/libbacktrace/UnwindMap.h
@@ -29,29 +29,35 @@
class UnwindMap : public BacktraceMap {
public:
UnwindMap(pid_t pid);
- virtual ~UnwindMap();
-
- virtual bool Build();
unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
protected:
- virtual bool GenerateMap();
-
unw_map_cursor_t map_cursor_;
};
+class UnwindMapRemote : public UnwindMap {
+public:
+ UnwindMapRemote(pid_t pid);
+ virtual ~UnwindMapRemote();
+
+ bool Build() override;
+
+private:
+ bool GenerateMap();
+};
+
class UnwindMapLocal : public UnwindMap {
public:
UnwindMapLocal();
virtual ~UnwindMapLocal();
- virtual bool Build();
+ bool Build() override;
- virtual void FillIn(uintptr_t addr, backtrace_map_t* map);
+ void FillIn(uintptr_t addr, backtrace_map_t* map) override;
-protected:
- virtual bool GenerateMap();
+private:
+ bool GenerateMap();
bool map_created_;
};
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 7d829fe..ab09564 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -404,17 +404,16 @@
char task_path[128];
snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
- DIR* tasks_dir = opendir(task_path);
+ std::unique_ptr<DIR, decltype(&closedir)> tasks_dir(opendir(task_path), closedir);
ASSERT_TRUE(tasks_dir != nullptr);
struct dirent* entry;
- while ((entry = readdir(tasks_dir)) != nullptr) {
+ while ((entry = readdir(tasks_dir.get())) != nullptr) {
char* end;
pid_t tid = strtoul(entry->d_name, &end, 10);
if (*end == '\0') {
threads->push_back(tid);
}
}
- closedir(tasks_dir);
}
TEST(libbacktrace, ptrace_threads) {
@@ -1413,9 +1412,9 @@
}
size_t new_pss = GetPssBytes();
ASSERT_TRUE(new_pss != 0);
- size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
- // As long as the new pss is within a certain amount, consider everything okay.
- ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
+ if (new_pss > stable_pss) {
+ ASSERT_LE(new_pss - stable_pss, MAX_LEAK_BYTES);
+ }
}
TEST(libbacktrace, check_for_leak_local) {
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 51c6d9d..c0d4d76 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -121,6 +121,9 @@
ifneq ($(ENABLE_CPUSETS),)
LOCAL_CFLAGS += -DUSE_CPUSETS
endif
+ifneq ($(ENABLE_SCHEDBOOST),)
+LOCAL_CFLAGS += -DUSE_SCHEDBOOST
+endif
LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
@@ -135,6 +138,9 @@
ifneq ($(ENABLE_CPUSETS),)
LOCAL_CFLAGS += -DUSE_CPUSETS
endif
+ifneq ($(ENABLE_SCHEDBOOST),)
+LOCAL_CFLAGS += -DUSE_SCHEDBOOST
+endif
LOCAL_CFLAGS += -Werror -Wall -Wextra
LOCAL_C_INCLUDES := $(libcutils_c_includes)
LOCAL_CLANG := true
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index d6a48c9..4a07d66 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -118,15 +118,16 @@
}
if (rdev) {
- ALOGE("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o %d:%d",
+ LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o %d:%d",
fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP,
major(rdev), minor(rdev));
} else {
- ALOGE("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o",
+ LOG_ALWAYS_FATAL("illegal fd=%d mode=0%o rdev=%d:%d expected 0%o",
fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP);
}
+ /* NOTREACHED */
errno = ENOTTY;
return -1;
diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c
index 00e211c..14a58ca 100644
--- a/libcutils/qtaguid.c
+++ b/libcutils/qtaguid.c
@@ -47,10 +47,7 @@
/* Only call once per process. */
void qtaguid_resTrack(void) {
- resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY));
- if (resTrackFd >=0) {
- TEMP_FAILURE_RETRY(fcntl(resTrackFd, F_SETFD, FD_CLOEXEC));
- }
+ resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY | O_CLOEXEC));
}
/*
@@ -63,7 +60,7 @@
ALOGV("write_ctrl(%s)", cmd);
- fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY));
+ fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY | O_CLOEXEC));
if (fd < 0) {
return -errno;
}
@@ -85,7 +82,7 @@
int param_fd;
int res;
- param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY));
+ param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY | O_CLOEXEC));
if (param_fd < 0) {
return -errno;
}
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index 298e3da..6bba3a7 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -64,6 +64,8 @@
// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
static int bg_cpuset_fd = -1;
static int fg_cpuset_fd = -1;
+static int bg_schedboost_fd = -1;
+static int fg_schedboost_fd = -1;
#endif
/* Add tid to the scheduling group defined by the policy */
@@ -128,6 +130,12 @@
fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
filename = "/dev/cpuset/background/tasks";
bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+#ifdef USE_SCHEDBOOST
+ filename = "/sys/fs/cgroup/stune/foreground/tasks";
+ fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/sys/fs/cgroup/stune/tasks";
+ bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+#endif
}
#endif
@@ -253,17 +261,20 @@
pthread_once(&the_once, __initialize);
int fd;
+ int boost_fd;
switch (policy) {
case SP_BACKGROUND:
fd = bg_cpuset_fd;
+ boost_fd = bg_schedboost_fd;
break;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
fd = fg_cpuset_fd;
+ boost_fd = fg_schedboost_fd;
break;
default:
- fd = -1;
+ boost_fd = fd = -1;
break;
}
@@ -272,6 +283,11 @@
return -errno;
}
+ if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+ if (errno != ESRCH && errno != ENOENT)
+ return -errno;
+ }
+
return 0;
#endif
}
diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c
index 4f23d09..8dafded 100644
--- a/libcutils/str_parms.c
+++ b/libcutils/str_parms.c
@@ -31,6 +31,20 @@
#define UNUSED __attribute__((unused))
+/* When an object is allocated but not freed in a function,
+ * because its ownership is released to other object like a hashmap,
+ * call RELEASE_OWNERSHIP to tell the clang analyzer and avoid
+ * false warnings about potential memory leak.
+ * For now, a "temporary" assignment to global variables
+ * is enough to confuse the clang static analyzer.
+ */
+#ifdef __clang_analyzer__
+static void *released_pointer;
+#define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }
+#else
+#define RELEASE_OWNERSHIP(x)
+#endif
+
struct str_parms {
Hashmap *map;
};
@@ -170,9 +184,12 @@
/* if we replaced a value, free it */
old_val = hashmapPut(str_parms->map, key, value);
+ RELEASE_OWNERSHIP(value);
if (old_val) {
free(old_val);
free(key);
+ } else {
+ RELEASE_OWNERSHIP(key);
}
items++;
@@ -222,10 +239,13 @@
goto clean_up;
}
// For new keys, hashmap takes ownership of tmp_key and tmp_val.
+ RELEASE_OWNERSHIP(tmp_key);
+ RELEASE_OWNERSHIP(tmp_val);
tmp_key = tmp_val = NULL;
} else {
// For existing keys, hashmap takes ownership of tmp_val.
// (It also gives up ownership of old_val.)
+ RELEASE_OWNERSHIP(tmp_val);
tmp_val = NULL;
}
diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk
index 4da5ed6..52cf5f4 100644
--- a/libcutils/tests/Android.mk
+++ b/libcutils/tests/Android.mk
@@ -23,8 +23,9 @@
test_target_only_src_files := \
MemsetTest.cpp \
PropertiesTest.cpp \
+ trace-dev_test.cpp \
-test_libraries := libcutils liblog
+test_libraries := libcutils liblog libbase
#
diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/tests/PropertiesTest.cpp
index 659821c..5f2396b 100644
--- a/libcutils/tests/PropertiesTest.cpp
+++ b/libcutils/tests/PropertiesTest.cpp
@@ -15,7 +15,7 @@
*/
#define LOG_TAG "Properties_test"
-#include <utils/Log.h>
+#include <cutils/log.h>
#include <gtest/gtest.h>
#include <cutils/properties.h>
@@ -106,14 +106,14 @@
ResetValue();
// Since the value is null, default value will be returned
- int len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len);
EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue);
}
// Trivial case => get returns what was set
{
- int len = SetAndGetProperty("hello_world");
+ size_t len = SetAndGetProperty("hello_world");
EXPECT_EQ(strlen("hello_world"), len) << "hello_world key";
EXPECT_STREQ("hello_world", mValue);
ResetValue();
@@ -122,7 +122,7 @@
// Set to empty string => get returns default always
{
const char* EMPTY_STRING_DEFAULT = "EMPTY_STRING";
- int len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
+ size_t len = SetAndGetProperty("", EMPTY_STRING_DEFAULT);
EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << "empty key";
EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue);
ResetValue();
@@ -147,7 +147,7 @@
// Expect that the value set fails since it's too long
EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str()));
- int len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
+ size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);
EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << "set should've failed";
EXPECT_STREQ(VALID_TEST_VALUE, mValue);
diff --git a/libcutils/tests/trace-dev_test.cpp b/libcutils/tests/trace-dev_test.cpp
new file mode 100644
index 0000000..edf981b
--- /dev/null
+++ b/libcutils/tests/trace-dev_test.cpp
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "../trace-dev.c"
+
+class TraceDevTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ lseek(tmp_file_.fd, 0, SEEK_SET);
+ atrace_marker_fd = tmp_file_.fd;
+ }
+
+ void TearDown() override {
+ atrace_marker_fd = -1;
+ }
+
+ TemporaryFile tmp_file_;
+
+ static std::string MakeName(size_t length) {
+ std::string name;
+ for (size_t i = 0; i < length; i++) {
+ name += '0' + (i % 10);
+ }
+ return name;
+ }
+};
+
+TEST_F(TraceDevTest, atrace_begin_body_normal) {
+ atrace_begin_body("fake_name");
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("B|%d|fake_name", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_begin_body_exact) {
+ std::string expected = android::base::StringPrintf("B|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1);
+ atrace_begin_body(name.c_str());
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += name;
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_begin_body(name.c_str());
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_begin_body_truncated) {
+ std::string expected = android::base::StringPrintf("B|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_begin_body(name.c_str());
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
+ expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_normal) {
+ atrace_async_begin_body("fake_name", 12345);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("S|%d|fake_name|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_exact) {
+ std::string expected = android::base::StringPrintf("S|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+ atrace_async_begin_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += name + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_async_begin_body(name.c_str(), 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_truncated) {
+ std::string expected = android::base::StringPrintf("S|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_begin_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_normal) {
+ atrace_async_end_body("fake_name", 12345);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("F|%d|fake_name|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_exact) {
+ std::string expected = android::base::StringPrintf("F|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+ atrace_async_end_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += name + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_async_end_body(name.c_str(), 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_truncated) {
+ std::string expected = android::base::StringPrintf("F|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_end_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_normal) {
+ atrace_int_body("fake_name", 12345);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("C|%d|fake_name|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_exact) {
+ std::string expected = android::base::StringPrintf("C|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+ atrace_int_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += name + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_int_body(name.c_str(), 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_truncated) {
+ std::string expected = android::base::StringPrintf("C|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_int_body(name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_normal) {
+ atrace_int64_body("fake_name", 17179869183L);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("C|%d|fake_name|17179869183", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_exact) {
+ std::string expected = android::base::StringPrintf("C|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 13);
+ atrace_int64_body(name.c_str(), 17179869183L);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += name + "|17179869183";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_int64_body(name.c_str(), 17179869183L);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_truncated) {
+ std::string expected = android::base::StringPrintf("C|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_int64_body(name.c_str(), 17179869183L);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 13;
+ expected += android::base::StringPrintf("%.*s|17179869183", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index f025256..5df1c5a 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -194,49 +194,47 @@
void atrace_begin_body(const char* name)
{
char buf[ATRACE_MESSAGE_LENGTH];
- size_t len;
- len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name);
+ int len = snprintf(buf, sizeof(buf), "B|%d|%s", getpid(), name);
+ if (len >= (int) sizeof(buf)) {
+ ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name);
+ len = sizeof(buf) - 1;
+ }
write(atrace_marker_fd, buf, len);
}
+#define WRITE_MSG(format_begin, format_end, pid, name, value) { \
+ char buf[ATRACE_MESSAGE_LENGTH]; \
+ int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
+ name, value); \
+ if (len >= (int) sizeof(buf)) { \
+ /* Given the sizeof(buf), and all of the current format buffers, \
+ * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
+ int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+ /* Truncate the name to make the message fit. */ \
+ ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+ len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
+ name_len, name, value); \
+ } \
+ write(atrace_marker_fd, buf, len); \
+}
void atrace_async_begin_body(const char* name, int32_t cookie)
{
- char buf[ATRACE_MESSAGE_LENGTH];
- size_t len;
-
- len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%" PRId32,
- getpid(), name, cookie);
- write(atrace_marker_fd, buf, len);
+ WRITE_MSG("S|%d|", "|%" PRId32, getpid(), name, cookie);
}
void atrace_async_end_body(const char* name, int32_t cookie)
{
- char buf[ATRACE_MESSAGE_LENGTH];
- size_t len;
-
- len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%" PRId32,
- getpid(), name, cookie);
- write(atrace_marker_fd, buf, len);
+ WRITE_MSG("F|%d|", "|%" PRId32, getpid(), name, cookie);
}
void atrace_int_body(const char* name, int32_t value)
{
- char buf[ATRACE_MESSAGE_LENGTH];
- size_t len;
-
- len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId32,
- getpid(), name, value);
- write(atrace_marker_fd, buf, len);
+ WRITE_MSG("C|%d|", "|%" PRId32, getpid(), name, value);
}
void atrace_int64_body(const char* name, int64_t value)
{
- char buf[ATRACE_MESSAGE_LENGTH];
- size_t len;
-
- len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId64,
- getpid(), name, value);
- write(atrace_marker_fd, buf, len);
+ WRITE_MSG("C|%d|", "|%" PRId64, getpid(), name, value);
}
diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk
index 624e385..a49ce58 100644
--- a/libdiskconfig/Android.mk
+++ b/libdiskconfig/Android.mk
@@ -13,6 +13,8 @@
LOCAL_MODULE_TAGS := optional
LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc
LOCAL_CFLAGS := -Werror
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include $(BUILD_SHARED_LIBRARY)
ifeq ($(HOST_OS),linux)
@@ -21,5 +23,7 @@
LOCAL_MODULE := libdiskconfig_host
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include $(BUILD_HOST_STATIC_LIBRARY)
endif # HOST_OS == linux
diff --git a/include/diskconfig/diskconfig.h b/libdiskconfig/include/diskconfig/diskconfig.h
similarity index 100%
rename from include/diskconfig/diskconfig.h
rename to libdiskconfig/include/diskconfig/diskconfig.h
diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk
index 894f90e..d4d07c2 100644
--- a/libion/tests/Android.mk
+++ b/libion/tests/Android.mk
@@ -17,8 +17,9 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_MODULE := ion-unit-tests
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
LOCAL_SHARED_LIBRARIES += libion
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers
LOCAL_SRC_FILES := \
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
index e26b302..3c4524e 100644
--- a/libion/tests/allocate_test.cpp
+++ b/libion/tests/allocate_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <memory>
#include <sys/mman.h>
#include <gtest/gtest.h>
@@ -90,7 +91,7 @@
TEST_F(Allocate, Zeroed)
{
- void *zeroes = calloc(4096, 1);
+ auto zeroes_ptr = std::make_unique<char[]>(4096);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -125,14 +126,11 @@
ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
ASSERT_TRUE(ptr != NULL);
- ASSERT_EQ(0, memcmp(ptr, zeroes, 4096));
+ ASSERT_EQ(0, memcmp(ptr, zeroes_ptr.get(), 4096));
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(zeroes);
-
}
TEST_F(Allocate, Large)
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
index 6f6e1bd..0be52bf 100644
--- a/libion/tests/device_test.cpp
+++ b/libion/tests/device_test.cpp
@@ -15,6 +15,7 @@
*/
#include <fcntl.h>
+#include <memory>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -133,8 +134,8 @@
TEST_F(Device, KernelReadCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -161,14 +162,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelWriteCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -195,14 +194,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAReadCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -227,14 +224,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAWriteCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -261,14 +256,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelReadCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -295,14 +288,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelWriteCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -329,14 +320,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAReadCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -363,14 +352,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAWriteCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -399,13 +386,11 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelRead)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -432,14 +417,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelWrite)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -466,14 +449,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMARead)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -498,14 +479,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAWrite)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -532,13 +511,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, IsCached)
{
- void *buf = malloc(4096);
+ auto buf_ptr = std::make_unique<char[]>(4096);
+ void *buf = buf_ptr.get();
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
diff --git a/liblog/Android.bp b/liblog/Android.bp
index ee883f0..607d667 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -14,15 +14,16 @@
// limitations under the License.
//
-liblog_host_sources = [
+liblog_sources = [
"logd_write.c",
+ "log_event_list.c",
"log_event_write.c",
+]
+liblog_host_sources = [
"fake_log_device.c",
//"event.logtags",
]
liblog_target_sources = [
- "logd_write.c",
- "log_event_write.c",
"event_tag_map.c",
"log_time.cpp",
"log_is_loggable.c",
@@ -36,6 +37,8 @@
name: "liblog",
host_supported: true,
+ srcs: liblog_sources,
+
target: {
host: {
srcs: liblog_host_sources,
@@ -71,7 +74,6 @@
// $(LOCAL_PATH)/event.logtags)
// so make sure we do not regret hard-coding it as follows:
"-DLIBLOG_LOG_TAG=1005",
- "-DSNET_EVENT_LOG_TAG=1397638484",
],
compile_multilib: "both",
stl: "none",
diff --git a/liblog/Android.mk b/liblog/Android.mk
index a183db8..dd5b518 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -24,10 +24,10 @@
# so make sure we do not regret hard-coding it as follows:
liblog_cflags := -DLIBLOG_LOG_TAG=1005
-liblog_host_sources := logd_write.c log_event_write.c fake_log_device.c event.logtags
-liblog_target_sources := logd_write.c log_event_write.c event_tag_map.c log_time.cpp log_is_loggable.c
-liblog_target_sources += logprint.c
-liblog_target_sources += log_read.c
+liblog_sources := logd_write.c log_event_list.c log_event_write.c
+liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags
+liblog_target_sources := $(liblog_sources) event_tag_map.c
+liblog_target_sources += log_time.cpp log_is_loggable.c logprint.c log_read.c
# Shared and static library for host
# ========================================================
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index cb80ee6..aa88bed 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -69,7 +69,7 @@
int fakeFd;
/* a printable name for this fake device */
- char *debugName;
+ char debugName[sizeof("/dev/log/security")];
/* nonzero if this is a binary log */
int isBinary;
@@ -123,8 +123,8 @@
* File descriptor management.
*/
#define FAKE_FD_BASE 10000
-#define MAX_OPEN_LOGS 16
-static LogState *openLogTable[MAX_OPEN_LOGS];
+#define MAX_OPEN_LOGS 8
+static LogState openLogTable[MAX_OPEN_LOGS];
/*
* Allocate an fd and associate a new LogState with it.
@@ -134,11 +134,10 @@
{
size_t i;
- for (i = 0; i < sizeof(openLogTable); i++) {
- if (openLogTable[i] == NULL) {
- openLogTable[i] = calloc(1, sizeof(LogState));
- openLogTable[i]->fakeFd = FAKE_FD_BASE + i;
- return openLogTable[i];
+ for (i = 0; i < (sizeof(openLogTable) / sizeof(openLogTable[0])); i++) {
+ if (openLogTable[i].fakeFd == 0) {
+ openLogTable[i].fakeFd = FAKE_FD_BASE + i;
+ return &openLogTable[i];
}
}
return NULL;
@@ -150,7 +149,7 @@
static LogState *fdToLogState(int fd)
{
if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
- return openLogTable[fd - FAKE_FD_BASE];
+ return &openLogTable[fd - FAKE_FD_BASE];
}
return NULL;
}
@@ -166,9 +165,7 @@
ls = fdToLogState(fd);
if (ls != NULL) {
- openLogTable[fd - FAKE_FD_BASE] = NULL;
- free(ls->debugName);
- free(ls);
+ memset(&openLogTable[fd - FAKE_FD_BASE], 0, sizeof(openLogTable[0]));
}
unlock();
@@ -191,10 +188,12 @@
{
static const int kDevLogLen = sizeof("/dev/log/") - 1;
- logState->debugName = strdup(pathName);
+ strncpy(logState->debugName, pathName, sizeof(logState->debugName));
+ logState->debugName[sizeof(logState->debugName) - 1] = '\0';
/* identify binary logs */
- if (strcmp(pathName + kDevLogLen, "events") == 0) {
+ if (!strcmp(pathName + kDevLogLen, "events") ||
+ !strcmp(pathName + kDevLogLen, "security")) {
logState->isBinary = 1;
}
@@ -218,8 +217,7 @@
i = 0;
while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
- i < kMaxTagLen)
- {
+ i < kMaxTagLen) {
tagName[i++] = *tags++;
}
if (i == kMaxTagLen) {
@@ -320,9 +318,9 @@
};
int idx;
- idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
+ idx = (int)priority - (int)ANDROID_LOG_VERBOSE;
if (idx < 0 ||
- idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+ idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
return "?unknown?";
return priorityStrings[idx];
}
@@ -367,7 +365,11 @@
char prefixBuf[128], suffixBuf[128];
char priChar;
time_t when;
+#if !defined(_WIN32)
pid_t pid, tid;
+#else
+ uint32_t pid, tid;
+#endif
TRACE("LOG %d: %s %s", logPrio, tag, msg);
@@ -450,13 +452,15 @@
while (p < end) {
if (*p++ == '\n') numLines++;
}
- if (p > msg && *(p-1) != '\n') numLines++;
+ if (p > msg && *(p-1) != '\n') {
+ numLines++;
+ }
/*
* Create an array of iovecs large enough to write all of
* the lines with a prefix and a suffix.
*/
- const size_t INLINE_VECS = 6;
+ const size_t INLINE_VECS = 64;
const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*));
struct iovec stackVec[INLINE_VECS];
struct iovec* vec = stackVec;
@@ -465,13 +469,13 @@
if (numLines > MAX_LINES)
numLines = MAX_LINES;
- numVecs = numLines*3; // 3 iovecs per line.
+ numVecs = numLines * 3; // 3 iovecs per line.
if (numVecs > INLINE_VECS) {
vec = (struct iovec*)malloc(sizeof(struct iovec)*numVecs);
if (vec == NULL) {
msg = "LOG: write failed, no memory";
- numVecs = 3;
- numLines = 1;
+ numVecs = INLINE_VECS;
+ numLines = numVecs / 3;
vec = stackVec;
}
}
@@ -490,7 +494,9 @@
v++;
}
const char* start = p;
- while (p < end && *p != '\n') p++;
+ while (p < end && *p != '\n') {
+ p++;
+ }
if ((p-start) > 0) {
v->iov_base = (void*)start;
v->iov_len = p-start;
@@ -685,6 +691,17 @@
return redirectOpen(pathName, flags);
}
+/*
+ * The logger API has no means or need to 'stop' or 'close' using the logs,
+ * and as such, there is no way for that 'stop' or 'close' to translate into
+ * a close operation to the fake log handler. fakeLogClose is provided for
+ * completeness only.
+ *
+ * We have no intention of adding a log close operation as it would complicate
+ * every user of the logging API with no gain since the only valid place to
+ * call is in the exit handler. Logging can continue in the exit handler to
+ * help debug HOST tools ...
+ */
int fakeLogClose(int fd)
{
/* Assume that open() was called first. */
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
new file mode 100644
index 0000000..2213f21
--- /dev/null
+++ b/liblog/log_event_list.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/log.h>
+#include <log/logger.h>
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+typedef struct {
+ uint32_t tag;
+ unsigned pos; /* Read/write position into buffer */
+ unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements */
+ unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* pos for list counter */
+ unsigned list_nest_depth;
+ unsigned len; /* Length or raw buffer. */
+ bool overflow;
+ bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+ enum {
+ kAndroidLoggerRead = 1,
+ kAndroidLoggerWrite = 2,
+ } read_write_flag;
+ uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+} android_log_context_internal;
+
+android_log_context create_android_logger(uint32_t tag) {
+ size_t needed, i;
+ android_log_context_internal *context;
+
+ context = calloc(1, sizeof(android_log_context_internal));
+ if (!context) {
+ return NULL;
+ }
+ context->tag = tag;
+ context->read_write_flag = kAndroidLoggerWrite;
+ needed = sizeof(uint8_t) + sizeof(uint8_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ }
+ /* Everything is a list */
+ context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+ context->list[0] = context->pos + 1;
+ context->pos += needed;
+
+ return (android_log_context)context;
+}
+
+android_log_context create_android_log_parser(const char *msg, size_t len) {
+ android_log_context_internal *context;
+ size_t i;
+
+ context = calloc(1, sizeof(android_log_context_internal));
+ if (!context) {
+ return NULL;
+ }
+ len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
+ context->len = len;
+ memcpy(context->storage, msg, len);
+ context->read_write_flag = kAndroidLoggerRead;
+
+ return (android_log_context)context;
+}
+
+int android_log_destroy(android_log_context *ctx) {
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)*ctx;
+ if (!context) {
+ return -EBADF;
+ }
+ memset(context, 0, sizeof(*context));
+ free(context);
+ *ctx = NULL;
+ return 0;
+}
+
+int android_log_write_list_begin(android_log_context ctx) {
+ size_t needed;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context ||
+ (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ needed = sizeof(uint8_t) + sizeof(uint8_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ context->list_nest_depth++;
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+ context->storage[context->pos + 1] = 0;
+ context->list[context->list_nest_depth] = context->pos + 1;
+ context->count[context->list_nest_depth] = 0;
+ context->pos += needed;
+ return 0;
+}
+
+static inline void copy4LE(uint8_t *buf, uint32_t val)
+{
+ buf[0] = val & 0xFF;
+ buf[1] = (val >> 8) & 0xFF;
+ buf[2] = (val >> 16) & 0xFF;
+ buf[3] = (val >> 24) & 0xFF;
+}
+
+int android_log_write_int32(android_log_context ctx, int32_t value) {
+ size_t needed;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ needed = sizeof(uint8_t) + sizeof(value);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ context->storage[context->pos + 0] = EVENT_TYPE_INT;
+ copy4LE(&context->storage[context->pos + 1], value);
+ context->pos += needed;
+ return 0;
+}
+
+static inline void copy8LE(uint8_t *buf, uint64_t val)
+{
+ buf[0] = val & 0xFF;
+ buf[1] = (val >> 8) & 0xFF;
+ buf[2] = (val >> 16) & 0xFF;
+ buf[3] = (val >> 24) & 0xFF;
+ buf[4] = (val >> 32) & 0xFF;
+ buf[5] = (val >> 40) & 0xFF;
+ buf[6] = (val >> 48) & 0xFF;
+ buf[7] = (val >> 56) & 0xFF;
+}
+
+int android_log_write_int64(android_log_context ctx, int64_t value) {
+ size_t needed;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ needed = sizeof(uint8_t) + sizeof(value);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ context->count[context->list_nest_depth]++;
+ context->storage[context->pos + 0] = EVENT_TYPE_LONG;
+ copy8LE(&context->storage[context->pos + 1], value);
+ context->pos += needed;
+ return 0;
+}
+
+int android_log_write_string8_len(android_log_context ctx,
+ const char *value, size_t maxlen) {
+ size_t needed;
+ ssize_t len;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ if (!value) {
+ value = "";
+ }
+ len = strnlen(value, maxlen);
+ needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ /* Truncate string for delivery */
+ len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+ if (len <= 0) {
+ context->overflow = true;
+ return -EIO;
+ }
+ }
+ context->count[context->list_nest_depth]++;
+ context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+ copy4LE(&context->storage[context->pos + 1], len);
+ if (len) {
+ memcpy(&context->storage[context->pos + 5], value, len);
+ }
+ context->pos += needed;
+ return len;
+}
+
+int android_log_write_string8(android_log_context ctx, const char *value) {
+ return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
+}
+
+int android_log_write_float32(android_log_context ctx, float value) {
+ size_t needed;
+ uint32_t ivalue;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ needed = sizeof(uint8_t) + sizeof(ivalue);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ return -EIO;
+ }
+ ivalue = *(uint32_t *)&value;
+ context->count[context->list_nest_depth]++;
+ context->storage[context->pos + 0] = EVENT_TYPE_FLOAT;
+ copy4LE(&context->storage[context->pos + 1], ivalue);
+ context->pos += needed;
+ return 0;
+}
+
+int android_log_write_list_end(android_log_context ctx) {
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->overflow = true;
+ context->list_nest_depth--;
+ return -EOVERFLOW;
+ }
+ if (!context->list_nest_depth) {
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ if (context->list[context->list_nest_depth] <= 0) {
+ context->list_nest_depth--;
+ context->overflow = true;
+ return -EOVERFLOW;
+ }
+ context->storage[context->list[context->list_nest_depth]] =
+ context->count[context->list_nest_depth];
+ context->list_nest_depth--;
+ return 0;
+}
+
+/*
+ * Logs the list of elements to the event log.
+ */
+int android_log_write_list(android_log_context ctx, log_id_t id) {
+ android_log_context_internal *context;
+ const char *msg;
+ ssize_t len;
+
+ if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY)) {
+ return -EINVAL;
+ }
+
+ context = (android_log_context_internal *)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->list_nest_depth) {
+ return -EIO;
+ }
+ /* NB: if there was overflow, then log is truncated. Nothing reported */
+ context->storage[1] = context->count[0];
+ len = context->len = context->pos;
+ msg = (const char *)context->storage;
+ /* it'snot a list */
+ if (context->count[0] <= 1) {
+ len -= sizeof(uint8_t) + sizeof(uint8_t);
+ if (len < 0) {
+ len = 0;
+ }
+ msg += sizeof(uint8_t) + sizeof(uint8_t);
+ }
+ return (id == LOG_ID_EVENTS) ?
+ __android_log_bwrite(context->tag, msg, len) :
+ __android_log_security_bwrite(context->tag, msg, len);
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+/*
+ * Extract an 8-byte value from a byte stream.
+ */
+static inline uint64_t get8LE(const uint8_t* src)
+{
+ uint32_t low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+ uint32_t high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+ return ((uint64_t) high << 32) | (uint64_t) low;
+}
+
+/*
+ * Gets the next element. Parsing errors result in an EVENT_TYPE_UNKNOWN type.
+ * If there is nothing to process, the complete field is set to non-zero. If
+ * an EVENT_TYPE_UNKNOWN type is returned once, and the caller does not check
+ * this and continues to call this function, the behavior is undefined
+ * (although it won't crash).
+ */
+static android_log_list_element android_log_read_next_internal(
+ android_log_context ctx, int peek) {
+ android_log_list_element elem;
+ unsigned pos;
+ android_log_context_internal *context;
+
+ context = (android_log_context_internal *)ctx;
+
+ memset(&elem, 0, sizeof(elem));
+
+ /* Nothing to parse from this context, so return complete. */
+ if (!context || (kAndroidLoggerRead != context->read_write_flag) ||
+ (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) ||
+ (context->count[context->list_nest_depth] >=
+ (MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ if (context &&
+ (context->list_stop ||
+ ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
+ !context->count[context->list_nest_depth]))) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ }
+ elem.complete = true;
+ return elem;
+ }
+
+ /*
+ * Use a different variable to update the position in case this
+ * operation is a "peek".
+ */
+ pos = context->pos;
+ if (context->list_stop) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ elem.complete = !context->count[0] && (!context->list_nest_depth ||
+ ((context->list_nest_depth == 1) && !context->count[1]));
+ if (!peek) {
+ /* Suck in superfluous stop */
+ if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
+ context->pos = pos + 1;
+ }
+ if (context->list_nest_depth) {
+ --context->list_nest_depth;
+ if (context->count[context->list_nest_depth]) {
+ context->list_stop = false;
+ }
+ } else {
+ context->list_stop = false;
+ }
+ }
+ return elem;
+ }
+ if ((pos + 1) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+
+ elem.type = context->storage[pos++];
+ switch ((int)elem.type) {
+ case EVENT_TYPE_FLOAT:
+ /* Rely on union to translate elem.data.int32 into elem.data.float32 */
+ /* FALLTHRU */
+ case EVENT_TYPE_INT:
+ elem.len = sizeof(int32_t);
+ if ((pos + elem.len) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ elem.data.int32 = get4LE(&context->storage[pos]);
+ /* common tangeable object suffix */
+ pos += elem.len;
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+
+ case EVENT_TYPE_LONG:
+ elem.len = sizeof(int64_t);
+ if ((pos + elem.len) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ elem.data.int64 = get8LE(&context->storage[pos]);
+ /* common tangeable object suffix */
+ pos += elem.len;
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+
+ case EVENT_TYPE_STRING:
+ if ((pos + sizeof(int32_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+ elem.len = get4LE(&context->storage[pos]);
+ pos += sizeof(int32_t);
+ if ((pos + elem.len) > context->len) {
+ elem.len = context->len - pos; /* truncate string */
+ elem.complete = true;
+ if (!elem.len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+ }
+ elem.data.string = (char *)&context->storage[pos];
+ /* common tangeable object suffix */
+ pos += elem.len;
+ elem.complete = !context->list_nest_depth && !context->count[0];
+ if (!peek) {
+ if (!context->count[context->list_nest_depth] ||
+ !--(context->count[context->list_nest_depth])) {
+ context->list_stop = true;
+ }
+ context->pos = pos;
+ }
+ return elem;
+
+ case EVENT_TYPE_LIST:
+ if ((pos + sizeof(uint8_t)) > context->len) {
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = true;
+ return elem;
+ }
+ elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
+ if (peek) {
+ return elem;
+ }
+ if (context->count[context->list_nest_depth]) {
+ context->count[context->list_nest_depth]--;
+ }
+ context->list_stop = !context->storage[pos];
+ context->list_nest_depth++;
+ if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
+ context->count[context->list_nest_depth] = context->storage[pos];
+ }
+ context->pos = pos + sizeof(uint8_t);
+ return elem;
+
+ case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
+ if (!peek) {
+ context->pos = pos;
+ }
+ elem.type = EVENT_TYPE_UNKNOWN;
+ elem.complete = !context->list_nest_depth;
+ if (context->list_nest_depth > 0) {
+ elem.type = EVENT_TYPE_LIST_STOP;
+ if (!peek) {
+ context->list_nest_depth--;
+ }
+ }
+ return elem;
+
+ default:
+ elem.type = EVENT_TYPE_UNKNOWN;
+ return elem;
+ }
+}
+
+android_log_list_element android_log_read_next(android_log_context ctx) {
+ return android_log_read_next_internal(ctx, 0);
+}
+
+android_log_list_element android_log_peek_next(android_log_context ctx) {
+ return android_log_read_next_internal(ctx, 1);
+}
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.c
index 0bc42d5..ad42edd 100644
--- a/liblog/log_event_write.c
+++ b/liblog/log_event_write.c
@@ -15,74 +15,33 @@
*/
#include <errno.h>
-#include <string.h>
#include <log/log.h>
-#include <log/logger.h>
-#define MAX_EVENT_PAYLOAD 512
#define MAX_SUBTAG_LEN 32
-static inline void copy4LE(uint8_t *buf, size_t pos, int val)
+int __android_log_error_write(int tag, const char *subTag, int32_t uid,
+ const char *data, uint32_t dataLen)
{
- buf[pos] = val & 0xFF;
- buf[pos+1] = (val >> 8) & 0xFF;
- buf[pos+2] = (val >> 16) & 0xFF;
- buf[pos+3] = (val >> 24) & 0xFF;
-}
+ int ret = -EINVAL;
-int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
- uint32_t dataLen)
-{
- uint8_t buf[MAX_EVENT_PAYLOAD];
- size_t pos = 0;
- uint32_t subTagLen = 0;
- uint32_t roomLeftForData = 0;
+ if (subTag && (data || !dataLen)) {
+ android_log_context ctx = create_android_logger(tag);
- if ((subTag == NULL) || ((data == NULL) && (dataLen != 0))) return -EINVAL;
-
- subTagLen = strlen(subTag);
-
- // Truncate subtags that are too long.
- subTagLen = subTagLen > MAX_SUBTAG_LEN ? MAX_SUBTAG_LEN : subTagLen;
-
- // Truncate dataLen if it is too long.
- roomLeftForData = MAX_EVENT_PAYLOAD -
- (1 + // EVENT_TYPE_LIST
- 1 + // Number of elements in list
- 1 + // EVENT_TYPE_STRING
- sizeof(subTagLen) +
- subTagLen +
- 1 + // EVENT_TYPE_INT
- sizeof(uid) +
- 1 + // EVENT_TYPE_STRING
- sizeof(dataLen));
- dataLen = dataLen > roomLeftForData ? roomLeftForData : dataLen;
-
- buf[pos++] = EVENT_TYPE_LIST;
- buf[pos++] = 3; // Number of elements in the list (subTag, uid, data)
-
- // Write sub tag.
- buf[pos++] = EVENT_TYPE_STRING;
- copy4LE(buf, pos, subTagLen);
- pos += 4;
- memcpy(&buf[pos], subTag, subTagLen);
- pos += subTagLen;
-
- // Write UID.
- buf[pos++] = EVENT_TYPE_INT;
- copy4LE(buf, pos, uid);
- pos += 4;
-
- // Write data.
- buf[pos++] = EVENT_TYPE_STRING;
- copy4LE(buf, pos, dataLen);
- pos += 4;
- if (dataLen != 0)
- {
- memcpy(&buf[pos], data, dataLen);
- pos += dataLen;
+ ret = -ENOMEM;
+ if (ctx) {
+ ret = android_log_write_string8_len(ctx, subTag, MAX_SUBTAG_LEN);
+ if (ret >= 0) {
+ ret = android_log_write_int32(ctx, uid);
+ if (ret >= 0) {
+ ret = android_log_write_string8_len(ctx, data, dataLen);
+ if (ret >= 0) {
+ ret = android_log_write_list(ctx, LOG_ID_EVENTS);
+ }
+ }
+ }
+ android_log_destroy(&ctx);
+ }
}
-
- return __android_log_bwrite(tag, buf, pos);
+ return ret;
}
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 1aff272..fc63d2c 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -503,10 +503,7 @@
}
if (logger_list->pid) {
- n = snprintf(cp, remaining, " pid=%u", logger_list->pid);
- n = min(n, remaining);
- remaining -= n;
- cp += n;
+ snprintf(cp, remaining, " pid=%u", logger_list->pid);
}
return send_log_msg(NULL, NULL, buf, len);
@@ -657,7 +654,6 @@
preread_count = 0;
}
- ret = 0;
while(1) {
if (preread_count < sizeof(buf)) {
ret = TEMP_FAILURE_RETRY(read(logger_list->sock,
@@ -834,7 +830,6 @@
if (logger_list->pid) {
ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
ret = min(ret, remaining);
- remaining -= ret;
cp += ret;
}
@@ -867,7 +862,6 @@
logger_list->sock = sock;
}
- ret = 0;
while(1) {
memset(log_msg, 0, sizeof(*log_msg));
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index 4946073..b173d1a 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -125,7 +125,7 @@
#if FAKE_LOG_DEVICE
for (i = 0; i < LOG_ID_MAX; i++) {
- char buf[sizeof("/dev/log_system")];
+ char buf[sizeof("/dev/log_security")];
snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
log_fds[i] = fakeLogOpen(buf, O_WRONLY);
}
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 9dd6f03..3d58147 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -14,126 +14,9 @@
* limitations under the License.
*/
-#include <fcntl.h>
-#include <sys/cdefs.h>
-
#include <gtest/gtest.h>
-// Should be in bionic test suite, *but* we are using liblog to confirm
-// end-to-end logging, so let the overly cute oedipus complex begin ...
-#include "../../../../bionic/libc/bionic/libc_logging.cpp" // not Standalone
-#define _ANDROID_LOG_H // Priorities redefined
-#define _LIBS_LOG_LOG_H // log ids redefined
-typedef unsigned char log_id_t; // log_id_t missing as a result
-#define _LIBS_LOG_LOG_READ_H // log_time redefined
-
-#include <log/log.h>
-#include <log/logger.h>
-#include <log/log_read.h>
-
-TEST(libc, __libc_android_log_event_int) {
- struct logger_list *logger_list;
-
- pid_t pid = getpid();
-
- ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- int value = ts.tv_nsec;
-
- __libc_android_log_event_int(0, value);
- usleep(1000000);
-
- int count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- ASSERT_EQ(log_msg.entry.pid, pid);
-
- if ((log_msg.entry.len != (4 + 1 + 4))
- || ((int)log_msg.id() != LOG_ID_EVENTS)) {
- continue;
- }
-
- char *eventData = log_msg.msg();
-
- int incoming = (eventData[0] & 0xFF) |
- ((eventData[1] & 0xFF) << 8) |
- ((eventData[2] & 0xFF) << 16) |
- ((eventData[3] & 0xFF) << 24);
-
- if (incoming != 0) {
- continue;
- }
-
- if (eventData[4] != EVENT_TYPE_INT) {
- continue;
- }
-
- incoming = (eventData[4 + 1 + 0] & 0xFF) |
- ((eventData[4 + 1 + 1] & 0xFF) << 8) |
- ((eventData[4 + 1 + 2] & 0xFF) << 16) |
- ((eventData[4 + 1 + 3] & 0xFF) << 24);
-
- if (incoming == value) {
- ++count;
- }
- }
-
- EXPECT_EQ(1, count);
-
- android_logger_list_close(logger_list);
-}
-
-TEST(libc, __libc_fatal_no_abort) {
- struct logger_list *logger_list;
-
- pid_t pid = getpid();
-
- ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- (log_id_t)LOG_ID_CRASH, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
- char b[80];
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
-
- __libc_fatal_no_abort("%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec);
- snprintf(b, sizeof(b),"%u.%09u", (unsigned)ts.tv_sec, (unsigned)ts.tv_nsec);
- usleep(1000000);
-
- int count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) {
- break;
- }
-
- ASSERT_EQ(log_msg.entry.pid, pid);
-
- if ((int)log_msg.id() != LOG_ID_CRASH) {
- continue;
- }
-
- char *data = log_msg.msg();
-
- if ((*data == ANDROID_LOG_FATAL)
- && !strcmp(data + 1, "libc")
- && !strcmp(data + 1 + strlen(data + 1) + 1, b)) {
- ++count;
- }
- }
-
- EXPECT_EQ(1, count);
-
- android_logger_list_close(logger_list);
-}
+#include <stdio.h>
TEST(libc, __pstore_append) {
FILE *fp;
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 8517c9f..50ecd2d 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Android Open Source Project
+ * Copyright (C) 2013-2016 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.
@@ -173,7 +173,7 @@
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
}
-TEST(liblog, __android_log_bswrite) {
+static void bswrite_test(const char *message) {
struct logger_list *logger_list;
pid_t pid = getpid();
@@ -181,10 +181,30 @@
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
- static const char buffer[] = "Hello World";
log_time ts(android_log_clockid());
- ASSERT_LT(0, __android_log_bswrite(0, buffer));
+ ASSERT_LT(0, __android_log_bswrite(0, message));
+ size_t num_lines = 1, size = 0, length = 0, total = 0;
+ const char *cp = message;
+ while (*cp) {
+ if (*cp == '\n') {
+ if (cp[1]) {
+ ++num_lines;
+ }
+ } else {
+ ++size;
+ }
+ ++cp;
+ ++total;
+ ++length;
+ if ((LOGGER_ENTRY_MAX_PAYLOAD - 4 - 1 - 4) <= length) {
+ break;
+ }
+ }
+ while (*cp) {
+ ++cp;
+ ++total;
+ }
usleep(1000000);
int count = 0;
@@ -199,7 +219,7 @@
if ((log_msg.entry.sec < (ts.tv_sec - 1))
|| ((ts.tv_sec + 1) < log_msg.entry.sec)
- || (log_msg.entry.len != (4 + 1 + 4 + sizeof(buffer) - 1))
+ || ((size_t)log_msg.entry.len != (4 + 1 + 4 + length))
|| (log_msg.id() != LOG_ID_EVENTS)) {
continue;
}
@@ -210,21 +230,22 @@
continue;
}
- int len = get4LE(eventData + 4 + 1);
- if (len == (sizeof(buffer) - 1)) {
+ size_t len = get4LE(eventData + 4 + 1);
+ if (len == total) {
++count;
AndroidLogFormat *logformat = android_log_format_new();
EXPECT_TRUE(NULL != logformat);
AndroidLogEntry entry;
char msgBuf[1024];
- EXPECT_EQ(0, android_log_processBinaryLogBuffer(&log_msg.entry_v1,
- &entry,
- NULL,
- msgBuf,
- sizeof(msgBuf)));
- fflush(stderr);
- EXPECT_EQ(31, android_log_printLogLine(logformat, fileno(stderr), &entry));
+ int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
+ &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+ EXPECT_EQ((length == total) ? 0 : -1, processBinaryLogBuffer);
+ if (processBinaryLogBuffer == 0) {
+ fflush(stderr);
+ EXPECT_EQ((int)((20 * num_lines) + size),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
+ }
android_log_format_free(logformat);
}
}
@@ -234,18 +255,55 @@
android_logger_list_close(logger_list);
}
-TEST(liblog, __android_log_bswrite__empty_string) {
+TEST(liblog, __android_log_bswrite_and_print) {
+ bswrite_test("Hello World");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__empty_string) {
+ bswrite_test("");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__newline_prefix) {
+ bswrite_test("\nHello World\n");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__newline_space_prefix) {
+ bswrite_test("\n Hello World \n");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__multiple_newline) {
+ bswrite_test("one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten");
+}
+
+static void buf_write_test(const char *message) {
struct logger_list *logger_list;
pid_t pid = getpid();
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
- static const char buffer[] = "";
+ static const char tag[] = "TEST__android_log_buf_write";
log_time ts(android_log_clockid());
- ASSERT_LT(0, __android_log_bswrite(0, buffer));
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ tag, message));
+ size_t num_lines = 1, size = 0, length = 0;
+ const char *cp = message;
+ while (*cp) {
+ if (*cp == '\n') {
+ if (cp[1]) {
+ ++num_lines;
+ }
+ } else {
+ ++size;
+ }
+ ++length;
+ if ((LOGGER_ENTRY_MAX_PAYLOAD - 2 - sizeof(tag)) <= length) {
+ break;
+ }
+ ++cp;
+ }
usleep(1000000);
int count = 0;
@@ -260,34 +318,25 @@
if ((log_msg.entry.sec < (ts.tv_sec - 1))
|| ((ts.tv_sec + 1) < log_msg.entry.sec)
- || (log_msg.entry.len != (4 + 1 + 4))
- || (log_msg.id() != LOG_ID_EVENTS)) {
+ || ((size_t)log_msg.entry.len != (sizeof(tag) + length + 2))
+ || (log_msg.id() != LOG_ID_MAIN)) {
continue;
}
- char *eventData = log_msg.msg();
+ ++count;
- if (eventData[4] != EVENT_TYPE_STRING) {
- continue;
- }
-
- int len = get4LE(eventData + 4 + 1);
- if (len == 0) {
- ++count;
-
- AndroidLogFormat *logformat = android_log_format_new();
- EXPECT_TRUE(NULL != logformat);
- AndroidLogEntry entry;
- char msgBuf[1024];
- EXPECT_EQ(0, android_log_processBinaryLogBuffer(&log_msg.entry_v1,
- &entry,
- NULL,
- msgBuf,
- sizeof(msgBuf)));
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ int processLogBuffer = android_log_processLogBuffer(&log_msg.entry_v1,
+ &entry);
+ EXPECT_EQ(0, processLogBuffer);
+ if (processLogBuffer == 0) {
fflush(stderr);
- EXPECT_EQ(20, android_log_printLogLine(logformat, fileno(stderr), &entry));
- android_log_format_free(logformat);
+ EXPECT_EQ((int)(((11 + sizeof(tag)) * num_lines) + size),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
}
+ android_log_format_free(logformat);
}
EXPECT_EQ(1, count);
@@ -295,6 +344,18 @@
android_logger_list_close(logger_list);
}
+TEST(liblog, __android_log_buf_write_and_print__empty) {
+ buf_write_test("");
+}
+
+TEST(liblog, __android_log_buf_write_and_print__newline_prefix) {
+ buf_write_test("\nHello World\n");
+}
+
+TEST(liblog, __android_log_buf_write_and_print__newline_space_prefix) {
+ buf_write_test("\n Hello World \n");
+}
+
TEST(liblog, __security) {
static const char persist_key[] = "persist.logd.security";
static const char readonly_key[] = "ro.device_owner";
@@ -609,7 +670,7 @@
EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
}
-static const char max_payload_tag[] = "TEST_max_payload_XXXX";
+static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
#define SIZEOF_MAX_PAYLOAD_BUF (LOGGER_ENTRY_MAX_PAYLOAD - \
sizeof(max_payload_tag) - 1)
static const char max_payload_buf[] = "LEONATO\n\
@@ -1368,7 +1429,7 @@
const int TAG = 123456782;
const char SUBTAG[] = "test-subtag";
const int UID = -1;
- const int DATA_LEN = SIZEOF_MAX_PAYLOAD_BUF;
+ const int DATA_LEN = sizeof(max_payload_buf);
struct logger_list *logger_list;
pid_t pid = getpid();
@@ -1439,9 +1500,9 @@
}
eventData += dataLen;
- // 4 bytes for the tag, and 512 bytes for the log since the
- // max_payload_buf should be truncated.
- ASSERT_EQ(4 + 512, eventData - original);
+ // 4 bytes for the tag, and max_payload_buf should be truncated.
+ ASSERT_LE(4 + 512, eventData - original); // worst expectations
+ ASSERT_GT(4 + DATA_LEN, eventData - original); // must be truncated
++count;
}
@@ -1576,6 +1637,14 @@
android_logger_list_close(logger_list);
}
+TEST(liblog, __android_log_bswrite_and_print___max) {
+ bswrite_test(max_payload_buf);
+}
+
+TEST(liblog, __android_log_buf_write_and_print__max) {
+ buf_write_test(max_payload_buf);
+}
+
TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
const int TAG = 123456785;
const char SUBTAG[] = "test-subtag";
@@ -1672,3 +1741,561 @@
android_logger_list_close(logger_list);
}
+
+static int is_real_element(int type) {
+ return ((type == EVENT_TYPE_INT) ||
+ (type == EVENT_TYPE_LONG) ||
+ (type == EVENT_TYPE_STRING) ||
+ (type == EVENT_TYPE_FLOAT));
+}
+
+int android_log_buffer_to_string(const char *msg, size_t len,
+ char *strOut, size_t strOutLen) {
+ android_log_context context = create_android_log_parser(msg, len);
+ android_log_list_element elem;
+ bool overflow = false;
+ /* Reserve 1 byte for null terminator. */
+ size_t origStrOutLen = strOutLen--;
+
+ if (!context) {
+ return -EBADF;
+ }
+
+ memset(&elem, 0, sizeof(elem));
+
+ size_t outCount;
+
+ do {
+ elem = android_log_read_next(context);
+ switch ((int)elem.type) {
+ case EVENT_TYPE_LIST:
+ if (strOutLen == 0) {
+ overflow = true;
+ } else {
+ *strOut++ = '[';
+ strOutLen--;
+ }
+ break;
+
+ case EVENT_TYPE_LIST_STOP:
+ if (strOutLen == 0) {
+ overflow = true;
+ } else {
+ *strOut++ = ']';
+ strOutLen--;
+ }
+ break;
+
+ case EVENT_TYPE_INT:
+ /*
+ * snprintf also requires room for the null terminator, which
+ * we don't care about but we have allocated enough room for
+ * that
+ */
+ outCount = snprintf(strOut, strOutLen + 1,
+ "%" PRId32, elem.data.int32);
+ if (outCount <= strOutLen) {
+ strOut += outCount;
+ strOutLen -= outCount;
+ } else {
+ overflow = true;
+ }
+ break;
+
+ case EVENT_TYPE_LONG:
+ /*
+ * snprintf also requires room for the null terminator, which
+ * we don't care about but we have allocated enough room for
+ * that
+ */
+ outCount = snprintf(strOut, strOutLen + 1,
+ "%" PRId64, elem.data.int64);
+ if (outCount <= strOutLen) {
+ strOut += outCount;
+ strOutLen -= outCount;
+ } else {
+ overflow = true;
+ }
+ break;
+
+ case EVENT_TYPE_FLOAT:
+ /*
+ * snprintf also requires room for the null terminator, which
+ * we don't care about but we have allocated enough room for
+ * that
+ */
+ outCount = snprintf(strOut, strOutLen + 1, "%f", elem.data.float32);
+ if (outCount <= strOutLen) {
+ strOut += outCount;
+ strOutLen -= outCount;
+ } else {
+ overflow = true;
+ }
+ break;
+
+ default:
+ elem.complete = true;
+ break;
+
+ case EVENT_TYPE_UNKNOWN:
+#if 0 // Ideal purity in the test, we want to complain about UNKNOWN showing up
+ if (elem.complete) {
+ break;
+ }
+#endif
+ elem.data.string = const_cast<char *>("<unknown>");
+ elem.len = strlen(elem.data.string);
+ /* FALLTHRU */
+ case EVENT_TYPE_STRING:
+ if (elem.len <= strOutLen) {
+ memcpy(strOut, elem.data.string, elem.len);
+ strOut += elem.len;
+ strOutLen -= elem.len;
+ } else if (strOutLen > 0) {
+ /* copy what we can */
+ memcpy(strOut, elem.data.string, strOutLen);
+ strOut += strOutLen;
+ strOutLen = 0;
+ overflow = true;
+ }
+ break;
+ }
+
+ if (elem.complete) {
+ break;
+ }
+ /* Determine whether to put a comma or not. */
+ if (!overflow && (is_real_element(elem.type) ||
+ (elem.type == EVENT_TYPE_LIST_STOP))) {
+ android_log_list_element next = android_log_peek_next(context);
+ if (!next.complete && (is_real_element(next.type) ||
+ (next.type == EVENT_TYPE_LIST))) {
+ if (strOutLen == 0) {
+ overflow = true;
+ } else {
+ *strOut++ = ',';
+ strOutLen--;
+ }
+ }
+ }
+ } while ((elem.type != EVENT_TYPE_UNKNOWN) && !overflow && !elem.complete);
+
+ android_log_destroy(&context);
+
+ if (overflow) {
+ if (strOutLen < origStrOutLen) {
+ /* leave an indicator */
+ *(strOut-1) = '!';
+ } else {
+ /* nothing was written at all */
+ *strOut++ = '!';
+ }
+ }
+ *strOut++ = '\0';
+
+ if ((elem.type == EVENT_TYPE_UNKNOWN) && !elem.complete) {
+ fprintf(stderr, "Binary log entry conversion failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const char *event_test_int32(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint32_t);
+
+ return "1076895760";
+}
+
+static const char *event_test_int64(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint64_t);
+
+ return "-9191740941672636400";
+}
+
+static const char *event_test_list_int64(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint64_t);
+
+ return "[-9191740941672636400]";
+}
+
+static const char *event_test_simple_automagic_list(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ // The convenience API where we allow a simple list to be
+ // created without explicit begin or end calls.
+ EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010));
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint64_t);
+
+ return "[1076895760,-9191740941672636400]";
+}
+
+static const char *event_test_list_empty(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t);
+
+ return "[]";
+}
+
+static const char *event_test_complex_nested_list(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+
+ EXPECT_LE(0, android_log_write_list_begin(ctx)); // [
+ EXPECT_LE(0, android_log_write_int32(ctx, 0x01020304));
+ EXPECT_LE(0, android_log_write_int64(ctx, 0x0102030405060708));
+ EXPECT_LE(0, android_log_write_string8(ctx, "Hello World"));
+ EXPECT_LE(0, android_log_write_list_begin(ctx)); // [
+ EXPECT_LE(0, android_log_write_int32(ctx, 1));
+ EXPECT_LE(0, android_log_write_int32(ctx, 2));
+ EXPECT_LE(0, android_log_write_int32(ctx, 3));
+ EXPECT_LE(0, android_log_write_int32(ctx, 4));
+ EXPECT_LE(0, android_log_write_list_end(ctx)); // ]
+ EXPECT_LE(0, android_log_write_float32(ctx, 1.0102030405060708));
+ EXPECT_LE(0, android_log_write_list_end(ctx)); // ]
+
+ //
+ // This one checks for the automagic list creation because a list
+ // begin and end was missing for it! This is actually an <oops> corner
+ // case, and not the behavior we morally support. The automagic API is to
+ // allow for a simple case of a series of objects in a single list. e.g.
+ // int32,int32,int32,string -> [int32,int32,int32,string]
+ //
+ EXPECT_LE(0, android_log_write_string8(ctx, "dlroW olleH"));
+
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint64_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof("Hello World") - 1 +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ 4 * (sizeof(uint8_t) + sizeof(uint32_t)) +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof("dlroW olleH") - 1;
+
+ return "[[16909060,72623859790382856,Hello World,[1,2,3,4],1.010203],dlroW olleH]";
+}
+
+static const char *event_test_7_level_prefix(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 1));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 2));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 3));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 4));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 5));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 6));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 7));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + 7 *
+ (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
+
+ return "[[[[[[[1],2],3],4],5],6],7]";
+}
+
+static const char *event_test_7_level_suffix(uint32_t tag, size_t &expected_len) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+ if (!ctx) {
+ return NULL;
+ }
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 1));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 2));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 3));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 4));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 5));
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, 6));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list_end(ctx));
+ EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+
+ expected_len = sizeof(uint32_t) + 6 *
+ (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
+
+ return "[1,[2,[3,[4,[5,[6]]]]]]";
+}
+
+static const char *event_test_android_log_error_write(uint32_t tag, size_t &expected_len) {
+ EXPECT_LE(0, __android_log_error_write(tag, "Hello World", 42, "dlroW olleH", 11));
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") - 1 +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("dlroW olleH") - 1;
+
+ return "[Hello World,42,dlroW olleH]";
+}
+
+static const char *event_test_android_log_error_write_null(uint32_t tag, size_t &expected_len) {
+ EXPECT_LE(0, __android_log_error_write(tag, "Hello World", 42, NULL, 0));
+
+ expected_len = sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint8_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") - 1 +
+ sizeof(uint8_t) + sizeof(uint32_t) +
+ sizeof(uint8_t) + sizeof(uint32_t) + sizeof("") - 1;
+
+ return "[Hello World,42,]";
+}
+
+// make sure all user buffers are flushed
+static void print_barrier() {
+ std::cout.flush();
+ fflush(stdout);
+ std::cerr.flush();
+ fflush(stderr); // everything else is paranoia ...
+}
+
+static void create_android_logger(const char *(*fn)(uint32_t tag, size_t &expected_len)) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ log_time ts(android_log_clockid());
+
+ size_t expected_len;
+ const char *expected_string = (*fn)(1005, expected_len);
+
+ if (!expected_string) {
+ android_logger_list_close(logger_list);
+ return;
+ }
+
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.sec < (ts.tv_sec - 1))
+ || ((ts.tv_sec + 1) < log_msg.entry.sec)
+ || ((size_t)log_msg.entry.len != expected_len)
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ ++count;
+
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ char msgBuf[1024];
+ int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
+ &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+ EXPECT_EQ(0, processBinaryLogBuffer);
+ if (processBinaryLogBuffer == 0) {
+ print_barrier();
+ int printLogLine = android_log_printLogLine(
+ logformat, fileno(stderr), &entry);
+ print_barrier();
+ EXPECT_EQ(20 + (int)strlen(expected_string), printLogLine);
+ }
+ android_log_format_free(logformat);
+
+ // test buffer reading API
+ snprintf(msgBuf, sizeof(msgBuf), "I/[%d]", get4LE(eventData));
+ print_barrier();
+ fprintf(stderr, "%-10s(%5u): ", msgBuf, pid);
+ memset(msgBuf, 0, sizeof(msgBuf));
+ int buffer_to_string = android_log_buffer_to_string(
+ eventData + sizeof(uint32_t),
+ log_msg.entry.len - sizeof(uint32_t),
+ msgBuf, sizeof(msgBuf));
+ fprintf(stderr, "%s\n", msgBuf);
+ print_barrier();
+ EXPECT_EQ(0, buffer_to_string);
+ EXPECT_EQ(strlen(expected_string), strlen(msgBuf));
+ EXPECT_EQ(0, strcmp(expected_string, msgBuf));
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+}
+
+TEST(liblog, create_android_logger_int32) {
+ create_android_logger(event_test_int32);
+}
+
+TEST(liblog, create_android_logger_int64) {
+ create_android_logger(event_test_int64);
+}
+
+TEST(liblog, create_android_logger_list_int64) {
+ create_android_logger(event_test_list_int64);
+}
+
+TEST(liblog, create_android_logger_simple_automagic_list) {
+ create_android_logger(event_test_simple_automagic_list);
+}
+
+TEST(liblog, create_android_logger_list_empty) {
+ create_android_logger(event_test_list_empty);
+}
+
+TEST(liblog, create_android_logger_complex_nested_list) {
+ create_android_logger(event_test_complex_nested_list);
+}
+
+TEST(liblog, create_android_logger_7_level_prefix) {
+ create_android_logger(event_test_7_level_prefix);
+}
+
+TEST(liblog, create_android_logger_7_level_suffix) {
+ create_android_logger(event_test_7_level_suffix);
+}
+
+TEST(liblog, create_android_logger_android_log_error_write) {
+ create_android_logger(event_test_android_log_error_write);
+}
+
+TEST(liblog, create_android_logger_android_log_error_write_null) {
+ create_android_logger(event_test_android_log_error_write_null);
+}
+
+TEST(liblog, create_android_logger_overflow) {
+ android_log_context ctx;
+
+ EXPECT_TRUE(NULL != (ctx = create_android_logger(1005)));
+ if (ctx) {
+ for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) {
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ }
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ /* One more for good measure, must be permanently unhappy */
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ EXPECT_TRUE(NULL == ctx);
+ }
+
+ ASSERT_TRUE(NULL != (ctx = create_android_logger(1005)));
+ for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) {
+ EXPECT_LE(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_write_int32(ctx, i));
+ }
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ /* One more for good measure, must be permanently unhappy */
+ EXPECT_GT(0, android_log_write_list_begin(ctx));
+ EXPECT_LE(0, android_log_destroy(&ctx));
+ ASSERT_TRUE(NULL == ctx);
+}
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk
index 7b170f5..ffc7244 100644
--- a/libmemtrack/Android.mk
+++ b/libmemtrack/Android.mk
@@ -5,6 +5,8 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := memtrack.c
LOCAL_MODULE := libmemtrack
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_C_INCLUDES += hardware/libhardware/include
LOCAL_SHARED_LIBRARIES := libhardware liblog
LOCAL_CFLAGS := -Wall -Werror
diff --git a/include/memtrack/memtrack.h b/libmemtrack/include/memtrack/memtrack.h
similarity index 98%
rename from include/memtrack/memtrack.h
rename to libmemtrack/include/memtrack/memtrack.h
index 3917300..8c0ab89 100644
--- a/include/memtrack/memtrack.h
+++ b/libmemtrack/include/memtrack/memtrack.h
@@ -19,7 +19,6 @@
#include <sys/types.h>
#include <stddef.h>
-#include <cutils/compiler.h>
#ifdef __cplusplus
extern "C" {
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
new file mode 100644
index 0000000..68f654c
--- /dev/null
+++ b/libmemunreachable/Allocator.cpp
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Header page:
+//
+// For minimum allocation size (8 bytes), bitmap can store used allocations for
+// up to 4032*8*8=258048, which is 256KiB minus the header page
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+#include "anon_vma_naming.h"
+#include "Allocator.h"
+#include "LinkedList.h"
+
+// runtime interfaces used:
+// abort
+// assert - fprintf + mmap
+// mmap
+// munmap
+// prctl
+
+constexpr size_t const_log2(size_t n, size_t p = 0) {
+ return (n <= 1) ? p : const_log2(n / 2, p + 1);
+}
+
+constexpr unsigned int div_round_up(unsigned int x, unsigned int y) {
+ return (x + y - 1) / y;
+}
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+static constexpr size_t kPageSize = 4096;
+static constexpr size_t kChunkSize = 256 * 1024;
+static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize;
+static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4;
+static constexpr size_t kMinBucketAllocationSize = 8;
+static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize)
+ - const_log2(kMinBucketAllocationSize) + 1;
+static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize
+ / kPageSize;
+
+std::atomic<int> heap_count;
+
+class Chunk;
+
+class HeapImpl {
+ public:
+ HeapImpl();
+ ~HeapImpl();
+ void* operator new(std::size_t count) noexcept;
+ void operator delete(void* ptr);
+
+ void* Alloc(size_t size);
+ void Free(void* ptr);
+ bool Empty();
+
+ void MoveToFullList(Chunk* chunk, int bucket_);
+ void MoveToFreeList(Chunk* chunk, int bucket_);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HeapImpl);
+
+ LinkedList<Chunk*> free_chunks_[kNumBuckets];
+ LinkedList<Chunk*> full_chunks_[kNumBuckets];
+
+ void MoveToList(Chunk* chunk, LinkedList<Chunk*>* head);
+ void* MapAlloc(size_t size);
+ void MapFree(void* ptr);
+ void* AllocLocked(size_t size);
+ void FreeLocked(void* ptr);
+
+ struct MapAllocation {
+ void *ptr;
+ size_t size;
+ MapAllocation* next;
+ };
+ MapAllocation* map_allocation_list_;
+ std::mutex m_;
+};
+
+// Integer log 2, rounds down
+static inline unsigned int log2(size_t n) {
+ return 8 * sizeof(unsigned long long) - __builtin_clzll(n) - 1;
+}
+
+static inline unsigned int size_to_bucket(size_t size) {
+ if (size < kMinBucketAllocationSize)
+ return kMinBucketAllocationSize;
+ return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize);
+}
+
+static inline size_t bucket_to_size(unsigned int bucket) {
+ return kMinBucketAllocationSize << bucket;
+}
+
+static void* MapAligned(size_t size, size_t align) {
+ const int prot = PROT_READ | PROT_WRITE;
+ const int flags = MAP_ANONYMOUS | MAP_PRIVATE;
+
+ size = (size + kPageSize - 1) & ~(kPageSize - 1);
+
+ // Over-allocate enough to align
+ size_t map_size = size + align - kPageSize;
+ if (map_size < size) {
+ return nullptr;
+ }
+
+ void* ptr = mmap(NULL, map_size, prot, flags, -1, 0);
+ if (ptr == MAP_FAILED) {
+ return nullptr;
+ }
+
+ size_t aligned_size = map_size;
+ void* aligned_ptr = ptr;
+
+ std::align(align, size, aligned_ptr, aligned_size);
+
+ // Trim beginning
+ if (aligned_ptr != ptr) {
+ ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr)
+ - reinterpret_cast<uintptr_t>(ptr);
+ munmap(ptr, extra);
+ map_size -= extra;
+ ptr = aligned_ptr;
+ }
+
+ // Trim end
+ if (map_size != size) {
+ assert(map_size > size);
+ assert(ptr != NULL);
+ munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size),
+ map_size - size);
+ }
+
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
+ reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc");
+
+ return ptr;
+}
+
+class Chunk {
+ public:
+ static void* operator new(std::size_t count) noexcept;
+ static void operator delete(void* ptr);
+ Chunk(HeapImpl* heap, int bucket);
+ ~Chunk() {}
+
+ void *Alloc();
+ void Free(void* ptr);
+ void Purge();
+ bool Empty();
+
+ static Chunk* ptr_to_chunk(void* ptr) {
+ return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr)
+ & ~(kChunkSize - 1));
+ }
+ static bool is_chunk(void* ptr) {
+ return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0;
+ }
+
+ unsigned int free_count() {
+ return free_count_;
+ }
+ HeapImpl* heap() {
+ return heap_;
+ }
+ LinkedList<Chunk*> node_; // linked list sorted by minimum free count
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Chunk);
+ HeapImpl* heap_;
+ unsigned int bucket_;
+ unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes
+ unsigned int max_allocations_; // maximum number of allocations in the chunk
+ unsigned int first_free_bitmap_; // index into bitmap for first non-full entry
+ unsigned int free_count_; // number of available allocations
+ unsigned int frees_since_purge_; // number of calls to Free since last Purge
+
+ // bitmap of pages that have been dirtied
+ uint32_t dirty_pages_[div_round_up(kUsablePagesPerChunk, 32)];
+
+ // bitmap of free allocations.
+ uint32_t free_bitmap_[kUsableChunkSize / kMinBucketAllocationSize / 32];
+
+ char data_[0];
+
+ unsigned int ptr_to_n(void* ptr) {
+ ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr)
+ - reinterpret_cast<uintptr_t>(data_);
+ return offset / allocation_size_;
+ }
+ void* n_to_ptr(unsigned int n) {
+ return data_ + n * allocation_size_;
+ }
+};
+static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page");
+
+// Override new operator on chunk to use mmap to allocate kChunkSize
+void* Chunk::operator new(std::size_t count __attribute__((unused))) noexcept {
+ assert(count == sizeof(Chunk));
+ void* mem = MapAligned(kChunkSize, kChunkSize);
+ if (!mem) {
+ abort(); //throw std::bad_alloc;
+ }
+
+ return mem;
+}
+
+// Override new operator on chunk to use mmap to allocate kChunkSize
+void Chunk::operator delete(void *ptr) {
+ assert(reinterpret_cast<Chunk*>(ptr) == ptr_to_chunk(ptr));
+ munmap(ptr, kChunkSize);
+}
+
+Chunk::Chunk(HeapImpl* heap, int bucket) :
+ node_(this), heap_(heap), bucket_(bucket), allocation_size_(
+ bucket_to_size(bucket)), max_allocations_(
+ kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_(
+ max_allocations_), frees_since_purge_(0) {
+ memset(dirty_pages_, 0, sizeof(dirty_pages_));
+ memset(free_bitmap_, 0xff, sizeof(free_bitmap_));
+}
+
+bool Chunk::Empty() {
+ return free_count_ == max_allocations_;
+}
+
+void* Chunk::Alloc() {
+ assert(free_count_ > 0);
+
+ unsigned int i = first_free_bitmap_;
+ while (free_bitmap_[i] == 0)
+ i++;
+ assert(i < ARRAY_SIZE(free_bitmap_));
+ unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1;
+ assert(free_bitmap_[i] & (1U << bit));
+ free_bitmap_[i] &= ~(1U << bit);
+ unsigned int n = i * 32 + bit;
+ assert(n < max_allocations_);
+
+ unsigned int page = n * allocation_size_ / kPageSize;
+ assert(page / 32 < ARRAY_SIZE(dirty_pages_));
+ dirty_pages_[page / 32] |= 1U << (page % 32);
+
+ free_count_--;
+ if (free_count_ == 0) {
+ heap_->MoveToFullList(this, bucket_);
+ }
+
+ return n_to_ptr(n);
+}
+
+void Chunk::Free(void* ptr) {
+ assert(is_chunk(ptr));
+ assert(ptr_to_chunk(ptr) == this);
+
+ unsigned int n = ptr_to_n(ptr);
+ unsigned int i = n / 32;
+ unsigned int bit = n % 32;
+
+ assert(i < ARRAY_SIZE(free_bitmap_));
+ assert(!(free_bitmap_[i] & (1U << bit)));
+ free_bitmap_[i] |= 1U << bit;
+ free_count_++;
+
+ if (i < first_free_bitmap_) {
+ first_free_bitmap_ = i;
+ }
+
+ if (free_count_ == 1) {
+ heap_->MoveToFreeList(this, bucket_);
+ } else {
+ // TODO(ccross): move down free list if necessary
+ }
+
+ if (frees_since_purge_++ * allocation_size_ > 16 * kPageSize) {
+ Purge();
+ }
+}
+
+void Chunk::Purge() {
+ frees_since_purge_ = 0;
+
+ //unsigned int allocsPerPage = kPageSize / allocation_size_;
+}
+
+// Override new operator on HeapImpl to use mmap to allocate a page
+void* HeapImpl::operator new(std::size_t count __attribute__((unused)))
+ noexcept {
+ assert(count == sizeof(HeapImpl));
+ void* mem = MapAligned(kPageSize, kPageSize);
+ if (!mem) {
+ abort(); //throw std::bad_alloc;
+ }
+
+ heap_count++;
+ return mem;
+}
+
+void HeapImpl::operator delete(void *ptr) {
+ munmap(ptr, kPageSize);
+}
+
+HeapImpl::HeapImpl() :
+ free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {
+}
+
+bool HeapImpl::Empty() {
+ for (unsigned int i = 0; i < kNumBuckets; i++) {
+ for (LinkedList<Chunk*> *it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+ if (!it->data()->Empty()) {
+ return false;
+ }
+ }
+ for (LinkedList<Chunk*> *it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+ if (!it->data()->Empty()) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+HeapImpl::~HeapImpl() {
+ for (unsigned int i = 0; i < kNumBuckets; i++) {
+ while (!free_chunks_[i].empty()) {
+ Chunk *chunk = free_chunks_[i].next()->data();
+ chunk->node_.remove();
+ delete chunk;
+ }
+ while (!full_chunks_[i].empty()) {
+ Chunk *chunk = full_chunks_[i].next()->data();
+ chunk->node_.remove();
+ delete chunk;
+ }
+ }
+}
+
+void* HeapImpl::Alloc(size_t size) {
+ std::lock_guard<std::mutex> lk(m_);
+ return AllocLocked(size);
+}
+
+void* HeapImpl::AllocLocked(size_t size) {
+ if (size > kMaxBucketAllocationSize) {
+ return MapAlloc(size);
+ }
+ int bucket = size_to_bucket(size);
+ if (free_chunks_[bucket].empty()) {
+ Chunk *chunk = new Chunk(this, bucket);
+ free_chunks_[bucket].insert(chunk->node_);
+ }
+ return free_chunks_[bucket].next()->data()->Alloc();
+}
+
+void HeapImpl::Free(void *ptr) {
+ std::lock_guard<std::mutex> lk(m_);
+ FreeLocked(ptr);
+}
+
+void HeapImpl::FreeLocked(void *ptr) {
+ if (!Chunk::is_chunk(ptr)) {
+ HeapImpl::MapFree(ptr);
+ } else {
+ Chunk* chunk = Chunk::ptr_to_chunk(ptr);
+ assert(chunk->heap() == this);
+ chunk->Free(ptr);
+ }
+}
+
+void* HeapImpl::MapAlloc(size_t size) {
+ size = (size + kPageSize - 1) & ~(kPageSize - 1);
+
+ MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(
+ sizeof(MapAllocation)));
+ void* ptr = MapAligned(size, kChunkSize);
+ if (!ptr) {
+ FreeLocked(allocation);
+ abort(); //throw std::bad_alloc;
+ }
+ allocation->ptr = ptr;
+ allocation->size = size;
+ allocation->next = map_allocation_list_;
+ map_allocation_list_ = allocation;
+
+ return ptr;
+}
+
+void HeapImpl::MapFree(void *ptr) {
+ MapAllocation **allocation = &map_allocation_list_;
+ while (*allocation && (*allocation)->ptr != ptr)
+ allocation = &(*allocation)->next;
+
+ assert(*allocation != nullptr);
+
+ munmap((*allocation)->ptr, (*allocation)->size);
+ FreeLocked(*allocation);
+
+ *allocation = (*allocation)->next;
+}
+
+void HeapImpl::MoveToFreeList(Chunk *chunk, int bucket) {
+ MoveToList(chunk, &free_chunks_[bucket]);
+}
+
+void HeapImpl::MoveToFullList(Chunk *chunk, int bucket) {
+ MoveToList(chunk, &full_chunks_[bucket]);
+}
+
+void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) {
+ // Remove from old list
+ chunk->node_.remove();
+
+ LinkedList<Chunk*> *node = head;
+ // Insert into new list, sorted by lowest free count
+ while (node->next() != head && node->data() != nullptr
+ && node->data()->free_count() < chunk->free_count())
+ node = node->next();
+
+ node->insert(chunk->node_);
+}
+
+Heap::Heap() {
+ // HeapImpl overloads the operator new in order to mmap itself instead of
+ // allocating with new.
+ // Can't use a shared_ptr to store the result because shared_ptr needs to
+ // allocate, and Allocator<T> is still being constructed.
+ impl_ = new HeapImpl();
+ owns_impl_ = true;
+}
+
+Heap::~Heap() {
+ if (owns_impl_) {
+ delete impl_;
+ }
+}
+
+void* Heap::allocate(size_t size) {
+ return impl_->Alloc(size);
+}
+
+void Heap::deallocate(void* ptr) {
+ impl_->Free(ptr);
+}
+
+void Heap::deallocate(HeapImpl*impl, void* ptr) {
+ impl->Free(ptr);
+}
+
+bool Heap::empty() {
+ return impl_->Empty();
+}
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
new file mode 100644
index 0000000..a8f579e
--- /dev/null
+++ b/libmemunreachable/Allocator.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_ALLOCATOR_H_
+#define LIBMEMUNREACHABLE_ALLOCATOR_H_
+
+#include <atomic>
+#include <cstddef>
+#include <functional>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+extern std::atomic<int> heap_count;
+
+class HeapImpl;
+
+template<typename T>
+class Allocator;
+
+
+// Non-templated class that implements wraps HeapImpl to keep
+// implementation out of the header file
+class Heap {
+public:
+ Heap();
+ ~Heap();
+
+ // Copy constructor that does not take ownership of impl_
+ Heap(const Heap& other) : impl_(other.impl_), owns_impl_(false) {}
+
+ // Assignment disabled
+ Heap& operator=(const Heap&) = delete;
+
+ // Allocate size bytes
+ void* allocate(size_t size);
+
+ // Deallocate allocation returned by allocate
+ void deallocate(void*);
+
+ bool empty();
+
+ static void deallocate(HeapImpl* impl, void* ptr);
+
+ // Allocate a class of type T
+ template<class T>
+ T* allocate() {
+ return reinterpret_cast<T*>(allocate(sizeof(T)));
+ }
+
+ // Comparators, copied objects will be equal
+ bool operator ==(const Heap& other) const {
+ return impl_ == other.impl_;
+ }
+ bool operator !=(const Heap& other) const {
+ return !(*this == other);
+ }
+
+ // std::unique_ptr wrapper that allocates using allocate and deletes using
+ // deallocate
+ template<class T>
+ using unique_ptr = std::unique_ptr<T, std::function<void(void*)>>;
+
+ template<class T, class... Args>
+ unique_ptr<T> make_unique(Args&&... args) {
+ HeapImpl* impl = impl_;
+ return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...),
+ [impl](void* ptr) {
+ reinterpret_cast<T*>(ptr)->~T();
+ deallocate(impl, ptr);
+ });
+ }
+
+ // std::unique_ptr wrapper that allocates using allocate and deletes using
+ // deallocate
+ template<class T>
+ using shared_ptr = std::shared_ptr<T>;
+
+ template<class T, class... Args>
+ shared_ptr<T> make_shared(Args&&... args);
+
+protected:
+ HeapImpl* impl_;
+ bool owns_impl_;
+};
+
+// STLAllocator implements the std allocator interface on top of a Heap
+template<typename T>
+class STLAllocator {
+public:
+ using value_type = T;
+ ~STLAllocator() {
+ }
+
+ // Construct an STLAllocator on top of a Heap
+ STLAllocator(const Heap& heap) :
+ heap_(heap) {
+ }
+
+ // Rebind an STLAllocator from an another STLAllocator
+ template<typename U>
+ STLAllocator(const STLAllocator<U>& other) :
+ heap_(other.heap_) {
+ }
+
+ STLAllocator(const STLAllocator&) = default;
+ STLAllocator<T>& operator=(const STLAllocator<T>&) = default;
+
+ T* allocate(std::size_t n) {
+ return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T)));
+ }
+
+ void deallocate(T* ptr, std::size_t) {
+ heap_.deallocate(ptr);
+ }
+
+ template<typename U>
+ bool operator ==(const STLAllocator<U>& other) const {
+ return heap_ == other.heap_;
+ }
+ template<typename U>
+ inline bool operator !=(const STLAllocator<U>& other) const {
+ return !(this == other);
+ }
+
+ template<typename U>
+ friend class STLAllocator;
+
+protected:
+ Heap heap_;
+};
+
+
+// Allocator extends STLAllocator with some convenience methods for allocating
+// a single object and for constructing unique_ptr and shared_ptr objects with
+// appropriate deleters.
+template<class T>
+class Allocator : public STLAllocator<T> {
+ public:
+ ~Allocator() {}
+
+ Allocator(const Heap& other) :
+ STLAllocator<T>(other) {
+ }
+
+ template<typename U>
+ Allocator(const STLAllocator<U>& other) :
+ STLAllocator<T>(other) {
+ }
+
+ Allocator(const Allocator&) = default;
+ Allocator<T>& operator=(const Allocator<T>&) = default;
+
+ using STLAllocator<T>::allocate;
+ using STLAllocator<T>::deallocate;
+ using STLAllocator<T>::heap_;
+
+ T* allocate() {
+ return STLAllocator<T>::allocate(1);
+ }
+ void deallocate(void* ptr) {
+ heap_.deallocate(ptr);
+ }
+
+ using shared_ptr = Heap::shared_ptr<T>;
+
+ template<class... Args>
+ shared_ptr make_shared(Args&& ...args) {
+ return heap_.template make_shared<T>(std::forward<Args>(args)...);
+ }
+
+ using unique_ptr = Heap::unique_ptr<T>;
+
+ template<class... Args>
+ unique_ptr make_unique(Args&& ...args) {
+ return heap_.template make_unique<T>(std::forward<Args>(args)...);
+ }
+};
+
+// std::unique_ptr wrapper that allocates using allocate and deletes using
+// deallocate. Implemented outside class definition in order to pass
+// Allocator<T> to shared_ptr.
+template<class T, class... Args>
+inline Heap::shared_ptr<T> Heap::make_shared(Args&&... args) {
+ return std::allocate_shared<T, Allocator<T>, Args...>(Allocator<T>(*this),
+ std::forward<Args>(args)...);
+}
+
+namespace allocator {
+
+template<class T>
+using vector = std::vector<T, Allocator<T>>;
+
+template<class T>
+using list = std::list<T, Allocator<T>>;
+
+template<class Key, class T, class Compare = std::less<Key>>
+using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>;
+
+template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
+
+template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>;
+
+template<class Key, class Compare = std::less<Key>>
+using set = std::set<Key, Compare, Allocator<Key>>;
+
+using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
+}
+
+#endif
diff --git a/libmemunreachable/Android.mk b/libmemunreachable/Android.mk
new file mode 100644
index 0000000..7b66d44
--- /dev/null
+++ b/libmemunreachable/Android.mk
@@ -0,0 +1,65 @@
+LOCAL_PATH := $(call my-dir)
+
+memunreachable_srcs := \
+ Allocator.cpp \
+ HeapWalker.cpp \
+ LeakFolding.cpp \
+ LeakPipe.cpp \
+ LineBuffer.cpp \
+ MemUnreachable.cpp \
+ ProcessMappings.cpp \
+ PtracerThread.cpp \
+ ThreadCapture.cpp \
+
+memunreachable_test_srcs := \
+ tests/Allocator_test.cpp \
+ tests/DisableMalloc_test.cpp \
+ tests/HeapWalker_test.cpp \
+ tests/LeakFolding_test.cpp \
+ tests/MemUnreachable_test.cpp \
+ tests/ThreadCapture_test.cpp \
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libmemunreachable
+LOCAL_SRC_FILES := $(memunreachable_srcs)
+LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
+LOCAL_SHARED_LIBRARIES := libbase liblog
+LOCAL_STATIC_LIBRARIES := libc_malloc_debug_backtrace libc_logging
+# Only need this for arm since libc++ uses its own unwind code that
+# doesn't mix with the other default unwind code.
+LOCAL_STATIC_LIBRARIES_arm := libunwind_llvm
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CLANG := true
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := memunreachable_test
+LOCAL_SRC_FILES := $(memunreachable_test_srcs)
+LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
+LOCAL_CLANG := true
+LOCAL_SHARED_LIBRARIES := libmemunreachable libbase liblog
+
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := memunreachable_test
+LOCAL_SRC_FILES := \
+ Allocator.cpp \
+ HeapWalker.cpp \
+ LeakFolding.cpp \
+ tests/Allocator_test.cpp \
+ tests/HeapWalker_test.cpp \
+ tests/HostMallocStub.cpp \
+ tests/LeakFolding_test.cpp \
+
+LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
+LOCAL_CLANG := true
+LOCAL_SHARED_LIBRARIES := libbase liblog
+LOCAL_MODULE_HOST_OS := linux
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
new file mode 100644
index 0000000..19393ec
--- /dev/null
+++ b/libmemunreachable/HeapWalker.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#include <map>
+#include <utility>
+
+#include "Allocator.h"
+#include "HeapWalker.h"
+#include "LeakFolding.h"
+#include "log.h"
+
+bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
+ if (end == begin) {
+ end = begin + 1;
+ }
+ Range range{begin, end};
+ auto inserted = allocations_.insert(std::pair<Range, AllocationInfo>(range, AllocationInfo{}));
+ if (inserted.second) {
+ valid_allocations_range_.begin = std::min(valid_allocations_range_.begin, begin);
+ valid_allocations_range_.end = std::max(valid_allocations_range_.end, end);
+ allocation_bytes_ += range.size();
+ return true;
+ } else {
+ Range overlap = inserted.first->first;
+ ALOGE("range %p-%p overlaps with existing range %p-%p",
+ reinterpret_cast<void*>(begin),
+ reinterpret_cast<void*>(end),
+ reinterpret_cast<void*>(overlap.begin),
+ reinterpret_cast<void*>(overlap.end));
+ return false;
+ }
+}
+
+bool HeapWalker::IsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info) {
+ if (ptr >= valid_allocations_range_.begin && ptr < valid_allocations_range_.end) {
+ AllocationMap::iterator it = allocations_.find(Range{ptr, ptr + 1});
+ if (it != allocations_.end()) {
+ *range = it->first;
+ *info = &it->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+void HeapWalker::RecurseRoot(const Range& root) {
+ allocator::vector<Range> to_do(1, root, allocator_);
+ while (!to_do.empty()) {
+ Range range = to_do.back();
+ to_do.pop_back();
+
+ ForEachPtrInRange(range, [&](Range& ref_range, AllocationInfo* ref_info) {
+ if (!ref_info->referenced_from_root) {
+ ref_info->referenced_from_root = true;
+ to_do.push_back(ref_range);
+ }
+ });
+ }
+}
+
+void HeapWalker::Root(uintptr_t begin, uintptr_t end) {
+ roots_.push_back(Range{begin, end});
+}
+
+void HeapWalker::Root(const allocator::vector<uintptr_t>& vals) {
+ root_vals_.insert(root_vals_.end(), vals.begin(), vals.end());
+}
+
+size_t HeapWalker::Allocations() {
+ return allocations_.size();
+}
+
+size_t HeapWalker::AllocationBytes() {
+ return allocation_bytes_;
+}
+
+bool HeapWalker::DetectLeaks() {
+ // Recursively walk pointers from roots to mark referenced allocations
+ for (auto it = roots_.begin(); it != roots_.end(); it++) {
+ RecurseRoot(*it);
+ }
+
+ Range vals;
+ vals.begin = reinterpret_cast<uintptr_t>(root_vals_.data());
+ vals.end = vals.begin + root_vals_.size() * sizeof(uintptr_t);
+
+ RecurseRoot(vals);
+
+ return true;
+}
+
+bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit,
+ size_t* num_leaks_out, size_t* leak_bytes_out) {
+ leaked.clear();
+
+ size_t num_leaks = 0;
+ size_t leak_bytes = 0;
+ for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
+ if (!it->second.referenced_from_root) {
+ num_leaks++;
+ leak_bytes += it->first.end - it->first.begin;
+ }
+ }
+
+ size_t n = 0;
+ for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
+ if (!it->second.referenced_from_root) {
+ if (n++ < limit) {
+ leaked.push_back(it->first);
+ }
+ }
+ }
+
+ if (num_leaks_out) {
+ *num_leaks_out = num_leaks;
+ }
+ if (leak_bytes_out) {
+ *leak_bytes_out = leak_bytes;
+ }
+
+ return true;
+}
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
new file mode 100644
index 0000000..7b851c4
--- /dev/null
+++ b/libmemunreachable/HeapWalker.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_HEAP_WALKER_H_
+#define LIBMEMUNREACHABLE_HEAP_WALKER_H_
+
+#include "android-base/macros.h"
+
+#include "Allocator.h"
+#include "Tarjan.h"
+
+// A range [begin, end)
+struct Range {
+ uintptr_t begin;
+ uintptr_t end;
+
+ size_t size() const { return end - begin; };
+};
+
+// Comparator for Ranges that returns equivalence for overlapping ranges
+struct compare_range {
+ bool operator()(const Range& a, const Range& b) const {
+ return a.end <= b.begin;
+ }
+};
+
+class HeapWalker {
+ public:
+ HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
+ allocations_(allocator), allocation_bytes_(0),
+ roots_(allocator), root_vals_(allocator) {
+ valid_allocations_range_.end = 0;
+ valid_allocations_range_.begin = ~valid_allocations_range_.end;
+ }
+ ~HeapWalker() {}
+ bool Allocation(uintptr_t begin, uintptr_t end);
+ void Root(uintptr_t begin, uintptr_t end);
+ void Root(const allocator::vector<uintptr_t>& vals);
+
+ bool DetectLeaks();
+
+ bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks,
+ size_t* leak_bytes);
+ size_t Allocations();
+ size_t AllocationBytes();
+
+ template<class F>
+ void ForEachPtrInRange(const Range& range, F&& f);
+
+ template<class F>
+ void ForEachAllocation(F&& f);
+
+ struct AllocationInfo {
+ bool referenced_from_root;
+ };
+
+ private:
+
+ void RecurseRoot(const Range& root);
+ bool IsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
+
+ DISALLOW_COPY_AND_ASSIGN(HeapWalker);
+ Allocator<HeapWalker> allocator_;
+ using AllocationMap = allocator::map<Range, AllocationInfo, compare_range>;
+ AllocationMap allocations_;
+ size_t allocation_bytes_;
+ Range valid_allocations_range_;
+
+ allocator::vector<Range> roots_;
+ allocator::vector<uintptr_t> root_vals_;
+};
+
+template<class F>
+inline void HeapWalker::ForEachPtrInRange(const Range& range, F&& f) {
+ uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1);
+ // TODO(ccross): we might need to consider a pointer to the end of a buffer
+ // to be inside the buffer, which means the common case of a pointer to the
+ // beginning of a buffer may keep two ranges live.
+ for (uintptr_t i = begin; i < range.end; i += sizeof(uintptr_t)) {
+ Range ref_range;
+ AllocationInfo* ref_info;
+ if (IsAllocationPtr(*reinterpret_cast<uintptr_t*>(i), &ref_range, &ref_info)) {
+ f(ref_range, ref_info);
+ }
+ }
+}
+
+template<class F>
+inline void HeapWalker::ForEachAllocation(F&& f) {
+ for (auto& it : allocations_) {
+ const Range& range = it.first;
+ HeapWalker::AllocationInfo& allocation = it.second;
+ f(range, allocation);
+ }
+}
+
+#endif
diff --git a/libmemunreachable/Leak.h b/libmemunreachable/Leak.h
new file mode 100644
index 0000000..eaeeea7
--- /dev/null
+++ b/libmemunreachable/Leak.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LEAK_H_
+#define LIBMEMUNREACHABLE_LEAK_H_
+
+#include <functional>
+#include <vector>
+
+#include "memunreachable/memunreachable.h"
+
+// Custom std::hash specialization so that Leak::Backtrace can be used
+// as a key in std::unordered_map.
+namespace std {
+
+template<>
+struct hash<Leak::Backtrace> {
+ std::size_t operator()(const Leak::Backtrace& key) const {
+ std::size_t seed = 0;
+
+ hash_combine(seed, key.num_frames);
+ for (size_t i = 0; i < key.num_frames; i++) {
+ hash_combine(seed, key.frames[i]);
+ }
+
+ return seed;
+ }
+
+ private:
+ template<typename T>
+ inline void hash_combine(std::size_t& seed, const T& v) const {
+ std::hash<T> hasher;
+ seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ }
+};
+
+} // namespace std
+
+static bool operator==(const Leak::Backtrace& lhs, const Leak::Backtrace& rhs) {
+ return (lhs.num_frames == rhs.num_frames) &&
+ memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+}
+
+#endif
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
new file mode 100644
index 0000000..be4d20c
--- /dev/null
+++ b/libmemunreachable/LeakFolding.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#include "Allocator.h"
+#include "HeapWalker.h"
+#include "LeakFolding.h"
+#include "Tarjan.h"
+#include "log.h"
+
+// Converts possibly cyclic graph of leaks to a DAG by combining
+// strongly-connected components into a object, stored in the scc pointer
+// of each node in the component.
+void LeakFolding::ComputeDAG() {
+ SCCList<LeakInfo> scc_list{allocator_};
+ Tarjan(leak_graph_, scc_list);
+
+ Allocator<SCCInfo> scc_allocator = allocator_;
+
+ for (auto& scc_nodes: scc_list) {
+ Allocator<SCCInfo>::unique_ptr leak_scc;
+ leak_scc = scc_allocator.make_unique(scc_allocator);
+
+ for (auto& node: scc_nodes) {
+ node->ptr->scc = leak_scc.get();
+ leak_scc->count++;
+ leak_scc->size += node->ptr->range.size();
+ }
+
+ leak_scc_.emplace_back(std::move(leak_scc));
+ }
+
+ for (auto& it : leak_map_) {
+ LeakInfo& leak = it.second;
+ for (auto& ref: leak.node.references_out) {
+ if (leak.scc != ref->ptr->scc) {
+ leak.scc->node.Edge(&ref->ptr->scc->node);
+ }
+ }
+ }
+}
+
+void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
+ std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_,
+ [&](SCCInfo* scc) {
+ if (scc->accumulator != dominator) {
+ scc->accumulator = dominator;
+ dominator->cuumulative_size += scc->size;
+ dominator->cuumulative_count += scc->count;
+ scc->node.Foreach([&](SCCInfo* ref) {
+ walk(ref);
+ });
+ }
+ });
+ walk(dominator);
+}
+
+bool LeakFolding::FoldLeaks() {
+ Allocator<LeakInfo> leak_allocator = allocator_;
+
+ // Find all leaked allocations insert them into leak_map_ and leak_graph_
+ heap_walker_.ForEachAllocation(
+ [&](const Range& range, HeapWalker::AllocationInfo& allocation) {
+ if (!allocation.referenced_from_root) {
+ auto it = leak_map_.emplace(std::piecewise_construct,
+ std::forward_as_tuple(range),
+ std::forward_as_tuple(range, allocator_));
+ LeakInfo& leak = it.first->second;
+ leak_graph_.push_back(&leak.node);
+ }
+ });
+
+ // Find references between leaked allocations and connect them in leak_graph_
+ for (auto& it : leak_map_) {
+ LeakInfo& leak = it.second;
+ heap_walker_.ForEachPtrInRange(leak.range,
+ [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
+ if (!ptr_info->referenced_from_root) {
+ LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
+ leak.node.Edge(&ptr_leak->node);
+ }
+ });
+ }
+
+ // Convert the cyclic graph to a DAG by grouping strongly connected components
+ ComputeDAG();
+
+ // Compute dominators and cuumulative sizes
+ for (auto& scc : leak_scc_) {
+ if (scc->node.references_in.size() == 0) {
+ scc->dominator = true;
+ AccumulateLeaks(scc.get());
+ }
+ }
+
+ return true;
+}
+
+bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked,
+ size_t* num_leaks_out, size_t* leak_bytes_out) {
+ size_t num_leaks = 0;
+ size_t leak_bytes = 0;
+ for (auto& it : leak_map_) {
+ const LeakInfo& leak = it.second;
+ num_leaks++;
+ leak_bytes += leak.range.size();
+ }
+
+ for (auto& it : leak_map_) {
+ const LeakInfo& leak = it.second;
+ if (leak.scc->dominator) {
+ leaked.emplace_back(Leak{leak.range,
+ leak.scc->cuumulative_count - 1,
+ leak.scc->cuumulative_size - leak.range.size()});
+ }
+ }
+
+ if (num_leaks_out) {
+ *num_leaks_out = num_leaks;
+ }
+ if (leak_bytes_out) {
+ *leak_bytes_out = leak_bytes;
+ }
+
+ return true;
+}
diff --git a/libmemunreachable/LeakFolding.h b/libmemunreachable/LeakFolding.h
new file mode 100644
index 0000000..732d3f2
--- /dev/null
+++ b/libmemunreachable/LeakFolding.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+#define LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+
+#include "HeapWalker.h"
+
+class LeakFolding {
+ public:
+ LeakFolding(Allocator<void> allocator, HeapWalker& heap_walker)
+ : allocator_(allocator), heap_walker_(heap_walker),
+ leak_map_(allocator), leak_graph_(allocator), leak_scc_(allocator) {}
+
+ bool FoldLeaks();
+
+ struct Leak {
+ const Range range;
+ size_t referenced_count;
+ size_t referenced_size;
+ };
+
+ bool Leaked(allocator::vector<Leak>& leaked,
+ size_t* num_leaks_out, size_t* leak_bytes_out);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LeakFolding);
+ Allocator<void> allocator_;
+ HeapWalker& heap_walker_;
+
+ struct SCCInfo {
+ public:
+ Node<SCCInfo> node;
+
+ size_t count;
+ size_t size;
+
+ size_t cuumulative_count;
+ size_t cuumulative_size;
+
+ bool dominator;
+ SCCInfo* accumulator;
+
+ SCCInfo(Allocator<SCCInfo> allocator) : node(this, allocator),
+ count(0), size(0), cuumulative_count(0), cuumulative_size(0),
+ dominator(false), accumulator(nullptr) {}
+ private:
+ SCCInfo(SCCInfo&&) = delete;
+ DISALLOW_COPY_AND_ASSIGN(SCCInfo);
+ };
+
+ struct LeakInfo {
+ public:
+ Node<LeakInfo> node;
+
+ const Range range;
+
+ SCCInfo* scc;
+
+ LeakInfo(const Range& range, Allocator<LeakInfo> allocator)
+ : node(this, allocator), range(range),
+ scc(nullptr) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LeakInfo);
+ };
+
+ void ComputeDAG();
+ void AccumulateLeaks(SCCInfo* dominator);
+
+ allocator::map<Range, LeakInfo, compare_range> leak_map_;
+ Graph<LeakInfo> leak_graph_;
+ allocator::vector<Allocator<SCCInfo>::unique_ptr> leak_scc_;
+};
+
+#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
new file mode 100644
index 0000000..080f8a7
--- /dev/null
+++ b/libmemunreachable/LeakPipe.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "LeakPipe.h"
+
+#include "log.h"
+
+bool LeakPipe::SendFd(int sock, int fd) {
+ struct msghdr hdr{};
+ struct iovec iov{};
+ unsigned int data = 0xfdfdfdfd;
+ alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ iov.iov_base = &data;
+ iov.iov_len = sizeof(data);
+
+ hdr.msg_control = cmsgbuf;
+ hdr.msg_controllen = CMSG_LEN(sizeof(int));
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ *(int*)CMSG_DATA(cmsg) = fd;
+
+ int ret = sendmsg(sock, &hdr, 0);
+ if (ret < 0) {
+ ALOGE("failed to send fd: %s", strerror(errno));
+ return false;
+ }
+ if (ret == 0) {
+ ALOGE("eof when sending fd");
+ return false;
+ }
+
+ return true;
+}
+
+int LeakPipe::ReceiveFd(int sock) {
+ struct msghdr hdr{};
+ struct iovec iov{};
+ unsigned int data;
+ alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ iov.iov_base = &data;
+ iov.iov_len = sizeof(data);
+
+ hdr.msg_control = cmsgbuf;
+ hdr.msg_controllen = CMSG_LEN(sizeof(int));
+
+ int ret = recvmsg(sock, &hdr, 0);
+ if (ret < 0) {
+ ALOGE("failed to receive fd: %s", strerror(errno));
+ return -1;
+ }
+ if (ret == 0) {
+ ALOGE("eof when receiving fd");
+ return -1;
+ }
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+ if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ ALOGE("missing fd while receiving fd");
+ return -1;
+ }
+
+ return *(int*)CMSG_DATA(cmsg);
+}
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
new file mode 100644
index 0000000..3f4e0b7
--- /dev/null
+++ b/libmemunreachable/LeakPipe.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LEAK_PIPE_H_
+#define LIBMEMUNREACHABLE_LEAK_PIPE_H_
+
+#include <sys/socket.h>
+
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "ScopedPipe.h"
+#include "log.h"
+
+// LeakPipe implements a pipe that can transfer vectors of simple objects
+// between processes. The pipe is created in the sending process and
+// transferred over a socketpair that was created before forking. This ensures
+// that only the sending process can have the send side of the pipe open, so if
+// the sending process dies the pipe will close.
+class LeakPipe {
+ public:
+ LeakPipe() {
+ int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
+ if (ret < 0) {
+ LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
+ }
+ }
+
+ ~LeakPipe() {
+ Close();
+ }
+
+ void Close() {
+ close(sv_[0]);
+ close(sv_[1]);
+ sv_[0] = -1;
+ sv_[1] = -1;
+ }
+
+ bool OpenReceiver() {
+ int fd = ReceiveFd(sv_[0]);
+ if (fd < 0) {
+ return false;
+ }
+
+ receiver_.SetFd(fd);
+ return true;
+ }
+
+ bool OpenSender() {
+ ScopedPipe pipe;
+
+ if (!SendFd(sv_[1], pipe.Receiver())) {
+ return false;
+ }
+ pipe.ReleaseReceiver();
+
+ sender_.SetFd(pipe.ReleaseSender());
+ return true;
+ }
+
+ class LeakPipeBase {
+ public:
+ LeakPipeBase() : fd_(-1) {}
+
+ ~LeakPipeBase() {
+ Close();
+ }
+
+ void SetFd(int fd) {
+ fd_ = fd;
+ }
+
+ void Close() {
+ close(fd_);
+ fd_ = -1;
+ }
+
+ protected:
+ int fd_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LeakPipeBase);
+ };
+
+ class LeakPipeSender : public LeakPipeBase {
+ public:
+ using LeakPipeBase::LeakPipeBase;
+
+ template<typename T>
+ bool Send(const T& value) {
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
+ if (ret < 0) {
+ ALOGE("failed to send value: %s", strerror(errno));
+ return false;
+ } else if (static_cast<size_t>(ret) != sizeof(T)) {
+ ALOGE("eof while writing value");
+ return false;
+ }
+
+ return true;
+ }
+
+ template<class T, class Alloc = std::allocator<T>>
+ bool SendVector(const std::vector<T, Alloc>& vector) {
+ size_t size = vector.size() * sizeof(T);
+ if (!Send(size)) {
+ return false;
+ }
+
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, vector.data(), size));
+ if (ret < 0) {
+ ALOGE("failed to send vector: %s", strerror(errno));
+ return false;
+ } else if (static_cast<size_t>(ret) != size) {
+ ALOGE("eof while writing vector");
+ return false;
+ }
+
+ return true;
+ }
+ };
+
+ class LeakPipeReceiver : public LeakPipeBase {
+ public:
+ using LeakPipeBase::LeakPipeBase;
+
+ template<typename T>
+ bool Receive(T* value) {
+ ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
+ if (ret < 0) {
+ ALOGE("failed to receive value: %s", strerror(errno));
+ return false;
+ } else if (static_cast<size_t>(ret) != sizeof(T)) {
+ ALOGE("eof while receiving value");
+ return false;
+ }
+
+ return true;
+ }
+
+ template<class T, class Alloc = std::allocator<T>>
+ bool ReceiveVector(std::vector<T, Alloc>& vector) {
+ size_t size = 0;
+ if (!Receive(&size)) {
+ return false;
+ }
+
+ vector.resize(size / sizeof(T));
+
+ char* ptr = reinterpret_cast<char*>(vector.data());
+ while (size > 0) {
+ ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, ptr, size));
+ if (ret < 0) {
+ ALOGE("failed to send vector: %s", strerror(errno));
+ return false;
+ } else if (ret == 0) {
+ ALOGE("eof while reading vector");
+ return false;
+ }
+ size -= ret;
+ ptr += ret;
+ }
+
+ return true;
+ }
+
+ };
+
+ LeakPipeReceiver& Receiver() {
+ return receiver_;
+ }
+
+ LeakPipeSender& Sender() {
+ return sender_;
+ }
+
+ private:
+ LeakPipeReceiver receiver_;
+ LeakPipeSender sender_;
+ bool SendFd(int sock, int fd);
+ int ReceiveFd(int sock);
+ DISALLOW_COPY_AND_ASSIGN(LeakPipe);
+ int sv_[2];
+};
+
+#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp
new file mode 100644
index 0000000..d3580c0
--- /dev/null
+++ b/libmemunreachable/LineBuffer.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// Copied from system/extras/memory_replay/LineBuffer.cpp
+// TODO(ccross): find a way to share between libmemunreachable and memory_replay?
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "LineBuffer.h"
+
+LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len) : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {
+}
+
+bool LineBuffer::GetLine(char** line, size_t* line_len) {
+ while (true) {
+ if (bytes_ > 0) {
+ char* newline = reinterpret_cast<char*>(memchr(buffer_ + start_, '\n', bytes_));
+ if (newline != nullptr) {
+ *newline = '\0';
+ *line = buffer_ + start_;
+ start_ = newline - buffer_ + 1;
+ bytes_ -= newline - *line + 1;
+ *line_len = newline - *line;
+ return true;
+ }
+ }
+ if (start_ > 0) {
+ // Didn't find anything, copy the current to the front of the buffer.
+ memmove(buffer_, buffer_ + start_, bytes_);
+ start_ = 0;
+ }
+ ssize_t bytes = TEMP_FAILURE_RETRY(read(fd_, buffer_ + bytes_, buffer_len_ - bytes_ - 1));
+ if (bytes <= 0) {
+ if (bytes_ > 0) {
+ // The read data might not contain a nul terminator, so add one.
+ buffer_[bytes_] = '\0';
+ *line = buffer_ + start_;
+ *line_len = bytes_;
+ bytes_ = 0;
+ start_ = 0;
+ return true;
+ }
+ return false;
+ }
+ bytes_ += bytes;
+ }
+}
diff --git a/include/nativeloader/native_loader.h b/libmemunreachable/LineBuffer.h
similarity index 61%
rename from include/nativeloader/native_loader.h
rename to libmemunreachable/LineBuffer.h
index da07253..a015c46 100644
--- a/include/nativeloader/native_loader.h
+++ b/libmemunreachable/LineBuffer.h
@@ -14,19 +14,23 @@
* limitations under the License.
*/
-#ifndef NATIVE_LOADER_H_
-#define NATIVE_LOADER_H_
+#ifndef _LIBMEMUNREACHABLE_LINE_BUFFER_H
+#define _LIBMEMUNREACHABLE_LINE_BUFFER_H
-#include "jni.h"
#include <stdint.h>
-namespace android {
+class LineBuffer {
+ public:
+ LineBuffer(int fd, char* buffer, size_t buffer_len);
-__attribute__((visibility("default")))
-void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
- jobject class_loader, bool is_shared, jstring library_path,
- jstring permitted_path);
+ bool GetLine(char** line, size_t* line_len);
-}; // namespace android
+ private:
+ int fd_;
+ char* buffer_ = nullptr;
+ size_t buffer_len_ = 0;
+ size_t start_ = 0;
+ size_t bytes_ = 0;
+};
-#endif // NATIVE_BRIDGE_H_
+#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/LinkedList.h b/libmemunreachable/LinkedList.h
new file mode 100644
index 0000000..3e44035
--- /dev/null
+++ b/libmemunreachable/LinkedList.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LINKED_LIST_H_
+#define LIBMEMUNREACHABLE_LINKED_LIST_H_
+
+template<class T>
+class LinkedList {
+public:
+ LinkedList() : next_(this), prev_(this), data_() {}
+ LinkedList(T data) : LinkedList() {
+ data_ = data;
+ }
+ ~LinkedList() {}
+ void insert(LinkedList<T>& node) {
+ assert(node.empty());
+ node.next_ = this->next_;
+ node.next_->prev_ = &node;
+ this->next_ = &node;
+ node.prev_ = this;
+ }
+ void remove() {
+ this->next_->prev_ = this->prev_;
+ this->prev_->next_ = this->next_;
+ this->next_ = this;
+ this->prev_ = this;
+ }
+ T data() { return data_; }
+ bool empty() { return next_ == this && prev_ == this; }
+ LinkedList<T> *next() { return next_; }
+private:
+ LinkedList<T> *next_;
+ LinkedList<T> *prev_;
+ T data_;
+};
+
+template<class T>
+class LinkedListHead {
+public:
+ LinkedListHead() : node_() {}
+ ~LinkedListHead() {}
+
+private:
+ LinkedList<T> node_;
+};
+
+#endif
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
new file mode 100644
index 0000000..ac19a66
--- /dev/null
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#include <functional>
+#include <iomanip>
+#include <mutex>
+#include <string>
+#include <sstream>
+#include <unordered_map>
+
+#include <backtrace.h>
+#include <android-base/macros.h>
+
+#include "Allocator.h"
+#include "HeapWalker.h"
+#include "Leak.h"
+#include "LeakFolding.h"
+#include "LeakPipe.h"
+#include "ProcessMappings.h"
+#include "PtracerThread.h"
+#include "ScopedDisableMalloc.h"
+#include "Semaphore.h"
+#include "ThreadCapture.h"
+
+#include "memunreachable/memunreachable.h"
+#include "bionic.h"
+#include "log.h"
+
+const size_t Leak::contents_length;
+
+using namespace std::chrono_literals;
+
+class MemUnreachable {
+ public:
+ MemUnreachable(pid_t pid, Allocator<void> allocator) : pid_(pid), allocator_(allocator),
+ heap_walker_(allocator_) {}
+ bool CollectAllocations(const allocator::vector<ThreadInfo>& threads,
+ const allocator::vector<Mapping>& mappings);
+ bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
+ size_t* num_leaks, size_t* leak_bytes);
+ size_t Allocations() { return heap_walker_.Allocations(); }
+ size_t AllocationBytes() { return heap_walker_.AllocationBytes(); }
+ private:
+ bool ClassifyMappings(const allocator::vector<Mapping>& mappings,
+ allocator::vector<Mapping>& heap_mappings,
+ allocator::vector<Mapping>& anon_mappings,
+ allocator::vector<Mapping>& globals_mappings,
+ allocator::vector<Mapping>& stack_mappings);
+ DISALLOW_COPY_AND_ASSIGN(MemUnreachable);
+ pid_t pid_;
+ Allocator<void> allocator_;
+ HeapWalker heap_walker_;
+};
+
+static void HeapIterate(const Mapping& heap_mapping,
+ const std::function<void(uintptr_t, size_t)>& func) {
+ malloc_iterate(heap_mapping.begin, heap_mapping.end - heap_mapping.begin,
+ [](uintptr_t base, size_t size, void* arg) {
+ auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
+ (*f)(base, size);
+ }, const_cast<void*>(reinterpret_cast<const void*>(&func)));
+}
+
+bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
+ const allocator::vector<Mapping>& mappings) {
+ ALOGI("searching process %d for allocations", pid_);
+ allocator::vector<Mapping> heap_mappings{mappings};
+ allocator::vector<Mapping> anon_mappings{mappings};
+ allocator::vector<Mapping> globals_mappings{mappings};
+ allocator::vector<Mapping> stack_mappings{mappings};
+ if (!ClassifyMappings(mappings, heap_mappings, anon_mappings,
+ globals_mappings, stack_mappings)) {
+ return false;
+ }
+
+ for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) {
+ ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ HeapIterate(*it, [&](uintptr_t base, size_t size) {
+ heap_walker_.Allocation(base, base + size);
+ });
+ }
+
+ for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) {
+ ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ heap_walker_.Allocation(it->begin, it->end);
+ }
+
+ for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) {
+ ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ heap_walker_.Root(it->begin, it->end);
+ }
+
+ for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) {
+ for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) {
+ if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) {
+ ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
+ heap_walker_.Root(thread_it->stack.first, it->end);
+ }
+ }
+ heap_walker_.Root(thread_it->regs);
+ }
+
+ ALOGI("searching done");
+
+ return true;
+}
+
+bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
+ size_t limit, size_t* num_leaks, size_t* leak_bytes) {
+ ALOGI("sweeping process %d for unreachable memory", pid_);
+ leaks.clear();
+
+ if (!heap_walker_.DetectLeaks()) {
+ return false;
+ }
+
+
+ allocator::vector<Range> leaked1{allocator_};
+ heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
+
+ ALOGI("sweeping done");
+
+ ALOGI("folding related leaks");
+
+ LeakFolding folding(allocator_, heap_walker_);
+ if (!folding.FoldLeaks()) {
+ return false;
+ }
+
+ allocator::vector<LeakFolding::Leak> leaked{allocator_};
+
+ if (!folding.Leaked(leaked, num_leaks, leak_bytes)) {
+ return false;
+ }
+
+ allocator::unordered_map<Leak::Backtrace, Leak*> backtrace_map{allocator_};
+
+ // Prevent reallocations of backing memory so we can store pointers into it
+ // in backtrace_map.
+ leaks.reserve(leaked.size());
+
+ for (auto& it: leaked) {
+ leaks.emplace_back();
+ Leak* leak = &leaks.back();
+
+ ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it.range.begin),
+ leak->backtrace.frames, leak->backtrace.max_frames);
+ if (num_backtrace_frames > 0) {
+ leak->backtrace.num_frames = num_backtrace_frames;
+
+ auto inserted = backtrace_map.emplace(leak->backtrace, leak);
+ if (!inserted.second) {
+ // Leak with same backtrace already exists, drop this one and
+ // increment similar counts on the existing one.
+ leaks.pop_back();
+ Leak* similar_leak = inserted.first->second;
+ similar_leak->similar_count++;
+ similar_leak->similar_size += it.range.size();
+ similar_leak->similar_referenced_count += it.referenced_count;
+ similar_leak->similar_referenced_size += it.referenced_size;
+ similar_leak->total_size += it.range.size();
+ similar_leak->total_size += it.referenced_size;
+ continue;
+ }
+ }
+
+ leak->begin = it.range.begin;
+ leak->size = it.range.size();
+ leak->referenced_count = it.referenced_count;
+ leak->referenced_size = it.referenced_size;
+ leak->total_size = leak->size + leak->referenced_size;
+ memcpy(leak->contents, reinterpret_cast<void*>(it.range.begin),
+ std::min(leak->size, Leak::contents_length));
+ }
+
+ ALOGI("folding done");
+
+ std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
+ return a.total_size > b.total_size;
+ });
+
+ if (leaks.size() > limit) {
+ leaks.resize(limit);
+ }
+
+ return true;
+}
+
+static bool has_prefix(const allocator::string& s, const char* prefix) {
+ int ret = s.compare(0, strlen(prefix), prefix);
+ return ret == 0;
+}
+
+bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
+ allocator::vector<Mapping>& heap_mappings,
+ allocator::vector<Mapping>& anon_mappings,
+ allocator::vector<Mapping>& globals_mappings,
+ allocator::vector<Mapping>& stack_mappings)
+{
+ heap_mappings.clear();
+ anon_mappings.clear();
+ globals_mappings.clear();
+ stack_mappings.clear();
+
+ allocator::string current_lib{allocator_};
+
+ for (auto it = mappings.begin(); it != mappings.end(); it++) {
+ if (it->execute) {
+ current_lib = it->name;
+ continue;
+ }
+
+ if (!it->read) {
+ continue;
+ }
+
+ const allocator::string mapping_name{it->name, allocator_};
+ if (mapping_name == "[anon:.bss]") {
+ // named .bss section
+ globals_mappings.emplace_back(*it);
+ } else if (mapping_name == current_lib) {
+ // .rodata or .data section
+ globals_mappings.emplace_back(*it);
+ } else if (mapping_name == "[anon:libc_malloc]") {
+ // named malloc mapping
+ heap_mappings.emplace_back(*it);
+ } else if (has_prefix(mapping_name, "/dev/ashmem/dalvik")) {
+ // named dalvik heap mapping
+ globals_mappings.emplace_back(*it);
+ } else if (has_prefix(mapping_name, "[stack")) {
+ // named stack mapping
+ stack_mappings.emplace_back(*it);
+ } else if (mapping_name.size() == 0) {
+ globals_mappings.emplace_back(*it);
+ } else if (has_prefix(mapping_name, "[anon:") && mapping_name != "[anon:leak_detector_malloc]") {
+ // TODO(ccross): it would be nice to treat named anonymous mappings as
+ // possible leaks, but naming something in a .bss or .data section makes
+ // it impossible to distinguish them from mmaped and then named mappings.
+ globals_mappings.emplace_back(*it);
+ }
+ }
+
+ return true;
+}
+
+template<typename T>
+static inline const char* plural(T val) {
+ return (val == 1) ? "" : "s";
+}
+
+bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) {
+ int parent_pid = getpid();
+ int parent_tid = gettid();
+
+ Heap heap;
+
+ Semaphore continue_parent_sem;
+ LeakPipe pipe;
+
+ PtracerThread thread{[&]() -> int {
+ /////////////////////////////////////////////
+ // Collection thread
+ /////////////////////////////////////////////
+ ALOGI("collecting thread info for process %d...", parent_pid);
+
+ ThreadCapture thread_capture(parent_pid, heap);
+ allocator::vector<ThreadInfo> thread_info(heap);
+ allocator::vector<Mapping> mappings(heap);
+
+ // ptrace all the threads
+ if (!thread_capture.CaptureThreads()) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
+ // collect register contents and stacks
+ if (!thread_capture.CapturedThreadInfo(thread_info)) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
+ // snapshot /proc/pid/maps
+ if (!ProcessMappings(parent_pid, mappings)) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
+ // malloc must be enabled to call fork, at_fork handlers take the same
+ // locks as ScopedDisableMalloc. All threads are paused in ptrace, so
+ // memory state is still consistent. Unfreeze the original thread so it
+ // can drop the malloc locks, it will block until the collection thread
+ // exits.
+ thread_capture.ReleaseThread(parent_tid);
+ continue_parent_sem.Post();
+
+ // fork a process to do the heap walking
+ int ret = fork();
+ if (ret < 0) {
+ return 1;
+ } else if (ret == 0) {
+ /////////////////////////////////////////////
+ // Heap walker process
+ /////////////////////////////////////////////
+ // Examine memory state in the child using the data collected above and
+ // the CoW snapshot of the process memory contents.
+
+ if (!pipe.OpenSender()) {
+ _exit(1);
+ }
+
+ MemUnreachable unreachable{parent_pid, heap};
+
+ if (!unreachable.CollectAllocations(thread_info, mappings)) {
+ _exit(2);
+ }
+ size_t num_allocations = unreachable.Allocations();
+ size_t allocation_bytes = unreachable.AllocationBytes();
+
+ allocator::vector<Leak> leaks{heap};
+
+ size_t num_leaks = 0;
+ size_t leak_bytes = 0;
+ bool ok = unreachable.GetUnreachableMemory(leaks, limit, &num_leaks, &leak_bytes);
+
+ ok = ok && pipe.Sender().Send(num_allocations);
+ ok = ok && pipe.Sender().Send(allocation_bytes);
+ ok = ok && pipe.Sender().Send(num_leaks);
+ ok = ok && pipe.Sender().Send(leak_bytes);
+ ok = ok && pipe.Sender().SendVector(leaks);
+
+ if (!ok) {
+ _exit(3);
+ }
+
+ _exit(0);
+ } else {
+ // Nothing left to do in the collection thread, return immediately,
+ // releasing all the captured threads.
+ ALOGI("collection thread done");
+ return 0;
+ }
+ }};
+
+ /////////////////////////////////////////////
+ // Original thread
+ /////////////////////////////////////////////
+
+ {
+ // Disable malloc to get a consistent view of memory
+ ScopedDisableMalloc disable_malloc;
+
+ // Start the collection thread
+ thread.Start();
+
+ // Wait for the collection thread to signal that it is ready to fork the
+ // heap walker process.
+ continue_parent_sem.Wait(30s);
+
+ // Re-enable malloc so the collection thread can fork.
+ }
+
+ // Wait for the collection thread to exit
+ int ret = thread.Join();
+ if (ret != 0) {
+ return false;
+ }
+
+ // Get a pipe from the heap walker process. Transferring a new pipe fd
+ // ensures no other forked processes can have it open, so when the heap
+ // walker process dies the remote side of the pipe will close.
+ if (!pipe.OpenReceiver()) {
+ return false;
+ }
+
+ bool ok = true;
+ ok = ok && pipe.Receiver().Receive(&info.num_allocations);
+ ok = ok && pipe.Receiver().Receive(&info.allocation_bytes);
+ ok = ok && pipe.Receiver().Receive(&info.num_leaks);
+ ok = ok && pipe.Receiver().Receive(&info.leak_bytes);
+ ok = ok && pipe.Receiver().ReceiveVector(info.leaks);
+ if (!ok) {
+ return false;
+ }
+
+ ALOGI("unreachable memory detection done");
+ ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
+ info.leak_bytes, info.num_leaks, plural(info.num_leaks),
+ info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
+ return true;
+}
+
+std::string Leak::ToString(bool log_contents) const {
+
+ std::ostringstream oss;
+
+ oss << " " << std::dec << size;
+ oss << " bytes unreachable at ";
+ oss << std::hex << begin;
+ oss << std::endl;
+ if (referenced_count > 0) {
+ oss << std::dec;
+ oss << " referencing " << referenced_size << " unreachable bytes";
+ oss << " in " << referenced_count << " allocation" << plural(referenced_count);
+ oss << std::endl;
+ }
+ if (similar_count > 0) {
+ oss << std::dec;
+ oss << " and " << similar_size << " similar unreachable bytes";
+ oss << " in " << similar_count << " allocation" << plural(similar_count);
+ oss << std::endl;
+ if (similar_referenced_count > 0) {
+ oss << " referencing " << similar_referenced_size << " unreachable bytes";
+ oss << " in " << similar_referenced_count << " allocation" << plural(similar_referenced_count);
+ oss << std::endl;
+ }
+ }
+
+ if (log_contents) {
+ const int bytes_per_line = 16;
+ const size_t bytes = std::min(size, contents_length);
+
+ if (bytes == size) {
+ oss << " contents:" << std::endl;
+ } else {
+ oss << " first " << bytes << " bytes of contents:" << std::endl;
+ }
+
+ for (size_t i = 0; i < bytes; i += bytes_per_line) {
+ oss << " " << std::hex << begin + i << ": ";
+ size_t j;
+ oss << std::setfill('0');
+ for (j = i; j < bytes && j < i + bytes_per_line; j++) {
+ oss << std::setw(2) << static_cast<int>(contents[j]) << " ";
+ }
+ oss << std::setfill(' ');
+ for (; j < i + bytes_per_line; j++) {
+ oss << " ";
+ }
+ for (j = i; j < bytes && j < i + bytes_per_line; j++) {
+ char c = contents[j];
+ if (c < ' ' || c >= 0x7f) {
+ c = '.';
+ }
+ oss << c;
+ }
+ oss << std::endl;
+ }
+ }
+ if (backtrace.num_frames > 0) {
+ oss << backtrace_string(backtrace.frames, backtrace.num_frames);
+ }
+
+ return oss.str();
+}
+
+// Figure out the abi based on defined macros.
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#else
+#error "Unsupported ABI"
+#endif
+
+std::string UnreachableMemoryInfo::ToString(bool log_contents) const {
+ std::ostringstream oss;
+ oss << " " << leak_bytes << " bytes in ";
+ oss << num_leaks << " unreachable allocation" << plural(num_leaks);
+ oss << std::endl;
+ oss << " ABI: '" ABI_STRING "'" << std::endl;
+ oss << std::endl;
+
+ for (auto it = leaks.begin(); it != leaks.end(); it++) {
+ oss << it->ToString(log_contents);
+ oss << std::endl;
+ }
+
+ return oss.str();
+}
+
+std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
+ UnreachableMemoryInfo info;
+ if (!GetUnreachableMemory(info, limit)) {
+ return "Failed to get unreachable memory\n";
+ }
+
+ return info.ToString(log_contents);
+}
+
+bool LogUnreachableMemory(bool log_contents, size_t limit) {
+ UnreachableMemoryInfo info;
+ if (!GetUnreachableMemory(info, limit)) {
+ return false;
+ }
+
+ for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) {
+ ALOGE("%s", it->ToString(log_contents).c_str());
+ }
+ return true;
+}
+
+
+bool NoLeaks() {
+ UnreachableMemoryInfo info;
+ if (!GetUnreachableMemory(info, 0)) {
+ return false;
+ }
+
+ return info.num_leaks == 0;
+}
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
new file mode 100644
index 0000000..7cca7c1
--- /dev/null
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+#include "LineBuffer.h"
+#include "ProcessMappings.h"
+#include "log.h"
+
+// This function is not re-entrant since it uses a static buffer for
+// the line data.
+bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
+ char map_buffer[1024];
+ snprintf(map_buffer, sizeof(map_buffer), "/proc/%d/maps", pid);
+ int fd = open(map_buffer, O_RDONLY);
+ if (fd < 0) {
+ return false;
+ }
+ android::base::unique_fd fd_guard{fd};
+
+ LineBuffer line_buf(fd, map_buffer, sizeof(map_buffer));
+ char* line;
+ size_t line_len;
+ while (line_buf.GetLine(&line, &line_len)) {
+ int name_pos;
+ char perms[5];
+ Mapping mapping{};
+ if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n",
+ &mapping.begin, &mapping.end, perms, &name_pos) == 3) {
+ if (perms[0] == 'r') {
+ mapping.read = true;
+ }
+ if (perms[1] == 'w') {
+ mapping.write = true;
+ }
+ if (perms[2] == 'x') {
+ mapping.execute = true;
+ }
+ if (perms[3] == 'p') {
+ mapping.priv = true;
+ }
+ if ((size_t)name_pos < line_len) {
+ strlcpy(mapping.name, line + name_pos, sizeof(mapping.name));
+ }
+ mappings.emplace_back(mapping);
+ }
+ }
+ return true;
+}
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h
new file mode 100644
index 0000000..d3b7496
--- /dev/null
+++ b/libmemunreachable/ProcessMappings.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+#define LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+
+#include "Allocator.h"
+
+struct Mapping {
+ uintptr_t begin;
+ uintptr_t end;
+ bool read;
+ bool write;
+ bool execute;
+ bool priv;
+ char name[96];
+};
+
+// This function is not re-entrant since it uses a static buffer for
+// the line data.
+bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings);
+
+#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
new file mode 100644
index 0000000..aa5b344
--- /dev/null
+++ b/libmemunreachable/PtracerThread.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "android-base/macros.h"
+
+#include "anon_vma_naming.h"
+#include "log.h"
+#include "PtracerThread.h"
+
+class Stack {
+ public:
+ Stack(size_t size) : size_(size) {
+ int prot = PROT_READ | PROT_WRITE;
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+ page_size_ = sysconf(_SC_PAGE_SIZE);
+ size_ += page_size_*2; // guard pages
+ base_ = mmap(NULL, size_, prot, flags, -1, 0);
+ if (base_ == MAP_FAILED) {
+ base_ = NULL;
+ size_ = 0;
+ return;
+ }
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base_, size_, "libmemunreachable stack");
+ mprotect(base_, page_size_, PROT_NONE);
+ mprotect(top(), page_size_, PROT_NONE);
+ };
+ ~Stack() {
+ munmap(base_, size_);
+ };
+ void* top() {
+ return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_) + size_ - page_size_);
+ };
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Stack);
+
+ void *base_;
+ size_t size_;
+ size_t page_size_;
+};
+
+PtracerThread::PtracerThread(const std::function<int()>& func) :
+ child_pid_(0) {
+ stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
+ if (stack_->top() == nullptr) {
+ LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
+ }
+
+ func_ = std::function<int()>{[&, func]() -> int {
+ // In the child thread, lock and unlock the mutex to wait for the parent
+ // to finish setting up for the child thread
+ std::unique_lock<std::mutex> lk(m_);
+ lk.unlock();
+ _exit(func());
+ }};
+}
+
+PtracerThread::~PtracerThread() {
+ Kill();
+ Join();
+ ClearTracer();
+ stack_ = nullptr;
+}
+
+bool PtracerThread::Start() {
+ std::unique_lock<std::mutex> lk(m_);
+
+ // Convert from void(*)(void*) to lambda with captures
+ auto proxy = [](void *arg) -> int {
+ prctl(PR_SET_NAME, "libmemunreachable ptrace thread");
+ return (*reinterpret_cast<std::function<int()>*>(arg))();
+ };
+
+ child_pid_ = clone(proxy, stack_->top(),
+ CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
+ reinterpret_cast<void*>(&func_));
+ if (child_pid_ < 0) {
+ ALOGE("failed to clone child: %s", strerror(errno));
+ return false;
+ }
+
+ SetTracer(child_pid_);
+
+ lk.unlock();
+
+ return true;
+}
+
+int PtracerThread::Join() {
+ if (child_pid_ == -1) {
+ return -1;
+ }
+ int status;
+ int ret = TEMP_FAILURE_RETRY(waitpid(child_pid_, &status, __WALL));
+ if (ret < 0) {
+ ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
+ return -1;
+ }
+
+ child_pid_ = -1;
+
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ return -WTERMSIG(status);
+ } else {
+ ALOGE("unexpected status %x", status);
+ return -1;
+ }
+}
+
+void PtracerThread::Kill() {
+ if (child_pid_ == -1) {
+ return;
+ }
+
+ syscall(SYS_tkill, child_pid_, SIGKILL);
+}
+
+void PtracerThread::SetTracer(pid_t tracer_pid) {
+ prctl(PR_SET_PTRACER, tracer_pid);
+}
+
+void PtracerThread::ClearTracer() {
+ prctl(PR_SET_PTRACER, 0);
+}
diff --git a/libmemunreachable/PtracerThread.h b/libmemunreachable/PtracerThread.h
new file mode 100644
index 0000000..4d6ca9a
--- /dev/null
+++ b/libmemunreachable/PtracerThread.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+#define LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+
+#include <functional>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+#include "Allocator.h"
+
+class Stack;
+
+// PtracerThread is similar to std::thread, except that it creates a "thread"
+// that can ptrace the other threads. The thread is actually a separate
+// process, with its own thread group, but shares address space and fds with
+// the parent.
+class PtracerThread {
+ public:
+ PtracerThread(const std::function<int()>& func);
+ ~PtracerThread();
+ bool Start();
+ int Join();
+ private:
+ void SetTracer(pid_t);
+ void ClearTracer();
+ void Kill();
+ DISALLOW_COPY_AND_ASSIGN(PtracerThread);
+ std::unique_ptr<Stack> stack_;
+ std::function<int()> func_;
+ std::mutex m_;
+ pid_t child_pid_;
+};
+
+#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
new file mode 100644
index 0000000..61a47de
--- /dev/null
+++ b/libmemunreachable/README.md
@@ -0,0 +1,71 @@
+libmemunreachable
+================
+
+Introduction
+--------------
+libmemunreachable is a zero-overhead native memory leak detector. It uses an imprecise mark-and-sweep garbage collector pass over all native memory, reporting any unreachable blocks as leaks. It is similar to the [Heap Checker from tcmalloc](http://htmlpreview.github.io/?https://github.com/gperftools/gperftools/blob/master/doc/heap_checker.html), but with a few key differences to remove the overhead. Instead of instrumenting every call to malloc and free, it queries the allocator (jemalloc) for active allocations when leak detection is requested. In addition, it performs a very short stop-the-world data collection on the main process, and then forks a copy of the process to perform the mark-and-sweep, minimizing disruption to the original process.
+
+In the default (zero-overhead) mode, the returned data on leaks is limited to the address, approximate (upper bound) size, and the the first 32 bytes of the contents of the leaked allocation. If malloc_debug backtraces are enabled they will be included in the leak information, but backtracing allocations requires significant overhead.
+
+----------
+
+Usage
+-------
+
+### C interface ###
+
+#### `bool LogUnreachableMemory(bool log_contents, size_t limit)` ####
+Writes a description of leaked memory to the log. A summary is always written, followed by details of up to `limit` leaks. If `log_contents` is `true`, details include up to 32 bytes of the contents of each leaked allocation.
+Returns true if leak detection succeeded.
+
+#### `bool NoLeaks()` ####
+Returns `true` if no unreachable memory was found.
+
+### C++ interface ###
+
+####`bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)`####
+Updates an `UnreachableMemoryInfo` object with information on leaks, including details on up to `limit` leaks. Returns true if leak detection succeeded.
+
+#### `std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100)` ####
+Returns a description of leaked memory. A summary is always written, followed by details of up to `limit` leaks. If `log_contents` is `true`, details include up to 32 bytes of the contents of each leaked allocation.
+Returns true if leak detection succeeded.
+
+Implementation
+-------------------
+The sequence of steps required to perform a leak detection pass is divided into three processes - the original process, the collection process, and the sweeper process.
+
+ 1. *Original process*: Leak detection is requested by calling `GetUnreachableMemory()`
+ 2. Allocations are disabled using `malloc_disable()`
+ 3. The collection process is spawned. The collection process is similar to a normal `fork()` child process, except that it shares the address space of the parent - any writes by the original process are visible to the collection process, and vice-versa.
+ 4. *Collection process*: All threads in the original process are paused with `ptrace()`.
+ 5. Registers contents, active stack areas, and memory mapping information are collected.
+ 6. *Original process*: Allocations are re-enabled using `malloc_enable()`, but all threads are still paused with `ptrace()`.
+ 7. *Collection process*: The sweeper process is spawned using a normal `fork()`. The sweeper process has a copy of all memory from the original process, including all the data collected by the collection process.
+ 8. Collection process releases all threads from `ptrace` and exits
+ 9. *Original process*: All threads continue, the thread that called `GetUnreachableMemory()` blocks waiting for leak data over a pipe.
+ 10. *Sweeper process*: A list of all active allocations is produced by examining the memory mappings and calling `malloc_iterate()` on any heap mappings.
+ 11. A list of all roots is produced from globals (.data and .bss sections of binaries), and registers and stacks from each thread.
+ 12. The mark-and-sweep pass is performed starting from roots.
+ 13. Unmarked allocations are sent over the pipe back to the original process.
+
+----------
+
+
+Components
+---------------
+- `MemUnreachable.cpp`: Entry points, implements the sequencing described above.
+- `PtracerThread.cpp`: Used to clone the collection process with shared address space.
+- `ThreadCapture.cpp`: Pauses threads in the main process and collects register contents.
+- `ProcessMappings.cpp`: Collects snapshots of `/proc/pid/maps`.
+- `HeapWalker.cpp`: Performs the mark-and-sweep pass over active allocations.
+- `LeakPipe.cpp`: transfers data describing leaks from the sweeper process to the original process.
+
+
+Heap allocator requirements
+----------------------------------
+libmemunreachable requires a small interface to the allocator in order to collect information about active allocations.
+
+ - `malloc_disable()`: prevent any thread from mutating internal allocator state.
+ - `malloc enable()`: re-enable allocations in all threads.
+ - `malloc_iterate()`: call a callback on each active allocation in a given heap region.
+ - `malloc_backtrace()`: return the backtrace from when the allocation at the given address was allocated, if it was collected.
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
new file mode 100644
index 0000000..287f479
--- /dev/null
+++ b/libmemunreachable/ScopedAlarm.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SCOPED_ALARM_H_
+#define LIBMEMUNREACHABLE_SCOPED_ALARM_H_
+
+#include <signal.h>
+#include <sys/time.h>
+
+#include <chrono>
+#include <functional>
+
+class ScopedAlarm {
+ public:
+ ScopedAlarm(std::chrono::microseconds us, std::function<void()> func) {
+ func_ = func;
+ struct sigaction oldact{};
+ struct sigaction act{};
+ act.sa_handler = [](int) {
+ ScopedAlarm::func_();
+ };
+ sigaction(SIGALRM, &act, &oldact);
+
+ std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
+ itimerval t = itimerval{};
+ t.it_value.tv_sec = s.count();
+ t.it_value.tv_usec = (us - s).count();
+ setitimer(ITIMER_REAL, &t, NULL);
+ }
+ ~ScopedAlarm() {
+ itimerval t = itimerval{};
+ setitimer(ITIMER_REAL, &t, NULL);
+ struct sigaction act{};
+ act.sa_handler = SIG_DFL;
+ sigaction(SIGALRM, &act, NULL);
+ }
+ private:
+ static std::function<void()> func_;
+};
+#endif
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
new file mode 100644
index 0000000..4f96376
--- /dev/null
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+#define LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+
+#include <memory>
+
+#include "android-base/macros.h"
+
+#include "bionic.h"
+#include "log.h"
+#include "ScopedAlarm.h"
+
+class DisableMallocGuard{
+ public:
+ DisableMallocGuard() : disabled_(false){}
+ ~DisableMallocGuard() {
+ Enable();
+ }
+
+ void Disable() {
+ if (!disabled_) {
+ malloc_disable();
+ disabled_ = true;
+ }
+ }
+
+ void Enable() {
+ if (disabled_) {
+ malloc_enable();
+ disabled_ = false;
+ }
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DisableMallocGuard);
+ bool disabled_;
+};
+
+// Any calls to malloc or free from this thread will deadlock as long as this
+// object is in scope. Calls to malloc from other threads may succeed (for
+// example if the allocation is satisfied out of the thread's tcache), or may
+// block until the object is destroyed.
+//
+// Don't call fork() while malloc is disabled, it needs the same locks held
+// here.
+class ScopedDisableMalloc {
+ public:
+ ScopedDisableMalloc() {
+ disable_malloc_.Disable();
+ }
+
+ ~ScopedDisableMalloc() {
+ disable_malloc_.Enable();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableMalloc);
+ DisableMallocGuard disable_malloc_;
+};
+
+class ScopedDisableMallocTimeout {
+ public:
+ ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) :
+ timeout_(timeout), timed_out_(false), disable_malloc_() {
+ Disable();
+ }
+
+ ~ScopedDisableMallocTimeout() {
+ Enable();
+ }
+
+ bool timed_out() {
+ return timed_out_;
+ }
+
+ void Enable() {
+ disable_malloc_.Enable();
+ alarm_ = nullptr;
+ }
+
+ void Disable() {
+ // set up the alarm before disabling malloc so unique_ptr can be used
+ alarm_ = std::make_unique<ScopedAlarm>(timeout_, [&]() {
+ disable_malloc_.Enable();
+ timed_out_ = true;
+ });
+
+ disable_malloc_.Disable();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableMallocTimeout);
+ std::chrono::milliseconds timeout_;
+ bool timed_out_;
+ std::unique_ptr<ScopedAlarm> alarm_;
+ DisableMallocGuard disable_malloc_;
+};
+
+#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
new file mode 100644
index 0000000..9beef9a
--- /dev/null
+++ b/libmemunreachable/ScopedPipe.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SCOPED_PIPE_H_
+#define LIBMEMUNREACHABLE_SCOPED_PIPE_H_
+
+#include <unistd.h>
+
+#include "log.h"
+
+class ScopedPipe {
+ public:
+ ScopedPipe() : pipefd_{-1, -1} {
+ int ret = pipe2(pipefd_, O_CLOEXEC);
+ if (ret < 0) {
+ LOG_ALWAYS_FATAL("failed to open pipe");
+ }
+ }
+ ~ScopedPipe() {
+ Close();
+ }
+
+ ScopedPipe(ScopedPipe&& other) {
+ SetReceiver(other.ReleaseReceiver());
+ SetSender(other.ReleaseSender());
+ }
+
+ ScopedPipe& operator = (ScopedPipe&& other) {
+ SetReceiver(other.ReleaseReceiver());
+ SetSender(other.ReleaseSender());
+ return *this;
+ }
+
+ void CloseReceiver() {
+ close(ReleaseReceiver());
+ }
+
+ void CloseSender() {
+ close(ReleaseSender());
+ }
+
+ void Close() {
+ CloseReceiver();
+ CloseSender();
+ }
+
+ int Receiver() { return pipefd_[0]; }
+ int Sender() { return pipefd_[1]; }
+
+ int ReleaseReceiver() {
+ int ret = Receiver();
+ SetReceiver(-1);
+ return ret;
+ }
+
+ int ReleaseSender() {
+ int ret = Sender();
+ SetSender(-1);
+ return ret;
+ }
+
+ private:
+ void SetReceiver(int fd) { pipefd_[0] = fd; };
+ void SetSender(int fd) { pipefd_[1] = fd; };
+
+ int pipefd_[2];
+};
+#endif
diff --git a/libmemunreachable/Semaphore.h b/libmemunreachable/Semaphore.h
new file mode 100644
index 0000000..45e8c81
--- /dev/null
+++ b/libmemunreachable/Semaphore.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SEMAPHORE_H_
+#define LIBMEMUNREACHABLE_SEMAPHORE_H_
+
+#include <chrono>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+class Semaphore {
+ public:
+ Semaphore(int count = 0) : count_(count) {}
+ ~Semaphore() = default;
+
+ void Wait(std::chrono::milliseconds ms) {
+ std::unique_lock<std::mutex> lk(m_);
+ cv_.wait_for(lk, ms, [&]{
+ if (count_ > 0) {
+ count_--;
+ return true;
+ }
+ return false;
+ });
+ }
+ void Post() {
+ {
+ std::lock_guard<std::mutex> lk(m_);
+ count_++;
+ }
+ cv_.notify_one();
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Semaphore);
+
+ int count_;
+ std::mutex m_;
+ std::condition_variable cv_;
+};
+
+
+#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
new file mode 100644
index 0000000..d7ecdb9
--- /dev/null
+++ b/libmemunreachable/Tarjan.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Based on system/update_engine/payload_generator/tarjan.cc
+
+#ifndef LIBMEMUNREACHABLE_TARJAN_H_
+#define LIBMEMUNREACHABLE_TARJAN_H_
+
+#include <algorithm>
+
+#include "Allocator.h"
+
+template<class T>
+class Node {
+ public:
+ allocator::set<Node<T>*> references_in;
+ allocator::set<Node<T>*> references_out;
+ size_t index;
+ size_t lowlink;
+
+ T* ptr;
+
+ Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator),
+ ptr(ptr) {};
+ Node(Node&& rhs) = default;
+ void Edge(Node<T>* ref) {
+ references_out.emplace(ref);
+ ref->references_in.emplace(this);
+ }
+ template<class F>
+ void Foreach(F&& f) {
+ for (auto& node: references_out) {
+ f(node->ptr);
+ }
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Node<T>);
+};
+
+template<class T>
+using Graph = allocator::vector<Node<T>*>;
+
+template<class T>
+using SCC = allocator::vector<Node<T>*>;
+
+template<class T>
+using SCCList = allocator::vector<SCC<T>>;
+
+template<class T>
+class TarjanAlgorithm {
+ public:
+ TarjanAlgorithm(Allocator<void> allocator) : index_(0),
+ stack_(allocator), components_(allocator) {}
+
+ void Execute(Graph<T>& graph, SCCList<T>& out);
+ private:
+ static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1);
+ void Tarjan(Node<T>* vertex, Graph<T>& graph);
+
+ size_t index_;
+ allocator::vector<Node<T>*> stack_;
+ SCCList<T> components_;
+};
+
+template<class T>
+void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) {
+ stack_.clear();
+ components_.clear();
+ index_ = 0;
+ for (auto& it: graph) {
+ it->index = UNDEFINED_INDEX;
+ it->lowlink = UNDEFINED_INDEX;
+ }
+
+ for (auto& it: graph) {
+ if (it->index == UNDEFINED_INDEX) {
+ Tarjan(it, graph);
+ }
+ }
+ out.swap(components_);
+}
+
+template<class T>
+void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) {
+ assert(vertex->index == UNDEFINED_INDEX);
+ vertex->index = index_;
+ vertex->lowlink = index_;
+ index_++;
+ stack_.push_back(vertex);
+ for (auto& it: vertex->references_out) {
+ Node<T>* vertex_next = it;
+ if (vertex_next->index == UNDEFINED_INDEX) {
+ Tarjan(vertex_next, graph);
+ vertex->lowlink = std::min(vertex->lowlink, vertex_next->lowlink);
+ } else if (std::find(stack_.begin(), stack_.end(), vertex_next) != stack_.end()) {
+ vertex->lowlink = std::min(vertex->lowlink, vertex_next->index);
+ }
+ }
+ if (vertex->lowlink == vertex->index) {
+ SCC<T> component{components_.get_allocator()};
+ Node<T>* other_vertex;
+ do {
+ other_vertex = stack_.back();
+ stack_.pop_back();
+ component.push_back(other_vertex);
+ } while (other_vertex != vertex && !stack_.empty());
+
+ components_.emplace_back(component);
+ }
+}
+
+template<class T>
+void Tarjan(Graph<T>& graph, SCCList<T>& out) {
+ TarjanAlgorithm<T> tarjan{graph.get_allocator()};
+ tarjan.Execute(graph, out);
+}
+
+#endif // LIBMEMUNREACHABLE_TARJAN_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
new file mode 100644
index 0000000..e8a8392
--- /dev/null
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2016 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 "ThreadCapture.h"
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "Allocator.h"
+#include "log.h"
+
+// bionic interfaces used:
+// atoi
+// strlcat
+// writev
+
+// bionic interfaces reimplemented to avoid allocation:
+// getdents64
+
+// Convert a pid > 0 to a string. sprintf might allocate, so we can't use it.
+// Returns a pointer somewhere in buf to a null terminated string, or NULL
+// on error.
+static char *pid_to_str(char *buf, size_t len, pid_t pid) {
+ if (pid <= 0) {
+ return nullptr;
+ }
+
+ char *ptr = buf + len - 1;
+ *ptr = 0;
+ while (pid > 0) {
+ ptr--;
+ if (ptr < buf) {
+ return nullptr;
+ }
+ *ptr = '0' + (pid % 10);
+ pid /= 10;
+ }
+
+ return ptr;
+}
+
+class ThreadCaptureImpl {
+ public:
+ ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator);
+ ~ThreadCaptureImpl() {}
+ bool ListThreads(TidList& tids);
+ bool CaptureThreads();
+ bool ReleaseThreads();
+ bool ReleaseThread(pid_t tid);
+ bool CapturedThreadInfo(ThreadInfoList& threads);
+ void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
+ private:
+ int CaptureThread(pid_t tid);
+ bool ReleaseThread(pid_t tid, unsigned int signal);
+ int PtraceAttach(pid_t tid);
+ void PtraceDetach(pid_t tid, unsigned int signal);
+ bool PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info);
+
+ allocator::map<pid_t, unsigned int> captured_threads_;
+ Allocator<ThreadCaptureImpl> allocator_;
+ pid_t pid_;
+ std::function<void(pid_t)> inject_test_func_;
+};
+
+ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) :
+ captured_threads_(allocator), allocator_(allocator), pid_(pid) {
+}
+
+bool ThreadCaptureImpl::ListThreads(TidList& tids) {
+ tids.clear();
+
+ char pid_buf[11];
+ char path[256] = "/proc/";
+ char* pid_str = pid_to_str(pid_buf, sizeof(pid_buf), pid_);
+ if (!pid_str) {
+ return false;
+ }
+ strlcat(path, pid_str, sizeof(path));
+ strlcat(path, "/task", sizeof(path));
+
+ int fd = open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
+ if (fd < 0) {
+ ALOGE("failed to open %s: %s", path, strerror(errno));
+ return false;
+ }
+ android::base::unique_fd fd_guard{fd};
+
+ struct linux_dirent64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ uint16_t d_reclen;
+ char d_type;
+ char d_name[];
+ } __attribute((packed));
+ char dirent_buf[4096];
+ ssize_t nread;
+ do {
+ nread = syscall(SYS_getdents64, fd, dirent_buf, sizeof(dirent_buf));
+ if (nread < 0) {
+ ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
+ return false;
+ } else if (nread > 0) {
+ ssize_t off = 0;
+ while (off < nread) {
+ linux_dirent64* dirent = reinterpret_cast<linux_dirent64*>(dirent_buf + off);
+ off += dirent->d_reclen;
+ pid_t tid = atoi(dirent->d_name);
+ if (tid <= 0) {
+ continue;
+ }
+ tids.push_back(tid);
+ }
+ }
+
+ } while (nread != 0);
+
+ return true;
+}
+
+bool ThreadCaptureImpl::CaptureThreads() {
+ TidList tids{allocator_};
+
+ bool found_new_thread;
+ do {
+ if (!ListThreads(tids)) {
+ ReleaseThreads();
+ return false;
+ }
+
+ found_new_thread = false;
+
+ for (auto it = tids.begin(); it != tids.end(); it++) {
+ auto captured = captured_threads_.find(*it);
+ if (captured == captured_threads_.end()) {
+ if (CaptureThread(*it) < 0) {
+ ReleaseThreads();
+ return false;
+ }
+ found_new_thread = true;
+ }
+ }
+ } while (found_new_thread);
+
+ return true;
+}
+
+// Detatches from a thread, delivering signal if nonzero, logs on error
+void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
+ void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
+ if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
+ ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ }
+}
+
+// Attaches to and pauses thread.
+// Returns 1 on attach, 0 on tid not found, -1 and logs on error
+int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
+ int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
+ if (ret < 0) {
+ ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ return -1;
+ }
+
+ if (inject_test_func_) {
+ inject_test_func_(tid);
+ }
+
+ if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) < 0) {
+ if (errno == ESRCH) {
+ return 0;
+ } else {
+ ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ PtraceDetach(tid, 0);
+ return -1;
+ }
+ }
+ return 1;
+}
+
+bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
+ thread_info.tid = tid;
+
+ const unsigned int max_num_regs = 128; // larger than number of registers on any device
+ uintptr_t regs[max_num_regs];
+ struct iovec iovec;
+ iovec.iov_base = ®s;
+ iovec.iov_len = sizeof(regs);
+
+ if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
+ ALOGE("ptrace getregset for thread %d of process %d failed: %s",
+ tid, pid_, strerror(errno));
+ return false;
+ }
+
+ unsigned int num_regs = iovec.iov_len / sizeof(uintptr_t);
+ thread_info.regs.assign(®s[0], ®s[num_regs]);
+
+ const int sp =
+#if defined(__x86_64__)
+ offsetof(struct pt_regs, rsp) / sizeof(uintptr_t)
+#elif defined(__i386__)
+ offsetof(struct pt_regs, esp) / sizeof(uintptr_t)
+#elif defined(__arm__)
+ offsetof(struct pt_regs, ARM_sp) / sizeof(uintptr_t)
+#elif defined(__aarch64__)
+ offsetof(struct user_pt_regs, sp) / sizeof(uintptr_t)
+#elif defined(__mips__) || defined(__mips64__)
+ offsetof(struct pt_regs, regs[29]) / sizeof(uintptr_t)
+#else
+#error Unrecognized architecture
+#endif
+ ;
+
+ // TODO(ccross): use /proc/tid/status or /proc/pid/maps to get start_stack
+
+ thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
+
+ return true;
+}
+
+int ThreadCaptureImpl::CaptureThread(pid_t tid) {
+ int ret = PtraceAttach(tid);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ int status = 0;
+ if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
+ ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ PtraceDetach(tid, 0);
+ return -1;
+ }
+
+ if (!WIFSTOPPED(status)) {
+ ALOGE("thread %d of process %d was not paused after waitpid, killed?",
+ tid, pid_);
+ return 0;
+ }
+
+ unsigned int resume_signal = 0;
+
+ unsigned int signal = WSTOPSIG(status);
+ if ((status >> 16) == PTRACE_EVENT_STOP) {
+ switch (signal) {
+ case SIGSTOP:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ // group-stop signals
+ break;
+ case SIGTRAP:
+ // normal ptrace interrupt stop
+ break;
+ default:
+ ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
+ signal, tid, pid_);
+ return -1;
+ }
+ } else {
+ // signal-delivery-stop
+ resume_signal = signal;
+ }
+
+ captured_threads_[tid] = resume_signal;
+ return 1;
+}
+
+bool ThreadCaptureImpl::ReleaseThread(pid_t tid) {
+ auto it = captured_threads_.find(tid);
+ if (it == captured_threads_.end()) {
+ return false;
+ }
+ return ReleaseThread(it->first, it->second);
+}
+
+bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) {
+ PtraceDetach(tid, signal);
+ return true;
+}
+
+bool ThreadCaptureImpl::ReleaseThreads() {
+ bool ret = true;
+ for (auto it = captured_threads_.begin(); it != captured_threads_.end(); ) {
+ if (ReleaseThread(it->first, it->second)) {
+ it = captured_threads_.erase(it);
+ } else {
+ it++;
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+bool ThreadCaptureImpl::CapturedThreadInfo(ThreadInfoList& threads) {
+ threads.clear();
+
+ for (auto it = captured_threads_.begin(); it != captured_threads_.end(); it++) {
+ ThreadInfo t{0, allocator::vector<uintptr_t>(allocator_), std::pair<uintptr_t, uintptr_t>(0, 0)};
+ if (!PtraceThreadInfo(it->first, t)) {
+ return false;
+ }
+ threads.push_back(t);
+ }
+ return true;
+}
+
+ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) {
+ Allocator<ThreadCaptureImpl> impl_allocator = allocator;
+ impl_ = impl_allocator.make_unique(pid, impl_allocator);
+}
+
+ThreadCapture::~ThreadCapture() {}
+
+bool ThreadCapture::ListThreads(TidList& tids) {
+ return impl_->ListThreads(tids);
+}
+
+bool ThreadCapture::CaptureThreads() {
+ return impl_->CaptureThreads();
+}
+
+bool ThreadCapture::ReleaseThreads() {
+ return impl_->ReleaseThreads();
+}
+
+bool ThreadCapture::ReleaseThread(pid_t tid) {
+ return impl_->ReleaseThread(tid);
+}
+
+bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) {
+ return impl_->CapturedThreadInfo(threads);
+}
+
+void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
+ impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
+}
diff --git a/libmemunreachable/ThreadCapture.h b/libmemunreachable/ThreadCapture.h
new file mode 100644
index 0000000..1022cad
--- /dev/null
+++ b/libmemunreachable/ThreadCapture.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_THREAD_CAPTURE_H_
+#define LIBMEMUNREACHABLE_THREAD_CAPTURE_H_
+
+#include <utility>
+
+#include "Allocator.h"
+
+struct ThreadInfo {
+ pid_t tid;
+ allocator::vector<uintptr_t> regs;
+ std::pair<uintptr_t, uintptr_t> stack;
+};
+
+using TidList = allocator::vector<pid_t>;
+using ThreadInfoList = allocator::vector<ThreadInfo>;
+
+class ThreadCaptureImpl;
+
+class ThreadCapture {
+public:
+ ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator);
+ ~ThreadCapture();
+
+ bool ListThreads(TidList& tids);
+ bool CaptureThreads();
+ bool ReleaseThreads();
+ bool ReleaseThread(pid_t tid);
+ bool CapturedThreadInfo(ThreadInfoList& threads);
+ void InjectTestFunc(std::function<void(pid_t)>&& f);
+
+private:
+ ThreadCapture(const ThreadCapture&) = delete;
+ void operator=(const ThreadCapture&) = delete;
+
+ Allocator<ThreadCaptureImpl>::unique_ptr impl_;
+};
+
+#endif
diff --git a/libmemunreachable/anon_vma_naming.h b/libmemunreachable/anon_vma_naming.h
new file mode 100644
index 0000000..1e4ade1
--- /dev/null
+++ b/libmemunreachable/anon_vma_naming.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+#define LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+
+#include <sys/prctl.h>
+
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+
+#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
new file mode 100644
index 0000000..83d07a8
--- /dev/null
+++ b/libmemunreachable/bionic.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_BIONIC_H_
+#define LIBMEMUNREACHABLE_BIONIC_H_
+
+#include <sys/cdefs.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+__BEGIN_DECLS
+
+/* Exported from bionic */
+extern void malloc_disable();
+extern void malloc_enable();
+extern int malloc_iterate(uintptr_t base, size_t size,
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+extern ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
+
+__END_DECLS
+
+#endif // LIBMEMUNREACHABLE_BIONIC_H_
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
new file mode 100644
index 0000000..9b227fd
--- /dev/null
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+#define LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+
+#include <sys/cdefs.h>
+
+#ifdef __cplusplus
+
+#include <vector>
+#include <string>
+
+struct Leak {
+ uintptr_t begin;
+ size_t size;
+
+ size_t referenced_count;
+ size_t referenced_size;
+
+ size_t similar_count;
+ size_t similar_size;
+ size_t similar_referenced_count;
+ size_t similar_referenced_size;
+
+ size_t total_size;
+
+ static const size_t contents_length = 32;
+ char contents[contents_length];
+
+ struct Backtrace {
+ size_t num_frames;
+
+ static const size_t max_frames = 16;
+ uintptr_t frames[max_frames];
+ } backtrace;
+
+ std::string ToString(bool log_contents) const;
+};
+
+struct UnreachableMemoryInfo {
+ std::vector<Leak> leaks;
+ size_t num_leaks;
+ size_t leak_bytes;
+ size_t num_allocations;
+ size_t allocation_bytes;
+
+ UnreachableMemoryInfo() {}
+ ~UnreachableMemoryInfo() {
+ // Clear the memory that holds the leaks, otherwise the next attempt to
+ // detect leaks may find the old data (for example in the jemalloc tcache)
+ // and consider all the leaks to be referenced.
+ memset(leaks.data(), 0, leaks.capacity() * sizeof(Leak));
+ }
+
+ std::string ToString(bool log_contents) const;
+};
+
+bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100);
+
+std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100);
+
+#endif
+
+__BEGIN_DECLS
+
+bool LogUnreachableMemory(bool log_contents, size_t limit);
+
+bool NoLeaks();
+
+__END_DECLS
+
+#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
new file mode 100644
index 0000000..cdfbfd9
--- /dev/null
+++ b/libmemunreachable/log.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LOG_H_
+#define LIBMEMUNREACHABLE_LOG_H_
+
+#define LOG_TAG "libmemunreachable"
+
+#include <log/log.h>
+
+#endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
new file mode 100644
index 0000000..fa76ae0
--- /dev/null
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 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 <Allocator.h>
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+
+
+std::function<void()> ScopedAlarm::func_;
+
+class AllocatorTest : public testing::Test {
+ protected:
+ AllocatorTest() : heap(), disable_malloc_() {}
+ virtual void SetUp() {
+ heap_count = 0;
+ }
+ virtual void TearDown() {
+ ASSERT_EQ(heap_count, 0);
+ ASSERT_TRUE(heap.empty());
+ ASSERT_FALSE(disable_malloc_.timed_out());
+ }
+ Heap heap;
+ private:
+ ScopedDisableMallocTimeout disable_malloc_;
+};
+
+TEST_F(AllocatorTest, simple) {
+ Allocator<char[100]> allocator(heap);
+ void *ptr = allocator.allocate();
+ ASSERT_TRUE(ptr != NULL);
+ allocator.deallocate(ptr);
+}
+
+TEST_F(AllocatorTest, multiple) {
+ Allocator<char[100]> allocator(heap);
+ void *ptr1 = allocator.allocate();
+ ASSERT_TRUE(ptr1 != NULL);
+ void *ptr2 = allocator.allocate();
+ ASSERT_TRUE(ptr2 != NULL);
+ ASSERT_NE(ptr1, ptr2);
+ allocator.deallocate(ptr1);
+ void *ptr3 = allocator.allocate();
+ ASSERT_EQ(ptr1, ptr3);
+ allocator.deallocate(ptr3);
+ allocator.deallocate(ptr2);
+}
+
+TEST_F(AllocatorTest, many) {
+ const int num = 4096;
+ const int size = 128;
+ Allocator<char[size]> allocator(heap);
+ void *ptr[num];
+ for (int i = 0; i < num; i++) {
+ ptr[i] = allocator.allocate();
+ memset(ptr[i], 0xaa, size);
+ *(reinterpret_cast<unsigned char*>(ptr[i])) = i;
+ }
+
+ for (int i = 0; i < num; i++) {
+ for (int j = 0; j < num; j++) {
+ if (i != j) {
+ ASSERT_NE(ptr[i], ptr[j]);
+ }
+ }
+ }
+
+ for (int i = 0; i < num; i++) {
+ ASSERT_EQ(*(reinterpret_cast<unsigned char*>(ptr[i])), i & 0xFF);
+ allocator.deallocate(ptr[i]);
+ }
+}
+
+TEST_F(AllocatorTest, large) {
+ const size_t size = 1024 * 1024;
+ Allocator<char[size]> allocator(heap);
+ void *ptr = allocator.allocate();
+ memset(ptr, 0xaa, size);
+ allocator.deallocate(ptr);
+}
+
+TEST_F(AllocatorTest, many_large) {
+ const int num = 128;
+ const int size = 1024 * 1024;
+ Allocator<char[size]> allocator(heap);
+ void *ptr[num];
+ for (int i = 0; i < num; i++) {
+ ptr[i] = allocator.allocate();
+ memset(ptr[i], 0xaa, size);
+ *(reinterpret_cast<unsigned char*>(ptr[i])) = i;
+ }
+
+ for (int i = 0; i < num; i++) {
+ ASSERT_EQ(*(reinterpret_cast<unsigned char*>(ptr[i])), i & 0xFF);
+ allocator.deallocate(ptr[i]);
+ }
+}
+
+TEST_F(AllocatorTest, copy) {
+ Allocator<char[100]> a(heap);
+ Allocator<char[200]> b = a;
+ Allocator<char[300]> c(b);
+ Allocator<char[100]> d(a);
+ Allocator<char[100]> e(heap);
+
+ ASSERT_EQ(a, b);
+ ASSERT_EQ(a, c);
+ ASSERT_EQ(a, d);
+ ASSERT_EQ(a, e);
+
+ void* ptr1 = a.allocate();
+ void* ptr2 = b.allocate();
+ void* ptr3 = c.allocate();
+ void* ptr4 = d.allocate();
+
+ b.deallocate(ptr1);
+ d.deallocate(ptr2);
+ a.deallocate(ptr3);
+ c.deallocate(ptr4);
+}
+
+TEST_F(AllocatorTest, stl_vector) {
+ auto v = allocator::vector<int>(Allocator<int>(heap));
+ for (int i = 0; i < 1024; i++) {
+ v.push_back(i);
+ }
+ for (int i = 0; i < 1024; i++) {
+ ASSERT_EQ(v[i], i);
+ }
+ v.clear();
+}
+
+TEST_F(AllocatorTest, stl_list) {
+ auto v = allocator::list<int>(Allocator<int>(heap));
+ for (int i = 0; i < 1024; i++) {
+ v.push_back(i);
+ }
+ int i = 0;
+ for (auto iter = v.begin(); iter != v.end(); iter++, i++) {
+ ASSERT_EQ(*iter, i);
+ }
+ v.clear();
+}
+
+TEST_F(AllocatorTest, shared) {
+ Allocator<int> allocator(heap);
+
+ Allocator<int>::shared_ptr ptr = allocator.make_shared(0);
+ {
+ auto ptr2 = ptr;
+ }
+ ASSERT_NE(ptr, nullptr);
+}
+
+TEST_F(AllocatorTest, unique) {
+ Allocator<int> allocator(heap);
+
+ Allocator<int>::unique_ptr ptr = allocator.make_unique(0);
+
+ ASSERT_NE(ptr, nullptr);
+}
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
new file mode 100644
index 0000000..ea5c22c
--- /dev/null
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/time.h>
+
+#include <chrono>
+#include <functional>
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+
+using namespace std::chrono_literals;
+
+class DisableMallocTest : public ::testing::Test {
+ protected:
+ void alarm(std::chrono::microseconds us) {
+ std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
+ itimerval t = itimerval();
+ t.it_value.tv_sec = s.count();
+ t.it_value.tv_usec = (us - s).count();
+ setitimer(ITIMER_REAL, &t, NULL);
+ }
+};
+
+TEST_F(DisableMallocTest, reenable) {
+ ASSERT_EXIT({
+ alarm(100ms);
+ void *ptr1 = malloc(128);
+ ASSERT_NE(ptr1, nullptr);
+ free(ptr1);
+ {
+ ScopedDisableMalloc disable_malloc;
+ }
+ void *ptr2 = malloc(128);
+ ASSERT_NE(ptr2, nullptr);
+ free(ptr2);
+ _exit(1);
+ }, ::testing::ExitedWithCode(1), "");
+}
+
+TEST_F(DisableMallocTest, deadlock_allocate) {
+ ASSERT_DEATH({
+ void *ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ void* ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_new) {
+ ASSERT_DEATH({
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ delete(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ delete(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_delete) {
+ ASSERT_DEATH({
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(250ms);
+ ScopedDisableMalloc disable_malloc;
+ delete(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_free) {
+ ASSERT_DEATH({
+ void *ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ free(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_fork) {
+ ASSERT_DEATH({
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ fork();
+ }
+ }, "");
+}
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
new file mode 100644
index 0000000..c3e1c4d
--- /dev/null
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 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 "HeapWalker.h"
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+#include "Allocator.h"
+
+class HeapWalkerTest : public ::testing::Test {
+ public:
+ HeapWalkerTest() : disable_malloc_(), heap_() {}
+
+ void TearDown() {
+ ASSERT_TRUE(heap_.empty());
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc_.timed_out());
+ }
+ }
+
+ protected:
+ ScopedDisableMallocTimeout disable_malloc_;
+ Heap heap_;
+};
+
+TEST_F(HeapWalkerTest, allocation) {
+ HeapWalker heap_walker(heap_);
+ ASSERT_TRUE(heap_walker.Allocation(3, 4));
+ ASSERT_TRUE(heap_walker.Allocation(2, 3));
+ ASSERT_TRUE(heap_walker.Allocation(4, 5));
+ ASSERT_TRUE(heap_walker.Allocation(6, 7));
+ ASSERT_TRUE(heap_walker.Allocation(0, 1));
+}
+
+TEST_F(HeapWalkerTest, overlap) {
+ HeapWalker heap_walker(heap_);
+ ASSERT_TRUE(heap_walker.Allocation(2, 3));
+ ASSERT_TRUE(heap_walker.Allocation(3, 4));
+ ASSERT_FALSE(heap_walker.Allocation(2, 3));
+ ASSERT_FALSE(heap_walker.Allocation(1, 3));
+ ASSERT_FALSE(heap_walker.Allocation(1, 4));
+ ASSERT_FALSE(heap_walker.Allocation(1, 5));
+ ASSERT_FALSE(heap_walker.Allocation(3, 4));
+ ASSERT_FALSE(heap_walker.Allocation(3, 5));
+ ASSERT_TRUE(heap_walker.Allocation(4, 5));
+ ASSERT_TRUE(heap_walker.Allocation(1, 2));
+}
+
+TEST_F(HeapWalkerTest, zero) {
+ HeapWalker heap_walker(heap_);
+ ASSERT_TRUE(heap_walker.Allocation(2, 2));
+ ASSERT_FALSE(heap_walker.Allocation(2, 2));
+ ASSERT_TRUE(heap_walker.Allocation(3, 3));
+ ASSERT_TRUE(heap_walker.Allocation(1, 1));
+ ASSERT_FALSE(heap_walker.Allocation(2, 3));
+}
+
+#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(buffer)
+#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(buffer) + sizeof(buffer))
+
+TEST_F(HeapWalkerTest, leak) {
+ void* buffer1[16]{};
+ char buffer2[16]{};
+ buffer1[0] = &buffer2[0] - sizeof(void*);
+ buffer1[1] = &buffer2[15] + sizeof(void*);
+
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(1U, num_leaks);
+ EXPECT_EQ(16U, leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(buffer_begin(buffer2), leaked[0].begin);
+ EXPECT_EQ(buffer_end(buffer2), leaked[0].end);
+}
+
+TEST_F(HeapWalkerTest, live) {
+ const int from_buffer_entries = 4;
+ const int to_buffer_bytes = 16;
+
+ for (int i = 0; i < from_buffer_entries; i++) {
+ for (int j = 0; j < to_buffer_bytes; j++) {
+ void* buffer1[from_buffer_entries]{};
+ char buffer2[to_buffer_bytes]{};
+ buffer1[i] = &buffer2[j];
+
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+ heap_walker.Root(buffer_begin(buffer1), buffer_end(buffer1));
+
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = SIZE_MAX;
+ size_t leaked_bytes = SIZE_MAX;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(0U, num_leaks);
+ EXPECT_EQ(0U, leaked_bytes);
+ EXPECT_EQ(0U, leaked.size());
+ }
+ }
+}
+
+TEST_F(HeapWalkerTest, unaligned) {
+ const int from_buffer_entries = 4;
+ const int to_buffer_bytes = 16;
+ void* buffer1[from_buffer_entries]{};
+ char buffer2[to_buffer_bytes]{};
+
+ buffer1[1] = &buffer2;
+
+ for (unsigned int i = 0; i < sizeof(uintptr_t); i++) {
+ for (unsigned int j = 0; j < sizeof(uintptr_t); j++) {
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+ heap_walker.Root(buffer_begin(buffer1) + i, buffer_end(buffer1) - j);
+
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = SIZE_MAX;
+ size_t leaked_bytes = SIZE_MAX;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(0U, num_leaks);
+ EXPECT_EQ(0U, leaked_bytes);
+ EXPECT_EQ(0U, leaked.size());
+ }
+ }
+}
+
+TEST_F(HeapWalkerTest, cycle) {
+ void* buffer1;
+ void* buffer2;
+
+ buffer1 = &buffer2;
+ buffer2 = &buffer1;
+
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer1), buffer_end(buffer1));
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(2U, num_leaks);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+}
diff --git a/libmemunreachable/tests/HostMallocStub.cpp b/libmemunreachable/tests/HostMallocStub.cpp
new file mode 100644
index 0000000..a7e3f07
--- /dev/null
+++ b/libmemunreachable/tests/HostMallocStub.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 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 "bionic.h"
+
+void malloc_disable() {
+}
+
+void malloc_enable() {
+}
diff --git a/libmemunreachable/tests/LeakFolding_test.cpp b/libmemunreachable/tests/LeakFolding_test.cpp
new file mode 100644
index 0000000..879a3a0
--- /dev/null
+++ b/libmemunreachable/tests/LeakFolding_test.cpp
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2016 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 "HeapWalker.h"
+#include "LeakFolding.h"
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+#include "Allocator.h"
+
+class LeakFoldingTest : public ::testing::Test {
+ public:
+ LeakFoldingTest() : disable_malloc_(), heap_() {}
+
+ void TearDown() {
+ ASSERT_TRUE(heap_.empty());
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc_.timed_out());
+ }
+ }
+
+ protected:
+ ScopedDisableMallocTimeout disable_malloc_;
+ Heap heap_;
+};
+
+#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(&buffer[0])
+#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(&buffer[0]) + sizeof(buffer))
+#define ALLOCATION(heap_walker, buffer) \
+ ASSERT_EQ(true, heap_walker.Allocation(buffer_begin(buffer), buffer_end(buffer)))
+
+TEST_F(LeakFoldingTest, one) {
+ void* buffer1[1] = {nullptr};
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(1U, num_leaks);
+ EXPECT_EQ(sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(0U, leaked[0].referenced_count);
+ EXPECT_EQ(0U, leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two) {
+ void* buffer1[1] = {nullptr};
+ void* buffer2[1] = {nullptr};
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(2U, num_leaks);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+ EXPECT_EQ(0U, leaked[0].referenced_count);
+ EXPECT_EQ(0U, leaked[0].referenced_size);
+ EXPECT_EQ(0U, leaked[1].referenced_count);
+ EXPECT_EQ(0U, leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, dominator) {
+ void* buffer1[1];
+ void* buffer2[1] = {nullptr};
+
+ buffer1[0] = buffer2;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(2U, num_leaks);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(1U, leaked[0].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, cycle) {
+ void* buffer1[1];
+ void* buffer2[1];
+ void* buffer3[1];
+
+ buffer1[0] = buffer2;
+ buffer2[0] = buffer3;
+ buffer3[0] = buffer2;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(3U, num_leaks);
+ EXPECT_EQ(3*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(2U, leaked[0].referenced_count);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, dominator_cycle) {
+ void* buffer1[2] = {nullptr, nullptr};
+ void* buffer2[2];
+ void* buffer3[1] = {nullptr};
+
+ buffer1[0] = &buffer2;
+ buffer2[0] = &buffer1;
+ buffer2[1] = &buffer3;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(3U, num_leaks);
+ EXPECT_EQ(5*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+
+ EXPECT_EQ(2U, leaked[0].referenced_count);
+ EXPECT_EQ(3*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(2U, leaked[1].referenced_count);
+ EXPECT_EQ(3*sizeof(uintptr_t), leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two_cycles) {
+ void* buffer1[1];
+ void* buffer2[1];
+ void* buffer3[1];
+ void* buffer4[1];
+ void* buffer5[1];
+ void* buffer6[1];
+
+ buffer1[0] = buffer3;
+ buffer2[0] = buffer5;
+ buffer3[0] = buffer4;
+ buffer4[0] = buffer3;
+ buffer5[0] = buffer6;
+ buffer6[0] = buffer5;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+ ALLOCATION(heap_walker, buffer5);
+ ALLOCATION(heap_walker, buffer6);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(6U, num_leaks);
+ EXPECT_EQ(6*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+ EXPECT_EQ(2U, leaked[0].referenced_count);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(2U, leaked[1].referenced_count);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two_dominator_cycles) {
+ void* buffer1[1];
+ void* buffer2[1];
+ void* buffer3[1];
+ void* buffer4[1];
+
+ buffer1[0] = buffer2;
+ buffer2[0] = buffer1;
+ buffer3[0] = buffer4;
+ buffer4[0] = buffer3;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(4U, num_leaks);
+ EXPECT_EQ(4*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(4U, leaked.size());
+ EXPECT_EQ(1U, leaked[0].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(1U, leaked[1].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[1].referenced_size);
+ EXPECT_EQ(1U, leaked[2].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[2].referenced_size);
+ EXPECT_EQ(1U, leaked[3].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[3].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, giant_dominator_cycle) {
+ const size_t n = 1000;
+ void* buffer[n];
+
+ HeapWalker heap_walker(heap_);
+
+ for (size_t i = 0; i < n; i ++) {
+ ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
+ reinterpret_cast<uintptr_t>(&buffer[i+1])));
+ }
+
+ for (size_t i = 0; i < n - 1; i++) {
+ buffer[i] = &buffer[i+1];
+ }
+ buffer[n - 1] = &buffer[0];
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(n, num_leaks);
+ EXPECT_EQ(n * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1000U, leaked.size());
+ EXPECT_EQ(n - 1, leaked[0].referenced_count);
+ EXPECT_EQ((n - 1) * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, giant_cycle) {
+ const size_t n = 1000;
+ void* buffer[n];
+ void* buffer1[1];
+
+ HeapWalker heap_walker(heap_);
+
+ for (size_t i = 0; i < n - 1; i++) {
+ buffer[i] = &buffer[i+1];
+ }
+ buffer[n - 1] = &buffer[0];
+
+ buffer1[0] = &buffer[0];
+
+ for (size_t i = 0; i < n; i ++) {
+ ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
+ reinterpret_cast<uintptr_t>(&buffer[i+1])));
+ }
+
+ ALLOCATION(heap_walker, buffer1);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(n + 1, num_leaks);
+ EXPECT_EQ((n + 1) * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(n, leaked[0].referenced_count);
+ EXPECT_EQ(n * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, multipath) {
+ void* buffer1[2];
+ void* buffer2[1];
+ void* buffer3[1];
+ void* buffer4[1] = {nullptr};
+
+ // 1
+ // / \
+ // v v
+ // 2 3
+ // \ /
+ // v
+ // 4
+
+ buffer1[0] = &buffer2;
+ buffer1[1] = &buffer3;
+ buffer2[0] = &buffer4;
+ buffer3[0] = &buffer4;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(4U, num_leaks);
+ EXPECT_EQ(5 * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(3U, leaked[0].referenced_count);
+ EXPECT_EQ(3 * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, multicycle) {
+ void* buffer1[2]{};
+ void* buffer2[2]{};
+ void* buffer3[2]{};
+ void* buffer4[2]{};
+
+ // 1
+ // / ^
+ // v \
+ // 2 -> 3
+ // \ ^
+ // v /
+ // 4
+
+ buffer1[0] = &buffer2;
+ buffer2[0] = &buffer3;
+ buffer2[1] = &buffer4;
+ buffer3[0] = &buffer1;
+ buffer4[0] = &buffer3;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(4U, num_leaks);
+ EXPECT_EQ(8 * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(4U, leaked.size());
+ EXPECT_EQ(3U, leaked[0].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(3U, leaked[1].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[1].referenced_size);
+ EXPECT_EQ(3U, leaked[2].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[2].referenced_size);
+ EXPECT_EQ(3U, leaked[3].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[3].referenced_size);
+}
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
new file mode 100644
index 0000000..0747b12
--- /dev/null
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2016 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 <stdlib.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+
+#include <gtest/gtest.h>
+
+#include <memunreachable/memunreachable.h>
+
+void* ptr;
+
+class HiddenPointer {
+ public:
+ HiddenPointer(size_t size = 256) {
+ Set(malloc(size));
+ }
+ ~HiddenPointer() {
+ Free();
+ }
+ void* Get() {
+ return reinterpret_cast<void*>(~ptr_);
+ }
+ void Free() {
+ free(Get());
+ Set(nullptr);
+ }
+ private:
+ void Set(void* ptr) {
+ ptr_ = ~reinterpret_cast<uintptr_t>(ptr);
+ }
+ volatile uintptr_t ptr_;
+};
+
+static void Ref(void* ptr) {
+ write(0, ptr, 0);
+}
+
+TEST(MemunreachableTest, clean) {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+}
+
+TEST(MemunreachableTest, stack) {
+ HiddenPointer hidden_ptr;
+
+ {
+ void* ptr = hidden_ptr.Get();
+ Ref(ptr);
+
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+
+ Ref(ptr);
+ }
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, global) {
+ HiddenPointer hidden_ptr;
+
+ ptr = hidden_ptr.Get();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+
+ ptr = NULL;
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, tls) {
+ HiddenPointer hidden_ptr;
+ pthread_key_t key;
+ pthread_key_create(&key, NULL);
+
+ pthread_setspecific(key, hidden_ptr.Get());
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+
+ pthread_setspecific(key, nullptr);
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+
+ pthread_key_delete(key);
+}
+
+TEST(MemunreachableTest, twice) {
+ HiddenPointer hidden_ptr;
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, log) {
+ HiddenPointer hidden_ptr;
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, notdumpable) {
+ ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 0));
+
+ HiddenPointer hidden_ptr;
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+ ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1));
+}
+
+TEST(MemunreachableTest, leak_lots) {
+ std::vector<HiddenPointer> hidden_ptrs;
+ hidden_ptrs.resize(1024);
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+}
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
new file mode 100644
index 0000000..cefe94e
--- /dev/null
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2016 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 "ThreadCapture.h"
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include <android-base/unique_fd.h>
+
+#include "Allocator.h"
+#include "ScopedDisableMalloc.h"
+#include "ScopedPipe.h"
+
+using namespace std::chrono_literals;
+
+class ThreadListTest : public ::testing::TestWithParam<int> {
+ public:
+ ThreadListTest() : stop_(false) {}
+
+ ~ThreadListTest() {
+ // pthread_join may return before the entry in /proc/pid/task/ is gone,
+ // loop until ListThreads only finds the main thread so the next test
+ // doesn't fail.
+ WaitForThreads();
+ }
+
+ virtual void TearDown() {
+ ASSERT_TRUE(heap.empty());
+ }
+
+ protected:
+ template<class Function>
+ void StartThreads(unsigned int threads, Function&& func) {
+ threads_.reserve(threads);
+ tids_.reserve(threads);
+ for (unsigned int i = 0; i < threads; i++) {
+ threads_.emplace_back([&, i, threads, this]() {
+ {
+ std::lock_guard<std::mutex> lk(m_);
+ tids_.push_back(gettid());
+ if (tids_.size() == threads) {
+ cv_start_.notify_one();
+ }
+ }
+
+ func();
+
+ {
+ std::unique_lock<std::mutex> lk(m_);
+ cv_stop_.wait(lk, [&] {return stop_;});
+ }
+ });
+ }
+
+ {
+ std::unique_lock<std::mutex> lk(m_);
+ cv_start_.wait(lk, [&]{ return tids_.size() == threads; });
+ }
+ }
+
+ void StopThreads() {
+ {
+ std::lock_guard<std::mutex> lk(m_);
+ stop_ = true;
+ }
+ cv_stop_.notify_all();
+
+ for (auto i = threads_.begin(); i != threads_.end(); i++) {
+ i->join();
+ }
+ threads_.clear();
+ tids_.clear();
+ }
+
+ std::vector<pid_t>& tids() {
+ return tids_;
+ }
+
+ Heap heap;
+
+ private:
+ void WaitForThreads() {
+ auto tids = TidList{heap};
+ ThreadCapture thread_capture{getpid(), heap};
+
+ for (unsigned int i = 0; i < 100; i++) {
+ EXPECT_TRUE(thread_capture.ListThreads(tids));
+ if (tids.size() == 1) {
+ break;
+ }
+ std::this_thread::sleep_for(10ms);
+ }
+ EXPECT_EQ(1U, tids.size());
+ }
+
+ std::mutex m_;
+ std::condition_variable cv_start_;
+ std::condition_variable cv_stop_;
+ bool stop_;
+ std::vector<pid_t> tids_;
+
+ std::vector<std::thread> threads_;
+};
+
+TEST_F(ThreadListTest, list_one) {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(getpid(), heap);
+
+ auto expected_tids = allocator::vector<pid_t>(1, getpid(), heap);
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+
+ ASSERT_EQ(expected_tids, list_tids);
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+}
+
+TEST_P(ThreadListTest, list_some) {
+ const unsigned int threads = GetParam() - 1;
+
+ StartThreads(threads, [](){});
+ std::vector<pid_t> expected_tids = tids();
+ expected_tids.push_back(getpid());
+
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(getpid(), heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+
+ StopThreads();
+
+ std::sort(list_tids.begin(), list_tids.end());
+ std::sort(expected_tids.begin(), expected_tids.end());
+
+ ASSERT_EQ(expected_tids.size(), list_tids.size());
+ EXPECT_TRUE(std::equal(expected_tids.begin(), expected_tids.end(), list_tids.begin()));
+}
+
+INSTANTIATE_TEST_CASE_P(ThreadListTest, ThreadListTest, ::testing::Values(1, 2, 10, 1024));
+
+class ThreadCaptureTest : public ThreadListTest {
+ public:
+ ThreadCaptureTest() {}
+ ~ThreadCaptureTest() {}
+ void Fork(std::function<void()>&& child_init,
+ std::function<void()>&& child_cleanup,
+ std::function<void(pid_t)>&& parent) {
+
+ ScopedPipe start_pipe;
+ ScopedPipe stop_pipe;
+
+ int pid = fork();
+
+ if (pid == 0) {
+ // child
+ child_init();
+ EXPECT_EQ(1, TEMP_FAILURE_RETRY(write(start_pipe.Sender(), "+", 1))) << strerror(errno);
+ char buf;
+ EXPECT_EQ(1, TEMP_FAILURE_RETRY(read(stop_pipe.Receiver(), &buf, 1))) << strerror(errno);
+ child_cleanup();
+ _exit(0);
+ } else {
+ // parent
+ ASSERT_GT(pid, 0);
+ char buf;
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(start_pipe.Receiver(), &buf, 1))) << strerror(errno);
+
+ parent(pid);
+
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(stop_pipe.Sender(), "+", 1))) << strerror(errno);
+ siginfo_t info{};
+ ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) << strerror(errno);
+ }
+ }
+};
+
+TEST_P(ThreadCaptureTest, capture_some) {
+ const unsigned int threads = GetParam();
+
+ Fork([&](){
+ // child init
+ StartThreads(threads - 1, [](){});
+ },
+ [&](){
+ // child cleanup
+ StopThreads();
+ },
+ [&](pid_t child){
+ // parent
+ ASSERT_GT(child, 0);
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(child, heap);
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(threads, list_tids.size());
+
+ ASSERT_TRUE(thread_capture.CaptureThreads());
+
+ auto thread_info = allocator::vector<ThreadInfo>(heap);
+ ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+ ASSERT_EQ(threads, thread_info.size());
+ ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+}
+ });
+}
+
+INSTANTIATE_TEST_CASE_P(ThreadCaptureTest, ThreadCaptureTest, ::testing::Values(1, 2, 10, 1024));
+
+TEST_F(ThreadCaptureTest, capture_kill) {
+ int ret = fork();
+
+ if (ret == 0) {
+ // child
+ sleep(10);
+ } else {
+ // parent
+ ASSERT_GT(ret, 0);
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(ret, heap);
+ thread_capture.InjectTestFunc([&](pid_t tid){
+ syscall(SYS_tgkill, ret, tid, SIGKILL);
+ usleep(10000);
+ });
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(1U, list_tids.size());
+
+ ASSERT_FALSE(thread_capture.CaptureThreads());
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+ }
+}
+
+TEST_F(ThreadCaptureTest, capture_signal) {
+ const int sig = SIGUSR1;
+
+ ScopedPipe pipe;
+
+ // For signal handler
+ static ScopedPipe* g_pipe;
+
+ Fork([&](){
+ // child init
+ pipe.CloseReceiver();
+
+ g_pipe = &pipe;
+
+ struct sigaction act{};
+ act.sa_handler = [](int){
+ char buf = '+';
+ write(g_pipe->Sender(), &buf, 1);
+ g_pipe->CloseSender();
+ };
+ sigaction(sig, &act, NULL);
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+ },
+ [&](){
+ // child cleanup
+ g_pipe = nullptr;
+ pipe.Close();
+ },
+ [&](pid_t child){
+ // parent
+ ASSERT_GT(child, 0);
+ pipe.CloseSender();
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(child, heap);
+ thread_capture.InjectTestFunc([&](pid_t tid){
+ syscall(SYS_tgkill, child, tid, sig);
+ usleep(10000);
+ });
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(1U, list_tids.size());
+
+ ASSERT_TRUE(thread_capture.CaptureThreads());
+
+ auto thread_info = allocator::vector<ThreadInfo>(heap);
+ ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+ ASSERT_EQ(1U, thread_info.size());
+ ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+ usleep(100000);
+ char buf;
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
+ ASSERT_EQ(buf, '+');
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+ });
+}
diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk
index 7906986..09fbbb1 100644
--- a/libmincrypt/Android.mk
+++ b/libmincrypt/Android.mk
@@ -14,5 +14,4 @@
LOCAL_CFLAGS := -Wall -Werror
include $(BUILD_HOST_STATIC_LIBRARY)
-include $(LOCAL_PATH)/tools/Android.mk \
- $(LOCAL_PATH)/test/Android.mk
+include $(LOCAL_PATH)/test/Android.mk
diff --git a/libmincrypt/tools/Android.mk b/libmincrypt/tools/Android.mk
deleted file mode 100644
index 3154914..0000000
--- a/libmincrypt/tools/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := dumpkey
-LOCAL_SRC_FILES := DumpPublicKey.java
-LOCAL_JAR_MANIFEST := DumpPublicKey.mf
-LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java
deleted file mode 100644
index 3eb1398..0000000
--- a/libmincrypt/tools/DumpPublicKey.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.dumpkey;
-
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-import java.io.FileInputStream;
-import java.math.BigInteger;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.KeyStore;
-import java.security.Key;
-import java.security.PublicKey;
-import java.security.Security;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.ECPoint;
-
-/**
- * Command line tool to extract RSA public keys from X.509 certificates
- * and output source code with data initializers for the keys.
- * @hide
- */
-class DumpPublicKey {
- /**
- * @param key to perform sanity checks on
- * @return version number of key. Supported versions are:
- * 1: 2048-bit RSA key with e=3 and SHA-1 hash
- * 2: 2048-bit RSA key with e=65537 and SHA-1 hash
- * 3: 2048-bit RSA key with e=3 and SHA-256 hash
- * 4: 2048-bit RSA key with e=65537 and SHA-256 hash
- * @throws Exception if the key has the wrong size or public exponent
- */
- static int checkRSA(RSAPublicKey key, boolean useSHA256) throws Exception {
- BigInteger pubexp = key.getPublicExponent();
- BigInteger modulus = key.getModulus();
- int version;
-
- if (pubexp.equals(BigInteger.valueOf(3))) {
- version = useSHA256 ? 3 : 1;
- } else if (pubexp.equals(BigInteger.valueOf(65537))) {
- version = useSHA256 ? 4 : 2;
- } else {
- throw new Exception("Public exponent should be 3 or 65537 but is " +
- pubexp.toString(10) + ".");
- }
-
- if (modulus.bitLength() != 2048) {
- throw new Exception("Modulus should be 2048 bits long but is " +
- modulus.bitLength() + " bits.");
- }
-
- return version;
- }
-
- /**
- * @param key to perform sanity checks on
- * @return version number of key. Supported versions are:
- * 5: 256-bit EC key with curve NIST P-256
- * @throws Exception if the key has the wrong size or public exponent
- */
- static int checkEC(ECPublicKey key) throws Exception {
- if (key.getParams().getCurve().getField().getFieldSize() != 256) {
- throw new Exception("Curve must be NIST P-256");
- }
-
- return 5;
- }
-
- /**
- * Perform sanity check on public key.
- */
- static int check(PublicKey key, boolean useSHA256) throws Exception {
- if (key instanceof RSAPublicKey) {
- return checkRSA((RSAPublicKey) key, useSHA256);
- } else if (key instanceof ECPublicKey) {
- if (!useSHA256) {
- throw new Exception("Must use SHA-256 with EC keys!");
- }
- return checkEC((ECPublicKey) key);
- } else {
- throw new Exception("Unsupported key class: " + key.getClass().getName());
- }
- }
-
- /**
- * @param key to output
- * @return a String representing this public key. If the key is a
- * version 1 key, the string will be a C initializer; this is
- * not true for newer key versions.
- */
- static String printRSA(RSAPublicKey key, boolean useSHA256) throws Exception {
- int version = check(key, useSHA256);
-
- BigInteger N = key.getModulus();
-
- StringBuilder result = new StringBuilder();
-
- int nwords = N.bitLength() / 32; // # of 32 bit integers in modulus
-
- if (version > 1) {
- result.append("v");
- result.append(Integer.toString(version));
- result.append(" ");
- }
-
- result.append("{");
- result.append(nwords);
-
- BigInteger B = BigInteger.valueOf(0x100000000L); // 2^32
- BigInteger N0inv = B.subtract(N.modInverse(B)); // -1 / N[0] mod 2^32
-
- result.append(",0x");
- result.append(N0inv.toString(16));
-
- BigInteger R = BigInteger.valueOf(2).pow(N.bitLength());
- BigInteger RR = R.multiply(R).mod(N); // 2^4096 mod N
-
- // Write out modulus as little endian array of integers.
- result.append(",{");
- for (int i = 0; i < nwords; ++i) {
- long n = N.mod(B).longValue();
- result.append(n);
-
- if (i != nwords - 1) {
- result.append(",");
- }
-
- N = N.divide(B);
- }
- result.append("}");
-
- // Write R^2 as little endian array of integers.
- result.append(",{");
- for (int i = 0; i < nwords; ++i) {
- long rr = RR.mod(B).longValue();
- result.append(rr);
-
- if (i != nwords - 1) {
- result.append(",");
- }
-
- RR = RR.divide(B);
- }
- result.append("}");
-
- result.append("}");
- return result.toString();
- }
-
- /**
- * @param key to output
- * @return a String representing this public key. If the key is a
- * version 1 key, the string will be a C initializer; this is
- * not true for newer key versions.
- */
- static String printEC(ECPublicKey key) throws Exception {
- int version = checkEC(key);
-
- StringBuilder result = new StringBuilder();
-
- result.append("v");
- result.append(Integer.toString(version));
- result.append(" ");
-
- BigInteger X = key.getW().getAffineX();
- BigInteger Y = key.getW().getAffineY();
- int nbytes = key.getParams().getCurve().getField().getFieldSize() / 8; // # of 32 bit integers in X coordinate
-
- result.append("{");
- result.append(nbytes);
-
- BigInteger B = BigInteger.valueOf(0x100L); // 2^8
-
- // Write out Y coordinate as array of characters.
- result.append(",{");
- for (int i = 0; i < nbytes; ++i) {
- long n = X.mod(B).longValue();
- result.append(n);
-
- if (i != nbytes - 1) {
- result.append(",");
- }
-
- X = X.divide(B);
- }
- result.append("}");
-
- // Write out Y coordinate as array of characters.
- result.append(",{");
- for (int i = 0; i < nbytes; ++i) {
- long n = Y.mod(B).longValue();
- result.append(n);
-
- if (i != nbytes - 1) {
- result.append(",");
- }
-
- Y = Y.divide(B);
- }
- result.append("}");
-
- result.append("}");
- return result.toString();
- }
-
- static String print(PublicKey key, boolean useSHA256) throws Exception {
- if (key instanceof RSAPublicKey) {
- return printRSA((RSAPublicKey) key, useSHA256);
- } else if (key instanceof ECPublicKey) {
- return printEC((ECPublicKey) key);
- } else {
- throw new Exception("Unsupported key class: " + key.getClass().getName());
- }
- }
-
- public static void main(String[] args) {
- if (args.length < 1) {
- System.err.println("Usage: DumpPublicKey certfile ... > source.c");
- System.exit(1);
- }
- Security.addProvider(new BouncyCastleProvider());
- try {
- for (int i = 0; i < args.length; i++) {
- FileInputStream input = new FileInputStream(args[i]);
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- X509Certificate cert = (X509Certificate) cf.generateCertificate(input);
-
- boolean useSHA256 = false;
- String sigAlg = cert.getSigAlgName();
- if ("SHA1withRSA".equals(sigAlg) || "MD5withRSA".equals(sigAlg)) {
- // SignApk has historically accepted "MD5withRSA"
- // certificates, but treated them as "SHA1withRSA"
- // anyway. Continue to do so for backwards
- // compatibility.
- useSHA256 = false;
- } else if ("SHA256withRSA".equals(sigAlg) || "SHA256withECDSA".equals(sigAlg)) {
- useSHA256 = true;
- } else {
- System.err.println(args[i] + ": unsupported signature algorithm \"" +
- sigAlg + "\"");
- System.exit(1);
- }
-
- PublicKey key = cert.getPublicKey();
- check(key, useSHA256);
- System.out.print(print(key, useSHA256));
- System.out.println(i < args.length - 1 ? "," : "");
- }
- } catch (Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- System.exit(0);
- }
-}
diff --git a/libmincrypt/tools/DumpPublicKey.mf b/libmincrypt/tools/DumpPublicKey.mf
deleted file mode 100644
index 7bb3bc8..0000000
--- a/libmincrypt/tools/DumpPublicKey.mf
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.dumpkey.DumpPublicKey
diff --git a/libnativeloader/Android.mk b/libnativeloader/Android.mk
index 5e65c4c..6c064c7 100644
--- a/libnativeloader/Android.mk
+++ b/libnativeloader/Android.mk
@@ -17,7 +17,8 @@
LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
LOCAL_LDFLAGS := -ldl
LOCAL_MULTILIB := both
-
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include $(BUILD_SHARED_LIBRARY)
# Shared library for host
@@ -34,7 +35,8 @@
LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
LOCAL_LDFLAGS := -ldl
LOCAL_MULTILIB := both
-
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include $(BUILD_HOST_SHARED_LIBRARY)
# Static library for host
@@ -50,5 +52,6 @@
LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
LOCAL_LDFLAGS := -ldl
LOCAL_MULTILIB := both
-
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
new file mode 100644
index 0000000..b16c0e6
--- /dev/null
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NATIVE_LOADER_H_
+#define NATIVE_LOADER_H_
+
+#include "jni.h"
+#include <stdint.h>
+#if defined(__ANDROID__)
+#include <android/dlext.h>
+#endif
+
+namespace android {
+
+__attribute__((visibility("default")))
+void PreloadPublicNativeLibraries();
+
+__attribute__((visibility("default")))
+jstring CreateClassLoaderNamespace(JNIEnv* env,
+ int32_t target_sdk_version,
+ jobject class_loader,
+ bool is_shared,
+ jstring library_path,
+ jstring permitted_path);
+
+__attribute__((visibility("default")))
+void* OpenNativeLibrary(JNIEnv* env,
+ int32_t target_sdk_version,
+ const char* path,
+ jobject class_loader,
+ jstring library_path);
+
+#if defined(__ANDROID__)
+// Look up linker namespace by class_loader. Returns nullptr if
+// there is no namespace associated with the class_loader.
+__attribute__((visibility("default")))
+android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+#endif
+
+}; // namespace android
+
+#endif // NATIVE_BRIDGE_H_
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 209ff1c..86d9d77 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -21,6 +21,7 @@
#ifdef __ANDROID__
#include <android/dlext.h>
#include "cutils/properties.h"
+#include "log/log.h"
#endif
#include <algorithm>
@@ -59,10 +60,11 @@
public:
LibraryNamespaces() : initialized_(false) { }
- android_namespace_t* GetOrCreate(JNIEnv* env, jobject class_loader,
- bool is_shared,
- jstring java_library_path,
- jstring java_permitted_path) {
+ android_namespace_t* Create(JNIEnv* env,
+ jobject class_loader,
+ bool is_shared,
+ jstring java_library_path,
+ jstring java_permitted_path) {
ScopedUtfChars library_path(env, java_library_path);
std::string permitted_path;
@@ -75,43 +77,49 @@
return nullptr;
}
- std::lock_guard<std::mutex> guard(mutex_);
+ android_namespace_t* ns = FindNamespaceByClassLoader(env, class_loader);
- auto it = FindNamespaceByClassLoader(env, class_loader);
-
- if (it != namespaces_.end()) {
- return it->second;
- }
+ LOG_FATAL_IF(ns != nullptr, "There is already a namespace associated with this classloader");
uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
if (is_shared) {
namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
}
- android_namespace_t* ns =
- android_create_namespace("classloader-namespace",
- nullptr,
- library_path.c_str(),
- namespace_type,
- java_permitted_path != nullptr ?
- permitted_path.c_str() :
- nullptr);
+ ns = android_create_namespace("classloader-namespace",
+ nullptr,
+ library_path.c_str(),
+ namespace_type,
+ java_permitted_path != nullptr ?
+ permitted_path.c_str() :
+ nullptr);
- namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), ns));
+ if (ns != nullptr) {
+ namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), ns));
+ }
return ns;
}
- private:
- bool InitPublicNamespace(const char* library_path) {
- // Make sure all the public libraries are loaded
+ android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+ auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
+ [&](const std::pair<jweak, android_namespace_t*>& value) {
+ return env->IsSameObject(value.first, class_loader);
+ });
+ return it != namespaces_.end() ? it->second : nullptr;
+ }
+
+ void PreloadPublicLibraries() {
+ // android_init_namespaces() expects all the public libraries
+ // to be loaded so that they can be found by soname alone.
std::vector<std::string> sonames = android::base::Split(kPublicNativeLibraries, ":");
for (const auto& soname : sonames) {
- if (dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr) {
- return false;
- }
+ dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE);
}
+ }
+ private:
+ bool InitPublicNamespace(const char* library_path) {
// Some apps call dlopen from generated code unknown to linker in which
// case linker uses anonymous namespace. See b/25844435 for details.
initialized_ = android_init_namespaces(kPublicNativeLibraries, library_path);
@@ -119,39 +127,75 @@
return initialized_;
}
- std::vector<std::pair<jweak, android_namespace_t*>>::const_iterator
- FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
- return std::find_if(namespaces_.begin(), namespaces_.end(),
- [&](const std::pair<jweak, android_namespace_t*>& value) {
- return env->IsSameObject(value.first, class_loader);
- });
- }
-
bool initialized_;
- std::mutex mutex_;
std::vector<std::pair<jweak, android_namespace_t*>> namespaces_;
DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
};
+static std::mutex g_namespaces_mutex;
static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
+
+static bool namespaces_enabled(uint32_t target_sdk_version) {
+ return target_sdk_version > 0;
+}
#endif
-
-void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
- jobject class_loader, bool is_shared, jstring java_library_path,
- jstring java_permitted_path) {
+void PreloadPublicNativeLibraries() {
#if defined(__ANDROID__)
- if (target_sdk_version == 0 || class_loader == nullptr) {
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ g_namespaces->PreloadPublicLibraries();
+#endif
+}
+
+
+jstring CreateClassLoaderNamespace(JNIEnv* env,
+ int32_t target_sdk_version,
+ jobject class_loader,
+ bool is_shared,
+ jstring library_path,
+ jstring permitted_path) {
+#if defined(__ANDROID__)
+ if (!namespaces_enabled(target_sdk_version)) {
+ return nullptr;
+ }
+
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ android_namespace_t* ns = g_namespaces->Create(env,
+ class_loader,
+ is_shared,
+ library_path,
+ permitted_path);
+ if (ns == nullptr) {
+ return env->NewStringUTF(dlerror());
+ }
+#else
+ UNUSED(env, target_sdk_version, class_loader, is_shared,
+ library_path, permitted_path);
+#endif
+ return nullptr;
+}
+
+void* OpenNativeLibrary(JNIEnv* env,
+ int32_t target_sdk_version,
+ const char* path,
+ jobject class_loader,
+ jstring library_path) {
+#if defined(__ANDROID__)
+ if (!namespaces_enabled(target_sdk_version) || class_loader == nullptr) {
return dlopen(path, RTLD_NOW);
}
- android_namespace_t* ns =
- g_namespaces->GetOrCreate(env, class_loader, is_shared,
- java_library_path, java_permitted_path);
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ android_namespace_t* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
if (ns == nullptr) {
- return nullptr;
+ // This is the case where the classloader was not created by ApplicationLoaders
+ // In this case we create an isolated not-shared namespace for it.
+ ns = g_namespaces->Create(env, class_loader, false, library_path, nullptr);
+ if (ns == nullptr) {
+ return nullptr;
+ }
}
android_dlextinfo extinfo;
@@ -160,10 +204,16 @@
return android_dlopen_ext(path, RTLD_NOW, &extinfo);
#else
- UNUSED(env, target_sdk_version, class_loader, is_shared,
- java_library_path, java_permitted_path);
+ UNUSED(env, target_sdk_version, class_loader, library_path);
return dlopen(path, RTLD_NOW);
#endif
}
+#if defined(__ANDROID__)
+android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+ std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+ return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+}
+#endif
+
}; // android namespace
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
index 2060df4..281b6c8 100644
--- a/libnetutils/Android.mk
+++ b/libnetutils/Android.mk
@@ -16,6 +16,9 @@
LOCAL_CFLAGS := -Werror
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
diff --git a/include/netutils/dhcp.h b/libnetutils/include/netutils/dhcp.h
similarity index 100%
rename from include/netutils/dhcp.h
rename to libnetutils/include/netutils/dhcp.h
diff --git a/include/netutils/ifc.h b/libnetutils/include/netutils/ifc.h
similarity index 100%
rename from include/netutils/ifc.h
rename to libnetutils/include/netutils/ifc.h
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
index 16052e2..e309027 100644
--- a/libpackagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -27,7 +27,7 @@
#include <sys/limits.h>
#define LOG_TAG "packagelistparser"
-#include <utils/Log.h>
+#include <cutils/log.h>
#include <packagelistparser/packagelistparser.h>
diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h
index 9db20df..ecc242a 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.h
+++ b/libpixelflinger/codeflinger/GGLAssembler.h
@@ -288,6 +288,14 @@
private:
+ // GGLAssembler hides RegisterAllocator's and ARMAssemblerProxy's reset
+ // methods by providing a reset method with a different parameter set. The
+ // intent of GGLAssembler's reset method is to wrap the inherited reset
+ // methods, so make these methods private in order to prevent direct calls
+ // to these methods from clients.
+ using RegisterAllocator::reset;
+ using ARMAssemblerProxy::reset;
+
struct tex_coord_t {
reg_t s;
reg_t t;
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index ad0500d..00a0357 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -28,6 +28,7 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <memory>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -162,11 +163,11 @@
static void removeUidProcessGroups(const char *uid_path)
{
- DIR *uid = opendir(uid_path);
+ std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path), closedir);
if (uid != NULL) {
struct dirent cur;
struct dirent *dir;
- while ((readdir_r(uid, &cur, &dir) == 0) && dir) {
+ while ((readdir_r(uid.get(), &cur, &dir) == 0) && dir) {
char path[PROCESSGROUP_MAX_PATH_LEN];
if (dir->d_type != DT_DIR) {
@@ -181,20 +182,19 @@
SLOGV("removing %s\n", path);
rmdir(path);
}
- closedir(uid);
}
}
void removeAllProcessGroups()
{
SLOGV("removeAllProcessGroups()");
- DIR *root = opendir(PROCESSGROUP_CGROUP_PATH);
+ std::unique_ptr<DIR, decltype(&closedir)> root(opendir(PROCESSGROUP_CGROUP_PATH), closedir);
if (root == NULL) {
SLOGE("failed to open %s: %s", PROCESSGROUP_CGROUP_PATH, strerror(errno));
} else {
struct dirent cur;
struct dirent *dir;
- while ((readdir_r(root, &cur, &dir) == 0) && dir) {
+ while ((readdir_r(root.get(), &cur, &dir) == 0) && dir) {
char path[PROCESSGROUP_MAX_PATH_LEN];
if (dir->d_type != DT_DIR) {
@@ -209,7 +209,6 @@
SLOGV("removing %s\n", path);
rmdir(path);
}
- closedir(root);
}
}
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
index ee9ea3c..9a5f7d8 100644
--- a/libsync/sync_test.c
+++ b/libsync/sync_test.c
@@ -92,7 +92,7 @@
for (j = 0; j < 2; j++) {
unsigned val = i + j * 3 + 1;
- sprintf(str, "test_fence%d-%d", i, j);
+ snprintf(str, sizeof(str), "test_fence%d-%d", i, j);
int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
if (fd < 0) {
printf("can't create sync pt %d: %s", val, strerror(errno));
@@ -106,7 +106,7 @@
sync_data[3].thread_no = 3;
for (j = 0; j < 2; j++) {
- sprintf(str, "merged_fence%d", j);
+ snprintf(str, sizeof(str), "merged_fence%d", j);
sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
if (sync_data[3].fd[j] < 0) {
printf("can't merge sync pts %d and %d: %s\n",
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 168899c..4d602a6 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -206,7 +206,7 @@
do {
alen = sizeof(ss);
- c = accept(mSock, addrp, &alen);
+ c = accept4(mSock, addrp, &alen, SOCK_CLOEXEC);
SLOGV("%s got %d from accept", mSocketName, c);
} while (c < 0 && errno == EINTR);
if (c < 0) {
@@ -214,7 +214,6 @@
sleep(1);
continue;
}
- fcntl(c, F_SETFD, FD_CLOEXEC);
pthread_mutex_lock(&mClientsLock);
mClients->push_back(new SocketClient(c, true, mUseCmdNum));
pthread_mutex_unlock(&mClientsLock);
diff --git a/libutils/Android.mk b/libutils/Android.mk
index 631b5a3..6f88a6d 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -22,7 +22,6 @@
Log.cpp \
NativeHandle.cpp \
Printer.cpp \
- ProcessCallStack.cpp \
PropertyMap.cpp \
RefBase.cpp \
SharedBuffer.cpp \
@@ -44,7 +43,7 @@
# =====================================================
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= $(commonSources)
-LOCAL_SRC_FILES_linux := Looper.cpp
+LOCAL_SRC_FILES_linux := Looper.cpp ProcessCallStack.cpp
LOCAL_CFLAGS_darwin := -Wno-unused-parameter
LOCAL_MODULE:= libutils
LOCAL_STATIC_LIBRARIES := liblog
@@ -67,12 +66,13 @@
$(commonSources) \
BlobCache.cpp \
Looper.cpp \
+ ProcessCallStack.cpp \
Trace.cpp
ifeq ($(TARGET_ARCH),mips)
LOCAL_CFLAGS += -DALIGN_DOUBLE
endif
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -fvisibility=protected
LOCAL_STATIC_LIBRARIES := \
libcutils \
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index 011c302..4e87a98 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -21,6 +21,7 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
+#include <memory>
#include <utils/Log.h>
#include <utils/Errors.h>
@@ -130,11 +131,10 @@
}
void ProcessCallStack::update() {
- DIR *dp;
struct dirent *ep;
struct dirent entry;
- dp = opendir(PATH_SELF_TASK);
+ std::unique_ptr<DIR, decltype(&closedir)> dp(opendir(PATH_SELF_TASK), closedir);
if (dp == NULL) {
ALOGE("%s: Failed to update the process's call stacks: %s",
__FUNCTION__, strerror(errno));
@@ -146,7 +146,6 @@
clear();
// Get current time.
-#ifndef USE_MINGW
{
time_t t = time(NULL);
struct tm tm;
@@ -160,7 +159,7 @@
* - Read every file in directory => get every tid
*/
int code;
- while ((code = readdir_r(dp, &entry, &ep)) == 0 && ep != NULL) {
+ while ((code = readdir_r(dp.get(), &entry, &ep)) == 0 && ep != NULL) {
pid_t tid = -1;
sscanf(ep->d_name, "%d", &tid);
@@ -199,9 +198,6 @@
ALOGE("%s: Failed to readdir from %s: %s",
__FUNCTION__, PATH_SELF_TASK, strerror(code));
}
-#endif
-
- closedir(dp);
}
void ProcessCallStack::log(const char* logtag, android_LogPriority priority,
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 02907ad..ea1e4db 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -190,17 +190,22 @@
{
Mutex::Autolock _l(mMutex);
char buf[128];
- sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
+ snprintf(buf, sizeof(buf),
+ "Strong references on RefBase %p (weakref_type %p):\n",
+ mBase, this);
text.append(buf);
printRefsLocked(&text, mStrongRefs);
- sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this);
+ snprintf(buf, sizeof(buf),
+ "Weak references on RefBase %p (weakref_type %p):\n",
+ mBase, this);
text.append(buf);
printRefsLocked(&text, mWeakRefs);
}
{
char name[100];
- snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this);
+ snprintf(name, sizeof(name), DEBUG_REFS_CALLSTACK_PATH "/%p.stack",
+ this);
int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
if (rc >= 0) {
write(rc, text.string(), text.length());
@@ -293,8 +298,8 @@
char buf[128];
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
- sprintf(buf, "\t%c ID %p (ref %d):\n",
- inc, refs->id, refs->ref);
+ snprintf(buf, sizeof(buf), "\t%c ID %p (ref %d):\n",
+ inc, refs->id, refs->ref);
out->append(buf);
#if DEBUG_REFS_CALLSTACK_ENABLED
out->append(refs->stack.toString("\t\t"));
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 6a5273f..87eda1b 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -18,7 +18,6 @@
#include <utils/Log.h>
#include <utils/Unicode.h>
-#include <utils/String8.h>
#include <utils/threads.h>
#include <memory.h>
@@ -77,7 +76,7 @@
//printf("Created UTF-16 string from UTF-8 \"%s\":", in);
//printHexData(1, str, buf->size(), 16, 1);
//printf("\n");
-
+
return u16str;
}
@@ -127,7 +126,7 @@
mString = str;
return;
}
-
+
mString = getEmptyString();
}
@@ -142,7 +141,7 @@
mString = str;
return;
}
-
+
mString = getEmptyString();
}
@@ -228,7 +227,7 @@
} else if (otherLen == 0) {
return NO_ERROR;
}
-
+
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize((myLen+otherLen+1)*sizeof(char16_t));
if (buf) {
@@ -249,7 +248,7 @@
} else if (otherLen == 0) {
return NO_ERROR;
}
-
+
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize((myLen+otherLen+1)*sizeof(char16_t));
if (buf) {
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 771d312..ad45282 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -362,7 +362,7 @@
status_t String8::real_append(const char* other, size_t otherLen)
{
const size_t myLen = bytes();
-
+
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize(myLen+otherLen+1);
if (buf) {
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index d9b32f9..24ecf86 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -89,9 +89,9 @@
vector.add(4);
vector.setCapacity(8);
- ASSERT_EQ(8, vector.capacity());
+ ASSERT_EQ(8U, vector.capacity());
vector.setCapacity(2);
- ASSERT_EQ(8, vector.capacity());
+ ASSERT_EQ(8U, vector.capacity());
}
// NOTE: All of the tests below are useless because of the "TODO" above.
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 056b3e1..3cd8b87 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -95,13 +95,12 @@
LOCAL_CFLAGS := $(libziparchive_common_c_flags)
LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(libziparchive_common_cpp_flags)
LOCAL_SRC_FILES := $(libziparchive_test_files)
-LOCAL_SHARED_LIBRARIES := \
- libziparchive-host \
- liblog \
- libbase \
-
LOCAL_STATIC_LIBRARIES := \
- libutils \
+ libziparchive-host \
libz \
+ libbase \
+ libutils \
+ liblog \
+LOCAL_MODULE_HOST_OS := darwin linux windows
include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 3b1e972..a2d6fcc 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -224,9 +224,7 @@
strerror(errno));
return kIoError;
}
- ssize_t actual = TEMP_FAILURE_RETRY(
- read(fd, scan_buffer, static_cast<size_t>(read_amount)));
- if (actual != static_cast<ssize_t>(read_amount)) {
+ if (!android::base::ReadFully(fd, scan_buffer, static_cast<size_t>(read_amount))) {
ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
strerror(errno));
return kIoError;
@@ -481,8 +479,7 @@
static int32_t UpdateEntryFromDataDescriptor(int fd,
ZipEntry *entry) {
uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
- ssize_t actual = TEMP_FAILURE_RETRY(read(fd, ddBuf, sizeof(ddBuf)));
- if (actual != sizeof(ddBuf)) {
+ if (!android::base::ReadFully(fd, ddBuf, sizeof(ddBuf))) {
return kIoError;
}
@@ -498,26 +495,14 @@
}
// Attempts to read |len| bytes into |buf| at offset |off|.
-//
-// This method uses pread64 on platforms that support it and
-// lseek64 + read on platforms that don't. This implies that
-// callers should not rely on the |fd| offset being incremented
+// Callers should not rely on the |fd| offset being incremented
// as a side effect of this call.
-static inline ssize_t ReadAtOffset(int fd, uint8_t* buf, size_t len,
- off64_t off) {
-#if !defined(_WIN32)
- return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
-#else
- // The only supported platform that doesn't support pread at the moment
- // is Windows. Only recent versions of windows support unix like forks,
- // and even there the semantics are quite different.
+static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off) {
if (lseek64(fd, off, SEEK_SET) != off) {
ALOGW("Zip: failed seek to offset %" PRId64, off);
- return kIoError;
+ return false;
}
-
- return TEMP_FAILURE_RETRY(read(fd, buf, len));
-#endif
+ return android::base::ReadFully(fd, buf, len);
}
static int32_t FindEntry(const ZipArchive* archive, const int ent,
@@ -567,9 +552,7 @@
}
uint8_t lfh_buf[sizeof(LocalFileHeader)];
- ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf),
- local_header_offset);
- if (actual != sizeof(lfh_buf)) {
+ if (!ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset)) {
ALOGW("Zip: failed reading lfh name from offset %" PRId64,
static_cast<int64_t>(local_header_offset));
return kIoError;
@@ -610,10 +593,7 @@
}
uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
- ssize_t actual = ReadAtOffset(archive->fd, name_buf, nameLen,
- name_offset);
-
- if (actual != nameLen) {
+ if (!ReadAtOffset(archive->fd, name_buf, nameLen, name_offset)) {
ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
free(name_buf);
return kIoError;
@@ -942,10 +922,9 @@
do {
/* read as much as we can */
if (zstream.avail_in == 0) {
- const ZD_TYPE getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
- const ZD_TYPE actual = TEMP_FAILURE_RETRY(read(fd, &read_buf[0], getSize));
- if (actual != getSize) {
- ALOGW("Zip: inflate read failed (" ZD " vs " ZD ")", actual, getSize);
+ const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
+ if (!android::base::ReadFully(fd, read_buf.data(), getSize)) {
+ ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
return kIoError;
}
@@ -1005,11 +984,9 @@
// Safe conversion because kBufSize is narrow enough for a 32 bit signed
// value.
- const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
- const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));
-
- if (actual != block_size) {
- ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
+ const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+ if (!android::base::ReadFully(fd, buf.data(), block_size)) {
+ ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
return kIoError;
}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index d426dc4..6aee1bb 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -21,9 +21,11 @@
#include <string.h>
#include <unistd.h>
+#include <memory>
#include <vector>
#include <android-base/file.h>
+#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_archive_stream_entry.h>
@@ -91,7 +93,7 @@
}
TEST(ziparchive, OpenAssumeFdOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
ASSERT_NE(-1, fd);
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
@@ -101,7 +103,7 @@
}
TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
ASSERT_NE(-1, fd);
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
@@ -373,30 +375,13 @@
static const std::string kAbTxtName("ab.txt");
static const size_t kAbUncompressedSize = 270216;
-static int make_temporary_file(const char* file_name_pattern) {
- char full_path[1024];
- // Account for differences between the host and the target.
- //
- // TODO: Maybe reuse bionic/tests/TemporaryFile.h.
- snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern);
- int fd = mkstemp(full_path);
- if (fd == -1) {
- snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern);
- fd = mkstemp(full_path);
- }
-
- return fd;
-}
-
TEST(ziparchive, EmptyEntries) {
- char temp_file_pattern[] = "empty_entries_test_XXXXXX";
- int fd = make_temporary_file(temp_file_pattern);
- ASSERT_NE(-1, fd);
- const ssize_t file_size = sizeof(kEmptyEntriesZip);
- ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
ZipEntry entry;
ZipString empty_name;
@@ -406,27 +391,23 @@
uint8_t buffer[1];
ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
- char output_file_pattern[] = "empty_entries_output_XXXXXX";
- int output_fd = make_temporary_file(output_file_pattern);
- ASSERT_NE(-1, output_fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+
+ TemporaryFile tmp_output_file;
+ ASSERT_NE(-1, tmp_output_file.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
struct stat stat_buf;
- ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+ ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
ASSERT_EQ(0, stat_buf.st_size);
-
- close(fd);
- close(output_fd);
}
TEST(ziparchive, EntryLargerThan32K) {
- char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX";
- int fd = make_temporary_file(temp_file_pattern);
- ASSERT_NE(-1, fd);
- ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip),
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
sizeof(kAbZip) - 1));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
ZipEntry entry;
ZipString ab_name;
@@ -439,21 +420,21 @@
ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
// Extract the entry to a file.
- char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX";
- int output_fd = make_temporary_file(output_file_pattern);
- ASSERT_NE(-1, output_fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+ TemporaryFile tmp_output_file;
+ ASSERT_NE(-1, tmp_output_file.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
// Make sure the extracted file size is as expected.
struct stat stat_buf;
- ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+ ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
// Read the file back to a buffer and make sure the contents are
// the same as the memory buffer we extracted directly to.
std::vector<uint8_t> file_contents(kAbUncompressedSize);
- ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET));
- ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size()));
+ ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0],
+ file_contents.size()));
ASSERT_EQ(file_contents, buffer);
for (int i = 0; i < 90072; ++i) {
@@ -462,35 +443,28 @@
ASSERT_EQ('b', line[1]);
ASSERT_EQ('\n', line[2]);
}
-
- close(fd);
- close(output_fd);
}
TEST(ziparchive, TrailerAfterEOCD) {
- char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
- int fd = make_temporary_file(temp_file_pattern);
- ASSERT_NE(-1, fd);
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
// Create a file with 8 bytes of random garbage.
static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
- const ssize_t file_size = sizeof(kEmptyEntriesZip);
- const ssize_t trailer_size = sizeof(trailer);
- ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
- ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size)));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
ZipArchiveHandle handle;
- ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
+ ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
}
TEST(ziparchive, ExtractToFile) {
- char kTempFilePattern[] = "zip_archive_input_XXXXXX";
- int fd = make_temporary_file(kTempFilePattern);
- ASSERT_NE(-1, fd);
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
- const ssize_t data_size = sizeof(data);
+ const size_t data_size = sizeof(data);
- ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -499,28 +473,25 @@
ZipString name;
SetZipString(&name, kATxtName);
ASSERT_EQ(0, FindEntry(handle, name, &entry));
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
// Assert that the first 8 bytes of the file haven't been clobbered.
uint8_t read_buffer[data_size];
- ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
- ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
+ ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
// Assert that the remainder of the file contains the incompressed data.
std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
- ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
- TEMP_FAILURE_RETRY(
- read(fd, &uncompressed_data[0], entry.uncompressed_length)));
+ ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
+ entry.uncompressed_length));
ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(),
kATxtContents.size()));
// Assert that the total length of the file is sane
- ASSERT_EQ(data_size + static_cast<ssize_t>(kATxtContents.size()),
- lseek64(fd, 0, SEEK_END));
-
- close(fd);
+ ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
+ lseek64(tmp_file.fd, 0, SEEK_END));
}
static void ZipArchiveStreamTest(
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index fe0846d..16a574d 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -154,12 +154,22 @@
tm->tm_mday = (zip_time >> 16) & 0x1f;
}
+static struct tm MakeTm() {
+ struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_year = 2001 - 1900;
+ tm.tm_mon = 1;
+ tm.tm_mday = 12;
+ tm.tm_hour = 18;
+ tm.tm_min = 30;
+ tm.tm_sec = 20;
+ return tm;
+}
+
TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
ZipWriter writer(file_);
- struct tm tm;
- memset(&tm, 0, sizeof(struct tm));
- ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
+ struct tm tm = MakeTm();
time_t time = mktime(&tm);
ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
ASSERT_EQ(0, writer.WriteBytes("he", 2));
@@ -210,9 +220,7 @@
TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
ZipWriter writer(file_);
- struct tm tm;
- memset(&tm, 0, sizeof(struct tm));
- ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
+ struct tm tm = MakeTm();
time_t time = mktime(&tm);
ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
ASSERT_EQ(0, writer.WriteBytes("he", 2));
diff --git a/logcat/Android.mk b/logcat/Android.mk
index c4a9550..b828a9f 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -11,8 +11,6 @@
LOCAL_CFLAGS := -Werror
-LOCAL_INIT_RC := logcatd.rc
-
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
@@ -20,6 +18,7 @@
LOCAL_MODULE := logpersist.start
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_INIT_RC := logcatd.rc
LOCAL_MODULE_PATH := $(bin_dir)
LOCAL_SRC_FILES := logpersist
ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index ddc91ca..c148d89 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -35,7 +35,7 @@
#include <log/logd.h>
#include <log/logger.h>
#include <log/logprint.h>
-#include <utils/threads.h>
+#include <system/thread_defs.h>
#define DEFAULT_MAX_ROTATED_LOGS 4
diff --git a/logcat/logpersist b/logcat/logpersist
index dab466d..8762ff1 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -1,8 +1,8 @@
#! /system/bin/sh
# logpersist cat start and stop handlers
progname="${0##*/}"
-case `getprop ro.build.type` in
-userdebug|eng) ;;
+case `getprop ro.debuggable` in
+1) ;;
*) echo "${progname} - Permission denied"
exit 1
;;
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 4f517bb..8459bd3 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
+#include <memory>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -734,8 +735,8 @@
EXPECT_FALSE(system(command));
return;
}
- DIR *dir;
- EXPECT_TRUE(NULL != (dir = opendir(tmp_out_dir)));
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+ EXPECT_NE(nullptr, dir);
if (!dir) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(system(command));
@@ -743,7 +744,7 @@
}
struct dirent *entry;
unsigned count = 0;
- while ((entry = readdir(dir))) {
+ while ((entry = readdir(dir.get()))) {
if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
continue;
}
@@ -766,7 +767,6 @@
free(line);
unlink(command);
}
- closedir(dir);
if (count > 1) {
char *brk = strpbrk(second_last_line, "\r\n");
if (!brk) {
diff --git a/logd/Android.mk b/logd/Android.mk
index feca8d5..203943c 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -42,6 +42,10 @@
LOCAL_CFLAGS := -Werror $(event_flag)
+ifeq ($(TARGET_BUILD_VARIANT),user)
+LOCAL_CFLAGS += -DAUDITD_ENFORCE_INTEGRITY=true
+endif
+
include $(BUILD_EXECUTABLE)
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index fd45c4a..6a26d00 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -16,7 +16,10 @@
#include <stdlib.h>
+#include <private/android_filesystem_config.h>
+
#include "FlushCommand.h"
+#include "LogBuffer.h"
#include "LogBufferElement.h"
#include "LogCommand.h"
#include "LogReader.h"
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 143fb04..4eb5e83 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -20,17 +20,27 @@
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/prctl.h>
#include <sys/uio.h>
#include <syslog.h>
+#include <string>
+
+#include <cutils/properties.h>
#include <log/logger.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "libaudit.h"
#include "LogAudit.h"
+#include "LogBuffer.h"
#include "LogKlog.h"
+#include "LogReader.h"
+
+#ifndef AUDITD_ENFORCE_INTEGRITY
+#define AUDITD_ENFORCE_INTEGRITY false
+#endif
#define KMSG_PRIORITY(PRI) \
'<', \
@@ -43,11 +53,10 @@
logbuf(buf),
reader(reader),
fdDmesg(fdDmesg),
+ policyLoaded(false),
+ rebootToSafeMode(false),
initialized(false) {
- static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
- 'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
- ' ', 's', 't', 'a', 'r', 't', '\n' };
- write(fdDmesg, auditd_message, sizeof(auditd_message));
+ logToDmesg("start");
}
bool LogAudit::onDataAvailable(SocketClient *cli) {
@@ -73,6 +82,55 @@
return true;
}
+void LogAudit::logToDmesg(const std::string& str)
+{
+ static const char prefix[] = { KMSG_PRIORITY(LOG_INFO),
+ 'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
+ ' ', '\0' };
+ std::string message = prefix + str + "\n";
+ write(fdDmesg, message.c_str(), message.length());
+}
+
+std::string LogAudit::getProperty(const std::string& name)
+{
+ char value[PROP_VALUE_MAX] = {0};
+ property_get(name.c_str(), value, "");
+ return value;
+}
+
+void LogAudit::enforceIntegrity() {
+ static bool loggedOnce;
+ bool once = loggedOnce;
+
+ loggedOnce = true;
+
+ if (!AUDITD_ENFORCE_INTEGRITY) {
+ if (!once) {
+ logToDmesg("integrity enforcement suppressed; not rebooting");
+ }
+ } else if (rebootToSafeMode) {
+ if (getProperty("persist.sys.safemode") == "1") {
+ if (!once) {
+ logToDmesg("integrity enforcement suppressed; in safe mode");
+ }
+ return;
+ }
+
+ logToDmesg("enforcing integrity; rebooting to safe mode");
+ property_set("persist.sys.safemode", "1");
+
+ std::string buildDate = getProperty("ro.build.date.utc");
+ if (!buildDate.empty()) {
+ property_set("persist.sys.audit_safemode", buildDate.c_str());
+ }
+
+ property_set("sys.powerctl", "reboot");
+ } else {
+ logToDmesg("enforcing integrity: rebooting to recovery");
+ property_set("sys.powerctl", "reboot,recovery");
+ }
+}
+
int LogAudit::logPrint(const char *fmt, ...) {
if (fmt == NULL) {
return -EINVAL;
@@ -94,7 +152,31 @@
memmove(cp, cp + 1, strlen(cp + 1) + 1);
}
- bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
+ bool loaded = strstr(str, " policy loaded ");
+
+ if (loaded) {
+ if (policyLoaded) {
+ // SELinux policy changes are not allowed
+ enforceIntegrity();
+ } else {
+ logToDmesg("policy loaded");
+ policyLoaded = true;
+ }
+ }
+
+ // Note: The audit log can include untrusted strings, but those containing
+ // "a control character, unprintable character, double quote mark, or a
+ // space" are hex encoded. The space character before the search term is
+ // therefore needed to prevent denial of service. Do not remove the space.
+ bool permissive = strstr(str, " enforcing=0") ||
+ strstr(str, " permissive=1");
+
+ if (permissive) {
+ // SELinux in permissive mode is not allowed
+ enforceIntegrity();
+ }
+
+ bool info = loaded || permissive;
if ((fdDmesg >= 0) && initialized) {
struct iovec iov[3];
static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 8a82630..3a84541 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -18,18 +18,24 @@
#define _LOGD_LOG_AUDIT_H__
#include <sysutils/SocketListener.h>
-#include "LogReader.h"
+
+#include "LogBuffer.h"
+
+class LogReader;
class LogAudit : public SocketListener {
LogBuffer *logbuf;
LogReader *reader;
int fdDmesg;
+ bool policyLoaded;
+ bool rebootToSafeMode;
bool initialized;
public:
LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
int log(char *buf, size_t len);
bool isMonotonic() { return logbuf->isMonotonic(); }
+ void allowSafeMode(bool allow = true) { rebootToSafeMode = allow; }
protected:
virtual bool onDataAvailable(SocketClient *cli);
@@ -38,6 +44,9 @@
static int getLogSocket();
int logPrint(const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 2, 3)));
+ void logToDmesg(const std::string& str);
+ std::string getProperty(const std::string& name);
+ void enforceIntegrity();
};
#endif
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index fde9ad7..eb5194c 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -25,9 +25,11 @@
#include <log/logger.h>
#include <private/android_logger.h>
+#include "LogBuffer.h"
#include "LogBufferElement.h"
#include "LogCommand.h"
#include "LogReader.h"
+#include "LogUtils.h"
const uint64_t LogBufferElement::FLUSH_ERROR(0);
atomic_int_fast64_t LogBufferElement::sequence(1);
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 9690489..ac2b128 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -20,13 +20,16 @@
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/prctl.h>
#include <sys/uio.h>
#include <syslog.h>
#include <log/logger.h>
+#include "LogBuffer.h"
#include "LogKlog.h"
+#include "LogReader.h"
#define KMSG_PRIORITY(PRI) \
'<', \
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 3c8cc87..ee73b71 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -19,10 +19,12 @@
#include <sysutils/SocketListener.h>
#include <log/log_read.h>
-#include "LogReader.h"
char *log_strntok_r(char *s, size_t *len, char **saveptr, size_t *sublen);
+class LogBuffer;
+class LogReader;
+
class LogKlog : public SocketListener {
LogBuffer *logbuf;
LogReader *reader;
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 846dd7c..39dd227 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -27,6 +27,7 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include "LogBuffer.h"
#include "LogListener.h"
#include "LogUtils.h"
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 667a3f2..2c07984 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -18,11 +18,15 @@
#include <poll.h>
#include <sys/prctl.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <cutils/sockets.h>
-#include "LogReader.h"
#include "FlushCommand.h"
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogReader.h"
+#include "LogUtils.h"
LogReader::LogReader(LogBuffer *logbuf) :
SocketListener(getLogSocket(), true),
@@ -176,6 +180,11 @@
}
FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
+
+ // Set acceptable upper limit to wait for slow reader processing b/27242723
+ struct timeval t = { LOGD_SNDTIMEO, 0 };
+ setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char *)&t, sizeof(t));
+
command.runSocketCommand(cli);
return true;
}
diff --git a/logd/LogReader.h b/logd/LogReader.h
index 91559a3..98674b8 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -18,8 +18,10 @@
#define _LOGD_LOG_WRITER_H__
#include <sysutils/SocketListener.h>
-#include "LogBuffer.h"
-#include "LogTimes.h"
+
+#define LOGD_SNDTIMEO 32
+
+class LogBuffer;
class LogReader : public SocketListener {
LogBuffer &mLogbuf;
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index a4b96d3..2a04880 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -90,7 +90,7 @@
while(it != times.end()) {
if (*it == me) {
times.erase(it);
- me->release_Locked();
+ me->release_nodelete_Locked();
break;
}
it++;
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 1117088..b66ff9e 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -27,6 +27,7 @@
#include <log/log.h>
class LogReader;
+class LogBufferElement;
class LogTimeEntry {
static pthread_mutex_t timesLock;
@@ -74,7 +75,13 @@
void triggerSkip_Locked(log_id_t id, unsigned int skip) { skipAhead[id] = skip; }
void cleanSkip_Locked(void);
- // Called after LogTimeEntry removed from list, lock implicitly held
+ // These called after LogTimeEntry removed from list, lock implicitly held
+ void release_nodelete_Locked(void) {
+ mRelease = true;
+ pthread_cond_signal(&threadTriggeredCondition);
+ // assumes caller code path will call decRef_Locked()
+ }
+
void release_Locked(void) {
mRelease = true;
pthread_cond_signal(&threadTriggeredCondition);
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fd4800e..aa4b6e1 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -17,6 +17,7 @@
#ifndef _LOGD_LOG_UTILS_H__
#define _LOGD_LOG_UTILS_H__
+#include <sys/cdefs.h>
#include <sys/types.h>
#include <log/log.h>
@@ -29,6 +30,7 @@
// Furnished in main.cpp. Caller must own and free returned value
char *uidToName(uid_t uid);
+void prdebug(const char *fmt, ...) __printflike(1, 2);
// Furnished in LogStatistics.cpp. Caller must own and free returned value
char *pidToName(pid_t pid);
diff --git a/logd/README.property b/logd/README.property
index 22f86b9..4bc5541 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -1,14 +1,13 @@
The properties that logd responds to are:
name type default description
-ro.logd.auditd bool true Enable selinux audit daemon
ro.logd.auditd.dmesg bool true selinux audit messages duplicated and
sent on to dmesg log
persist.logd.security bool false Enable security buffer.
ro.device_owner bool false Override persist.logd.security to false
ro.logd.kernel bool+ svelte+ Enable klogd daemon
ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
-ro.build.type string if user, logd.statistics &
+ro.debuggable number if not "1", logd.statistics &
ro.logd.kernel default false.
persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
turns on logcat -f in logd context
@@ -46,10 +45,10 @@
NB:
- bool+ - "true", "false" and comma separated list of "eng" (forced false if
- ro.build.type is "user") or "svelte" (forced false if ro.config.low_ram is
+ ro.debuggable is not "1") or "svelte" (forced false if ro.config.low_ram is
true).
- svelte - see ro.config.low_ram for details.
-- svelte+ - see ro.config.low_ram and ro.build.type for details.
+- svelte+ - see ro.config.low_ram and ro.debuggable for details.
- ro - <base property> temporary override, ro.<base property> platform default.
- persist - <base property> override, persist.<base property> platform default.
- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
diff --git a/logd/main.cpp b/logd/main.cpp
index ba56e57..3095f7f 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -165,7 +165,7 @@
char newkey[PROPERTY_KEY_MAX];
snprintf(newkey, sizeof(newkey), "ro.%s", key);
property_get(newkey, property, "");
- // persist properties set by /data require innoculation with
+ // persist properties set by /data require inoculation with
// logd-reinit. They may be set in init.rc early and function, but
// otherwise are defunct unless reset. Do not rely on persist
// properties for startup-only keys unless you are willing to restart
@@ -202,8 +202,8 @@
return false;
}
if (flag & BOOL_DEFAULT_FLAG_ENG) {
- property_get("ro.build.type", property, "");
- if (!strcmp(property, "user")) {
+ property_get("ro.debuggable", property, "");
+ if (strcmp(property, "1")) {
return false;
}
}
@@ -211,10 +211,32 @@
return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
}
-// Remove the static, and use this variable
-// globally for debugging if necessary. eg:
-// write(fdDmesg, "I am here\n", 10);
static int fdDmesg = -1;
+void inline android::prdebug(const char *fmt, ...) {
+ if (fdDmesg < 0) {
+ return;
+ }
+
+ static const char message[] = {
+ KMSG_PRIORITY(LOG_DEBUG), 'l', 'o', 'g', 'd', ':', ' '
+ };
+ char buffer[256];
+ memcpy(buffer, message, sizeof(message));
+
+ va_list ap;
+ va_start(ap, fmt);
+ int n = vsnprintf(buffer + sizeof(message),
+ sizeof(buffer) - sizeof(message), fmt, ap);
+ va_end(ap);
+ if (n > 0) {
+ buffer[sizeof(buffer) - 1] = '\0';
+ if (!strchr(buffer, '\n')) {
+ buffer[sizeof(buffer) - 2] = '\0';
+ strlcat(buffer, "\n", sizeof(buffer));
+ }
+ write(fdDmesg, buffer, strlen(buffer));
+ }
+}
static sem_t uidName;
static uid_t uid;
@@ -223,6 +245,7 @@
static sem_t reinit;
static bool reinit_running = false;
static LogBuffer *logBuf = NULL;
+static LogAudit *logAudit = NULL;
static bool package_list_parser_cb(pkg_info *info, void * /* userdata */) {
@@ -242,8 +265,11 @@
set_sched_policy(0, SP_BACKGROUND);
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
- setgid(AID_SYSTEM);
- setuid(AID_SYSTEM);
+ // If we are AID_ROOT, we should drop to AID_SYSTEM, if we are anything
+ // else, we have even lesser privileges and accept our fate. Not worth
+ // checking for error returns setting this thread's privileges.
+ (void)setgid(AID_SYSTEM);
+ (void)setuid(AID_SYSTEM);
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
@@ -270,6 +296,10 @@
logBuf->init();
logBuf->initPrune(NULL);
}
+
+ if (logAudit) {
+ logAudit->allowSafeMode();
+ }
}
return NULL;
@@ -490,25 +520,19 @@
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
- bool auditd = property_get_bool("logd.auditd",
- BOOL_DEFAULT_TRUE |
- BOOL_DEFAULT_FLAG_PERSIST);
- LogAudit *al = NULL;
- if (auditd) {
- al = new LogAudit(logBuf, reader,
- property_get_bool("logd.auditd.dmesg",
- BOOL_DEFAULT_TRUE |
- BOOL_DEFAULT_FLAG_PERSIST)
- ? fdDmesg
- : -1);
- }
+ logAudit = new LogAudit(logBuf, reader,
+ property_get_bool("logd.auditd.dmesg",
+ BOOL_DEFAULT_TRUE |
+ BOOL_DEFAULT_FLAG_PERSIST)
+ ? fdDmesg
+ : -1);
LogKlog *kl = NULL;
if (klogd) {
- kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL);
+ kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, logAudit != NULL);
}
- readDmesg(al, kl);
+ readDmesg(logAudit, kl);
// failure is an option ... messages are in dmesg (required by standard)
@@ -516,8 +540,9 @@
delete kl;
}
- if (al && al->startListener()) {
- delete al;
+ if (logAudit && logAudit->startListener()) {
+ delete logAudit;
+ logAudit = NULL;
}
TEMP_FAILURE_RETRY(pause());
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index de19790..2014374 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -30,6 +30,8 @@
#include <log/log.h>
#include <log/logger.h>
+#include "../LogReader.h" // pickup LOGD_SNDTIMEO
+
/*
* returns statistics
*/
@@ -253,6 +255,9 @@
fprintf(stderr, "lid=crash ");
break;
case 5:
+ fprintf(stderr, "lid=security ");
+ break;
+ case 6:
fprintf(stderr, "lid=kernel ");
break;
default:
@@ -710,3 +715,56 @@
EXPECT_TRUE(content_timeout);
EXPECT_NE(0U, alarm_timeout);
}
+
+// b/27242723 confirmed fixed
+TEST(logd, SNDTIMEO) {
+ static const unsigned sndtimeo = LOGD_SNDTIMEO; // <sigh> it has to be done!
+ static const unsigned sleep_time = sndtimeo + 3;
+ static const unsigned alarm_time = sleep_time + 5;
+
+ int fd;
+
+ ASSERT_TRUE((fd = socket_local_client("logdr",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET)) > 0);
+
+ struct sigaction ignore, old_sigaction;
+ memset(&ignore, 0, sizeof(ignore));
+ ignore.sa_handler = caught_signal;
+ sigemptyset(&ignore.sa_mask);
+ sigaction(SIGALRM, &ignore, &old_sigaction);
+ unsigned int old_alarm = alarm(alarm_time);
+
+ static const char ask[] = "stream lids=0,1,2,3,4,5,6"; // all sources
+ bool reader_requested = write(fd, ask, sizeof(ask)) == sizeof(ask);
+ EXPECT_TRUE(reader_requested);
+
+ log_msg msg;
+ bool read_one = recv(fd, msg.buf, sizeof(msg), 0) > 0;
+
+ EXPECT_TRUE(read_one);
+ if (read_one) {
+ dump_log_msg("user", &msg, 3, -1);
+ }
+
+ fprintf (stderr, "Sleep for >%d seconds logd SO_SNDTIMEO ...\n", sndtimeo);
+ sleep(sleep_time);
+
+ // flush will block if we did not trigger. if it did, last entry returns 0
+ int recv_ret;
+ do {
+ recv_ret = recv(fd, msg.buf, sizeof(msg), 0);
+ } while (recv_ret > 0);
+ int save_errno = (recv_ret < 0) ? errno : 0;
+
+ EXPECT_NE(0U, alarm(old_alarm));
+ sigaction(SIGALRM, &old_sigaction, NULL);
+
+ EXPECT_EQ(0, recv_ret);
+ if (recv_ret > 0) {
+ dump_log_msg("user", &msg, 3, -1);
+ }
+ EXPECT_EQ(0, save_errno);
+
+ close(fd);
+}
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 28d6de7..ccbe0bf 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -408,7 +408,7 @@
if (poll_fds[0].revents & POLLHUP) {
int ret;
- ret = waitpid(pid, &status, WNOHANG);
+ ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (ret < 0) {
rc = errno;
ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno));
diff --git a/metricsd/.clang-format b/metricsd/.clang-format
deleted file mode 100644
index c98efc2..0000000
--- a/metricsd/.clang-format
+++ /dev/null
@@ -1,10 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: Inline
-AllowShortIfStatementsOnASingleLine: false
-AllowShortLoopsOnASingleLine: false
-BinPackArguments: false
-BinPackParameters: false
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-PointerAlignment: Left
-TabWidth: 2
diff --git a/metricsd/.clang-format b/metricsd/.clang-format
new file mode 120000
index 0000000..f9066d4
--- /dev/null
+++ b/metricsd/.clang-format
@@ -0,0 +1 @@
+../../../build/tools/brillo-clang-format
\ No newline at end of file
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index e140c62..bb262b4 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -200,7 +200,7 @@
LOCAL_SRC_FILES := $(metricsd_tests_sources) $(metricsd_common)
LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_protos metricsd_binder_proxy
ifdef BRILLO
-LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_TAGS := eng
endif
include $(BUILD_NATIVE_TEST)
@@ -218,7 +218,7 @@
LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_binder_proxy \
$(metrics_collector_static_libraries)
ifdef BRILLO
-LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_TAGS := eng
endif
include $(BUILD_NATIVE_TEST)
diff --git a/metricsd/uploader/metricsd_service_runner.cc b/metricsd/uploader/metricsd_service_runner.cc
index 5a759d3..4361cac 100644
--- a/metricsd/uploader/metricsd_service_runner.cc
+++ b/metricsd/uploader/metricsd_service_runner.cc
@@ -20,6 +20,7 @@
#include <binder/IServiceManager.h>
#include <brillo/binder_watcher.h>
+#include <brillo/message_loops/base_message_loop.h>
#include <utils/Errors.h>
#include "uploader/bn_metricsd_impl.h"
@@ -40,15 +41,17 @@
CHECK(status == android::OK) << "Metricsd service registration failed";
message_loop_for_io_.reset(new base::MessageLoopForIO);
+ message_loop_.reset(new brillo::BaseMessageLoop(message_loop_for_io_.get()));
- brillo::BinderWatcher watcher;
+ brillo::BinderWatcher watcher(message_loop_.get());
CHECK(watcher.Init()) << "failed to initialize the binder file descriptor "
<< "watcher";
- message_loop_for_io_->Run();
+ message_loop_->Run();
// Delete the message loop here as it needs to be deconstructed in the thread
// it is attached to.
+ message_loop_.reset();
message_loop_for_io_.reset();
}
diff --git a/metricsd/uploader/metricsd_service_runner.h b/metricsd/uploader/metricsd_service_runner.h
index 1715de0..f5dad21 100644
--- a/metricsd/uploader/metricsd_service_runner.h
+++ b/metricsd/uploader/metricsd_service_runner.h
@@ -21,6 +21,7 @@
#include <thread>
#include <base/message_loop/message_loop.h>
+#include <brillo/message_loops/message_loop.h>
#include "uploader/crash_counters.h"
@@ -39,6 +40,7 @@
void Run();
std::unique_ptr<base::MessageLoopForIO> message_loop_for_io_;
+ std::unique_ptr<brillo::MessageLoop> message_loop_;
std::unique_ptr<std::thread> thread_;
std::shared_ptr<CrashCounters> counters_;
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 70112f4..0f77fe4 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -304,6 +304,8 @@
upload_service_->PersistToDisk();
EXPECT_EQ(
1, upload_service_->current_log_->uma_proto()->histogram_event().size());
+ // Destroy the old service before creating a new one.
+ upload_service_.reset();
upload_service_.reset(new UploadService(
"", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
upload_service_->InitForTest(nullptr);
@@ -325,6 +327,8 @@
// Write a bogus saved log.
EXPECT_EQ(5, base::WriteFile(upload_service_->saved_log_path_, "hello", 5));
+ // Destroy the old service before creating a new one.
+ upload_service_.reset();
upload_service_.reset(new UploadService(
"", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
index 5ab6195..2bd8230 100644
--- a/mkbootimg/bootimg.h
+++ b/mkbootimg/bootimg.h
@@ -43,7 +43,11 @@
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
- uint32_t unused[2]; /* future expansion: should be 0 */
+
+ /* operating system version; "1.2.34" -> 010234 */
+ uint32_t os_version;
+ /* operating system patch level; "2016-01-01" -> 20160101 */
+ uint32_t os_patch_level;
uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index aea2585..be342c9 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -19,6 +19,8 @@
from os import fstat
from struct import pack
from hashlib import sha1
+import sys
+import re
def filesize(f):
if f is None:
@@ -46,7 +48,7 @@
def write_header(args):
BOOT_MAGIC = 'ANDROID!'.encode()
args.output.write(pack('8s', BOOT_MAGIC))
- args.output.write(pack('8I',
+ args.output.write(pack('10I',
filesize(args.kernel), # size in bytes
args.base + args.kernel_offset, # physical load addr
filesize(args.ramdisk), # size in bytes
@@ -54,8 +56,9 @@
filesize(args.second), # size in bytes
args.base + args.second_offset, # physical load addr
args.base + args.tags_offset, # physical addr for kernel tags
- args.pagesize)) # flash page size we assume
- args.output.write(pack('8x')) # future expansion: should be 0
+ args.pagesize, # flash page size we assume
+ args.os_version, # operating system version
+ args.os_patch_level)) # security patch level
args.output.write(pack('16s', args.board.encode())) # asciiz product name
args.output.write(pack('512s', args.cmdline[:512].encode()))
@@ -96,6 +99,18 @@
def parse_int(x):
return int(x, 0)
+def match_to_int(x):
+ if (x and x.lastindex == 3):
+ return (parse_int(x.group(3)) +
+ parse_int(x.group(2)) * 100 +
+ parse_int(x.group(1)) * 10000)
+ return 0
+
+def parse_os_version(x):
+ return match_to_int(re.search(r'^(\d+)\.(\d{1,2})\.(\d{1,2})', x))
+
+def parse_os_patch_level(x):
+ return match_to_int(re.search(r'^(\d{4,})-(\d{2})-(\d{2})', x))
def parse_cmdline():
parser = ArgumentParser()
@@ -110,6 +125,10 @@
parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
default=0x00f00000)
+ parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
+ default=0)
+ parser.add_argument('--os_patch_level', help='operating system patch level',
+ type=parse_os_patch_level, default=0)
parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
maxlen=16)
@@ -133,8 +152,10 @@
img_id = write_header(args)
write_data(args)
if args.id:
- print('0x' + ''.join('{:02x}'.format(ord(c)) for c in img_id))
-
+ if isinstance(img_id, str):
+ # Python 2's struct.pack returns a string, but py3 returns bytes.
+ img_id = [ord(x) for x in img_id]
+ print('0x' + ''.join('{:02x}'.format(c) for c in img_id))
if __name__ == '__main__':
main()
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 895a25d..d53af2f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -12,6 +12,18 @@
include $(BUILD_PREBUILT)
#######################################
+# init-debug.rc
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init-debug.rc
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
+
+include $(BUILD_PREBUILT)
+
+#######################################
# asan.options
ifneq ($(filter address,$(SANITIZE_TARGET)),)
include $(CLEAR_VARS)
@@ -58,6 +70,11 @@
; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
endif
+# The A/B updater uses a top-level /postinstall directory to mount the new
+# system before reboot.
+ifeq ($(AB_OTA_UPDATER),true)
+ LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/postinstall
+endif
include $(BUILD_SYSTEM)/base_rules.mk
diff --git a/rootdir/init-debug.rc b/rootdir/init-debug.rc
new file mode 100644
index 0000000..435d4cb
--- /dev/null
+++ b/rootdir/init-debug.rc
@@ -0,0 +1,8 @@
+on property:persist.mmc.max_read_speed=*
+ write /sys/block/mmcblk0/max_read_speed ${persist.mmc.max_read_speed}
+
+on property:persist.mmc.max_write_speed=*
+ write /sys/block/mmcblk0/max_write_speed ${persist.mmc.max_write_speed}
+
+on property:persist.mmc.cache_size=*
+ write /sys/block/mmcblk0/cache_size ${persist.mmc.cache_size}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1f63fcf..6d5023e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -23,6 +23,9 @@
# Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
mkdir /mnt 0775 root system
+ # Set the security context of /postinstall if present.
+ restorecon /postinstall
+
start ueventd
on init
@@ -56,6 +59,17 @@
chown root system /sys/fs/cgroup/memory/sw/tasks
chmod 0660 /sys/fs/cgroup/memory/sw/tasks
+ # Create energy-aware scheduler tuning nodes
+ mkdir /sys/fs/cgroup/stune
+ mount cgroup none /sys/fs/cgroup/stune schedtune
+ mkdir /sys/fs/cgroup/stune/foreground
+ chown system system /sys/fs/cgroup/stune
+ chown system system /sys/fs/cgroup/stune/foreground
+ chown system system /sys/fs/cgroup/stune/tasks
+ chown system system /sys/fs/cgroup/stune/foreground/tasks
+ chmod 0664 /sys/fs/cgroup/stune/tasks
+ chmod 0664 /sys/fs/cgroup/stune/foreground/tasks
+
# Mount staging areas for devices managed by vold
# See storage config details at http://source.android.com/tech/storage/
mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000
@@ -71,7 +85,6 @@
mkdir /mnt/expand 0771 system system
# Storage views to support runtime permissions
- mkdir /storage 0755 root root
mkdir /mnt/runtime 0700 root root
mkdir /mnt/runtime/default 0755 root root
mkdir /mnt/runtime/default/self 0755 root root
@@ -167,13 +180,16 @@
chown system system /dev/cpuset/foreground
chown system system /dev/cpuset/foreground/boost
chown system system /dev/cpuset/background
+ chown system system /dev/cpuset/system-background
chown system system /dev/cpuset/tasks
chown system system /dev/cpuset/foreground/tasks
chown system system /dev/cpuset/foreground/boost/tasks
chown system system /dev/cpuset/background/tasks
+ chown system system /dev/cpuset/system-background/tasks
chmod 0664 /dev/cpuset/foreground/tasks
chmod 0664 /dev/cpuset/foreground/boost/tasks
chmod 0664 /dev/cpuset/background/tasks
+ chmod 0664 /dev/cpuset/system-background/tasks
chmod 0664 /dev/cpuset/tasks
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index ff25ac2..0ca38b9 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,4 +5,4 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 29bb1cf..1646c0f 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,10 +5,10 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
socket zygote_secondary stream 660 root system
onrestart restart zygote
- writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 5497524..b477c8e 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,4 +5,4 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 8ed5e9e..633a981 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,10 +5,10 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
socket zygote_secondary stream 660 root system
onrestart restart zygote
- writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index c5f3d1d..2d04a7f 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -5,6 +5,6 @@
LOCAL_SRC_FILES := sdcard.c
LOCAL_MODULE := sdcard
LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
-LOCAL_SHARED_LIBRARIES := libcutils libpackagelistparser
+LOCAL_SHARED_LIBRARIES := liblog libcutils libpackagelistparser
include $(BUILD_EXECUTABLE)
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 45efe36..f862561 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -1214,7 +1214,13 @@
}
out.fh = ptr_to_id(h);
out.open_flags = 0;
+
+#ifdef FUSE_STACKED_IO
+ out.lower_fd = h->fd;
+#else
out.padding = 0;
+#endif
+
fuse_reply(fuse, hdr->unique, &out, sizeof(out));
return NO_STATUS;
}
@@ -1378,7 +1384,13 @@
}
out.fh = ptr_to_id(h);
out.open_flags = 0;
+
+#ifdef FUSE_STACKED_IO
+ out.lower_fd = -1;
+#else
out.padding = 0;
+#endif
+
fuse_reply(fuse, hdr->unique, &out, sizeof(out));
return NO_STATUS;
}
@@ -1460,6 +1472,11 @@
out.major = FUSE_KERNEL_VERSION;
out.max_readahead = req->max_readahead;
out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
+
+#ifdef FUSE_STACKED_IO
+ out.flags |= FUSE_STACKED_IO;
+#endif
+
out.max_background = 32;
out.congestion_threshold = 32;
out.max_write = MAX_WRITE;
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 52716e9..b1cdb60 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -38,7 +38,6 @@
nandread \
newfs_msdos \
ps \
- prlimit \
sendevent \
start \
stop \
@@ -55,6 +54,7 @@
LOCAL_CONLYFLAGS += -std=gnu99
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libselinux \
@@ -80,10 +80,16 @@
$(LOCAL_PATH)/getevent.c: $(intermediates)/input.h-labels.h
+UAPI_INPUT_EVENT_CODES_H := bionic/libc/kernel/uapi/linux/input-event-codes.h
INPUT_H_LABELS_H := $(intermediates)/input.h-labels.h
$(INPUT_H_LABELS_H): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
-$(INPUT_H_LABELS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/generate-input.h-labels.py > $@
-$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py
+# The PRIVATE_CUSTOM_TOOL line uses = to evaluate the output path late.
+# We copy the input path so it can't be accidentally modified later.
+$(INPUT_H_LABELS_H): PRIVATE_UAPI_INPUT_EVENT_CODES_H := $(UAPI_INPUT_EVENT_CODES_H)
+$(INPUT_H_LABELS_H): PRIVATE_CUSTOM_TOOL = $(PRIVATE_LOCAL_PATH)/generate-input.h-labels.py $(PRIVATE_UAPI_INPUT_EVENT_CODES_H) > $@
+# The dependency line though gets evaluated now, so the PRIVATE_ copy doesn't exist yet,
+# and the original can't yet have been modified, so this is both sufficient and necessary.
+$(INPUT_H_LABELS_H): $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/generate-input.h-labels.py $(UAPI_INPUT_EVENT_CODES_H)
$(INPUT_H_LABELS_H):
$(transform-generated-source)
diff --git a/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
index ebb9588..a2b9111 100755
--- a/toolbox/generate-input.h-labels.py
+++ b/toolbox/generate-input.h-labels.py
@@ -16,8 +16,10 @@
#
# pylint: disable=bad-indentation,bad-continuation
+from __future__ import print_function
import os
import re
+import sys
input_prop_list = []
ev_list = []
@@ -36,7 +38,7 @@
r = re.compile(r'#define\s+(\S+)\s+((?:0x)?\d+)')
-with open('bionic/libc/kernel/uapi/linux/input.h', 'r') as f:
+with open(sys.argv[1], 'r') as f:
for line in f:
m = r.match(line)
if m:
@@ -71,11 +73,11 @@
ff_list.append(name)
def Dump(struct_name, values):
- print 'static struct label %s[] = {' % (struct_name)
+ print('static struct label %s[] = {' % (struct_name))
for value in values:
- print ' LABEL(%s),' % (value)
- print ' LABEL_END,'
- print '};'
+ print(' LABEL(%s),' % (value))
+ print(' LABEL_END,')
+ print('};')
Dump("input_prop_labels", input_prop_list)
Dump("ev_labels", ev_list)
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
index 5b98a01..27ea9e8 100644
--- a/toolbox/newfs_msdos.c
+++ b/toolbox/newfs_msdos.c
@@ -695,7 +695,7 @@
(u_int)tm->tm_min));
mk4(bsx->volid, x);
mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
- sprintf(buf, "FAT%u", fat);
+ snprintf(buf, sizeof(buf), "FAT%u", fat);
setstr(bsx->type, buf, sizeof(bsx->type));
if (!opt_B) {
x1 += sizeof(struct bsx);
diff --git a/toolbox/prlimit.c b/toolbox/prlimit.c
deleted file mode 100644
index 8cf202a..0000000
--- a/toolbox/prlimit.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2014, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-static void
-usage(const char *s)
-{
- fprintf(stderr, "usage: %s pid resource cur max\n", s);
- exit(EXIT_FAILURE);
-}
-
-int prlimit_main(int argc, char *argv[])
-{
- pid_t pid;
- struct rlimit64 rl;
- int resource;
- int rc;
-
- if (argc != 5)
- usage(*argv);
-
- if (sscanf(argv[1], "%d", &pid) != 1)
- usage(*argv);
-
- if (sscanf(argv[2], "%d", &resource) != 1)
- usage(*argv);
-
- if (sscanf(argv[3], "%llu", &rl.rlim_cur) != 1)
- usage(*argv);
-
- if (sscanf(argv[4], "%llu", &rl.rlim_max) != 1)
- usage(*argv);
-
- printf("setting resource %d of pid %d to [%llu,%llu]\n", resource, pid,
- rl.rlim_cur, rl.rlim_max);
- rc = prlimit64(pid, resource, &rl, NULL);
- if (rc < 0) {
- perror("prlimit");
- exit(EXIT_FAILURE);
- }
-
- return 0;
-}
diff --git a/toolbox/ps.c b/toolbox/ps.c
index 7e70c71..d366f3e 100644
--- a/toolbox/ps.c
+++ b/toolbox/ps.c
@@ -57,16 +57,16 @@
int prio, nice, rtprio, sched, psr;
struct passwd *pw;
- sprintf(statline, "/proc/%d", tid ? tid : pid);
+ snprintf(statline, sizeof(statline), "/proc/%d", tid ? tid : pid);
stat(statline, &stats);
if(tid) {
- sprintf(statline, "/proc/%d/task/%d/stat", pid, tid);
+ snprintf(statline, sizeof(statline), "/proc/%d/task/%d/stat", pid, tid);
cmdline[0] = 0;
snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid);
} else {
- sprintf(statline, "/proc/%d/stat", pid);
- sprintf(cmdline, "/proc/%d/cmdline", pid);
+ snprintf(statline, sizeof(statline), "/proc/%d/stat", pid);
+ snprintf(cmdline, sizeof(cmdline), "/proc/%d/cmdline", pid);
snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
int fd = open(cmdline, O_RDONLY);
if(fd == 0) {
@@ -149,7 +149,7 @@
pw = getpwuid(stats.st_uid);
if(pw == 0 || (display_flags & SHOW_NUMERIC_UID)) {
- sprintf(user,"%d",(int)stats.st_uid);
+ snprintf(user,sizeof(user),"%d",(int)stats.st_uid);
} else {
strcpy(user,pw->pw_name);
}
@@ -208,7 +208,7 @@
int fd, r;
char exeline[1024];
- sprintf(exeline, "/proc/%d/exe", pid);
+ snprintf(exeline, sizeof(exeline), "/proc/%d/exe", pid);
fd = open(exeline, O_RDONLY);
if(fd == 0) {
printf(" ");
@@ -243,7 +243,7 @@
DIR *d;
struct dirent *de;
- sprintf(tmp,"/proc/%d/task",pid);
+ snprintf(tmp,sizeof(tmp),"/proc/%d/task",pid);
d = opendir(tmp);
if(d == 0) return;
diff --git a/toolbox/top.c b/toolbox/top.c
index 0ea5a5e..003f4c9 100644
--- a/toolbox/top.c
+++ b/toolbox/top.c
@@ -62,12 +62,13 @@
char state;
uint64_t utime;
uint64_t stime;
+ char pr[3];
+ long ni;
uint64_t delta_utime;
uint64_t delta_stime;
uint64_t delta_time;
uint64_t vss;
uint64_t rss;
- int prs;
int num_threads;
char policy[POLICY_NAME_LEN];
};
@@ -183,11 +184,11 @@
old_procs = new_procs;
num_old_procs = num_new_procs;
memcpy(&old_cpu, &new_cpu, sizeof(old_cpu));
- sleep(delay);
read_procs();
print_procs();
free_old_procs();
fflush(stdout);
+ if (iterations != 0) sleep(delay);
}
return 0;
@@ -257,29 +258,29 @@
proc->pid = proc->tid = pid;
- sprintf(filename, "/proc/%d/stat", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
read_stat(filename, proc);
- sprintf(filename, "/proc/%d/cmdline", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
read_cmdline(filename, proc);
- sprintf(filename, "/proc/%d/status", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
read_status(filename, proc);
read_policy(pid, proc);
proc->num_threads = 0;
} else {
- sprintf(filename, "/proc/%d/cmdline", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
read_cmdline(filename, &cur_proc);
- sprintf(filename, "/proc/%d/status", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
read_status(filename, &cur_proc);
proc = NULL;
}
- sprintf(filename, "/proc/%d/task", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
task_dir = opendir(filename);
if (!task_dir) continue;
@@ -294,7 +295,7 @@
proc->pid = pid; proc->tid = tid;
- sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
+ snprintf(filename, sizeof(filename), "/proc/%d/task/%d/stat", pid, tid);
read_stat(filename, proc);
read_policy(tid, proc);
@@ -339,20 +340,30 @@
strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN);
proc->tname[THREAD_NAME_LEN-1] = 0;
- /* Scan rest of string. */
+ // Scan rest of string.
+ long pr;
sscanf(close_paren + 1,
- " %c " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
- "%" SCNu64
- "%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d "
- "%" SCNu64
- "%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
- "%d",
+ " %c "
+ "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
+ "%" SCNu64 // utime %lu (14)
+ "%" SCNu64 // stime %lu (15)
+ "%*d %*d "
+ "%ld " // priority %ld (18)
+ "%ld " // nice %ld (19)
+ "%*d %*d %*d "
+ "%" SCNu64 // vsize %lu (23)
+ "%" SCNu64, // rss %ld (24)
&proc->state,
&proc->utime,
&proc->stime,
+ &pr,
+ &proc->ni,
&proc->vss,
- &proc->rss,
- &proc->prs);
+ &proc->rss);
+
+ // Translate the PR field.
+ if (pr < -9) strcpy(proc->pr, "RT");
+ else snprintf(proc->pr, sizeof(proc->pr), "%ld", pr);
return 0;
}
@@ -414,11 +425,10 @@
}
static void print_procs(void) {
+ static int call = 0;
int i;
struct proc_info *old_proc, *proc;
long unsigned total_delta_time;
- struct passwd *user;
- char *user_str, user_buf[20];
for (i = 0; i < num_new_procs; i++) {
if (new_procs[i]) {
@@ -441,7 +451,7 @@
qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp);
- printf("\n\n\n");
+ if (call++ > 0) printf("\n\n\n");
printf("User %ld%%, System %ld%%, IOW %ld%%, IRQ %ld%%\n",
((new_cpu.utime + new_cpu.ntime) - (old_cpu.utime + old_cpu.ntime)) * 100 / total_delta_time,
((new_cpu.stime ) - (old_cpu.stime)) * 100 / total_delta_time,
@@ -459,30 +469,36 @@
total_delta_time);
printf("\n");
if (!threads)
- printf("%5s %2s %4s %1s %5s %7s %7s %3s %-8s %s\n", "PID", "PR", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "UID", "Name");
+ printf("%5s %-8s %2s %3s %4s %1s %5s %7s %7s %3s %s\n", "PID", "USER", "PR", "NI", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "Name");
else
- printf("%5s %5s %2s %4s %1s %7s %7s %3s %-8s %-15s %s\n", "PID", "TID", "PR", "CPU%", "S", "VSS", "RSS", "PCY", "UID", "Thread", "Proc");
+ printf("%5s %5s %-8s %2s %3s %4s %1s %7s %7s %3s %-15s %s\n", "PID", "TID", "USER", "PR", "NI", "CPU%", "S", "VSS", "RSS", "PCY", "Thread", "Proc");
for (i = 0; i < num_new_procs; i++) {
proc = new_procs[i];
if (!proc || (max_procs && (i >= max_procs)))
break;
- user = getpwuid(proc->uid);
+ struct passwd* user = getpwuid(proc->uid);
+ char user_buf[20];
+ char* user_str;
if (user && user->pw_name) {
user_str = user->pw_name;
} else {
- snprintf(user_buf, 20, "%d", proc->uid);
+ snprintf(user_buf, sizeof(user_buf), "%d", proc->uid);
user_str = user_buf;
}
if (!threads) {
- printf("%5d %2d %3" PRIu64 "%% %c %5d %6" PRIu64 "K %6" PRIu64 "K %3s %-8.8s %s\n",
- proc->pid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads,
- proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->name[0] != 0 ? proc->name : proc->tname);
+ printf("%5d %-8.8s %2s %3ld %3" PRIu64 "%% %c %5d %6" PRIu64 "K %6" PRIu64 "K %3s %s\n",
+ proc->pid, user_str, proc->pr, proc->ni,
+ proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads,
+ proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy,
+ proc->name[0] != 0 ? proc->name : proc->tname);
} else {
- printf("%5d %5d %2d %3" PRIu64 "%% %c %6" PRIu64 "K %6" PRIu64 "K %3s %-8.8s %-15s %s\n",
- proc->pid, proc->tid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state,
- proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->tname, proc->name);
+ printf("%5d %5d %-8.8s %2s %3ld %3" PRIu64 "%% %c %6" PRIu64 "K %6" PRIu64 "K %3s %-15s %s\n",
+ proc->pid, proc->tid, user_str, proc->pr, proc->ni,
+ proc->delta_time * 100 / total_delta_time, proc->state,
+ proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy,
+ proc->tname, proc->name);
}
}
}
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 55d5ee6..1fb34c9 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
+#include <sys/uio.h>
#include <trusty/tipc.h>
@@ -80,6 +81,8 @@
" ta2ta-ipc - execute TA to TA unittest\n"
" dev-uuid - print device uuid\n"
" ta-access - test ta-access flags\n"
+" writev - writev test\n"
+" readv - readv test\n"
"\n"
;
@@ -93,7 +96,7 @@
{
fprintf (stderr, usage, prog);
if (verbose)
- fprintf (stderr, usage_long);
+ fprintf (stderr, "%s", usage_long);
exit(code);
}
@@ -692,6 +695,171 @@
}
+static int writev_test(uint repeat, uint msgsz, bool var)
+{
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx0_buf[msgsz];
+ char tx1_buf[msgsz];
+ char rx_buf [msgsz];
+ struct iovec iovs[2]= {{tx0_buf, 0}, {tx1_buf, 0}};
+
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n",
+ __func__, repeat, msgsz, var ? "true" : "false");
+ }
+
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
+
+ for (i = 0; i < repeat; i++) {
+
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
+
+ iovs[0].iov_len = msg_len / 3;
+ iovs[1].iov_len = msg_len - iovs[0].iov_len;
+
+ memset(tx0_buf, i + 1, iovs[0].iov_len);
+ memset(tx1_buf, i + 2, iovs[1].iov_len);
+ memset(rx_buf, i + 3, sizeof(rx_buf));
+
+ rc = writev(echo_fd, iovs, 2);
+ if (rc < 0) {
+ perror("writev_test: writev");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "writev", (size_t)rc, msg_len);
+ break;
+ }
+
+ rc = read(echo_fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("writev_test: read");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "read", (size_t)rc, msg_len);
+ break;
+ }
+
+ if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
+ fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+ break;
+ }
+
+ if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+ fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+ break;
+ }
+ }
+
+ tipc_close(echo_fd);
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+static int readv_test(uint repeat, uint msgsz, bool var)
+{
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx_buf [msgsz];
+ char rx0_buf[msgsz];
+ char rx1_buf[msgsz];
+ struct iovec iovs[2]= {{rx0_buf, 0}, {rx1_buf, 0}};
+
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n",
+ __func__, repeat, msgsz, var ? "true" : "false");
+ }
+
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
+
+ for (i = 0; i < repeat; i++) {
+
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
+
+ iovs[0].iov_len = msg_len / 3;
+ iovs[1].iov_len = msg_len - iovs[0].iov_len;
+
+ memset(tx_buf, i + 1, sizeof(tx_buf));
+ memset(rx0_buf, i + 2, iovs[0].iov_len);
+ memset(rx1_buf, i + 3, iovs[1].iov_len);
+
+ rc = write(echo_fd, tx_buf, msg_len);
+ if (rc < 0) {
+ perror("readv_test: write");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "write", (size_t)rc, msg_len);
+ break;
+ }
+
+ rc = readv(echo_fd, iovs, 2);
+ if (rc < 0) {
+ perror("readv_test: readv");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "write", (size_t)rc, msg_len);
+ break;
+ }
+
+ if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
+ fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+ break;
+ }
+
+ if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+ fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+ break;
+ }
+ }
+
+ tipc_close(echo_fd);
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+
int main(int argc, char **argv)
{
int rc = 0;
@@ -735,6 +903,10 @@
rc = dev_uuid_test();
} else if (strcmp(test_name, "ta-access") == 0) {
rc = ta_access_test();
+ } else if (strcmp(test_name, "writev") == 0) {
+ rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
+ } else if (strcmp(test_name, "readv") == 0) {
+ rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
} else {
fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
print_usage_and_exit(argv[0], EXIT_FAILURE, true);