* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/ohos/ohos_constants.h"
#include "ecmascript/ohos/aot_runtime_info.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/platform/aot_crash_info.h"
#include "ecmascript/platform/file.h"
#if (defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE)) || defined(ENABLE_OHOS_PARAMETER)
#include "parameters.h"
#endif
namespace panda::ecmascript {
namespace {
constexpr const char *const AOT_BUILD_COUNT_DISABLE = "ark.aot.build.count.disable";
bool GetAotBuildCountDisable()
{
#ifdef ENABLE_OHOS_PARAMETER
return OHOS::system::GetBoolParameter(AOT_BUILD_COUNT_DISABLE, false);
#endif
return false;
}
bool IsAotCompileSuccessOnce(const std::string &pgoRealPath)
{
if (GetAotBuildCountDisable()) {
return false;
}
int count = ohos::AotRuntimeInfo::GetInstance().GetCompileCountByType(
ohos::RuntimeInfoType::AOT_BUILD, pgoRealPath);
return count > 0;
}
}
#ifdef ENABLE_OHOS_PARAMETER
static struct sigaction s_oldSa[SIGSYS + 1];
void GetSignalHandler(int signal, siginfo_t *info, void *context)
{
[[maybe_unused]] ucontext_t *ucontext = reinterpret_cast<ucontext_t*>(context);
[[maybe_unused]] mcontext_t &mcontext = ucontext->uc_mcontext;
uintptr_t pc = 0;
#if defined(PANDA_TARGET_AMD64)
pc = static_cast<uintptr_t>(mcontext.gregs[REG_RIP]);
#elif defined(PANDA_TARGET_ARM64)
pc = static_cast<uintptr_t>(mcontext.pc);
#endif
if (JsStackInfo::loader == nullptr) {
ecmascript::JsStackInfo::BuildCrashInfo(false);
} else if (!JsStackInfo::loader->InsideStub(pc) && !JsStackInfo::loader->InsideAOT(pc)) {
ecmascript::JsStackInfo::BuildCrashInfo(false);
} else {
ecmascript::JsStackInfo::BuildCrashInfo(false, pc);
}
sigaction(signal, &s_oldSa[signal], nullptr);
int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), syscall(SYS_gettid), info->si_signo, info);
if (rc != 0) {
LOG_ECMA(ERROR) << "GetSignalHandler() failed to resend signal during crash";
}
}
void SignalReg(int signo)
{
sigaction(signo, nullptr, &s_oldSa[signo]);
struct sigaction newAction;
if (memset_s(&newAction, sizeof(newAction), 0, sizeof(newAction)) != EOK) {
LOG_ECMA(ERROR) << "memset_s newAction failed : " << strerror(errno);
return;
}
newAction.sa_flags = SA_RESTART | SA_SIGINFO;
newAction.sa_sigaction = GetSignalHandler;
sigaction(signo, &newAction, nullptr);
}
#endif
void SignalAllReg()
{
#ifdef ENABLE_OHOS_PARAMETER
SignalReg(SIGABRT);
SignalReg(SIGBUS);
SignalReg(SIGSEGV);
SignalReg(SIGILL);
SignalReg(SIGKILL);
SignalReg(SIGSTKFLT);
SignalReg(SIGFPE);
SignalReg(SIGTRAP);
#endif
}
bool AotCrashInfo::IsAotEscapedOrNotInEnableList(EcmaVM *vm, const std::string &bundleName) const
{
if (!vm->GetJSOptions().WasAOTOutputFileSet() &&
!ohos::EnableAotJitListHelper::GetInstance()->IsEnableAot(bundleName)) {
LOG_ECMA(INFO) << "Stop load AOT because it's not in enable list";
return true;
}
if (IsAotEscaped()) {
LOG_ECMA(INFO) << "Stop load AOT because there are more crashes";
return true;
}
return false;
}
bool AotCrashInfo::IsAotEscapedOrCompiledOnce(AotCompilerPreprocessor &cPreprocessor, int32_t &ret) const
{
if (!cPreprocessor.GetMainPkgArgs()) {
return false;
}
std::string pgoRealPath = cPreprocessor.GetMainPkgArgs()->GetPgoDir();
pgoRealPath.append(ohos::OhosConstants::PATH_SEPARATOR);
pgoRealPath.append(ohos::OhosConstants::AOT_RUNTIME_INFO_NAME);
if (IsAotCompileSuccessOnce(pgoRealPath)) {
ret = 0;
LOG_ECMA(INFO) << "Aot has compile success once or escaped.";
return true;
}
if (IsAotEscaped(pgoRealPath)) {
ret = -1;
LOG_ECMA(INFO) << "Aot has escaped";
return true;
}
return false;
}
void AotCrashInfo::SetOptionPGOProfiler(JSRuntimeOptions *options, const std::string &bundleName) const
{
#ifdef ENABLE_OHOS_PARAMETER
if (ohos::EnableAotJitListHelper::GetInstance()->IsEnableAot(bundleName)) {
options->SetEnablePGOProfiler(true);
if (options->GetAOTHasException() || ecmascript::AnFileDataManager::GetInstance()->IsEnable()
|| IsAotEscaped()) {
options->SetEnablePGOProfiler(false);
pgo::PGOProfilerManager::GetInstance()->SetDisablePGO(true);
LOG_ECMA(INFO) << "Aot has compile success once or escaped.";
}
}
#endif
(void)options;
(void)bundleName;
}
bool AotCrashInfo::IsAotEscaped(const std::string &pgoRealPath)
{
if (AotCrashInfo::GetAotEscapeDisable()) {
return false;
}
std::string filePath = std::string(ohos::OhosConstants::SANDBOX_ARK_PROFILE_PATH)
+ std::string(ohos::OhosConstants::PATH_SEPARATOR)
+ std::string(ohos::OhosConstants::AOT_RUNTIME_INFO_NAME);
return FileExist(filePath.c_str());
}
bool AotCrashInfo::IsJitEscape()
{
std::string filePath = std::string(ohos::OhosConstants::SANDBOX_ARK_PROFILE_PATH)
+ std::string(ohos::OhosConstants::PATH_SEPARATOR)
+ std::string(ohos::OhosConstants::AOT_RUNTIME_INFO_NAME);
return FileExist(filePath.c_str());
}
bool AotCrashInfo::GetAotEscapeDisable()
{
#if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE)
return OHOS::system::GetBoolParameter(AOT_ESCAPE_DISABLE, false);
#endif
return false;
}
std::string AotCrashInfo::GetSandBoxPath()
{
return ohos::OhosConstants::SANDBOX_ARK_PROFILE_PATH;
}
int AotCrashInfo::GetAotCrashCount()
{
return AOT_CRASH_COUNT;
}
int AotCrashInfo::GetJitCrashCount()
{
return JIT_CRASH_COUNT;
}
int AotCrashInfo::GetJsCrashCount()
{
return JS_CRASH_COUNT;
}
int AotCrashInfo::GetOthersCrashCount()
{
return OTHERS_CRASH_COUNT;
}
}