Merge "native_loader: add libvulkan.so to public native library list" into nyc-dev
am: 2f9cd05c47
* commit '2f9cd05c47e7739cda5f931c120cc1f55e2a9e82':
native_loader: add libvulkan.so to public native library list
diff --git a/adb/.clang-format b/adb/.clang-format
index 6737535..fc4eb1b 100644
--- a/adb/.clang-format
+++ b/adb/.clang-format
@@ -2,6 +2,7 @@
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
+AccessModifierOffset: -2
ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
diff --git a/adb/Android.mk b/adb/Android.mk
index d629223..4777883 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -50,6 +50,7 @@
adb_listeners.cpp \
adb_trace.cpp \
adb_utils.cpp \
+ fdevent.cpp \
sockets.cpp \
transport.cpp \
transport_local.cpp \
@@ -58,6 +59,9 @@
LIBADB_TEST_SRCS := \
adb_io_test.cpp \
adb_utils_test.cpp \
+ fdevent_test.cpp \
+ socket_test.cpp \
+ sysdeps_test.cpp \
transport_test.cpp \
LIBADB_CFLAGS := \
@@ -74,12 +78,10 @@
$(ADB_COMMON_windows_CFLAGS) \
LIBADB_darwin_SRC_FILES := \
- fdevent.cpp \
get_my_path_darwin.cpp \
usb_osx.cpp \
LIBADB_linux_SRC_FILES := \
- fdevent.cpp \
get_my_path_linux.cpp \
usb_linux.cpp \
@@ -87,14 +89,6 @@
sysdeps_win32.cpp \
usb_windows.cpp \
-LIBADB_TEST_linux_SRCS := \
- fdevent_test.cpp \
- socket_test.cpp \
-
-LIBADB_TEST_darwin_SRCS := \
- fdevent_test.cpp \
- socket_test.cpp \
-
LIBADB_TEST_windows_SRCS := \
sysdeps_win32_test.cpp \
@@ -105,7 +99,6 @@
LOCAL_SRC_FILES := \
$(LIBADB_SRC_FILES) \
adb_auth_client.cpp \
- fdevent.cpp \
jdwp_service.cpp \
usb_linux_client.cpp \
@@ -157,7 +150,7 @@
LOCAL_SANITIZE := $(adb_target_sanitize)
LOCAL_STATIC_LIBRARIES := libadbd
-LOCAL_SHARED_LIBRARIES := libbase libcutils
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
include $(BUILD_NATIVE_TEST)
# libdiagnose_usb
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 58ccd0a..cb54d04 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -883,8 +883,6 @@
fprintf(stderr, "ADB server didn't ACK\n" );
return -1;
}
-
- setsid();
}
#endif /* !defined(_WIN32) */
return 0;
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 474d1b4..26e376c 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -134,7 +134,7 @@
return result;
}
-// Given a relative or absolute filepath, create the parent directory hierarchy
+// Given a relative or absolute filepath, create the directory hierarchy
// as needed. Returns true if the hierarchy is/was setup.
bool mkdirs(const std::string& path) {
// TODO: all the callers do unlink && mkdirs && adb_creat ---
@@ -157,12 +157,12 @@
return true;
}
+ const std::string parent(adb_dirname(path));
+
// If dirname returned the same path as what we passed in, don't go recursive.
// This can happen on Windows when walking up the directory hierarchy and not
// finding anything that already exists (unlike POSIX that will eventually
// find . or /).
- const std::string parent(adb_dirname(path));
-
if (parent == path) {
errno = ENOENT;
return false;
@@ -174,14 +174,14 @@
}
// Now that the parent directory hierarchy of 'path' has been ensured,
- // create parent itself.
+ // create path itself.
if (adb_mkdir(path, 0775) == -1) {
- // Can't just check for errno == EEXIST because it might be a file that
- // exists.
const int saved_errno = errno;
- if (directory_exists(parent)) {
+ // If someone else created the directory, that is ok.
+ if (directory_exists(path)) {
return true;
}
+ // There might be a pre-existing file at 'path', or there might have been some other error.
errno = saved_errno;
return false;
}
@@ -213,6 +213,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_test.cpp b/adb/adb_utils_test.cpp
index 794dce6..f1ebaa1 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -112,20 +112,26 @@
}
void test_mkdirs(const std::string basepath) {
+ // Test creating a directory hierarchy.
EXPECT_TRUE(mkdirs(basepath));
- EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600));
- EXPECT_FALSE(mkdirs(basepath + "/subdir/"));
+ // Test finding an existing directory hierarchy.
+ EXPECT_TRUE(mkdirs(basepath));
+ const std::string filepath = basepath + "/file";
+ // Verify that the hierarchy was created by trying to create a file in it.
+ EXPECT_NE(-1, adb_creat(filepath.c_str(), 0600));
+ // If a file exists where we want a directory, the operation should fail.
+ EXPECT_FALSE(mkdirs(filepath));
}
TEST(adb_utils, mkdirs) {
TemporaryDir td;
// Absolute paths.
- test_mkdirs(std::string(td.path) + "/dir/subdir/file");
+ test_mkdirs(std::string(td.path) + "/dir/subdir");
// Relative paths.
ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
- test_mkdirs(std::string("relative/subrel/file"));
+ test_mkdirs(std::string("relative/subrel"));
}
#if !defined(_WIN32)
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index b7b30c5..27b7109 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -21,6 +21,7 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
// We only build the affinity WAR code for Linux.
#if defined(__linux__)
@@ -125,6 +126,15 @@
close_stdin();
setup_daemon_logging();
+#if !defined(_WIN32)
+ // Start a new session for the daemon. Do this here instead of after the fork so
+ // that a ctrl-c between the "starting server" and "done starting server" messages
+ // gets a chance to terminate the server.
+ if (setsid() == -1) {
+ fatal("setsid() failed: %s", strerror(errno));
+ }
+#endif
+
// Any error output written to stderr now goes to adb.log. We could
// keep around a copy of the stderr fd and use that to write any errors
// encountered by the following code, but that is probably overkill.
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 85ab4d1..8e76168 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -482,7 +482,7 @@
// Loops to read from stdin and push the data to the given FD.
// The argument should be a pointer to a StdinReadArgs object. This function
// will take ownership of the object and delete it when finished.
-static void* stdin_read_thread_loop(void* x) {
+static void stdin_read_thread_loop(void* x) {
std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
#if !defined(_WIN32)
@@ -586,8 +586,6 @@
}
}
}
-
- return nullptr;
}
// Returns a shell service string with the indicated arguments and command.
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..85aaa61 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -88,7 +88,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 +118,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,6 +186,7 @@
p += sizeof(SyncRequest);
WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
+ expect_done_ = true;
total_bytes_ += data_length;
return true;
}
@@ -220,6 +232,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 +245,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 +381,7 @@
uint64_t expected_total_bytes_;
bool expect_multiple_files_;
+ bool expect_done_;
LinePrinter line_printer_;
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 29c6629..926dbcf 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -183,8 +183,6 @@
}
while (true) {
- unsigned int len;
-
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
if (msg.data.id != ID_DATA) {
@@ -193,17 +191,17 @@
break;
}
SendSyncFail(s, "invalid data message");
- goto fail;
+ goto abort;
}
- len = msg.data.size;
- if (len > buffer.size()) { // TODO: resize buffer?
+
+ if (msg.data.size > buffer.size()) { // TODO: resize buffer?
SendSyncFail(s, "oversize data message");
- goto fail;
+ goto abort;
}
- if (!ReadFdExactly(s, &buffer[0], len)) goto fail;
+ if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
- if (!WriteFdExactly(fd, &buffer[0], len)) {
+ if (!WriteFdExactly(fd, &buffer[0], msg.data.size)) {
SendSyncFailErrno(s, "write failed");
goto fail;
}
@@ -221,6 +219,35 @@
return WriteFdExactly(s, &msg.status, sizeof(msg.status));
fail:
+ // If there's a problem on the device, we'll send an ID_FAIL message and
+ // close the socket. Unfortunately the kernel will sometimes throw that
+ // data away if the other end keeps writing without reading (which is
+ // the case with old versions of adb). To maintain compatibility, keep
+ // reading and throwing away ID_DATA packets until the other side notices
+ // that we've reported an error.
+ while (true) {
+ if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
+
+ if (msg.data.id == ID_DONE) {
+ goto abort;
+ } else if (msg.data.id != ID_DATA) {
+ char id[5];
+ memcpy(id, &msg.data.id, sizeof(msg.data.id));
+ id[4] = '\0';
+ D("handle_send_fail received unexpected id '%s' during failure", id);
+ goto abort;
+ }
+
+ if (msg.data.size > buffer.size()) {
+ D("handle_send_fail received oversized packet of length '%u' during failure",
+ msg.data.size);
+ goto abort;
+ }
+
+ if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
+ }
+
+abort:
if (fd >= 0) adb_close(fd);
if (do_unlink) adb_unlink(path);
return false;
@@ -403,18 +430,6 @@
void file_sync_service(int fd, void* cookie) {
std::vector<char> buffer(SYNC_DATA_MAX);
- // If there's a problem on the device, we'll send an ID_FAIL message and
- // close the socket. Unfortunately the kernel will sometimes throw that
- // data away if the other end keeps writing without reading (which is
- // the normal case with our protocol --- they won't read until the end).
- // So set SO_LINGER to give the client 20s to get around to reading our
- // failure response. Without this, the other side's ability to report
- // useful errors is reduced.
- struct linger l;
- l.l_onoff = 1;
- l.l_linger = 20;
- adb_setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
-
while (handle_sync_command(fd, buffer)) {
}
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 38382c1..460e9dc 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -41,25 +41,25 @@
union syncmsg {
struct __attribute__((packed)) {
- unsigned id;
- unsigned mode;
- unsigned size;
- unsigned time;
+ uint32_t id;
+ uint32_t mode;
+ uint32_t size;
+ uint32_t time;
} stat;
struct __attribute__((packed)) {
- unsigned id;
- unsigned mode;
- unsigned size;
- unsigned time;
- unsigned namelen;
+ uint32_t id;
+ uint32_t mode;
+ uint32_t size;
+ uint32_t time;
+ uint32_t namelen;
} dent;
struct __attribute__((packed)) {
- unsigned id;
- unsigned size;
+ uint32_t id;
+ uint32_t size;
} data;
struct __attribute__((packed)) {
- unsigned id;
- unsigned msglen;
+ uint32_t id;
+ uint32_t msglen;
} status;
};
diff --git a/adb/services.cpp b/adb/services.cpp
index 9cbf787..2eef1c2 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -49,6 +49,7 @@
#include "remount_service.h"
#include "services.h"
#include "shell_service.h"
+#include "sysdeps.h"
#include "transport.h"
struct stinfo {
@@ -57,13 +58,11 @@
void *cookie;
};
-void *service_bootstrap_func(void *x)
-{
+static void service_bootstrap_func(void* x) {
stinfo* sti = reinterpret_cast<stinfo*>(x);
adb_thread_setname(android::base::StringPrintf("service %d", sti->fd));
sti->func(sti->fd, sti->cookie);
free(sti);
- return 0;
}
#if !ADB_HOST
@@ -371,12 +370,21 @@
std::string error = "unknown error";
const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
-
if (t != nullptr && t->connection_state == sinfo->state) {
SendOkay(fd);
break;
} else if (!is_ambiguous) {
- adb_sleep_ms(1000);
+ adb_pollfd pfd = {.fd = fd, .events = POLLIN };
+ int rc = adb_poll(&pfd, 1, 1000);
+ if (rc < 0) {
+ SendFail(fd, error);
+ break;
+ } else if (rc > 0 && (pfd.revents & POLLHUP) != 0) {
+ // The other end of the socket is closed, probably because the other side was
+ // terminated, bail out.
+ break;
+ }
+
// Try again...
} else {
SendFail(fd, error);
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index d080e09..f84447f 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -198,7 +198,7 @@
// Opens the file at |pts_name|.
int OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd);
- static void* ThreadHandler(void* userdata);
+ static void ThreadHandler(void* userdata);
void PassDataStreams();
void WaitForExit();
@@ -465,7 +465,7 @@
return child_fd;
}
-void* Subprocess::ThreadHandler(void* userdata) {
+void Subprocess::ThreadHandler(void* userdata) {
Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
adb_thread_setname(android::base::StringPrintf(
@@ -475,8 +475,6 @@
D("deleting Subprocess for PID %d", subprocess->pid());
delete subprocess;
-
- return nullptr;
}
void Subprocess::PassDataStreams() {
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 03cab64..471ca09 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,52 @@
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__)
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 16796cd..7af2979 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -30,6 +30,7 @@
#include <vector>
// Include this before open/unlink are defined as macros below.
+#include <android-base/errors.h>
#include <android-base/utf8.h>
/*
@@ -114,13 +115,62 @@
LeaveCriticalSection( lock );
}
-typedef void* (*adb_thread_func_t)(void* arg);
+typedef void (*adb_thread_func_t)(void* arg);
+typedef HANDLE adb_thread_t;
-typedef void (*win_thread_func_t)(void* arg);
+struct adb_winthread_args {
+ adb_thread_func_t func;
+ void* arg;
+};
-static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg) {
- uintptr_t tid = _beginthread((win_thread_func_t)func, 0, arg);
- return (tid != static_cast<uintptr_t>(-1L));
+static unsigned __stdcall adb_winthread_wrapper(void* heap_args) {
+ // Move the arguments from the heap onto the thread's stack.
+ adb_winthread_args thread_args = *static_cast<adb_winthread_args*>(heap_args);
+ delete static_cast<adb_winthread_args*>(heap_args);
+ thread_args.func(thread_args.arg);
+ return 0;
+}
+
+static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg,
+ adb_thread_t* thread = nullptr) {
+ adb_winthread_args* args = new adb_winthread_args{.func = func, .arg = arg};
+ uintptr_t handle = _beginthreadex(nullptr, 0, adb_winthread_wrapper, args, 0, nullptr);
+ if (handle != static_cast<uintptr_t>(0)) {
+ if (thread) {
+ *thread = reinterpret_cast<HANDLE>(handle);
+ } else {
+ CloseHandle(thread);
+ }
+ return true;
+ }
+ return false;
+}
+
+static __inline__ bool adb_thread_join(adb_thread_t thread) {
+ switch (WaitForSingleObject(thread, INFINITE)) {
+ case WAIT_OBJECT_0:
+ CloseHandle(thread);
+ return true;
+
+ case WAIT_FAILED:
+ fprintf(stderr, "adb_thread_join failed: %s\n",
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ break;
+
+ default:
+ abort();
+ }
+
+ return false;
+}
+
+static __inline__ bool adb_thread_detach(adb_thread_t thread) {
+ CloseHandle(thread);
+ return true;
+}
+
+static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
+ _endthreadex(0);
}
static __inline__ int adb_thread_setname(const std::string& name) {
@@ -130,6 +180,14 @@
return 0;
}
+static __inline__ adb_thread_t adb_thread_self() {
+ return GetCurrentThread();
+}
+
+static __inline__ bool adb_thread_equal(adb_thread_t lhs, adb_thread_t rhs) {
+ return GetThreadId(lhs) == GetThreadId(rhs);
+}
+
static __inline__ unsigned long adb_thread_id()
{
return GetCurrentThreadId();
@@ -213,24 +271,6 @@
/* normally provided by <cutils/misc.h> */
extern void* load_file(const char* pathname, unsigned* psize);
-/* normally provided by "fdevent.h" */
-
-#define FDE_READ 0x0001
-#define FDE_WRITE 0x0002
-#define FDE_ERROR 0x0004
-#define FDE_DONT_CLOSE 0x0080
-
-typedef void (*fd_func)(int fd, unsigned events, void *userdata);
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg);
-void fdevent_destroy(fdevent *fde);
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
-void fdevent_remove(fdevent *item);
-void fdevent_set(fdevent *fde, unsigned events);
-void fdevent_add(fdevent *fde, unsigned events);
-void fdevent_del(fdevent *fde, unsigned events);
-void fdevent_loop();
-
static __inline__ void adb_sleep_ms( int mseconds )
{
Sleep( mseconds );
@@ -254,6 +294,14 @@
extern int adb_socketpair( int sv[2] );
+struct adb_pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+extern int adb_poll(adb_pollfd* fds, size_t nfds, int timeout);
+#define poll ___xxx_poll
+
static __inline__ int adb_is_absolute_host_path(const char* path) {
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
}
@@ -406,14 +454,14 @@
#else /* !_WIN32 a.k.a. Unix */
-#include "fdevent.h"
#include <cutils/misc.h>
#include <cutils/sockets.h>
#include <cutils/threads.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
#include <pthread.h>
#include <unistd.h>
@@ -656,16 +704,53 @@
#define unix_write adb_write
#define unix_close adb_close
-typedef void* (*adb_thread_func_t)( void* arg );
+// Win32 is limited to DWORDs for thread return values; limit the POSIX systems to this as well to
+// ensure compatibility.
+typedef void (*adb_thread_func_t)(void* arg);
+typedef pthread_t adb_thread_t;
-static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg) {
+struct adb_pthread_args {
+ adb_thread_func_t func;
+ void* arg;
+};
+
+static void* adb_pthread_wrapper(void* heap_args) {
+ // Move the arguments from the heap onto the thread's stack.
+ adb_pthread_args thread_args = *reinterpret_cast<adb_pthread_args*>(heap_args);
+ delete static_cast<adb_pthread_args*>(heap_args);
+ thread_args.func(thread_args.arg);
+ return nullptr;
+}
+
+static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg,
+ adb_thread_t* thread = nullptr) {
+ pthread_t temp;
pthread_attr_t attr;
pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ pthread_attr_setdetachstate(&attr, thread ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED);
+ auto* pthread_args = new adb_pthread_args{.func = start, .arg = arg};
+ errno = pthread_create(&temp, &attr, adb_pthread_wrapper, pthread_args);
+ if (errno == 0) {
+ if (thread) {
+ *thread = temp;
+ }
+ return true;
+ }
+ return false;
+}
- pthread_t thread;
- errno = pthread_create(&thread, &attr, start, arg);
- return (errno == 0);
+static __inline__ bool adb_thread_join(adb_thread_t thread) {
+ errno = pthread_join(thread, nullptr);
+ return errno == 0;
+}
+
+static __inline__ bool adb_thread_detach(adb_thread_t thread) {
+ errno = pthread_detach(thread);
+ return errno == 0;
+}
+
+static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
+ pthread_exit(nullptr);
}
static __inline__ int adb_thread_setname(const std::string& name) {
@@ -716,6 +801,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 );
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
new file mode 100644
index 0000000..78efea8
--- /dev/null
+++ b/adb/sysdeps_test.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <atomic>
+
+#include "adb_io.h"
+#include "sysdeps.h"
+
+static void increment_atomic_int(void* c) {
+ sleep(1);
+ reinterpret_cast<std::atomic<int>*>(c)->fetch_add(1);
+}
+
+TEST(sysdeps_thread, smoke) {
+ std::atomic<int> counter(0);
+
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter));
+ }
+
+ sleep(2);
+ ASSERT_EQ(100, counter.load());
+}
+
+TEST(sysdeps_thread, join) {
+ std::atomic<int> counter(0);
+ std::vector<adb_thread_t> threads(500);
+ for (size_t i = 0; i < threads.size(); ++i) {
+ ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter, &threads[i]));
+ }
+
+ int current = counter.load();
+ ASSERT_GE(current, 0);
+ // Make sure that adb_thread_create actually creates threads, and doesn't do something silly
+ // like synchronously run the function passed in. The sleep in increment_atomic_int should be
+ // enough to keep this from being flakey.
+ ASSERT_LT(current, 500);
+
+ for (const auto& thread : threads) {
+ ASSERT_TRUE(adb_thread_join(thread));
+ }
+
+ ASSERT_EQ(500, counter.load());
+}
+
+TEST(sysdeps_thread, exit) {
+ adb_thread_t thread;
+ ASSERT_TRUE(adb_thread_create(
+ [](void*) {
+ adb_thread_exit();
+ for (;;) continue;
+ },
+ nullptr, &thread));
+ ASSERT_TRUE(adb_thread_join(thread));
+}
+
+TEST(sysdeps_socketpair, smoke) {
+ int fds[2];
+ ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
+ ASSERT_TRUE(WriteFdExactly(fds[0], "foo", 4));
+ ASSERT_TRUE(WriteFdExactly(fds[1], "bar", 4));
+
+ char buf[4];
+ ASSERT_TRUE(ReadFdExactly(fds[1], buf, 4));
+ ASSERT_STREQ(buf, "foo");
+ ASSERT_TRUE(ReadFdExactly(fds[0], buf, 4));
+ ASSERT_STREQ(buf, "bar");
+ ASSERT_EQ(0, adb_close(fds[0]));
+ ASSERT_EQ(0, adb_close(fds[1]));
+}
+
+TEST(sysdeps_fd, exhaustion) {
+ std::vector<int> fds;
+ int socketpair[2];
+
+ while (adb_socketpair(socketpair) == 0) {
+ fds.push_back(socketpair[0]);
+ fds.push_back(socketpair[1]);
+ }
+
+ ASSERT_EQ(EMFILE, errno) << strerror(errno);
+ for (int fd : fds) {
+ ASSERT_EQ(0, adb_close(fd));
+ }
+ ASSERT_EQ(0, adb_socketpair(socketpair));
+ ASSERT_EQ(socketpair[0], fds[0]);
+ ASSERT_EQ(socketpair[1], fds[1]);
+ ASSERT_EQ(0, adb_close(socketpair[0]));
+ ASSERT_EQ(0, adb_close(socketpair[1]));
+}
+
+class sysdeps_poll : public ::testing::Test {
+ protected:
+ int fds[2];
+ void SetUp() override {
+ ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
+ }
+
+ void TearDown() override {
+ if (fds[0] >= 0) {
+ ASSERT_EQ(0, adb_close(fds[0]));
+ }
+ if (fds[1] >= 0) {
+ ASSERT_EQ(0, adb_close(fds[1]));
+ }
+ }
+};
+
+TEST_F(sysdeps_poll, smoke) {
+ adb_pollfd pfd[2] = {};
+ pfd[0].fd = fds[0];
+ pfd[0].events = POLLRDNORM;
+ pfd[1].fd = fds[1];
+ pfd[1].events = POLLWRNORM;
+
+ pfd[0].revents = -1;
+ pfd[1].revents = -1;
+ EXPECT_EQ(1, adb_poll(pfd, 2, 0));
+ EXPECT_EQ(0, pfd[0].revents);
+ EXPECT_EQ(POLLWRNORM, pfd[1].revents);
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ // Wait for the socketpair to be flushed.
+ pfd[0].revents = -1;
+ EXPECT_EQ(1, adb_poll(pfd, 1, 100));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ pfd[0].revents = -1;
+ pfd[1].revents = -1;
+ EXPECT_EQ(2, adb_poll(pfd, 2, 0));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ EXPECT_EQ(POLLWRNORM, pfd[1].revents);
+}
+
+TEST_F(sysdeps_poll, timeout) {
+ adb_pollfd pfd = {};
+ pfd.fd = fds[0];
+ pfd.events = POLLRDNORM;
+
+ EXPECT_EQ(0, adb_poll(&pfd, 1, 100));
+ EXPECT_EQ(0, pfd.revents);
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
+ EXPECT_EQ(POLLRDNORM, pfd.revents);
+}
+
+TEST_F(sysdeps_poll, invalid_fd) {
+ adb_pollfd pfd[3] = {};
+ pfd[0].fd = fds[0];
+ pfd[0].events = POLLRDNORM;
+ pfd[1].fd = INT_MAX;
+ pfd[1].events = POLLRDNORM;
+ pfd[2].fd = fds[1];
+ pfd[2].events = POLLWRNORM;
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ // Wait for the socketpair to be flushed.
+ EXPECT_EQ(1, adb_poll(pfd, 1, 100));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+
+ EXPECT_EQ(3, adb_poll(pfd, 3, 0));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ EXPECT_EQ(POLLNVAL, pfd[1].revents);
+ EXPECT_EQ(POLLWRNORM, pfd[2].revents);
+}
+
+TEST_F(sysdeps_poll, duplicate_fd) {
+ adb_pollfd pfd[2] = {};
+ pfd[0].fd = fds[0];
+ pfd[0].events = POLLRDNORM;
+ pfd[1] = pfd[0];
+
+ EXPECT_EQ(0, adb_poll(pfd, 2, 0));
+ EXPECT_EQ(0, pfd[0].revents);
+ EXPECT_EQ(0, pfd[1].revents);
+
+ ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+ EXPECT_EQ(2, adb_poll(pfd, 2, 100));
+ EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+ EXPECT_EQ(POLLRDNORM, pfd[1].revents);
+}
+
+TEST_F(sysdeps_poll, disconnect) {
+ adb_pollfd pfd = {};
+ pfd.fd = fds[0];
+ pfd.events = POLLIN;
+
+ EXPECT_EQ(0, adb_poll(&pfd, 1, 0));
+ EXPECT_EQ(0, pfd.revents);
+
+ EXPECT_EQ(0, adb_close(fds[1]));
+ fds[1] = -1;
+
+ EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
+
+ // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
+ EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 0b08981..7eae186 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,85 @@
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;
}
}
-
-#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;
- }
-
- pnode = event_looper_find_p( looper, f );
- node = *pnode;
- if ( node == NULL ) {
- node = event_hook_alloc( f );
- node->next = *pnode;
- *pnode = node;
- }
-
- 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);
- }
-}
-
-static void
-event_looper_unhook( EventLooper looper, int fd, int events )
-{
- FH fh = _fh_from_int(fd, __func__);
- EventHook *pnode = event_looper_find_p( looper, fh );
- EventHook node = *pnode;
-
- if (node != NULL) {
- int events2 = events & node->wanted;
- if ( events2 == 0 ) {
- D( "event_looper_unhook: events %x not registered for fd %d", events, fd );
- return;
- }
- node->wanted &= ~events2;
- if (!node->wanted) {
- *pnode = node->next;
- event_hook_free( node );
- }
- }
-}
-
-/*
- * A fixer for WaitForMultipleObjects on condition that there are more than 64
- * handles to wait on.
- *
- * In cetain cases DDMS may establish more than 64 connections with ADB. For
- * instance, this may happen if there are more than 64 processes running on a
- * device, or there are multiple devices connected (including the emulator) with
- * the combined number of running processes greater than 64. In this case using
- * WaitForMultipleObjects to wait on connection events simply wouldn't cut,
- * because of the API limitations (64 handles max). So, we need to provide a way
- * to scale WaitForMultipleObjects to accept an arbitrary number of handles. The
- * easiest (and "Microsoft recommended") way to do that would be dividing the
- * handle array into chunks with the chunk size less than 64, and fire up as many
- * waiting threads as there are chunks. Then each thread would wait on a chunk of
- * handles, and will report back to the caller which handle has been set.
- * Here is the implementation of that algorithm.
- */
-
-/* Number of handles to wait on in each wating thread. */
-#define WAIT_ALL_CHUNK_SIZE 63
-
-/* Descriptor for a wating thread */
-typedef struct WaitForAllParam {
- /* A handle to an event to signal when waiting is over. This handle is shared
- * accross all the waiting threads, so each waiting thread knows when any
- * other thread has exited, so it can exit too. */
- HANDLE main_event;
- /* Upon exit from a waiting thread contains the index of the handle that has
- * been signaled. The index is an absolute index of the signaled handle in
- * the original array. This pointer is shared accross all the waiting threads
- * and it's not guaranteed (due to a race condition) that when all the
- * waiting threads exit, the value contained here would indicate the first
- * handle that was signaled. This is fine, because the caller cares only
- * about any handle being signaled. It doesn't care about the order, nor
- * about the whole list of handles that were signaled. */
- LONG volatile *signaled_index;
- /* Array of handles to wait on in a waiting thread. */
- HANDLE* handles;
- /* Number of handles in 'handles' array to wait on. */
- int handles_count;
- /* Index inside the main array of the first handle in the 'handles' array. */
- int first_handle_index;
- /* Waiting thread handle. */
- HANDLE thread;
-} WaitForAllParam;
-
-/* Waiting thread routine. */
-static unsigned __stdcall
-_in_waiter_thread(void* arg)
-{
- HANDLE wait_on[WAIT_ALL_CHUNK_SIZE + 1];
- int res;
- WaitForAllParam* const param = (WaitForAllParam*)arg;
-
- /* We have to wait on the main_event in order to be notified when any of the
- * sibling threads is exiting. */
- wait_on[0] = param->main_event;
- /* The rest of the handles go behind the main event handle. */
- memcpy(wait_on + 1, param->handles, param->handles_count * sizeof(HANDLE));
-
- res = WaitForMultipleObjects(param->handles_count + 1, wait_on, FALSE, INFINITE);
- if (res > 0 && res < (param->handles_count + 1)) {
- /* One of the original handles got signaled. Save its absolute index into
- * the output variable. */
- InterlockedCompareExchange(param->signaled_index,
- res - 1L + param->first_handle_index, -1L);
- }
-
- /* Notify the caller (and the siblings) that the wait is over. */
- SetEvent(param->main_event);
-
- _endthreadex(0);
- return 0;
-}
-
-/* WaitForMultipeObjects fixer routine.
- * Param:
- * handles Array of handles to wait on.
- * handles_count Number of handles in the array.
- * Return:
- * (>= 0 && < handles_count) - Index of the signaled handle in the array, or
- * WAIT_FAILED on an error.
- */
-static int
-_wait_for_all(HANDLE* handles, int handles_count)
-{
- WaitForAllParam* threads;
- HANDLE main_event;
- int chunks, chunk, remains;
-
- /* This variable is going to be accessed by several threads at the same time,
- * this is bound to fail randomly when the core is run on multi-core machines.
- * To solve this, we need to do the following (1 _and_ 2):
- * 1. Use the "volatile" qualifier to ensure the compiler doesn't optimize
- * out the reads/writes in this function unexpectedly.
- * 2. Ensure correct memory ordering. The "simple" way to do that is to wrap
- * all accesses inside a critical section. But we can also use
- * InterlockedCompareExchange() which always provide a full memory barrier
- * on Win32.
- */
- volatile LONG sig_index = -1;
-
- /* Calculate number of chunks, and allocate thread param array. */
- chunks = handles_count / WAIT_ALL_CHUNK_SIZE;
- remains = handles_count % WAIT_ALL_CHUNK_SIZE;
- threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) *
- sizeof(WaitForAllParam));
- if (threads == NULL) {
- D("Unable to allocate thread array for %d handles.", handles_count);
- return (int)WAIT_FAILED;
- }
-
- /* Create main event to wait on for all waiting threads. This is a "manualy
- * reset" event that will remain set once it was set. */
- main_event = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (main_event == NULL) {
- D("Unable to create main event. Error: %ld", GetLastError());
- free(threads);
- return (int)WAIT_FAILED;
- }
-
- /*
- * Initialize waiting thread parameters.
- */
-
- for (chunk = 0; chunk < chunks; chunk++) {
- threads[chunk].main_event = main_event;
- threads[chunk].signaled_index = &sig_index;
- threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
- threads[chunk].handles = handles + threads[chunk].first_handle_index;
- threads[chunk].handles_count = WAIT_ALL_CHUNK_SIZE;
- }
- if (remains) {
- threads[chunk].main_event = main_event;
- threads[chunk].signaled_index = &sig_index;
- threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
- threads[chunk].handles = handles + threads[chunk].first_handle_index;
- threads[chunk].handles_count = remains;
- chunks++;
- }
-
- /* Start the waiting threads. */
- for (chunk = 0; chunk < chunks; chunk++) {
- /* Note that using adb_thread_create is not appropriate here, since we
- * need a handle to wait on for thread termination. */
- threads[chunk].thread = (HANDLE)_beginthreadex(NULL, 0, _in_waiter_thread,
- &threads[chunk], 0, NULL);
- if (threads[chunk].thread == NULL) {
- /* Unable to create a waiter thread. Collapse. */
- D("Unable to create a waiting thread %d of %d. errno=%d",
- chunk, chunks, errno);
- chunks = chunk;
- SetEvent(main_event);
- break;
- }
- }
-
- /* Wait on any of the threads to get signaled. */
- WaitForSingleObject(main_event, INFINITE);
-
- /* Wait on all the waiting threads to exit. */
- for (chunk = 0; chunk < chunks; chunk++) {
- WaitForSingleObject(threads[chunk].thread, INFINITE);
- CloseHandle(threads[chunk].thread);
- }
-
- CloseHandle(main_event);
- free(threads);
-
-
- const int ret = (int)InterlockedCompareExchange(&sig_index, -1, -1);
- return (ret >= 0) ? ret : (int)WAIT_FAILED;
-}
-
-static EventLooperRec win32_looper;
-
-static void fdevent_init(void)
-{
- win32_looper.htab_count = 0;
- win32_looper.hooks = NULL;
-}
-
-static void fdevent_connect(fdevent *fde)
-{
- EventLooper looper = &win32_looper;
- int events = fde->state & FDE_EVENTMASK;
-
- if (events != 0)
- event_looper_hook( looper, fde->fd, events );
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
- EventLooper looper = &win32_looper;
- int events = fde->state & FDE_EVENTMASK;
-
- if (events != 0)
- event_looper_unhook( looper, fde->fd, events );
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
- EventLooper looper = &win32_looper;
- unsigned events0 = fde->state & FDE_EVENTMASK;
-
- if (events != events0) {
- int removes = events0 & ~events;
- int adds = events & ~events0;
- if (removes) {
- D("fdevent_update: remove %x from %d", removes, fde->fd);
- event_looper_unhook( looper, fde->fd, removes );
- }
- if (adds) {
- D("fdevent_update: add %x to %d", adds, fde->fd);
- event_looper_hook ( looper, fde->fd, adds );
- }
- }
-}
-
-static void fdevent_process()
-{
- EventLooper looper = &win32_looper;
- EventHook hook;
- int gotone = 0;
-
- /* if we have at least one ready hook, execute it/them */
- for (hook = looper->hooks; hook; hook = hook->next) {
- hook->ready = 0;
- if (hook->prepare) {
- hook->prepare(hook);
- if (hook->ready != 0) {
- event_hook_signal( hook );
- gotone = 1;
- }
- }
- }
-
- /* nothing's ready yet, so wait for something to happen */
- if (!gotone)
- {
- looper->htab_count = 0;
-
- for (hook = looper->hooks; hook; hook = hook->next)
- {
- if (hook->start && !hook->start(hook)) {
- D( "fdevent_process: error when starting a hook" );
- return;
- }
- if (hook->h != INVALID_HANDLE_VALUE) {
- int nn;
-
- for (nn = 0; nn < looper->htab_count; nn++)
- {
- if ( looper->htab[nn] == hook->h )
- goto DontAdd;
- }
- looper->htab[ looper->htab_count++ ] = hook->h;
- DontAdd:
- ;
- }
- }
-
- if (looper->htab_count == 0) {
- D( "fdevent_process: nothing to wait for !!" );
- return;
- }
-
- do
- {
- int wait_ret;
-
- D( "adb_win32: waiting for %d events", looper->htab_count );
- if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
- D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.", looper->htab_count);
- wait_ret = _wait_for_all(looper->htab, looper->htab_count);
- } else {
- wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE );
- }
- if (wait_ret == (int)WAIT_FAILED) {
- D( "adb_win32: wait failed, error %ld", GetLastError() );
- } else {
- D( "adb_win32: got one (index %d)", wait_ret );
-
- /* according to Cygwin, some objects like consoles wake up on "inappropriate" events
- * like mouse movements. we need to filter these with the "check" function
- */
- if ((unsigned)wait_ret < (unsigned)looper->htab_count)
- {
- for (hook = looper->hooks; hook; hook = hook->next)
- {
- if ( looper->htab[wait_ret] == hook->h &&
- (!hook->check || hook->check(hook)) )
- {
- D( "adb_win32: signaling %s for %x", hook->fh->name, hook->ready );
- event_hook_signal( hook );
- gotone = 1;
- break;
- }
- }
- }
- }
- }
- while (!gotone);
-
- for (hook = looper->hooks; hook; hook = hook->next) {
- if (hook->stop)
- hook->stop( hook );
- }
- }
-
- for (hook = looper->hooks; hook; hook = hook->next) {
- if (hook->peek && hook->peek(hook))
- event_hook_signal( hook );
- }
-}
-
-
-static void fdevent_register(fdevent *fde)
-{
- int fd = fde->fd - WIN32_FH_BASE;
-
- if(fd < 0) {
- FATAL("bogus negative fd (%d)\n", fde->fd);
- }
-
- if(fd >= fd_table_max) {
- int oldmax = fd_table_max;
- if(fde->fd > 32000) {
- FATAL("bogus huuuuge fd (%d)\n", fde->fd);
- }
- if(fd_table_max == 0) {
- fdevent_init();
- fd_table_max = 256;
- }
- while(fd_table_max <= fd) {
- fd_table_max *= 2;
- }
- fd_table = reinterpret_cast<fdevent**>(realloc(fd_table, sizeof(fdevent*) * fd_table_max));
- if(fd_table == 0) {
- FATAL("could not expand fd_table to %d entries\n", fd_table_max);
- }
- memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
- }
-
- fd_table[fd] = fde;
-}
-
-static void fdevent_unregister(fdevent *fde)
-{
- int fd = fde->fd - WIN32_FH_BASE;
-
- if((fd < 0) || (fd >= fd_table_max)) {
- FATAL("fd out of range (%d)\n", fde->fd);
- }
-
- if(fd_table[fd] != fde) {
- FATAL("fd_table out of sync");
- }
-
- fd_table[fd] = 0;
-
- if(!(fde->state & FDE_DONT_CLOSE)) {
- dump_fde(fde, "close");
- adb_close(fde->fd);
- }
-}
-
-static void fdevent_plist_enqueue(fdevent *node)
-{
- fdevent *list = &list_pending;
-
- node->next = list;
- node->prev = list->prev;
- node->prev->next = node;
- list->prev = node;
-}
-
-static void fdevent_plist_remove(fdevent *node)
-{
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->next = 0;
- node->prev = 0;
-}
-
-static fdevent *fdevent_plist_dequeue(void)
-{
- fdevent *list = &list_pending;
- fdevent *node = list->next;
-
- if(node == list) return 0;
-
- list->next = node->next;
- list->next->prev = list;
- node->next = 0;
- node->prev = 0;
-
- return node;
-}
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
- fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
- if(fde == 0) return 0;
- fdevent_install(fde, fd, func, arg);
- fde->state |= FDE_CREATED;
- return fde;
-}
-
-void fdevent_destroy(fdevent *fde)
-{
- if(fde == 0) return;
- if(!(fde->state & FDE_CREATED)) {
- FATAL("fde %p not created by fdevent_create()\n", fde);
- }
- fdevent_remove(fde);
-}
-
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
-{
- memset(fde, 0, sizeof(fdevent));
- fde->state = FDE_ACTIVE;
- fde->fd = fd;
- fde->func = func;
- fde->arg = arg;
-
- fdevent_register(fde);
- dump_fde(fde, "connect");
- fdevent_connect(fde);
- fde->state |= FDE_ACTIVE;
-}
-
-void fdevent_remove(fdevent *fde)
-{
- if(fde->state & FDE_PENDING) {
- fdevent_plist_remove(fde);
- }
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_disconnect(fde);
- dump_fde(fde, "disconnect");
- fdevent_unregister(fde);
- }
-
- fde->state = 0;
- fde->events = 0;
-}
-
-
-void fdevent_set(fdevent *fde, unsigned events)
-{
- events &= FDE_EVENTMASK;
-
- if((fde->state & FDE_EVENTMASK) == (int)events) return;
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_update(fde, events);
- dump_fde(fde, "update");
- }
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-
- if(fde->state & FDE_PENDING) {
- /* if we're pending, make sure
- ** we don't signal an event that
- ** is no longer wanted.
- */
- fde->events &= (~events);
- if(fde->events == 0) {
- fdevent_plist_remove(fde);
- fde->state &= (~FDE_PENDING);
- }
- }
-}
-
-void fdevent_add(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
-}
-
-void fdevent_del(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
-}
-
-void fdevent_loop()
-{
- fdevent *fde;
-
- for(;;) {
-#if DEBUG
- fprintf(stderr,"--- ---- waiting for events\n");
-#endif
- fdevent_process();
-
- while((fde = fdevent_plist_dequeue())) {
- unsigned events = fde->events;
- fde->events = 0;
- fde->state &= (~FDE_PENDING);
- dump_fde(fde, "callback");
- fde->func(fde->fd, events, fde->arg);
- }
- }
-}
-
-/** FILE EVENT HOOKS
- **/
-
-static void _event_file_prepare( EventHook hook )
-{
- if (hook->wanted & (FDE_READ|FDE_WRITE)) {
- /* we can always read/write */
- hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE);
- }
-}
-
-static int _event_file_peek( EventHook hook )
-{
- return (hook->wanted & (FDE_READ|FDE_WRITE));
-}
-
-static void _fh_file_hook( FH f, int events, EventHook hook )
-{
- hook->h = f->fh_handle;
- hook->prepare = _event_file_prepare;
- hook->peek = _event_file_peek;
-}
-
-/** SOCKET EVENT HOOKS
- **/
-
-static void _event_socket_verify( EventHook hook, WSANETWORKEVENTS* evts )
-{
- if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) {
- if (hook->wanted & FDE_READ)
- hook->ready |= FDE_READ;
- if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
- if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) {
- if (hook->wanted & FDE_WRITE)
- hook->ready |= FDE_WRITE;
- if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
- if ( evts->lNetworkEvents & FD_OOB ) {
- if (hook->wanted & FDE_ERROR)
- hook->ready |= FDE_ERROR;
- }
-}
-
-static void _event_socket_prepare( EventHook hook )
-{
- WSANETWORKEVENTS evts;
-
- /* look if some of the events we want already happened ? */
- if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts ))
- _event_socket_verify( hook, &evts );
-}
-
-static int _socket_wanted_to_flags( int wanted )
-{
- int flags = 0;
- if (wanted & FDE_READ)
- flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
-
- if (wanted & FDE_WRITE)
- flags |= FD_WRITE | FD_CONNECT | FD_CLOSE;
-
- if (wanted & FDE_ERROR)
- flags |= FD_OOB;
-
- return flags;
-}
-
-static int _event_socket_start( EventHook hook )
-{
- /* create an event which we're going to wait for */
- FH fh = hook->fh;
- long flags = _socket_wanted_to_flags( hook->wanted );
-
- hook->h = fh->event;
- if (hook->h == INVALID_HANDLE_VALUE) {
- D( "_event_socket_start: no event for %s", fh->name );
- return 0;
- }
-
- if ( flags != fh->mask ) {
- D( "_event_socket_start: hooking %s for %x (flags %ld)", hook->fh->name, hook->wanted, flags );
- if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
- D( "_event_socket_start: WSAEventSelect() for %s failed, error %d", hook->fh->name, WSAGetLastError() );
- CloseHandle( hook->h );
- hook->h = INVALID_HANDLE_VALUE;
- exit(1);
- return 0;
- }
- fh->mask = flags;
- }
- return 1;
-}
-
-static void _event_socket_stop( EventHook hook )
-{
- hook->h = INVALID_HANDLE_VALUE;
-}
-
-static int _event_socket_check( EventHook hook )
-{
- int result = 0;
- FH fh = hook->fh;
- WSANETWORKEVENTS evts;
-
- if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) {
- _event_socket_verify( hook, &evts );
- result = (hook->ready != 0);
- if (result) {
- ResetEvent( hook->h );
- }
- }
- D( "_event_socket_check %s returns %d", fh->name, result );
- return result;
-}
-
-static int _event_socket_peek( EventHook hook )
-{
- WSANETWORKEVENTS evts;
- FH fh = hook->fh;
-
- /* look if some of the events we want already happened ? */
- if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) {
- _event_socket_verify( hook, &evts );
- if (hook->ready)
- ResetEvent( hook->h );
- }
-
- return hook->ready != 0;
-}
-
-
-
-static void _fh_socket_hook( FH f, int events, EventHook hook )
-{
- hook->prepare = _event_socket_prepare;
- hook->start = _event_socket_start;
- hook->stop = _event_socket_stop;
- hook->check = _event_socket_check;
- hook->peek = _event_socket_peek;
-
- // TODO: check return value?
- _event_socket_start( hook );
-}
-
-/** SOCKETPAIR EVENT HOOKS
- **/
-
-static void _event_socketpair_prepare( EventHook hook )
-{
- FH fh = hook->fh;
- SocketPair pair = fh->fh_pair;
- BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
- BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
-
- if (hook->wanted & FDE_READ && rbip->can_read)
- hook->ready |= FDE_READ;
-
- if (hook->wanted & FDE_WRITE && wbip->can_write)
- hook->ready |= FDE_WRITE;
- }
-
- static int _event_socketpair_start( EventHook hook )
- {
- FH fh = hook->fh;
- SocketPair pair = fh->fh_pair;
- BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
- BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
-
- if (hook->wanted == FDE_READ)
- hook->h = rbip->evt_read;
-
- else if (hook->wanted == FDE_WRITE)
- hook->h = wbip->evt_write;
-
- else {
- D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE" );
- return 0;
- }
- D( "_event_socketpair_start: hook %s for %x wanted=%x",
- hook->fh->name, _fh_to_int(fh), hook->wanted);
- return 1;
-}
-
-static int _event_socketpair_peek( EventHook hook )
-{
- _event_socketpair_prepare( hook );
- return hook->ready != 0;
-}
-
-static void _fh_socketpair_hook( FH fh, int events, EventHook hook )
-{
- hook->prepare = _event_socketpair_prepare;
- hook->start = _event_socketpair_start;
- hook->peek = _event_socketpair_peek;
-}
-
-
static adb_mutex_t g_console_output_buffer_lock;
void
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 8f610cf..c3a3fd7 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -87,8 +87,12 @@
TestAdbStrError(-1, "Unknown error");
// Test very big, positive unknown error.
TestAdbStrError(1000000, "Unknown error");
+
// Test success case.
- TestAdbStrError(0, "No error");
+ // Wine returns "Success" for strerror(0), Windows returns "No error", so accept both.
+ std::string success = adb_strerror(0);
+ EXPECT_TRUE(success == "Success" || success == "No error") << "strerror(0) = " << success;
+
// Test error that regular strerror() should have a string for.
TestAdbStrError(EPERM, "Operation not permitted");
// Test error that regular strerror() doesn't have a string for, but that
diff --git a/adb/test_device.py b/adb/test_device.py
index afc061a..18174a2 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -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)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index f8c8c61..8ca1e49 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -190,8 +190,7 @@
//
// read_transport thread reads data from a transport (representing a usb/tcp connection),
// and makes the main thread call handle_packet().
-static void *read_transport_thread(void *_t)
-{
+static void read_transport_thread(void* _t) {
atransport *t = reinterpret_cast<atransport*>(_t);
apacket *p;
@@ -244,13 +243,11 @@
D("%s: read_transport thread is exiting", t->serial);
kick_transport(t);
transport_unref(t);
- return 0;
}
// write_transport thread gets packets sent by the main thread (through send_packet()),
// and writes to a transport (representing a usb/tcp connection).
-static void *write_transport_thread(void *_t)
-{
+static void write_transport_thread(void* _t) {
atransport *t = reinterpret_cast<atransport*>(_t);
apacket *p;
int active = 0;
@@ -295,7 +292,6 @@
D("%s: write_transport thread is exiting, fd %d", t->serial, t->fd);
kick_transport(t);
transport_unref(t);
- return 0;
}
static void kick_transport_locked(atransport* t) {
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index d2a375a..e6e699b 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -121,8 +121,7 @@
}
#if ADB_HOST
-static void *client_socket_thread(void *x)
-{
+static void client_socket_thread(void* x) {
adb_thread_setname("client_socket_thread");
D("transport: client_socket_thread() starting");
while (true) {
@@ -135,13 +134,11 @@
}
sleep(1);
}
- return 0;
}
#else // ADB_HOST
-static void *server_socket_thread(void * arg)
-{
+static void server_socket_thread(void* arg) {
int serverfd, fd;
sockaddr_storage ss;
sockaddr *addrp = reinterpret_cast<sockaddr*>(&ss);
@@ -174,7 +171,6 @@
}
}
D("transport: server_socket_thread() exiting");
- return 0;
}
/* This is relevant only for ADB daemon running inside the emulator. */
@@ -220,14 +216,13 @@
* the transport registration is completed. That's why we need to send the
* 'start' request after the transport is registered.
*/
-static void *qemu_socket_thread(void * arg)
-{
-/* 'accept' request to the adb QEMUD service. */
-static const char _accept_req[] = "accept";
-/* 'start' request to the adb QEMUD service. */
-static const char _start_req[] = "start";
-/* 'ok' reply from the adb QEMUD service. */
-static const char _ok_resp[] = "ok";
+static void qemu_socket_thread(void* arg) {
+ /* 'accept' request to the adb QEMUD service. */
+ static const char _accept_req[] = "accept";
+ /* 'start' request to the adb QEMUD service. */
+ static const char _start_req[] = "start";
+ /* 'ok' reply from the adb QEMUD service. */
+ static const char _ok_resp[] = "ok";
const int port = (int) (uintptr_t) arg;
int res, fd;
@@ -247,7 +242,7 @@
* implement adb QEMUD service. Fall back to the old TCP way. */
D("adb service is not available. Falling back to TCP socket.");
adb_thread_create(server_socket_thread, arg);
- return 0;
+ return;
}
for(;;) {
@@ -275,21 +270,21 @@
fd = qemu_pipe_open(con_name);
if (fd < 0) {
D("adb service become unavailable.");
- return 0;
+ return;
}
} else {
D("Unable to send the '%s' request to ADB service.", _accept_req);
- return 0;
+ return;
}
}
D("transport: qemu_socket_thread() exiting");
- return 0;
+ return;
}
#endif // !ADB_HOST
void local_init(int port)
{
- void* (*func)(void *);
+ adb_thread_func_t func;
const char* debug_name = "";
#if ADB_HOST
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index ed5d2d67..500898a 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -571,7 +571,7 @@
register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
}
-static void* device_poll_thread(void* unused) {
+static void device_poll_thread(void*) {
adb_thread_setname("device poll");
D("Created device thread");
while (true) {
@@ -580,7 +580,6 @@
kick_disconnected_devices();
sleep(1);
}
- return nullptr;
}
void usb_init() {
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index a4f1a70..c863ed2 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -232,10 +232,7 @@
},
};
-
-
-static void *usb_adb_open_thread(void *x)
-{
+static void usb_adb_open_thread(void* x) {
struct usb_handle *usb = (struct usb_handle *)x;
int fd;
@@ -270,7 +267,7 @@
}
// never gets here
- return 0;
+ abort();
}
static int usb_adb_write(usb_handle *h, const void *data, int len)
@@ -434,8 +431,7 @@
return;
}
-static void *usb_ffs_open_thread(void *x)
-{
+static void usb_ffs_open_thread(void* x) {
struct usb_handle *usb = (struct usb_handle *)x;
adb_thread_setname("usb ffs open");
@@ -462,7 +458,7 @@
}
// never gets here
- return 0;
+ abort();
}
static int usb_ffs_write(usb_handle* h, const void* data, int len) {
diff --git a/adb/usb_osx.cpp b/adb/usb_osx.cpp
index 148be1d..54d4c6c 100644
--- a/adb/usb_osx.cpp
+++ b/adb/usb_osx.cpp
@@ -400,9 +400,7 @@
return NULL;
}
-
-void* RunLoopThread(void* unused)
-{
+static void RunLoopThread(void* unused) {
adb_thread_setname("RunLoop");
InitUSB();
@@ -420,7 +418,6 @@
IONotificationPortDestroy(notificationPort);
LOG(DEBUG) << "RunLoopThread done";
- return NULL;
}
static void usb_cleanup() {
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index e79008f..8ecca37 100644
--- a/adb/usb_windows.cpp
+++ b/adb/usb_windows.cpp
@@ -97,7 +97,7 @@
/// Entry point for thread that polls (every second) for new usb interfaces.
/// This routine calls find_devices in infinite loop.
-void* device_poll_thread(void* unused);
+static void device_poll_thread(void*);
/// Initializes this module
void usb_init();
@@ -172,7 +172,7 @@
return 1;
}
-void* device_poll_thread(void* unused) {
+void device_poll_thread(void*) {
adb_thread_setname("Device Poll");
D("Created device thread");
@@ -180,8 +180,6 @@
find_devices();
adb_sleep_ms(1000);
}
-
- return NULL;
}
static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
@@ -203,7 +201,7 @@
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
-static void* _power_notification_thread(void* unused) {
+static void _power_notification_thread(void*) {
// This uses a thread with its own window message pump to get power
// notifications. If adb runs from a non-interactive service account, this
// might not work (not sure). If that happens to not work, we could use
@@ -255,8 +253,6 @@
// shutting down. Not a big deal since the whole process will be going away
// soon anyway.
D("Power notification thread exiting");
-
- return NULL;
}
void usb_init() {
diff --git a/base/file.cpp b/base/file.cpp
index bcdfc5e..da1adba 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -29,10 +29,6 @@
#include "cutils/log.h"
#include "utils/Compat.h"
-#if !defined(_WIN32)
-#define O_BINARY 0
-#endif
-
namespace android {
namespace base {
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 486befc..5342d98 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -20,6 +20,10 @@
#include <sys/stat.h>
#include <string>
+#if !defined(_WIN32) && !defined(O_BINARY)
+#define O_BINARY 0
+#endif
+
namespace android {
namespace base {
diff --git a/base/logging.cpp b/base/logging.cpp
index a385902..1741871 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -68,7 +68,13 @@
#include <windows.h>
#endif
-static pid_t GetThreadId() {
+#if defined(_WIN32)
+typedef uint32_t thread_id;
+#else
+typedef pid_t thread_id;
+#endif
+
+static thread_id GetThreadId() {
#if defined(__BIONIC__)
return gettid();
#elif defined(__APPLE__)
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 337ba7c..635af6c 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -16,7 +16,6 @@
#include "android-base/logging.h"
#include "android-base/test_utils.h"
-#include "utils/Compat.h" // For OS_PATH_SEPARATOR.
#include <fcntl.h>
#include <stdio.h>
@@ -27,6 +26,9 @@
#if defined(_WIN32)
#include <windows.h>
#include <direct.h>
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
#endif
#include <string>
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 8efbacc..ac1c58c 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -678,13 +678,6 @@
// Ignore failed writes to closed sockets
signal(SIGPIPE, SIG_IGN);
- int logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);
- if (logsocket < 0) {
- logsocket = -1;
- } else {
- fcntl(logsocket, F_SETFD, FD_CLOEXEC);
- }
-
struct sigaction act;
act.sa_handler = SIG_DFL;
sigemptyset(&act.sa_mask);
@@ -692,10 +685,9 @@
act.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &act, 0);
- int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
- if (s < 0)
- return 1;
- fcntl(s, F_SETFD, FD_CLOEXEC);
+ int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
+ SOCK_STREAM | SOCK_CLOEXEC);
+ if (s == -1) return 1;
ALOGI("debuggerd: starting\n");
@@ -705,14 +697,12 @@
socklen_t alen = sizeof(ss);
ALOGV("waiting for connection\n");
- int fd = accept(s, addrp, &alen);
- if (fd < 0) {
- ALOGV("accept failed: %s\n", strerror(errno));
+ int fd = accept4(s, addrp, &alen, SOCK_CLOEXEC);
+ if (fd == -1) {
+ ALOGE("accept failed: %s\n", strerror(errno));
continue;
}
- fcntl(fd, F_SETFD, FD_CLOEXEC);
-
handle_request(fd);
}
return 0;
diff --git a/debuggerd/getevent.cpp b/debuggerd/getevent.cpp
index 751c4fb..e5acd17 100644
--- a/debuggerd/getevent.cpp
+++ b/debuggerd/getevent.cpp
@@ -26,6 +26,7 @@
#include <sys/poll.h>
#include <linux/input.h>
#include <errno.h>
+#include <memory>
#include <cutils/log.h>
static struct pollfd* ufds;
@@ -143,22 +144,20 @@
static int scan_dir(const char* dirname) {
char devname[PATH_MAX];
char* filename;
- DIR* dir;
struct dirent* de;
- dir = opendir(dirname);
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
if (dir == NULL)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
- while ((de = readdir(dir))) {
+ while ((de = readdir(dir.get()))) {
if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
(de->d_name[1] == '.' && de->d_name[2] == '\0'))
continue;
strcpy(filename, de->d_name);
open_device(devname);
}
- closedir(dir);
return 0;
}
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 02ffcd9..d4824fb 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -145,7 +145,7 @@
int in, out;
unsigned i;
unsigned e;
-
+
if (check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
return -1;
dev = (struct usb_device_descriptor *)ptr;
@@ -333,15 +333,14 @@
char desc[1024];
int n, in, out, ifc;
- DIR *busdir;
struct dirent *de;
int fd;
int writable;
- busdir = opendir(base);
+ std::unique_ptr<DIR, decltype(&closedir)> busdir(opendir(base), closedir);
if (busdir == 0) return 0;
- while ((de = readdir(busdir)) && (usb == nullptr)) {
+ while ((de = readdir(busdir.get())) && (usb == nullptr)) {
if (badname(de->d_name)) continue;
if (!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
@@ -377,7 +376,6 @@
}
}
}
- closedir(busdir);
return usb;
}
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
index f8df081..cd3c8fc 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -33,7 +33,7 @@
static int format_ext4(char *fs_blkdev, char *fs_mnt_point)
{
- unsigned int nr_sec;
+ uint64_t dev_sz;
int fd, rc = 0;
if ((fd = open(fs_blkdev, O_WRONLY, 0644)) < 0) {
@@ -41,7 +41,7 @@
return -1;
}
- if ((ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+ if ((ioctl(fd, BLKGETSIZE64, &dev_sz)) == -1) {
ERROR("Cannot get block device size. %s\n", strerror(errno));
close(fd);
return -1;
@@ -49,7 +49,7 @@
/* Format the partition using the calculated length */
reset_ext4fs_info();
- info.len = ((off64_t)nr_sec * 512);
+ info.len = (off64_t)dev_sz;
/* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL);
diff --git a/healthd/Android.mk b/healthd/Android.mk
index a4469fc..ddd9f1f 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -20,12 +20,22 @@
include $(CLEAR_VARS)
+ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
+LOCAL_CHARGER_NO_UI := true
+endif
+ifdef BRILLO
+LOCAL_CHARGER_NO_UI := true
+endif
+
LOCAL_SRC_FILES := \
healthd.cpp \
healthd_mode_android.cpp \
- healthd_mode_charger.cpp \
BatteryPropertiesRegistrar.cpp
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_SRC_FILES += healthd_mode_charger.cpp
+endif
+
LOCAL_MODULE := healthd
LOCAL_MODULE_TAGS := optional
LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -42,9 +52,19 @@
LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
endif
-LOCAL_C_INCLUDES := bootable/recovery
+ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_CFLAGS += -DCHARGER_NO_UI
+endif
-LOCAL_STATIC_LIBRARIES := libbatterymonitor libbatteryservice libbinder libminui libpng libz libutils libcutils liblog libm libc
+LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+
+LOCAL_STATIC_LIBRARIES := libbatterymonitor libbatteryservice libbinder
+
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_STATIC_LIBRARIES += libminui libpng libz
+endif
+
+LOCAL_STATIC_LIBRARIES += libutils libcutils liblog libm libc
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
LOCAL_STATIC_LIBRARIES += libsuspend
@@ -59,6 +79,7 @@
include $(BUILD_EXECUTABLE)
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
define _add-charger-image
include $$(CLEAR_VARS)
LOCAL_MODULE := system_core_charger_$(notdir $(1))
@@ -84,3 +105,4 @@
_add-charger-image :=
_img_modules :=
+endif # LOCAL_CHARGER_NO_UI
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 48af70e..5123505 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
+#include <memory>
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
@@ -322,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" : "",
@@ -467,13 +467,13 @@
char pval[PROPERTY_VALUE_MAX];
mHealthdConfig = hc;
- DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
if (dir == NULL) {
KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
} else {
struct dirent* entry;
- while ((entry = readdir(dir))) {
+ while ((entry = readdir(dir.get()))) {
const char* name = entry->d_name;
if (!strcmp(name, ".") || !strcmp(name, ".."))
@@ -611,7 +611,6 @@
break;
}
}
- closedir(dir);
}
// This indicates that there is no charger driver registered.
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index d9ac356..20a6bf6 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -109,10 +109,17 @@
};
static struct healthd_mode_ops charger_ops = {
+#ifdef CHARGER_NO_UI
+ .init = healthd_mode_nop_init,
+ .preparetowait = healthd_mode_nop_preparetowait,
+ .heartbeat = healthd_mode_nop_heartbeat,
+ .battery_update = healthd_mode_nop_battery_update,
+#else
.init = healthd_mode_charger_init,
.preparetowait = healthd_mode_charger_preparetowait,
.heartbeat = healthd_mode_charger_heartbeat,
.battery_update = healthd_mode_charger_battery_update,
+#endif
};
static struct healthd_mode_ops recovery_ops = {
diff --git a/include/utils/String16.h b/include/utils/String16.h
index 9a67c7a..4a5874a 100644
--- a/include/utils/String16.h
+++ b/include/utils/String16.h
@@ -17,8 +17,10 @@
#ifndef ANDROID_STRING16_H
#define ANDROID_STRING16_H
+#include <string> // for std::string
+
#include <utils/Errors.h>
-#include <utils/Unicode.h>
+#include <utils/String8.h>
#include <utils/TypeHelpers.h>
// ---------------------------------------------------------------------------
@@ -62,9 +64,10 @@
explicit String16(const char* o, size_t len);
~String16();
-
+
inline const char16_t* string() const;
-
+
+ static inline std::string std_string(const String16& str);
size_t size() const;
void setTo(const String16& other);
status_t setTo(const char16_t* other);
@@ -72,12 +75,12 @@
status_t setTo(const String16& other,
size_t len,
size_t begin=0);
-
+
status_t append(const String16& other);
status_t append(const char16_t* other, size_t len);
-
+
inline String16& operator=(const String16& other);
-
+
inline String16& operator+=(const String16& other);
inline String16 operator+(const String16& other) const;
@@ -90,7 +93,7 @@
bool startsWith(const String16& prefix) const;
bool startsWith(const char16_t* prefix) const;
-
+
status_t makeLower();
status_t replaceAll(char16_t replaceThis,
@@ -106,16 +109,16 @@
inline bool operator!=(const String16& other) const;
inline bool operator>=(const String16& other) const;
inline bool operator>(const String16& other) const;
-
+
inline bool operator<(const char16_t* other) const;
inline bool operator<=(const char16_t* other) const;
inline bool operator==(const char16_t* other) const;
inline bool operator!=(const char16_t* other) const;
inline bool operator>=(const char16_t* other) const;
inline bool operator>(const char16_t* other) const;
-
+
inline operator const char16_t*() const;
-
+
private:
const char16_t* mString;
};
@@ -142,6 +145,11 @@
return mString;
}
+inline std::string String16::std_string(const String16& str)
+{
+ return std::string(String8(str).string());
+}
+
inline String16& String16::operator=(const String16& other)
{
setTo(other);
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 2a75b98..1d12994 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_STRING8_H
#define ANDROID_STRING8_H
+#include <string> // for std::string
+
#include <utils/Errors.h>
#include <utils/Unicode.h>
#include <utils/TypeHelpers.h>
@@ -49,7 +51,7 @@
String8(const String8& o);
explicit String8(const char* o);
explicit String8(const char* o, size_t numChars);
-
+
explicit String8(const String16& o);
explicit String8(const char16_t* o);
explicit String8(const char16_t* o, size_t numChars);
@@ -63,12 +65,13 @@
static String8 formatV(const char* fmt, va_list args);
inline const char* string() const;
+ static inline std::string std_string(const String8& str);
inline size_t size() const;
inline size_t bytes() const;
inline bool isEmpty() const;
-
+
size_t length() const;
-
+
void clear();
void setTo(const String8& other);
@@ -95,10 +98,10 @@
inline String8& operator=(const String8& other);
inline String8& operator=(const char* other);
-
+
inline String8& operator+=(const String8& other);
inline String8 operator+(const String8& other) const;
-
+
inline String8& operator+=(const char* other);
inline String8 operator+(const char* other) const;
@@ -110,20 +113,20 @@
inline bool operator!=(const String8& other) const;
inline bool operator>=(const String8& other) const;
inline bool operator>(const String8& other) const;
-
+
inline bool operator<(const char* other) const;
inline bool operator<=(const char* other) const;
inline bool operator==(const char* other) const;
inline bool operator!=(const char* other) const;
inline bool operator>=(const char* other) const;
inline bool operator>(const char* other) const;
-
+
inline operator const char*() const;
-
+
char* lockBuffer(size_t size);
void unlockBuffer();
status_t unlockBuffer(size_t size);
-
+
// return the index of the first byte of other in this at or after
// start, or -1 if not found
ssize_t find(const char* other, size_t start = 0) const;
@@ -261,6 +264,11 @@
return mString;
}
+inline std::string String8::std_string(const String8& str)
+{
+ return std::string(str.string());
+}
+
inline size_t String8::size() const
{
return length();
diff --git a/include/utils/ThreadDefs.h b/include/utils/ThreadDefs.h
index 9711c13..ae091e4 100644
--- a/include/utils/ThreadDefs.h
+++ b/include/utils/ThreadDefs.h
@@ -29,7 +29,11 @@
extern "C" {
#endif
+#ifdef _WIN32
+typedef uint32_t android_thread_id_t;
+#else
typedef void* android_thread_id_t;
+#endif
typedef int (*android_thread_func_t)(void*);
diff --git a/init/devices.cpp b/init/devices.cpp
index 39cd706..557a6ac 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -31,6 +31,8 @@
#include <sys/un.h>
#include <linux/netlink.h>
+#include <memory>
+
#include <selinux/selinux.h>
#include <selinux/label.h>
#include <selinux/android.h>
@@ -957,10 +959,9 @@
static void coldboot(const char *path)
{
- DIR *d = opendir(path);
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path), closedir);
if(d) {
- do_coldboot(d);
- closedir(d);
+ do_coldboot(d.get());
}
}
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index ee56a5e..397dfda 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -71,14 +71,22 @@
build_type := host
libbacktrace_multilib := both
include $(LOCAL_PATH)/Android.build.mk
+
+libbacktrace_shared_libraries :=
+
libbacktrace_static_libraries := \
libbase \
liblog \
libunwind \
+ liblzma \
+module := libbacktrace
+build_type := target
build_target := STATIC_LIBRARY
include $(LOCAL_PATH)/Android.build.mk
-libbacktrace_static_libraries :=
+build_type := host
+libbacktrace_multilib := both
+include $(LOCAL_PATH)/Android.build.mk
#-------------------------------------------------------------------------
# The libbacktrace_offline shared library.
@@ -106,7 +114,6 @@
libLLVMSupport \
module := libbacktrace_offline
-module_tag := optional
build_type := target
build_target := SHARED_LIBRARY
include $(LOCAL_PATH)/Android.build.mk
@@ -114,6 +121,26 @@
libbacktrace_multilib := both
include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_offline_shared_libraries :=
+libbacktrace_offline_static_libraries := \
+ libbacktrace \
+ libbase \
+ libcutils \
+ liblog \
+ libunwind \
+ liblzma \
+ libLLVMObject \
+ libLLVMBitReader \
+ libLLVMMC \
+ libLLVMMCParser \
+ libLLVMCore \
+ libLLVMSupport \
+
+module := libbacktrace_offline
+build_type := target
+build_target := STATIC_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+
#-------------------------------------------------------------------------
# The libbacktrace_test library needed by backtrace_test.
#-------------------------------------------------------------------------
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 7d829fe..b975db9 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -404,17 +404,16 @@
char task_path[128];
snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
- DIR* tasks_dir = opendir(task_path);
+ std::unique_ptr<DIR, decltype(&closedir)> tasks_dir(opendir(task_path), closedir);
ASSERT_TRUE(tasks_dir != nullptr);
struct dirent* entry;
- while ((entry = readdir(tasks_dir)) != nullptr) {
+ while ((entry = readdir(tasks_dir.get())) != nullptr) {
char* end;
pid_t tid = strtoul(entry->d_name, &end, 10);
if (*end == '\0') {
threads->push_back(tid);
}
}
- closedir(tasks_dir);
}
TEST(libbacktrace, ptrace_threads) {
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/tests/PropertiesTest.cpp b/libcutils/tests/PropertiesTest.cpp
index 659821c..22ca706 100644
--- a/libcutils/tests/PropertiesTest.cpp
+++ b/libcutils/tests/PropertiesTest.cpp
@@ -15,7 +15,7 @@
*/
#define LOG_TAG "Properties_test"
-#include <utils/Log.h>
+#include <cutils/log.h>
#include <gtest/gtest.h>
#include <cutils/properties.h>
diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk
index 624e385..a49ce58 100644
--- a/libdiskconfig/Android.mk
+++ b/libdiskconfig/Android.mk
@@ -13,6 +13,8 @@
LOCAL_MODULE_TAGS := optional
LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc
LOCAL_CFLAGS := -Werror
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include $(BUILD_SHARED_LIBRARY)
ifeq ($(HOST_OS),linux)
@@ -21,5 +23,7 @@
LOCAL_MODULE := libdiskconfig_host
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
include $(BUILD_HOST_STATIC_LIBRARY)
endif # HOST_OS == linux
diff --git a/include/diskconfig/diskconfig.h b/libdiskconfig/include/diskconfig/diskconfig.h
similarity index 100%
rename from include/diskconfig/diskconfig.h
rename to libdiskconfig/include/diskconfig/diskconfig.h
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index f4e071b..aa88bed 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -69,7 +69,7 @@
int fakeFd;
/* a printable name for this fake device */
- char *debugName;
+ char debugName[sizeof("/dev/log/security")];
/* nonzero if this is a binary log */
int isBinary;
@@ -123,8 +123,8 @@
* File descriptor management.
*/
#define FAKE_FD_BASE 10000
-#define MAX_OPEN_LOGS 16
-static LogState *openLogTable[MAX_OPEN_LOGS];
+#define MAX_OPEN_LOGS 8
+static LogState openLogTable[MAX_OPEN_LOGS];
/*
* Allocate an fd and associate a new LogState with it.
@@ -134,11 +134,10 @@
{
size_t i;
- for (i = 0; i < sizeof(openLogTable); i++) {
- if (openLogTable[i] == NULL) {
- openLogTable[i] = calloc(1, sizeof(LogState));
- openLogTable[i]->fakeFd = FAKE_FD_BASE + i;
- return openLogTable[i];
+ for (i = 0; i < (sizeof(openLogTable) / sizeof(openLogTable[0])); i++) {
+ if (openLogTable[i].fakeFd == 0) {
+ openLogTable[i].fakeFd = FAKE_FD_BASE + i;
+ return &openLogTable[i];
}
}
return NULL;
@@ -150,7 +149,7 @@
static LogState *fdToLogState(int fd)
{
if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) {
- return openLogTable[fd - FAKE_FD_BASE];
+ return &openLogTable[fd - FAKE_FD_BASE];
}
return NULL;
}
@@ -166,9 +165,7 @@
ls = fdToLogState(fd);
if (ls != NULL) {
- openLogTable[fd - FAKE_FD_BASE] = NULL;
- free(ls->debugName);
- free(ls);
+ memset(&openLogTable[fd - FAKE_FD_BASE], 0, sizeof(openLogTable[0]));
}
unlock();
@@ -191,10 +188,12 @@
{
static const int kDevLogLen = sizeof("/dev/log/") - 1;
- logState->debugName = strdup(pathName);
+ strncpy(logState->debugName, pathName, sizeof(logState->debugName));
+ logState->debugName[sizeof(logState->debugName) - 1] = '\0';
/* identify binary logs */
- if (strcmp(pathName + kDevLogLen, "events") == 0) {
+ if (!strcmp(pathName + kDevLogLen, "events") ||
+ !strcmp(pathName + kDevLogLen, "security")) {
logState->isBinary = 1;
}
@@ -218,8 +217,7 @@
i = 0;
while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
- i < kMaxTagLen)
- {
+ i < kMaxTagLen) {
tagName[i++] = *tags++;
}
if (i == kMaxTagLen) {
@@ -320,9 +318,9 @@
};
int idx;
- idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
+ idx = (int)priority - (int)ANDROID_LOG_VERBOSE;
if (idx < 0 ||
- idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+ idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
return "?unknown?";
return priorityStrings[idx];
}
@@ -367,7 +365,11 @@
char prefixBuf[128], suffixBuf[128];
char priChar;
time_t when;
+#if !defined(_WIN32)
pid_t pid, tid;
+#else
+ uint32_t pid, tid;
+#endif
TRACE("LOG %d: %s %s", logPrio, tag, msg);
@@ -450,13 +452,15 @@
while (p < end) {
if (*p++ == '\n') numLines++;
}
- if (p > msg && *(p-1) != '\n') numLines++;
+ if (p > msg && *(p-1) != '\n') {
+ numLines++;
+ }
/*
* Create an array of iovecs large enough to write all of
* the lines with a prefix and a suffix.
*/
- const size_t INLINE_VECS = 6;
+ const size_t INLINE_VECS = 64;
const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*));
struct iovec stackVec[INLINE_VECS];
struct iovec* vec = stackVec;
@@ -490,7 +494,9 @@
v++;
}
const char* start = p;
- while (p < end && *p != '\n') p++;
+ while (p < end && *p != '\n') {
+ p++;
+ }
if ((p-start) > 0) {
v->iov_base = (void*)start;
v->iov_len = p-start;
@@ -685,6 +691,17 @@
return redirectOpen(pathName, flags);
}
+/*
+ * The logger API has no means or need to 'stop' or 'close' using the logs,
+ * and as such, there is no way for that 'stop' or 'close' to translate into
+ * a close operation to the fake log handler. fakeLogClose is provided for
+ * completeness only.
+ *
+ * We have no intention of adding a log close operation as it would complicate
+ * every user of the logging API with no gain since the only valid place to
+ * call is in the exit handler. Logging can continue in the exit handler to
+ * help debug HOST tools ...
+ */
int fakeLogClose(int fd)
{
/* Assume that open() was called first. */
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index 4946073..b173d1a 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -125,7 +125,7 @@
#if FAKE_LOG_DEVICE
for (i = 0; i < LOG_ID_MAX; i++) {
- char buf[sizeof("/dev/log_system")];
+ char buf[sizeof("/dev/log_security")];
snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
log_fds[i] = fakeLogOpen(buf, O_WRONLY);
}
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 65d1456..1317853 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -173,7 +173,7 @@
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
}
-TEST(liblog, __android_log_bswrite) {
+static void bswrite_test(const char *message) {
struct logger_list *logger_list;
pid_t pid = getpid();
@@ -181,10 +181,30 @@
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
- static const char buffer[] = "Hello World";
log_time ts(android_log_clockid());
- ASSERT_LT(0, __android_log_bswrite(0, buffer));
+ ASSERT_LT(0, __android_log_bswrite(0, message));
+ size_t num_lines = 1, size = 0, length = 0, total = 0;
+ const char *cp = message;
+ while (*cp) {
+ if (*cp == '\n') {
+ if (cp[1]) {
+ ++num_lines;
+ }
+ } else {
+ ++size;
+ }
+ ++cp;
+ ++total;
+ ++length;
+ if ((LOGGER_ENTRY_MAX_PAYLOAD - 4 - 1 - 4) <= length) {
+ break;
+ }
+ }
+ while (*cp) {
+ ++cp;
+ ++total;
+ }
usleep(1000000);
int count = 0;
@@ -199,7 +219,7 @@
if ((log_msg.entry.sec < (ts.tv_sec - 1))
|| ((ts.tv_sec + 1) < log_msg.entry.sec)
- || (log_msg.entry.len != (4 + 1 + 4 + sizeof(buffer) - 1))
+ || ((size_t)log_msg.entry.len != (4 + 1 + 4 + length))
|| (log_msg.id() != LOG_ID_EVENTS)) {
continue;
}
@@ -210,21 +230,22 @@
continue;
}
- int len = get4LE(eventData + 4 + 1);
- if (len == (sizeof(buffer) - 1)) {
+ size_t len = get4LE(eventData + 4 + 1);
+ if (len == total) {
++count;
AndroidLogFormat *logformat = android_log_format_new();
EXPECT_TRUE(NULL != logformat);
AndroidLogEntry entry;
char msgBuf[1024];
- EXPECT_EQ(0, android_log_processBinaryLogBuffer(&log_msg.entry_v1,
- &entry,
- NULL,
- msgBuf,
- sizeof(msgBuf)));
- fflush(stderr);
- EXPECT_EQ(31, android_log_printLogLine(logformat, fileno(stderr), &entry));
+ int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
+ &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+ EXPECT_EQ((length == total) ? 0 : -1, processBinaryLogBuffer);
+ if (processBinaryLogBuffer == 0) {
+ fflush(stderr);
+ EXPECT_EQ((int)((20 * num_lines) + size),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
+ }
android_log_format_free(logformat);
}
}
@@ -234,18 +255,55 @@
android_logger_list_close(logger_list);
}
-TEST(liblog, __android_log_bswrite__empty_string) {
+TEST(liblog, __android_log_bswrite_and_print) {
+ bswrite_test("Hello World");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__empty_string) {
+ bswrite_test("");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__newline_prefix) {
+ bswrite_test("\nHello World\n");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__newline_space_prefix) {
+ bswrite_test("\n Hello World \n");
+}
+
+TEST(liblog, __android_log_bswrite_and_print__multiple_newline) {
+ bswrite_test("one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten");
+}
+
+static void buf_write_test(const char *message) {
struct logger_list *logger_list;
pid_t pid = getpid();
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
- LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
- static const char buffer[] = "";
+ static const char tag[] = "TEST__android_log_buf_write";
log_time ts(android_log_clockid());
- ASSERT_LT(0, __android_log_bswrite(0, buffer));
+ EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ tag, message));
+ size_t num_lines = 1, size = 0, length = 0;
+ const char *cp = message;
+ while (*cp) {
+ if (*cp == '\n') {
+ if (cp[1]) {
+ ++num_lines;
+ }
+ } else {
+ ++size;
+ }
+ ++length;
+ if ((LOGGER_ENTRY_MAX_PAYLOAD - 2 - sizeof(tag)) <= length) {
+ break;
+ }
+ ++cp;
+ }
usleep(1000000);
int count = 0;
@@ -260,34 +318,25 @@
if ((log_msg.entry.sec < (ts.tv_sec - 1))
|| ((ts.tv_sec + 1) < log_msg.entry.sec)
- || (log_msg.entry.len != (4 + 1 + 4))
- || (log_msg.id() != LOG_ID_EVENTS)) {
+ || ((size_t)log_msg.entry.len != (sizeof(tag) + length + 2))
+ || (log_msg.id() != LOG_ID_MAIN)) {
continue;
}
- char *eventData = log_msg.msg();
+ ++count;
- if (eventData[4] != EVENT_TYPE_STRING) {
- continue;
- }
-
- int len = get4LE(eventData + 4 + 1);
- if (len == 0) {
- ++count;
-
- AndroidLogFormat *logformat = android_log_format_new();
- EXPECT_TRUE(NULL != logformat);
- AndroidLogEntry entry;
- char msgBuf[1024];
- EXPECT_EQ(0, android_log_processBinaryLogBuffer(&log_msg.entry_v1,
- &entry,
- NULL,
- msgBuf,
- sizeof(msgBuf)));
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ int processLogBuffer = android_log_processLogBuffer(&log_msg.entry_v1,
+ &entry);
+ EXPECT_EQ(0, processLogBuffer);
+ if (processLogBuffer == 0) {
fflush(stderr);
- EXPECT_EQ(20, android_log_printLogLine(logformat, fileno(stderr), &entry));
- android_log_format_free(logformat);
+ EXPECT_EQ((int)(((11 + sizeof(tag)) * num_lines) + size),
+ android_log_printLogLine(logformat, fileno(stderr), &entry));
}
+ android_log_format_free(logformat);
}
EXPECT_EQ(1, count);
@@ -295,6 +344,18 @@
android_logger_list_close(logger_list);
}
+TEST(liblog, __android_log_buf_write_and_print__empty) {
+ buf_write_test("");
+}
+
+TEST(liblog, __android_log_buf_write_and_print__newline_prefix) {
+ buf_write_test("\nHello World\n");
+}
+
+TEST(liblog, __android_log_buf_write_and_print__newline_space_prefix) {
+ buf_write_test("\n Hello World \n");
+}
+
TEST(liblog, __security) {
static const char persist_key[] = "persist.logd.security";
static const char readonly_key[] = "ro.device_owner";
@@ -609,7 +670,7 @@
EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
}
-static const char max_payload_tag[] = "TEST_max_payload_XXXX";
+static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
#define SIZEOF_MAX_PAYLOAD_BUF (LOGGER_ENTRY_MAX_PAYLOAD - \
sizeof(max_payload_tag) - 1)
static const char max_payload_buf[] = "LEONATO\n\
@@ -1576,6 +1637,14 @@
android_logger_list_close(logger_list);
}
+TEST(liblog, __android_log_bswrite_and_print___max) {
+ bswrite_test(max_payload_buf);
+}
+
+TEST(liblog, __android_log_buf_write_and_print__max) {
+ buf_write_test(max_payload_buf);
+}
+
TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
const int TAG = 123456785;
const char SUBTAG[] = "test-subtag";
diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk
index 7b170f5..ffc7244 100644
--- a/libmemtrack/Android.mk
+++ b/libmemtrack/Android.mk
@@ -5,6 +5,8 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := memtrack.c
LOCAL_MODULE := libmemtrack
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_C_INCLUDES += hardware/libhardware/include
LOCAL_SHARED_LIBRARIES := libhardware liblog
LOCAL_CFLAGS := -Wall -Werror
diff --git a/include/memtrack/memtrack.h b/libmemtrack/include/memtrack/memtrack.h
similarity index 98%
rename from include/memtrack/memtrack.h
rename to libmemtrack/include/memtrack/memtrack.h
index 3917300..8c0ab89 100644
--- a/include/memtrack/memtrack.h
+++ b/libmemtrack/include/memtrack/memtrack.h
@@ -19,7 +19,6 @@
#include <sys/types.h>
#include <stddef.h>
-#include <cutils/compiler.h>
#ifdef __cplusplus
extern "C" {
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
new file mode 100644
index 0000000..b75f1e5
--- /dev/null
+++ b/libmemunreachable/Allocator.cpp
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Header page:
+//
+// For minimum allocation size (8 bytes), bitmap can store used allocations for
+// up to 4032*8*8=258048, which is 256KiB minus the header page
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+#include "anon_vma_naming.h"
+#include "Allocator.h"
+#include "LinkedList.h"
+
+// runtime interfaces used:
+// abort
+// assert - fprintf + mmap
+// mmap
+// munmap
+// prctl
+
+constexpr size_t const_log2(size_t n, size_t p = 0) {
+ return (n <= 1) ? p : const_log2(n / 2, p + 1);
+}
+
+constexpr unsigned int div_round_up(unsigned int x, unsigned int y) {
+ return (x + y - 1) / y;
+}
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+static constexpr size_t kPageSize = 4096;
+static constexpr size_t kChunkSize = 256 * 1024;
+static constexpr size_t kUsableChunkSize = kChunkSize - kPageSize;
+static constexpr size_t kMaxBucketAllocationSize = kChunkSize / 4;
+static constexpr size_t kMinBucketAllocationSize = 8;
+static constexpr unsigned int kNumBuckets = const_log2(kMaxBucketAllocationSize)
+ - const_log2(kMinBucketAllocationSize) + 1;
+static constexpr unsigned int kUsablePagesPerChunk = kUsableChunkSize
+ / kPageSize;
+
+std::atomic<int> heap_count;
+
+class Chunk;
+
+class HeapImpl {
+ public:
+ HeapImpl();
+ ~HeapImpl();
+ void* operator new(std::size_t count) noexcept;
+ void operator delete(void* ptr);
+
+ void* Alloc(size_t size);
+ void Free(void* ptr);
+ bool Empty();
+
+ void MoveToFullList(Chunk* chunk, int bucket_);
+ void MoveToFreeList(Chunk* chunk, int bucket_);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HeapImpl);
+
+ LinkedList<Chunk*> free_chunks_[kNumBuckets];
+ LinkedList<Chunk*> full_chunks_[kNumBuckets];
+
+ void MoveToList(Chunk* chunk, LinkedList<Chunk*>* head);
+ void* MapAlloc(size_t size);
+ void MapFree(void* ptr);
+ void* AllocLocked(size_t size);
+ void FreeLocked(void* ptr);
+
+ struct MapAllocation {
+ void *ptr;
+ size_t size;
+ MapAllocation* next;
+ };
+ MapAllocation* map_allocation_list_;
+ std::mutex m_;
+};
+
+// Integer log 2, rounds down
+static inline unsigned int log2(size_t n) {
+ return 8 * sizeof(unsigned long long) - __builtin_clzll(n) - 1;
+}
+
+static inline unsigned int size_to_bucket(size_t size) {
+ if (size < kMinBucketAllocationSize)
+ return kMinBucketAllocationSize;
+ return log2(size - 1) + 1 - const_log2(kMinBucketAllocationSize);
+}
+
+static inline size_t bucket_to_size(unsigned int bucket) {
+ return kMinBucketAllocationSize << bucket;
+}
+
+static void* MapAligned(size_t size, size_t align) {
+ const int prot = PROT_READ | PROT_WRITE;
+ const int flags = MAP_ANONYMOUS | MAP_PRIVATE;
+
+ size = (size + kPageSize - 1) & ~(kPageSize - 1);
+
+ // Over-allocate enough to align
+ size_t map_size = size + align - kPageSize;
+ if (map_size < size) {
+ return nullptr;
+ }
+
+ void* ptr = mmap(NULL, map_size, prot, flags, -1, 0);
+ if (ptr == MAP_FAILED) {
+ return nullptr;
+ }
+
+ size_t aligned_size = map_size;
+ void* aligned_ptr = ptr;
+
+ std::align(align, size, aligned_ptr, aligned_size);
+
+ // Trim beginning
+ if (aligned_ptr != ptr) {
+ ptrdiff_t extra = reinterpret_cast<uintptr_t>(aligned_ptr)
+ - reinterpret_cast<uintptr_t>(ptr);
+ munmap(ptr, extra);
+ map_size -= extra;
+ ptr = aligned_ptr;
+ }
+
+ // Trim end
+ if (map_size != size) {
+ assert(map_size > size);
+ assert(ptr != NULL);
+ munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size),
+ map_size - size);
+ }
+
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
+ reinterpret_cast<uintptr_t>(ptr), size, "leak_detector_malloc");
+
+ return ptr;
+}
+
+class Chunk {
+ public:
+ static void* operator new(std::size_t count) noexcept;
+ static void operator delete(void* ptr);
+ Chunk(HeapImpl* heap, int bucket);
+ ~Chunk() {}
+
+ void *Alloc();
+ void Free(void* ptr);
+ void Purge();
+ bool Empty();
+
+ static Chunk* ptr_to_chunk(void* ptr) {
+ return reinterpret_cast<Chunk*>(reinterpret_cast<uintptr_t>(ptr)
+ & ~(kChunkSize - 1));
+ }
+ static bool is_chunk(void* ptr) {
+ return (reinterpret_cast<uintptr_t>(ptr) & (kChunkSize - 1)) != 0;
+ }
+
+ unsigned int free_count() {
+ return free_count_;
+ }
+ HeapImpl* heap() {
+ return heap_;
+ }
+ LinkedList<Chunk*> node_; // linked list sorted by minimum free count
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Chunk);
+ HeapImpl* heap_;
+ unsigned int bucket_;
+ unsigned int allocation_size_; // size of allocations in chunk, min 8 bytes
+ unsigned int max_allocations_; // maximum number of allocations in the chunk
+ unsigned int first_free_bitmap_; // index into bitmap for first non-full entry
+ unsigned int free_count_; // number of available allocations
+ unsigned int frees_since_purge_; // number of calls to Free since last Purge
+
+ // bitmap of pages that have been dirtied
+ uint32_t dirty_pages_[div_round_up(kUsablePagesPerChunk, 32)];
+
+ // bitmap of free allocations.
+ uint32_t free_bitmap_[kUsableChunkSize / kMinBucketAllocationSize / 32];
+
+ char data_[0];
+
+ unsigned int ptr_to_n(void* ptr) {
+ ptrdiff_t offset = reinterpret_cast<uintptr_t>(ptr)
+ - reinterpret_cast<uintptr_t>(data_);
+ return offset / allocation_size_;
+ }
+ void* n_to_ptr(unsigned int n) {
+ return data_ + n * allocation_size_;
+ }
+};
+static_assert(sizeof(Chunk) <= kPageSize, "header must fit in page");
+
+// Override new operator on chunk to use mmap to allocate kChunkSize
+void* Chunk::operator new(std::size_t count __attribute__((unused))) noexcept {
+ assert(count == sizeof(Chunk));
+ void* mem = MapAligned(kChunkSize, kChunkSize);
+ if (!mem) {
+ abort(); //throw std::bad_alloc;
+ }
+
+ return mem;
+}
+
+// Override new operator on chunk to use mmap to allocate kChunkSize
+void Chunk::operator delete(void *ptr) {
+ assert(reinterpret_cast<Chunk*>(ptr) == ptr_to_chunk(ptr));
+ munmap(ptr, kChunkSize);
+}
+
+Chunk::Chunk(HeapImpl* heap, int bucket) :
+ node_(this), heap_(heap), bucket_(bucket), allocation_size_(
+ bucket_to_size(bucket)), max_allocations_(
+ kUsableChunkSize / allocation_size_), first_free_bitmap_(0), free_count_(
+ max_allocations_), frees_since_purge_(0) {
+ memset(dirty_pages_, 0, sizeof(dirty_pages_));
+ memset(free_bitmap_, 0xff, sizeof(free_bitmap_));
+}
+
+bool Chunk::Empty() {
+ return free_count_ == max_allocations_;
+}
+
+void* Chunk::Alloc() {
+ assert(free_count_ > 0);
+
+ unsigned int i = first_free_bitmap_;
+ while (free_bitmap_[i] == 0)
+ i++;
+ assert(i < ARRAY_SIZE(free_bitmap_));
+ unsigned int bit = __builtin_ffs(free_bitmap_[i]) - 1;
+ assert(free_bitmap_[i] & (1U << bit));
+ free_bitmap_[i] &= ~(1U << bit);
+ unsigned int n = i * 32 + bit;
+ assert(n < max_allocations_);
+
+ unsigned int page = n * allocation_size_ / kPageSize;
+ assert(page / 32 < ARRAY_SIZE(dirty_pages_));
+ dirty_pages_[page / 32] |= 1U << (page % 32);
+
+ free_count_--;
+ if (free_count_ == 0) {
+ heap_->MoveToFullList(this, bucket_);
+ }
+
+ return n_to_ptr(n);
+}
+
+void Chunk::Free(void* ptr) {
+ assert(is_chunk(ptr));
+ assert(ptr_to_chunk(ptr) == this);
+
+ unsigned int n = ptr_to_n(ptr);
+ unsigned int i = n / 32;
+ unsigned int bit = n % 32;
+
+ assert(i < ARRAY_SIZE(free_bitmap_));
+ assert(!(free_bitmap_[i] & (1U << bit)));
+ free_bitmap_[i] |= 1U << bit;
+ free_count_++;
+
+ if (i < first_free_bitmap_) {
+ first_free_bitmap_ = i;
+ }
+
+ if (free_count_ == 1) {
+ heap_->MoveToFreeList(this, bucket_);
+ } else {
+ // TODO(ccross): move down free list if necessary
+ }
+
+ if (frees_since_purge_++ * allocation_size_ > 16 * kPageSize) {
+ Purge();
+ }
+}
+
+void Chunk::Purge() {
+ frees_since_purge_ = 0;
+
+ //unsigned int allocsPerPage = kPageSize / allocation_size_;
+}
+
+// Override new operator on HeapImpl to use mmap to allocate a page
+void* HeapImpl::operator new(std::size_t count __attribute__((unused)))
+ noexcept {
+ assert(count == sizeof(HeapImpl));
+ void* mem = MapAligned(kPageSize, kPageSize);
+ if (!mem) {
+ abort(); //throw std::bad_alloc;
+ }
+
+ heap_count++;
+ return mem;
+}
+
+void HeapImpl::operator delete(void *ptr) {
+ munmap(ptr, kPageSize);
+}
+
+HeapImpl::HeapImpl() :
+ free_chunks_(), full_chunks_(), map_allocation_list_(NULL) {
+}
+
+bool HeapImpl::Empty() {
+ for (unsigned int i = 0; i < kNumBuckets; i++) {
+ for (LinkedList<Chunk*> *it = free_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+ if (!it->data()->Empty()) {
+ return false;
+ }
+ }
+ for (LinkedList<Chunk*> *it = full_chunks_[i].next(); it->data() != NULL; it = it->next()) {
+ if (!it->data()->Empty()) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+HeapImpl::~HeapImpl() {
+ for (unsigned int i = 0; i < kNumBuckets; i++) {
+ while (!free_chunks_[i].empty()) {
+ Chunk *chunk = free_chunks_[i].next()->data();
+ chunk->node_.remove();
+ delete chunk;
+ }
+ while (!full_chunks_[i].empty()) {
+ Chunk *chunk = full_chunks_[i].next()->data();
+ chunk->node_.remove();
+ delete chunk;
+ }
+ }
+}
+
+void* HeapImpl::Alloc(size_t size) {
+ std::lock_guard<std::mutex> lk(m_);
+ return AllocLocked(size);
+}
+
+void* HeapImpl::AllocLocked(size_t size) {
+ if (__predict_false(size > kMaxBucketAllocationSize)) {
+ return MapAlloc(size);
+ }
+ int bucket = size_to_bucket(size);
+ if (__predict_false(free_chunks_[bucket].empty())) {
+ Chunk *chunk = new Chunk(this, bucket);
+ free_chunks_[bucket].insert(chunk->node_);
+ }
+ return free_chunks_[bucket].next()->data()->Alloc();
+}
+
+void HeapImpl::Free(void *ptr) {
+ std::lock_guard<std::mutex> lk(m_);
+ FreeLocked(ptr);
+}
+
+void HeapImpl::FreeLocked(void *ptr) {
+ if (!Chunk::is_chunk(ptr)) {
+ HeapImpl::MapFree(ptr);
+ } else {
+ Chunk* chunk = Chunk::ptr_to_chunk(ptr);
+ assert(chunk->heap() == this);
+ chunk->Free(ptr);
+ }
+}
+
+void* HeapImpl::MapAlloc(size_t size) {
+ size = (size + kPageSize - 1) & ~(kPageSize - 1);
+
+ MapAllocation* allocation = reinterpret_cast<MapAllocation*>(AllocLocked(
+ sizeof(MapAllocation)));
+ void* ptr = MapAligned(size, kChunkSize);
+ if (!ptr) {
+ FreeLocked(allocation);
+ abort(); //throw std::bad_alloc;
+ }
+ allocation->ptr = ptr;
+ allocation->size = size;
+ allocation->next = map_allocation_list_;
+ map_allocation_list_ = allocation;
+
+ return ptr;
+}
+
+void HeapImpl::MapFree(void *ptr) {
+ MapAllocation **allocation = &map_allocation_list_;
+ while (*allocation && (*allocation)->ptr != ptr)
+ allocation = &(*allocation)->next;
+
+ assert(*allocation != nullptr);
+
+ munmap((*allocation)->ptr, (*allocation)->size);
+ FreeLocked(*allocation);
+
+ *allocation = (*allocation)->next;
+}
+
+void HeapImpl::MoveToFreeList(Chunk *chunk, int bucket) {
+ MoveToList(chunk, &free_chunks_[bucket]);
+}
+
+void HeapImpl::MoveToFullList(Chunk *chunk, int bucket) {
+ MoveToList(chunk, &full_chunks_[bucket]);
+}
+
+void HeapImpl::MoveToList(Chunk *chunk, LinkedList<Chunk*>* head) {
+ // Remove from old list
+ chunk->node_.remove();
+
+ LinkedList<Chunk*> *node = head;
+ // Insert into new list, sorted by lowest free count
+ while (node->next() != head && node->data() != nullptr
+ && node->data()->free_count() < chunk->free_count())
+ node = node->next();
+
+ node->insert(chunk->node_);
+}
+
+Heap::Heap() {
+ // HeapImpl overloads the operator new in order to mmap itself instead of
+ // allocating with new.
+ // Can't use a shared_ptr to store the result because shared_ptr needs to
+ // allocate, and Allocator<T> is still being constructed.
+ impl_ = new HeapImpl();
+ owns_impl_ = true;
+}
+
+Heap::~Heap() {
+ if (owns_impl_) {
+ delete impl_;
+ }
+}
+
+void* Heap::allocate(size_t size) {
+ return impl_->Alloc(size);
+}
+
+void Heap::deallocate(void* ptr) {
+ impl_->Free(ptr);
+}
+
+void Heap::deallocate(HeapImpl*impl, void* ptr) {
+ impl->Free(ptr);
+}
+
+bool Heap::empty() {
+ return impl_->Empty();
+}
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
new file mode 100644
index 0000000..b0a4d4c
--- /dev/null
+++ b/libmemunreachable/Allocator.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_ALLOCATOR_H_
+#define LIBMEMUNREACHABLE_ALLOCATOR_H_
+
+#include <atomic>
+#include <cstddef>
+#include <functional>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <unordered_set>
+#include <vector>
+extern std::atomic<int> heap_count;
+
+class HeapImpl;
+
+template<typename T>
+class Allocator;
+
+
+// Non-templated class that implements wraps HeapImpl to keep
+// implementation out of the header file
+class Heap {
+public:
+ Heap();
+ ~Heap();
+
+ // Copy constructor that does not take ownership of impl_
+ Heap(const Heap& other) : impl_(other.impl_), owns_impl_(false) {}
+
+ // Assignment disabled
+ Heap& operator=(const Heap&) = delete;
+
+ // Allocate size bytes
+ void* allocate(size_t size);
+
+ // Deallocate allocation returned by allocate
+ void deallocate(void*);
+
+ bool empty();
+
+ static void deallocate(HeapImpl* impl, void* ptr);
+
+ // Allocate a class of type T
+ template<class T>
+ T* allocate() {
+ return reinterpret_cast<T*>(allocate(sizeof(T)));
+ }
+
+ // Comparators, copied objects will be equal
+ bool operator ==(const Heap& other) const {
+ return impl_ == other.impl_;
+ }
+ bool operator !=(const Heap& other) const {
+ return !(*this == other);
+ }
+
+ // std::unique_ptr wrapper that allocates using allocate and deletes using
+ // deallocate
+ template<class T>
+ using unique_ptr = std::unique_ptr<T, std::function<void(void*)>>;
+
+ template<class T, class... Args>
+ unique_ptr<T> make_unique(Args&&... args) {
+ HeapImpl* impl = impl_;
+ return unique_ptr<T>(new (allocate<T>()) T(std::forward<Args>(args)...),
+ [impl](void* ptr) {
+ reinterpret_cast<T*>(ptr)->~T();
+ deallocate(impl, ptr);
+ });
+ }
+
+ // std::unique_ptr wrapper that allocates using allocate and deletes using
+ // deallocate
+ template<class T>
+ using shared_ptr = std::shared_ptr<T>;
+
+ template<class T, class... Args>
+ shared_ptr<T> make_shared(Args&&... args);
+
+protected:
+ HeapImpl* impl_;
+ bool owns_impl_;
+};
+
+// STLAllocator implements the std allocator interface on top of a Heap
+template<typename T>
+class STLAllocator {
+public:
+ using value_type = T;
+ ~STLAllocator() {
+ }
+
+ // Construct an STLAllocator on top of a Heap
+ STLAllocator(const Heap& heap) :
+ heap_(heap) {
+ }
+
+ // Rebind an STLAllocator from an another STLAllocator
+ template<typename U>
+ STLAllocator(const STLAllocator<U>& other) :
+ heap_(other.heap_) {
+ }
+
+ STLAllocator(const STLAllocator&) = default;
+ STLAllocator<T>& operator=(const STLAllocator<T>&) = default;
+
+ T* allocate(std::size_t n) {
+ return reinterpret_cast<T*>(heap_.allocate(n * sizeof(T)));
+ }
+
+ void deallocate(T* ptr, std::size_t) {
+ heap_.deallocate(ptr);
+ }
+
+ template<typename U>
+ bool operator ==(const STLAllocator<U>& other) const {
+ return heap_ == other.heap_;
+ }
+ template<typename U>
+ inline bool operator !=(const STLAllocator<U>& other) const {
+ return !(this == other);
+ }
+
+ template<typename U>
+ friend class STLAllocator;
+
+protected:
+ Heap heap_;
+};
+
+
+// Allocator extends STLAllocator with some convenience methods for allocating
+// a single object and for constructing unique_ptr and shared_ptr objects with
+// appropriate deleters.
+template<class T>
+class Allocator : public STLAllocator<T> {
+ public:
+ ~Allocator() {}
+
+ Allocator(const Heap& other) :
+ STLAllocator<T>(other) {
+ }
+
+ template<typename U>
+ Allocator(const STLAllocator<U>& other) :
+ STLAllocator<T>(other) {
+ }
+
+ Allocator(const Allocator&) = default;
+ Allocator<T>& operator=(const Allocator<T>&) = default;
+
+ using STLAllocator<T>::allocate;
+ using STLAllocator<T>::deallocate;
+ using STLAllocator<T>::heap_;
+
+ T* allocate() {
+ return STLAllocator<T>::allocate(1);
+ }
+ void deallocate(void* ptr) {
+ heap_.deallocate(ptr);
+ }
+
+ using shared_ptr = Heap::shared_ptr<T>;
+
+ template<class... Args>
+ shared_ptr make_shared(Args&& ...args) {
+ return heap_.template make_shared<T>(std::forward<Args>(args)...);
+ }
+
+ using unique_ptr = Heap::unique_ptr<T>;
+
+ template<class... Args>
+ unique_ptr make_unique(Args&& ...args) {
+ return heap_.template make_unique<T>(std::forward<Args>(args)...);
+ }
+};
+
+// std::unique_ptr wrapper that allocates using allocate and deletes using
+// deallocate. Implemented outside class definition in order to pass
+// Allocator<T> to shared_ptr.
+template<class T, class... Args>
+inline Heap::shared_ptr<T> Heap::make_shared(Args&&... args) {
+ return std::allocate_shared<T, Allocator<T>, Args...>(Allocator<T>(*this),
+ std::forward<Args>(args)...);
+}
+
+namespace allocator {
+
+template<class T>
+using vector = std::vector<T, Allocator<T>>;
+
+template<class T>
+using list = std::list<T, Allocator<T>>;
+
+template<class T, class Key, class Compare = std::less<Key>>
+using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>;
+
+template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>;
+
+template<class Key, class Compare = std::less<Key>>
+using set = std::set<Key, Compare, Allocator<Key>>;
+
+using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
+}
+
+#endif
diff --git a/libmemunreachable/Android.mk b/libmemunreachable/Android.mk
new file mode 100644
index 0000000..0df26a8
--- /dev/null
+++ b/libmemunreachable/Android.mk
@@ -0,0 +1,43 @@
+LOCAL_PATH := $(call my-dir)
+
+memunreachable_srcs := \
+ Allocator.cpp \
+ HeapWalker.cpp \
+ LeakPipe.cpp \
+ LineBuffer.cpp \
+ MemUnreachable.cpp \
+ ProcessMappings.cpp \
+ PtracerThread.cpp \
+ ThreadCapture.cpp \
+
+memunreachable_test_srcs := \
+ tests/Allocator_test.cpp \
+ tests/HeapWalker_test.cpp \
+ tests/MemUnreachable_test.cpp \
+ tests/ThreadCapture_test.cpp \
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libmemunreachable
+LOCAL_SRC_FILES := $(memunreachable_srcs)
+LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
+LOCAL_SHARED_LIBRARIES := libbase liblog
+LOCAL_STATIC_LIBRARIES := libc_malloc_debug_backtrace libc_logging
+# Only need this for arm since libc++ uses its own unwind code that
+# doesn't mix with the other default unwind code.
+LOCAL_STATIC_LIBRARIES_arm := libunwind_llvm
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CLANG := true
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := memunreachable_test
+LOCAL_SRC_FILES := $(memunreachable_test_srcs)
+LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
+LOCAL_CLANG := true
+LOCAL_SHARED_LIBRARIES := libmemunreachable libbase liblog
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
new file mode 100644
index 0000000..1a0c33d
--- /dev/null
+++ b/libmemunreachable/HeapWalker.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#include <map>
+#include <utility>
+
+#include "Allocator.h"
+#include "HeapWalker.h"
+#include "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}));
+ 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;
+ return true;
+ } else {
+ Range overlap = inserted.first->first;
+ ALOGE("range %p-%p overlaps with existing range %p-%p",
+ reinterpret_cast<void*>(begin),
+ reinterpret_cast<void*>(end),
+ reinterpret_cast<void*>(overlap.begin),
+ reinterpret_cast<void*>(overlap.end));
+ return false;
+ }
+}
+
+void HeapWalker::Walk(const Range& range, bool RangeInfo::*flag) {
+ allocator::vector<Range> to_do(1, range, 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;
+ }
+ }
+ }
+ }
+ }
+}
+
+void HeapWalker::Root(uintptr_t begin, uintptr_t end) {
+ roots_.push_back(Range{begin, end});
+}
+
+void HeapWalker::Root(const allocator::vector<uintptr_t>& vals) {
+ root_vals_.insert(root_vals_.end(), vals.begin(), vals.end());
+}
+
+size_t HeapWalker::Allocations() {
+ return allocations_.size();
+}
+
+size_t HeapWalker::AllocationBytes() {
+ return allocation_bytes_;
+}
+
+bool HeapWalker::DetectLeaks() {
+ for (auto it = roots_.begin(); it != roots_.end(); it++) {
+ Walk(*it, &RangeInfo::referenced_from_root);
+ }
+
+ 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);
+ }
+ }
+
+ 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;
+ size_t leak_bytes = 0;
+ for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
+ if (!it->second.referenced_from_root) {
+ num_leaks++;
+ leak_bytes += it->first.end - it->first.begin;
+ }
+ }
+
+ size_t n = 0;
+ for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
+ if (!it->second.referenced_from_root) {
+ if (n++ <= limit) {
+ leaked.push_back(it->first);
+ }
+ }
+ }
+
+ if (num_leaks_out) {
+ *num_leaks_out = num_leaks;
+ }
+ if (leak_bytes_out) {
+ *leak_bytes_out = leak_bytes;
+ }
+
+ return true;
+}
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
new file mode 100644
index 0000000..4be1934
--- /dev/null
+++ b/libmemunreachable/HeapWalker.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_HEAP_WALKER_H_
+#define LIBMEMUNREACHABLE_HEAP_WALKER_H_
+
+#include "android-base/macros.h"
+
+#include "Allocator.h"
+
+// A range [begin, end)
+struct Range {
+ uintptr_t begin;
+ uintptr_t end;
+};
+
+// Comparator for Ranges that returns equivalence for overlapping ranges
+struct compare_range {
+ bool operator()(const Range& a, const Range& b) const {
+ return a.end <= b.begin;
+ }
+};
+
+
+class HeapWalker {
+ public:
+ HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
+ allocations_(allocator), allocation_bytes_(0),
+ roots_(allocator), root_vals_(allocator) {
+ valid_allocations_range_.end = 0;
+ valid_allocations_range_.begin = ~valid_allocations_range_.end;
+ }
+ ~HeapWalker() {}
+ bool Allocation(uintptr_t begin, uintptr_t end);
+ void Root(uintptr_t begin, uintptr_t end);
+ void Root(const allocator::vector<uintptr_t>& vals);
+
+ bool DetectLeaks();
+
+ bool Leaked(allocator::vector<Range>&, size_t limit, size_t* num_leaks,
+ size_t* leak_bytes);
+ size_t Allocations();
+ size_t AllocationBytes();
+
+ private:
+ struct RangeInfo {
+ bool referenced_from_root;
+ bool referenced_from_leak;
+ };
+ void Walk(const Range& range, bool RangeInfo::* flag);
+ DISALLOW_COPY_AND_ASSIGN(HeapWalker);
+ Allocator<HeapWalker> allocator_;
+ using RangeMap = allocator::map<RangeInfo, Range, compare_range>;
+ RangeMap allocations_;
+ size_t allocation_bytes_;
+ Range valid_allocations_range_;
+
+ allocator::vector<Range> roots_;
+ allocator::vector<uintptr_t> root_vals_;
+};
+
+#endif
diff --git a/libmemunreachable/LeakPipe.cpp b/libmemunreachable/LeakPipe.cpp
new file mode 100644
index 0000000..080f8a7
--- /dev/null
+++ b/libmemunreachable/LeakPipe.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "LeakPipe.h"
+
+#include "log.h"
+
+bool LeakPipe::SendFd(int sock, int fd) {
+ struct msghdr hdr{};
+ struct iovec iov{};
+ unsigned int data = 0xfdfdfdfd;
+ alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ iov.iov_base = &data;
+ iov.iov_len = sizeof(data);
+
+ hdr.msg_control = cmsgbuf;
+ hdr.msg_controllen = CMSG_LEN(sizeof(int));
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ *(int*)CMSG_DATA(cmsg) = fd;
+
+ int ret = sendmsg(sock, &hdr, 0);
+ if (ret < 0) {
+ ALOGE("failed to send fd: %s", strerror(errno));
+ return false;
+ }
+ if (ret == 0) {
+ ALOGE("eof when sending fd");
+ return false;
+ }
+
+ return true;
+}
+
+int LeakPipe::ReceiveFd(int sock) {
+ struct msghdr hdr{};
+ struct iovec iov{};
+ unsigned int data;
+ alignas(struct cmsghdr) char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ iov.iov_base = &data;
+ iov.iov_len = sizeof(data);
+
+ hdr.msg_control = cmsgbuf;
+ hdr.msg_controllen = CMSG_LEN(sizeof(int));
+
+ int ret = recvmsg(sock, &hdr, 0);
+ if (ret < 0) {
+ ALOGE("failed to receive fd: %s", strerror(errno));
+ return -1;
+ }
+ if (ret == 0) {
+ ALOGE("eof when receiving fd");
+ return -1;
+ }
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+ if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ ALOGE("missing fd while receiving fd");
+ return -1;
+ }
+
+ return *(int*)CMSG_DATA(cmsg);
+}
diff --git a/libmemunreachable/LeakPipe.h b/libmemunreachable/LeakPipe.h
new file mode 100644
index 0000000..3f4e0b7
--- /dev/null
+++ b/libmemunreachable/LeakPipe.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LEAK_PIPE_H_
+#define LIBMEMUNREACHABLE_LEAK_PIPE_H_
+
+#include <sys/socket.h>
+
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "ScopedPipe.h"
+#include "log.h"
+
+// LeakPipe implements a pipe that can transfer vectors of simple objects
+// between processes. The pipe is created in the sending process and
+// transferred over a socketpair that was created before forking. This ensures
+// that only the sending process can have the send side of the pipe open, so if
+// the sending process dies the pipe will close.
+class LeakPipe {
+ public:
+ LeakPipe() {
+ int ret = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sv_);
+ if (ret < 0) {
+ LOG_ALWAYS_FATAL("failed to create socketpair: %s", strerror(errno));
+ }
+ }
+
+ ~LeakPipe() {
+ Close();
+ }
+
+ void Close() {
+ close(sv_[0]);
+ close(sv_[1]);
+ sv_[0] = -1;
+ sv_[1] = -1;
+ }
+
+ bool OpenReceiver() {
+ int fd = ReceiveFd(sv_[0]);
+ if (fd < 0) {
+ return false;
+ }
+
+ receiver_.SetFd(fd);
+ return true;
+ }
+
+ bool OpenSender() {
+ ScopedPipe pipe;
+
+ if (!SendFd(sv_[1], pipe.Receiver())) {
+ return false;
+ }
+ pipe.ReleaseReceiver();
+
+ sender_.SetFd(pipe.ReleaseSender());
+ return true;
+ }
+
+ class LeakPipeBase {
+ public:
+ LeakPipeBase() : fd_(-1) {}
+
+ ~LeakPipeBase() {
+ Close();
+ }
+
+ void SetFd(int fd) {
+ fd_ = fd;
+ }
+
+ void Close() {
+ close(fd_);
+ fd_ = -1;
+ }
+
+ protected:
+ int fd_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LeakPipeBase);
+ };
+
+ class LeakPipeSender : public LeakPipeBase {
+ public:
+ using LeakPipeBase::LeakPipeBase;
+
+ template<typename T>
+ bool Send(const T& value) {
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, &value, sizeof(T)));
+ if (ret < 0) {
+ ALOGE("failed to send value: %s", strerror(errno));
+ return false;
+ } else if (static_cast<size_t>(ret) != sizeof(T)) {
+ ALOGE("eof while writing value");
+ return false;
+ }
+
+ return true;
+ }
+
+ template<class T, class Alloc = std::allocator<T>>
+ bool SendVector(const std::vector<T, Alloc>& vector) {
+ size_t size = vector.size() * sizeof(T);
+ if (!Send(size)) {
+ return false;
+ }
+
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd_, vector.data(), size));
+ if (ret < 0) {
+ ALOGE("failed to send vector: %s", strerror(errno));
+ return false;
+ } else if (static_cast<size_t>(ret) != size) {
+ ALOGE("eof while writing vector");
+ return false;
+ }
+
+ return true;
+ }
+ };
+
+ class LeakPipeReceiver : public LeakPipeBase {
+ public:
+ using LeakPipeBase::LeakPipeBase;
+
+ template<typename T>
+ bool Receive(T* value) {
+ ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, reinterpret_cast<void*>(value), sizeof(T)));
+ if (ret < 0) {
+ ALOGE("failed to receive value: %s", strerror(errno));
+ return false;
+ } else if (static_cast<size_t>(ret) != sizeof(T)) {
+ ALOGE("eof while receiving value");
+ return false;
+ }
+
+ return true;
+ }
+
+ template<class T, class Alloc = std::allocator<T>>
+ bool ReceiveVector(std::vector<T, Alloc>& vector) {
+ size_t size = 0;
+ if (!Receive(&size)) {
+ return false;
+ }
+
+ vector.resize(size / sizeof(T));
+
+ char* ptr = reinterpret_cast<char*>(vector.data());
+ while (size > 0) {
+ ssize_t ret = TEMP_FAILURE_RETRY(read(fd_, ptr, size));
+ if (ret < 0) {
+ ALOGE("failed to send vector: %s", strerror(errno));
+ return false;
+ } else if (ret == 0) {
+ ALOGE("eof while reading vector");
+ return false;
+ }
+ size -= ret;
+ ptr += ret;
+ }
+
+ return true;
+ }
+
+ };
+
+ LeakPipeReceiver& Receiver() {
+ return receiver_;
+ }
+
+ LeakPipeSender& Sender() {
+ return sender_;
+ }
+
+ private:
+ LeakPipeReceiver receiver_;
+ LeakPipeSender sender_;
+ bool SendFd(int sock, int fd);
+ int ReceiveFd(int sock);
+ DISALLOW_COPY_AND_ASSIGN(LeakPipe);
+ int sv_[2];
+};
+
+#endif // LIBMEMUNREACHABLE_LEAK_PIPE_H_
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp
new file mode 100644
index 0000000..d3580c0
--- /dev/null
+++ b/libmemunreachable/LineBuffer.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Copied from system/extras/memory_replay/LineBuffer.cpp
+// TODO(ccross): find a way to share between libmemunreachable and memory_replay?
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "LineBuffer.h"
+
+LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len) : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {
+}
+
+bool LineBuffer::GetLine(char** line, size_t* line_len) {
+ while (true) {
+ if (bytes_ > 0) {
+ char* newline = reinterpret_cast<char*>(memchr(buffer_ + start_, '\n', bytes_));
+ if (newline != nullptr) {
+ *newline = '\0';
+ *line = buffer_ + start_;
+ start_ = newline - buffer_ + 1;
+ bytes_ -= newline - *line + 1;
+ *line_len = newline - *line;
+ return true;
+ }
+ }
+ if (start_ > 0) {
+ // Didn't find anything, copy the current to the front of the buffer.
+ memmove(buffer_, buffer_ + start_, bytes_);
+ start_ = 0;
+ }
+ ssize_t bytes = TEMP_FAILURE_RETRY(read(fd_, buffer_ + bytes_, buffer_len_ - bytes_ - 1));
+ if (bytes <= 0) {
+ if (bytes_ > 0) {
+ // The read data might not contain a nul terminator, so add one.
+ buffer_[bytes_] = '\0';
+ *line = buffer_ + start_;
+ *line_len = bytes_;
+ bytes_ = 0;
+ start_ = 0;
+ return true;
+ }
+ return false;
+ }
+ bytes_ += bytes;
+ }
+}
diff --git a/libmemunreachable/LineBuffer.h b/libmemunreachable/LineBuffer.h
new file mode 100644
index 0000000..a015c46
--- /dev/null
+++ b/libmemunreachable/LineBuffer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBMEMUNREACHABLE_LINE_BUFFER_H
+#define _LIBMEMUNREACHABLE_LINE_BUFFER_H
+
+#include <stdint.h>
+
+class LineBuffer {
+ public:
+ LineBuffer(int fd, char* buffer, size_t buffer_len);
+
+ bool GetLine(char** line, size_t* line_len);
+
+ private:
+ int fd_;
+ char* buffer_ = nullptr;
+ size_t buffer_len_ = 0;
+ size_t start_ = 0;
+ size_t bytes_ = 0;
+};
+
+#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/LinkedList.h b/libmemunreachable/LinkedList.h
new file mode 100644
index 0000000..3e44035
--- /dev/null
+++ b/libmemunreachable/LinkedList.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LINKED_LIST_H_
+#define LIBMEMUNREACHABLE_LINKED_LIST_H_
+
+template<class T>
+class LinkedList {
+public:
+ LinkedList() : next_(this), prev_(this), data_() {}
+ LinkedList(T data) : LinkedList() {
+ data_ = data;
+ }
+ ~LinkedList() {}
+ void insert(LinkedList<T>& node) {
+ assert(node.empty());
+ node.next_ = this->next_;
+ node.next_->prev_ = &node;
+ this->next_ = &node;
+ node.prev_ = this;
+ }
+ void remove() {
+ this->next_->prev_ = this->prev_;
+ this->prev_->next_ = this->next_;
+ this->next_ = this;
+ this->prev_ = this;
+ }
+ T data() { return data_; }
+ bool empty() { return next_ == this && prev_ == this; }
+ LinkedList<T> *next() { return next_; }
+private:
+ LinkedList<T> *next_;
+ LinkedList<T> *prev_;
+ T data_;
+};
+
+template<class T>
+class LinkedListHead {
+public:
+ LinkedListHead() : node_() {}
+ ~LinkedListHead() {}
+
+private:
+ LinkedList<T> node_;
+};
+
+#endif
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
new file mode 100644
index 0000000..eca26eb
--- /dev/null
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+
+#include <functional>
+#include <iomanip>
+#include <mutex>
+#include <string>
+#include <sstream>
+
+#include <backtrace.h>
+#include <android-base/macros.h>
+
+#include "Allocator.h"
+#include "HeapWalker.h"
+#include "LeakPipe.h"
+#include "ProcessMappings.h"
+#include "PtracerThread.h"
+#include "ScopedDisableMalloc.h"
+#include "Semaphore.h"
+#include "ThreadCapture.h"
+
+#include "memunreachable/memunreachable.h"
+#include "bionic.h"
+#include "log.h"
+
+const size_t Leak::contents_length;
+
+using namespace std::chrono_literals;
+
+class MemUnreachable {
+ public:
+ MemUnreachable(pid_t pid, Allocator<void> allocator) : pid_(pid), allocator_(allocator),
+ heap_walker_(allocator_) {}
+ bool CollectAllocations(const allocator::vector<ThreadInfo>& threads,
+ const allocator::vector<Mapping>& mappings);
+ bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
+ size_t* num_leaks, size_t* leak_bytes);
+ size_t Allocations() { return heap_walker_.Allocations(); }
+ size_t AllocationBytes() { return heap_walker_.AllocationBytes(); }
+ private:
+ bool ClassifyMappings(const allocator::vector<Mapping>& mappings,
+ allocator::vector<Mapping>& heap_mappings,
+ allocator::vector<Mapping>& anon_mappings,
+ allocator::vector<Mapping>& globals_mappings,
+ allocator::vector<Mapping>& stack_mappings);
+ DISALLOW_COPY_AND_ASSIGN(MemUnreachable);
+ pid_t pid_;
+ Allocator<void> allocator_;
+ HeapWalker heap_walker_;
+};
+
+static void HeapIterate(const Mapping& heap_mapping,
+ const std::function<void(uintptr_t, size_t)>& func) {
+ malloc_iterate(heap_mapping.begin, heap_mapping.end - heap_mapping.begin,
+ [](uintptr_t base, size_t size, void* arg) {
+ auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg);
+ (*f)(base, size);
+ }, const_cast<void*>(reinterpret_cast<const void*>(&func)));
+}
+
+bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
+ const allocator::vector<Mapping>& mappings) {
+ ALOGI("searching process %d for allocations", pid_);
+ allocator::vector<Mapping> heap_mappings{mappings};
+ allocator::vector<Mapping> anon_mappings{mappings};
+ allocator::vector<Mapping> globals_mappings{mappings};
+ allocator::vector<Mapping> stack_mappings{mappings};
+ if (!ClassifyMappings(mappings, heap_mappings, anon_mappings,
+ globals_mappings, stack_mappings)) {
+ return false;
+ }
+
+ for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) {
+ ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ HeapIterate(*it, [&](uintptr_t base, size_t size) {
+ heap_walker_.Allocation(base, base + size);
+ });
+ }
+
+ for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) {
+ ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ heap_walker_.Allocation(it->begin, it->end);
+ }
+
+ for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) {
+ ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name);
+ heap_walker_.Root(it->begin, it->end);
+ }
+
+ for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) {
+ for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) {
+ if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) {
+ ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name);
+ heap_walker_.Root(thread_it->stack.first, it->end);
+ }
+ }
+ heap_walker_.Root(thread_it->regs);
+ }
+
+ ALOGI("searching done");
+
+ return true;
+}
+
+bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
+ size_t* num_leaks, size_t* leak_bytes) {
+ ALOGI("sweeping process %d for unreachable memory", pid_);
+ leaks.clear();
+
+ allocator::vector<Range> leaked{allocator_};
+ if (!heap_walker_.Leaked(leaked, limit, num_leaks, leak_bytes)) {
+ 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);
+ }
+
+ ALOGI("sweeping done");
+
+ return true;
+}
+
+static bool has_prefix(const allocator::string& s, const char* prefix) {
+ int ret = s.compare(0, strlen(prefix), prefix);
+ return ret == 0;
+}
+
+bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
+ allocator::vector<Mapping>& heap_mappings,
+ allocator::vector<Mapping>& anon_mappings,
+ allocator::vector<Mapping>& globals_mappings,
+ allocator::vector<Mapping>& stack_mappings)
+{
+ heap_mappings.clear();
+ anon_mappings.clear();
+ globals_mappings.clear();
+ stack_mappings.clear();
+
+ allocator::string current_lib{allocator_};
+
+ for (auto it = mappings.begin(); it != mappings.end(); it++) {
+ if (it->execute) {
+ current_lib = it->name;
+ continue;
+ }
+
+ if (!it->read) {
+ continue;
+ }
+
+ const allocator::string mapping_name{it->name, allocator_};
+ if (mapping_name == "[anon:.bss]") {
+ // named .bss section
+ globals_mappings.emplace_back(*it);
+ } else if (mapping_name == current_lib) {
+ // .rodata or .data section
+ globals_mappings.emplace_back(*it);
+ } else if (mapping_name == "[anon:libc_malloc]") {
+ // named malloc mapping
+ heap_mappings.emplace_back(*it);
+ } else if (has_prefix(mapping_name, "/dev/ashmem/dalvik")) {
+ // named dalvik heap mapping
+ globals_mappings.emplace_back(*it);
+ } else if (has_prefix(mapping_name, "[stack")) {
+ // named stack mapping
+ stack_mappings.emplace_back(*it);
+ } else if (mapping_name.size() == 0) {
+ globals_mappings.emplace_back(*it);
+ } else if (has_prefix(mapping_name, "[anon:") && mapping_name != "[anon:leak_detector_malloc]") {
+ // TODO(ccross): it would be nice to treat named anonymous mappings as
+ // possible leaks, but naming something in a .bss or .data section makes
+ // it impossible to distinguish them from mmaped and then named mappings.
+ globals_mappings.emplace_back(*it);
+ }
+ }
+
+ return true;
+}
+
+bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) {
+ int parent_pid = getpid();
+ int parent_tid = gettid();
+
+ Heap heap;
+
+ Semaphore continue_parent_sem;
+ LeakPipe pipe;
+
+ PtracerThread thread{[&]() -> int {
+ /////////////////////////////////////////////
+ // Collection thread
+ /////////////////////////////////////////////
+ ALOGI("collecting thread info for process %d...", parent_pid);
+
+ ThreadCapture thread_capture(parent_pid, heap);
+ allocator::vector<ThreadInfo> thread_info(heap);
+ allocator::vector<Mapping> mappings(heap);
+
+ // ptrace all the threads
+ if (!thread_capture.CaptureThreads()) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
+ // collect register contents and stacks
+ if (!thread_capture.CapturedThreadInfo(thread_info)) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
+ // snapshot /proc/pid/maps
+ if (!ProcessMappings(parent_pid, mappings)) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
+ // malloc must be enabled to call fork, at_fork handlers take the same
+ // locks as ScopedDisableMalloc. All threads are paused in ptrace, so
+ // memory state is still consistent. Unfreeze the original thread so it
+ // can drop the malloc locks, it will block until the collection thread
+ // exits.
+ thread_capture.ReleaseThread(parent_tid);
+ continue_parent_sem.Post();
+
+ // fork a process to do the heap walking
+ int ret = fork();
+ if (ret < 0) {
+ return 1;
+ } else if (ret == 0) {
+ /////////////////////////////////////////////
+ // Heap walker process
+ /////////////////////////////////////////////
+ // Examine memory state in the child using the data collected above and
+ // the CoW snapshot of the process memory contents.
+
+ if (!pipe.OpenSender()) {
+ _exit(1);
+ }
+
+ MemUnreachable unreachable{parent_pid, heap};
+
+ if (!unreachable.CollectAllocations(thread_info, mappings)) {
+ _exit(2);
+ }
+ size_t num_allocations = unreachable.Allocations();
+ size_t allocation_bytes = unreachable.AllocationBytes();
+
+ allocator::vector<Leak> leaks{heap};
+
+ size_t num_leaks = 0;
+ size_t leak_bytes = 0;
+ bool ok = unreachable.GetUnreachableMemory(leaks, limit, &num_leaks, &leak_bytes);
+
+ ok = ok && pipe.Sender().Send(num_allocations);
+ ok = ok && pipe.Sender().Send(allocation_bytes);
+ ok = ok && pipe.Sender().Send(num_leaks);
+ ok = ok && pipe.Sender().Send(leak_bytes);
+ ok = ok && pipe.Sender().SendVector(leaks);
+
+ if (!ok) {
+ _exit(3);
+ }
+
+ _exit(0);
+ } else {
+ // Nothing left to do in the collection thread, return immediately,
+ // releasing all the captured threads.
+ ALOGI("collection thread done");
+ return 0;
+ }
+ }};
+
+ /////////////////////////////////////////////
+ // Original thread
+ /////////////////////////////////////////////
+
+ {
+ // Disable malloc to get a consistent view of memory
+ ScopedDisableMalloc disable_malloc;
+
+ // Start the collection thread
+ thread.Start();
+
+ // Wait for the collection thread to signal that it is ready to fork the
+ // heap walker process.
+ continue_parent_sem.Wait(30s);
+
+ // Re-enable malloc so the collection thread can fork.
+ }
+
+ // Wait for the collection thread to exit
+ int ret = thread.Join();
+ if (ret != 0) {
+ return false;
+ }
+
+ // Get a pipe from the heap walker process. Transferring a new pipe fd
+ // ensures no other forked processes can have it open, so when the heap
+ // walker process dies the remote side of the pipe will close.
+ if (!pipe.OpenReceiver()) {
+ return false;
+ }
+
+ bool ok = true;
+ ok = ok && pipe.Receiver().Receive(&info.num_allocations);
+ ok = ok && pipe.Receiver().Receive(&info.allocation_bytes);
+ ok = ok && pipe.Receiver().Receive(&info.num_leaks);
+ ok = ok && pipe.Receiver().Receive(&info.leak_bytes);
+ ok = ok && pipe.Receiver().ReceiveVector(info.leaks);
+ if (!ok) {
+ return false;
+ }
+
+ ALOGI("unreachable memory detection done");
+ ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
+ info.leak_bytes, info.num_leaks, info.num_leaks == 1 ? "" : "s",
+ info.allocation_bytes, info.num_allocations, info.num_allocations == 1 ? "" : "s");
+
+ return true;
+}
+
+std::string Leak::ToString(bool log_contents) const {
+
+ std::ostringstream oss;
+
+ oss << " " << std::dec << size;
+ oss << " bytes unreachable at ";
+ oss << std::hex << begin;
+ oss << std::endl;
+
+ if (log_contents) {
+ const int bytes_per_line = 16;
+ const size_t bytes = std::min(size, contents_length);
+
+ if (bytes == size) {
+ oss << " contents:" << std::endl;
+ } else {
+ oss << " first " << bytes << " bytes of contents:" << std::endl;
+ }
+
+ for (size_t i = 0; i < bytes; i += bytes_per_line) {
+ oss << " " << std::hex << begin + i << ": ";
+ size_t j;
+ oss << std::setfill('0');
+ for (j = i; j < bytes && j < i + bytes_per_line; j++) {
+ oss << std::setw(2) << static_cast<int>(contents[j]) << " ";
+ }
+ oss << std::setfill(' ');
+ for (; j < i + bytes_per_line; j++) {
+ oss << " ";
+ }
+ for (j = i; j < bytes && j < i + bytes_per_line; j++) {
+ char c = contents[j];
+ if (c < ' ' || c >= 0x7f) {
+ c = '.';
+ }
+ oss << c;
+ }
+ oss << std::endl;
+ }
+ }
+ if (num_backtrace_frames > 0) {
+ oss << backtrace_string(backtrace_frames, num_backtrace_frames);
+ }
+
+ return oss.str();
+}
+
+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 << std::endl;
+
+ for (auto it = leaks.begin(); it != leaks.end(); it++) {
+ oss << it->ToString(log_contents);
+ }
+
+ return oss.str();
+}
+
+std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
+ UnreachableMemoryInfo info;
+ if (!GetUnreachableMemory(info, limit)) {
+ return "Failed to get unreachable memory\n";
+ }
+
+ return info.ToString(log_contents);
+}
+
+bool LogUnreachableMemory(bool log_contents, size_t limit) {
+ UnreachableMemoryInfo info;
+ if (!GetUnreachableMemory(info, limit)) {
+ return false;
+ }
+
+ for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) {
+ ALOGE("%s", it->ToString(log_contents).c_str());
+ }
+ return true;
+}
+
+
+bool NoLeaks() {
+ UnreachableMemoryInfo info;
+ if (!GetUnreachableMemory(info, 0)) {
+ return false;
+ }
+
+ return info.num_leaks == 0;
+}
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
new file mode 100644
index 0000000..7cca7c1
--- /dev/null
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+#include "LineBuffer.h"
+#include "ProcessMappings.h"
+#include "log.h"
+
+// This function is not re-entrant since it uses a static buffer for
+// the line data.
+bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
+ char map_buffer[1024];
+ snprintf(map_buffer, sizeof(map_buffer), "/proc/%d/maps", pid);
+ int fd = open(map_buffer, O_RDONLY);
+ if (fd < 0) {
+ return false;
+ }
+ android::base::unique_fd fd_guard{fd};
+
+ LineBuffer line_buf(fd, map_buffer, sizeof(map_buffer));
+ char* line;
+ size_t line_len;
+ while (line_buf.GetLine(&line, &line_len)) {
+ int name_pos;
+ char perms[5];
+ Mapping mapping{};
+ if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n",
+ &mapping.begin, &mapping.end, perms, &name_pos) == 3) {
+ if (perms[0] == 'r') {
+ mapping.read = true;
+ }
+ if (perms[1] == 'w') {
+ mapping.write = true;
+ }
+ if (perms[2] == 'x') {
+ mapping.execute = true;
+ }
+ if (perms[3] == 'p') {
+ mapping.priv = true;
+ }
+ if ((size_t)name_pos < line_len) {
+ strlcpy(mapping.name, line + name_pos, sizeof(mapping.name));
+ }
+ mappings.emplace_back(mapping);
+ }
+ }
+ return true;
+}
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h
new file mode 100644
index 0000000..d3b7496
--- /dev/null
+++ b/libmemunreachable/ProcessMappings.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+#define LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+
+#include "Allocator.h"
+
+struct Mapping {
+ uintptr_t begin;
+ uintptr_t end;
+ bool read;
+ bool write;
+ bool execute;
+ bool priv;
+ char name[96];
+};
+
+// This function is not re-entrant since it uses a static buffer for
+// the line data.
+bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings);
+
+#endif // LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
new file mode 100644
index 0000000..aa5b344
--- /dev/null
+++ b/libmemunreachable/PtracerThread.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "android-base/macros.h"
+
+#include "anon_vma_naming.h"
+#include "log.h"
+#include "PtracerThread.h"
+
+class Stack {
+ public:
+ Stack(size_t size) : size_(size) {
+ int prot = PROT_READ | PROT_WRITE;
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+ page_size_ = sysconf(_SC_PAGE_SIZE);
+ size_ += page_size_*2; // guard pages
+ base_ = mmap(NULL, size_, prot, flags, -1, 0);
+ if (base_ == MAP_FAILED) {
+ base_ = NULL;
+ size_ = 0;
+ return;
+ }
+ prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, base_, size_, "libmemunreachable stack");
+ mprotect(base_, page_size_, PROT_NONE);
+ mprotect(top(), page_size_, PROT_NONE);
+ };
+ ~Stack() {
+ munmap(base_, size_);
+ };
+ void* top() {
+ return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_) + size_ - page_size_);
+ };
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Stack);
+
+ void *base_;
+ size_t size_;
+ size_t page_size_;
+};
+
+PtracerThread::PtracerThread(const std::function<int()>& func) :
+ child_pid_(0) {
+ stack_ = std::make_unique<Stack>(PTHREAD_STACK_MIN);
+ if (stack_->top() == nullptr) {
+ LOG_ALWAYS_FATAL("failed to mmap child stack: %s", strerror(errno));
+ }
+
+ func_ = std::function<int()>{[&, func]() -> int {
+ // In the child thread, lock and unlock the mutex to wait for the parent
+ // to finish setting up for the child thread
+ std::unique_lock<std::mutex> lk(m_);
+ lk.unlock();
+ _exit(func());
+ }};
+}
+
+PtracerThread::~PtracerThread() {
+ Kill();
+ Join();
+ ClearTracer();
+ stack_ = nullptr;
+}
+
+bool PtracerThread::Start() {
+ std::unique_lock<std::mutex> lk(m_);
+
+ // Convert from void(*)(void*) to lambda with captures
+ auto proxy = [](void *arg) -> int {
+ prctl(PR_SET_NAME, "libmemunreachable ptrace thread");
+ return (*reinterpret_cast<std::function<int()>*>(arg))();
+ };
+
+ child_pid_ = clone(proxy, stack_->top(),
+ CLONE_VM|CLONE_FS|CLONE_FILES/*|CLONE_UNTRACED*/,
+ reinterpret_cast<void*>(&func_));
+ if (child_pid_ < 0) {
+ ALOGE("failed to clone child: %s", strerror(errno));
+ return false;
+ }
+
+ SetTracer(child_pid_);
+
+ lk.unlock();
+
+ return true;
+}
+
+int PtracerThread::Join() {
+ if (child_pid_ == -1) {
+ return -1;
+ }
+ int status;
+ int ret = TEMP_FAILURE_RETRY(waitpid(child_pid_, &status, __WALL));
+ if (ret < 0) {
+ ALOGE("waitpid %d failed: %s", child_pid_, strerror(errno));
+ return -1;
+ }
+
+ child_pid_ = -1;
+
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ return -WTERMSIG(status);
+ } else {
+ ALOGE("unexpected status %x", status);
+ return -1;
+ }
+}
+
+void PtracerThread::Kill() {
+ if (child_pid_ == -1) {
+ return;
+ }
+
+ syscall(SYS_tkill, child_pid_, SIGKILL);
+}
+
+void PtracerThread::SetTracer(pid_t tracer_pid) {
+ prctl(PR_SET_PTRACER, tracer_pid);
+}
+
+void PtracerThread::ClearTracer() {
+ prctl(PR_SET_PTRACER, 0);
+}
diff --git a/libmemunreachable/PtracerThread.h b/libmemunreachable/PtracerThread.h
new file mode 100644
index 0000000..4d6ca9a
--- /dev/null
+++ b/libmemunreachable/PtracerThread.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+#define LIBMEMUNREACHABLE_PTRACER_THREAD_H_
+
+#include <functional>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+#include "Allocator.h"
+
+class Stack;
+
+// PtracerThread is similar to std::thread, except that it creates a "thread"
+// that can ptrace the other threads. The thread is actually a separate
+// process, with its own thread group, but shares address space and fds with
+// the parent.
+class PtracerThread {
+ public:
+ PtracerThread(const std::function<int()>& func);
+ ~PtracerThread();
+ bool Start();
+ int Join();
+ private:
+ void SetTracer(pid_t);
+ void ClearTracer();
+ void Kill();
+ DISALLOW_COPY_AND_ASSIGN(PtracerThread);
+ std::unique_ptr<Stack> stack_;
+ std::function<int()> func_;
+ std::mutex m_;
+ pid_t child_pid_;
+};
+
+#endif // LIBMEMUNREACHABLE_PTRACER_THREAD_H_
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
new file mode 100644
index 0000000..61a47de
--- /dev/null
+++ b/libmemunreachable/README.md
@@ -0,0 +1,71 @@
+libmemunreachable
+================
+
+Introduction
+--------------
+libmemunreachable is a zero-overhead native memory leak detector. It uses an imprecise mark-and-sweep garbage collector pass over all native memory, reporting any unreachable blocks as leaks. It is similar to the [Heap Checker from tcmalloc](http://htmlpreview.github.io/?https://github.com/gperftools/gperftools/blob/master/doc/heap_checker.html), but with a few key differences to remove the overhead. Instead of instrumenting every call to malloc and free, it queries the allocator (jemalloc) for active allocations when leak detection is requested. In addition, it performs a very short stop-the-world data collection on the main process, and then forks a copy of the process to perform the mark-and-sweep, minimizing disruption to the original process.
+
+In the default (zero-overhead) mode, the returned data on leaks is limited to the address, approximate (upper bound) size, and the the first 32 bytes of the contents of the leaked allocation. If malloc_debug backtraces are enabled they will be included in the leak information, but backtracing allocations requires significant overhead.
+
+----------
+
+Usage
+-------
+
+### C interface ###
+
+#### `bool LogUnreachableMemory(bool log_contents, size_t limit)` ####
+Writes a description of leaked memory to the log. A summary is always written, followed by details of up to `limit` leaks. If `log_contents` is `true`, details include up to 32 bytes of the contents of each leaked allocation.
+Returns true if leak detection succeeded.
+
+#### `bool NoLeaks()` ####
+Returns `true` if no unreachable memory was found.
+
+### C++ interface ###
+
+####`bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)`####
+Updates an `UnreachableMemoryInfo` object with information on leaks, including details on up to `limit` leaks. Returns true if leak detection succeeded.
+
+#### `std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100)` ####
+Returns a description of leaked memory. A summary is always written, followed by details of up to `limit` leaks. If `log_contents` is `true`, details include up to 32 bytes of the contents of each leaked allocation.
+Returns true if leak detection succeeded.
+
+Implementation
+-------------------
+The sequence of steps required to perform a leak detection pass is divided into three processes - the original process, the collection process, and the sweeper process.
+
+ 1. *Original process*: Leak detection is requested by calling `GetUnreachableMemory()`
+ 2. Allocations are disabled using `malloc_disable()`
+ 3. The collection process is spawned. The collection process is similar to a normal `fork()` child process, except that it shares the address space of the parent - any writes by the original process are visible to the collection process, and vice-versa.
+ 4. *Collection process*: All threads in the original process are paused with `ptrace()`.
+ 5. Registers contents, active stack areas, and memory mapping information are collected.
+ 6. *Original process*: Allocations are re-enabled using `malloc_enable()`, but all threads are still paused with `ptrace()`.
+ 7. *Collection process*: The sweeper process is spawned using a normal `fork()`. The sweeper process has a copy of all memory from the original process, including all the data collected by the collection process.
+ 8. Collection process releases all threads from `ptrace` and exits
+ 9. *Original process*: All threads continue, the thread that called `GetUnreachableMemory()` blocks waiting for leak data over a pipe.
+ 10. *Sweeper process*: A list of all active allocations is produced by examining the memory mappings and calling `malloc_iterate()` on any heap mappings.
+ 11. A list of all roots is produced from globals (.data and .bss sections of binaries), and registers and stacks from each thread.
+ 12. The mark-and-sweep pass is performed starting from roots.
+ 13. Unmarked allocations are sent over the pipe back to the original process.
+
+----------
+
+
+Components
+---------------
+- `MemUnreachable.cpp`: Entry points, implements the sequencing described above.
+- `PtracerThread.cpp`: Used to clone the collection process with shared address space.
+- `ThreadCapture.cpp`: Pauses threads in the main process and collects register contents.
+- `ProcessMappings.cpp`: Collects snapshots of `/proc/pid/maps`.
+- `HeapWalker.cpp`: Performs the mark-and-sweep pass over active allocations.
+- `LeakPipe.cpp`: transfers data describing leaks from the sweeper process to the original process.
+
+
+Heap allocator requirements
+----------------------------------
+libmemunreachable requires a small interface to the allocator in order to collect information about active allocations.
+
+ - `malloc_disable()`: prevent any thread from mutating internal allocator state.
+ - `malloc enable()`: re-enable allocations in all threads.
+ - `malloc_iterate()`: call a callback on each active allocation in a given heap region.
+ - `malloc_backtrace()`: return the backtrace from when the allocation at the given address was allocated, if it was collected.
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
new file mode 100644
index 0000000..019deea
--- /dev/null
+++ b/libmemunreachable/ScopedAlarm.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SCOPED_ALARM_H_
+#define LIBMEMUNREACHABLE_SCOPED_ALARM_H_
+
+#include <signal.h>
+
+#include <chrono>
+#include <functional>
+
+class ScopedAlarm {
+ public:
+ ScopedAlarm(std::chrono::microseconds us, std::function<void()> func) {
+ func_ = func;
+ struct sigaction oldact{};
+ struct sigaction act{};
+ act.sa_handler = [](int) {
+ ScopedAlarm::func_();
+ };
+ sigaction(SIGALRM, &act, &oldact);
+
+ std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
+ itimerval t = itimerval{};
+ t.it_value.tv_sec = s.count();
+ t.it_value.tv_usec = (us - s).count();
+ setitimer(ITIMER_REAL, &t, NULL);
+ }
+ ~ScopedAlarm() {
+ itimerval t = itimerval{};
+ setitimer(ITIMER_REAL, &t, NULL);
+ struct sigaction act{};
+ act.sa_handler = SIG_DFL;
+ sigaction(SIGALRM, &act, NULL);
+ }
+ private:
+ static std::function<void()> func_;
+};
+#endif
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
new file mode 100644
index 0000000..4f96376
--- /dev/null
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+#define LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
+
+#include <memory>
+
+#include "android-base/macros.h"
+
+#include "bionic.h"
+#include "log.h"
+#include "ScopedAlarm.h"
+
+class DisableMallocGuard{
+ public:
+ DisableMallocGuard() : disabled_(false){}
+ ~DisableMallocGuard() {
+ Enable();
+ }
+
+ void Disable() {
+ if (!disabled_) {
+ malloc_disable();
+ disabled_ = true;
+ }
+ }
+
+ void Enable() {
+ if (disabled_) {
+ malloc_enable();
+ disabled_ = false;
+ }
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DisableMallocGuard);
+ bool disabled_;
+};
+
+// Any calls to malloc or free from this thread will deadlock as long as this
+// object is in scope. Calls to malloc from other threads may succeed (for
+// example if the allocation is satisfied out of the thread's tcache), or may
+// block until the object is destroyed.
+//
+// Don't call fork() while malloc is disabled, it needs the same locks held
+// here.
+class ScopedDisableMalloc {
+ public:
+ ScopedDisableMalloc() {
+ disable_malloc_.Disable();
+ }
+
+ ~ScopedDisableMalloc() {
+ disable_malloc_.Enable();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableMalloc);
+ DisableMallocGuard disable_malloc_;
+};
+
+class ScopedDisableMallocTimeout {
+ public:
+ ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) :
+ timeout_(timeout), timed_out_(false), disable_malloc_() {
+ Disable();
+ }
+
+ ~ScopedDisableMallocTimeout() {
+ Enable();
+ }
+
+ bool timed_out() {
+ return timed_out_;
+ }
+
+ void Enable() {
+ disable_malloc_.Enable();
+ alarm_ = nullptr;
+ }
+
+ void Disable() {
+ // set up the alarm before disabling malloc so unique_ptr can be used
+ alarm_ = std::make_unique<ScopedAlarm>(timeout_, [&]() {
+ disable_malloc_.Enable();
+ timed_out_ = true;
+ });
+
+ disable_malloc_.Disable();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableMallocTimeout);
+ std::chrono::milliseconds timeout_;
+ bool timed_out_;
+ std::unique_ptr<ScopedAlarm> alarm_;
+ DisableMallocGuard disable_malloc_;
+};
+
+#endif // LIBMEMUNREACHABLE_SCOPED_DISABLE_MALLOC_H_
diff --git a/libmemunreachable/ScopedPipe.h b/libmemunreachable/ScopedPipe.h
new file mode 100644
index 0000000..9beef9a
--- /dev/null
+++ b/libmemunreachable/ScopedPipe.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SCOPED_PIPE_H_
+#define LIBMEMUNREACHABLE_SCOPED_PIPE_H_
+
+#include <unistd.h>
+
+#include "log.h"
+
+class ScopedPipe {
+ public:
+ ScopedPipe() : pipefd_{-1, -1} {
+ int ret = pipe2(pipefd_, O_CLOEXEC);
+ if (ret < 0) {
+ LOG_ALWAYS_FATAL("failed to open pipe");
+ }
+ }
+ ~ScopedPipe() {
+ Close();
+ }
+
+ ScopedPipe(ScopedPipe&& other) {
+ SetReceiver(other.ReleaseReceiver());
+ SetSender(other.ReleaseSender());
+ }
+
+ ScopedPipe& operator = (ScopedPipe&& other) {
+ SetReceiver(other.ReleaseReceiver());
+ SetSender(other.ReleaseSender());
+ return *this;
+ }
+
+ void CloseReceiver() {
+ close(ReleaseReceiver());
+ }
+
+ void CloseSender() {
+ close(ReleaseSender());
+ }
+
+ void Close() {
+ CloseReceiver();
+ CloseSender();
+ }
+
+ int Receiver() { return pipefd_[0]; }
+ int Sender() { return pipefd_[1]; }
+
+ int ReleaseReceiver() {
+ int ret = Receiver();
+ SetReceiver(-1);
+ return ret;
+ }
+
+ int ReleaseSender() {
+ int ret = Sender();
+ SetSender(-1);
+ return ret;
+ }
+
+ private:
+ void SetReceiver(int fd) { pipefd_[0] = fd; };
+ void SetSender(int fd) { pipefd_[1] = fd; };
+
+ int pipefd_[2];
+};
+#endif
diff --git a/libmemunreachable/Semaphore.h b/libmemunreachable/Semaphore.h
new file mode 100644
index 0000000..45e8c81
--- /dev/null
+++ b/libmemunreachable/Semaphore.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_SEMAPHORE_H_
+#define LIBMEMUNREACHABLE_SEMAPHORE_H_
+
+#include <chrono>
+#include <mutex>
+
+#include "android-base/macros.h"
+
+class Semaphore {
+ public:
+ Semaphore(int count = 0) : count_(count) {}
+ ~Semaphore() = default;
+
+ void Wait(std::chrono::milliseconds ms) {
+ std::unique_lock<std::mutex> lk(m_);
+ cv_.wait_for(lk, ms, [&]{
+ if (count_ > 0) {
+ count_--;
+ return true;
+ }
+ return false;
+ });
+ }
+ void Post() {
+ {
+ std::lock_guard<std::mutex> lk(m_);
+ count_++;
+ }
+ cv_.notify_one();
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Semaphore);
+
+ int count_;
+ std::mutex m_;
+ std::condition_variable cv_;
+};
+
+
+#endif // LIBMEMUNREACHABLE_SEMAPHORE_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
new file mode 100644
index 0000000..6357840
--- /dev/null
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ThreadCapture.h"
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "Allocator.h"
+#include "log.h"
+
+// bionic interfaces used:
+// atoi
+// strlcat
+// writev
+
+// bionic interfaces reimplemented to avoid allocation:
+// getdents64
+
+// Convert a pid > 0 to a string. sprintf might allocate, so we can't use it.
+// Returns a pointer somewhere in buf to a null terminated string, or NULL
+// on error.
+static char *pid_to_str(char *buf, size_t len, pid_t pid) {
+ if (pid <= 0) {
+ return nullptr;
+ }
+
+ char *ptr = buf + len - 1;
+ *ptr = 0;
+ while (pid > 0) {
+ ptr--;
+ if (ptr < buf) {
+ return nullptr;
+ }
+ *ptr = '0' + (pid % 10);
+ pid /= 10;
+ }
+
+ return ptr;
+}
+
+class ThreadCaptureImpl {
+ public:
+ ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator);
+ ~ThreadCaptureImpl() {}
+ bool ListThreads(TidList& tids);
+ bool CaptureThreads();
+ bool ReleaseThreads();
+ bool ReleaseThread(pid_t tid);
+ bool CapturedThreadInfo(ThreadInfoList& threads);
+ void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
+ private:
+ int CaptureThread(pid_t tid);
+ bool ReleaseThread(pid_t tid, unsigned int signal);
+ int PtraceAttach(pid_t tid);
+ void PtraceDetach(pid_t tid, unsigned int signal);
+ bool PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info);
+
+ allocator::map<unsigned int, pid_t> captured_threads_;
+ Allocator<ThreadCaptureImpl> allocator_;
+ pid_t pid_;
+ std::function<void(pid_t)> inject_test_func_;
+};
+
+ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) :
+ captured_threads_(allocator), allocator_(allocator), pid_(pid) {
+}
+
+bool ThreadCaptureImpl::ListThreads(TidList& tids) {
+ tids.clear();
+
+ char pid_buf[11];
+ char path[256] = "/proc/";
+ char* pid_str = pid_to_str(pid_buf, sizeof(pid_buf), pid_);
+ if (!pid_str) {
+ return false;
+ }
+ strlcat(path, pid_str, sizeof(path));
+ strlcat(path, "/task", sizeof(path));
+
+ int fd = open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
+ if (fd < 0) {
+ ALOGE("failed to open %s: %s", path, strerror(errno));
+ return false;
+ }
+ android::base::unique_fd fd_guard{fd};
+
+ struct linux_dirent64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ uint16_t d_reclen;
+ char d_type;
+ char d_name[];
+ } __attribute((packed));
+ char dirent_buf[4096];
+ ssize_t nread;
+ do {
+ nread = syscall(SYS_getdents64, fd, dirent_buf, sizeof(dirent_buf));
+ if (nread < 0) {
+ ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
+ return false;
+ } else if (nread > 0) {
+ ssize_t off = 0;
+ while (off < nread) {
+ linux_dirent64* dirent = reinterpret_cast<linux_dirent64*>(dirent_buf + off);
+ off += dirent->d_reclen;
+ pid_t tid = atoi(dirent->d_name);
+ if (tid <= 0) {
+ continue;
+ }
+ tids.push_back(tid);
+ }
+ }
+
+ } while (nread != 0);
+
+ return true;
+}
+
+bool ThreadCaptureImpl::CaptureThreads() {
+ TidList tids{allocator_};
+
+ bool found_new_thread;
+ do {
+ if (!ListThreads(tids)) {
+ ReleaseThreads();
+ return false;
+ }
+
+ found_new_thread = false;
+
+ for (auto it = tids.begin(); it != tids.end(); it++) {
+ auto captured = captured_threads_.find(*it);
+ if (captured == captured_threads_.end()) {
+ if (CaptureThread(*it) < 0) {
+ ReleaseThreads();
+ return false;
+ }
+ found_new_thread = true;
+ }
+ }
+ } while (found_new_thread);
+
+ return true;
+}
+
+// Detatches from a thread, delivering signal if nonzero, logs on error
+void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
+ void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
+ if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
+ ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ }
+}
+
+// Attaches to and pauses thread.
+// Returns 1 on attach, 0 on tid not found, -1 and logs on error
+int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
+ int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
+ if (ret < 0) {
+ ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ return -1;
+ }
+
+ if (inject_test_func_) {
+ inject_test_func_(tid);
+ }
+
+ if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) < 0) {
+ if (errno == ESRCH) {
+ return 0;
+ } else {
+ ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ PtraceDetach(tid, 0);
+ return -1;
+ }
+ }
+ return 1;
+}
+
+bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
+ thread_info.tid = tid;
+
+ const unsigned int max_num_regs = 128; // larger than number of registers on any device
+ uintptr_t regs[max_num_regs];
+ struct iovec iovec;
+ iovec.iov_base = ®s;
+ iovec.iov_len = sizeof(regs);
+
+ if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
+ ALOGE("ptrace getregset for thread %d of process %d failed: %s",
+ tid, pid_, strerror(errno));
+ return false;
+ }
+
+ unsigned int num_regs = iovec.iov_len / sizeof(uintptr_t);
+ thread_info.regs.assign(®s[0], ®s[num_regs]);
+
+ const int sp =
+#if defined(__x86_64__)
+ offsetof(struct pt_regs, rsp) / sizeof(uintptr_t)
+#elif defined(__i386__)
+ offsetof(struct pt_regs, esp) / sizeof(uintptr_t)
+#elif defined(__arm__)
+ offsetof(struct pt_regs, ARM_sp) / sizeof(uintptr_t)
+#elif defined(__aarch64__)
+ offsetof(struct user_pt_regs, sp) / sizeof(uintptr_t)
+#elif defined(__mips__) || defined(__mips64__)
+ offsetof(struct pt_regs, regs[29]) / sizeof(uintptr_t)
+#else
+#error Unrecognized architecture
+#endif
+ ;
+
+ // TODO(ccross): use /proc/tid/status or /proc/pid/maps to get start_stack
+
+ thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
+
+ return true;
+}
+
+int ThreadCaptureImpl::CaptureThread(pid_t tid) {
+ int ret = PtraceAttach(tid);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ int status = 0;
+ if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
+ ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
+ strerror(errno));
+ PtraceDetach(tid, 0);
+ return -1;
+ }
+
+ if (!WIFSTOPPED(status)) {
+ ALOGE("thread %d of process %d was not paused after waitpid, killed?",
+ tid, pid_);
+ return 0;
+ }
+
+ unsigned int resume_signal = 0;
+
+ unsigned int signal = WSTOPSIG(status);
+ if ((status >> 16) == PTRACE_EVENT_STOP) {
+ switch (signal) {
+ case SIGSTOP:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ // group-stop signals
+ break;
+ case SIGTRAP:
+ // normal ptrace interrupt stop
+ break;
+ default:
+ ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
+ signal, tid, pid_);
+ return -1;
+ }
+ } else {
+ // signal-delivery-stop
+ resume_signal = signal;
+ }
+
+ captured_threads_[tid] = resume_signal;
+ return 1;
+}
+
+bool ThreadCaptureImpl::ReleaseThread(pid_t tid) {
+ auto it = captured_threads_.find(tid);
+ if (it == captured_threads_.end()) {
+ return false;
+ }
+ return ReleaseThread(it->first, it->second);
+}
+
+bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) {
+ PtraceDetach(tid, signal);
+ return true;
+}
+
+bool ThreadCaptureImpl::ReleaseThreads() {
+ bool ret = true;
+ for (auto it = captured_threads_.begin(); it != captured_threads_.end(); ) {
+ if (ReleaseThread(it->first, it->second)) {
+ it = captured_threads_.erase(it);
+ } else {
+ it++;
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+bool ThreadCaptureImpl::CapturedThreadInfo(ThreadInfoList& threads) {
+ threads.clear();
+
+ for (auto it = captured_threads_.begin(); it != captured_threads_.end(); it++) {
+ ThreadInfo t{0, allocator::vector<uintptr_t>(allocator_), std::pair<uintptr_t, uintptr_t>(0, 0)};
+ if (!PtraceThreadInfo(it->first, t)) {
+ return false;
+ }
+ threads.push_back(t);
+ }
+ return true;
+}
+
+ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) {
+ Allocator<ThreadCaptureImpl> impl_allocator = allocator;
+ impl_ = impl_allocator.make_unique(pid, impl_allocator);
+}
+
+ThreadCapture::~ThreadCapture() {}
+
+bool ThreadCapture::ListThreads(TidList& tids) {
+ return impl_->ListThreads(tids);
+}
+
+bool ThreadCapture::CaptureThreads() {
+ return impl_->CaptureThreads();
+}
+
+bool ThreadCapture::ReleaseThreads() {
+ return impl_->ReleaseThreads();
+}
+
+bool ThreadCapture::ReleaseThread(pid_t tid) {
+ return impl_->ReleaseThread(tid);
+}
+
+bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) {
+ return impl_->CapturedThreadInfo(threads);
+}
+
+void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
+ impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
+}
diff --git a/libmemunreachable/ThreadCapture.h b/libmemunreachable/ThreadCapture.h
new file mode 100644
index 0000000..1022cad
--- /dev/null
+++ b/libmemunreachable/ThreadCapture.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_THREAD_CAPTURE_H_
+#define LIBMEMUNREACHABLE_THREAD_CAPTURE_H_
+
+#include <utility>
+
+#include "Allocator.h"
+
+struct ThreadInfo {
+ pid_t tid;
+ allocator::vector<uintptr_t> regs;
+ std::pair<uintptr_t, uintptr_t> stack;
+};
+
+using TidList = allocator::vector<pid_t>;
+using ThreadInfoList = allocator::vector<ThreadInfo>;
+
+class ThreadCaptureImpl;
+
+class ThreadCapture {
+public:
+ ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator);
+ ~ThreadCapture();
+
+ bool ListThreads(TidList& tids);
+ bool CaptureThreads();
+ bool ReleaseThreads();
+ bool ReleaseThread(pid_t tid);
+ bool CapturedThreadInfo(ThreadInfoList& threads);
+ void InjectTestFunc(std::function<void(pid_t)>&& f);
+
+private:
+ ThreadCapture(const ThreadCapture&) = delete;
+ void operator=(const ThreadCapture&) = delete;
+
+ Allocator<ThreadCaptureImpl>::unique_ptr impl_;
+};
+
+#endif
diff --git a/libmemunreachable/anon_vma_naming.h b/libmemunreachable/anon_vma_naming.h
new file mode 100644
index 0000000..1e4ade1
--- /dev/null
+++ b/libmemunreachable/anon_vma_naming.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+#define LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+
+#include <sys/prctl.h>
+
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+
+#endif // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
new file mode 100644
index 0000000..92de24a
--- /dev/null
+++ b/libmemunreachable/bionic.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_BIONIC_H_
+#define LIBMEMUNREACHABLE_BIONIC_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Exported from bionic */
+extern void malloc_disable();
+extern void malloc_enable();
+extern int malloc_iterate(uintptr_t base, size_t size,
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+extern ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
+
+__END_DECLS
+
+#endif // LIBMEMUNREACHABLE_BIONIC_H_
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
new file mode 100644
index 0000000..f4f01ce
--- /dev/null
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+#define LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
+
+#include <sys/cdefs.h>
+
+#ifdef __cplusplus
+
+#include <vector>
+#include <string>
+
+struct Leak {
+ uintptr_t begin;
+ size_t size;
+ size_t num_backtrace_frames;
+ static const size_t contents_length = 32;
+ char contents[contents_length];
+ static const size_t backtrace_length = 16;
+ uintptr_t backtrace_frames[backtrace_length];
+
+ std::string ToString(bool log_contents) const;
+};
+
+struct UnreachableMemoryInfo {
+ std::vector<Leak> leaks;
+ size_t num_leaks;
+ size_t leak_bytes;
+ size_t num_allocations;
+ size_t allocation_bytes;
+
+ UnreachableMemoryInfo() {}
+ ~UnreachableMemoryInfo() {
+ // Clear the memory that holds the leaks, otherwise the next attempt to
+ // detect leaks may find the old data (for example in the jemalloc tcache)
+ // and consider all the leaks to be referenced.
+ memset(leaks.data(), 0, leaks.capacity() * sizeof(Leak));
+ }
+
+ std::string ToString(bool log_contents) const;
+};
+
+bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100);
+
+std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100);
+
+#endif
+
+__BEGIN_DECLS
+
+bool LogUnreachableMemory(bool log_contents, size_t limit);
+
+bool NoLeaks();
+
+__END_DECLS
+
+#endif // LIBMEMUNREACHABLE_MEMUNREACHABLE_H_
diff --git a/libmemunreachable/log.h b/libmemunreachable/log.h
new file mode 100644
index 0000000..cdfbfd9
--- /dev/null
+++ b/libmemunreachable/log.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_LOG_H_
+#define LIBMEMUNREACHABLE_LOG_H_
+
+#define LOG_TAG "libmemunreachable"
+
+#include <log/log.h>
+
+#endif // LIBMEMUNREACHABLE_LOG_H_
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
new file mode 100644
index 0000000..d8e473e
--- /dev/null
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <Allocator.h>
+#include <sys/time.h>
+
+#include <chrono>
+#include <functional>
+#include <list>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+
+
+std::function<void()> ScopedAlarm::func_;
+
+using namespace std::chrono_literals;
+
+class AllocatorTest : public testing::Test {
+ protected:
+ AllocatorTest() : heap(), disable_malloc_() {}
+ virtual void SetUp() {
+ heap_count = 0;
+ }
+ virtual void TearDown() {
+ ASSERT_EQ(heap_count, 0);
+ ASSERT_TRUE(heap.empty());
+ ASSERT_FALSE(disable_malloc_.timed_out());
+ }
+ Heap heap;
+ private:
+ ScopedDisableMallocTimeout disable_malloc_;
+};
+
+TEST_F(AllocatorTest, simple) {
+ Allocator<char[100]> allocator(heap);
+ void *ptr = allocator.allocate();
+ ASSERT_TRUE(ptr != NULL);
+ allocator.deallocate(ptr);
+}
+
+TEST_F(AllocatorTest, multiple) {
+ Allocator<char[100]> allocator(heap);
+ void *ptr1 = allocator.allocate();
+ ASSERT_TRUE(ptr1 != NULL);
+ void *ptr2 = allocator.allocate();
+ ASSERT_TRUE(ptr2 != NULL);
+ ASSERT_NE(ptr1, ptr2);
+ allocator.deallocate(ptr1);
+ void *ptr3 = allocator.allocate();
+ ASSERT_EQ(ptr1, ptr3);
+ allocator.deallocate(ptr3);
+ allocator.deallocate(ptr2);
+}
+
+TEST_F(AllocatorTest, many) {
+ const int num = 4096;
+ const int size = 128;
+ Allocator<char[size]> allocator(heap);
+ void *ptr[num];
+ for (int i = 0; i < num; i++) {
+ ptr[i] = allocator.allocate();
+ memset(ptr[i], 0xaa, size);
+ *(reinterpret_cast<unsigned char*>(ptr[i])) = i;
+ }
+
+ for (int i = 0; i < num; i++) {
+ for (int j = 0; j < num; j++) {
+ if (i != j) {
+ ASSERT_NE(ptr[i], ptr[j]);
+ }
+ }
+ }
+
+ for (int i = 0; i < num; i++) {
+ ASSERT_EQ(*(reinterpret_cast<unsigned char*>(ptr[i])), i & 0xFF);
+ allocator.deallocate(ptr[i]);
+ }
+}
+
+TEST_F(AllocatorTest, large) {
+ const size_t size = 1024 * 1024;
+ Allocator<char[size]> allocator(heap);
+ void *ptr = allocator.allocate();
+ memset(ptr, 0xaa, size);
+ allocator.deallocate(ptr);
+}
+
+TEST_F(AllocatorTest, many_large) {
+ const int num = 128;
+ const int size = 1024 * 1024;
+ Allocator<char[size]> allocator(heap);
+ void *ptr[num];
+ for (int i = 0; i < num; i++) {
+ ptr[i] = allocator.allocate();
+ memset(ptr[i], 0xaa, size);
+ *(reinterpret_cast<unsigned char*>(ptr[i])) = i;
+ }
+
+ for (int i = 0; i < num; i++) {
+ ASSERT_EQ(*(reinterpret_cast<unsigned char*>(ptr[i])), i & 0xFF);
+ allocator.deallocate(ptr[i]);
+ }
+}
+
+TEST_F(AllocatorTest, copy) {
+ Allocator<char[100]> a(heap);
+ Allocator<char[200]> b = a;
+ Allocator<char[300]> c(b);
+ Allocator<char[100]> d(a);
+ Allocator<char[100]> e(heap);
+
+ ASSERT_EQ(a, b);
+ ASSERT_EQ(a, c);
+ ASSERT_EQ(a, d);
+ ASSERT_EQ(a, e);
+
+ void* ptr1 = a.allocate();
+ void* ptr2 = b.allocate();
+ void* ptr3 = c.allocate();
+ void* ptr4 = d.allocate();
+
+ b.deallocate(ptr1);
+ d.deallocate(ptr2);
+ a.deallocate(ptr3);
+ c.deallocate(ptr4);
+}
+
+TEST_F(AllocatorTest, stl_vector) {
+ auto v = allocator::vector<int>(Allocator<int>(heap));
+ for (int i = 0; i < 1024; i++) {
+ v.push_back(i);
+ }
+ for (int i = 0; i < 1024; i++) {
+ ASSERT_EQ(v[i], i);
+ }
+ v.clear();
+}
+
+TEST_F(AllocatorTest, stl_list) {
+ auto v = allocator::list<int>(Allocator<int>(heap));
+ for (int i = 0; i < 1024; i++) {
+ v.push_back(i);
+ }
+ int i = 0;
+ for (auto iter = v.begin(); iter != v.end(); iter++, i++) {
+ ASSERT_EQ(*iter, i);
+ }
+ v.clear();
+}
+
+TEST_F(AllocatorTest, shared) {
+ Allocator<int> allocator(heap);
+
+ Allocator<int>::shared_ptr ptr = allocator.make_shared(0);
+ {
+ auto ptr2 = ptr;
+ }
+ ASSERT_NE(ptr, nullptr);
+}
+
+TEST_F(AllocatorTest, unique) {
+ Allocator<int> allocator(heap);
+
+ Allocator<int>::unique_ptr ptr = allocator.make_unique(0);
+
+ ASSERT_NE(ptr, nullptr);
+}
+
+class DisableMallocTest : public ::testing::Test {
+ protected:
+ void alarm(std::chrono::microseconds us) {
+ std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
+ itimerval t = itimerval();
+ t.it_value.tv_sec = s.count();
+ t.it_value.tv_usec = (us - s).count();
+ setitimer(ITIMER_REAL, &t, NULL);
+ }
+};
+
+TEST_F(DisableMallocTest, reenable) {
+ ASSERT_EXIT({
+ alarm(100ms);
+ void *ptr1 = malloc(128);
+ ASSERT_NE(ptr1, nullptr);
+ free(ptr1);
+ {
+ ScopedDisableMalloc disable_malloc;
+ }
+ void *ptr2 = malloc(128);
+ ASSERT_NE(ptr2, nullptr);
+ free(ptr2);
+ _exit(1);
+ }, ::testing::ExitedWithCode(1), "");
+}
+
+TEST_F(DisableMallocTest, deadlock_allocate) {
+ ASSERT_DEATH({
+ void *ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ void* ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_new) {
+ ASSERT_DEATH({
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ delete(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ delete(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_delete) {
+ ASSERT_DEATH({
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(250ms);
+ ScopedDisableMalloc disable_malloc;
+ delete(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_free) {
+ ASSERT_DEATH({
+ void *ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ free(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_fork) {
+ ASSERT_DEATH({
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ fork();
+ }
+ }, "");
+}
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
new file mode 100644
index 0000000..9921eb6
--- /dev/null
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HeapWalker.h"
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+#include "Allocator.h"
+
+class HeapWalkerTest : public ::testing::Test {
+ public:
+ HeapWalkerTest() : disable_malloc_(), heap_() {}
+
+ void TearDown() {
+ ASSERT_TRUE(heap_.empty());
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc_.timed_out());
+ }
+ }
+
+ protected:
+ ScopedDisableMallocTimeout disable_malloc_;
+ Heap heap_;
+};
+
+TEST_F(HeapWalkerTest, allocation) {
+ HeapWalker heap_walker(heap_);
+ ASSERT_TRUE(heap_walker.Allocation(3, 4));
+ ASSERT_TRUE(heap_walker.Allocation(2, 3));
+ ASSERT_TRUE(heap_walker.Allocation(4, 5));
+ ASSERT_TRUE(heap_walker.Allocation(6, 7));
+ ASSERT_TRUE(heap_walker.Allocation(0, 1));
+}
+
+TEST_F(HeapWalkerTest, overlap) {
+ HeapWalker heap_walker(heap_);
+ ASSERT_TRUE(heap_walker.Allocation(2, 3));
+ ASSERT_TRUE(heap_walker.Allocation(3, 4));
+ ASSERT_FALSE(heap_walker.Allocation(2, 3));
+ ASSERT_FALSE(heap_walker.Allocation(1, 3));
+ ASSERT_FALSE(heap_walker.Allocation(1, 4));
+ ASSERT_FALSE(heap_walker.Allocation(1, 5));
+ ASSERT_FALSE(heap_walker.Allocation(3, 4));
+ ASSERT_FALSE(heap_walker.Allocation(3, 5));
+ ASSERT_TRUE(heap_walker.Allocation(4, 5));
+ ASSERT_TRUE(heap_walker.Allocation(1, 2));
+}
+
+TEST_F(HeapWalkerTest, zero) {
+ HeapWalker heap_walker(heap_);
+ ASSERT_TRUE(heap_walker.Allocation(2, 2));
+ ASSERT_FALSE(heap_walker.Allocation(2, 2));
+ ASSERT_TRUE(heap_walker.Allocation(3, 3));
+ ASSERT_TRUE(heap_walker.Allocation(1, 1));
+ ASSERT_FALSE(heap_walker.Allocation(2, 3));
+}
+
+#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(buffer)
+#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(buffer) + sizeof(buffer))
+
+TEST_F(HeapWalkerTest, leak) {
+ void* buffer1[16]{};
+ char buffer2[16]{};
+ buffer1[0] = &buffer2[0] - sizeof(void*);
+ buffer1[1] = &buffer2[15] + sizeof(void*);
+
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(1U, num_leaks);
+ EXPECT_EQ(16U, leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(buffer_begin(buffer2), leaked[0].begin);
+ EXPECT_EQ(buffer_end(buffer2), leaked[0].end);
+}
+
+TEST_F(HeapWalkerTest, live) {
+ const int from_buffer_entries = 4;
+ const int to_buffer_bytes = 16;
+
+ for (int i = 0; i < from_buffer_entries; i++) {
+ for (int j = 0; j < to_buffer_bytes; j++) {
+ void* buffer1[from_buffer_entries]{};
+ char buffer2[to_buffer_bytes]{};
+ buffer1[i] = &buffer2[j];
+
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+ heap_walker.Root(buffer_begin(buffer1), buffer_end(buffer1));
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = SIZE_T_MAX;
+ size_t leaked_bytes = SIZE_T_MAX;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(0U, num_leaks);
+ EXPECT_EQ(0U, leaked_bytes);
+ EXPECT_EQ(0U, leaked.size());
+ }
+ }
+}
+
+TEST_F(HeapWalkerTest, unaligned) {
+ const int from_buffer_entries = 4;
+ const int to_buffer_bytes = 16;
+ void* buffer1[from_buffer_entries]{};
+ char buffer2[to_buffer_bytes]{};
+
+ buffer1[1] = &buffer2;
+
+ for (unsigned int i = 0; i < sizeof(uintptr_t); i++) {
+ for (unsigned int j = 0; j < sizeof(uintptr_t); j++) {
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+ heap_walker.Root(buffer_begin(buffer1) + i, buffer_end(buffer1) - j);
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = SIZE_T_MAX;
+ size_t leaked_bytes = SIZE_T_MAX;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(0U, num_leaks);
+ EXPECT_EQ(0U, leaked_bytes);
+ EXPECT_EQ(0U, leaked.size());
+ }
+ }
+}
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
new file mode 100644
index 0000000..0747b12
--- /dev/null
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+
+#include <gtest/gtest.h>
+
+#include <memunreachable/memunreachable.h>
+
+void* ptr;
+
+class HiddenPointer {
+ public:
+ HiddenPointer(size_t size = 256) {
+ Set(malloc(size));
+ }
+ ~HiddenPointer() {
+ Free();
+ }
+ void* Get() {
+ return reinterpret_cast<void*>(~ptr_);
+ }
+ void Free() {
+ free(Get());
+ Set(nullptr);
+ }
+ private:
+ void Set(void* ptr) {
+ ptr_ = ~reinterpret_cast<uintptr_t>(ptr);
+ }
+ volatile uintptr_t ptr_;
+};
+
+static void Ref(void* ptr) {
+ write(0, ptr, 0);
+}
+
+TEST(MemunreachableTest, clean) {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+}
+
+TEST(MemunreachableTest, stack) {
+ HiddenPointer hidden_ptr;
+
+ {
+ void* ptr = hidden_ptr.Get();
+ Ref(ptr);
+
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+
+ Ref(ptr);
+ }
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, global) {
+ HiddenPointer hidden_ptr;
+
+ ptr = hidden_ptr.Get();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+
+ ptr = NULL;
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, tls) {
+ HiddenPointer hidden_ptr;
+ pthread_key_t key;
+ pthread_key_create(&key, NULL);
+
+ pthread_setspecific(key, hidden_ptr.Get());
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+
+ pthread_setspecific(key, nullptr);
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+
+ pthread_key_delete(key);
+}
+
+TEST(MemunreachableTest, twice) {
+ HiddenPointer hidden_ptr;
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(1U, info.leaks.size());
+ }
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, log) {
+ HiddenPointer hidden_ptr;
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+ hidden_ptr.Free();
+
+ {
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+ }
+}
+
+TEST(MemunreachableTest, notdumpable) {
+ ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 0));
+
+ HiddenPointer hidden_ptr;
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+
+ ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1));
+}
+
+TEST(MemunreachableTest, leak_lots) {
+ std::vector<HiddenPointer> hidden_ptrs;
+ hidden_ptrs.resize(1024);
+
+ ASSERT_TRUE(LogUnreachableMemory(true, 100));
+}
diff --git a/libmemunreachable/tests/ThreadCapture_test.cpp b/libmemunreachable/tests/ThreadCapture_test.cpp
new file mode 100644
index 0000000..cefe94e
--- /dev/null
+++ b/libmemunreachable/tests/ThreadCapture_test.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ThreadCapture.h"
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include <android-base/unique_fd.h>
+
+#include "Allocator.h"
+#include "ScopedDisableMalloc.h"
+#include "ScopedPipe.h"
+
+using namespace std::chrono_literals;
+
+class ThreadListTest : public ::testing::TestWithParam<int> {
+ public:
+ ThreadListTest() : stop_(false) {}
+
+ ~ThreadListTest() {
+ // pthread_join may return before the entry in /proc/pid/task/ is gone,
+ // loop until ListThreads only finds the main thread so the next test
+ // doesn't fail.
+ WaitForThreads();
+ }
+
+ virtual void TearDown() {
+ ASSERT_TRUE(heap.empty());
+ }
+
+ protected:
+ template<class Function>
+ void StartThreads(unsigned int threads, Function&& func) {
+ threads_.reserve(threads);
+ tids_.reserve(threads);
+ for (unsigned int i = 0; i < threads; i++) {
+ threads_.emplace_back([&, i, threads, this]() {
+ {
+ std::lock_guard<std::mutex> lk(m_);
+ tids_.push_back(gettid());
+ if (tids_.size() == threads) {
+ cv_start_.notify_one();
+ }
+ }
+
+ func();
+
+ {
+ std::unique_lock<std::mutex> lk(m_);
+ cv_stop_.wait(lk, [&] {return stop_;});
+ }
+ });
+ }
+
+ {
+ std::unique_lock<std::mutex> lk(m_);
+ cv_start_.wait(lk, [&]{ return tids_.size() == threads; });
+ }
+ }
+
+ void StopThreads() {
+ {
+ std::lock_guard<std::mutex> lk(m_);
+ stop_ = true;
+ }
+ cv_stop_.notify_all();
+
+ for (auto i = threads_.begin(); i != threads_.end(); i++) {
+ i->join();
+ }
+ threads_.clear();
+ tids_.clear();
+ }
+
+ std::vector<pid_t>& tids() {
+ return tids_;
+ }
+
+ Heap heap;
+
+ private:
+ void WaitForThreads() {
+ auto tids = TidList{heap};
+ ThreadCapture thread_capture{getpid(), heap};
+
+ for (unsigned int i = 0; i < 100; i++) {
+ EXPECT_TRUE(thread_capture.ListThreads(tids));
+ if (tids.size() == 1) {
+ break;
+ }
+ std::this_thread::sleep_for(10ms);
+ }
+ EXPECT_EQ(1U, tids.size());
+ }
+
+ std::mutex m_;
+ std::condition_variable cv_start_;
+ std::condition_variable cv_stop_;
+ bool stop_;
+ std::vector<pid_t> tids_;
+
+ std::vector<std::thread> threads_;
+};
+
+TEST_F(ThreadListTest, list_one) {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(getpid(), heap);
+
+ auto expected_tids = allocator::vector<pid_t>(1, getpid(), heap);
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+
+ ASSERT_EQ(expected_tids, list_tids);
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+}
+
+TEST_P(ThreadListTest, list_some) {
+ const unsigned int threads = GetParam() - 1;
+
+ StartThreads(threads, [](){});
+ std::vector<pid_t> expected_tids = tids();
+ expected_tids.push_back(getpid());
+
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(getpid(), heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+
+ StopThreads();
+
+ std::sort(list_tids.begin(), list_tids.end());
+ std::sort(expected_tids.begin(), expected_tids.end());
+
+ ASSERT_EQ(expected_tids.size(), list_tids.size());
+ EXPECT_TRUE(std::equal(expected_tids.begin(), expected_tids.end(), list_tids.begin()));
+}
+
+INSTANTIATE_TEST_CASE_P(ThreadListTest, ThreadListTest, ::testing::Values(1, 2, 10, 1024));
+
+class ThreadCaptureTest : public ThreadListTest {
+ public:
+ ThreadCaptureTest() {}
+ ~ThreadCaptureTest() {}
+ void Fork(std::function<void()>&& child_init,
+ std::function<void()>&& child_cleanup,
+ std::function<void(pid_t)>&& parent) {
+
+ ScopedPipe start_pipe;
+ ScopedPipe stop_pipe;
+
+ int pid = fork();
+
+ if (pid == 0) {
+ // child
+ child_init();
+ EXPECT_EQ(1, TEMP_FAILURE_RETRY(write(start_pipe.Sender(), "+", 1))) << strerror(errno);
+ char buf;
+ EXPECT_EQ(1, TEMP_FAILURE_RETRY(read(stop_pipe.Receiver(), &buf, 1))) << strerror(errno);
+ child_cleanup();
+ _exit(0);
+ } else {
+ // parent
+ ASSERT_GT(pid, 0);
+ char buf;
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(start_pipe.Receiver(), &buf, 1))) << strerror(errno);
+
+ parent(pid);
+
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(stop_pipe.Sender(), "+", 1))) << strerror(errno);
+ siginfo_t info{};
+ ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) << strerror(errno);
+ }
+ }
+};
+
+TEST_P(ThreadCaptureTest, capture_some) {
+ const unsigned int threads = GetParam();
+
+ Fork([&](){
+ // child init
+ StartThreads(threads - 1, [](){});
+ },
+ [&](){
+ // child cleanup
+ StopThreads();
+ },
+ [&](pid_t child){
+ // parent
+ ASSERT_GT(child, 0);
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(child, heap);
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(threads, list_tids.size());
+
+ ASSERT_TRUE(thread_capture.CaptureThreads());
+
+ auto thread_info = allocator::vector<ThreadInfo>(heap);
+ ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+ ASSERT_EQ(threads, thread_info.size());
+ ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+}
+ });
+}
+
+INSTANTIATE_TEST_CASE_P(ThreadCaptureTest, ThreadCaptureTest, ::testing::Values(1, 2, 10, 1024));
+
+TEST_F(ThreadCaptureTest, capture_kill) {
+ int ret = fork();
+
+ if (ret == 0) {
+ // child
+ sleep(10);
+ } else {
+ // parent
+ ASSERT_GT(ret, 0);
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(ret, heap);
+ thread_capture.InjectTestFunc([&](pid_t tid){
+ syscall(SYS_tgkill, ret, tid, SIGKILL);
+ usleep(10000);
+ });
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(1U, list_tids.size());
+
+ ASSERT_FALSE(thread_capture.CaptureThreads());
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+ }
+}
+
+TEST_F(ThreadCaptureTest, capture_signal) {
+ const int sig = SIGUSR1;
+
+ ScopedPipe pipe;
+
+ // For signal handler
+ static ScopedPipe* g_pipe;
+
+ Fork([&](){
+ // child init
+ pipe.CloseReceiver();
+
+ g_pipe = &pipe;
+
+ struct sigaction act{};
+ act.sa_handler = [](int){
+ char buf = '+';
+ write(g_pipe->Sender(), &buf, 1);
+ g_pipe->CloseSender();
+ };
+ sigaction(sig, &act, NULL);
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+ },
+ [&](){
+ // child cleanup
+ g_pipe = nullptr;
+ pipe.Close();
+ },
+ [&](pid_t child){
+ // parent
+ ASSERT_GT(child, 0);
+ pipe.CloseSender();
+
+ {
+ ScopedDisableMallocTimeout disable_malloc;
+
+ ThreadCapture thread_capture(child, heap);
+ thread_capture.InjectTestFunc([&](pid_t tid){
+ syscall(SYS_tgkill, child, tid, sig);
+ usleep(10000);
+ });
+ auto list_tids = allocator::vector<pid_t>(heap);
+
+ ASSERT_TRUE(thread_capture.ListThreads(list_tids));
+ ASSERT_EQ(1U, list_tids.size());
+
+ ASSERT_TRUE(thread_capture.CaptureThreads());
+
+ auto thread_info = allocator::vector<ThreadInfo>(heap);
+ ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
+ ASSERT_EQ(1U, thread_info.size());
+ ASSERT_TRUE(thread_capture.ReleaseThreads());
+
+ usleep(100000);
+ char buf;
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
+ ASSERT_EQ(buf, '+');
+
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc.timed_out());
+ }
+ }
+ });
+}
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
index 2060df4..281b6c8 100644
--- a/libnetutils/Android.mk
+++ b/libnetutils/Android.mk
@@ -16,6 +16,9 @@
LOCAL_CFLAGS := -Werror
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
diff --git a/include/netutils/dhcp.h b/libnetutils/include/netutils/dhcp.h
similarity index 100%
rename from include/netutils/dhcp.h
rename to libnetutils/include/netutils/dhcp.h
diff --git a/include/netutils/ifc.h b/libnetutils/include/netutils/ifc.h
similarity index 100%
rename from include/netutils/ifc.h
rename to libnetutils/include/netutils/ifc.h
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
index 16052e2..e309027 100644
--- a/libpackagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -27,7 +27,7 @@
#include <sys/limits.h>
#define LOG_TAG "packagelistparser"
-#include <utils/Log.h>
+#include <cutils/log.h>
#include <packagelistparser/packagelistparser.h>
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 5ab957d..ba97f32 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -29,6 +29,7 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <memory>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -194,11 +195,11 @@
static void removeUidProcessGroups(const char *uid_path)
{
- DIR *uid = opendir(uid_path);
+ std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path), closedir);
if (uid != NULL) {
struct dirent cur;
struct dirent *dir;
- while ((readdir_r(uid, &cur, &dir) == 0) && dir) {
+ while ((readdir_r(uid.get(), &cur, &dir) == 0) && dir) {
char path[PROCESSGROUP_MAX_PATH_LEN];
if (dir->d_type != DT_DIR) {
@@ -213,7 +214,6 @@
SLOGV("removing %s\n", path);
rmdir(path);
}
- closedir(uid);
}
}
@@ -221,13 +221,13 @@
{
SLOGV("removeAllProcessGroups()");
const char *cgroup_root_path = getCgroupRootPath();
- DIR *root = opendir(cgroup_root_path);
+ std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
if (root == NULL) {
SLOGE("failed to open %s: %s", cgroup_root_path, strerror(errno));
} else {
struct dirent cur;
struct dirent *dir;
- while ((readdir_r(root, &cur, &dir) == 0) && dir) {
+ while ((readdir_r(root.get(), &cur, &dir) == 0) && dir) {
char path[PROCESSGROUP_MAX_PATH_LEN];
if (dir->d_type != DT_DIR) {
@@ -242,7 +242,6 @@
SLOGV("removing %s\n", path);
rmdir(path);
}
- closedir(root);
}
}
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 8c4fd15..9e0d0ac 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -22,7 +22,6 @@
Log.cpp \
NativeHandle.cpp \
Printer.cpp \
- ProcessCallStack.cpp \
PropertyMap.cpp \
RefBase.cpp \
SharedBuffer.cpp \
@@ -44,7 +43,7 @@
# =====================================================
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= $(commonSources)
-LOCAL_SRC_FILES_linux := Looper.cpp
+LOCAL_SRC_FILES_linux := Looper.cpp ProcessCallStack.cpp
LOCAL_CFLAGS_darwin := -Wno-unused-parameter
LOCAL_MODULE:= libutils
LOCAL_STATIC_LIBRARIES := liblog
@@ -67,6 +66,7 @@
$(commonSources) \
BlobCache.cpp \
Looper.cpp \
+ ProcessCallStack.cpp \
Trace.cpp
ifeq ($(TARGET_ARCH),mips)
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index 011c302..4e87a98 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -21,6 +21,7 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
+#include <memory>
#include <utils/Log.h>
#include <utils/Errors.h>
@@ -130,11 +131,10 @@
}
void ProcessCallStack::update() {
- DIR *dp;
struct dirent *ep;
struct dirent entry;
- dp = opendir(PATH_SELF_TASK);
+ std::unique_ptr<DIR, decltype(&closedir)> dp(opendir(PATH_SELF_TASK), closedir);
if (dp == NULL) {
ALOGE("%s: Failed to update the process's call stacks: %s",
__FUNCTION__, strerror(errno));
@@ -146,7 +146,6 @@
clear();
// Get current time.
-#ifndef USE_MINGW
{
time_t t = time(NULL);
struct tm tm;
@@ -160,7 +159,7 @@
* - Read every file in directory => get every tid
*/
int code;
- while ((code = readdir_r(dp, &entry, &ep)) == 0 && ep != NULL) {
+ while ((code = readdir_r(dp.get(), &entry, &ep)) == 0 && ep != NULL) {
pid_t tid = -1;
sscanf(ep->d_name, "%d", &tid);
@@ -199,9 +198,6 @@
ALOGE("%s: Failed to readdir from %s: %s",
__FUNCTION__, PATH_SELF_TASK, strerror(code));
}
-#endif
-
- closedir(dp);
}
void ProcessCallStack::log(const char* logtag, android_LogPriority priority,
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 6a5273f..87eda1b 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -18,7 +18,6 @@
#include <utils/Log.h>
#include <utils/Unicode.h>
-#include <utils/String8.h>
#include <utils/threads.h>
#include <memory.h>
@@ -77,7 +76,7 @@
//printf("Created UTF-16 string from UTF-8 \"%s\":", in);
//printHexData(1, str, buf->size(), 16, 1);
//printf("\n");
-
+
return u16str;
}
@@ -127,7 +126,7 @@
mString = str;
return;
}
-
+
mString = getEmptyString();
}
@@ -142,7 +141,7 @@
mString = str;
return;
}
-
+
mString = getEmptyString();
}
@@ -228,7 +227,7 @@
} else if (otherLen == 0) {
return NO_ERROR;
}
-
+
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize((myLen+otherLen+1)*sizeof(char16_t));
if (buf) {
@@ -249,7 +248,7 @@
} else if (otherLen == 0) {
return NO_ERROR;
}
-
+
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize((myLen+otherLen+1)*sizeof(char16_t));
if (buf) {
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 771d312..ad45282 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -362,7 +362,7 @@
status_t String8::real_append(const char* other, size_t otherLen)
{
const size_t myLen = bytes();
-
+
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize(myLen+otherLen+1);
if (buf) {
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 056b3e1..3cd8b87 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -95,13 +95,12 @@
LOCAL_CFLAGS := $(libziparchive_common_c_flags)
LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(libziparchive_common_cpp_flags)
LOCAL_SRC_FILES := $(libziparchive_test_files)
-LOCAL_SHARED_LIBRARIES := \
- libziparchive-host \
- liblog \
- libbase \
-
LOCAL_STATIC_LIBRARIES := \
- libutils \
+ libziparchive-host \
libz \
+ libbase \
+ libutils \
+ liblog \
+LOCAL_MODULE_HOST_OS := darwin linux windows
include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 3b1e972..a2d6fcc 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -224,9 +224,7 @@
strerror(errno));
return kIoError;
}
- ssize_t actual = TEMP_FAILURE_RETRY(
- read(fd, scan_buffer, static_cast<size_t>(read_amount)));
- if (actual != static_cast<ssize_t>(read_amount)) {
+ if (!android::base::ReadFully(fd, scan_buffer, static_cast<size_t>(read_amount))) {
ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
strerror(errno));
return kIoError;
@@ -481,8 +479,7 @@
static int32_t UpdateEntryFromDataDescriptor(int fd,
ZipEntry *entry) {
uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
- ssize_t actual = TEMP_FAILURE_RETRY(read(fd, ddBuf, sizeof(ddBuf)));
- if (actual != sizeof(ddBuf)) {
+ if (!android::base::ReadFully(fd, ddBuf, sizeof(ddBuf))) {
return kIoError;
}
@@ -498,26 +495,14 @@
}
// Attempts to read |len| bytes into |buf| at offset |off|.
-//
-// This method uses pread64 on platforms that support it and
-// lseek64 + read on platforms that don't. This implies that
-// callers should not rely on the |fd| offset being incremented
+// Callers should not rely on the |fd| offset being incremented
// as a side effect of this call.
-static inline ssize_t ReadAtOffset(int fd, uint8_t* buf, size_t len,
- off64_t off) {
-#if !defined(_WIN32)
- return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
-#else
- // The only supported platform that doesn't support pread at the moment
- // is Windows. Only recent versions of windows support unix like forks,
- // and even there the semantics are quite different.
+static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off) {
if (lseek64(fd, off, SEEK_SET) != off) {
ALOGW("Zip: failed seek to offset %" PRId64, off);
- return kIoError;
+ return false;
}
-
- return TEMP_FAILURE_RETRY(read(fd, buf, len));
-#endif
+ return android::base::ReadFully(fd, buf, len);
}
static int32_t FindEntry(const ZipArchive* archive, const int ent,
@@ -567,9 +552,7 @@
}
uint8_t lfh_buf[sizeof(LocalFileHeader)];
- ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf),
- local_header_offset);
- if (actual != sizeof(lfh_buf)) {
+ if (!ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset)) {
ALOGW("Zip: failed reading lfh name from offset %" PRId64,
static_cast<int64_t>(local_header_offset));
return kIoError;
@@ -610,10 +593,7 @@
}
uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
- ssize_t actual = ReadAtOffset(archive->fd, name_buf, nameLen,
- name_offset);
-
- if (actual != nameLen) {
+ if (!ReadAtOffset(archive->fd, name_buf, nameLen, name_offset)) {
ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
free(name_buf);
return kIoError;
@@ -942,10 +922,9 @@
do {
/* read as much as we can */
if (zstream.avail_in == 0) {
- const ZD_TYPE getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
- const ZD_TYPE actual = TEMP_FAILURE_RETRY(read(fd, &read_buf[0], getSize));
- if (actual != getSize) {
- ALOGW("Zip: inflate read failed (" ZD " vs " ZD ")", actual, getSize);
+ const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
+ if (!android::base::ReadFully(fd, read_buf.data(), getSize)) {
+ ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
return kIoError;
}
@@ -1005,11 +984,9 @@
// Safe conversion because kBufSize is narrow enough for a 32 bit signed
// value.
- const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
- const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));
-
- if (actual != block_size) {
- ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
+ const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+ if (!android::base::ReadFully(fd, buf.data(), block_size)) {
+ ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
return kIoError;
}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index d426dc4..6aee1bb 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -21,9 +21,11 @@
#include <string.h>
#include <unistd.h>
+#include <memory>
#include <vector>
#include <android-base/file.h>
+#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_archive_stream_entry.h>
@@ -91,7 +93,7 @@
}
TEST(ziparchive, OpenAssumeFdOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
ASSERT_NE(-1, fd);
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
@@ -101,7 +103,7 @@
}
TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
- int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
+ int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
ASSERT_NE(-1, fd);
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
@@ -373,30 +375,13 @@
static const std::string kAbTxtName("ab.txt");
static const size_t kAbUncompressedSize = 270216;
-static int make_temporary_file(const char* file_name_pattern) {
- char full_path[1024];
- // Account for differences between the host and the target.
- //
- // TODO: Maybe reuse bionic/tests/TemporaryFile.h.
- snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern);
- int fd = mkstemp(full_path);
- if (fd == -1) {
- snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern);
- fd = mkstemp(full_path);
- }
-
- return fd;
-}
-
TEST(ziparchive, EmptyEntries) {
- char temp_file_pattern[] = "empty_entries_test_XXXXXX";
- int fd = make_temporary_file(temp_file_pattern);
- ASSERT_NE(-1, fd);
- const ssize_t file_size = sizeof(kEmptyEntriesZip);
- ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
ZipEntry entry;
ZipString empty_name;
@@ -406,27 +391,23 @@
uint8_t buffer[1];
ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
- char output_file_pattern[] = "empty_entries_output_XXXXXX";
- int output_fd = make_temporary_file(output_file_pattern);
- ASSERT_NE(-1, output_fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+
+ TemporaryFile tmp_output_file;
+ ASSERT_NE(-1, tmp_output_file.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
struct stat stat_buf;
- ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+ ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
ASSERT_EQ(0, stat_buf.st_size);
-
- close(fd);
- close(output_fd);
}
TEST(ziparchive, EntryLargerThan32K) {
- char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX";
- int fd = make_temporary_file(temp_file_pattern);
- ASSERT_NE(-1, fd);
- ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip),
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
sizeof(kAbZip) - 1));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
ZipEntry entry;
ZipString ab_name;
@@ -439,21 +420,21 @@
ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
// Extract the entry to a file.
- char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX";
- int output_fd = make_temporary_file(output_file_pattern);
- ASSERT_NE(-1, output_fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+ TemporaryFile tmp_output_file;
+ ASSERT_NE(-1, tmp_output_file.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
// Make sure the extracted file size is as expected.
struct stat stat_buf;
- ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+ ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
// Read the file back to a buffer and make sure the contents are
// the same as the memory buffer we extracted directly to.
std::vector<uint8_t> file_contents(kAbUncompressedSize);
- ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET));
- ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size()));
+ ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0],
+ file_contents.size()));
ASSERT_EQ(file_contents, buffer);
for (int i = 0; i < 90072; ++i) {
@@ -462,35 +443,28 @@
ASSERT_EQ('b', line[1]);
ASSERT_EQ('\n', line[2]);
}
-
- close(fd);
- close(output_fd);
}
TEST(ziparchive, TrailerAfterEOCD) {
- char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
- int fd = make_temporary_file(temp_file_pattern);
- ASSERT_NE(-1, fd);
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
// Create a file with 8 bytes of random garbage.
static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
- const ssize_t file_size = sizeof(kEmptyEntriesZip);
- const ssize_t trailer_size = sizeof(trailer);
- ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
- ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size)));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
ZipArchiveHandle handle;
- ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
+ ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
}
TEST(ziparchive, ExtractToFile) {
- char kTempFilePattern[] = "zip_archive_input_XXXXXX";
- int fd = make_temporary_file(kTempFilePattern);
- ASSERT_NE(-1, fd);
+ TemporaryFile tmp_file;
+ ASSERT_NE(-1, tmp_file.fd);
const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
- const ssize_t data_size = sizeof(data);
+ const size_t data_size = sizeof(data);
- ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
+ ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -499,28 +473,25 @@
ZipString name;
SetZipString(&name, kATxtName);
ASSERT_EQ(0, FindEntry(handle, name, &entry));
- ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
// Assert that the first 8 bytes of the file haven't been clobbered.
uint8_t read_buffer[data_size];
- ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
- ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
+ ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
// Assert that the remainder of the file contains the incompressed data.
std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
- ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
- TEMP_FAILURE_RETRY(
- read(fd, &uncompressed_data[0], entry.uncompressed_length)));
+ ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
+ entry.uncompressed_length));
ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(),
kATxtContents.size()));
// Assert that the total length of the file is sane
- ASSERT_EQ(data_size + static_cast<ssize_t>(kATxtContents.size()),
- lseek64(fd, 0, SEEK_END));
-
- close(fd);
+ ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
+ lseek64(tmp_file.fd, 0, SEEK_END));
}
static void ZipArchiveStreamTest(
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index fe0846d..16a574d 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -154,12 +154,22 @@
tm->tm_mday = (zip_time >> 16) & 0x1f;
}
+static struct tm MakeTm() {
+ struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_year = 2001 - 1900;
+ tm.tm_mon = 1;
+ tm.tm_mday = 12;
+ tm.tm_hour = 18;
+ tm.tm_min = 30;
+ tm.tm_sec = 20;
+ return tm;
+}
+
TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
ZipWriter writer(file_);
- struct tm tm;
- memset(&tm, 0, sizeof(struct tm));
- ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
+ struct tm tm = MakeTm();
time_t time = mktime(&tm);
ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
ASSERT_EQ(0, writer.WriteBytes("he", 2));
@@ -210,9 +220,7 @@
TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
ZipWriter writer(file_);
- struct tm tm;
- memset(&tm, 0, sizeof(struct tm));
- ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
+ struct tm tm = MakeTm();
time_t time = mktime(&tm);
ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
ASSERT_EQ(0, writer.WriteBytes("he", 2));
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index ddc91ca..c148d89 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -35,7 +35,7 @@
#include <log/logd.h>
#include <log/logger.h>
#include <log/logprint.h>
-#include <utils/threads.h>
+#include <system/thread_defs.h>
#define DEFAULT_MAX_ROTATED_LOGS 4
diff --git a/logcat/logpersist b/logcat/logpersist
index dab466d..8762ff1 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -1,8 +1,8 @@
#! /system/bin/sh
# logpersist cat start and stop handlers
progname="${0##*/}"
-case `getprop ro.build.type` in
-userdebug|eng) ;;
+case `getprop ro.debuggable` in
+1) ;;
*) echo "${progname} - Permission denied"
exit 1
;;
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 4f517bb..8459bd3 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
+#include <memory>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -734,8 +735,8 @@
EXPECT_FALSE(system(command));
return;
}
- DIR *dir;
- EXPECT_TRUE(NULL != (dir = opendir(tmp_out_dir)));
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(tmp_out_dir), closedir);
+ EXPECT_NE(nullptr, dir);
if (!dir) {
snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(system(command));
@@ -743,7 +744,7 @@
}
struct dirent *entry;
unsigned count = 0;
- while ((entry = readdir(dir))) {
+ while ((entry = readdir(dir.get()))) {
if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
continue;
}
@@ -766,7 +767,6 @@
free(line);
unlink(command);
}
- closedir(dir);
if (count > 1) {
char *brk = strpbrk(second_last_line, "\r\n");
if (!brk) {
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fd4800e..aa4b6e1 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -17,6 +17,7 @@
#ifndef _LOGD_LOG_UTILS_H__
#define _LOGD_LOG_UTILS_H__
+#include <sys/cdefs.h>
#include <sys/types.h>
#include <log/log.h>
@@ -29,6 +30,7 @@
// Furnished in main.cpp. Caller must own and free returned value
char *uidToName(uid_t uid);
+void prdebug(const char *fmt, ...) __printflike(1, 2);
// Furnished in LogStatistics.cpp. Caller must own and free returned value
char *pidToName(pid_t pid);
diff --git a/logd/README.property b/logd/README.property
index 6c84b25..4bc5541 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -7,7 +7,7 @@
ro.device_owner bool false Override persist.logd.security to false
ro.logd.kernel bool+ svelte+ Enable klogd daemon
ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
-ro.build.type string if user, logd.statistics &
+ro.debuggable number if not "1", logd.statistics &
ro.logd.kernel default false.
persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
turns on logcat -f in logd context
@@ -45,10 +45,10 @@
NB:
- bool+ - "true", "false" and comma separated list of "eng" (forced false if
- ro.build.type is "user") or "svelte" (forced false if ro.config.low_ram is
+ ro.debuggable is not "1") or "svelte" (forced false if ro.config.low_ram is
true).
- svelte - see ro.config.low_ram for details.
-- svelte+ - see ro.config.low_ram and ro.build.type for details.
+- svelte+ - see ro.config.low_ram and ro.debuggable for details.
- ro - <base property> temporary override, ro.<base property> platform default.
- persist - <base property> override, persist.<base property> platform default.
- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
diff --git a/logd/main.cpp b/logd/main.cpp
index aa5718e..f4d7464 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -202,8 +202,8 @@
return false;
}
if (flag & BOOL_DEFAULT_FLAG_ENG) {
- property_get("ro.build.type", property, "");
- if (!strcmp(property, "user")) {
+ property_get("ro.debuggable", property, "");
+ if (strcmp(property, "1")) {
return false;
}
}
@@ -211,10 +211,32 @@
return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
}
-// Remove the static, and use this variable
-// globally for debugging if necessary. eg:
-// write(fdDmesg, "I am here\n", 10);
static int fdDmesg = -1;
+void inline android::prdebug(const char *fmt, ...) {
+ if (fdDmesg < 0) {
+ return;
+ }
+
+ static const char message[] = {
+ KMSG_PRIORITY(LOG_DEBUG), 'l', 'o', 'g', 'd', ':', ' '
+ };
+ char buffer[256];
+ memcpy(buffer, message, sizeof(message));
+
+ va_list ap;
+ va_start(ap, fmt);
+ int n = vsnprintf(buffer + sizeof(message),
+ sizeof(buffer) - sizeof(message), fmt, ap);
+ va_end(ap);
+ if (n > 0) {
+ buffer[sizeof(buffer) - 1] = '\0';
+ if (!strchr(buffer, '\n')) {
+ buffer[sizeof(buffer) - 2] = '\0';
+ strlcat(buffer, "\n", sizeof(buffer));
+ }
+ write(fdDmesg, buffer, strlen(buffer));
+ }
+}
static sem_t uidName;
static uid_t uid;
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 28d6de7..ccbe0bf 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -408,7 +408,7 @@
if (poll_fds[0].revents & POLLHUP) {
int ret;
- ret = waitpid(pid, &status, WNOHANG);
+ ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (ret < 0) {
rc = errno;
ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno));
diff --git a/metricsd/.clang-format b/metricsd/.clang-format
deleted file mode 100644
index c98efc2..0000000
--- a/metricsd/.clang-format
+++ /dev/null
@@ -1,10 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: Inline
-AllowShortIfStatementsOnASingleLine: false
-AllowShortLoopsOnASingleLine: false
-BinPackArguments: false
-BinPackParameters: false
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-PointerAlignment: Left
-TabWidth: 2
diff --git a/metricsd/.clang-format b/metricsd/.clang-format
new file mode 120000
index 0000000..f9066d4
--- /dev/null
+++ b/metricsd/.clang-format
@@ -0,0 +1 @@
+../../../build/tools/brillo-clang-format
\ No newline at end of file
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 70112f4..0f77fe4 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -304,6 +304,8 @@
upload_service_->PersistToDisk();
EXPECT_EQ(
1, upload_service_->current_log_->uma_proto()->histogram_event().size());
+ // Destroy the old service before creating a new one.
+ upload_service_.reset();
upload_service_.reset(new UploadService(
"", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
upload_service_->InitForTest(nullptr);
@@ -325,6 +327,8 @@
// Write a bogus saved log.
EXPECT_EQ(5, base::WriteFile(upload_service_->saved_log_path_, "hello", 5));
+ // Destroy the old service before creating a new one.
+ upload_service_.reset();
upload_service_.reset(new UploadService(
"", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index c5f3d1d..2d04a7f 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -5,6 +5,6 @@
LOCAL_SRC_FILES := sdcard.c
LOCAL_MODULE := sdcard
LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
-LOCAL_SHARED_LIBRARIES := libcutils libpackagelistparser
+LOCAL_SHARED_LIBRARIES := liblog libcutils libpackagelistparser
include $(BUILD_EXECUTABLE)
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 9ade759..fc2898e 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -55,6 +55,7 @@
LOCAL_CONLYFLAGS += -std=gnu99
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libselinux \