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