1 /*
2 * Copyright (c) 2024 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 #include "memory/rs_memory_snapshot.h"
16
17 #include "platform/common/rs_log.h"
18 #include <vector>
19
20 namespace OHOS {
21 namespace Rosen {
22 namespace {
23 constexpr uint32_t MEMUNIT_RATE = 1024;
24 constexpr int64_t MEMORY_SNAPSHOT_INTERVAL = 3 * 60 * 1000; // EachProcess can print at most once per 3 minute.
25 // Threshold for hilog in rs mem.
26 constexpr uint32_t MEMORY_SNAPSHOT_PRINT_HILOG_LIMIT = 1300 * MEMUNIT_RATE * MEMUNIT_RATE;
27 constexpr uint32_t HILOG_INFO_COUNT = 5; // The number of info printed each time.
28 constexpr float WEIGHT_CPU = 0.3; // Weight of CPU.
29 constexpr float WEIGHT_GPU = 0.3; // Weight of GPU.
30 constexpr float WEIGHT_SUM = 0.4; // Weight of CPU+GPU.
31 }
32
Instance()33 MemorySnapshot& MemorySnapshot::Instance()
34 {
35 static MemorySnapshot instance;
36 return instance;
37 }
38
AddCpuMemory(const pid_t pid,const size_t size)39 void MemorySnapshot::AddCpuMemory(const pid_t pid, const size_t size)
40 {
41 bool shouldReport = false;
42 size_t cpuMemory = 0;
43 {
44 std::lock_guard<std::mutex> lock(mutex_);
45 MemorySnapshotInfo& mInfo = appMemorySnapshots_[pid];
46 mInfo.pid = pid;
47 mInfo.cpuMemory += size;
48 totalMemory_ += size;
49 if (mInfo.cpuMemory > singleCpuMemoryLimit_ && mInfo.cpuMemory - size < singleCpuMemoryLimit_) {
50 shouldReport = true;
51 cpuMemory = mInfo.cpuMemory;
52 }
53 }
54 if (shouldReport && callback_) {
55 callback_(pid, cpuMemory, false);
56 }
57 }
58
RemoveCpuMemory(const pid_t pid,const size_t size)59 void MemorySnapshot::RemoveCpuMemory(const pid_t pid, const size_t size)
60 {
61 std::lock_guard<std::mutex> lock(mutex_);
62 auto it = appMemorySnapshots_.find(pid);
63 if (it != appMemorySnapshots_.end()) {
64 it->second.cpuMemory -= size;
65 totalMemory_ -= size;
66 }
67 }
68
GetMemorySnapshotInfoByPid(const pid_t pid,MemorySnapshotInfo & info)69 bool MemorySnapshot::GetMemorySnapshotInfoByPid(const pid_t pid, MemorySnapshotInfo& info)
70 {
71 std::lock_guard<std::mutex> lock(mutex_);
72 auto it = appMemorySnapshots_.find(pid);
73 if (it == appMemorySnapshots_.end()) {
74 return false;
75 }
76 info = it->second;
77 return true;
78 }
79
UpdateGpuMemoryInfo(const std::unordered_map<pid_t,size_t> & gpuInfo,std::unordered_map<pid_t,MemorySnapshotInfo> & pidForReport,bool & isTotalOver)80 void MemorySnapshot::UpdateGpuMemoryInfo(const std::unordered_map<pid_t, size_t>& gpuInfo,
81 std::unordered_map<pid_t, MemorySnapshotInfo>& pidForReport, bool& isTotalOver)
82 {
83 std::lock_guard<std::mutex> lock(mutex_);
84 for (auto& [pid, info] : appMemorySnapshots_) {
85 auto it = gpuInfo.find(pid);
86 if (it != gpuInfo.end()) {
87 totalMemory_ = totalMemory_ - info.gpuMemory + it->second;
88 info.gpuMemory = it->second;
89 }
90 if (info.TotalMemory() > singleMemoryWarning_) {
91 pidForReport.emplace(pid, info);
92 }
93 }
94 if (totalMemory_ > totalMemoryLimit_) {
95 pidForReport = appMemorySnapshots_;
96 isTotalOver = true;
97 }
98 if (totalMemory_ > MEMORY_SNAPSHOT_PRINT_HILOG_LIMIT) {
99 PrintMemorySnapshotToHilog();
100 }
101 }
102
EraseSnapshotInfoByPid(const std::set<pid_t> & exitedPidSet)103 void MemorySnapshot::EraseSnapshotInfoByPid(const std::set<pid_t>& exitedPidSet)
104 {
105 std::lock_guard<std::mutex> lock(mutex_);
106 for (auto pid : exitedPidSet) {
107 auto it = appMemorySnapshots_.find(pid);
108 if (it != appMemorySnapshots_.end()) {
109 totalMemory_ -= it->second.TotalMemory();
110 appMemorySnapshots_.erase(it);
111 }
112 }
113 }
114
InitMemoryLimit(MemoryOverflowCalllback callback,uint64_t warning,uint64_t overflow,uint64_t totalSize)115 void MemorySnapshot::InitMemoryLimit(MemoryOverflowCalllback callback,
116 uint64_t warning, uint64_t overflow, uint64_t totalSize)
117 {
118 if (callback_ == nullptr) {
119 callback_ = callback;
120 singleMemoryWarning_ = warning;
121 singleCpuMemoryLimit_ = overflow;
122 totalMemoryLimit_ = totalSize;
123 }
124 }
125
GetMemorySnapshot(std::unordered_map<pid_t,MemorySnapshotInfo> & map)126 void MemorySnapshot::GetMemorySnapshot(std::unordered_map<pid_t, MemorySnapshotInfo>& map)
127 {
128 std::lock_guard<std::mutex> lock(mutex_);
129 map = appMemorySnapshots_;
130 }
131
GetTotalMemory()132 size_t MemorySnapshot::GetTotalMemory()
133 {
134 return totalMemory_;
135 }
136
PrintMemorySnapshotToHilog()137 void MemorySnapshot::PrintMemorySnapshotToHilog()
138 {
139 auto now = std::chrono::steady_clock::now().time_since_epoch();
140 int64_t currentTime = std::chrono::duration_cast<std::chrono::milliseconds>(now).count();
141 if (currentTime < memorySnapshotHilogTime_) {
142 return;
143 }
144
145 std::vector<MemorySnapshotInfo> memorySnapshotsList;
146 size_t maxCpu;
147 size_t maxGpu;
148 size_t maxSum;
149 FindMaxValues(memorySnapshotsList, maxCpu, maxGpu, maxSum);
150
151 // Sort by risk in descending order
152 std::sort(memorySnapshotsList.begin(), memorySnapshotsList.end(),
153 [=](const MemorySnapshotInfo& a, const MemorySnapshotInfo& b) {
154 float scoreA = CalculateRiskScore(a, maxCpu, maxGpu, maxSum);
155 float scoreB = CalculateRiskScore(b, maxCpu, maxGpu, maxSum);
156 return scoreA > scoreB;
157 });
158
159 std::string hilogInfo = "[";
160 for (size_t i = 0 ; i < HILOG_INFO_COUNT && i < memorySnapshotsList.size() ; i++) {
161 MemorySnapshotInfo info = memorySnapshotsList[i];
162 hilogInfo += "pid[" + std::to_string(info.pid) +
163 "] cpu[" + std::to_string(info.cpuMemory / MEMUNIT_RATE) +
164 "KB] gpu[" + std::to_string(info.gpuMemory / MEMUNIT_RATE) + "KB], ";
165 }
166 hilogInfo += "]";
167 RS_LOGE("TotalMemoryOverReport. TotalMemory:[%{public}zuKB], memorySnapshots:%{public}s",
168 totalMemory_ / MEMUNIT_RATE, hilogInfo.c_str());
169
170 memorySnapshotHilogTime_ = currentTime + MEMORY_SNAPSHOT_INTERVAL;
171 }
172
FindMaxValues(std::vector<MemorySnapshotInfo> & memorySnapshotsList,size_t & maxCpu,size_t & maxGpu,size_t & maxSum)173 void MemorySnapshot::FindMaxValues(std::vector<MemorySnapshotInfo>& memorySnapshotsList,
174 size_t& maxCpu, size_t& maxGpu, size_t& maxSum)
175 {
176 maxCpu = maxGpu = maxSum = 0;
177 for (const auto& [pid, snapshotInfo] : appMemorySnapshots_) {
178 memorySnapshotsList.push_back(snapshotInfo);
179
180 if (snapshotInfo.cpuMemory > maxCpu) {
181 maxCpu = snapshotInfo.cpuMemory;
182 }
183
184 if (snapshotInfo.gpuMemory > maxGpu) {
185 maxGpu = snapshotInfo.gpuMemory;
186 }
187
188 size_t totalMemory = snapshotInfo.TotalMemory();
189 if (totalMemory > maxSum) {
190 maxSum = totalMemory;
191 }
192 }
193 }
194
CalculateRiskScore(const MemorySnapshotInfo snapshotInfo,size_t maxCpu,size_t maxGpu,size_t maxSum)195 float MemorySnapshot::CalculateRiskScore(const MemorySnapshotInfo snapshotInfo,
196 size_t maxCpu, size_t maxGpu, size_t maxSum)
197 {
198 float normCpu = (maxCpu == 0) ? 0 : static_cast<float>(snapshotInfo.cpuMemory) / maxCpu;
199 float normGpu = (maxGpu == 0) ? 0 : static_cast<float>(snapshotInfo.gpuMemory) / maxGpu;
200 float normSum = (maxSum == 0) ? 0 : static_cast<float>(snapshotInfo.TotalMemory()) / maxSum;
201 return WEIGHT_CPU * normCpu + WEIGHT_GPU * normGpu + WEIGHT_SUM * normSum;
202 }
203 }
204 } // namespace OHOS::Rosen