1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <dirent.h> 18 #include <errno.h> 19 #include <getopt.h> 20 #include <inttypes.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <unistd.h> 24 25 #include <fstream> 26 #include <iostream> 27 #include <map> 28 #include <set> 29 #include <sstream> 30 #include <string> 31 #include <vector> 32 33 #include <android-base/stringprintf.h> 34 #include <dmabufinfo/dmabufinfo.h> 35 36 using DmaBuffer = ::android::dmabufinfo::DmaBuffer; 37 38 [[noreturn]] static void usage(int exit_status) { 39 fprintf(stderr, 40 "Usage: %s [-ah] [PID] \n" 41 "-a\t show all dma buffers (ion) in big table, [buffer x process] grid \n" 42 "-h\t show this help\n" 43 " \t If PID is supplied, the dmabuf information for that process is shown.\n", 44 getprogname()); 45 46 exit(exit_status); 47 } 48 49 static std::string GetProcessComm(const pid_t pid) { 50 std::string pid_path = android::base::StringPrintf("/proc/%d/comm", pid); 51 std::ifstream in{pid_path}; 52 if (!in) return std::string("N/A"); 53 std::string line; 54 std::getline(in, line); 55 if (!in) return std::string("N/A"); 56 return line; 57 } 58 59 static void PrintDmaBufTable(const std::vector<DmaBuffer>& bufs) { 60 if (bufs.empty()) { 61 printf("dmabuf info not found ¯\\_(ツ)_/¯\n"); 62 return; 63 } 64 65 // Find all unique pids in the input vector, create a set 66 std::set<pid_t> pid_set; 67 for (auto& buf : bufs) { 68 pid_set.insert(buf.pids().begin(), buf.pids().end()); 69 } 70 71 // Format the header string spaced and separated with '|' 72 printf(" Dmabuf Inode | Size | Fd Ref Counts | Map Ref Counts |"); 73 for (auto pid : pid_set) { 74 printf("%16s:%-5d |", GetProcessComm(pid).c_str(), pid); 75 } 76 printf("\n"); 77 78 // holds per-process dmabuf size in kB 79 std::map<pid_t, uint64_t> per_pid_size = {}; 80 uint64_t dmabuf_total_size = 0; 81 82 // Iterate through all dmabufs and collect per-process sizes, refs 83 for (auto& buf : bufs) { 84 printf("%16ju |%13" PRIu64 " kB |%16zu |%16zu |", 85 static_cast<uintmax_t>(buf.inode()), buf.size() / 1024, buf.fdrefs().size(), 86 buf.maprefs().size()); 87 // Iterate through each process to find out per-process references for each buffer, 88 // gather total size used by each process etc. 89 for (pid_t pid : pid_set) { 90 int pid_refs = 0; 91 if (buf.fdrefs().count(pid) == 1) { 92 // Get the total number of ref counts the process is holding 93 // on this buffer. We don't differentiate between mmap or fd. 94 pid_refs += buf.fdrefs().at(pid); 95 if (buf.maprefs().count(pid) == 1) { 96 pid_refs += buf.maprefs().at(pid); 97 } 98 } 99 100 if (pid_refs) { 101 // Add up the per-pid total size. Note that if a buffer is mapped 102 // in 2 different processes, the size will be shown as mapped or opened 103 // in both processes. This is intended for visibility. 104 // 105 // If one wants to get the total *unique* dma buffers, they can simply 106 // sum the size of all dma bufs shown by the tool 107 per_pid_size[pid] += buf.size() / 1024; 108 printf("%17d refs |", pid_refs); 109 } else { 110 printf("%22s |", "--"); 111 } 112 } 113 dmabuf_total_size += buf.size() / 1024; 114 printf("\n"); 115 } 116 117 printf("------------------------------------\n"); 118 printf("%-16s %13" PRIu64 " kB |%16s |", "TOTALS", dmabuf_total_size, "n/a"); 119 for (auto pid : pid_set) { 120 printf("%19" PRIu64 " kB |", per_pid_size[pid]); 121 } 122 printf("\n"); 123 124 return; 125 } 126 127 static void PrintDmaBufPerProcess(const std::vector<DmaBuffer>& bufs) { 128 if (bufs.empty()) { 129 printf("dmabuf info not found ¯\\_(ツ)_/¯\n"); 130 return; 131 } 132 133 // Create a reverse map from pid to dmabufs 134 std::unordered_map<pid_t, std::set<ino_t>> pid_to_inodes = {}; 135 uint64_t total_size = 0; // Total size of dmabufs in the system 136 uint64_t kernel_rss = 0; // Total size of dmabufs NOT mapped or opened by a process 137 for (auto& buf : bufs) { 138 for (auto pid : buf.pids()) { 139 pid_to_inodes[pid].insert(buf.inode()); 140 } 141 total_size += buf.size(); 142 if (buf.fdrefs().empty() && buf.maprefs().empty()) { 143 kernel_rss += buf.size(); 144 } 145 } 146 // Create an inode to dmabuf map. We know inodes are unique.. 147 std::unordered_map<ino_t, DmaBuffer> inode_to_dmabuf; 148 for (auto buf : bufs) { 149 inode_to_dmabuf[buf.inode()] = buf; 150 } 151 152 uint64_t total_rss = 0, total_pss = 0; 153 for (auto& [pid, inodes] : pid_to_inodes) { 154 uint64_t pss = 0; 155 uint64_t rss = 0; 156 157 printf("%16s:%-5d\n", GetProcessComm(pid).c_str(), pid); 158 printf("%22s %16s %16s %16s %16s\n", "Name", "Rss", "Pss", "nr_procs", "Inode"); 159 for (auto& inode : inodes) { 160 DmaBuffer& buf = inode_to_dmabuf[inode]; 161 uint64_t proc_pss = buf.Pss(pid); 162 printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16zu %16" PRIuMAX "\n", 163 buf.name().empty() ? "<unknown>" : buf.name().c_str(), buf.size() / 1024, 164 proc_pss / 1024, buf.pids().size(), static_cast<uintmax_t>(buf.inode())); 165 rss += buf.size(); 166 pss += proc_pss; 167 } 168 printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16s\n", "PROCESS TOTAL", rss / 1024, 169 pss / 1024, ""); 170 printf("----------------------\n"); 171 total_rss += rss; 172 total_pss += pss; 173 } 174 printf("dmabuf total: %" PRIu64 " kB kernel_rss: %" PRIu64 " kB userspace_rss: %" PRIu64 175 " kB userspace_pss: %" PRIu64 " kB\n ", 176 total_size / 1024, kernel_rss / 1024, total_rss / 1024, total_pss / 1024); 177 } 178 179 static bool ReadDmaBufs(std::vector<DmaBuffer>* bufs) { 180 bufs->clear(); 181 182 if (!ReadDmaBufInfo(bufs)) { 183 printf("debugfs entry for dmabuf not available, using /proc/<pid>/fdinfo instead\n"); 184 } 185 186 std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir); 187 if (!dir) { 188 fprintf(stderr, "Failed to open /proc directory\n"); 189 bufs->clear(); 190 return false; 191 } 192 193 struct dirent* dent; 194 while ((dent = readdir(dir.get()))) { 195 if (dent->d_type != DT_DIR) continue; 196 197 int pid = atoi(dent->d_name); 198 if (pid == 0) { 199 continue; 200 } 201 202 if (!AppendDmaBufInfo(pid, bufs)) { 203 fprintf(stderr, "Unable to read dmabuf info for pid %d\n", pid); 204 bufs->clear(); 205 return false; 206 } 207 } 208 209 return true; 210 } 211 212 int main(int argc, char* argv[]) { 213 struct option longopts[] = {{"all", no_argument, nullptr, 'a'}, 214 {"help", no_argument, nullptr, 'h'}, 215 {0, 0, nullptr, 0}}; 216 217 int opt; 218 bool show_table = false; 219 while ((opt = getopt_long(argc, argv, "ah", longopts, nullptr)) != -1) { 220 switch (opt) { 221 case 'a': 222 show_table = true; 223 break; 224 case 'h': 225 usage(EXIT_SUCCESS); 226 default: 227 usage(EXIT_FAILURE); 228 } 229 } 230 231 pid_t pid = -1; 232 if (optind < argc) { 233 if (show_table) { 234 fprintf(stderr, "Invalid arguments: -a does not need arguments\n"); 235 usage(EXIT_FAILURE); 236 } 237 if (optind != (argc - 1)) { 238 fprintf(stderr, "Invalid arguments - only one [PID] argument is allowed\n"); 239 usage(EXIT_FAILURE); 240 } 241 pid = atoi(argv[optind]); 242 if (pid == 0) { 243 fprintf(stderr, "Invalid process id %s\n", argv[optind]); 244 usage(EXIT_FAILURE); 245 } 246 } 247 248 std::vector<DmaBuffer> bufs; 249 if (pid != -1) { 250 if (!ReadDmaBufInfo(pid, &bufs)) { 251 fprintf(stderr, "Unable to read dmabuf info for %d\n", pid); 252 exit(EXIT_FAILURE); 253 } 254 } else { 255 if (!ReadDmaBufs(&bufs)) exit(EXIT_FAILURE); 256 } 257 258 // Show the old dmabuf table, inode x process 259 if (show_table) { 260 PrintDmaBufTable(bufs); 261 return 0; 262 } 263 264 PrintDmaBufPerProcess(bufs); 265 266 return 0; 267 } 268