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