1 /*
2 * Copyright (C) 2018 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 <ctype.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <inttypes.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include <algorithm>
27 #include <cctype>
28 #include <cstdio>
29 #include <fstream>
30 #include <functional>
31 #include <iterator>
32 #if defined(__ANDROID__) && !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
33 #include "bpf/BpfMap.h"
34 #endif
35 #include <sstream>
36 #include <string>
37 #include <unordered_set>
38 #include <utility>
39 #include <vector>
40
41 #include <android-base/file.h>
42 #include <android-base/logging.h>
43 #include <android-base/parseint.h>
44 #include <android-base/stringprintf.h>
45 #include <android-base/strings.h>
46 #include <android-base/unique_fd.h>
47 #include <dmabufinfo/dmabuf_sysfs_stats.h>
48
49 #include "meminfo_private.h"
50
51 namespace android {
52 namespace meminfo {
53
ReadMemInfo(const char * path)54 bool SysMemInfo::ReadMemInfo(const char* path) {
55 return ReadMemInfo(path, SysMemInfo::kDefaultSysMemInfoTags.size(),
56 &*SysMemInfo::kDefaultSysMemInfoTags.begin(),
57 [&](std::string_view tag, uint64_t val) {
58 // Safe to store the string_view in the map
59 // because the tags from
60 // kDefaultSysMemInfoTags are all
61 // statically-allocated.
62 mem_in_kb_[tag] = val;
63 });
64 }
65
ReadMemInfo(std::vector<uint64_t> * out,const char * path)66 bool SysMemInfo::ReadMemInfo(std::vector<uint64_t>* out, const char* path) {
67 out->clear();
68 out->resize(SysMemInfo::kDefaultSysMemInfoTags.size());
69 return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags.size(),
70 &*SysMemInfo::kDefaultSysMemInfoTags.begin(), out->data(), path);
71 }
72
ReadMemInfo(size_t ntags,const std::string_view * tags,uint64_t * out,const char * path)73 bool SysMemInfo::ReadMemInfo(size_t ntags, const std::string_view* tags, uint64_t* out,
74 const char* path) {
75 return ReadMemInfo(path, ntags, tags, [&]([[maybe_unused]] std::string_view tag, uint64_t val) {
76 auto it = std::find(tags, tags + ntags, tag);
77 if (it == tags + ntags) {
78 LOG(ERROR) << "Tried to store invalid tag: " << tag;
79 return;
80 }
81 auto index = std::distance(tags, it);
82 // store the values in the same order as the tags
83 out[index] = val;
84 });
85 }
86
ReadVmallocInfo()87 uint64_t SysMemInfo::ReadVmallocInfo() {
88 return ::android::meminfo::ReadVmallocInfo();
89 }
90
ReadMemInfo(const char * path,size_t ntags,const std::string_view * tags,std::function<void (std::string_view,uint64_t)> store_val)91 bool SysMemInfo::ReadMemInfo(const char* path, size_t ntags, const std::string_view* tags,
92 std::function<void(std::string_view, uint64_t)> store_val) {
93 char buffer[4096];
94 int fd = open(path, O_RDONLY | O_CLOEXEC);
95 if (fd < 0) {
96 PLOG(ERROR) << "Failed to open file :" << path;
97 return false;
98 }
99
100 const int len = read(fd, buffer, sizeof(buffer) - 1);
101 close(fd);
102 if (len < 0) {
103 return false;
104 }
105
106 buffer[len] = '\0';
107 char* p = buffer;
108 uint32_t found = 0;
109 uint32_t lineno = 0;
110 bool zram_tag_found = false;
111 while (*p && found < ntags) {
112 for (size_t tagno = 0; tagno < ntags; ++tagno) {
113 const std::string_view& tag = tags[tagno];
114 // Special case for "Zram:" tag that android_os_Debug and friends look
115 // up along with the rest of the numbers from /proc/meminfo
116 if (!zram_tag_found && tag == "Zram:") {
117 store_val(tag, mem_zram_kb());
118 zram_tag_found = true;
119 found++;
120 continue;
121 }
122
123 if (strncmp(p, tag.data(), tag.size()) == 0) {
124 p += tag.size();
125 while (*p == ' ') p++;
126 char* endptr = nullptr;
127 uint64_t val = strtoull(p, &endptr, 10);
128 if (p == endptr) {
129 PLOG(ERROR) << "Failed to parse line:" << lineno + 1 << " in file: " << path;
130 return false;
131 }
132 store_val(tag, val);
133 p = endptr;
134 found++;
135 break;
136 }
137 }
138
139 while (*p && *p != '\n') {
140 p++;
141 }
142 if (*p) p++;
143 lineno++;
144 }
145
146 return true;
147 }
148
mem_zram_kb(const char * zram_dev_cstr) const149 uint64_t SysMemInfo::mem_zram_kb(const char* zram_dev_cstr) const {
150 uint64_t mem_zram_total = 0;
151 if (zram_dev_cstr) {
152 if (!MemZramDevice(zram_dev_cstr, &mem_zram_total)) {
153 return 0;
154 }
155 return mem_zram_total / 1024;
156 }
157
158 constexpr uint32_t kMaxZramDevices = 256;
159 for (uint32_t i = 0; i < kMaxZramDevices; i++) {
160 std::string zram_dev_abspath = ::android::base::StringPrintf("/sys/block/zram%u/", i);
161 if (access(zram_dev_abspath.c_str(), F_OK)) {
162 // We assume zram devices appear in range 0-255 and appear always in sequence
163 // under /sys/block. So, stop looking for them once we find one is missing.
164 break;
165 }
166
167 uint64_t mem_zram_dev;
168 if (!MemZramDevice(zram_dev_abspath.c_str(), &mem_zram_dev)) {
169 return 0;
170 }
171
172 mem_zram_total += mem_zram_dev;
173 }
174
175 return mem_zram_total / 1024;
176 }
177
MemZramDevice(const char * zram_dev,uint64_t * mem_zram_dev) const178 bool SysMemInfo::MemZramDevice(const char* zram_dev, uint64_t* mem_zram_dev) const {
179 std::string mmstat = ::android::base::StringPrintf("%s/%s", zram_dev, "mm_stat");
180 auto mmstat_fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mmstat.c_str(), "re"), fclose};
181 if (mmstat_fp != nullptr) {
182 // only if we do have mmstat, use it. Otherwise, fall through to trying out the old
183 // 'mem_used_total'
184 if (fscanf(mmstat_fp.get(), "%*" SCNu64 " %*" SCNu64 " %" SCNu64, mem_zram_dev) != 1) {
185 PLOG(ERROR) << "Malformed mm_stat file in: " << zram_dev;
186 return false;
187 }
188 return true;
189 }
190
191 std::string content;
192 if (::android::base::ReadFileToString(
193 ::android::base::StringPrintf("%s/mem_used_total", zram_dev), &content)) {
194 *mem_zram_dev = strtoull(content.c_str(), NULL, 10);
195 if (*mem_zram_dev == ULLONG_MAX) {
196 PLOG(ERROR) << "Malformed mem_used_total file for zram dev: " << zram_dev
197 << " content: " << content;
198 return false;
199 }
200
201 return true;
202 }
203
204 LOG(ERROR) << "Can't find memory status under: " << zram_dev;
205 return false;
206 }
207
mem_compacted_kb(const char * zram_dev_cstr)208 uint64_t SysMemInfo::mem_compacted_kb(const char* zram_dev_cstr) {
209 uint64_t mem_compacted_total = 0;
210 if (zram_dev_cstr) {
211 // Fast-path, single device
212 if (!GetTotalMemCompacted(zram_dev_cstr, &mem_compacted_total)) {
213 return 0;
214 }
215 return mem_compacted_total / 1024;
216 }
217
218 // Slow path - multiple devices
219 constexpr uint32_t kMaxZramDevices = 256;
220 for (uint32_t i = 0; i < kMaxZramDevices; i++) {
221 std::string zram_dev_abspath = ::android::base::StringPrintf("/sys/block/zram%u/", i);
222 if (access(zram_dev_abspath.c_str(), F_OK)) {
223 // We assume zram devices appear in range 0-255 and appear always in sequence
224 // under /sys/block. So, stop looking for them once we find one is missing.
225 break;
226 }
227
228 uint64_t mem_compacted;
229 if (!GetTotalMemCompacted(zram_dev_abspath.c_str(), &mem_compacted)) {
230 return 0;
231 }
232
233 mem_compacted_total += mem_compacted;
234 }
235
236 return mem_compacted_total / 1024; // transform to KBs
237 }
238
239 // Returns the total memory compacted in bytes which corresponds to the following formula
240 // compacted memory = uncompressed memory size - compressed memory size
GetTotalMemCompacted(const char * zram_dev,uint64_t * out_mem_compacted)241 bool SysMemInfo::GetTotalMemCompacted(const char* zram_dev, uint64_t* out_mem_compacted) {
242 std::string mmstat = ::android::base::StringPrintf("%s/%s", zram_dev, "mm_stat");
243 auto mmstat_fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mmstat.c_str(), "re"), fclose};
244 if (mmstat_fp != nullptr) {
245 uint64_t uncompressed_size_bytes;
246 uint64_t compressed_size_bytes;
247
248 if (fscanf(mmstat_fp.get(), "%" SCNu64 "%" SCNu64, &uncompressed_size_bytes,
249 &compressed_size_bytes) != 2) {
250 PLOG(ERROR) << "Malformed mm_stat file in: " << zram_dev;
251 *out_mem_compacted = 0;
252 return false;
253 }
254
255 *out_mem_compacted = uncompressed_size_bytes - compressed_size_bytes;
256 return true;
257 }
258
259 *out_mem_compacted = 0;
260 return false;
261 }
262
263 // Public methods
ReadVmallocInfo(const char * path)264 uint64_t ReadVmallocInfo(const char* path) {
265 uint64_t vmalloc_total = 0;
266 auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path, "re"), fclose};
267 if (fp == nullptr) {
268 return vmalloc_total;
269 }
270
271 char* line = nullptr;
272 size_t line_alloc = 0;
273 while (getline(&line, &line_alloc, fp.get()) > 0) {
274 // We are looking for lines like
275 //
276 // 0x0000000000000000-0x0000000000000000 12288 drm_property_create_blob+0x44/0xec pages=2 vmalloc
277 // 0x0000000000000000-0x0000000000000000 8192 wlan_logging_sock_init_svc+0xf8/0x4f0 [wlan] pages=1 vmalloc
278 //
279 // Notice that if the caller is coming from a module, the kernel prints and extra
280 // "[module_name]" after the address and the symbol of the call site. This means we can't
281 // use the old sscanf() method of getting the # of pages.
282 char* p_start = strstr(line, "pages=");
283 if (p_start == nullptr) {
284 // we didn't find anything
285 continue;
286 }
287
288 uint64_t nr_pages;
289 if (sscanf(p_start, "pages=%" SCNu64 "", &nr_pages) == 1) {
290 vmalloc_total += (nr_pages * getpagesize());
291 }
292 }
293
294 free(line);
295
296 return vmalloc_total;
297 }
298
ReadSysfsFile(const std::string & path,uint64_t * value)299 static bool ReadSysfsFile(const std::string& path, uint64_t* value) {
300 std::string content;
301 if (!::android::base::ReadFileToString(path, &content)) {
302 LOG(ERROR) << "Can't open file: " << path;
303 return false;
304 }
305
306 *value = strtoull(content.c_str(), NULL, 10);
307 if (*value == ULLONG_MAX) {
308 PLOG(ERROR) << "Invalid file format: " << path;
309 return false;
310 }
311
312 return true;
313 }
314
ReadIonHeapsSizeKb(uint64_t * size,const std::string & path)315 bool ReadIonHeapsSizeKb(uint64_t* size, const std::string& path) {
316 return ReadSysfsFile(path, size);
317 }
318
ReadIonPoolsSizeKb(uint64_t * size,const std::string & path)319 bool ReadIonPoolsSizeKb(uint64_t* size, const std::string& path) {
320 return ReadSysfsFile(path, size);
321 }
322
ReadDmabufHeapPoolsSizeKb(uint64_t * size,const std::string & dma_heap_pool_size_path)323 bool ReadDmabufHeapPoolsSizeKb(uint64_t* size, const std::string& dma_heap_pool_size_path) {
324 static bool support_dmabuf_heap_pool_size = [dma_heap_pool_size_path]() -> bool {
325 bool ret = (access(dma_heap_pool_size_path.c_str(), R_OK) == 0);
326 if (!ret)
327 LOG(ERROR) << "Unable to read DMA-BUF heap total pool size, read ION total pool "
328 "size instead.";
329 return ret;
330 }();
331
332 if (!support_dmabuf_heap_pool_size) return ReadIonPoolsSizeKb(size);
333
334 return ReadSysfsFile(dma_heap_pool_size_path, size);
335 }
336
ReadDmabufHeapTotalExportedKb(uint64_t * size,const std::string & dma_heap_root_path,const std::string & dmabuf_sysfs_stats_path)337 bool ReadDmabufHeapTotalExportedKb(uint64_t* size, const std::string& dma_heap_root_path,
338 const std::string& dmabuf_sysfs_stats_path) {
339 static bool support_dmabuf_heaps = [dma_heap_root_path]() -> bool {
340 bool ret = (access(dma_heap_root_path.c_str(), R_OK) == 0);
341 if (!ret) LOG(ERROR) << "DMA-BUF heaps not supported, read ION heap total instead.";
342 return ret;
343 }();
344
345 if (!support_dmabuf_heaps) return ReadIonHeapsSizeKb(size);
346
347 std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(dma_heap_root_path.c_str()), closedir);
348
349 if (!dir) {
350 return false;
351 }
352
353 std::unordered_set<std::string> heap_list;
354 struct dirent* dent;
355 while ((dent = readdir(dir.get()))) {
356 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue;
357
358 heap_list.insert(dent->d_name);
359 }
360
361 if (heap_list.empty()) return false;
362
363 android::dmabufinfo::DmabufSysfsStats stats;
364 if (!android::dmabufinfo::GetDmabufSysfsStats(&stats, dmabuf_sysfs_stats_path)) return false;
365
366 auto exporter_info = stats.exporter_info();
367
368 *size = 0;
369 for (const auto& heap : heap_list) {
370 auto iter = exporter_info.find(heap);
371 if (iter != exporter_info.end()) *size += iter->second.size;
372 }
373
374 *size = *size / 1024;
375
376 return true;
377 }
378
ReadPerProcessGpuMem(std::unordered_map<uint32_t,uint64_t> * out)379 bool ReadPerProcessGpuMem([[maybe_unused]] std::unordered_map<uint32_t, uint64_t>* out) {
380 #if defined(__ANDROID__) && !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
381 static constexpr const char kBpfGpuMemTotalMap[] = "/sys/fs/bpf/map_gpuMem_gpu_mem_total_map";
382
383 // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
384 auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kBpfGpuMemTotalMap);
385 if (!map.isValid()) {
386 LOG(ERROR) << "Can't open file: " << kBpfGpuMemTotalMap;
387 return false;
388 }
389
390 if (!out) {
391 LOG(ERROR) << "ReadPerProcessGpuMem: out param is null";
392 return false;
393 }
394 out->clear();
395
396 auto map_key = map.getFirstKey();
397 if (!map_key.ok()) {
398 return true;
399 }
400
401 do {
402 uint64_t key = map_key.value();
403 uint32_t pid = key; // BPF Key [32-bits GPU ID | 32-bits PID]
404
405 auto gpu_mem = map.readValue(key);
406 if (!gpu_mem.ok()) {
407 LOG(ERROR) << "Invalid file format: " << kBpfGpuMemTotalMap;
408 return false;
409 }
410
411 const auto& iter = out->find(pid);
412 if (iter == out->end()) {
413 out->insert({pid, gpu_mem.value() / 1024});
414 } else {
415 iter->second += gpu_mem.value() / 1024;
416 }
417
418 map_key = map.getNextKey(key);
419 } while (map_key.ok());
420
421 return true;
422 #else
423 return false;
424 #endif
425 }
426
ReadProcessGpuUsageKb(uint32_t pid,uint32_t gpu_id,uint64_t * size)427 bool ReadProcessGpuUsageKb([[maybe_unused]] uint32_t pid, [[maybe_unused]] uint32_t gpu_id,
428 uint64_t* size) {
429 #if defined(__ANDROID__) && !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
430 static constexpr const char kBpfGpuMemTotalMap[] = "/sys/fs/bpf/map_gpuMem_gpu_mem_total_map";
431
432 uint64_t gpu_mem;
433
434 // BPF Key [32-bits GPU ID | 32-bits PID]
435 uint64_t kBpfKeyGpuUsage = ((uint64_t)gpu_id << 32) | pid;
436
437 // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
438 auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kBpfGpuMemTotalMap);
439 if (!map.isValid()) {
440 LOG(ERROR) << "Can't open file: " << kBpfGpuMemTotalMap;
441 return false;
442 }
443
444 auto res = map.readValue(kBpfKeyGpuUsage);
445
446 if (res.ok()) {
447 gpu_mem = res.value();
448 } else if (res.error().code() == ENOENT) {
449 gpu_mem = 0;
450 } else {
451 LOG(ERROR) << "Invalid file format: " << kBpfGpuMemTotalMap;
452 return false;
453 }
454
455 if (size) {
456 *size = gpu_mem / 1024;
457 }
458 return true;
459 #else
460 if (size) {
461 *size = 0;
462 }
463 return false;
464 #endif
465 }
466
ReadGpuTotalUsageKb(uint64_t * size)467 bool ReadGpuTotalUsageKb(uint64_t* size) {
468 // gpu_mem_total tracepoint defines PID 0 as global total
469 // GPU ID 0 suffices for current android devices.
470 // This will need to check all GPU IDs in future if more than
471 // one is GPU device is present on the device.
472 return ReadProcessGpuUsageKb(0, 0, size);
473 }
474
475 } // namespace meminfo
476 } // namespace android
477