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