• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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