1 /*
2 * Copyright (c) 2020-2021 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 "fatal_handler.h"
17 #include "ace_event_error_code.h"
18 #include "ace_log.h"
19 #include "async_task_manager.h"
20 #if (OHOS_ACELITE_PRODUCT_WATCH == 1)
21 #include "cmsis_os2.h"
22 #endif // OHOS_ACELITE_PRODUCT_WATCH
23 #include "jerryscript-port-default.h"
24 #include "js_app_context.h"
25 #include "js_fwk_common.h"
26 #include "presets/console_log_impl.h"
27 #include "presets/feature_ability_module.h"
28 #include "product_adapter.h"
29 #include "root_view.h"
30 #include "task_manager.h"
31
32 namespace OHOS {
33 namespace ACELite {
34 /**
35 * Global instance for saving fatal error code and handling fatal errors
36 */
GetInstance()37 FatalHandler &FatalHandler::GetInstance()
38 {
39 static FatalHandler instance;
40 return instance;
41 }
42
IsErrorHittedWrapper()43 bool FatalHandler::IsErrorHittedWrapper()
44 {
45 return FatalHandler::GetInstance().IsFatalErrorHitted();
46 }
47
IsAppExitingWrapper()48 bool FatalHandler::IsAppExitingWrapper()
49 {
50 return FatalHandler::GetInstance().IsAppExiting();
51 }
52
HandleFatal(int errorCode)53 static void HandleFatal(int errorCode)
54 {
55 #if (FEATURE_FATAL_ERROR_HANDLING == 1)
56 HILOG_ERROR(HILOG_MODULE_ACE, "hitted by fatal error[%{public}d]", errorCode);
57 if (FatalHandler::GetInstance().IsFatalErrorHitted()) {
58 // fatal hitted again during one fatal handing, return to avoid dead loop
59 return;
60 }
61 // record error code
62 FatalHandler::GetInstance().SetFatalError(errorCode);
63 // send message to AMS to tear down us
64 // try to recycling resource first
65 FatalHandler::GetInstance().HandleFatalInternal();
66 // inform AMS we are facing fatal error
67 ProductAdapter::SendTerminatingRequest(JsAppContext::GetInstance()->GetCurrentAbilityToken(), true);
68 // sleep to stuck engien to make ANR for triggering force-stop
69 const uint32_t sleepTime = 3000;
70 uint8_t maxRetryCount = 20;
71 while (maxRetryCount > 0) {
72 #if (OHOS_ACELITE_PRODUCT_WATCH == 1)
73 osDelay(sleepTime);
74 #endif // OHOS_ACELITE_PRODUCT_WATCH
75 maxRetryCount--;
76 }
77 HILOG_ERROR(HILOG_MODULE_ACE, "the JS task is not killed after sleep very long time");
78 #else
79 FatalHandler::GetInstance().DumpFatalTrace(errorCode);
80 #endif // FEATURE_FATAL_ERROR_HANDLING
81 }
82
DumpFatalTrace(int errorCode) const83 void FatalHandler::DumpFatalTrace(int errorCode) const
84 {
85 LogString(LogLevel::LOG_LEVEL_ERR, "[JS Exception]: ");
86 LogString(LogLevel::LOG_LEVEL_ERR, GetErrorStr(errorCode));
87 LogString(LogLevel::LOG_LEVEL_ERR, "\n");
88 }
89
RegisterFatalHandler(JSAbility * ability)90 void FatalHandler::RegisterFatalHandler(JSAbility *ability)
91 {
92 jsAbility_ = ability;
93 // set callback into jerry
94 jerry_port_default_set_fatal_handler(HandleFatal);
95 }
96
IsErrorSupported(int errorCode) const97 bool FatalHandler::IsErrorSupported(int errorCode) const
98 {
99 switch (errorCode) {
100 case ERR_NATIVE_OUT_OF_MEMORY:
101 // fall through
102 case ERR_READ_FWK_FILE_FAILED:
103 // fall through
104 case ERR_EVAL_FWK_FAILED:
105 // fall through
106 case ERR_READ_JS_FILE_FAILED:
107 // fall through
108 case ERR_EVAL_JS_FAILED:
109 // fall through
110 case ERR_OUT_OF_MEMORY: // JS heap overflow
111 // fall through
112 case ERR_REF_COUNT_LIMIT:
113 // fall through
114 case ERR_DISABLED_BYTE_CODE:
115 // fall through
116 case ERR_FAILED_INTERNAL_ASSERTION:
117 return true;
118 default:
119 break;
120 }
121 return false;
122 }
123
HandleFatalInternal()124 void FatalHandler::HandleFatalInternal()
125 {
126 // detach the root component from root view
127 RootView *rootView = RootView::GetInstance();
128 if ((rootView != nullptr) && (pageRootView_ != nullptr)) {
129 rootView->Remove(pageRootView_);
130 }
131 // do the recycling
132 HandleFatalError(FatalHandler::ERR_INVALID);
133 // transfer ability to destroy
134 if (jsAbility_ != nullptr) {
135 jsAbility_->TransferToDestroy();
136 }
137 // reset locals
138 CleanUpFatalResource();
139 }
140
HandleFatalError(int errorCode)141 void FatalHandler::HandleFatalError(int errorCode)
142 {
143 if (errorCode == ERR_INVALID && fatalErrorCode_ == ERR_INVALID) {
144 // no fatal error happens
145 return;
146 }
147
148 if (errorCode != ERR_INVALID) {
149 // update fatal error to new one if it's valid
150 fatalErrorCode_ = errorCode;
151 }
152 isFatalHandled_ = false;
153 if (!isRecycling_) {
154 // recycle any remain components first
155 RecycleComponents();
156 } else {
157 HILOG_WARN(HILOG_MODULE_ACE, "hitted again when handling fatal error");
158 }
159
160 DumpFatalTrace(fatalErrorCode_);
161
162 AsyncTaskManager::GetInstance().SetFront(false);
163 // reset the low layer rendering flag if needed
164 FatalHandler::GetInstance().ResetRendering();
165 FeaAbilityModule::Release();
166 componentNodes_.Clear();
167 isFatalHandled_ = true;
168 }
169
170 /*
171 * Reset render flag or task handler mutex if needed
172 */
ResetRendering()173 void FatalHandler::ResetRendering()
174 {
175 if (IsTEHandling()) {
176 // release the render flag as we are going into sleep and will be force killed
177 TaskManager::GetInstance()->ResetTaskHandlerMutex();
178 SetTEHandlingFlag(false);
179 }
180 #if (OHOS_ACELITE_PRODUCT_WATCH == 1)
181 ProductAdapter::NotifyRenderEnd();
182 #endif
183 }
184
GetErrorStr(int errorCode) const185 const char *FatalHandler::GetErrorStr(int errorCode) const
186 {
187 switch (errorCode) {
188 case ERR_NATIVE_OUT_OF_MEMORY:
189 return "NATIVE OOM";
190 case ERR_READ_FWK_FILE_FAILED:
191 return "READ FWK FAILED";
192 case ERR_EVAL_FWK_FAILED:
193 return "EVAL FWK FAILED";
194 case ERR_READ_JS_FILE_FAILED:
195 return "READ JS FAILED";
196 case ERR_EVAL_JS_FAILED:
197 return "EVAL JS FAILED";
198 case ERR_OUT_OF_MEMORY:
199 return "JS HEAP OOM";
200 case ERR_REF_COUNT_LIMIT:
201 return "JS REF LIMIT";
202 case ERR_DISABLED_BYTE_CODE:
203 return "JS DISABLED BYTE CODE";
204 case ERR_FAILED_INTERNAL_ASSERTION:
205 return "JS ASSERTION FAILED";
206 default:
207 break;
208 }
209 return "";
210 }
211
SetFatalError(int errorCode)212 void FatalHandler::SetFatalError(int errorCode)
213 {
214 switch (errorCode) {
215 case ERR_OUT_OF_MEMORY:
216 ACE_ERROR_CODE_PRINT(EXCE_ACE_ENGINE_RUNTIME_ERROR, EXCE_ACE_JS_HEAP_OOM);
217 break;
218 case ERR_REF_COUNT_LIMIT:
219 ACE_ERROR_CODE_PRINT(EXCE_ACE_ENGINE_RUNTIME_ERROR, EXCE_ACE_JS_REF_COUNT_LIMIT);
220 break;
221 case ERR_DISABLED_BYTE_CODE:
222 ACE_ERROR_CODE_PRINT(EXCE_ACE_ENGINE_RUNTIME_ERROR, EXCE_ACE_JS_DISABLED_BYTE_CODE);
223 break;
224 case ERR_FAILED_INTERNAL_ASSERTION:
225 ACE_ERROR_CODE_PRINT(EXCE_ACE_ENGINE_RUNTIME_ERROR, EXCE_ACE_JS_FAILED_INTERNAL_ASSERTION);
226 break;
227 default:
228 break;
229 }
230
231 if (fatalErrorCode_ == ERR_INVALID && IsErrorSupported(errorCode)) {
232 // reset handling flag
233 isFatalHandled_ = false;
234 fatalErrorCode_ = errorCode;
235 }
236 }
237
RecycleComponents()238 void FatalHandler::RecycleComponents()
239 {
240 isRecycling_ = true;
241 ListNode<Component *> *node = componentNodes_.Begin();
242 while (node != componentNodes_.End()) {
243 if (node->data_ != nullptr) {
244 Component *component = node->data_;
245 component->Release();
246 delete component;
247 component = nullptr;
248 node->data_ = nullptr;
249 }
250 node = node->next_;
251 }
252 isRecycling_ = false;
253 }
254
CleanUpFatalResource()255 void FatalHandler::CleanUpFatalResource()
256 {
257 // make sure we have released all nodes
258 componentNodes_.Clear();
259 isFatalHandled_ = false;
260 // reset error code when exitting the whole application
261 fatalErrorCode_ = ERR_INVALID;
262 isExiting_ = false;
263 pageRootView_ = nullptr;
264 }
265
IsJSRuntimeFatal() const266 bool FatalHandler::IsJSRuntimeFatal() const
267 {
268 bool result = false;
269 switch (fatalErrorCode_) {
270 case ERR_OUT_OF_MEMORY: // JS heap overflow
271 // fall through
272 case ERR_REF_COUNT_LIMIT:
273 // fall through
274 case ERR_DISABLED_BYTE_CODE:
275 // fall through
276 case ERR_FAILED_INTERNAL_ASSERTION:
277 result = true;
278 break;
279 default:
280 result = false;
281 break;
282 }
283 return result;
284 }
285
286 /**
287 * As the exception can interrupt the tree rationship building, so we can't recycle the Components as normal,
288 * because we even not get the root Component object when exception happen. So we record all Components when
289 * they are created out, and remove them when they are released normally, if exception happens, if any Componets
290 * still in our list, that means there is no chance to release them through the normal way, we must release them
291 * manually.
292 */
AttachComponentNode(Component * component)293 void FatalHandler::AttachComponentNode(Component *component)
294 {
295 if (component == nullptr) {
296 return;
297 }
298 componentNodes_.PushBack(component);
299 }
300
DetachComponentNode(const Component * component)301 void FatalHandler::DetachComponentNode(const Component *component)
302 {
303 if ((component == nullptr) || isRecycling_) {
304 return;
305 }
306 ListNode<Component *> *node = componentNodes_.Begin();
307 while (node != componentNodes_.End()) {
308 if (node->data_ == component) {
309 componentNodes_.Remove(node);
310 break;
311 }
312 node = node->next_;
313 }
314 }
315
IsFatalErrorHitted() const316 bool FatalHandler::IsFatalErrorHitted() const
317 {
318 return (fatalErrorCode_ != ERR_INVALID);
319 }
320
IsFatalErrorHandling() const321 bool FatalHandler::IsFatalErrorHandling() const
322 {
323 return isRecycling_;
324 }
325
IsFatalErrorHandleDone() const326 bool FatalHandler::IsFatalErrorHandleDone() const
327 {
328 return isFatalHandled_;
329 }
330
SetTEHandlingFlag(bool flag)331 void FatalHandler::SetTEHandlingFlag(bool flag)
332 {
333 isTEHandling_ = flag;
334 }
335
IsTEHandling() const336 bool FatalHandler::IsTEHandling() const
337 {
338 return isTEHandling_;
339 }
340
SetExitingFlag(bool flag)341 void FatalHandler::SetExitingFlag(bool flag)
342 {
343 isExiting_ = flag;
344 }
345
IsAppExiting() const346 bool FatalHandler::IsAppExiting() const
347 {
348 return isExiting_;
349 }
350
SetCurrentPageRootView(UIView * pageRoot)351 void FatalHandler::SetCurrentPageRootView(UIView *pageRoot)
352 {
353 pageRootView_ = pageRoot;
354 }
355
GetComponentCount() const356 uint16_t FatalHandler::GetComponentCount() const
357 {
358 return componentNodes_.Size();
359 }
360
NotifyVisibleStatusChange(bool isVisible) const361 void FatalHandler::NotifyVisibleStatusChange(bool isVisible) const
362 {
363 ListNode<Component *> *node = componentNodes_.Begin();
364 while (node != componentNodes_.End()) {
365 if (node->data_ != nullptr) {
366 node->data_->OnVisibilityChanged(isVisible);
367 }
368 node = node->next_;
369 }
370 }
371 } // namespace ACELite
372 } // namespace OHOS
373