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