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/dmabufinfo.h>
38
39 namespace android {
40 namespace dmabufinfo {
41
FileIsDmaBuf(const std::string & path)42 static bool FileIsDmaBuf(const std::string& path) {
43 return ::android::base::StartsWith(path, "/dmabuf");
44 }
45
ReadDmaBufFdInfo(pid_t pid,int fd,std::string * name,std::string * exporter,uint64_t * count)46 static bool ReadDmaBufFdInfo(pid_t pid, int fd, std::string* name, std::string* exporter,
47 uint64_t* count) {
48 std::string fdinfo = ::android::base::StringPrintf("/proc/%d/fdinfo/%d", pid, fd);
49 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fdinfo.c_str(), "re"), fclose};
50 if (fp == nullptr) {
51 LOG(ERROR) << "Failed to open dmabuf info from debugfs";
52 return false;
53 }
54
55 char* line = nullptr;
56 size_t len = 0;
57 while (getline(&line, &len, fp.get()) > 0) {
58 switch (line[0]) {
59 case 'c':
60 if (strncmp(line, "count:", 6) == 0) {
61 char* c = line + 6;
62 *count = strtoull(c, nullptr, 10);
63 }
64 break;
65 case 'e':
66 if (strncmp(line, "exp_name:", 9) == 0) {
67 char* c = line + 9;
68 *exporter = ::android::base::Trim(c);
69 }
70 break;
71 case 'n':
72 if (strncmp(line, "name:", 5) == 0) {
73 char* c = line + 5;
74 *name = ::android::base::Trim(std::string(c));
75 }
76 break;
77 }
78 }
79
80 free(line);
81 return true;
82 }
83
84 // TODO: std::filesystem::is_symlink fails to link on vendor code,
85 // forcing this workaround.
86 // Move back to libc++fs once it is vendor-available. See b/124012728
is_symlink(const char * filename)87 static bool is_symlink(const char *filename)
88 {
89 struct stat p_statbuf;
90 if (lstat(filename, &p_statbuf) < 0) {
91 return false;
92 }
93 if (S_ISLNK(p_statbuf.st_mode) == 1) {
94 return true;
95 }
96 return false;
97 }
98
ReadDmaBufFdRefs(pid_t pid,std::vector<DmaBuffer> * dmabufs)99 static bool ReadDmaBufFdRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
100 std::string fdpath = ::android::base::StringPrintf("/proc/%d/fd", pid);
101
102 std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(fdpath.c_str()), closedir);
103 if (!dir) {
104 LOG(ERROR) << "Failed to open " << fdpath << " directory" << std::endl;
105 return false;
106 }
107 struct dirent* dent;
108 while ((dent = readdir(dir.get()))) {
109 std::string path =
110 ::android::base::StringPrintf("%s/%s", fdpath.c_str(), dent->d_name);
111
112 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..") ||
113 !is_symlink(path.c_str())) {
114 continue;
115 }
116
117 std::string target;
118 if (!::android::base::Readlink(path, &target)) {
119 LOG(ERROR) << "Failed to find target for symlink: " << path;
120 return false;
121 }
122
123 if (!FileIsDmaBuf(target)) {
124 continue;
125 }
126
127 int fd;
128 if (!::android::base::ParseInt(dent->d_name, &fd)) {
129 LOG(ERROR) << "Dmabuf fd: " << path << " is invalid";
130 return false;
131 }
132
133 // Set defaults in case the kernel doesn't give us the information
134 // we need in fdinfo
135 std::string name = "<unknown>";
136 std::string exporter = "<unknown>";
137 uint64_t count = 0;
138 if (!ReadDmaBufFdInfo(pid, fd, &name, &exporter, &count)) {
139 LOG(ERROR) << "Failed to read fdinfo for: " << path;
140 return false;
141 }
142
143 struct stat sb;
144 if (stat(path.c_str(), &sb) < 0) {
145 PLOG(ERROR) << "Failed to stat: " << path;
146 return false;
147 }
148
149 uint64_t inode = sb.st_ino;
150 auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
151 [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
152 if (buf != dmabufs->end()) {
153 if (buf->name() == "" || buf->name() == "<unknown>") buf->SetName(name);
154 if (buf->exporter() == "" || buf->exporter() == "<unknown>") buf->SetExporter(exporter);
155 if (buf->count() == 0) buf->SetCount(count);
156 buf->AddFdRef(pid);
157 continue;
158 }
159
160 DmaBuffer& db = dmabufs->emplace_back(sb.st_ino, sb.st_blocks * 512, count, exporter, name);
161 db.AddFdRef(pid);
162 }
163
164 return true;
165 }
166
ReadDmaBufMapRefs(pid_t pid,std::vector<DmaBuffer> * dmabufs)167 static bool ReadDmaBufMapRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
168 std::string mapspath = ::android::base::StringPrintf("/proc/%d/maps", pid);
169 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mapspath.c_str(), "re"), fclose};
170 if (fp == nullptr) {
171 LOG(ERROR) << "Failed to open maps for pid: " << pid;
172 return false;
173 }
174
175 char* line = nullptr;
176 size_t len = 0;
177
178 // Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
179 // if it was already found. If it wasn't create a new one and append it to 'dmabufs'
180 auto account_dmabuf = [&](uint64_t start, uint64_t end, uint16_t /* flags */,
181 uint64_t /* pgoff */, ino_t inode, const char* name) {
182 // no need to look into this mapping if it is not dmabuf
183 if (!FileIsDmaBuf(std::string(name))) {
184 return;
185 }
186
187 auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
188 [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
189 if (buf != dmabufs->end()) {
190 buf->AddMapRef(pid);
191 return;
192 }
193
194 // We have a new buffer, but unknown count and name
195 DmaBuffer& dbuf = dmabufs->emplace_back(inode, end - start, 0, "<unknown>", "<unknown>");
196 dbuf.AddMapRef(pid);
197 };
198
199 while (getline(&line, &len, fp.get()) > 0) {
200 if (!::android::procinfo::ReadMapFileContent(line, account_dmabuf)) {
201 LOG(ERROR) << "Failed t parse maps for pid: " << pid;
202 return false;
203 }
204 }
205
206 free(line);
207 return true;
208 }
209
210 // Public methods
ReadDmaBufInfo(std::vector<DmaBuffer> * dmabufs,const std::string & path)211 bool ReadDmaBufInfo(std::vector<DmaBuffer>* dmabufs, const std::string& path) {
212 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
213 if (fp == nullptr) {
214 LOG(ERROR) << "Failed to open dmabuf info from debugfs";
215 return false;
216 }
217
218 char* line = nullptr;
219 size_t len = 0;
220 dmabufs->clear();
221 while (getline(&line, &len, fp.get()) > 0) {
222 // The new dmabuf bufinfo format adds inode number and a name at the end
223 // We are looking for lines as follows:
224 // size flags mode count exp_name ino name
225 // 01048576 00000002 00000007 00000001 ion 00018758 CAMERA
226 // 01048576 00000002 00000007 00000001 ion 00018758
227 uint64_t size, count;
228 char* exporter_name = nullptr;
229 ino_t inode;
230 char* name = nullptr;
231 int matched = sscanf(line, "%" SCNu64 "%*x %*x %" SCNu64 " %ms %lu %ms", &size, &count,
232 &exporter_name, &inode, &name);
233 if (matched < 4) {
234 continue;
235 }
236 dmabufs->emplace_back(inode, size, count, exporter_name, matched > 4 ? name : "");
237 free(exporter_name);
238 free(name);
239 }
240
241 free(line);
242
243 return true;
244 }
245
ReadDmaBufInfo(pid_t pid,std::vector<DmaBuffer> * dmabufs)246 bool ReadDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
247 dmabufs->clear();
248 return AppendDmaBufInfo(pid, dmabufs);
249 }
250
AppendDmaBufInfo(pid_t pid,std::vector<DmaBuffer> * dmabufs)251 bool AppendDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
252 if (!ReadDmaBufFdRefs(pid, dmabufs)) {
253 LOG(ERROR) << "Failed to read dmabuf fd references";
254 return false;
255 }
256
257 if (!ReadDmaBufMapRefs(pid, dmabufs)) {
258 LOG(ERROR) << "Failed to read dmabuf map references";
259 return false;
260 }
261 return true;
262 }
263
264 } // namespace dmabufinfo
265 } // namespace android
266