Merge "Make ReadDmabufHeapPoolsSizeKb/HeapTotalExportedKb fall back to ION"
diff --git a/Android.bp b/Android.bp
index f52c2ee..a634b0f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -72,6 +72,7 @@
         "com.android.art",
         "com.android.art.debug",
     ],
+    min_sdk_version: "S",
 }
 
 cc_test {
diff --git a/include/meminfo/procmeminfo.h b/include/meminfo/procmeminfo.h
index ca29f80..8e55b88 100644
--- a/include/meminfo/procmeminfo.h
+++ b/include/meminfo/procmeminfo.h
@@ -65,6 +65,10 @@
     // Returns 'false' if the file is malformed.
     bool ForEachVma(const VmaCallback& callback, bool use_smaps = true);
 
+    // Reads all VMAs from /proc/<pid>/maps and calls the callback() for each one of them.
+    // Returns false in case of failure during parsing.
+    bool ForEachVmaFromMaps(const VmaCallback& callback);
+
     // Used to parse either of /proc/<pid>/{smaps, smaps_rollup} and record the process's
     // Pss and Private memory usage in 'stats'.  In particular, the method only populates the fields
     // of the MemUsage structure that are intended to be used by Android's periodic Pss collection.
diff --git a/libdmabufinfo/Android.bp b/libdmabufinfo/Android.bp
index ebd5862..42be21c 100644
--- a/libdmabufinfo/Android.bp
+++ b/libdmabufinfo/Android.bp
@@ -57,6 +57,7 @@
         "com.android.art",
         "com.android.art.debug",
     ],
+    min_sdk_version: "S",
 }
 
 cc_test {
diff --git a/libdmabufinfo/dmabufinfo.cpp b/libdmabufinfo/dmabufinfo.cpp
index 7777f1a..ebc2590 100644
--- a/libdmabufinfo/dmabufinfo.cpp
+++ b/libdmabufinfo/dmabufinfo.cpp
@@ -97,7 +97,8 @@
     return true;
 }
 
-static bool ReadDmaBufFdRefs(int pid, std::vector<DmaBuffer>* dmabufs,
+// Public methods
+bool ReadDmaBufFdRefs(int pid, std::vector<DmaBuffer>* dmabufs,
                              const std::string& procfs_path) {
     std::string fdinfo_dir_path =
             ::android::base::StringPrintf("%s/%d/fdinfo", procfs_path.c_str(), pid);
@@ -163,7 +164,7 @@
     return true;
 }
 
-static bool ReadDmaBufMapRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs,
+bool ReadDmaBufMapRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs,
                               const std::string& procfs_path,
                               const std::string& dmabuf_sysfs_path) {
     std::string mapspath = ::android::base::StringPrintf("%s/%d/maps", procfs_path.c_str(), pid);
@@ -214,7 +215,6 @@
     return true;
 }
 
