1/* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "kernel_interface.h" 17 18#include <climits> 19#include <csignal> 20#include <dirent.h> 21#include <fstream> 22#include <regex> 23#include <securec.h> 24#include <sstream> 25#include <sys/stat.h> 26#include <unistd.h> 27 28#include "directory_ex.h" 29#include "file_ex.h" 30#include "memmgr_log.h" 31 32namespace OHOS { 33namespace Memory { 34namespace { 35const std::string TAG = "KernelInterface"; 36} 37 38IMPLEMENT_SINGLE_INSTANCE(KernelInterface); 39 40const std::string KernelInterface::ROOT_PROC_PATH = "/proc"; 41const std::string KernelInterface::MEMCG_BASE_PATH = "/dev/memcg"; 42const std::string KernelInterface::FILE_MEMCG_PROCS = "cgroup.procs"; 43 44#ifdef USE_HYPERHOLD_MEMORY 45const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_PATH = "/dev/memcg/memory.zswapd_pressure_show"; 46const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_BUFFER_SIZE = "buffer_size"; 47#else 48const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_PATH = "/proc/meminfo"; 49const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_BUFFER_SIZE = "MemAvailable"; 50#endif 51const std::string KernelInterface::MEMINFO_PATH = "/proc/meminfo"; 52const std::string KernelInterface::FILE_PROC_STATUS = "status"; 53const std::string KernelInterface::TOTAL_MEMORY = "MemTotal"; 54 55bool KernelInterface::EchoToPath(const char* path, const char* content) 56{ 57 int fd = open(path, O_WRONLY); 58 if (fd == -1) { 59 HILOGE("echo %{public}s > %{public}s failed: file is not open", content, path); 60 return false; 61 } 62 if (write(fd, content, strlen(content)) < 0) { 63 HILOGE("echo %{public}s > %{public}s failed: write failed", content, path); 64 close(fd); 65 return false; 66 } 67 close(fd); 68 HILOGI("echo %{public}s > %{public}s", content, path); 69 return true; 70} 71 72bool KernelInterface::IsFileExists(const std::string& path) 73{ 74 if (path.empty()) { 75 return false; 76 } 77 struct stat st = {}; 78 if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) { 79 return true; 80 } 81 return false; 82} 83 84bool KernelInterface::CreateFile(const std::string& path, const mode_t& mode) 85{ 86 if (path.empty()) { 87 return false; 88 } 89 if (IsFileExists(path)) { 90 return true; 91 } 92 93 std::ofstream fout(path); 94 if (!fout.is_open()) { 95 return false; 96 } 97 fout.flush(); 98 fout.close(); 99 if (chmod(path.c_str(), mode) != 0) { 100 return false; 101 } 102 103 return true; 104} 105 106bool KernelInterface::CreateFile(const std::string& path) 107{ 108 return CreateFile(path, FILE_MODE_644); 109} 110 111bool KernelInterface::RemoveFile(const std::string& path) 112{ 113 return OHOS::RemoveFile(path); 114} 115 116bool KernelInterface::WriteToFile(const std::string& path, const std::string& content, bool truncated) 117{ 118 int fd; 119 char actualPath[PATH_MAX + 1] = {0}; 120 char* ptrRet = NULL; 121 122 if (strlen(path.c_str()) == 0 || strlen(path.c_str()) > PATH_MAX) { 123 HILOGE("file path is invalid"); 124 return false; 125 } 126 ptrRet = realpath(path.c_str(), actualPath); 127 if (!ptrRet) { 128 HILOGE("file path cannot be canonicalized"); 129 return false; 130 } 131 HILOGD("path:%{public}s, actualPath:%{public}s", path.c_str(), actualPath); 132 fd = open(actualPath, O_RDWR | (truncated ? O_TRUNC : O_APPEND)); 133 if (fd == -1) { 134 HILOGE("echo %{public}s %{public}s %{public}s failed: file is not open", 135 content.c_str(), truncated ? ">" : ">>", path.c_str()); 136 ptrRet = NULL; 137 return false; 138 } 139 if (write(fd, content.c_str(), strlen(content.c_str())) < 0) { 140 HILOGE("echo %{public}s %{public}s %{public}s failed: write failed", 141 content.c_str(), truncated ? ">" : ">>", path.c_str()); 142 ptrRet = NULL; 143 close(fd); 144 return false; 145 } 146 ptrRet = NULL; 147 close(fd); 148 HILOGD("echo %{public}s %{public}s %{public}s", content.c_str(), truncated ? ">" : ">>", path.c_str()); 149 return true; 150} 151 152bool KernelInterface::ReadFromFile(const std::string& path, std::string& content) 153{ 154 return OHOS::LoadStringFromFile(path, content); 155} 156 157bool KernelInterface::ReadLinesFromFile(const std::string& path, std::vector<std::string>& lines) 158{ 159 if (!IsFileExists(path)) { 160 HILOGE("no such file: %{public}s", path.c_str()); 161 return false; 162 } 163 std::string line; 164 std::ifstream inf(path, std::ifstream::in); 165 if (!inf) { 166 HILOGE("ifstream(%{public}s) failed", path.c_str()); 167 return false; 168 } 169 lines.clear(); 170 while (!inf.eof()) { 171 getline(inf, line); 172 lines.push_back(line); 173 } 174 inf.close(); 175 return true; 176} 177 178bool KernelInterface::IsDirExists(const std::string& path) 179{ 180 if (path.empty()) { 181 return false; 182 } 183 struct stat st = {}; 184 if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) { 185 return true; 186 } 187 return false; 188} 189 190int64_t KernelInterface::GetSystemCurTime() 191{ 192 return std::chrono::duration_cast<std::chrono::milliseconds> 193 (std::chrono::system_clock::now().time_since_epoch()).count(); 194} 195 196int64_t KernelInterface::GetSystemTimeMs() 197{ 198 return std::chrono::duration_cast<std::chrono::seconds> 199 (std::chrono::system_clock::now().time_since_epoch()).count(); 200} 201 202bool KernelInterface::IsExists(const std::string& path) 203{ 204 return OHOS::FileExists(path); 205} 206 207bool KernelInterface::IsEmptyDir(const std::string& path) 208{ 209 return OHOS::IsEmptyFolder(path); 210} 211 212bool KernelInterface::CreateDir(const std::string& path) 213{ 214 return OHOS::ForceCreateDirectory(path); // default mode 755 215} 216 217bool KernelInterface::RemoveDirRecursively(const std::string& path) 218{ 219 return OHOS::ForceRemoveDirectory(path) || (remove(path.c_str()) == 0); 220} 221 222std::string KernelInterface::RmDelimiter(const std::string& path) 223{ 224 if (path.empty()) { 225 return path; 226 } 227 return OHOS::ExcludeTrailingPathDelimiter(path); 228} 229 230std::string KernelInterface::AddDelimiter(const std::string& path) 231{ 232 if (path.empty()) { 233 return path; 234 } 235 return OHOS::IncludeTrailingPathDelimiter(path); 236} 237 238std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& subPath) 239{ 240 return AddDelimiter(prefixPath) + subPath; 241} 242 243std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& midPath, 244 const std::string& subPath) 245{ 246 return JoinPath(JoinPath(prefixPath, midPath), subPath); 247} 248 249bool KernelInterface::GetPidProcInfo(struct ProcInfo &procInfo) 250{ 251 HILOGD("called!"); 252 253 std::string statPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/stat"); 254 255 // format like: 256 // 1 (init) S 0 0 0 0 -1 4210944 1 ... 257 std::string stat; 258 std::string statm; 259 std::string statPid; 260 std::string vss; 261 std::string rss; 262 if (!ReadFromFile(statPath, stat)) { 263 HILOGD("stat file error!"); 264 return false; 265 } 266 std::istringstream isStat(stat); 267 isStat >> statPid >> procInfo.name >> procInfo.status; 268 269 if (statPid != std::to_string(procInfo.pid)) { 270 HILOGD("pid error!"); 271 return false; 272 } 273 274 std::string statmPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/statm"); 275 // format like: 276 // 640 472 369 38 0 115 0 277 if (!ReadFromFile(statmPath, statm)) { 278 HILOGD("statm file error!"); 279 return false; 280 } 281 std::istringstream isStatm(statm); 282 isStatm >> vss >> rss; // pages 283 int rssValue = 0; 284 try { 285 rssValue = std::stoi(rss); 286 } catch (...) { 287 HILOGE("stoi(%{public}s) failed!", rss.c_str()); 288 return false; 289 } 290 291 if (rssValue < 0 || rssValue > INT_MAX / PAGE_TO_KB) { 292 HILOGE("rssValue=%{public}d, rss is less than 0 or overflow!", rssValue); 293 return false; 294 } 295 procInfo.size = rssValue * PAGE_TO_KB; 296 HILOGI("GetProcInfo success: name is %{public}s, status is %{public}s, size = %{public}d KB", 297 procInfo.name.c_str(), procInfo.status.c_str(), procInfo.size); 298 return true; 299} 300 301bool KernelInterface::GetProcNameByPid(int pid, std::string &name) 302{ 303 std::string statusPath = JoinPath("/proc/", std::to_string(pid), "/status"); 304 std::string statusContent; 305 std::string nameTag; 306 if (!ReadFromFile(statusPath, statusContent)) { 307 HILOGE("status file [%{public}s] error!", statusPath.c_str()); 308 return false; 309 } 310 std::istringstream statusStream(statusContent); 311 statusStream >> nameTag >> name; 312 return true; 313} 314 315void KernelInterface::ReadZswapdPressureShow(std::map<std::string, std::string>& result) 316{ 317 std::string contentStr; 318 if (!ReadFromFile(ZWAPD_PRESSURE_SHOW_PATH, contentStr)) { 319 HILOGE("read %{public}s faild, content=[%{public}s]", ZWAPD_PRESSURE_SHOW_PATH.c_str(), contentStr.c_str()); 320 return; 321 } 322 char *contentPtr = new char[contentStr.size() + 1]; 323 if (contentPtr == nullptr) { 324 HILOGE("alloc buffer fail"); 325 return; 326 } 327 if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) { 328 HILOGE("copy fail"); 329 delete [] contentPtr; 330 return; 331 } 332 char *restPtr; 333 char *line = strtok_r(contentPtr, "\n", &restPtr); 334 do { 335 for (size_t i = 0; i < strlen(line); i++) { 336 if (line[i] == ':') { 337 line[i] = ' '; 338 } 339 } 340 std::string lineStr(line); 341 std::istringstream is(lineStr); 342 std::string name; 343 std::string value; 344 is >> name >> value; 345 result.insert(std::make_pair(name, value)); 346 347 line = strtok_r(NULL, "\n", &restPtr); 348 } while (line); 349 if (restPtr) { 350 delete [] restPtr; 351 } 352 if (contentPtr) { 353 delete [] contentPtr; 354 } 355 return; 356} 357 358int KernelInterface::GetCurrentBuffer() 359{ 360 std::map<std::string, std::string> result; 361 ReadZswapdPressureShow(result); 362 auto value = result.find(ZWAPD_PRESSURE_SHOW_BUFFER_SIZE); 363 if (value != result.end()) { 364#ifdef USE_HYPERHOLD_MEMORY 365 HILOGD("buffer_size=%{public}s MB", result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str()); 366 return atoi(result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str()) * KB_PER_MB; 367#else 368 HILOGD("buffer_size=%{public}s KB", result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str()); 369 return atoi(result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str()); 370#endif 371 } 372 return MAX_BUFFER_KB; 373} 374 375int KernelInterface::KillOneProcessByPid(int pid) 376{ 377 HILOGD("called! pid=%{public}d", pid); 378 struct ProcInfo procInfo; 379 int freedBuffer = 0; 380 procInfo.pid = pid; 381 382 if (!GetPidProcInfo(procInfo)) { 383 HILOGE("GetPidProcInfo fail !!!"); 384 goto out; 385 } 386 387 if (procInfo.status == "D") { 388 HILOGE("Task %{public}s is at D status!", procInfo.name.c_str()); 389 goto out; 390 } 391 392 if (kill(pid, SIGKILL)) { 393 HILOGE("Kill %{public}s errno=%{public}d!", procInfo.name.c_str(), errno); 394 } 395 HILOGE("%{public}s has been Killed ! (pid=%{public}d, freedSize=%{public}d KB)", 396 procInfo.name.c_str(), procInfo.pid, procInfo.size); 397 398 freedBuffer = procInfo.size; 399out: 400 return freedBuffer; 401} 402 403bool KernelInterface::GetAllProcPids(std::vector<unsigned int> &pids) 404{ 405 pids.clear(); 406 DIR *dir = opendir(ROOT_PROC_PATH.c_str()); 407 if (dir == nullptr) { 408 HILOGE("dir %{public}s is not exist", ROOT_PROC_PATH.c_str()); 409 return false; 410 } 411 struct dirent *ptr = nullptr; 412 while ((ptr = readdir(dir)) != nullptr) { 413 if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) { 414 // current dir OR parent dir 415 continue; 416 } else if (ptr->d_type == DT_DIR) { 417 int pid = atoi(ptr->d_name); 418 if (pid > 0) { 419 pids.push_back((unsigned int)pid); 420 } 421 } 422 } 423 if (dir) { 424 closedir(dir); 425 } 426 HILOGD("there are %{public}zu pids under %{public}s", pids.size(), ROOT_PROC_PATH.c_str()); 427 return true; 428} 429 430bool KernelInterface::GetUidByPid(unsigned int pid, unsigned int& uid) 431{ 432 std::string path = JoinPath(ROOT_PROC_PATH, std::to_string(pid), "status"); 433 std::string content; 434 if (!ReadFromFile(path, content)) { 435 HILOGE("read file failed. %{public}s", path.c_str()); 436 return false; 437 } 438 content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space 439 std::regex re(".*Uid:[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+).*"); 440 std::smatch res; 441 if (!std::regex_match(content, res, re)) { 442 HILOGD("re not match. %{public}s", content.c_str()); 443 return false; 444 } 445 try { 446 uid = (unsigned int)std::stoi(res.str(1)); // 1: Uid index 447 } catch (...) { 448 HILOGE("stoi(%{public}s) failed", res.str(1).c_str()); 449 return false; 450 } 451 return true; 452} 453 454void DeleteCharArrayIfNotNull(char * charArray) 455{ 456 if (charArray) { 457 delete [] charArray; 458 charArray = nullptr; 459 } 460} 461 462bool KernelInterface::ReadSwapOutKBSinceKernelBoot(const std::string &path, const std::string &tagStr, 463 unsigned long long &ret) 464{ 465 std::string contentStr; 466 if (!ReadFromFile(path, contentStr)) { 467 return false; 468 } 469 char *contentPtr = new char[contentStr.size() + 1]; 470 if (contentPtr == nullptr) { 471 HILOGE("alloc buffer fail"); 472 return false; 473 } 474 if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) { 475 HILOGE("copy fail"); 476 DeleteCharArrayIfNotNull(contentPtr); 477 return false; 478 } 479 bool success = false; 480 char *restPtr; 481 char *line = strtok_r(contentPtr, "\n", &restPtr); 482 do { 483 std::string lineStr(line); 484 485 size_t i = 0; 486 for (; i < strlen(line); i++) { 487 if (line[i] == ':') { 488 break; 489 } 490 } 491 if (i >= strlen(line) - 2) { // 2: no : in the line or : is at end of line 492 line = strtok_r(NULL, "\n", &restPtr); 493 continue; 494 } 495 std::string tag = lineStr.substr(0, i); 496 if (tag == tagStr) { 497 std::string value = lineStr.substr(i + 1); 498 std::istringstream iss(value); 499 std::string sizeStr; 500 std::string unitStr; 501 iss >> sizeStr >> unitStr; 502 try { 503 ret = std::strtoull(sizeStr.c_str(), nullptr, 10); // 10:Decimal 504 success = true; 505 } catch (...) { 506 HILOGE("parse [%{public}s] to unsigned long long error!", sizeStr.c_str()); 507 } 508 break; 509 } 510 511 line = strtok_r(NULL, "\n", &restPtr); 512 } while (line); 513 DeleteCharArrayIfNotNull(contentPtr); 514 return success; 515} 516 517int KernelInterface::ParseMeminfo(const std::string &contentStr, const std::string &itemName) 518{ 519 char *contentPtr = new (std::nothrow) char[contentStr.size() + 1]; 520 if (contentPtr == nullptr) { 521 HILOGE("alloc buffer fail"); 522 return -1; 523 } 524 if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) { 525 HILOGE("copy fail"); 526 delete [] contentPtr; 527 return -1; 528 } 529 char *restPtr = nullptr; 530 char *line = strtok_r(contentPtr, "\n", &restPtr); 531 std::string name; 532 std::string value; 533 bool findTotalMem = false; 534 do { 535 for (size_t i = 0; i < strlen(line); i++) { 536 if (line[i] == ':') { 537 line[i] = ' '; 538 } 539 } 540 std::string lineStr(line); 541 std::istringstream is(lineStr); 542 543 is >> name >> value; 544 if (name == itemName) { 545 findTotalMem = true; 546 break; 547 } 548 line = strtok_r(NULL, "\n", &restPtr); 549 } while (line); 550 if (contentPtr) { 551 delete [] contentPtr; 552 } 553 554 if (findTotalMem == false) { 555 return -1; 556 } 557 std::string valueTemp = ""; 558 for (auto c : value) { 559 if (c >= '0' && c <= '9') { 560 valueTemp = valueTemp + c; 561 } 562 } 563 return atoi(valueTemp.c_str()); 564} 565 566int KernelInterface::GetTotalBuffer() 567{ 568 if (totalBuffer_ >= 0) { 569 return totalBuffer_; 570 } 571 572 std::string contentStr; 573 if (!ReadFromFile(MEMINFO_PATH, contentStr)) { 574 HILOGE("read %{public}s faild, content=[%{public}s]", MEMINFO_PATH.c_str(), contentStr.c_str()); 575 return -1; 576 } 577 totalBuffer_ = ParseMeminfo(contentStr, TOTAL_MEMORY); 578 return totalBuffer_; 579} 580 581bool KernelInterface::GetMemcgPids(const std::string &memcgPath, std::vector<int> &memcgPids) 582{ 583 std::string path = JoinPath(memcgPath, FILE_MEMCG_PROCS); 584 std::vector<std::string> strLines; 585 if (!ReadLinesFromFile(path, strLines)) { 586 HILOGE("read file and split to lines failed : %{public}s", path.c_str()); 587 return false; 588 } 589 590 memcgPids.clear(); 591 int pid; 592 for (auto &it : strLines) { 593 try { 594 pid = stoi(it); 595 } catch (...) { 596 continue; 597 } 598 memcgPids.emplace_back(pid); 599 } 600 HILOGD("there are %{public}zu pids in %{public}s", memcgPids.size(), path.c_str()); 601 return true; 602} 603 604bool KernelInterface::GetAllUserIds(std::vector<int> &userIds) 605{ 606 userIds.clear(); 607 DIR *dir = opendir(MEMCG_BASE_PATH.c_str()); 608 if (dir == nullptr) { 609 HILOGE("dir %{public}s is not exist", MEMCG_BASE_PATH.c_str()); 610 return false; 611 } 612 struct dirent *ptr = nullptr; 613 while ((ptr = readdir(dir)) != nullptr) { 614 if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) { 615 // current dir OR parent dir 616 continue; 617 } else if (ptr->d_type == DT_DIR) { 618 int userId = atoi(ptr->d_name); 619 if (userId > 0) { 620 userIds.push_back(userId); 621 } 622 } 623 } 624 if (dir) { 625 closedir(dir); 626 } 627 HILOGD("there are %{public}zu userIds under %{public}s", userIds.size(), MEMCG_BASE_PATH.c_str()); 628 return true; 629} 630 631void KernelInterface::SplitOneLineByDelim(const std::string &input, const char delimiter, 632 std::vector<std::string> &res) 633{ 634 std::stringstream ss(input); 635 std::string temp; 636 while (getline(ss, temp, delimiter)) { 637 if (!temp.empty()) { 638 res.emplace_back(temp); 639 } 640 } 641} 642 643void KernelInterface::SplitOneLineByBlank(const std::string &input, std::vector<std::string> &res) 644{ 645 std::stringstream ss(input); 646 std::string temp; 647 while (ss >> temp) { 648 if (!temp.empty()) { 649 res.emplace_back(temp); 650 } 651 } 652} 653} // namespace Memory 654} // namespace OHOS 655