1/*
2 * Copyright (c) 2024 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
17#include "ecmascript/platform/aot_crash_info.h"
18#if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE)
19#include "parameters.h"
20#endif
21
22namespace panda::ecmascript {
23#ifdef JIT_ESCAPE_ENABLE
24static struct sigaction s_oldSa[SIGSYS + 1]; // SIGSYS = 31
25void GetSignalHandler(int signal, siginfo_t *info, void *context)
26{
27    [[maybe_unused]] ucontext_t *ucontext = reinterpret_cast<ucontext_t*>(context);
28    [[maybe_unused]] mcontext_t &mcontext = ucontext->uc_mcontext;
29    uintptr_t pc = 0;
30#if defined(PANDA_TARGET_AMD64)
31    pc = static_cast<uintptr_t>(mcontext.gregs[REG_RIP]);
32#elif defined(PANDA_TARGET_ARM64)
33    pc = static_cast<uintptr_t>(mcontext.pc);
34#endif
35    if (JsStackInfo::loader == nullptr) {
36        ecmascript::JsStackInfo::BuildCrashInfo(false);
37    } else if (!JsStackInfo::loader->InsideStub(pc) && !JsStackInfo::loader->InsideAOT(pc)) {
38        ecmascript::JsStackInfo::BuildCrashInfo(false);
39    } else {
40        ecmascript::JsStackInfo::BuildCrashInfo(false, pc);
41    }
42    sigaction(signal, &s_oldSa[signal], nullptr);
43    int rc = syscall(SYS_rt_tgsigqueueinfo, getpid(), syscall(SYS_gettid), info->si_signo, info);
44    if (rc != 0) {
45        LOG_ECMA(ERROR) << "GetSignalHandler() failed to resend signal during crash";
46    }
47}
48
49void SignalReg(int signo)
50{
51    sigaction(signo, nullptr, &s_oldSa[signo]);
52    struct sigaction newAction;
53    newAction.sa_flags = SA_RESTART | SA_SIGINFO;
54    newAction.sa_sigaction = GetSignalHandler;
55    sigaction(signo, &newAction, nullptr);
56}
57#endif
58
59void SignalAllReg()
60{
61#ifdef JIT_ESCAPE_ENABLE
62    SignalReg(SIGABRT);
63    SignalReg(SIGBUS);
64    SignalReg(SIGSEGV);
65    SignalReg(SIGILL);
66    SignalReg(SIGKILL);
67    SignalReg(SIGSTKFLT);
68    SignalReg(SIGFPE);
69    SignalReg(SIGTRAP);
70#endif
71}
72
73bool AotCrashInfo::IsAotEscapedOrNotInEnableList(EcmaVM *vm, const std::string &bundleName) const
74{
75    if (!vm->GetJSOptions().WasAOTOutputFileSet() &&
76        !ohos::EnableAotJitListHelper::GetInstance()->IsEnableAot(bundleName)) {
77        LOG_ECMA(INFO) << "Stop load AOT because it's not in enable list";
78        return true;
79    }
80    if (IsAotEscaped()) {
81        LOG_ECMA(INFO) << "Stop load AOT because there are more crashes";
82        return true;
83    }
84    return false;
85}
86
87bool AotCrashInfo::IsAotEscapedOrCompiledOnce(AotCompilerPreprocessor &cPreprocessor, int32_t &ret) const
88{
89    if (!cPreprocessor.GetMainPkgArgs()) {
90        return false;
91    }
92    std::string pgoRealPath = cPreprocessor.GetMainPkgArgs()->GetPgoDir();
93    pgoRealPath.append(ohos::OhosConstants::PATH_SEPARATOR);
94    pgoRealPath.append(ohos::OhosConstants::AOT_RUNTIME_INFO_NAME);
95    if (ohos::EnableAotJitListHelper::GetInstance()->IsAotCompileSuccessOnce(pgoRealPath)) {
96        ret = 0;
97        LOG_ECMA(INFO) << "Aot has compile success once or escaped.";
98        return true;
99    }
100    if (IsAotEscaped(pgoRealPath)) {
101        ret = -1;
102        LOG_ECMA(INFO) << "Aot has escaped";
103        return true;
104    }
105    return false;
106}
107
108void AotCrashInfo::SetOptionPGOProfiler(JSRuntimeOptions *options, const std::string &bundleName) const
109{
110#ifdef AOT_ESCAPE_ENABLE
111    if (ohos::EnableAotJitListHelper::GetInstance()->IsEnableAot(bundleName)) {
112        options->SetEnablePGOProfiler(true);
113        if (options->GetAOTHasException() ||
114            ohos::EnableAotJitListHelper::GetInstance()->IsAotCompileSuccessOnce() ||
115            IsAotEscaped()) {
116            options->SetEnablePGOProfiler(false);
117            LOG_ECMA(INFO) << "Aot has compile success once or escaped.";
118        }
119    }
120#endif
121    (void)options;
122    (void)bundleName;
123}
124
125bool AotCrashInfo::IsAotEscaped(const std::string &pgoRealPath)
126{
127    if (AotCrashInfo::GetAotEscapeDisable()) {
128        return false;
129    }
130    auto escapeMap = ohos::AotRuntimeInfo::GetInstance().CollectCrashSum(pgoRealPath);
131    int totalCrashes = escapeMap[ohos::RuntimeInfoType::AOT_CRASH] +
132                       escapeMap[ohos::RuntimeInfoType::JIT] +
133                       escapeMap[ohos::RuntimeInfoType::OTHERS] +
134                       escapeMap[ohos::RuntimeInfoType::JS];
135    return totalCrashes >= OPT_CODE_CRASH_THRESHOLD;
136}
137
138bool AotCrashInfo::IsJitEscape()
139{
140    auto escapeMap = ohos::AotRuntimeInfo::GetInstance().CollectCrashSum();
141    int totalCrashes = escapeMap[ohos::RuntimeInfoType::AOT_CRASH] +
142                       escapeMap[ohos::RuntimeInfoType::JIT] +
143                       escapeMap[ohos::RuntimeInfoType::OTHERS] +
144                       escapeMap[ohos::RuntimeInfoType::JS];
145    return totalCrashes >= OPT_CODE_CRASH_THRESHOLD;
146}
147
148bool AotCrashInfo::GetAotEscapeDisable()
149{
150#if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE)
151        return OHOS::system::GetBoolParameter(AOT_ESCAPE_DISABLE, false);
152#endif
153        return false;
154}
155
156std::string AotCrashInfo::GetSandBoxPath()
157{
158    return ohos::OhosConstants::SANDBOX_ARK_PROFILE_PATH;
159}
160
161int AotCrashInfo::GetAotCrashCount()
162{
163    return AOT_CRASH_COUNT;
164}
165
166int AotCrashInfo::GetJitCrashCount()
167{
168    return JIT_CRASH_COUNT;
169}
170
171int AotCrashInfo::GetJsCrashCount()
172{
173    return JS_CRASH_COUNT;
174}
175
176int AotCrashInfo::GetOthersCrashCount()
177{
178    return OTHERS_CRASH_COUNT;
179}
180
181}  // namespace panda::ecmascript
182