diff options
author | Erick Reyes <erickreyes@google.com> | 2019-01-24 15:26:23 -0800 |
---|---|---|
committer | Sandeep Patil <sspatil@google.com> | 2019-01-28 14:16:23 -0800 |
commit | d2918fe2181f1cc2113fbb5398886a1e6e68aa2d (patch) | |
tree | 5a8e465059adc2e6ddb8a12544fb837728dc70a3 /libmeminfo/libdmabufinfo/dmabufinfo_test.cpp | |
parent | 561498e357c00f9ae18e06efefc16ec68fb5d439 (diff) | |
download | system_core-d2918fe2181f1cc2113fbb5398886a1e6e68aa2d.tar.gz system_core-d2918fe2181f1cc2113fbb5398886a1e6e68aa2d.tar.bz2 system_core-d2918fe2181f1cc2113fbb5398886a1e6e68aa2d.zip |
meminfo: Add memtrack dmabufinfo library
This is the userspace component for the improved shared memory buffer
tracking.
A new static library called libdmabufinfo will provide APIs to parse dmabuf
debugfs entries. The library can also map and read the file descriptors
per process to cross-reference the global data per process.
This change only adds a global API to read dmabuf stats from debugfs.
Bug: 63860998
Test: dmabufinfo_test
Change-Id: Ib0deedc73337a70875e03e07d3e9a692c4c33e63
Signed-off-by: Greg Hackmann <ghackmann@google.com>
Signed-off-by: Erick Reyes <erickreyes@google.com>
Signed-off-by: Sandeep Patil <sspatil@google.com>
Diffstat (limited to 'libmeminfo/libdmabufinfo/dmabufinfo_test.cpp')
-rw-r--r-- | libmeminfo/libdmabufinfo/dmabufinfo_test.cpp | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp new file mode 100644 index 000000000..aa5f16c22 --- /dev/null +++ b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp @@ -0,0 +1,252 @@ +/* Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <linux/dma-buf.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android-base/unique_fd.h> +#include <ion/ion.h> + +#include <dmabufinfo/dmabufinfo.h> + +using namespace ::android::dmabufinfo; +using namespace ::android::base; + +#define MAX_HEAP_NAME 32 +#define ION_HEAP_ANY_MASK (0x7fffffff) + +struct ion_heap_data { + char name[MAX_HEAP_NAME]; + __u32 type; + __u32 heap_id; + __u32 reserved0; + __u32 reserved1; + __u32 reserved2; +}; + +#ifndef DMA_BUF_SET_NAME +#define DMA_BUF_SET_NAME _IOW(DMA_BUF_BASE, 5, const char*) +#endif + +#define EXPECT_ONE_BUF_EQ(_bufptr, _name, _fdrefs, _maprefs, _expname, _count, _size) \ + do { \ + EXPECT_EQ(_bufptr->name(), _name); \ + EXPECT_EQ(_bufptr->fdrefs().size(), _fdrefs); \ + EXPECT_EQ(_bufptr->maprefs().size(), _maprefs); \ + EXPECT_EQ(_bufptr->exporter(), _expname); \ + EXPECT_EQ(_bufptr->count(), _count); \ + EXPECT_EQ(_bufptr->size(), _size); \ + } while (0) + +#define EXPECT_PID_IN_FDREFS(_bufptr, _pid, _expect) \ + do { \ + const std::vector<pid_t>& _fdrefs = _bufptr->fdrefs(); \ + auto _ref = std::find_if(_fdrefs.begin(), _fdrefs.end(), \ + [&](const pid_t& p) { return p == _pid; }); \ + EXPECT_EQ((_ref == _fdrefs.end()), _expect); \ + } while (0) + +#define EXPECT_PID_IN_MAPREFS(_bufptr, _pid, _expect) \ + do { \ + const std::vector<pid_t>& _maprefs = _bufptr->maprefs(); \ + auto _ref = std::find_if(_maprefs.begin(), _maprefs.end(), \ + [&](const pid_t& p) { return p == _pid; }); \ + EXPECT_EQ((_ref == _maprefs.end()), _expect); \ + } while (0) + +TEST(DmaBufInfoParser, TestReadDmaBufInfo) { + std::string bufinfo = R"bufinfo(00045056 00000002 00000007 00000002 ion 00022069 + Attached Devices: +Total 0 devices attached +01048576 00000002 00000007 00000001 ion 00019834 CAMERA + Attached Devices: + soc:qcom,cam_smmu:msm_cam_smmu_icp +Total 1 devices attached)bufinfo"; + + TemporaryFile tf; + ASSERT_TRUE(tf.fd != -1); + ASSERT_TRUE(::android::base::WriteStringToFd(bufinfo, tf.fd)); + std::string path = std::string(tf.path); + + std::vector<DmaBuffer> dmabufs; + EXPECT_TRUE(ReadDmaBufInfo(&dmabufs, path)); + + EXPECT_EQ(dmabufs.size(), 2UL); + + EXPECT_EQ(dmabufs[0].size(), 45056UL); + EXPECT_EQ(dmabufs[0].inode(), 22069UL); + EXPECT_EQ(dmabufs[0].count(), 2UL); + EXPECT_EQ(dmabufs[0].exporter(), "ion"); + EXPECT_TRUE(dmabufs[0].name().empty()); + EXPECT_EQ(dmabufs[0].total_refs(), 0ULL); + EXPECT_TRUE(dmabufs[0].fdrefs().empty()); + EXPECT_TRUE(dmabufs[0].maprefs().empty()); + + EXPECT_EQ(dmabufs[1].size(), 1048576UL); + EXPECT_EQ(dmabufs[1].inode(), 19834UL); + EXPECT_EQ(dmabufs[1].count(), 1UL); + EXPECT_EQ(dmabufs[1].exporter(), "ion"); + EXPECT_FALSE(dmabufs[1].name().empty()); + EXPECT_EQ(dmabufs[1].name(), "CAMERA"); + EXPECT_EQ(dmabufs[1].total_refs(), 0ULL); + EXPECT_TRUE(dmabufs[1].fdrefs().empty()); + EXPECT_TRUE(dmabufs[1].maprefs().empty()); +} + +class DmaBufTester : public ::testing::Test { + public: + DmaBufTester() : ion_fd(ion_open()), ion_heap_mask(get_ion_heap_mask()) {} + + ~DmaBufTester() { + if (is_valid()) { + ion_close(ion_fd); + } + } + + bool is_valid() { return (ion_fd >= 0 && ion_heap_mask > 0); } + + unique_fd allocate(uint64_t size, const std::string& name) { + int fd; + int err = ion_alloc_fd(ion_fd, size, 0, ion_heap_mask, 0, &fd); + if (err < 0) { + return unique_fd{err}; + } + + if (!name.empty()) { + err = ioctl(fd, DMA_BUF_SET_NAME, name.c_str()); + if (err < 0) return unique_fd{-errno}; + } + + return unique_fd{fd}; + } + + private: + int get_ion_heap_mask() { + if (ion_fd < 0) { + return 0; + } + + if (ion_is_legacy(ion_fd)) { + // Since ION is still in staging, we've seen that the heap mask ids are also + // changed across kernels for some reason. So, here we basically ask for a buffer + // from _any_ heap. + return ION_HEAP_ANY_MASK; + } + + int cnt; + int err = ion_query_heap_cnt(ion_fd, &cnt); + if (err < 0) { + return err; + } + + std::vector<ion_heap_data> heaps; + heaps.resize(cnt); + err = ion_query_get_heaps(ion_fd, cnt, &heaps[0]); + if (err < 0) { + return err; + } + + unsigned int ret = 0; + for (auto& it : heaps) { + if (!strcmp(it.name, "ion_system_heap")) { + ret |= (1 << it.heap_id); + } + } + + return ret; + } + + unique_fd ion_fd; + const int ion_heap_mask; +}; + +TEST_F(DmaBufTester, TestFdRef) { + // Test if a dma buffer is found while the corresponding file descriptor + // is open + 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"; + ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs)); + + EXPECT_EQ(dmabufs.size(), 1UL); + 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); + } + + // Now make sure the buffer has disappeared + ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs)); + EXPECT_TRUE(dmabufs.empty()); +} + +TEST_F(DmaBufTester, TestMapRef) { + // Test to make sure we can find a buffer if the fd is closed but the buffer + // is mapped + 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"; + auto ptr = mmap(0, 4096, PROT_READ, MAP_SHARED, buf, 0); + ASSERT_NE(ptr, MAP_FAILED); + ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs)); + + EXPECT_EQ(dmabufs.size(), 1UL); + 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); + + // close the file descriptor and re-read the stats + buf.reset(-1); + ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs)); + + 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); + + // unmap the bufer and lose all references + munmap(ptr, 4096); + } + + // 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); + return RUN_ALL_TESTS(); +} |