diff --git a/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h b/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h index a16c3fdaf..a6e7f6907 100644 --- a/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h +++ b/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ struct DmaBuffer { : inode_(inode), size_(size), count_(count), exporter_(exporter), name_(name) { total_refs_ = 0; } + DmaBuffer() = default; ~DmaBuffer() = default; // Adds one file descriptor reference for the given pid @@ -54,11 +56,13 @@ struct DmaBuffer { ino_t inode() const { return inode_; } uint64_t total_refs() const { return total_refs_; } uint64_t count() const { return count_; }; + const std::set& pids() const { return pids_; } const std::string& name() const { return name_; } const std::string& exporter() const { return exporter_; } void SetName(const std::string& name) { name_ = name; } void SetExporter(const std::string& exporter) { exporter_ = exporter; } void SetCount(uint64_t count) { count_ = count; } + uint64_t Pss() const { return size_ / pids_.size(); } bool operator==(const DmaBuffer& rhs) { return (inode_ == rhs.inode()) && (size_ == rhs.size()) && (name_ == rhs.name()) && @@ -70,6 +74,7 @@ struct DmaBuffer { uint64_t size_; uint64_t count_; uint64_t total_refs_; + std::set pids_; std::string exporter_; std::string name_; std::unordered_map fdrefs_; @@ -80,6 +85,7 @@ struct DmaBuffer { auto [it, inserted] = map->insert(std::make_pair(pid, 1)); if (!inserted) it->second++; + pids_.insert(pid); } }; diff --git a/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp b/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp index 9a80c82fc..48901b1da 100644 --- a/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp +++ b/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -36,9 +37,10 @@ using DmaBuffer = ::android::dmabufinfo::DmaBuffer; [[noreturn]] static void usage(int exit_status) { fprintf(stderr, - "Usage: %s [PID] \n" - "\t If PID is supplied, the dmabuf information for this process is shown.\n" - "\t Otherwise, shows the information for all processes.\n", + "Usage: %s [-ah] [PID] \n" + "-a\t show all dma buffers (ion) in big table, [buffer x process] grid \n" + "-h\t show this help\n" + " \t If PID is supplied, the dmabuf information for that process is shown.\n", getprogname()); exit(exit_status); @@ -54,11 +56,7 @@ static std::string GetProcessComm(const pid_t pid) { return line; } -static void AddPidsToSet(const std::unordered_map& map, std::set* set) { - for (auto it = map.begin(); it != map.end(); ++it) set->insert(it->first); -} - -static void PrintDmaBufInfo(const std::vector& bufs) { +static void PrintDmaBufTable(const std::vector& bufs) { if (bufs.empty()) { printf("dmabuf info not found ¯\\_(ツ)_/¯\n"); return; @@ -66,9 +64,8 @@ static void PrintDmaBufInfo(const std::vector& bufs) { // Find all unique pids in the input vector, create a set std::set pid_set; - for (int i = 0; i < bufs.size(); i++) { - AddPidsToSet(bufs[i].fdrefs(), &pid_set); - AddPidsToSet(bufs[i].maprefs(), &pid_set); + for (auto& buf : bufs) { + pid_set.insert(buf.pids().begin(), buf.pids().end()); } // Format the header string spaced and separated with '|' @@ -126,50 +123,144 @@ static void PrintDmaBufInfo(const std::vector& bufs) { return; } -int main(int argc, char* argv[]) { - pid_t pid = -1; - std::vector bufs; - bool show_all = true; +static void PrintDmaBufPerProcess(const std::vector& bufs) { + if (bufs.empty()) { + printf("dmabuf info not found ¯\\_(ツ)_/¯\n"); + return; + } - if (argc > 1) { - if (sscanf(argv[1], "%d", &pid) == 1) { - show_all = false; - } else { + // Create a reverse map from pid to dmabufs + std::unordered_map> pid_to_inodes = {}; + uint64_t total_size = 0; // Total size of dmabufs in the system + uint64_t kernel_rss = 0; // Total size of dmabufs NOT mapped or opened by a process + for (auto& buf : bufs) { + for (auto pid : buf.pids()) { + pid_to_inodes[pid].insert(buf.inode()); + } + total_size += buf.size(); + if (buf.fdrefs().empty() && buf.maprefs().empty()) { + kernel_rss += buf.size(); + } + } + // Create an inode to dmabuf map. We know inodes are unique.. + std::unordered_map inode_to_dmabuf; + for (auto buf : bufs) { + inode_to_dmabuf[buf.inode()] = buf; + } + + uint64_t total_rss = 0, total_pss = 0; + for (auto& [pid, inodes] : pid_to_inodes) { + uint64_t pss = 0; + uint64_t rss = 0; + + printf("%16s:%-5d\n", GetProcessComm(pid).c_str(), pid); + printf("%22s %16s %16s %16s %16s\n", "Name", "Rss", "Pss", "nr_procs", "Inode"); + for (auto& inode : inodes) { + DmaBuffer& buf = inode_to_dmabuf[inode]; + printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16zu %16" PRIuMAX "\n", + buf.name().empty() ? "" : buf.name().c_str(), buf.size() / 1024, + buf.Pss() / 1024, buf.pids().size(), static_cast(buf.inode())); + rss += buf.size(); + pss += buf.Pss(); + } + printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16s\n", "PROCESS TOTAL", rss / 1024, + pss / 1024, ""); + printf("----------------------\n"); + total_rss += rss; + total_pss += pss; + } + printf("dmabuf total: %" PRIu64 " kB kernel_rss: %" PRIu64 " kB userspace_rss: %" PRIu64 + " kB userspace_pss: %" PRIu64 " kB\n ", + total_size / 1024, kernel_rss / 1024, total_rss / 1024, total_pss / 1024); +} + +static bool ReadDmaBufs(std::vector* bufs) { + bufs->clear(); + + if (!ReadDmaBufInfo(bufs)) { + fprintf(stderr, "debugfs entry for dmabuf not available, skipping\n"); + return false; + } + + std::unique_ptr dir(opendir("/proc"), closedir); + if (!dir) { + fprintf(stderr, "Failed to open /proc directory\n"); + bufs->clear(); + return false; + } + + struct dirent* dent; + while ((dent = readdir(dir.get()))) { + if (dent->d_type != DT_DIR) continue; + + int pid = atoi(dent->d_name); + if (pid == 0) { + continue; + } + + if (!AppendDmaBufInfo(pid, bufs)) { + fprintf(stderr, "Unable to read dmabuf info for pid %d\n", pid); + bufs->clear(); + return false; + } + } + + return true; +} + +int main(int argc, char* argv[]) { + struct option longopts[] = {{"all", no_argument, nullptr, 'a'}, + {"help", no_argument, nullptr, 'h'}, + {0, 0, nullptr, 0}}; + + int opt; + bool show_table = false; + while ((opt = getopt_long(argc, argv, "ah", longopts, nullptr)) != -1) { + switch (opt) { + case 'a': + show_table = true; + break; + case 'h': + usage(EXIT_SUCCESS); + default: + usage(EXIT_FAILURE); + } + } + + pid_t pid = -1; + if (optind < argc) { + if (show_table) { + fprintf(stderr, "Invalid arguments: -a does not need arguments\n"); + usage(EXIT_FAILURE); + } + if (optind != (argc - 1)) { + fprintf(stderr, "Invalid arguments - only one [PID] argument is allowed\n"); + usage(EXIT_FAILURE); + } + pid = atoi(argv[optind]); + if (pid == 0) { + fprintf(stderr, "Invalid process id %s\n", argv[optind]); usage(EXIT_FAILURE); } } - if (show_all) { - if (!ReadDmaBufInfo(&bufs)) { - std::cerr << "debugfs entry for dmabuf not available, skipping" << std::endl; - bufs.clear(); - } - std::unique_ptr dir(opendir("/proc"), closedir); - if (!dir) { - std::cerr << "Failed to open /proc directory" << std::endl; + std::vector bufs; + if (pid != -1) { + if (!ReadDmaBufInfo(pid, &bufs)) { + fprintf(stderr, "Unable to read dmabuf info for %d\n", pid); exit(EXIT_FAILURE); } - struct dirent* dent; - while ((dent = readdir(dir.get()))) { - if (dent->d_type != DT_DIR) continue; - - int matched = sscanf(dent->d_name, "%d", &pid); - if (matched != 1) { - continue; - } - - if (!AppendDmaBufInfo(pid, &bufs)) { - std::cerr << "Unable to read dmabuf info for pid " << pid << std::endl; - exit(EXIT_FAILURE); - } - } } else { - if (!ReadDmaBufInfo(pid, &bufs)) { - std::cerr << "Unable to read dmabuf info" << std::endl; - exit(EXIT_FAILURE); - } + if (!ReadDmaBufs(&bufs)) exit(EXIT_FAILURE); } - PrintDmaBufInfo(bufs); + // Show the old dmabuf table, inode x process + if (show_table) { + PrintDmaBufTable(bufs); + return 0; + } + + PrintDmaBufPerProcess(bufs); + return 0; }