• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <memory>
29 #include <set>
30 #include <sstream>
31 #include <string>
32 #include <vector>
33 
34 #include <android-base/stringprintf.h>
35 #include <dmabufinfo/dmabuf_sysfs_stats.h>
36 #include <dmabufinfo/dmabufinfo.h>
37 #include <meminfo/procmeminfo.h>
38 
39 #include "include/dmabuf_output_helper.h"
40 
41 using DmaBuffer = ::android::dmabufinfo::DmaBuffer;
42 using Format = ::android::meminfo::Format;
43 
44 std::unique_ptr<DmabufOutputHelper> outputHelper;
45 
usage(int exit_status)46 [[noreturn]] static void usage(int exit_status) {
47     fprintf(stderr,
48             "Usage: %s [-abh] [PID] [-o <raw|csv>]\n"
49             "-a\t show all dma buffers (ion) in big table, [buffer x process] grid \n"
50             "-b\t show DMA-BUF per-buffer, per-exporter and per-device statistics \n"
51             "-o\t [raw][csv] print output in the specified format.\n"
52             "-h\t show this help\n"
53             "  \t If PID is supplied, the dmabuf information for that process is shown.\n"
54             "  \t Per-buffer DMA-BUF stats do not take an argument.\n",
55             getprogname());
56 
57     exit(exit_status);
58 }
59 
GetProcessComm(const pid_t pid)60 static std::string GetProcessComm(const pid_t pid) {
61     std::string pid_path = android::base::StringPrintf("/proc/%d/comm", pid);
62     std::ifstream in{pid_path};
63     if (!in) return std::string("N/A");
64     std::string line;
65     std::getline(in, line);
66     if (!in) return std::string("N/A");
67     return line;
68 }
69 
PrintDmaBufTable(const std::vector<DmaBuffer> & bufs)70 static void PrintDmaBufTable(const std::vector<DmaBuffer>& bufs) {
71     if (bufs.empty()) {
72         printf("dmabuf info not found ¯\\_(ツ)_/¯\n");
73         return;
74     }
75 
76     printf("\n----------------------- DMA-BUF Table buffer x process --------------------------\n");
77 
78     // Find all unique pids in the input vector, create a set
79     std::set<pid_t> pid_set;
80     for (auto& buf : bufs) {
81         pid_set.insert(buf.pids().begin(), buf.pids().end());
82     }
83 
84     outputHelper->BufTableMainHeaders();
85     for (auto pid : pid_set) {
86         std::string process = GetProcessComm(pid);
87         outputHelper->BufTableProcessHeader(pid, process);
88     }
89     printf("\n");
90 
91     // holds per-process dmabuf size in kB
92     std::map<pid_t, uint64_t> per_pid_size = {};
93     uint64_t dmabuf_total_size = 0;
94 
95     // Iterate through all dmabufs and collect per-process sizes, refs
96     for (auto& buf : bufs) {
97         outputHelper->BufTableStats(buf);
98         // Iterate through each process to find out per-process references for each buffer,
99         // gather total size used by each process etc.
100         for (pid_t pid : pid_set) {
101             int pid_fdrefs = 0, pid_maprefs = 0;
102             if (buf.fdrefs().count(pid) == 1) {
103                 // Get the total number of ref counts the process is holding
104                 // on this buffer. We don't differentiate between mmap or fd.
105                 pid_fdrefs += buf.fdrefs().at(pid);
106             }
107             if (buf.maprefs().count(pid) == 1) {
108                 pid_maprefs += buf.maprefs().at(pid);
109             }
110 
111             if (pid_fdrefs || pid_maprefs) {
112                 // Add up the per-pid total size. Note that if a buffer is mapped
113                 // in 2 different processes, the size will be shown as mapped or opened
114                 // in both processes. This is intended for visibility.
115                 //
116                 // If one wants to get the total *unique* dma buffers, they can simply
117                 // sum the size of all dma bufs shown by the tool
118                 per_pid_size[pid] += buf.size() / 1024;
119             }
120             outputHelper->BufTableProcessSize(pid_fdrefs, pid_maprefs);
121         }
122         dmabuf_total_size += buf.size() / 1024;
123         printf("\n");
124     }
125 
126     printf("------------------------------------\n");
127     outputHelper->BufTableTotalHeader();
128     for (auto pid : pid_set) {
129         std::string process = GetProcessComm(pid);
130         outputHelper->BufTableTotalProcessHeader(pid, process);
131     }
132 
133     outputHelper->BufTableTotalStats(dmabuf_total_size);
134     for (auto const& [pid, pid_size] : per_pid_size) {
135         outputHelper->BufTableTotalProcessStats(pid_size);
136     }
137     printf("\n");
138 }
139 
PrintDmaBufPerProcess(const std::vector<DmaBuffer> & bufs)140 static void PrintDmaBufPerProcess(const std::vector<DmaBuffer>& bufs) {
141     if (bufs.empty()) {
142         printf("dmabuf info not found ¯\\_(ツ)_/¯\n");
143         return;
144     }
145 
146     // Create a reverse map from pid to dmabufs
147     std::unordered_map<pid_t, std::set<ino_t>> pid_to_inodes = {};
148     uint64_t userspace_size = 0;  // Size of userspace dmabufs in the system
149     for (auto& buf : bufs) {
150         for (auto pid : buf.pids()) {
151             pid_to_inodes[pid].insert(buf.inode());
152         }
153         userspace_size += buf.size();
154     }
155     // Create an inode to dmabuf map. We know inodes are unique..
156     std::unordered_map<ino_t, DmaBuffer> inode_to_dmabuf;
157     for (auto buf : bufs) {
158         inode_to_dmabuf[buf.inode()] = buf;
159     }
160 
161     uint64_t total_rss = 0, total_pss = 0;
162     for (auto& [pid, inodes] : pid_to_inodes) {
163         uint64_t pss = 0;
164         uint64_t rss = 0;
165 
166         outputHelper->PerProcessHeader(GetProcessComm(pid), pid);
167 
168         for (auto& inode : inodes) {
169             DmaBuffer& buf = inode_to_dmabuf[inode];
170             outputHelper->PerProcessBufStats(buf);
171             rss += buf.size();
172             pss += buf.Pss();
173         }
174 
175         outputHelper->PerProcessTotalStat(pss, rss);
176         printf("----------------------\n");
177         total_rss += rss;
178         total_pss += pss;
179     }
180 
181     uint64_t kernel_rss = 0;  // Total size of dmabufs NOT mapped or opened by a process
182     if (android::dmabufinfo::GetDmabufTotalExportedKb(&kernel_rss)) {
183         kernel_rss *= 1024;  // KiB -> bytes
184         if (kernel_rss >= userspace_size)
185             kernel_rss -= userspace_size;
186         else
187             printf("Warning: Total dmabufs < userspace dmabufs\n");
188     } else {
189         printf("Warning: Could not get total exported dmabufs. Kernel size will be 0.\n");
190     }
191 
192     outputHelper->TotalProcessesStats(total_rss, total_pss, userspace_size, kernel_rss);
193 }
194 
DumpDmabufSysfsStats()195 static void DumpDmabufSysfsStats() {
196     android::dmabufinfo::DmabufSysfsStats stats;
197 
198     if (!android::dmabufinfo::GetDmabufSysfsStats(&stats)) {
199         printf("Unable to read DMA-BUF sysfs stats from device\n");
200         return;
201     }
202 
203     auto buffer_stats = stats.buffer_stats();
204     auto exporter_stats = stats.exporter_info();
205 
206     const char separator[] = "-----------------------";
207     printf("\n\n%s DMA-BUF per-buffer stats %s\n", separator, separator);
208     outputHelper->PerBufferHeader();
209     for (const auto& buf : buffer_stats) {
210         outputHelper->PerBufferStats(buf);
211     }
212 
213     printf("\n\n%s DMA-BUF exporter stats %s\n", separator, separator);
214     outputHelper->ExporterHeader();
215     for (auto const& [exporter_name, dmaBufTotal] : exporter_stats) {
216         outputHelper->ExporterStats(exporter_name, dmaBufTotal);
217     }
218 
219     printf("\n\n%s DMA-BUF total stats %s\n", separator, separator);
220     outputHelper->SysfsBufTotalStats(stats);
221 }
222 
main(int argc,char * argv[])223 int main(int argc, char* argv[]) {
224     struct option longopts[] = {{"all", no_argument, nullptr, 'a'},
225                                 {"per-buffer", no_argument, nullptr, 'b'},
226                                 {"help", no_argument, nullptr, 'h'},
227                                 {0, 0, nullptr, 0}};
228 
229     int opt;
230     bool show_table = false;
231     bool show_dmabuf_sysfs_stats = false;
232     Format format = Format::RAW;
233     while ((opt = getopt_long(argc, argv, "abho:", longopts, nullptr)) != -1) {
234         switch (opt) {
235             case 'a':
236                 show_table = true;
237                 break;
238             case 'b':
239                 show_dmabuf_sysfs_stats = true;
240                 break;
241             case 'o':
242                 format = android::meminfo::GetFormat(optarg);
243                 switch (format) {
244                     case Format::CSV:
245                         outputHelper = std::make_unique<CsvOutput>();
246                         break;
247                     case Format::RAW:
248                         outputHelper = std::make_unique<RawOutput>();
249                         break;
250                     default:
251                         fprintf(stderr, "Invalid output format.\n");
252                         usage(EXIT_FAILURE);
253                 }
254                 break;
255             case 'h':
256                 usage(EXIT_SUCCESS);
257             default:
258                 usage(EXIT_FAILURE);
259         }
260     }
261 
262     if (!outputHelper) {
263         outputHelper = std::make_unique<RawOutput>();
264     }
265 
266     pid_t pid = -1;
267     if (optind < argc) {
268         if (show_table || show_dmabuf_sysfs_stats) {
269             fprintf(stderr, "Invalid arguments: -a and -b does not need arguments\n");
270             usage(EXIT_FAILURE);
271         }
272         if (optind != (argc - 1)) {
273             fprintf(stderr, "Invalid arguments - only one [PID] argument is allowed\n");
274             usage(EXIT_FAILURE);
275         }
276         pid = atoi(argv[optind]);
277         if (pid == 0) {
278             fprintf(stderr, "Invalid process id %s\n", argv[optind]);
279             usage(EXIT_FAILURE);
280         }
281     }
282 
283     if (show_dmabuf_sysfs_stats) {
284         DumpDmabufSysfsStats();
285     }
286 
287     if (!show_table && show_dmabuf_sysfs_stats) {
288         return 0;
289     }
290 
291     std::vector<DmaBuffer> bufs;
292     if (pid != -1) {
293         if (!ReadDmaBufInfo(pid, &bufs)) {
294             fprintf(stderr, "Unable to read dmabuf info for %d\n", pid);
295             exit(EXIT_FAILURE);
296         }
297     } else {
298         if (!ReadProcfsDmaBufs(&bufs)) {
299             fprintf(stderr, "Failed to ReadProcfsDmaBufs, check logcat for info\n");
300             exit(EXIT_FAILURE);
301         }
302     }
303 
304     // Show the old dmabuf table, inode x process
305     if (show_table) {
306         printf("%s", (show_dmabuf_sysfs_stats) ? "\n\n" : "");
307         PrintDmaBufTable(bufs);
308         return 0;
309     }
310 
311     if (!show_table && !show_dmabuf_sysfs_stats) {
312         PrintDmaBufPerProcess(bufs);
313     }
314 
315     return 0;
316 }
317