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