• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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