Merge "libprocinfo: add functions reading process map file."
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 89577cb..3a3f399 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -292,8 +292,6 @@
goto CloseProcess;
}
- adb_close(fd);
-
D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
proc->out_fds.pop_back();
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index d679a6d..7876368 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -25,14 +25,18 @@
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
+#include <sys/vfs.h>
#include <unistd.h>
#include <string>
+#include <vector>
#include <android-base/properties.h>
+#include <ext4_utils/ext4_utils.h>
#include "adb.h"
#include "adb_io.h"
+#include "adb_unique_fd.h"
#include "adb_utils.h"
#include "fs_mgr.h"
@@ -82,7 +86,27 @@
return result;
}
-static bool remount_partition(int fd, const char* dir) {
+static bool fs_has_shared_blocks(const char* dev) {
+ struct statfs fs;
+ if (statfs(dev, &fs) == -1 || fs.f_type == EXT4_SUPER_MAGIC) {
+ return false;
+ }
+ unique_fd fd(unix_open(dev, O_RDONLY));
+ if (fd < 0) {
+ return false;
+ }
+ struct ext4_super_block sb;
+ if (lseek64(fd, 1024, SEEK_SET) < 0 || unix_read(fd, &sb, sizeof(sb)) < 0) {
+ return false;
+ }
+ struct fs_info info;
+ if (ext4_parse_sb(&sb, &info) < 0) {
+ return false;
+ }
+ return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
+}
+
+static bool remount_partition(int fd, const char* dir, std::vector<std::string>& dedup) {
if (!directory_exists(dir)) {
return true;
}
@@ -108,6 +132,12 @@
return false;
}
if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
+ if (errno == EROFS && fs_has_shared_blocks(dev.c_str())) {
+ // We return true so remount_service() can detect that the only
+ // failure was deduplicated filesystems.
+ dedup.push_back(dev);
+ return true;
+ }
WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
return false;
}
@@ -140,17 +170,29 @@
}
bool success = true;
+ std::vector<std::string> dedup;
if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
- success &= remount_partition(fd, "/");
+ success &= remount_partition(fd, "/", dedup);
} else {
- success &= remount_partition(fd, "/system");
+ success &= remount_partition(fd, "/system", dedup);
}
- success &= remount_partition(fd, "/odm");
- success &= remount_partition(fd, "/oem");
- success &= remount_partition(fd, "/product");
- success &= remount_partition(fd, "/vendor");
+ success &= remount_partition(fd, "/odm", dedup);
+ success &= remount_partition(fd, "/oem", dedup);
+ success &= remount_partition(fd, "/product", dedup);
+ success &= remount_partition(fd, "/vendor", dedup);
- WriteFdExactly(fd, success ? "remount succeeded\n" : "remount failed\n");
+ if (!success) {
+ WriteFdExactly(fd, "remount failed\n");
+ } else if (dedup.empty()) {
+ WriteFdExactly(fd, "remount succeeded\n");
+ } else {
+ WriteFdExactly(fd,
+ "The following partitions are deduplicated and could "
+ "not be remounted:\n");
+ for (const std::string& name : dedup) {
+ WriteFdFmt(fd, " %s\n", name.c_str());
+ }
+ }
adb_close(fd);
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 9b64be7..dfb7a6a 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -80,8 +80,13 @@
return value; \
}()
+// Backtrace frame dump could contain:
+// #01 pc 0001cded /data/tmp/debuggerd_test32 (raise_debugger_signal+80)
+// or
+// #01 pc 00022a09 /data/tmp/debuggerd_test32 (offset 0x12000) (raise_debugger_signal+80)
#define ASSERT_BACKTRACE_FRAME(result, frame_name) \
- ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ \S+ \()" frame_name R"(\+)");
+ ASSERT_MATCH(result, \
+ R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)");
static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
InterceptStatus* status, DebuggerdDumpType intercept_type) {
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 10c56e3..293736d 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -276,7 +276,7 @@
inotify_fd = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (inotify_fd < 0) {
PLOG(WARNING) << "Could not instantiate inotify for " << kDevicePath;
- } else if (::inotify_add_watch(inotify_fd, kDevicePath, IN_DELETE | IN_CREATE) < 0) {
+ } else if (::inotify_add_watch(inotify_fd, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) < 0) {
PLOG(WARNING) << "Could not add watch for " << kDevicePath;
::close(inotify_fd);
inotify_fd = -1;
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index 24fdc7f..529a043 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -495,6 +495,21 @@
return oss.str();
}
+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::vector<Leak> tmp;
+ leaks.swap(tmp);
+
+ // Disable and re-enable malloc to flush the jemalloc tcache to make sure
+ // there are no copies of the leaked pointer addresses there.
+ malloc_disable();
+ malloc_enable();
+}
+
std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
UnreachableMemoryInfo info;
if (!GetUnreachableMemory(info, limit)) {
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index 438fcaf..c028eab 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -62,12 +62,7 @@
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));
- }
+ ~UnreachableMemoryInfo();
std::string ToString(bool log_contents) const;
};
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index 87417f1..bba0c6d 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -23,6 +23,8 @@
#include <memunreachable/memunreachable.h>
+#include "bionic.h"
+
namespace android {
class HiddenPointer {
@@ -48,7 +50,35 @@
write(0, ptr, 0);
}
-TEST(MemunreachableTest, clean) {
+class MemunreachableTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ CleanStack(8192);
+ CleanTcache();
+ }
+
+ virtual void TearDown() {
+ CleanStack(8192);
+ CleanTcache();
+ }
+
+ // Allocate a buffer on the stack and zero it to make sure there are no
+ // stray pointers from old test runs.
+ void __attribute__((noinline)) CleanStack(size_t size) {
+ void* buf = alloca(size);
+ memset(buf, 0, size);
+ Ref(&buf);
+ }
+
+ // Disable and re-enable malloc to flush the jemalloc tcache to make sure
+ // there are stray pointers from old test runs there.
+ void CleanTcache() {
+ malloc_disable();
+ malloc_enable();
+ }
+};
+
+TEST_F(MemunreachableTest, clean) {
UnreachableMemoryInfo info;
ASSERT_TRUE(LogUnreachableMemory(true, 100));
@@ -57,7 +87,7 @@
ASSERT_EQ(0U, info.leaks.size());
}
-TEST(MemunreachableTest, stack) {
+TEST_F(MemunreachableTest, stack) {
HiddenPointer hidden_ptr;
{
@@ -91,7 +121,7 @@
void* g_ptr;
-TEST(MemunreachableTest, global) {
+TEST_F(MemunreachableTest, global) {
HiddenPointer hidden_ptr;
g_ptr = hidden_ptr.Get();
@@ -122,7 +152,7 @@
}
}
-TEST(MemunreachableTest, tls) {
+TEST_F(MemunreachableTest, tls) {
HiddenPointer hidden_ptr;
pthread_key_t key;
pthread_key_create(&key, nullptr);
@@ -157,10 +187,22 @@
pthread_key_delete(key);
}
-TEST(MemunreachableTest, twice) {
+TEST_F(MemunreachableTest, twice) {
HiddenPointer hidden_ptr;
{
+ void* ptr = hidden_ptr.Get();
+ Ref(&ptr);
+
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+
+ ptr = nullptr;
+ }
+
+ {
UnreachableMemoryInfo info;
ASSERT_TRUE(GetUnreachableMemory(info));
@@ -184,7 +226,7 @@
}
}
-TEST(MemunreachableTest, log) {
+TEST_F(MemunreachableTest, log) {
HiddenPointer hidden_ptr;
ASSERT_TRUE(LogUnreachableMemory(true, 100));
@@ -199,17 +241,23 @@
}
}
-TEST(MemunreachableTest, notdumpable) {
+TEST_F(MemunreachableTest, notdumpable) {
+ if (getuid() == 0) {
+ // TODO(ccross): make this a skipped test when gtest supports them
+ printf("[ SKIP ] Not testable when running as root\n");
+ return;
+ }
+
ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 0));
HiddenPointer hidden_ptr;
- ASSERT_TRUE(LogUnreachableMemory(true, 100));
+ EXPECT_FALSE(LogUnreachableMemory(true, 100));
ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1));
}
-TEST(MemunreachableTest, leak_lots) {
+TEST_F(MemunreachableTest, leak_lots) {
std::vector<HiddenPointer> hidden_ptrs;
hidden_ptrs.resize(1024);