-// Public methods
 bool ReadDmaBufInfo(std::vector<DmaBuffer>* dmabufs, const std::string& path) {
     auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
     if (fp == nullptr) {
@@ -252,11 +252,7 @@
 bool ReadDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs, bool read_fdrefs,
                     const std::string& procfs_path, const std::string& dmabuf_sysfs_path) {
     dmabufs->clear();
-    return AppendDmaBufInfo(pid, dmabufs, read_fdrefs, procfs_path, dmabuf_sysfs_path);
-}
 
-bool AppendDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs, bool read_fdrefs,
-                      const std::string& procfs_path, const std::string& dmabuf_sysfs_path) {
     if (read_fdrefs) {
         if (!ReadDmaBufFdRefs(pid, dmabufs, procfs_path)) {
             LOG(ERROR) << "Failed to read dmabuf fd references";
diff --git a/libdmabufinfo/dmabufinfo_test.cpp b/libdmabufinfo/dmabufinfo_test.cpp
index 38ae3be..362eca7 100644
--- a/libdmabufinfo/dmabufinfo_test.cpp
+++ b/libdmabufinfo/dmabufinfo_test.cpp
@@ -435,6 +435,95 @@
     ASSERT_EQ(pss, expected_pss);
 }
 
+TEST_F(DmaBufProcessStatsTest, TestReadDmaBufFdRefs) {
+    AddFdInfo(1, 1024, false);
+    AddFdInfo(2, 2048, true);  // Dmabuf 1
+    AddFdInfo(2, 2048, true);  // Dmabuf 1
+    AddFdInfo(3, 1024, true);  // Dmabuf 2
+
+    std::vector<DmaBuffer> dmabufs;
+    ASSERT_TRUE(ReadDmaBufFdRefs(pid, &dmabufs, procfs_path));
+    ASSERT_EQ(dmabufs.size(), 2u);
+
+    const auto& dmabuf1 = std::find_if(dmabufs.begin(), dmabufs.end(),
+                                       [](const DmaBuffer& dmabuf) { return dmabuf.inode() == 2; });
+
+    ASSERT_EQ(dmabuf1->size(), 2048u);
+    ASSERT_EQ(dmabuf1->fdrefs().size(), 1u);  // Only one process has FDs to this buffer
+    ASSERT_EQ(dmabuf1->maprefs().size(), 0u);
+    ASSERT_EQ(dmabuf1->total_refs(), 2u);
+    ASSERT_EQ(dmabuf1->exporter(), exporter);
+
+    // Verify process has 2 FDs to this buffer
+    ASSERT_NE(dmabuf1, dmabufs.end());
+    const auto& fdrefs1 = dmabuf1->fdrefs();
+    const auto& pid_fdrefs1 = fdrefs1.find(pid);
+    ASSERT_NE(pid_fdrefs1, fdrefs1.end());
+    ASSERT_EQ(pid_fdrefs1->second, 2);
+
+    const auto& dmabuf2 = std::find_if(dmabufs.begin(), dmabufs.end(),
+                                       [](const DmaBuffer& dmabuf) { return dmabuf.inode() == 3; });
+    ASSERT_EQ(dmabuf2->size(), 1024u);
+    ASSERT_EQ(dmabuf2->fdrefs().size(), 1u);  // Only one process has FDs to this buffer
+    ASSERT_EQ(dmabuf2->maprefs().size(), 0u);
+    ASSERT_EQ(dmabuf2->total_refs(), 1u);
+    ASSERT_EQ(dmabuf2->exporter(), exporter);
+
+    // Verify process only has 1 FD to this buffer
+    ASSERT_NE(dmabuf2, dmabufs.end());
+    const auto& fdrefs2 = dmabuf2->fdrefs();
+    const auto& pid_fdrefs2 = fdrefs2.find(pid);
+    ASSERT_NE(pid_fdrefs2, fdrefs2.end());
+    ASSERT_EQ(pid_fdrefs2->second, 1);
+}
+
+TEST_F(DmaBufProcessStatsTest, TestReadDmaBufMapRefs) {
+    std::vector<std::string> map_entries;
+    map_entries.emplace_back(CreateMapEntry(1, 1024, false));
+    map_entries.emplace_back(CreateMapEntry(2, 1024, true));  // Dmabuf 1
+    map_entries.emplace_back(CreateMapEntry(2, 1024, true));  // Dmabuf 1
+    map_entries.emplace_back(CreateMapEntry(3, 2048, true));  // Dmabuf 2
+    AddMapEntries(map_entries);
+
+    AddSysfsDmaBufStats(2, 1024, 2);  // Dmabuf 1
+    AddSysfsDmaBufStats(3, 1024, 1);  // Dmabuf 2
+
+    std::vector<DmaBuffer> dmabufs;
+    ASSERT_TRUE(ReadDmaBufMapRefs(pid, &dmabufs, procfs_path, dmabuf_sysfs_path));
+    ASSERT_EQ(dmabufs.size(), 2u);
+
+    const auto& dmabuf1 = std::find_if(dmabufs.begin(), dmabufs.end(),
+                                       [](const DmaBuffer& dmabuf) { return dmabuf.inode() == 2; });
+
+    ASSERT_EQ(dmabuf1->size(), 1024u);
+    ASSERT_EQ(dmabuf1->fdrefs().size(), 0u);
+    ASSERT_EQ(dmabuf1->maprefs().size(), 1u);  // Only one process mapped this buffer
+    ASSERT_EQ(dmabuf1->total_refs(), 2u);
+    ASSERT_EQ(dmabuf1->exporter(), exporter);
+
+    // Verify process mapped this buffer twice
+    ASSERT_NE(dmabuf1, dmabufs.end());
+    const auto& maprefs1 = dmabuf1->maprefs();
+    const auto& pid_maprefs1 = maprefs1.find(pid);
+    ASSERT_NE(pid_maprefs1, maprefs1.end());
+    ASSERT_EQ(pid_maprefs1->second, 2);
+
+    const auto& dmabuf2 = std::find_if(dmabufs.begin(), dmabufs.end(),
+                                       [](const DmaBuffer& dmabuf) { return dmabuf.inode() == 3; });
+    ASSERT_EQ(dmabuf2->size(), 2048u);
+    ASSERT_EQ(dmabuf2->fdrefs().size(), 0u);
+    ASSERT_EQ(dmabuf2->maprefs().size(), 1u);  // Only one process mapped this buffer
+    ASSERT_EQ(dmabuf2->total_refs(), 1u);
+    ASSERT_EQ(dmabuf2->exporter(), exporter);
+
+    // Verify process mapped this buffer only once
+    ASSERT_NE(dmabuf2, dmabufs.end());
+    const auto& maprefs2 = dmabuf2->maprefs();
+    const auto& pid_maprefs2 = maprefs2.find(pid);
+    ASSERT_NE(pid_maprefs2, maprefs2.end());
+    ASSERT_EQ(pid_maprefs2->second, 1);
+}
+
 class DmaBufTester : public ::testing::Test {
   public:
     DmaBufTester() : ion_fd(ion_open()), ion_heap_mask(get_ion_heap_mask()) {}
diff --git a/libdmabufinfo/include/dmabufinfo/dmabufinfo.h b/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
index c097a45..93736df 100644
--- a/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
+++ b/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
@@ -102,14 +102,25 @@
                     const std::string& procfs_path = "/proc",
                     const std::string& dmabuf_sysfs_path = "/sys/kernel/dmabuf/buffers");
 
-// Append new dmabuf objects from a given process to an existing vector.
-// When the vector contains an existing element with a matching inode,
-// the reference counts will be updated.
-// Does not depend on DEBUGFS.
-// Returns false if something went wrong with the function, true otherwise.
-bool AppendDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs, bool read_fdrefs = true,
-                      const std::string& procfs_path = "/proc",
-                      const std::string& dmabuf_sysfs_path = "/sys/kernel/dmabuf/buffers");
+// Appends new fd-referenced dmabuf objects from a given process to an existing vector.
+// If the vector contains an existing element with a matching inode, the reference
+// counts are updated.
+// On common kernels earlier than 5.4, reading fd-referenced dmabufs of other processes
+// is only possible if the caller has root privileges. On 5.4+ common kernels the caller
+// can read this information with the PTRACE_MODE_READ permission.
+// Returns true on success, otherwise false.
+bool ReadDmaBufFdRefs(int pid, std::vector<DmaBuffer>* dmabufs,
+                      const std::string& procfs_path = "/proc");
+
+// Appends new mapped dmabuf objects from a given process to an existing vector.
+// If the vector contains an existing element with a matching inode, the reference
+// counts are updated.
+// Returns true on success, otherwise false.
+bool ReadDmaBufMapRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs,
+                       const std::string& procfs_path = "/proc",
+                       const std::string& dmabuf_sysfs_path = "/sys/kernel/dmabuf/buffers");
+
+
 
 // Get the DMA buffers PSS contribution for the specified @pid
 // Returns true on success, false otherwise
