Add more tests to libdmabufinfo

Add some test to verify the refcount and fd reference is correct when
the dma_buf is shared between processes.

Bug; 63860998
Test: libdmabufinfo_test

Change-Id: Id22e68e7a65820f19847b2faab11c78e6d942d92
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
index 95aa2c7..eb53e57 100644
--- a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
+++ b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
@@ -14,14 +14,17 @@
  */
 
 #include <gtest/gtest.h>
+#include <inttypes.h>
 #include <linux/dma-buf.h>
+#include <poll.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <fstream>
 #include <string>
-#include <vector>
 #include <unordered_map>
+#include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -50,6 +53,115 @@
 #define DMA_BUF_SET_NAME _IOW(DMA_BUF_BASE, 5, const char*)
 #endif
 
+class fd_sharer {
+  public:
+    fd_sharer();
+    ~fd_sharer() { kill(); }
+
+    bool ok() const { return child_pid > 0; }
+    bool sendfd(int fd);
+    bool kill();
+    pid_t pid() const { return child_pid; }
+
+  private:
+    unique_fd parent_fd, child_fd;
+    pid_t child_pid;
+
+    void run();
+};
+
+fd_sharer::fd_sharer() : parent_fd{}, child_fd{}, child_pid{-1} {
+    bool sp_ok = android::base::Socketpair(SOCK_STREAM, &parent_fd, &child_fd);
+    if (!sp_ok) return;
+
+    child_pid = fork();
+    if (child_pid < 0) return;
+
+    if (child_pid == 0) run();
+}
+
+bool fd_sharer::kill() {
+    int err = ::kill(child_pid, SIGKILL);
+    if (err < 0) return false;
+
+    return ::waitpid(child_pid, nullptr, 0) == child_pid;
+}
+
+void fd_sharer::run() {
+    while (true) {
+        int fd;
+        char unused = 0;
+
+        iovec iov{};
+        iov.iov_base = &unused;
+        iov.iov_len = sizeof(unused);
+
+        msghdr msg{};
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+
+        char cmsg_buf[CMSG_SPACE(sizeof(fd))];
+        msg.msg_control = cmsg_buf;
+        msg.msg_controllen = sizeof(cmsg_buf);
+
+        cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+
+        ssize_t s = TEMP_FAILURE_RETRY(recvmsg(child_fd, &msg, 0));
+        if (s == -1) break;
+
+        s = TEMP_FAILURE_RETRY(write(child_fd, &unused, sizeof(unused)));
+        if (s == -1) break;
+    }
+}
+
+bool fd_sharer::sendfd(int fd) {
+    char unused = 0;
+
+    iovec iov{};
+    iov.iov_base = &unused;
+    iov.iov_len = sizeof(unused);
+
+    msghdr msg{};
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    char cmsg_buf[CMSG_SPACE(sizeof(fd))];
+    msg.msg_control = cmsg_buf;
+    msg.msg_controllen = sizeof(cmsg_buf);
+
+    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+
+    int* fd_buf = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+    *fd_buf = fd;
+
+    ssize_t s = TEMP_FAILURE_RETRY(sendmsg(parent_fd, &msg, 0));
+    if (s == -1) return false;
+
+    // The target process installs the fd into its fd table during recvmsg().
+    // So if we return now, there's a brief window between sendfd() finishing
+    // and libmemoryinfo actually seeing that the buffer has been shared.  This
+    // window is just large enough to break tests.
+    //
+    // To work around this, wait for the target process to respond with a dummy
+    // byte, with a timeout of 1 s.
+    pollfd p{};
+    p.fd = parent_fd;
+    p.events = POLL_IN;
+    int ready = poll(&p, 1, 1000);
+    if (ready != 1) return false;
+
+    s = TEMP_FAILURE_RETRY(read(parent_fd, &unused, sizeof(unused)));
+    if (s == -1) return false;
+
+    return true;
+}
+
 #define EXPECT_ONE_BUF_EQ(_bufptr, _name, _fdrefs, _maprefs, _expname, _count, _size) \
     do {                                                                              \
         EXPECT_EQ(_bufptr->name(), _name);                                            \
@@ -140,6 +252,24 @@
         return unique_fd{fd};
     }
 
+    void readAndCheckDmaBuffer(std::vector<DmaBuffer>* dmabufs, pid_t pid, const std::string name,
+                               size_t fdrefs_size, size_t maprefs_size, const std::string exporter,
+                               size_t refcount, uint64_t buf_size, bool expectFdrefs,
+                               bool expectMapRefs) {
+        EXPECT_TRUE(ReadDmaBufInfo(pid, dmabufs));
+        EXPECT_EQ(dmabufs->size(), 1UL);
+        EXPECT_ONE_BUF_EQ(dmabufs->begin(), name, fdrefs_size, maprefs_size, exporter, refcount,
+                          buf_size);
+        // Make sure the buffer has the right pid too.
+        EXPECT_PID_IN_FDREFS(dmabufs->begin(), pid, expectFdrefs);
+        EXPECT_PID_IN_MAPREFS(dmabufs->begin(), pid, expectMapRefs);
+    }
+
+    bool checkPidRef(DmaBuffer& dmabuf, pid_t pid, int expectFdrefs) {
+        int fdrefs = dmabuf.fdrefs().find(pid)->second;
+        return fdrefs == expectFdrefs;
+    }
+
   private:
     int get_ion_heap_mask() {
         if (ion_fd < 0) {
@@ -196,7 +326,7 @@
         EXPECT_ONE_BUF_EQ(dmabufs.begin(), "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL);
 
         // Make sure the buffer has the right pid too.
-        EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, false);
+        EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, true);
     }
 
     // Now make sure the buffer has disappeared
