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 <memory>
27 #include <string>
28 #include <vector>
29
30 #include <android-base/file.h>
31 #include <android-base/logging.h>
32 #include <android-base/parseint.h>
33 #include <android-base/stringprintf.h>
34 #include <android-base/strings.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 ::android::base::StartsWith(path, "/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 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fdinfo.c_str(), "re"), fclose};
59 if (fp == nullptr) {
60 if (errno == ENOENT) {
61 return NOT_FOUND;
62 }
63 PLOG(ERROR) << "Failed to open " << fdinfo;
64 return ERROR;
65 }
66
67 char* line = nullptr;
68 size_t len = 0;
69 while (getline(&line, &len, fp.get()) > 0) {
70 switch (line[0]) {
71 case 'c':
72 if (strncmp(line, "count:", 6) == 0) {
73 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 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 char* c = line + 5;
87 *name = ::android::base::Trim(std::string(c));
88 }
89 break;
90 case 's':
91 if (strncmp(line, "size:", 5) == 0) {
92 char* c = line + 5;
93 *size = strtoull(c, nullptr, 10);
94 }
95 break;
96 case 'i':
97 if (strncmp(line, "ino:", 4) == 0) {
98 char* c = line + 4;
99 *inode = strtoull(c, nullptr, 10);
100 }
101 break;
102 }
103 }
104
105 free(line);
106 return OK;
107 }
108
109 // Public methods
ReadDmaBufFdRefs(int pid,std::vector<DmaBuffer> * dmabufs,const std::string & procfs_path)110 bool ReadDmaBufFdRefs(int pid, std::vector<DmaBuffer>* dmabufs,
111 const std::string& procfs_path) {
112 constexpr char permission_err_msg[] =
113 "Failed to read fdinfo - requires either PTRACE_MODE_READ or root depending on "
114 "the device kernel";
115 static bool logged_permission_err = false;
116
117 std::string fdinfo_dir_path =
118 ::android::base::StringPrintf("%s/%d/fdinfo", procfs_path.c_str(), pid);
119 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(fdinfo_dir_path.c_str()), &closedir);
120 if (!dir) {
121 // Don't log permission errors to reduce log spam on devices where fdinfo
122 // of other processes can only be read by root.
123 if (errno != EACCES) {
124 PLOG(ERROR) << "Failed to open " << fdinfo_dir_path << " directory";
125 } else if (!logged_permission_err) {
126 LOG(ERROR) << permission_err_msg;
127 logged_permission_err = true;
128 }
129 return false;
130 }
131 struct dirent* dent;
132 while ((dent = readdir(dir.get()))) {
133 int fd;
134 if (!::android::base::ParseInt(dent->d_name, &fd)) {
135 continue;
136 }
137
138 // Set defaults in case the kernel doesn't give us the information
139 // we need in fdinfo
140 std::string name = "<unknown>";
141 std::string exporter = "<unknown>";
142 uint64_t count = 0;
143 uint64_t size = 0;
144 uint64_t inode = -1;
145 bool is_dmabuf_file = false;
146
147 auto fdinfo_result = ReadDmaBufFdInfo(pid, fd, &name, &exporter, &count, &size, &inode,
148 &is_dmabuf_file, procfs_path);
149 if (fdinfo_result != OK) {
150 if (fdinfo_result == NOT_FOUND) {
151 continue;
152 }
153 // Don't log permission errors to reduce log spam when the process doesn't
154 // have the PTRACE_MODE_READ permission.
155 if (errno != EACCES) {
156 LOG(ERROR) << "Failed to read fd info for pid: " << pid << ", fd: " << fd;
157 } else if (!logged_permission_err) {
158 LOG(ERROR) << permission_err_msg;
159 logged_permission_err = true;
160 }
161 return false;
162 }
163 if (!is_dmabuf_file) {
164 continue;
165 }
166 if (inode == static_cast<uint64_t>(-1)) {
167 // Fallback to stat() on the fd path to get inode number
168 std::string fd_path =
169 ::android::base::StringPrintf("%s/%d/fd/%d", procfs_path.c_str(), pid, fd);
170
171 struct stat sb;
172 if (stat(fd_path.c_str(), &sb) < 0) {
173 if (errno == ENOENT) {
174 continue;
175 }
176 PLOG(ERROR) << "Failed to stat: " << fd_path;
177 return false;
178 }
179
180 inode = sb.st_ino;
181 // If root, calculate size from the allocated blocks.
182 size = sb.st_blocks * 512;
183 }
184
185 auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
186 [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
187 if (buf != dmabufs->end()) {
188 if (buf->name() == "" || buf->name() == "<unknown>") buf->SetName(name);
189 if (buf->exporter() == "" || buf->exporter() == "<unknown>") buf->SetExporter(exporter);
190 if (buf->count() == 0) buf->SetCount(count);
191 buf->AddFdRef(pid);
192 continue;
193 }
194
195 DmaBuffer& db = dmabufs->emplace_back(inode, size, count, exporter, name);
196 db.AddFdRef(pid);
197 }
198
199 return true;
200 }
201
ReadDmaBufMapRefs(pid_t pid,std::vector<DmaBuffer> * dmabufs,const std::string & procfs_path,const std::string & dmabuf_sysfs_path)202 bool ReadDmaBufMapRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs,
203 const std::string& procfs_path,
204 const std::string& dmabuf_sysfs_path) {
205 std::string mapspath = ::android::base::StringPrintf("%s/%d/maps", procfs_path.c_str(), pid);
206 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mapspath.c_str(), "re"), fclose};
207 if (fp == nullptr) {
208 LOG(ERROR) << "Failed to open maps for pid: " << pid;
209 return false;
210 }
211
212 char* line = nullptr;
213 size_t len = 0;
214
215 // Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
216 // if it was already found. If it wasn't create a new one and append it to 'dmabufs'
217 auto account_dmabuf = [&](const android::procinfo::MapInfo& mapinfo) {
218 // no need to look into this mapping if it is not dmabuf
219 if (!FileIsDmaBuf(mapinfo.name)) {
220 return;
221 }
222
223 auto buf = std::find_if(
224 dmabufs->begin(), dmabufs->end(),
225 [&mapinfo](const DmaBuffer& dbuf) { return dbuf.inode() == mapinfo.inode; });
226 if (buf != dmabufs->end()) {
227 buf->AddMapRef(pid);
228 return;
229 }
230
231 // We have a new buffer, but unknown count and name and exporter name
232 // Try to lookup exporter name in sysfs
233 std::string exporter;
234 if (!ReadBufferExporter(mapinfo.inode, &exporter, dmabuf_sysfs_path)) {
235 exporter = "<unknown>";
236 }
237 DmaBuffer& dbuf = dmabufs->emplace_back(mapinfo.inode, mapinfo.end - mapinfo.start, 0,
238 exporter, "<unknown>");
239 dbuf.AddMapRef(pid);
240 };
241
242 while (getline(&line, &len, fp.get()) > 0) {
243 if (!::android::procinfo::ReadMapFileContent(line, account_dmabuf)) {
244 LOG(ERROR) << "Failed to parse maps for pid: " << pid;
245 return false;
246 }
247 }
248
249 free(line);
250 return true;
251 }
252
ReadDmaBufInfo(std::vector<DmaBuffer> * dmabufs,const std::string & path)253 bool ReadDmaBufInfo(std::vector<DmaBuffer>* dmabufs, const std::string& path) {
254 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
255 if (fp == nullptr) {
256 LOG(ERROR) << "Failed to open dmabuf info from debugfs";
257 return false;
258 }
259
260 char* line = nullptr;
261 size_t len = 0;
262 dmabufs->clear();
263 while (getline(&line, &len, fp.get()) > 0) {
264 // The new dmabuf bufinfo format adds inode number and a name at the end
265 // We are looking for lines as follows:
266 // size flags mode count exp_name ino name
267 // 01048576 00000002 00000007 00000001 ion 00018758 CAMERA
268 // 01048576 00000002 00000007 00000001 ion 00018758
269 uint64_t size, count, inode;
270 char* exporter_name = nullptr;
271 char* name = nullptr;
272 int matched = sscanf(line, "%" SCNu64 "%*x %*x %" SCNu64 " %ms %" SCNu64 " %ms", &size,
273 &count, &exporter_name, &inode, &name);
274 if (matched < 4) {
275 continue;
276 }
277 dmabufs->emplace_back((ino_t)inode, size, count, exporter_name, matched > 4 ? name : "");
278 free(exporter_name);
279 free(name);
280 }
281
282 free(line);
283
284 return true;
285 }
286
ReadDmaBufInfo(pid_t pid,std::vector<DmaBuffer> * dmabufs,bool read_fdrefs,const std::string & procfs_path,const std::string & dmabuf_sysfs_path)287 bool ReadDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs, bool read_fdrefs,
288 const std::string& procfs_path, const std::string& dmabuf_sysfs_path) {
289 dmabufs->clear();
290
291 if (read_fdrefs) {
292 if (!ReadDmaBufFdRefs(pid, dmabufs, procfs_path)) {
293 LOG(ERROR) << "Failed to read dmabuf fd references";
294 return false;
295 }
296 }
297
298 if (!ReadDmaBufMapRefs(pid, dmabufs, procfs_path, dmabuf_sysfs_path)) {
299 LOG(ERROR) << "Failed to read dmabuf map references";
300 return false;
301 }
302 return true;
303 }
304
305 } // namespace dmabufinfo
306 } // namespace android
307