blob: 439cf687321d00e35f5a4e51589b2c44ca3c0f4b [file] [log] [blame]
Erick Reyesd2918fe2019-01-24 15:26:23 -08001/*
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
Erick Reyes2f6792e2019-02-01 19:19:12 -080017#include <dirent.h>
Erick Reyesd2918fe2019-01-24 15:26:23 -080018#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
Erick Reyes2f6792e2019-02-01 19:19:12 -080037#include <dmabufinfo/dmabufinfo.h>
38
Erick Reyesd2918fe2019-01-24 15:26:23 -080039namespace android {
40namespace dmabufinfo {
41
42static bool FileIsDmaBuf(const std::string& path) {
43 return ::android::base::StartsWith(path, "/dmabuf");
44}
45
46static 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
Erick Reyes2f6792e2019-02-01 19:19:12 -080084// 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
87static 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
Erick Reyesd2918fe2019-01-24 15:26:23 -080099static bool ReadDmaBufFdRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
100 std::string fdpath = ::android::base::StringPrintf("/proc/%d/fd", pid);
Erick Reyes2f6792e2019-02-01 19:19:12 -0800101
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())) {
Erick Reyesd2918fe2019-01-24 15:26:23 -0800114 continue;
115 }
116
117 std::string target;
Erick Reyes2f6792e2019-02-01 19:19:12 -0800118 if (!::android::base::Readlink(path, &target)) {
119 LOG(ERROR) << "Failed to find target for symlink: " << path;
Erick Reyesd2918fe2019-01-24 15:26:23 -0800120 return false;
121 }
122
123 if (!FileIsDmaBuf(target)) {
124 continue;
125 }
126
127 int fd;
Erick Reyes2f6792e2019-02-01 19:19:12 -0800128 if (!::android::base::ParseInt(dent->d_name, &fd)) {
129 LOG(ERROR) << "Dmabuf fd: " << path << " is invalid";
Erick Reyesd2918fe2019-01-24 15:26:23 -0800130 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)) {
Erick Reyes2f6792e2019-02-01 19:19:12 -0800139 LOG(ERROR) << "Failed to read fdinfo for: " << path;
Erick Reyesd2918fe2019-01-24 15:26:23 -0800140 return false;
141 }
142
143 struct stat sb;
Erick Reyes2f6792e2019-02-01 19:19:12 -0800144 if (stat(path.c_str(), &sb) < 0) {
145 PLOG(ERROR) << "Failed to stat: " << path;
Erick Reyesd2918fe2019-01-24 15:26:23 -0800146 return false;
147 }
148
Erick Reyes302a2592019-01-28 19:53:30 -0800149 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>")
154 buf->SetName(name);
155 if (buf->exporter() == "" || buf->exporter() == "<unknown>")
156 buf->SetExporter(exporter);
157 if (buf->count() == 0)
158 buf->SetCount(count);
159 buf->AddFdRef(pid);
Erick Reyese3518d42019-01-30 12:02:26 -0800160 continue;
Erick Reyes302a2592019-01-28 19:53:30 -0800161 }
162
163 DmaBuffer& db =
Erick Reyesd2918fe2019-01-24 15:26:23 -0800164 dmabufs->emplace_back(sb.st_ino, sb.st_blocks * 512, count, exporter, name);
Erick Reyes302a2592019-01-28 19:53:30 -0800165 db.AddFdRef(pid);
Erick Reyesd2918fe2019-01-24 15:26:23 -0800166 }
167
168 return true;
169}
170
171static bool ReadDmaBufMapRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
172 std::string mapspath = ::android::base::StringPrintf("/proc/%d/maps", pid);
173 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mapspath.c_str(), "re"), fclose};
174 if (fp == nullptr) {
175 LOG(ERROR) << "Failed to open maps for pid: " << pid;
176 return false;
177 }
178
179 char* line = nullptr;
180 size_t len = 0;
181
182 // Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
183 // if it was already found. If it wasn't create a new one and append it to 'dmabufs'
184 auto account_dmabuf = [&](uint64_t start, uint64_t end, uint16_t /* flags */,
185 uint64_t /* pgoff */, const char* name) {
186 // no need to look into this mapping if it is not dmabuf
187 if (!FileIsDmaBuf(std::string(name))) {
188 return;
189 }
190
191 // TODO (b/123532375) : Add inode number to the callback of ReadMapFileContent.
192 //
193 // Workaround: we know 'name' points to the name at the end of 'line'.
194 // We use that to backtrack and pick up the inode number from the line as well.
195 // start end flag pgoff mj:mn inode name
196 // 00400000-00409000 r-xp 00000000 00:00 426998 /dmabuf (deleted)
197 const char* p = name;
198 p--;
199 // skip spaces
200 while (p != line && *p == ' ') {
201 p--;
202 }
203 // walk backwards to the beginning of inode number
204 while (p != line && isdigit(*p)) {
205 p--;
206 }
207 uint64_t inode = strtoull(p, nullptr, 10);
208 auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
209 [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
210 if (buf != dmabufs->end()) {
211 buf->AddMapRef(pid);
212 return;
213 }
214
215 // We have a new buffer, but unknown count and name
216 DmaBuffer& dbuf = dmabufs->emplace_back(inode, end - start, 0, "<unknown>", "<unknown>");
217 dbuf.AddMapRef(pid);
218 };
219
220 while (getline(&line, &len, fp.get()) > 0) {
221 if (!::android::procinfo::ReadMapFileContent(line, account_dmabuf)) {
222 LOG(ERROR) << "Failed t parse maps for pid: " << pid;
223 return false;
224 }
225 }
226
227 free(line);
228 return true;
229}
230
231// Public methods
232bool ReadDmaBufInfo(std::vector<DmaBuffer>* dmabufs, const std::string& path) {
233 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
234 if (fp == nullptr) {
235 LOG(ERROR) << "Failed to open dmabuf info from debugfs";
236 return false;
237 }
238
239 char* line = nullptr;
240 size_t len = 0;
241 dmabufs->clear();
242 while (getline(&line, &len, fp.get()) > 0) {
243 // The new dmabuf bufinfo format adds inode number and a name at the end
244 // We are looking for lines as follows:
245 // size flags mode count exp_name ino name
246 // 01048576 00000002 00000007 00000001 ion 00018758 CAMERA
247 // 01048576 00000002 00000007 00000001 ion 00018758
248 uint64_t size, count;
249 char* exporter_name = nullptr;
250 ino_t inode;
251 char* name = nullptr;
252 int matched = sscanf(line, "%" SCNu64 "%*x %*x %" SCNu64 " %ms %lu %ms", &size, &count,
253 &exporter_name, &inode, &name);
254 if (matched < 4) {
255 continue;
256 }
257 dmabufs->emplace_back(inode, size, count, exporter_name, matched > 4 ? name : "");
258 free(exporter_name);
259 free(name);
260 }
261
262 free(line);
263
264 return true;
265}
266
267bool ReadDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
268 dmabufs->clear();
Erick Reyes302a2592019-01-28 19:53:30 -0800269 return AppendDmaBufInfo(pid, dmabufs);
270}
271
272bool AppendDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
Erick Reyesd2918fe2019-01-24 15:26:23 -0800273 if (!ReadDmaBufFdRefs(pid, dmabufs)) {
274 LOG(ERROR) << "Failed to read dmabuf fd references";
275 return false;
276 }
277
278 if (!ReadDmaBufMapRefs(pid, dmabufs)) {
279 LOG(ERROR) << "Failed to read dmabuf map references";
280 return false;
281 }
282 return true;
283}
284
285} // namespace dmabufinfo
286} // namespace android