1 /*
2 * Copyright (c) 2022-2023 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 "plugin_mgr.h"
17 #include <cinttypes>
18 #include <algorithm>
19 #include <cstdint>
20 #include <dlfcn.h>
21 #include <iostream>
22 #include <string>
23 #ifdef RESSCHED_CUSTOMIZATION_CONFIG_POLICY_ENABLE
24 #include "config_policy_utils.h"
25 #endif // RESSCHED_CUSTOMIZATION_CONFIG_POLICY_ENABLE
26 #include "event_runner.h"
27 #include "hisysevent.h"
28 #include "refbase.h"
29 #include "res_sched_log.h"
30 #include "hitrace_meter.h"
31
32 using namespace std;
33
34 namespace OHOS {
35 namespace ResourceSchedule {
36 using namespace AppExecFwk;
37 using namespace HiviewDFX;
38 using OnPluginInitFunc = bool (*)(std::string);
39
40 namespace {
41 const int32_t DISPATCH_WARNING_TIME = 10; // ms
42 const std::string RUNNER_NAME = "rssDispatcher";
43 const std::string ASYNC_RUNNER_NAME = "rssAsyncRunner";
44 #ifdef RESSCHED_CUSTOMIZATION_CONFIG_POLICY_ENABLE
45 const char* PLUGIN_SWITCH_FILE_NAME = "etc/ressched/res_sched_plugin_switch.xml";
46 const char* CONFIG_FILE_NAME = "etc/ressched/res_sched_config.xml";
47 #endif // RESSCHED_CUSTOMIZATION_CONFIG_POLICY_ENABLE
48 }
49
50 IMPLEMENT_SINGLE_INSTANCE(PluginMgr);
51
~PluginMgr()52 PluginMgr::~PluginMgr()
53 {
54 OnDestroy();
55 }
56
Init()57 void PluginMgr::Init()
58 {
59 if (pluginSwitch_) {
60 RESSCHED_LOGW("%{public}s, PluginMgr has Initialized!", __func__);
61 return;
62 }
63
64 #ifdef RESSCHED_CUSTOMIZATION_CONFIG_POLICY_ENABLE
65 if (!pluginSwitch_) {
66 pluginSwitch_ = make_unique<PluginSwitch>();
67 std::string realPath = GetRealConfigPath(PLUGIN_SWITCH_FILE_NAME);
68 if (realPath.empty() || !pluginSwitch_->LoadFromConfigFile(realPath)) {
69 RESSCHED_LOGW("%{public}s, PluginMgr load switch config file failed!", __func__);
70 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::RSS, "INIT_FAULT", HiviewDFX::HiSysEvent::EventType::FAULT,
71 "COMPONENT_NAME", "MAIN",
72 "ERR_TYPE", "configure error",
73 "ERR_MSG", "PluginMgr load switch config file failed!");
74 }
75 }
76
77 if (!configReader_) {
78 configReader_ = make_unique<ConfigReader>();
79 std::string realPath = GetRealConfigPath(CONFIG_FILE_NAME);
80 if (realPath.empty() || !configReader_->LoadFromCustConfigFile(realPath)) {
81 RESSCHED_LOGW("%{public}s, PluginMgr load config file failed!", __func__);
82 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::RSS, "INIT_FAULT", HiviewDFX::HiSysEvent::EventType::FAULT,
83 "COMPONENT_NAME", "MAIN",
84 "ERR_TYPE", "configure error",
85 "ERR_MSG", "PluginMgr load parameter config file failed!");
86 }
87 }
88 #endif // RESSCHED_CUSTOMIZATION_CONFIG_POLICY_ENABLE
89
90 {
91 std::lock_guard<std::mutex> autoLock(dispatcherHandlerMutex_);
92 if (!dispatcher_) {
93 dispatcher_ = std::make_shared<EventHandler>(EventRunner::Create(RUNNER_NAME));
94 }
95 if (!asyncRunner_) {
96 asyncRunner_ = EventRunner::Create(ASYNC_RUNNER_NAME);
97 }
98 }
99 LoadPlugin();
100 RESSCHED_LOGI("PluginMgr::Init success!");
101 }
102
LoadPlugin()103 void PluginMgr::LoadPlugin()
104 {
105 if (!pluginSwitch_) {
106 RESSCHED_LOGW("%{public}s, configReader null!", __func__);
107 return;
108 }
109
110 std::list<PluginInfo> pluginInfoList = pluginSwitch_->GetPluginSwitch();
111 for (const auto& info : pluginInfoList) {
112 if (!info.switchOn) {
113 continue;
114 }
115 shared_ptr<PluginLib> libInfoPtr = LoadOnePlugin(info);
116 if (libInfoPtr == nullptr) {
117 continue;
118 }
119 std::lock_guard<std::mutex> autoLock(pluginMutex_);
120 pluginLibMap_.emplace(info.libPath, *libInfoPtr);
121
122 RESSCHED_LOGI("%{public}s, init %{public}s success!", __func__, info.libPath.c_str());
123 }
124 }
125
LoadOnePlugin(const PluginInfo & info)126 shared_ptr<PluginLib> PluginMgr::LoadOnePlugin(const PluginInfo& info)
127 {
128 auto pluginHandle = dlopen(info.libPath.c_str(), RTLD_NOW);
129 if (!pluginHandle) {
130 RESSCHED_LOGE("%{public}s, not find plugin lib !", __func__);
131 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::RSS, "INIT_FAULT", HiviewDFX::HiSysEvent::EventType::FAULT,
132 "COMPONENT_NAME", info.libPath,
133 "ERR_TYPE", "plugin failure",
134 "ERR_MSG", "PluginMgr dlopen " + info.libPath + " failed!");
135 return nullptr;
136 }
137
138 auto onPluginInitFunc = reinterpret_cast<OnPluginInitFunc>(dlsym(pluginHandle, "OnPluginInit"));
139 if (!onPluginInitFunc) {
140 RESSCHED_LOGE("%{public}s, dlsym OnPluginInit failed!", __func__);
141 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::RSS, "INIT_FAULT", HiviewDFX::HiSysEvent::EventType::FAULT,
142 "COMPONENT_NAME", info.libPath,
143 "ERR_TYPE", "plugin failure",
144 "ERR_MSG", "PluginMgr don't found dlsym " + info.libPath + "!");
145 dlclose(pluginHandle);
146 return nullptr;
147 }
148
149 auto onPluginDisableFunc = reinterpret_cast<OnPluginDisableFunc>(dlsym(pluginHandle, "OnPluginDisable"));
150 if (!onPluginDisableFunc) {
151 RESSCHED_LOGE("%{public}s, dlsym OnPluginDisable failed!", __func__);
152 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::RSS, "INIT_FAULT", HiviewDFX::HiSysEvent::EventType::FAULT,
153 "COMPONENT_NAME", info.libPath,
154 "ERR_TYPE", "plugin failure",
155 "ERR_MSG", "PluginMgr don't found dlsym " + info.libPath + "!");
156 dlclose(pluginHandle);
157 return nullptr;
158 }
159
160 if (!onPluginInitFunc(info.libPath)) {
161 RESSCHED_LOGE("%{public}s, %{public}s init failed!", __func__, info.libPath.c_str());
162 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::RSS, "INIT_FAULT", HiviewDFX::HiSysEvent::EventType::FAULT,
163 "COMPONENT_NAME", info.libPath,
164 "ERR_TYPE", "plugin failure",
165 "ERR_MSG", "Plugin " + info.libPath + " init failed!");
166 dlclose(pluginHandle);
167 return nullptr;
168 }
169
170 // OnDispatchResource is not necessary for plugin
171 auto onDispatchResourceFunc = reinterpret_cast<OnDispatchResourceFunc>(dlsym(pluginHandle,
172 "OnDispatchResource"));
173
174 // OnDispatchResource is not necessary for plugin
175 auto onDumpFunc = reinterpret_cast<OnDumpFunc>(dlsym(pluginHandle, "OnDump"));
176
177 PluginLib libInfo;
178 libInfo.handle = nullptr;
179 libInfo.onPluginInitFunc_ = onPluginInitFunc;
180 libInfo.onDispatchResourceFunc_ = onDispatchResourceFunc;
181 libInfo.onDumpFunc_ = onDumpFunc;
182 libInfo.onPluginDisableFunc_ = onPluginDisableFunc;
183
184 return make_shared<PluginLib>(libInfo);
185 }
186
GetConfig(const std::string & pluginName,const std::string & configName)187 PluginConfig PluginMgr::GetConfig(const std::string& pluginName, const std::string& configName)
188 {
189 PluginConfig config;
190 if (!configReader_) {
191 return config;
192 }
193 return configReader_->GetConfig(pluginName, configName);
194 }
195
GetRunner()196 const std::shared_ptr<AppExecFwk::EventRunner>& PluginMgr::GetRunner()
197 {
198 std::lock_guard<std::mutex> autoLock(dispatcherHandlerMutex_);
199 if (!asyncRunner_) {
200 asyncRunner_ = EventRunner::Create(ASYNC_RUNNER_NAME);
201 }
202 return asyncRunner_;
203 }
204
Stop()205 void PluginMgr::Stop()
206 {
207 OnDestroy();
208 }
209
GetPluginListByResType(uint32_t resType,std::list<std::string> & pluginList)210 bool PluginMgr::GetPluginListByResType(uint32_t resType, std::list<std::string>& pluginList)
211 {
212 std::lock_guard<std::mutex> autoLock(resTypeMutex_);
213 auto iter = resTypeLibMap_.find(resType);
214 if (iter == resTypeLibMap_.end()) {
215 RESSCHED_LOGD("%{public}s, PluginMgr resType no lib register!", __func__);
216 return false;
217 }
218 pluginList = iter->second;
219 return true;
220 }
221
DispatchResource(const std::shared_ptr<ResData> & resData)222 void PluginMgr::DispatchResource(const std::shared_ptr<ResData>& resData)
223 {
224 if (!resData) {
225 RESSCHED_LOGE("%{public}s, failed, null res data.", __func__);
226 return;
227 }
228 std::list<std::string> pluginList;
229 if (!GetPluginListByResType(resData->resType, pluginList)) {
230 return;
231 }
232 std::string libNameAll = "[";
233 for (const auto& libName : pluginList) {
234 libNameAll.append(libName);
235 libNameAll.append(",");
236 }
237 libNameAll.append("]");
238 string trace_str(__func__);
239 string resTypeString =
240 ResType::resTypeToStr.count(resData->resType) ? ResType::resTypeToStr.at(resData->resType) : "UNKNOWN";
241 trace_str.append(" PluginMgr ,resType[").append(std::to_string(resData->resType)).append("]");
242 trace_str.append(",resTypeStr[").append(resTypeString).append("]");
243 trace_str.append(",value[").append(std::to_string(resData->value)).append("]");
244 trace_str.append(",pluginlist:").append(libNameAll);
245 StartTrace(HITRACE_TAG_OHOS, trace_str, -1);
246 RESSCHED_LOGD("%{public}s, PluginMgr, resType = %{public}d, "
247 "value = %{public}lld, pluginlist is %{public}s.", __func__,
248 resData->resType, (long long)resData->value, libNameAll.c_str());
249 FinishTrace(HITRACE_TAG_OHOS);
250
251 std::lock_guard<std::mutex> autoLock(dispatcherHandlerMutex_);
252 if (dispatcher_) {
253 dispatcher_->PostTask(
254 [pluginList, resData, this] {
255 DeliverResourceToPlugin(pluginList, resData);
256 });
257 }
258 }
259
SubscribeResource(const std::string & pluginLib,uint32_t resType)260 void PluginMgr::SubscribeResource(const std::string& pluginLib, uint32_t resType)
261 {
262 if (pluginLib.size() == 0) {
263 RESSCHED_LOGE("%{public}s, PluginMgr failed, pluginLib is null.", __func__);
264 return;
265 }
266 std::lock_guard<std::mutex> autoLock(resTypeMutex_);
267 resTypeLibMap_[resType].emplace_back(pluginLib);
268 }
269
UnSubscribeResource(const std::string & pluginLib,uint32_t resType)270 void PluginMgr::UnSubscribeResource(const std::string& pluginLib, uint32_t resType)
271 {
272 if (pluginLib.size() == 0) {
273 RESSCHED_LOGE("%{public}s, PluginMgr failed, pluginLib is null.", __func__);
274 return;
275 }
276 std::lock_guard<std::mutex> autoLock(resTypeMutex_);
277 auto iter = resTypeLibMap_.find(resType);
278 if (iter == resTypeLibMap_.end()) {
279 RESSCHED_LOGE("%{public}s, PluginMgr failed, res type has no plugin subscribe.", __func__);
280 return;
281 }
282
283 iter->second.remove(pluginLib);
284 if (iter->second.empty()) {
285 resTypeLibMap_.erase(iter);
286 }
287 }
288
DumpAllPlugin(std::string & result)289 void PluginMgr::DumpAllPlugin(std::string &result)
290 {
291 std::list<PluginInfo> pluginInfoList = pluginSwitch_->GetPluginSwitch();
292 for (const auto& info : pluginInfoList) {
293 result.append(info.libPath + std::string(DUMP_ONE_STRING_SIZE - info.libPath.size(), ' '));
294 DumpPluginInfoAppend(result, info);
295 }
296 }
297
DumpOnePlugin(std::string & result,std::string pluginName,std::vector<std::string> & args)298 void PluginMgr::DumpOnePlugin(std::string &result, std::string pluginName, std::vector<std::string>& args)
299 {
300 std::list<PluginInfo> pluginInfoList = pluginSwitch_->GetPluginSwitch();
301 auto pos = std::find_if(pluginInfoList.begin(),
302 pluginInfoList.end(), [&pluginName](PluginInfo &info) { return pluginName == info.libPath; });
303 if (pos == pluginInfoList.end()) {
304 result.append(" Error params.\n");
305 return;
306 }
307 if (args.size() == 0) {
308 result.append(pluginName + std::string(DUMP_ONE_STRING_SIZE - pluginName.size(), ' '));
309 DumpPluginInfoAppend(result, *pos);
310 } else {
311 result.append("\n");
312 std::string errMsg = DumpInfoFromPlugin(result, pos->libPath, args);
313 if (errMsg != "") {
314 result.append(errMsg);
315 }
316 }
317 }
318
DumpInfoFromPlugin(std::string & result,std::string libPath,std::vector<std::string> & args)319 std::string PluginMgr::DumpInfoFromPlugin(std::string& result, std::string libPath, std::vector<std::string>& args)
320 {
321 std::lock_guard<std::mutex> autoLock(pluginMutex_);
322 auto pluginLib = pluginLibMap_.find(libPath);
323 if (pluginLib == pluginLibMap_.end()) {
324 return "Error params.";
325 }
326
327 if (pluginLib->second.onDumpFunc_) {
328 pluginLib->second.onDumpFunc_(args, result);
329 result.append("\n");
330 }
331 return "";
332 }
333
DumpHelpFromPlugin(std::string & result)334 void PluginMgr::DumpHelpFromPlugin(std::string& result)
335 {
336 std::vector<std::string> args;
337 args.emplace_back("-h");
338 std::string pluginHelpMsg = "";
339 std::list<PluginInfo> pluginInfoList = pluginSwitch_->GetPluginSwitch();
340 for (auto &pluginInfo : pluginInfoList) {
341 DumpInfoFromPlugin(pluginHelpMsg, pluginInfo.libPath, args);
342 }
343 result.append(pluginHelpMsg);
344 }
345
DumpPluginInfoAppend(std::string & result,PluginInfo info)346 void PluginMgr::DumpPluginInfoAppend(std::string &result, PluginInfo info)
347 {
348 if (info.switchOn) {
349 result.append(" | switch on\t");
350 } else {
351 result.append(" | switch off\t");
352 }
353 std::lock_guard<std::mutex> autoLock(pluginMutex_);
354 if (pluginLibMap_.find(info.libPath) != pluginLibMap_.end()) {
355 result.append(" | running now\n");
356 } else {
357 result.append(" | disabled\n");
358 }
359 }
360
361 #ifdef RESSCHED_CUSTOMIZATION_CONFIG_POLICY_ENABLE
GetRealConfigPath(const char * configName)362 std::string PluginMgr::GetRealConfigPath(const char* configName)
363 {
364 char buf[PATH_MAX + 1];
365 char* configFilePath = GetOneCfgFile(configName, buf, PATH_MAX + 1);
366 char tmpPath[PATH_MAX + 1] = {0};
367 if (!configFilePath || strlen(configFilePath) == 0 || strlen(configFilePath) > PATH_MAX ||
368 !realpath(configFilePath, tmpPath)) {
369 RESSCHED_LOGE("%{public}s load config file wrong !", __func__);
370 return "";
371 }
372 return std::string(tmpPath);
373 }
374 #endif // RESSCHED_CUSTOMIZATION_CONFIG_POLICY_ENABLE
375
ClearResource()376 void PluginMgr::ClearResource()
377 {
378 std::lock_guard<std::mutex> autoLock(resTypeMutex_);
379 resTypeLibMap_.clear();
380 }
381
RepairPlugin(TimePoint endTime,const std::string & pluginLib,PluginLib libInfo)382 void PluginMgr::RepairPlugin(TimePoint endTime, const std::string& pluginLib, PluginLib libInfo)
383 {
384 auto& timeOutTime = pluginStat_[pluginLib].timeOutTime;
385 int32_t crash_time = (int32_t)((endTime - timeOutTime.front()) / std::chrono::milliseconds(1));
386 timeOutTime.emplace_back(endTime);
387 RESSCHED_LOGW("%{public}s %{public}s crash %{public}d times in %{public}d ms!", __func__,
388 pluginLib.c_str(), (int32_t)timeOutTime.size(), crash_time);
389 if ((int32_t)timeOutTime.size() >= MAX_PLUGIN_TIMEOUT_TIMES) {
390 if (crash_time < DISABLE_PLUGIN_TIME) {
391 // disable plugin forever
392 RESSCHED_LOGE("%{public}s, %{public}s disable it forever.", __func__, pluginLib.c_str());
393 if (libInfo.onPluginDisableFunc_) {
394 libInfo.onPluginDisableFunc_();
395 }
396 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::RSS, "PLUGIN_DISABLE",
397 HiSysEvent::EventType::FAULT, "plugin_name", pluginLib);
398 timeOutTime.clear();
399 pluginMutex_.lock();
400 pluginLibMap_.erase(pluginLib);
401 pluginMutex_.unlock();
402 return;
403 }
404
405 timeOutTime.pop_front();
406 }
407
408 if (libInfo.onPluginDisableFunc_ && libInfo.onPluginInitFunc_) {
409 RESSCHED_LOGW("%{public}s, %{public}s disable and enable it.", __func__, pluginLib.c_str());
410 libInfo.onPluginDisableFunc_();
411 libInfo.onPluginInitFunc_(pluginLib);
412 }
413 }
414
DeliverResourceToPlugin(const std::list<std::string> & pluginList,const std::shared_ptr<ResData> & resData)415 void PluginMgr::DeliverResourceToPlugin(const std::list<std::string>& pluginList,
416 const std::shared_ptr<ResData>& resData)
417 {
418 auto sortPluginList = pluginList;
419 sortPluginList.sort([&](const std::string& a, const std::string& b) -> bool {
420 if (pluginStat_.find(a) == pluginStat_.end() || pluginStat_.find(b) == pluginStat_.end()) {
421 return false;
422 }
423 return pluginStat_[a].AverageTime() < pluginStat_[b].AverageTime();
424 });
425
426 for (auto& pluginLib : sortPluginList) {
427 PluginLib libInfo;
428 {
429 std::lock_guard<std::mutex> autoLock(pluginMutex_);
430 auto itMap = pluginLibMap_.find(pluginLib);
431 if (itMap == pluginLibMap_.end()) {
432 RESSCHED_LOGE("%{public}s, no plugin %{public}s !", __func__, pluginLib.c_str());
433 continue;
434 }
435 libInfo = itMap->second;
436 }
437 OnDispatchResourceFunc pluginDispatchFunc = libInfo.onDispatchResourceFunc_;
438 if (!pluginDispatchFunc) {
439 RESSCHED_LOGE("%{public}s, no DispatchResourceFun !", __func__);
440 continue;
441 }
442 StartTrace(HITRACE_TAG_OHOS, pluginLib);
443 auto beginTime = Clock::now();
444 pluginDispatchFunc(resData);
445 auto endTime = Clock::now();
446 FinishTrace(HITRACE_TAG_OHOS);
447 int32_t costTimeUs = (endTime - beginTime) / std::chrono::microseconds(1);
448 int32_t costTime = costTimeUs / 1000;
449 pluginStat_[pluginLib].Update(costTimeUs);
450
451 if (costTime > DISPATCH_TIME_OUT) {
452 // dispatch resource use too long time, unload it
453 RESSCHED_LOGE("%{public}s, ERROR :"
454 "%{public}s plugin cost time(%{public}dms) over %{public}d ms! disable it.",
455 __func__, pluginLib.c_str(), costTime, DISPATCH_TIME_OUT);
456 auto task = [endTime, pluginLib, libInfo, this] {
457 RepairPlugin(endTime, pluginLib, libInfo);
458 };
459 std::lock_guard<std::mutex> autoLock2(dispatcherHandlerMutex_);
460 if (dispatcher_) {
461 dispatcher_->PostTask(task);
462 }
463 } else if (costTime > DISPATCH_WARNING_TIME) {
464 RESSCHED_LOGW("%{public}s, WARNING :"
465 "%{public}s plugin cost time(%{public}dms) over %{public}d ms!",
466 __func__, pluginLib.c_str(), costTime, DISPATCH_WARNING_TIME);
467 }
468 }
469 }
470
UnLoadPlugin()471 void PluginMgr::UnLoadPlugin()
472 {
473 std::lock_guard<std::mutex> autoLock(pluginMutex_);
474 // unload all plugin
475 for (const auto& [libPath, libInfo] : pluginLibMap_) {
476 if (!libInfo.onPluginDisableFunc_) {
477 continue;
478 }
479 libInfo.onPluginDisableFunc_();
480 }
481 // close all plugin handle
482 pluginLibMap_.clear();
483 }
484
OnDestroy()485 void PluginMgr::OnDestroy()
486 {
487 UnLoadPlugin();
488 configReader_ = nullptr;
489 ClearResource();
490 std::lock_guard<std::mutex> autoLock(dispatcherHandlerMutex_);
491 if (dispatcher_) {
492 dispatcher_->RemoveAllEvents();
493 dispatcher_ = nullptr;
494 }
495 if (asyncRunner_) {
496 asyncRunner_ = nullptr;
497 }
498 }
499 } // namespace ResourceSchedule
500 } // namespace OHOS