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 <iterator>
31 #include <sstream>
32 #include <string>
33 #include <utility>
34 #include <vector>
35 
36 #include <android-base/file.h>
37 #include <android-base/logging.h>
38 #include <android-base/parseint.h>
39 #include <android-base/stringprintf.h>
40 #include <android-base/strings.h>
41 #include <android-base/unique_fd.h>
42 
43 #include "meminfo_private.h"
44 
45 namespace android {
46 namespace meminfo {
47 
48 bool SysMemInfo::ReadMemInfo(const char* path) {
49     return ReadMemInfo(path, SysMemInfo::kDefaultSysMemInfoTags.size(),
50                        &*SysMemInfo::kDefaultSysMemInfoTags.begin(),
51                        [&](std::string_view tag, uint64_t val) {
52                            // Safe to store the string_view in the map
53                            // because the tags from
54                            // kDefaultSysMemInfoTags are all
55                            // statically-allocated.
56                            mem_in_kb_[tag] = val;
57                        });
58 }
59 
60 bool SysMemInfo::ReadMemInfo(std::vector<uint64_t>* out, const char* path) {
61     out->clear();
62     out->resize(SysMemInfo::kDefaultSysMemInfoTags.size());
63     return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags.size(),
64                        &*SysMemInfo::kDefaultSysMemInfoTags.begin(), out->data(), path);
65 }
66 
67 bool SysMemInfo::ReadMemInfo(size_t ntags, const std::string_view* tags, uint64_t* out,
68                              const char* path) {
69     return ReadMemInfo(path, ntags, tags, [&]([[maybe_unused]] std::string_view tag, uint64_t val) {
70         auto it = std::find(tags, tags + ntags, tag);
71         if (it == tags + ntags) {
72             LOG(ERROR) << "Tried to store invalid tag: " << tag;
73             return;
74         }
75         auto index = std::distance(tags, it);
76         // store the values in the same order as the tags
77         out[index] = val;
78     });
79 }
80 
81 uint64_t SysMemInfo::ReadVmallocInfo() {
82     return ::android::meminfo::ReadVmallocInfo();
83 }
84 
85 bool SysMemInfo::ReadMemInfo(const char* path, size_t ntags, const std::string_view* tags,
86                              std::function<void(std::string_view, uint64_t)> store_val) {
87     char buffer[4096];
88     int fd = open(path, O_RDONLY | O_CLOEXEC);
89     if (fd < 0) {
90         PLOG(ERROR) << "Failed to open file :" << path;
91         return false;
92     }
93 
94     const int len = read(fd, buffer, sizeof(buffer) - 1);
95     close(fd);
96     if (len < 0) {
97         return false;
98     }
99 
100     buffer[len] = '\0';
101     char* p = buffer;
102     uint32_t found = 0;
103     uint32_t lineno = 0;
104     bool zram_tag_found = false;
105     while (*p && found < ntags) {
106         for (size_t tagno = 0; tagno < ntags; ++tagno) {
107             const std::string_view& tag = tags[tagno];
108             // Special case for "Zram:" tag that android_os_Debug and friends look
109             // up along with the rest of the numbers from /proc/meminfo
110             if (!zram_tag_found && tag == "Zram:") {
111                 store_val(tag, mem_zram_kb());
112                 zram_tag_found = true;
113                 found++;
114                 continue;
115             }
116 
117             if (strncmp(p, tag.data(), tag.size()) == 0) {
118                 p += tag.size();
119                 while (*p == ' ') p++;
120                 char* endptr = nullptr;
121                 uint64_t val = strtoull(p, &endptr, 10);
122                 if (p == endptr) {
123                     PLOG(ERROR) << "Failed to parse line:" << lineno + 1 << " in file: " << path;
124                     return false;
125                 }
126                 store_val(tag, val);
127                 p = endptr;
128                 found++;
129                 break;
130             }
131         }
132 
133         while (*p && *p != '\n') {
134             p++;
135         }
136         if (*p) p++;
137         lineno++;
138     }
139 
140     return true;
141 }
142 
143 uint64_t SysMemInfo::mem_zram_kb(const char* zram_dev_cstr) {
144     uint64_t mem_zram_total = 0;
145     if (zram_dev_cstr) {
146         if (!MemZramDevice(zram_dev_cstr, &mem_zram_total)) {
147             return 0;
148         }
149         return mem_zram_total / 1024;
150     }
151 
152     constexpr uint32_t kMaxZramDevices = 256;
153     for (uint32_t i = 0; i < kMaxZramDevices; i++) {
154         std::string zram_dev_abspath = ::android::base::StringPrintf("/sys/block/zram%u/", i);
155         if (access(zram_dev_abspath.c_str(), F_OK)) {
156             // We assume zram devices appear in range 0-255 and appear always in sequence
157             // under /sys/block. So, stop looking for them once we find one is missing.
158             break;
159         }
160 
161         uint64_t mem_zram_dev;
162         if (!MemZramDevice(zram_dev_abspath.c_str(), &mem_zram_dev)) {
163             return 0;
164         }
165 
166         mem_zram_total += mem_zram_dev;
167     }
168 
169     return mem_zram_total / 1024;
170 }
171 
172 bool SysMemInfo::MemZramDevice(const char* zram_dev, uint64_t* mem_zram_dev) {
173     std::string mmstat = ::android::base::StringPrintf("%s/%s", zram_dev, "mm_stat");
174     auto mmstat_fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mmstat.c_str(), "re"), fclose};
175     if (mmstat_fp != nullptr) {
176         // only if we do have mmstat, use it. Otherwise, fall through to trying out the old
177         // 'mem_used_total'
178         if (fscanf(mmstat_fp.get(), "%*" SCNu64 " %*" SCNu64 " %" SCNu64, mem_zram_dev) != 1) {
179             PLOG(ERROR) << "Malformed mm_stat file in: " << zram_dev;
180             return false;
181         }
182         return true;
183     }
184 
185     std::string content;
186     if (::android::base::ReadFileToString(
187                 ::android::base::StringPrintf("%s/mem_used_total", zram_dev), &content)) {
188         *mem_zram_dev = strtoull(content.c_str(), NULL, 10);
189         if (*mem_zram_dev == ULLONG_MAX) {
190             PLOG(ERROR) << "Malformed mem_used_total file for zram dev: " << zram_dev
191                         << " content: " << content;
192             return false;
193         }
194 
195         return true;
196     }
197 
198     LOG(ERROR) << "Can't find memory status under: " << zram_dev;
199     return false;
200 }
201 
202 // Public methods
203 uint64_t ReadVmallocInfo(const char* path) {
204     uint64_t vmalloc_total = 0;
205     auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path, "re"), fclose};
206     if (fp == nullptr) {
207         return vmalloc_total;
208     }
209 
210     char* line = nullptr;
211     size_t line_alloc = 0;
212     while (getline(&line, &line_alloc, fp.get()) > 0) {
213         // We are looking for lines like
214         //
215         // 0x0000000000000000-0x0000000000000000   12288 drm_property_create_blob+0x44/0xec pages=2 vmalloc
216         // 0x0000000000000000-0x0000000000000000    8192 wlan_logging_sock_init_svc+0xf8/0x4f0 [wlan] pages=1 vmalloc
217         //
218         // Notice that if the caller is coming from a module, the kernel prints and extra
219         // "[module_name]" after the address and the symbol of the call site. This means we can't
220         // use the old sscanf() method of getting the # of pages.
221         char* p_start = strstr(line, "pages=");
222         if (p_start == nullptr) {
223             // we didn't find anything
224             continue;
225         }
226 
227         uint64_t nr_pages;
228         if (sscanf(p_start, "pages=%" SCNu64 "", &nr_pages) == 1) {
229             vmalloc_total += (nr_pages * getpagesize());
230         }
231     }
232 
233     free(line);
234 
235     return vmalloc_total;
236 }
237 
238 static bool ReadSysfsFile(const std::string& path, uint64_t* value) {
239     std::string content;
240     if (!::android::base::ReadFileToString(path, &content)) {
241         LOG(ERROR) << "Can't open file: " << path;
242         return false;
243     }
244 
245     *value = strtoull(content.c_str(), NULL, 10);
246     if (*value == ULLONG_MAX) {
247         PLOG(ERROR) << "Invalid file format: " << path;
248         return false;
249     }
250 
251     return true;
252 }
253 
254 bool ReadIonHeapsSizeKb(uint64_t* size, const std::string& path) {
255     return ReadSysfsFile(path, size);
256 }
257 
258 bool ReadIonPoolsSizeKb(uint64_t* size, const std::string& path) {
259     return ReadSysfsFile(path, size);
260 }
261 
262 }  // namespace meminfo
263 }  // namespace android
264