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