1 /*
2  * Copyright (c) 2021-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 "core/common/ace_engine.h"
17 
18 #include <csignal>
19 #include "base/thread/background_task_executor.h"
20 #ifdef PLUGIN_COMPONENT_SUPPORTED
21 #include "core/common/plugin_manager.h"
22 #endif
23 
24 namespace OHOS::Ace {
25 namespace {
26 
27 #ifdef OHOS_STANDARD_SYSTEM
28 enum class SignalType {
29     SIGNAL_JSHEAP_OLD,
30     SIGNAL_JSHEAP,
31     SIGNAL_JSHEAP_PRIV,
32     SIGNAL_NO_TRIGGERID,
33     SIGNAL_NO_TRIGGERID_PRIV,
34     SIGNAL_FORCE_FULLGC,
35 };
36 
37 void HandleSignal(int signal, [[maybe_unused]] siginfo_t *siginfo, void *context)
38 {
39     LOGW("HandleSignal start, signal is %{public}d", signal);
40     if (signal != MUSL_SIGNAL_JSHEAP) {
41         LOGW("HandleSignal failed, signal is %{public}d", signal);
42         return;
43     }
44     LOGW("HandleSignal sival_int is %{public}d", siginfo->si_value.sival_int);
45     switch (static_cast<SignalType>(siginfo->si_value.sival_int)) {
46         case SignalType::SIGNAL_JSHEAP_OLD: {
47             AceEngine::Get().DumpJsHeap(false);
48             break;
49         }
50         case SignalType::SIGNAL_JSHEAP: {
51             AceEngine::Get().DumpJsHeap(false);
52             break;
53         }
54         case SignalType::SIGNAL_JSHEAP_PRIV: {
55             AceEngine::Get().DumpJsHeap(true);
56             break;
57         }
58         case SignalType::SIGNAL_NO_TRIGGERID: {
59             AceEngine::Get().DumpJsHeap(false);
60             AceEngine::Get().DestroyHeapProfiler();
61             break;
62         }
63         case SignalType::SIGNAL_NO_TRIGGERID_PRIV: {
64             AceEngine::Get().DumpJsHeap(true);
65             AceEngine::Get().DestroyHeapProfiler();
66             break;
67         }
68         case SignalType::SIGNAL_FORCE_FULLGC: {
69             AceEngine::Get().ForceFullGC();
70             break;
71         }
72         default:
73             break;
74     }
75 }
76 #endif
77 
78 } // namespace
79 
AceEngine()80 AceEngine::AceEngine()
81 {
82     if (!SystemProperties::GetHookModeEnabled()) {
83         watchDog_ = AceType::MakeRefPtr<WatchDog>();
84     }
85 }
86 
~AceEngine()87 AceEngine::~AceEngine() {}
88 
Get()89 AceEngine& AceEngine::Get()
90 {
91     static AceEngine engine;
92     return engine;
93 }
94 
InitJsDumpHeadSignal()95 void AceEngine::InitJsDumpHeadSignal()
96 {
97 #ifdef OHOS_STANDARD_SYSTEM
98     struct sigaction sigAct;
99     sigemptyset(&sigAct.sa_mask);
100     sigAct.sa_flags = SA_SIGINFO;
101     sigAct.sa_sigaction = HandleSignal;
102     sigaction(MUSL_SIGNAL_JSHEAP, &sigAct, NULL);
103 #endif
104 }
105 
AddContainer(int32_t instanceId,const RefPtr<Container> & container)106 void AceEngine::AddContainer(int32_t instanceId, const RefPtr<Container>& container)
107 {
108     std::unique_lock<std::shared_mutex> lock(mutex_);
109     containerMap_.try_emplace(instanceId, container);
110 }
111 
RemoveContainer(int32_t instanceId)112 void AceEngine::RemoveContainer(int32_t instanceId)
113 {
114     size_t num = 0;
115     {
116         std::unique_lock<std::shared_mutex> lock(mutex_);
117         num = containerMap_.erase(instanceId);
118     }
119     if (num == 0) {
120         LOGW("container not found with instance id: %{public}d", instanceId);
121     }
122 }
123 
GetContainer(int32_t instanceId)124 RefPtr<Container> AceEngine::GetContainer(int32_t instanceId)
125 {
126 #ifdef PLUGIN_COMPONENT_SUPPORTED
127     if (instanceId >= MIN_PLUGIN_SUBCONTAINER_ID) {
128         instanceId = PluginManager::GetInstance().GetPluginParentContainerId(instanceId);
129     }
130 #endif
131     std::shared_lock<std::shared_mutex> lock(mutex_);
132     auto container = containerMap_.find(instanceId);
133     if (container != containerMap_.end()) {
134         return container->second;
135     } else {
136         return nullptr;
137     }
138 }
139 
HasContainer(int32_t containerId) const140 bool AceEngine::HasContainer(int32_t containerId) const
141 {
142     std::shared_lock<std::shared_mutex> lock(mutex_);
143     auto iter = containerMap_.find(containerId);
144     return iter != containerMap_.end();
145 }
146 
RegisterToWatchDog(int32_t instanceId,const RefPtr<TaskExecutor> & taskExecutor,bool useUIAsJSThread)147 void AceEngine::RegisterToWatchDog(int32_t instanceId, const RefPtr<TaskExecutor>& taskExecutor, bool useUIAsJSThread)
148 {
149     CHECK_NULL_VOID(watchDog_);
150     watchDog_->Register(instanceId, taskExecutor, useUIAsJSThread);
151 }
152 
UnRegisterFromWatchDog(int32_t instanceId)153 void AceEngine::UnRegisterFromWatchDog(int32_t instanceId)
154 {
155     CHECK_NULL_VOID(watchDog_);
156     watchDog_->Unregister(instanceId);
157 }
158 
BuriedBomb(int32_t instanceId,uint64_t bombId)159 void AceEngine::BuriedBomb(int32_t instanceId, uint64_t bombId)
160 {
161     CHECK_NULL_VOID(watchDog_);
162     watchDog_->BuriedBomb(instanceId, bombId);
163 }
164 
DefusingBomb(int32_t instanceId)165 void AceEngine::DefusingBomb(int32_t instanceId)
166 {
167     CHECK_NULL_VOID(watchDog_);
168     watchDog_->DefusingBomb(instanceId);
169 }
170 
TriggerGarbageCollection()171 void AceEngine::TriggerGarbageCollection()
172 {
173     decltype(containerMap_) copied;
174     {
175         std::shared_lock<std::shared_mutex> lock(mutex_);
176         if (containerMap_.empty()) {
177             return;
178         }
179         copied = containerMap_;
180     }
181 
182     auto taskExecutor = copied.begin()->second->GetTaskExecutor();
183     taskExecutor->PostTask([] { PurgeMallocCache(); },
184         TaskExecutor::TaskType::PLATFORM, "ArkUIPurgeMallocCache");
185 #if defined(OHOS_PLATFORM) && defined(ENABLE_NATIVE_VIEW)
186     // GPU and IO thread is shared while enable native view
187     taskExecutor->PostTask([] { PurgeMallocCache(); },
188         TaskExecutor::TaskType::GPU, "ArkUIPurgeMallocCache");
189     taskExecutor->PostTask([] { PurgeMallocCache(); },
190         TaskExecutor::TaskType::IO, "ArkUIPurgeMallocCache");
191 #endif
192 
193     for (const auto& container : copied) {
194         container.second->TriggerGarbageCollection();
195     }
196 
197     ImageCache::Purge();
198     BackgroundTaskExecutor::GetInstance().TriggerGarbageCollection();
199     PurgeMallocCache();
200 }
201 
NotifyContainers(const std::function<void (const RefPtr<Container> &)> & callback)202 void AceEngine::NotifyContainers(const std::function<void(const RefPtr<Container>&)>& callback)
203 {
204     CHECK_NULL_VOID(callback);
205     decltype(containerMap_) copied;
206     {
207         std::shared_lock<std::shared_mutex> lock(mutex_);
208         copied = containerMap_;
209     }
210     for (const auto& [first, second] : copied) {
211         // first = container ID
212         ContainerScope scope(first);
213         callback(second);
214     }
215 }
216 
NotifyContainersOrderly(const std::function<void (const RefPtr<Container> &)> & callback)217 void AceEngine::NotifyContainersOrderly(const std::function<void(const RefPtr<Container>&)>& callback)
218 {
219     CHECK_NULL_VOID(callback);
220     std::map<int32_t, RefPtr<Container>> copied;
221     {
222         std::shared_lock<std::shared_mutex> lock(mutex_);
223         for (const auto& pair : containerMap_) {
224             copied.insert(pair);
225         }
226     }
227     for (const auto& [first, second] : copied) {
228         // first = container ID
229         ContainerScope scope(first);
230         callback(second);
231     }
232 }
233 
DumpJsHeap(bool isPrivate) const234 void AceEngine::DumpJsHeap(bool isPrivate) const
235 {
236     decltype(containerMap_) copied;
237     {
238         std::shared_lock<std::shared_mutex> lock(mutex_);
239         copied = containerMap_;
240     }
241     for (const auto& container : copied) {
242         container.second->DumpHeapSnapshot(isPrivate);
243     }
244 }
245 
DestroyHeapProfiler() const246 void AceEngine::DestroyHeapProfiler() const
247 {
248     decltype(containerMap_) copied;
249     {
250         std::shared_lock<std::shared_mutex> lock(mutex_);
251         copied = containerMap_;
252     }
253     for (const auto& container : copied) {
254         container.second->DestroyHeapProfiler();
255     }
256 }
257 
ForceFullGC() const258 void AceEngine::ForceFullGC() const
259 {
260     decltype(containerMap_) copied;
261     {
262         std::shared_lock<std::shared_mutex> lock(mutex_);
263         copied = containerMap_;
264     }
265     for (const auto& container : copied) {
266         container.second->ForceFullGC();
267     }
268 }
269 
270 } // namespace OHOS::Ace
271