diff --git a/libdmabufinfo/tools/dmabuf_dump.cpp b/libdmabufinfo/tools/dmabuf_dump.cpp
index 00e43b2..2466e78 100644
--- a/libdmabufinfo/tools/dmabuf_dump.cpp
+++ b/libdmabufinfo/tools/dmabuf_dump.cpp
@@ -90,17 +90,17 @@
         // Iterate through each process to find out per-process references for each buffer,
         // gather total size used by each process etc.
         for (pid_t pid : pid_set) {
-            int pid_refs = 0;
+            int pid_fdrefs = 0, pid_maprefs = 0;
             if (buf.fdrefs().count(pid) == 1) {
                 // Get the total number of ref counts the process is holding
                 // on this buffer. We don't differentiate between mmap or fd.
-                pid_refs += buf.fdrefs().at(pid);
-                if (buf.maprefs().count(pid) == 1) {
-                    pid_refs += buf.maprefs().at(pid);
-                }
+                pid_fdrefs += buf.fdrefs().at(pid);
+            }
+            if (buf.maprefs().count(pid) == 1) {
+                pid_maprefs += buf.maprefs().at(pid);
             }
 
-            if (pid_refs) {
+            if (pid_fdrefs || pid_maprefs) {
                 // Add up the per-pid total size. Note that if a buffer is mapped
                 // in 2 different processes, the size will be shown as mapped or opened
                 // in both processes. This is intended for visibility.
@@ -108,7 +108,7 @@
                 // If one wants to get the total *unique* dma buffers, they can simply
                 // sum the size of all dma bufs shown by the tool
                 per_pid_size[pid] += buf.size() / 1024;
-                printf("%17d refs |", pid_refs);
+                printf("%9d(%6d) refs |", pid_fdrefs, pid_maprefs);
             } else {
                 printf("%22s |", "--");
             }
@@ -118,7 +118,7 @@
     }
 
     printf("------------------------------------\n");
-    printf("%-16s  %13" PRIu64 " kB |%16s |", "TOTALS", dmabuf_total_size, "n/a");
+    printf("%-16s  %13" PRIu64 " kB |%16s |%16s |", "TOTALS", dmabuf_total_size, "n/a", "n/a");
     for (auto pid : pid_set) {
         printf("%19" PRIu64 " kB |", per_pid_size[pid]);
     }
@@ -202,8 +202,14 @@
             continue;
         }
 
