• 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 <inttypes.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #include <filesystem>
26 #include <fstream>
27 #include <memory>
28 #include <string>
29 #include <vector>
30 
31 #include <android-base/file.h>
32 #include <android-base/logging.h>
33 #include <android-base/parseint.h>
34 #include <android-base/stringprintf.h>
35 #include <android-base/strings.h>
36 #include <procinfo/process_map.h>
37 
38 #include <dmabufinfo/dmabuf_sysfs_stats.h>
39 #include <dmabufinfo/dmabufinfo.h>
40 
41 namespace android {
42 namespace dmabufinfo {
43 
FileIsDmaBuf(const std::string & path)44 static bool FileIsDmaBuf(const std::string& path) {
45     return ::android::base::StartsWith(path, "/dmabuf");
46 }
47 
48 enum FdInfoResult {
49     OK,
50     NOT_FOUND,
51     ERROR,
52 };
53 
ReadDmaBufFdInfo(pid_t pid,int fd,std::string * name,std::string * exporter,uint64_t * count,uint64_t * size,uint64_t * inode,bool * is_dmabuf_file,const std::string & procfs_path)54 static FdInfoResult ReadDmaBufFdInfo(pid_t pid, int fd, std::string* name, std::string* exporter,
55                              uint64_t* count, uint64_t* size, uint64_t* inode, bool* is_dmabuf_file,
56                              const std::string& procfs_path) {
57     std::string fdinfo =
58             ::android::base::StringPrintf("%s/%d/fdinfo/%d", procfs_path.c_str(), pid, fd);
59     std::ifstream fp(fdinfo);
60     if (!fp) {
61         if (errno == ENOENT) {
62             return NOT_FOUND;
63         }
64         PLOG(ERROR) << "Failed to open " << fdinfo;
65         return ERROR;
66     }
67 
68     for (std::string file_line; getline(fp, file_line);) {
69         const char* line = file_line.c_str();
70         switch (line[0]) {
71             case 'c':
72                 if (strncmp(line, "count:", 6) == 0) {
73                     const char* c = line + 6;
74                     *count = strtoull(c, nullptr, 10);
75                 }
76                 break;
77             case 'e':
78                 if (strncmp(line, "exp_name:", 9) == 0) {
79                     const char* c = line + 9;
80                     *exporter = ::android::base::Trim(c);
81                     *is_dmabuf_file = true;
82                 }
83                 break;
84             case 'n':
85                 if (strncmp(line, "name:", 5) == 0) {
86                     const char* c = line + 5;
87                     *name = ::android::base::Trim(c);
88                 }
89                 break;
90             case 's':
91                 if (strncmp(line, "size:", 5) == 0) {
92                     const char* c = line + 5;
93                     *size = strtoull(c, nullptr, 10);
94                 }
95                 break;
96             case 'i':
97                 if (strncmp(line, "ino:", 4) == 0) {
98                     const char* c = line + 4;
99                     *inode = strtoull(c, nullptr, 10);
100                 }
101                 break;
102         }
103     }
104 
105     return OK;
106 }
107 
108 // Public methods
ReadDmaBufFdRefs(int pid,std::vector<DmaBuffer> * dmabufs,const std::string & procfs_path)109 bool ReadDmaBufFdRefs(int pid, std::vector<DmaBuffer>* dmabufs,
110                              const std::string& procfs_path) {
111     constexpr char permission_err_msg[] =
112             "Failed to read fdinfo - requires either PTRACE_MODE_READ or root depending on "
113             "the device kernel";
114     static bool logged_permission_err = false;
115 
116     std::string fdinfo_dir_path =
117             ::android::base::StringPrintf("%s/%d/fdinfo", procfs_path.c_str(), pid);
118     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(fdinfo_dir_path.c_str()), &closedir);
119     if (!dir) {
120         // Don't log permission errors to reduce log spam on devices where fdinfo
121         // of other processes can only be read by root.
122         if (errno != EACCES) {
123             PLOG(ERROR) << "Failed to open " << fdinfo_dir_path << " directory";
124         } else if (!logged_permission_err) {
125             LOG(ERROR) << permission_err_msg;
126             logged_permission_err = true;
127         }
128         return false;
129     }
130     struct dirent* dent;
131     while ((dent = readdir(dir.get()))) {
132         int fd;
133         if (!::android::base::ParseInt(dent->d_name, &fd)) {
134             continue;
135         }
136 
137         // Set defaults in case the kernel doesn't give us the information
138         // we need in fdinfo
139         std::string name = "<unknown>";
140         std::string exporter = "<unknown>";
141         uint64_t count = 0;
142         uint64_t size = 0;
143         uint64_t inode = -1;
144         bool is_dmabuf_file = false;
145 
146         auto fdinfo_result = ReadDmaBufFdInfo(pid, fd, &name, &exporter, &count, &size, &inode,
147                                               &is_dmabuf_file, procfs_path);
148         if (fdinfo_result != OK) {
149             if (fdinfo_result == NOT_FOUND) {
150                 continue;
151             }
152             // Don't log permission errors to reduce log spam when the process doesn't
153             // have the PTRACE_MODE_READ permission.
154             if (errno != EACCES) {
155                 LOG(ERROR) << "Failed to read fd info for pid: " << pid << ", fd: " << fd;
156             } else if (!logged_permission_err) {
157                 LOG(ERROR) << permission_err_msg;
158                 logged_permission_err = true;
159             }
160             return false;
161         }
162         if (!is_dmabuf_file) {
163             continue;
164         }
165         if (inode == static_cast<uint64_t>(-1)) {
166             // Fallback to stat() on the fd path to get inode number
167             std::string fd_path =
168                     ::android::base::StringPrintf("%s/%d/fd/%d", procfs_path.c_str(), pid, fd);
169 
170             struct stat sb;
171             if (stat(fd_path.c_str(), &sb) < 0) {
172                 if (errno == ENOENT) {
173                   continue;
174                 }
175                 PLOG(ERROR) << "Failed to stat: " << fd_path;
176                 return false;
177             }
178 
179             inode = sb.st_ino;
180             // If root, calculate size from the allocated blocks.
181             size = sb.st_blocks * 512;
182         }
183 
184         auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
185                                 [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
186         if (buf != dmabufs->end()) {
187             if (buf->name() == "" || buf->name() == "<unknown>") buf->SetName(name);
188             if (buf->exporter() == "" || buf->exporter() == "<unknown>") buf->SetExporter(exporter);
189             if (buf->count() == 0) buf->SetCount(count);
190             buf->AddFdRef(pid);
191             continue;
192         }
193 
194         DmaBuffer& db = dmabufs->emplace_back(inode, size, count, exporter, name);
195         db.AddFdRef(pid);
196     }
197 
198     return true;
199 }
200 
ReadDmaBufMapRefs(pid_t pid,std::vector<DmaBuffer> * dmabufs,const std::string & procfs_path,const std::string & dmabuf_sysfs_path)201 bool ReadDmaBufMapRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs,
202                               const std::string& procfs_path,
203                               const std::string& dmabuf_sysfs_path) {
204     std::string mapspath = ::android::base::StringPrintf("%s/%d/maps", procfs_path.c_str(), pid);
205     std::ifstream fp(mapspath);
206     if (!fp) {
207         LOG(ERROR) << "Failed to open " << mapspath << " for pid: " << pid;
208         return false;
209     }
210 
211     // Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
212     // if it was already found. If it wasn't create a new one and append it to 'dmabufs'
213     auto account_dmabuf = [&](const android::procinfo::MapInfo& mapinfo) {
214         // no need to look into this mapping if it is not dmabuf
215         if (!FileIsDmaBuf(mapinfo.name)) {
216             return;
217         }
218 
219         auto buf = std::find_if(
220                 dmabufs->begin(), dmabufs->end(),
221                 [&mapinfo](const DmaBuffer& dbuf) { return dbuf.inode() == mapinfo.inode; });
222         if (buf != dmabufs->end()) {
223             buf->AddMapRef(pid);
224             return;
225         }
226 
227         // We have a new buffer, but unknown count and name and exporter name
228         // Try to lookup exporter name in sysfs
229         std::string exporter;
230         bool sysfs_stats = ReadBufferExporter(mapinfo.inode, &exporter, dmabuf_sysfs_path);
231         if (!sysfs_stats) {
232             exporter = "<unknown>";
233         }
234 
235         // Using the VMA range as the size of the buffer can be misleading,
236         // due to partially mapped buffers or VMAs that extend beyond the
237         // buffer size.
238         //
239         // Attempt to retrieve the real buffer size from sysfs.
240         uint64_t size = 0;
241         if (!sysfs_stats || !ReadBufferSize(mapinfo.inode, &size, dmabuf_sysfs_path)) {
242             size = mapinfo.end - mapinfo.start;
243         }
244 
245         DmaBuffer& dbuf = dmabufs->emplace_back(mapinfo.inode, size, 0, exporter, "<unknown>");
246         dbuf.AddMapRef(pid);
247     };
248 
249     for (std::string line; getline(fp, line);) {
250         if (!::android::procinfo::ReadMapFileContent(line.data(), account_dmabuf)) {
251             LOG(ERROR) << "Failed to parse " << mapspath << " for pid: " << pid;
252             return false;
253         }
254     }
255 
256     return true;
257 }
258 
ReadDmaBufInfo(pid_t pid,std::vector<DmaBuffer> * dmabufs,bool read_fdrefs,const std::string & procfs_path,const std::string & dmabuf_sysfs_path)259 bool ReadDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs, bool read_fdrefs,
260                     const std::string& procfs_path, const std::string& dmabuf_sysfs_path) {
261     dmabufs->clear();
262 
263     if (read_fdrefs) {
264         if (!ReadDmaBufFdRefs(pid, dmabufs, procfs_path)) {
265             LOG(ERROR) << "Failed to read dmabuf fd references";
266             return false;
267         }
268     }
269 
270     if (!ReadDmaBufMapRefs(pid, dmabufs, procfs_path, dmabuf_sysfs_path)) {
271         LOG(ERROR) << "Failed to read dmabuf map references";
272         return false;
273     }
274     return true;
275 }
276 
ReadProcfsDmaBufs(std::vector<DmaBuffer> * bufs)277 bool ReadProcfsDmaBufs(std::vector<DmaBuffer>* bufs) {
278     bufs->clear();
279 
280     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
281     if (!dir) {
282         LOG(ERROR) << "Failed to open /proc directory";
283         bufs->clear();
284         return false;
285     }
286 
287     struct dirent* dent;
288     while ((dent = readdir(dir.get()))) {
289         if (dent->d_type != DT_DIR) continue;
290 
291         int pid = atoi(dent->d_name);
292         if (pid == 0) {
293             continue;
294         }
295 
296         if (!ReadDmaBufFdRefs(pid, bufs)) {
297             LOG(ERROR) << "Failed to read dmabuf fd references for pid " << pid;
298         }
299 
300         if (!ReadDmaBufMapRefs(pid, bufs)) {
301             LOG(ERROR) << "Failed to read dmabuf map references for pid " << pid;
302         }
303     }
304 
305     return true;
306 }
307 
308 }  // namespace dmabufinfo
309 }  // namespace android
310