Merge changes I7aca7c7c,If9582ae6
* changes:
trusty: tipc_test: fix for building with -Wformat-security flag
trusty: tipc_test: add tests for readv/writev APIs
diff --git a/adb/Android.mk b/adb/Android.mk
index b792bd4..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,8 @@
LIBADB_TEST_SRCS := \
adb_io_test.cpp \
adb_utils_test.cpp \
+ fdevent_test.cpp \
+ socket_test.cpp \
sysdeps_test.cpp \
transport_test.cpp \
@@ -75,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 \
@@ -106,7 +101,6 @@
LOCAL_SRC_FILES := \
$(LIBADB_SRC_FILES) \
adb_auth_client.cpp \
- fdevent.cpp \
jdwp_service.cpp \
usb_linux_client.cpp \
@@ -294,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_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 8a16e51..3333fc6 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -153,7 +153,9 @@
// - 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;
}
@@ -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/commandline.cpp b/adb/commandline.cpp
index 8e76168..30a38e3 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1072,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,
@@ -1106,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);
@@ -1219,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;
@@ -1631,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;
@@ -1644,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 75cbe5d..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 {
@@ -369,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);
@@ -411,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 f84447f..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];
@@ -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",
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 3bae09e..81d201e 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -180,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();
@@ -263,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 );
@@ -304,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] == '\\';
}
@@ -456,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>
@@ -578,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
@@ -803,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 );
@@ -836,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
index 360eaa7..78efea8 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -18,6 +18,7 @@
#include <unistd.h>
#include <atomic>
+#include "adb_io.h"
#include "sysdeps.h"
static void increment_atomic_int(void* c) {
@@ -67,3 +68,150 @@
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/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 d9180bc..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>
@@ -679,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;
@@ -837,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 e6e699b..372bedf 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -225,7 +225,7 @@
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];
@@ -251,19 +251,19 @@
*/
/* 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. */
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/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/bootstat/README.md b/bootstat/README.md
index b3964ce..76ea6c1 100644
--- a/bootstat/README.md
+++ b/bootstat/README.md
@@ -12,6 +12,7 @@
-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/bootstat.cpp b/bootstat/bootstat.cpp
index 0c49f82..bcc8abd 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -80,7 +80,8 @@
" -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_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
@@ -169,7 +170,9 @@
// 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);
- boot_event_store.AddBootEventWithValue("time_since_factory_reset", 0);
+
+ // 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;
}
@@ -192,7 +195,7 @@
int option_index = 0;
static const char boot_reason_str[] = "record_boot_reason";
- static const char factory_reset_str[] = "record_factory_reset";
+ 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' },
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 218b9f8..3c20fc8 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -3,15 +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/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 294bda9..ac1c58c 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -685,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");
@@ -698,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/fastboot/Android.mk b/fastboot/Android.mk
index a326f55..1b4ecbe 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -90,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)
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 853bf0b..c63ff67 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -36,7 +36,7 @@
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;
}
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/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 2022ae7..b33dd28 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -323,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" : "",
@@ -341,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;
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 31c4e09..440f2e4 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -40,6 +40,7 @@
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/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/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/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/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index b975db9..ab09564 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -1412,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/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/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 22ca706..5f2396b 100644
--- a/libcutils/tests/PropertiesTest.cpp
+++ b/libcutils/tests/PropertiesTest.cpp
@@ -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/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 4942c08..aa88bed 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -469,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;
}
}
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/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 f8d4c4c..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.
@@ -1429,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();
@@ -1500,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;
}
@@ -1741,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/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
index b75f1e5..68f654c 100644
--- a/libmemunreachable/Allocator.cpp
+++ b/libmemunreachable/Allocator.cpp
@@ -370,11 +370,11 @@
}
void* HeapImpl::AllocLocked(size_t size) {
- if (__predict_false(size > kMaxBucketAllocationSize)) {
+ if (size > kMaxBucketAllocationSize) {
return MapAlloc(size);
}
int bucket = size_to_bucket(size);
- if (__predict_false(free_chunks_[bucket].empty())) {
+ if (free_chunks_[bucket].empty()) {
Chunk *chunk = new Chunk(this, bucket);
free_chunks_[bucket].insert(chunk->node_);
}
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
index b0a4d4c..a8f579e 100644
--- a/libmemunreachable/Allocator.h
+++ b/libmemunreachable/Allocator.h
@@ -24,6 +24,7 @@
#include <map>
#include <memory>
#include <set>
+#include <unordered_map>
#include <unordered_set>
#include <vector>
extern std::atomic<int> heap_count;
@@ -209,9 +210,12 @@
template<class T>
using list = std::list<T, Allocator<T>>;
-template<class T, class Key, class Compare = std::less<Key>>
+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>>;
diff --git a/libmemunreachable/Android.mk b/libmemunreachable/Android.mk
index 0df26a8..7b66d44 100644
--- a/libmemunreachable/Android.mk
+++ b/libmemunreachable/Android.mk
@@ -3,6 +3,7 @@
memunreachable_srcs := \
Allocator.cpp \
HeapWalker.cpp \
+ LeakFolding.cpp \
LeakPipe.cpp \
LineBuffer.cpp \
MemUnreachable.cpp \
@@ -12,7 +13,9 @@
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 \
@@ -41,3 +44,22 @@
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
index 1a0c33d..19393ec 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -21,17 +21,19 @@
#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;
}
- auto inserted = allocations_.insert(std::pair<Range, RangeInfo>(Range{begin, end}, RangeInfo{false, false}));
+ 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_ += end - begin;
+ allocation_bytes_ += range.size();
return true;
} else {
Range overlap = inserted.first->first;
@@ -44,27 +46,30 @@
}
}
-void HeapWalker::Walk(const Range& range, bool RangeInfo::*flag) {
- allocator::vector<Range> to_do(1, range, allocator_);
+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();
- 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)) {
- uintptr_t val = *reinterpret_cast<uintptr_t*>(i);
- if (val >= valid_allocations_range_.begin && val < valid_allocations_range_.end) {
- RangeMap::iterator it = allocations_.find(Range{val, val + 1});
- if (it != allocations_.end()) {
- if (!(it->second.*flag)) {
- to_do.push_back(it->first);
- it->second.*flag = true;
- }
- }
+
+ 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);
}
- }
+ });
}
}
@@ -85,27 +90,22 @@
}
bool HeapWalker::DetectLeaks() {
+ // Recursively walk pointers from roots to mark referenced allocations
for (auto it = roots_.begin(); it != roots_.end(); it++) {
- Walk(*it, &RangeInfo::referenced_from_root);
+ RecurseRoot(*it);
}
Range vals;
vals.begin = reinterpret_cast<uintptr_t>(root_vals_.data());
vals.end = vals.begin + root_vals_.size() * sizeof(uintptr_t);
- Walk(vals, &RangeInfo::referenced_from_root);
- for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
- if (!it->second.referenced_from_root) {
- Walk(it->first, &RangeInfo::referenced_from_leak);
- }
- }
+ RecurseRoot(vals);
return true;
}
bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit,
size_t* num_leaks_out, size_t* leak_bytes_out) {
- DetectLeaks();
leaked.clear();
size_t num_leaks = 0;
@@ -120,7 +120,7 @@
size_t n = 0;
for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
if (!it->second.referenced_from_root) {
- if (n++ <= limit) {
+ if (n++ < limit) {
leaked.push_back(it->first);
}
}
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 4be1934..7b851c4 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -20,11 +20,14 @@
#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
@@ -34,7 +37,6 @@
}
};
-
class HeapWalker {
public:
HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
@@ -55,16 +57,25 @@
size_t Allocations();
size_t AllocationBytes();
- private:
- struct RangeInfo {
+ template<class F>
+ void ForEachPtrInRange(const Range& range, F&& f);
+
+ template<class F>
+ void ForEachAllocation(F&& f);
+
+ struct AllocationInfo {
bool referenced_from_root;
- bool referenced_from_leak;
};
- void Walk(const Range& range, bool RangeInfo::* flag);
+
+ private:
+
+ void RecurseRoot(const Range& root);
+ bool IsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
+
DISALLOW_COPY_AND_ASSIGN(HeapWalker);
Allocator<HeapWalker> allocator_;
- using RangeMap = allocator::map<RangeInfo, Range, compare_range>;
- RangeMap allocations_;
+ using AllocationMap = allocator::map<Range, AllocationInfo, compare_range>;
+ AllocationMap allocations_;
size_t allocation_bytes_;
Range valid_allocations_range_;
@@ -72,4 +83,28 @@
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/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index cb3f1a5..ac19a66 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -21,12 +21,15 @@
#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"
@@ -117,32 +120,84 @@
return true;
}
-bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
- size_t* num_leaks, size_t* leak_bytes) {
+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();
- allocator::vector<Range> leaked{allocator_};
- if (!heap_walker_.Leaked(leaked, limit, num_leaks, leak_bytes)) {
+ if (!heap_walker_.DetectLeaks()) {
return false;
}
- for (auto it = leaked.begin(); it != leaked.end(); it++) {
- Leak leak{};
- leak.begin = it->begin;
- leak.size = it->end - it->begin;;
- memcpy(leak.contents, reinterpret_cast<void*>(it->begin),
- std::min(leak.size, Leak::contents_length));
- ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it->begin),
- leak.backtrace_frames, leak.backtrace_length);
- if (num_backtrace_frames > 0) {
- leak.num_backtrace_frames = num_backtrace_frames;
- }
- leaks.emplace_back(leak);
- }
+
+ 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;
}
@@ -203,6 +258,11 @@
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();
@@ -224,16 +284,19 @@
// 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;
}
@@ -306,7 +369,7 @@
// Wait for the collection thread to signal that it is ready to fork the
// heap walker process.
- continue_parent_sem.Wait(100s);
+ continue_parent_sem.Wait(30s);
// Re-enable malloc so the collection thread can fork.
}
@@ -336,9 +399,8 @@
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, info.num_leaks == 1 ? "" : "s",
- info.allocation_bytes, info.num_allocations, info.num_allocations == 1 ? "" : "s");
-
+ info.leak_bytes, info.num_leaks, plural(info.num_leaks),
+ info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
return true;
}
@@ -347,9 +409,26 @@
std::ostringstream oss;
oss << " " << std::dec << size;
- oss << " bytes at ";
+ 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;
@@ -358,7 +437,7 @@
if (bytes == size) {
oss << " contents:" << std::endl;
} else {
- oss << " first " << bytes << " bytes of contents:" << std::endl;
+ oss << " first " << bytes << " bytes of contents:" << std::endl;
}
for (size_t i = 0; i < bytes; i += bytes_per_line) {
@@ -382,21 +461,41 @@
oss << std::endl;
}
}
- if (num_backtrace_frames > 0) {
- oss << backtrace_string(backtrace_frames, num_backtrace_frames);
+ 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" << (num_leaks == 1 ? "" : "s");
+ 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();
@@ -405,7 +504,7 @@
std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
UnreachableMemoryInfo info;
if (!GetUnreachableMemory(info, limit)) {
- return "Failed to get unreachable memory";
+ return "Failed to get unreachable memory\n";
}
return info.ToString(log_contents);
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
index 019deea..287f479 100644
--- a/libmemunreachable/ScopedAlarm.h
+++ b/libmemunreachable/ScopedAlarm.h
@@ -18,6 +18,7 @@
#define LIBMEMUNREACHABLE_SCOPED_ALARM_H_
#include <signal.h>
+#include <sys/time.h>
#include <chrono>
#include <functional>
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
index 6357840..e8a8392 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -86,7 +86,7 @@
void PtraceDetach(pid_t tid, unsigned int signal);
bool PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info);
- allocator::map<unsigned int, pid_t> captured_threads_;
+ allocator::map<pid_t, unsigned int> captured_threads_;
Allocator<ThreadCaptureImpl> allocator_;
pid_t pid_;
std::function<void(pid_t)> inject_test_func_;
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
index 92de24a..83d07a8 100644
--- a/libmemunreachable/bionic.h
+++ b/libmemunreachable/bionic.h
@@ -18,6 +18,8 @@
#define LIBMEMUNREACHABLE_BIONIC_H_
#include <sys/cdefs.h>
+#include <stdint.h>
+#include <stdlib.h>
__BEGIN_DECLS
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index f4f01ce..9b227fd 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -27,11 +27,26 @@
struct Leak {
uintptr_t begin;
size_t size;
- size_t num_backtrace_frames;
+
+ 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];
- static const size_t backtrace_length = 16;
- uintptr_t backtrace_frames[backtrace_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;
};
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
index d8e473e..fa76ae0 100644
--- a/libmemunreachable/tests/Allocator_test.cpp
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -15,12 +15,6 @@
*/
#include <Allocator.h>
-#include <sys/time.h>
-
-#include <chrono>
-#include <functional>
-#include <list>
-#include <vector>
#include <gtest/gtest.h>
#include <ScopedDisableMalloc.h>
@@ -28,8 +22,6 @@
std::function<void()> ScopedAlarm::func_;
-using namespace std::chrono_literals;
-
class AllocatorTest : public testing::Test {
protected:
AllocatorTest() : heap(), disable_malloc_() {}
@@ -180,94 +172,3 @@
ASSERT_NE(ptr, nullptr);
}
-
-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/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
index 9921eb6..c3e1c4d 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -80,6 +80,8 @@
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;
@@ -106,9 +108,11 @@
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_T_MAX;
- size_t leaked_bytes = SIZE_T_MAX;
+ 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);
@@ -132,9 +136,11 @@
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_T_MAX;
- size_t leaked_bytes = SIZE_T_MAX;
+ 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);
@@ -143,3 +149,26 @@
}
}
}
+
+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/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/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index da07253..b16c0e6 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -19,13 +19,36 @@
#include "jni.h"
#include <stdint.h>
+#if defined(__ANDROID__)
+#include <android/dlext.h>
+#endif
namespace android {
__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);
+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
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index a7a0713..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>
@@ -57,14 +58,13 @@
class LibraryNamespaces {
public:
- LibraryNamespaces() : initialized_(false) {
- PreloadPublicLibraries();
- }
+ 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;
@@ -77,34 +77,38 @@
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:
+ 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.
@@ -114,6 +118,7 @@
}
}
+ 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.
@@ -122,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;
@@ -163,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/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/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 3663c52..6f88a6d 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -72,7 +72,7 @@
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/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/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/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index fd45c4a0..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 fffc9ba..4eb5e83 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -20,10 +20,13 @@
#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>
@@ -31,7 +34,9 @@
#include "libaudit.h"
#include "LogAudit.h"
+#include "LogBuffer.h"
#include "LogKlog.h"
+#include "LogReader.h"
#ifndef AUDITD_ENFORCE_INTEGRITY
#define AUDITD_ENFORCE_INTEGRITY false
@@ -94,11 +99,20 @@
}
void LogAudit::enforceIntegrity() {
+ static bool loggedOnce;
+ bool once = loggedOnce;
+
+ loggedOnce = true;
+
if (!AUDITD_ENFORCE_INTEGRITY) {
- logToDmesg("integrity enforcement suppressed; not rebooting");
+ if (!once) {
+ logToDmesg("integrity enforcement suppressed; not rebooting");
+ }
} else if (rebootToSafeMode) {
if (getProperty("persist.sys.safemode") == "1") {
- logToDmesg("integrity enforcement suppressed; in safe mode");
+ if (!once) {
+ logToDmesg("integrity enforcement suppressed; in safe mode");
+ }
return;
}
@@ -150,6 +164,10 @@
}
}
+ // 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");
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 455ed58..3a84541 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -18,7 +18,10 @@
#define _LOGD_LOG_AUDIT_H__
#include <sysutils/SocketListener.h>
-#include "LogReader.h"
+
+#include "LogBuffer.h"
+
+class LogReader;
class LogAudit : public SocketListener {
LogBuffer *logbuf;
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/main.cpp b/logd/main.cpp
index f4d7464..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
@@ -265,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) {
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/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 d90f988..d53af2f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -70,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.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/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 fc2898e..b1cdb60 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -38,7 +38,6 @@
nandread \
newfs_msdos \
ps \
- prlimit \
sendevent \
start \
stop \
diff --git a/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
index 30485a0..a2b9111 100755
--- a/toolbox/generate-input.h-labels.py
+++ b/toolbox/generate-input.h-labels.py
@@ -16,6 +16,7 @@
#
# pylint: disable=bad-indentation,bad-continuation
+from __future__ import print_function
import os
import re
import sys
@@ -72,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);
}
}
}