-        if (!AppendDmaBufInfo(pid, bufs)) {
-            fprintf(stderr, "Unable to read dmabuf info for pid %d\n", pid);
+        if (!ReadDmaBufFdRefs(pid, bufs)) {
+            fprintf(stderr, "Failed to read dmabuf fd references for pid %d\n", pid);
+            bufs->clear();
+            return false;
+        }
+
+        if (!ReadDmaBufMapRefs(pid, bufs)) {
+            fprintf(stderr, "Failed to read dmabuf map references for pid %d\n", pid);
             bufs->clear();
             return false;
         }
diff --git a/libmeminfo_benchmark.cpp b/libmeminfo_benchmark.cpp
index 7450c6c..e076c9c 100644
--- a/libmeminfo_benchmark.cpp
+++ b/libmeminfo_benchmark.cpp
@@ -22,6 +22,7 @@
 #include <stdio.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include <string>
 
@@ -36,6 +37,7 @@
 using ::android::meminfo::ProcMemInfo;
 using ::android::meminfo::SmapsOrRollupFromFile;
 using ::android::meminfo::SysMemInfo;
+using ::android::meminfo::Vma;
 
 enum {
     MEMINFO_TOTAL,
@@ -544,4 +546,40 @@
 }
 BENCHMARK(BM_SmapsRollup_new);
 
+static void BM_MapsVmaParsing_ForEachVmaFromMaps(benchmark::State& state) {
+    state.PauseTiming();
+    int pid = getpid();
+    ProcMemInfo meminfo(pid);
+    state.ResumeTiming();
+
+    std::vector<Vma> vmas;
+    auto vmaCollectorCb = [&](const Vma& vma) { vmas.push_back(vma); };
+    for (int i = 0; i < 100000; ++i) {
+        meminfo.ForEachVmaFromMaps(vmaCollectorCb);
+
+        state.PauseTiming();
+        vmas.clear();
+        state.ResumeTiming();
+    }
+}
+BENCHMARK(BM_MapsVmaParsing_ForEachVmaFromMaps)->Unit(benchmark::kMillisecond);
+
+static void BM_MapsVmaParsing_ForEachVma(benchmark::State& state) {
+    state.PauseTiming();
+    int pid = getpid();
+    ProcMemInfo meminfo(pid);
+    state.ResumeTiming();
+
+    std::vector<Vma> vmas;
+    auto vmaCollectorCb = [&](const Vma& vma) { vmas.push_back(vma); };
+    for (int i = 0; i < 100000; ++i) {
+        meminfo.ForEachVma(vmaCollectorCb, false);
+
+        state.PauseTiming();
+        vmas.clear();
+        state.ResumeTiming();
+    }
+}
+BENCHMARK(BM_MapsVmaParsing_ForEachVma)->Unit(benchmark::kMillisecond);
+
 BENCHMARK_MAIN();
diff --git a/procmeminfo.cpp b/procmeminfo.cpp
index 20522be..43bdc06 100644
--- a/procmeminfo.cpp
+++ b/procmeminfo.cpp
@@ -206,6 +206,25 @@
     return ForEachVmaFromFile(path, callback, use_smaps);
 }
 
+bool ProcMemInfo::ForEachVmaFromMaps(const VmaCallback& callback) {
+    Vma vma;
+    auto vmaCollect = [&callback,&vma](const uint64_t start, uint64_t end, uint16_t flags,
+                            uint64_t pgoff, ino_t inode, const char* name, bool shared) {
+        vma.start = start;
+        vma.end = end;
+        vma.flags = flags;
+        vma.offset = pgoff;
+        vma.name = name;
+        vma.inode = inode;
+        vma.is_shared = shared;
+        callback(vma);
+    };
+
+    bool success = ::android::procinfo::ReadProcessMaps(pid_, vmaCollect);
+
+    return success;
+}
+
 bool ProcMemInfo::SmapsOrRollup(MemUsage* stats) const {
     std::string path = ::android::base::StringPrintf(
             "/proc/%d/%s", pid_, IsSmapsRollupSupported() ? "smaps_rollup" : "smaps");