From 24be35498d8cce947bb5370ec02a38535cf933f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E5=86=AC=E5=86=AC=E5=86=AC?= Date: Mon, 15 Dec 2025 11:57:02 +0800 Subject: [PATCH] =?UTF-8?q?Message:=20uncatchable=20Exception=20change=20t?= =?UTF-8?q?o=20sync,=20others=20which=20are=20async=20limt=20in=202s=20Sig?= =?UTF-8?q?ned-off-by:=20=E9=83=91=E5=86=AC=E5=86=AC=E5=86=AC=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/error_manager/js_error_manager.cpp | 133 ++++++++++++------ .../appkit/app/application_data_manager.cpp | 108 +++++++++++++- frameworks/native/appkit/app/main_thread.cpp | 92 +++++++----- .../inner_api/runtime/include/js_runtime.h | 3 +- .../appkit/app/application_data_manager.h | 13 +- .../kits/native/appkit/app/main_thread.h | 22 ++- .../js_environment/src/js_environment.cpp | 3 +- .../src/uncaught_exception_callback.cpp | 2 +- .../interfaces/inner_api/js_environment.h | 3 +- .../inner_api/uncaught_exception_callback.h | 9 +- .../uncaught_exception_callback_test.cpp | 10 +- 11 files changed, 304 insertions(+), 94 deletions(-) diff --git a/frameworks/js/napi/app/error_manager/js_error_manager.cpp b/frameworks/js/napi/app/error_manager/js_error_manager.cpp index d8a73458ee..4e6504fbe2 100644 --- a/frameworks/js/napi/app/error_manager/js_error_manager.cpp +++ b/frameworks/js/napi/app/error_manager/js_error_manager.cpp @@ -46,6 +46,7 @@ struct WorkItem { std::string name; std::string message; std::string stack; + std::shared_ptr> remainingCount; }; struct GlobalObserverItem { napi_ref ref; @@ -75,6 +76,9 @@ static std::once_flag registerCallbackFlag; static std::shared_ptr loopObserver_; static bool freezeCallbackRegistered = false; static bool isSetHandler = false; +static std::mutex onErrorMtx; +static std::condition_variable onErrorCv; +constexpr int ON_ERROR_ASYNC_TIMEOUT = 2; constexpr int32_t INDEX_ZERO = 0; constexpr int32_t INDEX_ONE = 1; constexpr int32_t INDEX_TWO = 2; @@ -332,17 +336,15 @@ static void CallJsFunction(napi_env env, napi_value obj, const char *methodName, napi_call_function(env, obj, method, argc, argv, &callResult); } -static void DoFunctionCallback(uv_work_t *reqwork, int status) +static bool NapiDoFunctionCallBack(WorkItem *newItem) { - WorkItem *newItem = static_cast(reqwork->data); - if (newItem == nullptr) { - TAG_LOGI(AAFwkTag::JSNAPI, "Get WorkItem Failed"); - return; - } napi_handle_scope scope_ = nullptr; napi_status scopeStatus = napi_open_handle_scope(newItem->env, &scope_); if (scopeStatus != napi_ok || scope_ == nullptr) { TAG_LOGE(AAFwkTag::JSNAPI, "napi_open_handle_scope failed"); + delete newItem; + newItem = nullptr; + return false; } napi_value global = nullptr; if (napi_get_global(newItem->env, &global) != napi_ok) { @@ -350,7 +352,7 @@ static void DoFunctionCallback(uv_work_t *reqwork, int status) napi_close_handle_scope(newItem->env, scope_); delete newItem; newItem = nullptr; - return; + return false; } size_t argc = ARGC_ONE; napi_value args[] = {CreateGlobalObject(newItem->env, newItem)}; @@ -360,7 +362,7 @@ static void DoFunctionCallback(uv_work_t *reqwork, int status) napi_close_handle_scope(newItem->env, scope_); delete newItem; newItem = nullptr; - return; + return false; } napi_value result = nullptr; if (napi_call_function(newItem->env, global, function, argc, args, &result) != napi_ok) { @@ -368,44 +370,98 @@ static void DoFunctionCallback(uv_work_t *reqwork, int status) napi_close_handle_scope(newItem->env, scope_); delete newItem; newItem = nullptr; - return; + return false; } napi_close_handle_scope(newItem->env, scope_); + return true; +} + +static void DoFunctionCallback(uv_work_t *reqwork, int status) +{ + WorkItem *newItem = static_cast(reqwork->data); + if (newItem == nullptr) { + TAG_LOGI(AAFwkTag::JSNAPI, "Get WorkItem Failed"); + return; + } + if (!NapiDoFunctionCallBack(newItem)) { + return; + } + if (newItem->remainingCount) { + int oldValue = newItem->remainingCount->fetch_sub(1, std::memory_order_acq_rel); + if (oldValue == 1) { + std::lock_guard lock(onErrorMtx); + onErrorCv.notify_all(); + TAG_LOGI(AAFwkTag::JSNAPI, "onError cv notify"); + } + } delete newItem; newItem = nullptr; } + +static void OnErrorWorkerWait(std::shared_ptr>& remainingCount) +{ + if (remainingCount && remainingCount->load() > 0) { + std::unique_lock onErrorLock(onErrorMtx); + if (onErrorCv.wait_for(onErrorLock, std::chrono::seconds(ON_ERROR_ASYNC_TIMEOUT)) == std::cv_status::timeout) { + TAG_LOGI(AAFwkTag::JSNAPI, "async onError callback has been extecting more than 2s"); + } else { + TAG_LOGI(AAFwkTag::JSNAPI, "async onError callback has finished less than 2s"); + } + } +} + static void DoCallbackInRegesterThread(napi_env env, WorkItem &info) { - std::lock_guard lock(globalErrorMtx); - for (auto iter : globalObserverList) { - uv_loop_t *loop = nullptr; - if (napi_get_uv_event_loop(iter.env, &loop) != napi_ok) { - TAG_LOGI(AAFwkTag::JSNAPI, "Get Loop Failed"); - continue; + std::shared_ptr> remainingCount = nullptr; + { + std::lock_guard lock(globalErrorMtx); + if (AppExecFwk::ApplicationDataManager::GetInstance().GetIsUncatchable() && !globalObserverList.empty()) { + remainingCount = std::make_shared>(globalObserverList.size()); } - WorkItem *item = new (std::nothrow) WorkItem(); - if (item == nullptr) { - TAG_LOGI(AAFwkTag::JSNAPI, "new WorkItem Failed"); - continue; - } - item->env = iter.env; - item->ref = iter.ref; - item->instanceName = info.instanceName; - item->instanceType = info.instanceType; - item->work.data = item; - item->name = info.name; - item->stack = info.stack; - item->message = info.message; - int ret = uv_queue_work( - loop, &item->work, [](uv_work_t *reqwork) {}, DoFunctionCallback); - if (ret != 0) { - if (item != nullptr) { + + for (auto iter : globalObserverList) { + WorkItem *item = new (std::nothrow) WorkItem(); + if (item == nullptr) { + TAG_LOGI(AAFwkTag::JSNAPI, "new WorkItem Failed"); + continue; + } + item->env = iter.env; + item->ref = iter.ref; + item->instanceName = info.instanceName; + item->instanceType = info.instanceType; + item->work.data = item; + item->name = info.name; + item->stack = info.stack; + item->message = info.message; + item->remainingCount = remainingCount; + + if (remainingCount && env == item->env) { + remainingCount->fetch_sub(1, std::memory_order_acq_rel); + if (NapiDoFunctionCallBack(item)) { + delete item; + item = nullptr; + TAG_LOGI(AAFwkTag::JSNAPI, "Do uncatchable callback successfully with the same env"); + } + continue; + } + uv_loop_t *loop = nullptr; + if (napi_get_uv_event_loop(iter.env, &loop) != napi_ok) { + delete item; + item = nullptr; + TAG_LOGI(AAFwkTag::JSNAPI, "Get Loop Failed"); + continue; + } + + int ret = uv_queue_work(loop, &item->work, [](uv_work_t *reqwork) {}, DoFunctionCallback); + if (ret != 0 && item != nullptr) { delete item; item = nullptr; } } } + OnErrorWorkerWait(remainingCount); } + static void DoGlobalCallback(napi_env env, napi_value exception, std::string instanceName, uint32_t type) { { @@ -508,14 +564,11 @@ static void DoWorkThreadCallback(napi_env env, napi_value exception) CallJsFunction(env, functionTemp.second->GetNapiValue(), "onException", args, argc); } } + static bool ErrorManagerWorkerCallback(napi_env env, napi_value exception, std::string instanceName, uint32_t type) { - if (!AppExecFwk::ApplicationDataManager::GetInstance().GetIsUncatchable()) { - DoGlobalCallback(env, exception, instanceName, type); - } else { - TAG_LOGI(AAFwkTag::JSNAPI, "Uncatchable exception, skip this step."); - } DoWorkThreadCallback(env, exception); + DoGlobalCallback(env, exception, instanceName, type); return true; } @@ -589,11 +642,7 @@ static bool ErrorManagerMainWorkerCallback( item.stack = stack; item.instanceName = ""; item.instanceType = 0; - if (!AppExecFwk::ApplicationDataManager::GetInstance().GetIsUncatchable()) { - DoCallbackInRegesterThread(env, item); - } else { - TAG_LOGI(AAFwkTag::JSNAPI, "Uncatchable exception, skip this step."); - } + DoCallbackInRegesterThread(env, item); return true; } diff --git a/frameworks/native/appkit/app/application_data_manager.cpp b/frameworks/native/appkit/app/application_data_manager.cpp index d39d879940..0f30d4c331 100644 --- a/frameworks/native/appkit/app/application_data_manager.cpp +++ b/frameworks/native/appkit/app/application_data_manager.cpp @@ -17,9 +17,23 @@ #include "app_recovery.h" #include "hilog_tag_wrapper.h" +#include "native_engine.h" namespace OHOS { namespace AppExecFwk { +namespace { + constexpr size_t STACK_MAX_SZIE = 1024; + constexpr size_t AT_SKIP_SZIE = 3; + constexpr const char* TASK_POOL_THREAD = "Taskpool Thread"; + enum class InstanceType { + DEFAULT_TYPE = -1, + WORKER_THREAD_TYPE = 1, + TASK_POOL_THREAD_TYPE = 2, + }; + thread_local bool g_hasNotified = false; +} + +std::atomic ApplicationDataManager::jsErrorHasReport_{false}; ApplicationDataManager::ApplicationDataManager() {} ApplicationDataManager::~ApplicationDataManager() {} @@ -94,7 +108,7 @@ bool ApplicationDataManager::NotifyCJExceptionObject(const AppExecFwk::ErrorObje } // if apprecovery is enabled, we could callback to save current state - // and restart as developer wants + // and restart developer wants return AppRecovery::GetInstance().TryRecoverApp(StateReason::CJ_ERROR); } @@ -118,5 +132,97 @@ bool ApplicationDataManager::GetIsUncatchable() bool isUncatchable = isUncatchable_.load(); return isUncatchable; } + +std::string ApplicationDataManager::GetFuncNameFromError(napi_env env, napi_value error) +{ + if (error == nullptr) { + return TASK_POOL_THREAD; + } + + napi_value stack; + if (napi_get_named_property(env, error, "stack", &stack) != napi_ok ||stack == nullptr) { + return TASK_POOL_THREAD; + } + + std::string rawStack; + size_t rawStackSize = 0; + napi_get_value_string_utf8(env, stack, nullptr, 0, &rawStackSize); + rawStackSize = std::min(rawStackSize, STACK_MAX_SZIE); + rawStack.reserve(rawStackSize + 1); + rawStack.resize(rawStackSize); + napi_get_value_string_utf8(env, stack, rawStack.data(), rawStack.size() + 1, &rawStackSize); + + size_t pos = rawStack.find("at"); + if (pos == std::string::npos) { + return TASK_POOL_THREAD; + } + size_t endPos = rawStack.find("(", pos); + if (endPos == std::string::npos) { + return TASK_POOL_THREAD; + } + size_t startPos = pos + AT_SKIP_SZIE; + if (endPos <= startPos + 1) { + return TASK_POOL_THREAD; + } + + std::string funcName = std::string(TASK_POOL_THREAD); + funcName.append(rawStack.substr(startPos, endPos - startPos - 1)); + return funcName; +} + +bool ApplicationDataManager::NotifyUncaughtException(const ExceptionParams ¶ms, + const AppExecFwk::ErrorObject &errorObj) +{ + if (params.isUncatchable && g_hasNotified) { + return false; + } + g_hasNotified = params.isUncatchable; + + bool isMainEnv = (params.env == params.mainEnv); + auto napiEnv = params.env ? params.env : params.mainEnv; + if (isMainEnv) { + TAG_LOGI(AAFwkTag::APPKIT, "main thread"); + if (NapiErrorManager::GetInstance()->NotifyUncaughtException(napiEnv, params.summary, + errorObj.name, errorObj.message, errorObj.stack)) { + TAG_LOGI(AAFwkTag::APPKIT, "Complete all callbacks"); + } + } else if (params.isUncatchable) { + NativeEngine* engine = reinterpret_cast(napiEnv); + if (engine == nullptr) { + return false; + } + std::string instanceName; + InstanceType instanceType = InstanceType::DEFAULT_TYPE; + if (engine->IsWorkerThread()) { + instanceType = InstanceType::WORKER_THREAD_TYPE; + napi_value workerGlobalObject = nullptr; + napi_get_global(napiEnv, &workerGlobalObject); + napi_value valueStr = nullptr; + if (napi_get_named_property(napiEnv, workerGlobalObject, "name", &valueStr) != napi_ok) { + return false; + } + napi_valuetype valueType = napi_undefined; + napi_typeof(napiEnv, valueStr, &valueType); + + if (valueType == napi_string) { + size_t instanceNameSize = 0; + napi_get_value_string_utf8(napiEnv, valueStr, nullptr, 0, &instanceNameSize); + instanceName.reserve(instanceNameSize + 1); + instanceName.resize(instanceNameSize); + napi_get_value_string_utf8(napiEnv, valueStr, instanceName.data(), instanceName.size() + 1, + &instanceNameSize); + } + TAG_LOGE(AAFwkTag::APPKIT, "worker thread, instanceType=1, instanceName=%{public}s", instanceName.c_str()); + } else if (engine->IsTaskPoolThread()) { + instanceType = InstanceType::TASK_POOL_THREAD_TYPE; + instanceName = GetFuncNameFromError(napiEnv, params.exception); + TAG_LOGE(AAFwkTag::APPKIT, "task pool thread, instanceType=2, instanceName=%{public}s", + instanceName.c_str()); + } + NapiErrorManager::GetInstance()->NotifyUncaughtException(napiEnv, params.exception, instanceName, + static_cast(instanceType)); + } + return (isMainEnv && !g_hasNotified); +} } // namespace AppExecFwk } // namespace OHOS diff --git a/frameworks/native/appkit/app/main_thread.cpp b/frameworks/native/appkit/app/main_thread.cpp index 3c861c120b..9e5d75d8cd 100644 --- a/frameworks/native/appkit/app/main_thread.cpp +++ b/frameworks/native/appkit/app/main_thread.cpp @@ -172,7 +172,7 @@ constexpr char EVENT_KEY_CANGJIE[] = "CANGJIE"; constexpr char EVENT_KEY_SUMMARY[] = "SUMMARY"; constexpr char EVENT_KEY_PNAME[] = "PNAME"; constexpr char EVENT_KEY_THREAD_NAME[] = "THREAD_NAME"; -constexpr char EVENT_KEY_APP_RUNING_UNIQUE_ID[] = "APP_RUNNING_UNIQUE_ID"; +constexpr char EVENT_KEY_APP_RUNNING_UNIQUE_ID[] = "APP_RUNNING_UNIQUE_ID"; constexpr char EVENT_KEY_PROCESS_RSS_MEMINFO[] = "PROCESS_RSS_MEMINFO"; constexpr char EVENT_KEY_PROCESS_LIFETIME[] = "PROCESS_LIFETIME"; constexpr char DEVELOPER_MODE_STATE[] = "const.security.developermode.state"; @@ -1470,7 +1470,7 @@ EtsEnv::ETSUncaughtExceptionInfo MainThread::CreateEtsExceptionInfo(const std::s OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, EVENT_KEY_PACKAGE_NAME, bundleName, EVENT_KEY_VERSION, std::to_string(versionCode), EVENT_KEY_TYPE, JSCRASH_TYPE, EVENT_KEY_HAPPEN_TIME, timet, EVENT_KEY_REASON, errorObj.name, EVENT_KEY_JSVM, JSVM_TYPE, EVENT_KEY_SUMMARY, summary, - EVENT_KEY_PNAME, processName, EVENT_KEY_APP_RUNING_UNIQUE_ID, appRunningId, + EVENT_KEY_PNAME, processName, EVENT_KEY_APP_RUNNING_UNIQUE_ID, appRunningId, EVENT_KEY_PROCESS_RSS_MEMINFO, std::to_string(DumpProcessHelper::GetProcRssMemInfo()), EVENT_KEY_THREAD_NAME, DumpProcessHelper::GetThreadName()); ErrorObject appExecErrorObj = { .name = errorObj.name, .message = errorObj.message, .stack = errorObj.stack }; @@ -1491,7 +1491,7 @@ EtsEnv::ETSUncaughtExceptionInfo MainThread::CreateEtsExceptionInfo(const std::s } int result = HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::FRAMEWORK, "PROCESS_KILL", HiviewDFX::HiSysEvent::EventType::FAULT, "PID", pid, "PROCESS_NAME", processName, "MSG", KILL_REASON, - EVENT_KEY_APP_RUNING_UNIQUE_ID, appRunningId, EVENT_KEY_REASON, "JsError", + EVENT_KEY_APP_RUNNING_UNIQUE_ID, appRunningId, EVENT_KEY_REASON, "JsError", "FOREGROUND", foreground); TAG_LOGW(AAFwkTag::APPKIT, "hisysevent write result=%{public}d, send event " "[FRAMEWORK,PROCESS_KILL]," @@ -2052,59 +2052,77 @@ void MainThread::InitUncatchableTask(JsEnv::UncatchableTask &uncatchableTask, co uncatchableTask = [weak, bundleName = uncatchableTaskInfo.bundleName, versionCode = uncatchableTaskInfo.versionCode, appRunningId = uncatchableTaskInfo.appRunningId, pid = uncatchableTaskInfo.pid, processName = uncatchableTaskInfo.processName, isUncatchable] - (std::string summary, const JsEnv::ErrorObject errorObject) { + (std::string summary, const JsEnv::ErrorObject errorObject, napi_env env, napi_value exception) { auto appThread = weak.promote(); if (appThread == nullptr) { TAG_LOGE(AAFwkTag::APPKIT, "null appThread"); return; } + time_t timet; time(&timet); std::string lifeTime = GetProcessLifeCycleByPid(pid); - HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::AAFWK, "JS_ERROR", - OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, EVENT_KEY_PACKAGE_NAME, bundleName, - EVENT_KEY_VERSION, std::to_string(versionCode), EVENT_KEY_TYPE, JSCRASH_TYPE, EVENT_KEY_HAPPEN_TIME, timet, - EVENT_KEY_REASON, errorObject.name, EVENT_KEY_JSVM, JSVM_TYPE, EVENT_KEY_SUMMARY, summary, - EVENT_KEY_PNAME, processName, EVENT_KEY_APP_RUNING_UNIQUE_ID, appRunningId, - EVENT_KEY_PROCESS_RSS_MEMINFO, std::to_string(DumpProcessHelper::GetProcRssMemInfo()), - EVENT_KEY_THREAD_NAME, DumpProcessHelper::GetThreadName(), EVENT_KEY_PROCESS_LIFETIME, lifeTime); + if (!ApplicationDataManager::jsErrorHasReport_.exchange(true)) { + int result = HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::AAFWK, "JS_ERROR", + OHOS::HiviewDFX::HiSysEvent::EventType::FAULT, EVENT_KEY_PACKAGE_NAME, bundleName, EVENT_KEY_VERSION, + std::to_string(versionCode), EVENT_KEY_TYPE, JSCRASH_TYPE, EVENT_KEY_HAPPEN_TIME, timet, + EVENT_KEY_REASON, errorObject.name, EVENT_KEY_JSVM, JSVM_TYPE, EVENT_KEY_SUMMARY, summary, + EVENT_KEY_PNAME, processName, EVENT_KEY_APP_RUNNING_UNIQUE_ID, appRunningId, + EVENT_KEY_PROCESS_RSS_MEMINFO, std::to_string(DumpProcessHelper::GetProcRssMemInfo()), + EVENT_KEY_THREAD_NAME, DumpProcessHelper::GetThreadName(), EVENT_KEY_PROCESS_LIFETIME, lifeTime); + TAG_LOGW(AAFwkTag::APPKIT, "hisysevent write result=%{public}d, send event [FRAMEWORK,JS_ERROR]," + " packageName=%{public}s, pid=%{public}d, appRunningId=%{public}s, threadName=%{public}s," + " isUncatchable=%{public}d", result, bundleName.c_str(), pid, appRunningId.c_str(), + DumpProcessHelper::GetThreadName().c_str(), isUncatchable); + } + + ApplicationDataManager::GetInstance().SetIsUncatchable(isUncatchable); ErrorObject appExecErrorObj = { errorObject.name, errorObject.message, errorObject.stack}; - auto napiEnv = (static_cast(*appThread->application_->GetRuntime())).GetNapiEnv(); - AAFwk::ExitReason exitReason = { REASON_JS_ERROR, errorObject.name }; - AbilityManagerClient::GetInstance()->RecordAppExitReason(exitReason); - AppExecFwk::ApplicationDataManager::GetInstance().SetIsUncatchable(isUncatchable); - if (NapiErrorManager::GetInstance()->NotifyUncaughtException(napiEnv, summary, - appExecErrorObj.name, appExecErrorObj.message, appExecErrorObj.stack)) { - TAG_LOGI(AAFwkTag::APPKIT, "Complete all callbacks"); - if (!isUncatchable) { - return; - } + auto mainEnv = (static_cast(*appThread->application_->GetRuntime())).GetNapiEnv(); + ApplicationDataManager::ExceptionParams params = {env, mainEnv, exception, summary, isUncatchable}; + if (ApplicationDataManager::NotifyUncaughtException(params, appExecErrorObj)) { + return; } + if (!isUncatchable && ApplicationDataManager::GetInstance().NotifyUnhandledException(summary) && ApplicationDataManager::GetInstance().NotifyExceptionObject(appExecErrorObj)) { return; } - // if app's callback has been registered, let app decide whether exit or not. - TAG_LOGE(AAFwkTag::APPKIT, "\n%{public}s is about to exit due to RuntimeError\nError type:%{public}s\n" - "%{public}s", bundleName.c_str(), errorObject.name.c_str(), summary.c_str()); - bool foreground = false; - if (appThread->applicationImpl_ && appThread->applicationImpl_->GetState() == - ApplicationImpl::APP_STATE_FOREGROUND) { - foreground = true; - } - int result = HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::FRAMEWORK, "PROCESS_KILL", - HiviewDFX::HiSysEvent::EventType::FAULT, "PID", pid, "PROCESS_NAME", processName, - EVENT_KEY_APP_RUNING_UNIQUE_ID, appRunningId, EVENT_KEY_REASON, "JsError", - "MSG", KILL_REASON, "FOREGROUND", foreground, "IS_UNCATCHABLE", isUncatchable); - TAG_LOGW(AAFwkTag::APPKIT, "hisysevent write result=%{public}d, send event [FRAMEWORK,PROCESS_KILL]," - " pid=%{public}d, processName=%{public}s, msg=%{public}s, foreground=%{public}d, isUncatchable=%{public}d", - result, pid, processName.c_str(), KILL_REASON, foreground, isUncatchable); - _exit(JS_ERROR_EXIT); + bool foreground = (appThread->applicationImpl_ && appThread->applicationImpl_->GetState() == + ApplicationImpl::APP_STATE_FOREGROUND) ? true : false; + ProcessExitInfo info = {bundleName, errorObject.name, summary, appRunningId, processName, pid, foreground, + isUncatchable}; + ProcessExit(info); }; } +/** + * + * @brief Handle process exit. + * + * @param processExitInfo The info of the process exit info. + * + */ +void MainThread::ProcessExit(const ProcessExitInfo& info) +{ + AAFwk::ExitReason exitReason = { REASON_JS_ERROR, info.errorObjectName }; + AbilityManagerClient::GetInstance()->RecordAppExitReason(exitReason); + + // if app's callback has been registered, let app decide whether exit or not. + TAG_LOGE(AAFwkTag::APPKIT, "\n%{public}s is about to exit due to RuntimeError\nError type:%{public}s\n" + "%{public}s", info.bundleName.c_str(), info.errorObjectName.c_str(), info.summary.c_str()); + int result = HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::FRAMEWORK, "PROCESS_KILL", + HiviewDFX::HiSysEvent::EventType::FAULT, "PID", info.pid, "PROCESS_NAME", info.processName, + EVENT_KEY_APP_RUNNING_UNIQUE_ID, info.appRunningId, EVENT_KEY_REASON, "JsError", + "MSG", KILL_REASON, "FOREGROUND", info.foreground, "IS_UNCATCHABLE", info.isUncatchable); + TAG_LOGW(AAFwkTag::APPKIT, "hisysevent write result=%{public}d, send event [FRAMEWORK,PROCESS_KILL]," + " pid=%{public}d, processName=%{public}s, msg=%{public}s, foreground=%{public}d, isUncatchable=%{public}d", + result, info.pid, info.processName.c_str(), KILL_REASON, info.foreground, info.isUncatchable); + _exit(JS_ERROR_EXIT); +} + #if defined(NWEB) && defined(NWEB_GRAPHIC) void MainThread::HandleNWebPreload() { diff --git a/interfaces/inner_api/runtime/include/js_runtime.h b/interfaces/inner_api/runtime/include/js_runtime.h index a8d12a7801..62a92f3150 100644 --- a/interfaces/inner_api/runtime/include/js_runtime.h +++ b/interfaces/inner_api/runtime/include/js_runtime.h @@ -47,7 +47,8 @@ class JsEnvironment; class SourceMapOperator; struct ErrorObject; struct UncaughtExceptionInfo; -using UncatchableTask = std::function; +using UncatchableTask = std::function; } // namespace JsEnv using AppLibPathMap = std::map>; diff --git a/interfaces/kits/native/appkit/app/application_data_manager.h b/interfaces/kits/native/appkit/app/application_data_manager.h index 57b8e8bec7..d228a8cb63 100644 --- a/interfaces/kits/native/appkit/app/application_data_manager.h +++ b/interfaces/kits/native/appkit/app/application_data_manager.h @@ -17,6 +17,7 @@ #define OHOS_ABILITY_RUNTIME_APPLICATION_DATA_MANAGER_H #include +#include "napi/native_api.h" #include "ierror_observer.h" #include "nocopyable.h" @@ -25,6 +26,15 @@ namespace OHOS { namespace AppExecFwk { class ApplicationDataManager { public: + struct ExceptionParams { + napi_env env; + napi_env mainEnv; + napi_value exception; + std::string summary; + bool isUncatchable; + }; + + static std::atomic jsErrorHasReport_; static ApplicationDataManager &GetInstance(); void AddErrorObserver(const std::shared_ptr &observer); bool NotifyUnhandledException(const std::string &errMsg); @@ -36,10 +46,11 @@ public: bool NotifyETSExceptionObject(const AppExecFwk::ErrorObject &errorObj); void SetIsUncatchable(bool isUncatchable); bool GetIsUncatchable(); - + static bool NotifyUncaughtException(const ExceptionParams ¶ms, const AppExecFwk::ErrorObject &errorObj); private: ApplicationDataManager(); ~ApplicationDataManager(); + static std::string GetFuncNameFromError(napi_env env, napi_value error); DISALLOW_COPY_AND_MOVE(ApplicationDataManager); std::shared_ptr errorObserver_; std::atomic_bool isUncatchable_; diff --git a/interfaces/kits/native/appkit/app/main_thread.h b/interfaces/kits/native/appkit/app/main_thread.h index caebf827f0..09677be30f 100644 --- a/interfaces/kits/native/appkit/app/main_thread.h +++ b/interfaces/kits/native/appkit/app/main_thread.h @@ -55,7 +55,8 @@ class Runtime; namespace OHOS { namespace JsEnv { struct ErrorObject; -using UncatchableTask = std::function; +using UncatchableTask = std::function; } // namespace JsEnv namespace EtsEnv { struct ETSUncaughtExceptionInfo; @@ -80,6 +81,16 @@ struct RuntimeUpdateParam { UncatchableTaskInfo uncatchableTaskInfo; std::string hapPath; }; +struct ProcessExitInfo { + std::string bundleName; + std::string errorObjectName; + std::string summary; + std::string appRunningId; + std::string processName; + int32_t pid; + bool foreground; + bool isUncatchable; +}; class ContextDeal; struct ModuleTestRunner; // class Global::Resource::ResourceManager; @@ -459,6 +470,15 @@ private: void InitUncatchableTask(JsEnv::UncatchableTask &uncatchableTask, const UncatchableTaskInfo &uncatchableTaskInfo, bool isUncatchable = false); + /** + * + * @brief Handle process exit. + * + * @param processExitInfo The info of the process exit info. + * + */ + static void ProcessExit(const ProcessExitInfo& info); + /** * * @brief update the application info after new module installed. diff --git a/js_environment/frameworks/js_environment/src/js_environment.cpp b/js_environment/frameworks/js_environment/src/js_environment.cpp index 33630da460..fb5858fb59 100644 --- a/js_environment/frameworks/js_environment/src/js_environment.cpp +++ b/js_environment/frameworks/js_environment/src/js_environment.cpp @@ -177,8 +177,9 @@ void JsEnvironment::RegisterUncatchableExceptionHandler(const JsEnv::Uncatchable [weakThis, uncatchableTask] (auto& trycatch) { auto sharedThis = weakThis.lock(); if (sharedThis) { + void* env = trycatch.GetEnv(); NapiUncaughtExceptionCallback napiUncaughtExceptionCallback(uncatchableTask, - sharedThis->sourceMapOperator_, reinterpret_cast(sharedThis->engine_)); + sharedThis->sourceMapOperator_, reinterpret_cast(env)); napiUncaughtExceptionCallback(trycatch); } else { TAG_LOGE(AAFwkTag::JSENV, "JsEnvironment has been destructed."); diff --git a/js_environment/frameworks/js_environment/src/uncaught_exception_callback.cpp b/js_environment/frameworks/js_environment/src/uncaught_exception_callback.cpp index d0f1550352..63b31ce108 100644 --- a/js_environment/frameworks/js_environment/src/uncaught_exception_callback.cpp +++ b/js_environment/frameworks/js_environment/src/uncaught_exception_callback.cpp @@ -129,7 +129,7 @@ void NapiUncaughtExceptionCallback::CallbackTask(napi_value& obj) } } if (uncaughtTask_) { - uncaughtTask_(summary, errorObj); + uncaughtTask_(summary, errorObj, env_, obj); } } diff --git a/js_environment/interfaces/inner_api/js_environment.h b/js_environment/interfaces/inner_api/js_environment.h index 03adec4879..33f4c3c536 100644 --- a/js_environment/interfaces/inner_api/js_environment.h +++ b/js_environment/interfaces/inner_api/js_environment.h @@ -30,7 +30,8 @@ class JsEnvironmentImpl; using DebuggerPostTask = std::function&&)>; using RequestAotCallback = std::function; -using UncatchableTask = std::function; +using UncatchableTask = std::function; class JsEnvironment final : public std::enable_shared_from_this { public: JsEnvironment() {} diff --git a/js_environment/interfaces/inner_api/uncaught_exception_callback.h b/js_environment/interfaces/inner_api/uncaught_exception_callback.h index f326203f12..94c33b54cb 100644 --- a/js_environment/interfaces/inner_api/uncaught_exception_callback.h +++ b/js_environment/interfaces/inner_api/uncaught_exception_callback.h @@ -33,13 +33,15 @@ struct ErrorObject { struct UncaughtExceptionInfo { std::string hapPath; - std::function uncaughtTask; + std::function uncaughtTask; }; class NapiUncaughtExceptionCallback final { public: NapiUncaughtExceptionCallback( - std::function uncaughtTask, + std::function uncaughtTask, std::shared_ptr sourceMapOperator, napi_env env) : uncaughtTask_(uncaughtTask), sourceMapOperator_(sourceMapOperator), env_(env) {} @@ -65,7 +67,8 @@ public: #endif // SUPPORT_GRAPHICS private: - std::function uncaughtTask_; + std::function uncaughtTask_; std::shared_ptr sourceMapOperator_ = nullptr; napi_env env_ = nullptr; }; diff --git a/js_environment/test/unittest/uncaught_exception_callback_test/uncaught_exception_callback_test.cpp b/js_environment/test/unittest/uncaught_exception_callback_test/uncaught_exception_callback_test.cpp index 25fb291de7..4b739248ab 100644 --- a/js_environment/test/unittest/uncaught_exception_callback_test/uncaught_exception_callback_test.cpp +++ b/js_environment/test/unittest/uncaught_exception_callback_test/uncaught_exception_callback_test.cpp @@ -72,7 +72,7 @@ HWTEST_F(NapiUncaughtExceptionCallbackTest, NapiUncaughtExceptionCallbackTest_01 EXPECT_NE(env, nullptr); // Test with null object - auto task = [](std::string summary, const JsEnv::ErrorObject errorObj) { + auto task = [](std::string summary, const JsEnv::ErrorObject errorObj, napi_env env, napi_value exception) { summary += "test"; }; NapiUncaughtExceptionCallback callback(task, nullptr, env); @@ -120,7 +120,7 @@ HWTEST_F(NapiUncaughtExceptionCallbackTest, NapiUncaughtExceptionCallbackTest_01 EXPECT_NE(env, nullptr); // Test with null object - auto task = [](std::string summary, const JsEnv::ErrorObject errorObj) { + auto task = [](std::string summary, const JsEnv::ErrorObject errorObj, napi_env env, napi_value exception) { summary += "test"; }; @@ -149,7 +149,7 @@ HWTEST_F(NapiUncaughtExceptionCallbackTest, NapiUncaughtExceptionCallbackTest_02 auto env = jsRuntime->GetNapiEnv(); EXPECT_NE(env, nullptr); // Test with null object - auto task = [](std::string summary, const JsEnv::ErrorObject errorObj) { + auto task = [](std::string summary, const JsEnv::ErrorObject errorObj, napi_env env, napi_value exception) { summary += "test"; }; napi_value nullValue = nullptr; @@ -197,7 +197,7 @@ HWTEST_F(NapiUncaughtExceptionCallbackTest, NapiUncaughtExceptionCallbackTest_03 napi_create_string_utf8(env, errorStack.c_str(), errorStack.length(), &nativeErrorStack); napi_set_named_property(env, object, "code", nativeErrorCode); napi_set_named_property(env, object, "stack", nativeErrorStack); - auto task = [](std::string summary, const JsEnv::ErrorObject errorObj) { + auto task = [](std::string summary, const JsEnv::ErrorObject errorObj, napi_env env, napi_value exception) { summary += "test"; }; NapiUncaughtExceptionCallback callback(task, nullptr, env); @@ -232,7 +232,7 @@ HWTEST_F(NapiUncaughtExceptionCallbackTest, NapiUncaughtExceptionCallbackTest_04 return thisVar; }; - auto task = [](std::string summary, const JsEnv::ErrorObject errorObj) { + auto task = [](std::string summary, const JsEnv::ErrorObject errorObj, napi_env env, napi_value exception) { summary += "test"; }; napi_value nativeErrorCode = nullptr;