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