1 /*
2 * Copyright (C) 2025 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 "instance_memory_update_event_handler.h"
17 #include <fstream>
18 #include <cmath>
19 #include "cJSON.h"
20 #include "client/memory_collector.h"
21 #include "parameters.h"
22 #include "avcodec_info.h"
23 #include "avcodec_server_manager.h"
24 #include "avcodec_log.h"
25 #include "avcodec_errors.h"
26 #include "meta/meta_key.h"
27
28 namespace {
29 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_FRAMEWORK, "InstanceMemoryUpdateEventHandler"};
30 constexpr int32_t MEMORY_LEAK_UPLOAD_TIMEOUT = 180; // seconds
31 constexpr uint32_t THRESHOLD_CONFIG_FILE_SIZE_MAX = 1024'576; // 1024576, 1M
32 } // namespace
33
34 namespace OHOS {
35 namespace MediaAVCodec {
GetInstance()36 InstanceMemoryUpdateEventHandler &InstanceMemoryUpdateEventHandler::GetInstance()
37 {
38 static InstanceMemoryUpdateEventHandler handler;
39 return handler;
40 }
41
OnInstanceMemoryUpdate(const Media::Meta & meta)42 void InstanceMemoryUpdateEventHandler::OnInstanceMemoryUpdate(const Media::Meta &meta)
43 {
44 if (appMemoryThreshold_ == UINT32_MAX) {
45 return;
46 }
47 auto instanceId = EventInfoExtentedKey::GetInstanceIdFromMeta(meta);
48 CHECK_AND_RETURN_LOG(instanceId != INVALID_INSTANCE_ID, "Can not find instance id");
49
50 auto calculator = GetCalculator(meta);
51 CHECK_AND_RETURN_LOG(calculator != std::nullopt, "Can not find a calculator");
52 auto instanceMemory = calculator.value()(GetBlockCount(meta));
53
54 auto instanceInfo = UpdateInstanceMemory(instanceId, instanceMemory);
55 if (instanceInfo == std::nullopt) {
56 return;
57 }
58
59 DeterminAppMemoryExceedThresholdAndReport(instanceInfo.value().caller.pid, instanceInfo.value().forwardCaller.pid);
60 }
61
OnInstanceMemoryReset(const Media::Meta & meta)62 void InstanceMemoryUpdateEventHandler::OnInstanceMemoryReset(const Media::Meta &meta)
63 {
64 if (appMemoryThreshold_ == UINT32_MAX) {
65 return;
66 }
67 auto instanceId = EventInfoExtentedKey::GetInstanceIdFromMeta(meta);
68 CHECK_AND_RETURN_LOG(instanceId != INVALID_INSTANCE_ID, "Can not find instance id");
69
70 auto instanceInfo = UpdateInstanceMemory(instanceId, 0);
71 if (instanceInfo == std::nullopt) {
72 return;
73 }
74
75 DeterminAppMemoryExceedThresholdAndReport(instanceInfo.value().caller.pid, instanceInfo.value().forwardCaller.pid);
76 }
77
OnInstanceRelease(const Media::Meta & meta)78 void InstanceMemoryUpdateEventHandler::OnInstanceRelease(const Media::Meta &meta)
79 {
80 if (appMemoryThreshold_ == UINT32_MAX) {
81 return;
82 }
83 auto callerPid = INVALID_PID;
84 auto forwardCallerPid = INVALID_PID;
85 meta.GetData(Media::Tag::AV_CODEC_CALLER_PID, callerPid);
86 meta.GetData(Media::Tag::AV_CODEC_FORWARD_CALLER_PID, forwardCallerPid);
87 DeterminAppMemoryExceedThresholdAndReport(callerPid, forwardCallerPid);
88 }
89
RemoveTimer(pid_t pid)90 void InstanceMemoryUpdateEventHandler::RemoveTimer(pid_t pid)
91 {
92 std::lock_guard<std::shared_mutex> lock(timerMutex_);
93 auto num = timerMap_.erase(pid);
94 EXPECT_AND_LOGI(num > 0, "Timer for pid %{public}d has been removed", pid);
95 }
96
AddApp2ExceedThresholdList(pid_t pid)97 void InstanceMemoryUpdateEventHandler::AddApp2ExceedThresholdList(pid_t pid)
98 {
99 std::lock_guard<std::shared_mutex> lock(appMemoryExceedThresholdListMutex_);
100 appMemoryExceedThresholdList_.emplace(pid);
101 AVCODEC_LOGI("Pid: %{public}d has been added to exceeded threshold list", pid);
102 }
103
InstanceMemoryUpdateEventHandler()104 InstanceMemoryUpdateEventHandler::InstanceMemoryUpdateEventHandler()
105 {
106 UpdateAppMemoryThreshold();
107 }
108
GetBlockCount(const Media::Meta & meta)109 uint32_t InstanceMemoryUpdateEventHandler::GetBlockCount(const Media::Meta &meta)
110 {
111 int32_t width = 0;
112 int32_t height = 0;
113 AVCodecType codecType = AVCODEC_TYPE_VIDEO_DECODER;
114 meta.GetData(EventInfoExtentedKey::CODEC_TYPE.data(), codecType);
115 if (codecType == AVCODEC_TYPE_VIDEO_DECODER) {
116 meta.GetData(Media::Tag::VIDEO_PIC_WIDTH, width);
117 meta.GetData(Media::Tag::VIDEO_PIC_HEIGHT, height);
118 } else {
119 meta.GetData(Media::Tag::VIDEO_WIDTH, width);
120 meta.GetData(Media::Tag::VIDEO_HEIGHT, height);
121 }
122 constexpr int32_t blockWidth = 16;
123 constexpr int32_t blockHeight = 16;
124 return std::ceil(width / blockWidth) * std::ceil(height / blockHeight);
125 }
126
UpdateInstanceMemory(int32_t instanceId,uint32_t memory)127 std::optional<InstanceInfo> InstanceMemoryUpdateEventHandler::UpdateInstanceMemory(int32_t instanceId, uint32_t memory)
128 {
129 auto instanceInfo = AVCodecServerManager::GetInstance().GetInstanceInfoByInstanceId(instanceId);
130 CHECK_AND_RETURN_RET_LOG(instanceInfo != std::nullopt,
131 std::nullopt, "Can not find this instance, id: %{public}d", instanceId);
132
133 if (instanceInfo.value().memoryUsage == memory) {
134 return std::nullopt;
135 }
136 instanceInfo.value().memoryUsage = memory;
137 AVCodecServerManager::GetInstance().SetInstanceInfoByInstanceId(instanceId, instanceInfo.value());
138 AVCODEC_LOGD("The memory usage of instance[%{public}d] has been updated to %{public}u", instanceId, memory);
139
140 return instanceInfo;
141 }
142
UpdateAppMemoryThreshold()143 void InstanceMemoryUpdateEventHandler::UpdateAppMemoryThreshold()
144 {
145 appMemoryThreshold_ = ThresholdParser::GetThreshold();
146 }
147
SumAppMemory(pid_t callerPid,pid_t actualCallerPid)148 uint32_t InstanceMemoryUpdateEventHandler::SumAppMemory(pid_t callerPid, pid_t actualCallerPid)
149 {
150 auto instanceInfoList = AVCodecServerManager::GetInstance().GetInstanceInfoListByActualPid(actualCallerPid);
151 uint32_t appMemoryUsage = 0;
152 for (const auto &info : instanceInfoList) {
153 if (actualCallerPid != callerPid && actualCallerPid != info.second.forwardCaller.pid) {
154 continue;
155 }
156 appMemoryUsage += info.second.memoryUsage;
157 }
158 return appMemoryUsage;
159 }
160
ReportAppMemory(pid_t callerPid,pid_t actualCallerPid,bool isInTimer,uint32_t memory)161 void InstanceMemoryUpdateEventHandler::ReportAppMemory(pid_t callerPid, pid_t actualCallerPid,
162 bool isInTimer, uint32_t memory)
163 {
164 if (memory == 0) {
165 memory = SumAppMemory(callerPid, actualCallerPid);
166 }
167 if (isInTimer) {
168 InstanceMemoryUpdateEventHandler::GetInstance().RemoveTimer(actualCallerPid);
169 InstanceMemoryUpdateEventHandler::GetInstance().AddApp2ExceedThresholdList(actualCallerPid);
170 }
171
172 auto memoryCollector = HiviewDFX::UCollectClient::MemoryCollector::Create();
173 CHECK_AND_RETURN_LOG(memoryCollector != nullptr, "Create Hiview DFX memory collector failed");
174
175 std::vector<HiviewDFX::UCollectClient::MemoryCaller> memList;
176 HiviewDFX::UCollectClient::MemoryCaller memoryCaller = {
177 .pid = actualCallerPid,
178 .resourceType = "AVCodec",
179 .limitValue = memory,
180 };
181 memList.emplace_back(memoryCaller);
182 memoryCollector->SetSplitMemoryValue(memList);
183 AVCODEC_LOGI("The memory usage of pid %{public}d is %{public}u KB, report to hivew", actualCallerPid, memory);
184 }
185
DeterminAppMemoryExceedThresholdAndReport(pid_t callerPid,pid_t forwardCallerPid)186 void InstanceMemoryUpdateEventHandler::DeterminAppMemoryExceedThresholdAndReport(pid_t callerPid,
187 pid_t forwardCallerPid)
188 {
189 auto actualCallerPid = forwardCallerPid == INVALID_PID ? callerPid : forwardCallerPid;
190 if (actualCallerPid == INVALID_PID) {
191 return;
192 }
193 auto memory = SumAppMemory(callerPid, actualCallerPid);
194 auto appMemoryExceedThreshold = memory > appMemoryThreshold_;
195 auto appExistTimer = false;
196 {
197 std::shared_lock<std::shared_mutex> timerLock(timerMutex_);
198 appExistTimer = timerMap_.count(actualCallerPid) != 0;
199 }
200 auto appInExceedThresholdList = false;
201 {
202 std::shared_lock<std::shared_mutex> appMemoryExceedThresholdListlock(appMemoryExceedThresholdListMutex_);
203 appInExceedThresholdList = appMemoryExceedThresholdList_.count(actualCallerPid) != 0;
204 }
205 if (appMemoryExceedThreshold && !appExistTimer && !appInExceedThresholdList) {
206 auto timeName = std::string("Pid_") + std::to_string(actualCallerPid) + " memory exceeded threshold";
207 auto timer = std::make_shared<AVCodecXcollieTimer>(timeName, false, MEMORY_LEAK_UPLOAD_TIMEOUT,
208 [=](void *) -> void { ReportAppMemory(callerPid, actualCallerPid); });
209 std::lock_guard<std::shared_mutex> timerLock(timerMutex_);
210 timerMap_.emplace(actualCallerPid, timer);
211 AVCODEC_LOGI("Determined pid %{public}d memory(%{public}u KB) exceed threshold(%{public}u KB), "
212 "event timer added", actualCallerPid, memory, appMemoryThreshold_);
213 } else if (!appMemoryExceedThreshold && appExistTimer && !appInExceedThresholdList) {
214 InstanceMemoryUpdateEventHandler::GetInstance().RemoveTimer(actualCallerPid);
215 } else if (appMemoryExceedThreshold && !appExistTimer && appInExceedThresholdList) {
216 ReportAppMemory(callerPid, actualCallerPid, false, memory);
217 } else if (!appMemoryExceedThreshold && !appExistTimer && appInExceedThresholdList) {
218 ReportAppMemory(callerPid, actualCallerPid, false, memory);
219 std::lock_guard<std::shared_mutex> appMemoryExceedThresholdListlock(appMemoryExceedThresholdListMutex_);
220 appMemoryExceedThresholdList_.erase(actualCallerPid);
221 AVCODEC_LOGI("Pid: %{public}d has been removed from exceeded threshold list", actualCallerPid);
222 }
223 }
224
GetThreshold()225 uint32_t InstanceMemoryUpdateEventHandler::ThresholdParser::GetThreshold()
226 {
227 std::ifstream thresholdConfigFile("/system/etc/hiview/native_leak_config.json");
228 CHECK_AND_RETURN_RET_LOG(thresholdConfigFile.is_open(), UINT32_MAX, "Can not open threshold config json file");
229
230 std::string line;
231 std::string configJson;
232 while (thresholdConfigFile >> line && configJson.size() < THRESHOLD_CONFIG_FILE_SIZE_MAX) {
233 configJson += line;
234 }
235 auto json = cJSON_Parse(configJson.c_str());
236 CHECK_AND_RETURN_RET_LOG(json != nullptr, UINT32_MAX, "Can not parse threshold config json");
237
238 auto root = std::shared_ptr<cJSON>(json, cJSON_Delete);
239 auto deviceType = system::GetDeviceType();
240 CHECK_AND_RETURN_RET_LOG(deviceType != "" && deviceType != "unknown", UINT32_MAX, "Can not get device type");
241
242 auto avcodecConfigItem = cJSON_GetObjectItem(root.get(), "av_codec_config");
243 CHECK_AND_RETURN_RET_LOG(avcodecConfigItem != nullptr,
244 UINT32_MAX, "Can not find av_codec_config from threshold config json");
245
246 auto thresholdItem = cJSON_GetObjectItem(avcodecConfigItem, deviceType.c_str());
247 CHECK_AND_RETURN_RET_LOG(thresholdItem != nullptr && cJSON_IsNumber(thresholdItem),
248 UINT32_MAX, "Can not find threshold of %{public}s from av_codec_config", deviceType.c_str());
249
250 AVCODEC_LOGI("Got threshold of %{public}s, %{public}u KB", deviceType.c_str(), thresholdItem->valueint);
251 return thresholdItem->valueint;
252 }
253 } // namespace MediaAVCodec
254 } // namespace OHOS