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