/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "purgeable_mem_utils.h" #include <climits> #include <unordered_map> #include <string> #include "kernel_interface.h" #include "memmgr_log.h" namespace OHOS { namespace Memory { namespace { const std::string TAG = "PurgeableMemUtils"; } IMPLEMENT_SINGLE_INSTANCE(PurgeableMemUtils); const std::string PurgeableMemUtils::PATH_PURGE_HEAP = "/proc/sys/kernel/purgeable"; const std::string PurgeableMemUtils::PATH_PURGEABLE_ASHMEM = "/proc/purgeable_ashmem_trigger"; const std::string PurgeableMemUtils::FILE_PURGE_MEMCG_HEAP = "memory.force_shrink_purgeable_bysize"; const std::string PurgeableMemUtils::ACTIVE_PURGEABLE_HEAP = "Active(purg):"; const std::string PurgeableMemUtils::INACTIVE_PURGEABLE_HEAP = "Inactive(purg):"; const std::string PurgeableMemUtils::PINED_PURGEABLE_HEAP = "Pined(purg):"; const std::string PurgeableMemUtils::PROC_PURGEABLE_HEAP = "PurgSum:"; const std::string PurgeableMemUtils::PROC_PINED_PURGEABLE_HEAP = "PurgPin:"; const unsigned int PurgeableMemUtils::ASHM_PARAM_SIZE_ONE_LINE = 10; const unsigned int PurgeableMemUtils::ASHM_ID_INDEX = 6; const unsigned int PurgeableMemUtils::ASHM_ADJ_INDEX = 2; const unsigned int PurgeableMemUtils::ASHM_REF_COUNT_INDEX = 8; const unsigned int PurgeableMemUtils::ASHM_PURGED_INDEX = 9; const unsigned int PurgeableMemUtils::ASHM_SIZE_INDEX = 5; const unsigned int PurgeableMemUtils::ASHM_TIME_INDEX = 7; const unsigned int PurgeableMemUtils::HEAPINFO_SIZE_ONE_LINE = 3; const unsigned int PurgeableMemUtils::ASHM_PROCESS_NAME_INDEX = 0; bool PurgeableMemUtils::GetPurgeableHeapInfo(int &reclaimableKB) { std::vector<std::string> strLines; if (!KernelInterface::GetInstance().ReadLinesFromFile(KernelInterface::MEMINFO_PATH, strLines)) { HILOGE("read file and split to lines failed : %{public}s", KernelInterface::MEMINFO_PATH.c_str()); return false; } int activeKB = -1; int inactiveKB = -1; int pinedKB = -1; for (auto &it : strLines) { std::vector<std::string> words; KernelInterface::GetInstance().SplitOneLineByBlank(it, words); if (words.size() != HEAPINFO_SIZE_ONE_LINE) { continue; } try { if (words[0] == ACTIVE_PURGEABLE_HEAP) { activeKB = stoi(words[1]); } else if (words[0] == INACTIVE_PURGEABLE_HEAP) { inactiveKB = stoi(words[1]); } else if (words[0] == PINED_PURGEABLE_HEAP) { pinedKB = stoi(words[1]); } } catch (...) { HILOGE("stoi(%{public}s) failed", words[1].c_str()); return false; } } if (activeKB < 0 || inactiveKB < 0 || pinedKB < 0) { return false; } if (activeKB > (INT_MAX - inactiveKB) || (activeKB + inactiveKB) < (INT_MIN + pinedKB)) { return false; } reclaimableKB = activeKB + inactiveKB - pinedKB; if (reclaimableKB >= 0) { return true; } return false; } bool PurgeableMemUtils::GetProcPurgeableHeapInfo(const int pid, int &reclaimableKB) { std::string path = KernelInterface::GetInstance().JoinPath(KernelInterface::ROOT_PROC_PATH, std::to_string(pid), KernelInterface::FILE_PROC_STATUS); std::vector<std::string> strLines; if (!KernelInterface::GetInstance().ReadLinesFromFile(path, strLines)) { HILOGE("read file and split to lines failed : %{public}s", path.c_str()); return false; } int purgSumKB = -1; int purgPinKB = -1; for (auto &it : strLines) { std::vector<std::string> words; KernelInterface::GetInstance().SplitOneLineByBlank(it, words); if (words.size() != HEAPINFO_SIZE_ONE_LINE) { continue; } try { if (words[0] == PROC_PURGEABLE_HEAP) { purgSumKB = stoi(words[1]); } else if (words[0] == PROC_PINED_PURGEABLE_HEAP) { purgPinKB = stoi(words[1]); } } catch (...) { HILOGE("stoi(%{public}s) failed", words[1].c_str()); return false; } } if (purgSumKB < 0 || purgPinKB < 0) { return false; } reclaimableKB = purgSumKB - purgPinKB; if (reclaimableKB >= 0) { return true; } return false; } bool PurgeableMemUtils::PurgeHeapAll() { HILOGD("enter! Purg heap memory all"); return KernelInterface::GetInstance().EchoToPath(PATH_PURGE_HEAP.c_str(), "1"); } bool PurgeableMemUtils::PurgeHeapMemcg(const std::string &memcgPath, const int sizeKB) { std::string path = KernelInterface::GetInstance().JoinPath(memcgPath, FILE_PURGE_MEMCG_HEAP); HILOGD("enter! Purg heap memory by memcg: size=%{public}d, path=%{public}s\n", sizeKB, path.c_str()); return KernelInterface::GetInstance().EchoToPath(path.c_str(), std::to_string(sizeKB).c_str()); } bool PurgeableMemUtils::GetPurgeableAshmInfo(int &reclaimableKB, std::vector<PurgeableAshmInfo> &ashmInfoToReclaim) { std::vector<std::string> strLines; if (!KernelInterface::GetInstance().ReadLinesFromFile(PATH_PURGEABLE_ASHMEM, strLines)) { HILOGE("read file and split to lines failed : %{public}s", PATH_PURGEABLE_ASHMEM.c_str()); return false; } std::unordered_map<std::string, PurgeableAshmInfo> ashmIdToInfoMap; ashmIdToInfoMap = PurgeableMemUtils::GetashmIdToInfoMap(strLines); reclaimableKB = 0; ashmInfoToReclaim.clear(); for (const auto &[_1, value] : ashmIdToInfoMap) { if (value.sizeKB > 0) { ashmInfoToReclaim.emplace_back(value); reclaimableKB += value.sizeKB; } } HILOGD("there are %{public}dKB reclaimable purgeable [ASHM], ashmInfoVector.size()=%{public}zu", reclaimableKB, ashmInfoToReclaim.size()); return true; } bool PurgeableMemUtils::PurgeAshmAll() { HILOGD("enter! Purg ashmem memory all"); return KernelInterface::GetInstance().EchoToPath(PATH_PURGEABLE_ASHMEM.c_str(), "0 0"); } bool PurgeableMemUtils::PurgeAshmByIdWithTime(const std::string &idWithTime) { HILOGD("enter! Purg ashmem memory: IdWithTime=%{public}s", idWithTime.c_str()); return KernelInterface::GetInstance().EchoToPath(PATH_PURGEABLE_ASHMEM.c_str(), idWithTime.c_str()); } PurgeableAshmInfoMap PurgeableMemUtils::GetashmIdToInfoMap(const std::vector<std::string> &strLines) const { std::unordered_map<std::string, PurgeableAshmInfo> ashmIdToInfoMap; for (auto &it : strLines) { HILOGD("[ASHM]: %{public}s", it.c_str()); std::vector<std::string> words; KernelInterface::GetInstance().SplitOneLineByDelim(it, ',', words); if (words.size() != ASHM_PARAM_SIZE_ONE_LINE || words[ASHM_REF_COUNT_INDEX] != "0" || words[ASHM_PURGED_INDEX] != "0") { continue; } std::string curAppName; int minPriority; int sizeKB; try { curAppName = words[ASHM_PROCESS_NAME_INDEX]; minPriority = stoi(words[ASHM_ADJ_INDEX]); sizeKB = stoi(words[ASHM_SIZE_INDEX]); } catch (...) { HILOGE("stoi(%{public}s) or stoi(%{public}s) or stoi(%{public}s) failed", words[ASHM_PROCESS_NAME_INDEX].c_str(), words[ASHM_ADJ_INDEX].c_str(), words[ASHM_SIZE_INDEX].c_str()); continue; } std::string key = words[ASHM_ID_INDEX] + std::string(" ") + words[ASHM_TIME_INDEX]; auto iter = ashmIdToInfoMap.find(key); if (iter == ashmIdToInfoMap.end()) { PurgeableAshmInfo info; info.curAppName = curAppName; info.minPriority = minPriority; info.sizeKB = sizeKB; info.idWithTime = key; ashmIdToInfoMap[key] = info; } else if (iter->second.minPriority > minPriority) { iter->second.minPriority = minPriority; } } return ashmIdToInfoMap; } } // namespace Memory } // namespace OHOS