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);