@@ -222,8 +352,8 @@
         EXPECT_ONE_BUF_EQ(dmabufs.begin(), "dmabuftester-4k", 1UL, 1UL, "ion", 2UL, 4096ULL);
 
         // Make sure the buffer has the right pid too.
-        EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, false);
-        EXPECT_PID_IN_MAPREFS(dmabufs.begin(), pid, false);
+        EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, true);
+        EXPECT_PID_IN_MAPREFS(dmabufs.begin(), pid, true);
 
         // close the file descriptor and re-read the stats
         buf.reset(-1);
@@ -232,8 +362,8 @@
         EXPECT_EQ(dmabufs.size(), 1UL);
         EXPECT_ONE_BUF_EQ(dmabufs.begin(), "<unknown>", 0UL, 1UL, "<unknown>", 0UL, 4096ULL);
 
-        EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, true);
-        EXPECT_PID_IN_MAPREFS(dmabufs.begin(), pid, false);
+        EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, false);
+        EXPECT_PID_IN_MAPREFS(dmabufs.begin(), pid, true);
 
         // unmap the bufer and lose all references
         munmap(ptr, 4096);
@@ -244,6 +374,109 @@
     EXPECT_TRUE(dmabufs.empty());
 }
 
+TEST_F(DmaBufTester, TestSharedfd) {
+    // Each time a shared buffer is received over a socket, the remote process
+    // will take an extra reference on it.
+
+    ASSERT_TRUE(is_valid());
+
+    pid_t pid = getpid();
+    std::vector<DmaBuffer> dmabufs;
+    {
+        fd_sharer sharer{};
+        ASSERT_TRUE(sharer.ok());
+        // Allocate one buffer and make sure the library can see it
+        unique_fd buf = allocate(4096, "dmabuftester-4k");
+        ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+
+        ASSERT_TRUE(sharer.sendfd(buf));
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 2UL, 4096ULL, true,
+                              false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 1));
+        readAndCheckDmaBuffer(&dmabufs, sharer.pid(), "dmabuftester-4k", 1UL, 0UL, "ion", 2UL,
+                              4096ULL, true, false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], sharer.pid(), 1));
+
+        ASSERT_TRUE(sharer.sendfd(buf));
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 3UL, 4096ULL, true,
+                              false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 1));
+        readAndCheckDmaBuffer(&dmabufs, sharer.pid(), "dmabuftester-4k", 1UL, 0UL, "ion", 3UL,
+                              4096ULL, true, false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], sharer.pid(), 2));
+
+        ASSERT_TRUE(sharer.kill());
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+    }
+
+    // Now make sure the buffer has disappeared
+    ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+    EXPECT_TRUE(dmabufs.empty());
+}
+
+TEST_F(DmaBufTester, DupFdTest) {
+    // dup()ing an fd will make this process take an extra reference on the
+    // shared buffer.
+
+    ASSERT_TRUE(is_valid());
+
+    pid_t pid = getpid();
+    std::vector<DmaBuffer> dmabufs;
+    {
+        // Allocate one buffer and make sure the library can see it
+        unique_fd buf = allocate(4096, "dmabuftester-4k");
+        ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+
+        unique_fd buf2{dup(buf)};
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 2UL, 4096ULL, true,
+                              false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 2));
+
+        close(buf2.release());
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+        EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 1));
+    }
+
+    // Now make sure the buffer has disappeared
+    ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+    EXPECT_TRUE(dmabufs.empty());
+}
+
+TEST_F(DmaBufTester, ForkTest) {
+    // fork()ing a child will cause the child to automatically take a reference
+    // on any existing shared buffers.
+    ASSERT_TRUE(is_valid());
+
+    pid_t pid = getpid();
+    std::vector<DmaBuffer> dmabufs;
+    {
+        // Allocate one buffer and make sure the library can see it
+        unique_fd buf = allocate(4096, "dmabuftester-4k");
+        ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+        fd_sharer sharer{};
+        ASSERT_TRUE(sharer.ok());
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 2UL, 4096ULL, true,
+                              false);
+        readAndCheckDmaBuffer(&dmabufs, sharer.pid(), "dmabuftester-4k", 1UL, 0UL, "ion", 2UL,
+                              4096ULL, true, false);
+        ASSERT_TRUE(sharer.kill());
+        readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+                              false);
+    }
+
+    // Now make sure the buffer has disappeared
+    ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+    EXPECT_TRUE(dmabufs.empty());
+}
+
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     ::android::base::InitLogging(argv, android::base::StderrLogger);
diff --git a/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h b/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
index 74eff3c..e3be320 100644
--- a/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
+++ b/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
@@ -44,7 +44,7 @@
     }
 
     // Getters for each property
-    uint64_t size() { return size_; }
+    uint64_t size() const { return size_; }
     const std::unordered_map<pid_t, int>& fdrefs() const { return fdrefs_; }
     const std::unordered_map<pid_t, int>& maprefs() const { return maprefs_; }
     ino_t inode() const { return inode_; }
@@ -56,6 +56,11 @@
     void SetExporter(const std::string& exporter) { exporter_ = exporter; }
     void SetCount(uint64_t count) { count_ = count; }
 
+    bool operator==(const DmaBuffer& rhs) {
+        return (inode_ == rhs.inode()) && (size_ == rhs.size()) && (name_ == rhs.name()) &&
+               (exporter_ == rhs.exporter());
+    }
+
   private:
     ino_t inode_;
     uint64_t size_;