Merge "dmabuf_dump: Change the default view to per-process stats"
This commit is contained in:
commit
581f45892c
|
@ -19,6 +19,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
@ -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<pid_t>& 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<pid_t> pids_;
|
||||
std::string exporter_;
|
||||
std::string name_;
|
||||
std::unordered_map<pid_t, int> fdrefs_;
|
||||
|
@ -80,6 +85,7 @@ struct DmaBuffer {
|
|||
auto [it, inserted] = map->insert(std::make_pair(pid, 1));
|
||||
if (!inserted)
|
||||
it->second++;
|
||||
pids_.insert(pid);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -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<pid_t, int>& map, std::set<pid_t>* set) {
|
||||
for (auto it = map.begin(); it != map.end(); ++it) set->insert(it->first);
|
||||
}
|
||||
|
||||
static void PrintDmaBufInfo(const std::vector<DmaBuffer>& bufs) {
|
||||
static void PrintDmaBufTable(const std::vector<DmaBuffer>& bufs) {
|
||||
if (bufs.empty()) {
|
||||
printf("dmabuf info not found ¯\\_(ツ)_/¯\n");
|
||||
return;
|
||||
|
@ -66,9 +64,8 @@ static void PrintDmaBufInfo(const std::vector<DmaBuffer>& bufs) {
|
|||
|
||||
// Find all unique pids in the input vector, create a set
|
||||
std::set<pid_t> 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<DmaBuffer>& bufs) {
|
|||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
pid_t pid = -1;
|
||||
std::vector<DmaBuffer> bufs;
|
||||
bool show_all = true;
|
||||
static void PrintDmaBufPerProcess(const std::vector<DmaBuffer>& 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_t, std::set<ino_t>> 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<ino_t, DmaBuffer> 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() ? "<unknown>" : buf.name().c_str(), buf.size() / 1024,
|
||||
buf.Pss() / 1024, buf.pids().size(), static_cast<uintmax_t>(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<DmaBuffer>* bufs) {
|
||||
bufs->clear();
|
||||
|
||||
if (!ReadDmaBufInfo(bufs)) {
|
||||
fprintf(stderr, "debugfs entry for dmabuf not available, skipping\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<DIR, int (*)(DIR*)> 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, int (*)(DIR*)> dir(opendir("/proc"), closedir);
|
||||
if (!dir) {
|
||||
std::cerr << "Failed to open /proc directory" << std::endl;
|
||||
std::vector<DmaBuffer> 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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue