14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
174514f5e3Sopenharmony_ci
184514f5e3Sopenharmony_ci#include <atomic>
194514f5e3Sopenharmony_ci#include <chrono>
204514f5e3Sopenharmony_ci#include <climits>
214514f5e3Sopenharmony_ci#include <fstream>
224514f5e3Sopenharmony_ci
234514f5e3Sopenharmony_ci#include "ecmascript/compiler/aot_file/aot_file_manager.h"
244514f5e3Sopenharmony_ci#include "ecmascript/jspandafile/js_pandafile_manager.h"
254514f5e3Sopenharmony_ci#include "ecmascript/platform/ffrt.h"
264514f5e3Sopenharmony_ci
274514f5e3Sopenharmony_ci#if defined(ENABLE_FFRT_INTERFACES)
284514f5e3Sopenharmony_ci#include "c/executor_task.h"
294514f5e3Sopenharmony_ci#endif
304514f5e3Sopenharmony_ci
314514f5e3Sopenharmony_ci#if !defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
324514f5e3Sopenharmony_ci    #error "ECMASCRIPT_SUPPORT_CPUPROFILER not defined"
334514f5e3Sopenharmony_ci#endif
344514f5e3Sopenharmony_ci
354514f5e3Sopenharmony_cinamespace panda::ecmascript {
364514f5e3Sopenharmony_ciMutex CpuProfiler::synchronizationMutex_;
374514f5e3Sopenharmony_ciCMap<pthread_t, struct TaskInfo> CpuProfiler::profilerMap_ = CMap<pthread_t, struct TaskInfo>();
384514f5e3Sopenharmony_ciCpuProfiler::CpuProfiler(const EcmaVM *vm, const int interval) : vm_(vm), interval_(interval)
394514f5e3Sopenharmony_ci{
404514f5e3Sopenharmony_ci    enableVMTag_ = const_cast<EcmaVM *>(vm)->GetJSOptions().EnableCpuProfilerVMTag();
414514f5e3Sopenharmony_ci    generator_ = new SamplesRecord();
424514f5e3Sopenharmony_ci    generator_->SetEnableVMTag(enableVMTag_);
434514f5e3Sopenharmony_ci    generator_->SetSourceMapTranslateCallback(vm->GetSourceMapTranslateCallback());
444514f5e3Sopenharmony_ci    generator_->NodeInit();
454514f5e3Sopenharmony_ci    if (generator_->SemInit(0, 0, 0) != 0) {
464514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "sem_[0] init failed";
474514f5e3Sopenharmony_ci    }
484514f5e3Sopenharmony_ci    if (generator_->SemInit(1, 0, 0) != 0) {
494514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "sem_[1] init failed";
504514f5e3Sopenharmony_ci    }
514514f5e3Sopenharmony_ci    if (generator_->SemInit(2, 0, 0) != 0) { // 2: signal 2
524514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "sem_[2] init failed";
534514f5e3Sopenharmony_ci    }
544514f5e3Sopenharmony_ci}
554514f5e3Sopenharmony_ci
564514f5e3Sopenharmony_cibool CpuProfiler::RegisterGetStackSignal()
574514f5e3Sopenharmony_ci{
584514f5e3Sopenharmony_ci    struct sigaction sa;
594514f5e3Sopenharmony_ci    sa.sa_sigaction = &GetStackSignalHandler;
604514f5e3Sopenharmony_ci    if (sigemptyset(&sa.sa_mask) != 0) {
614514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::RegisterGetStackSignal, sigemptyset failed, errno = " << errno;
624514f5e3Sopenharmony_ci        return false;
634514f5e3Sopenharmony_ci    }
644514f5e3Sopenharmony_ci    sa.sa_flags = SA_RESTART | SA_SIGINFO;
654514f5e3Sopenharmony_ci    if (sigaction(SIGPROF, &sa, nullptr) != 0) {
664514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::RegisterGetStackSignal, sigaction failed, errno = " << errno;
674514f5e3Sopenharmony_ci        return false;
684514f5e3Sopenharmony_ci    }
694514f5e3Sopenharmony_ci    return true;
704514f5e3Sopenharmony_ci}
714514f5e3Sopenharmony_ci
724514f5e3Sopenharmony_cibool CpuProfiler::StartCpuProfilerForInfo()
734514f5e3Sopenharmony_ci{
744514f5e3Sopenharmony_ci    LOG_ECMA(INFO) << "CpuProfiler::StartCpuProfilerForInfo, sampling interval = " << interval_;
754514f5e3Sopenharmony_ci    if (isProfiling_) {
764514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StartCpuProfilerForInfo, can not start when CpuProfiler is Profiling";
774514f5e3Sopenharmony_ci        return false;
784514f5e3Sopenharmony_ci    }
794514f5e3Sopenharmony_ci    if (!RegisterGetStackSignal()) {
804514f5e3Sopenharmony_ci        return false;
814514f5e3Sopenharmony_ci    }
824514f5e3Sopenharmony_ci    // when the ffrt is enabled for the thread, the tid_ will be task id
834514f5e3Sopenharmony_ci    tid_ = static_cast<pthread_t>(JSThread::GetCurrentThreadId());
844514f5e3Sopenharmony_ci    void *taskHandle = nullptr;
854514f5e3Sopenharmony_ci    // when the ffrt is enabled for the thread,
864514f5e3Sopenharmony_ci    // we record the task handle, which can be used to obtain the running thread id in the task
874514f5e3Sopenharmony_ci#if defined(ENABLE_FFRT_INTERFACES)
884514f5e3Sopenharmony_ci    taskHandle = ffrt_get_cur_task();
894514f5e3Sopenharmony_ci    TaskInfo taskInfo { vm_, taskHandle };
904514f5e3Sopenharmony_ci#else
914514f5e3Sopenharmony_ci    TaskInfo taskInfo { vm_, nullptr };
924514f5e3Sopenharmony_ci#endif // defined(ENABLE_FFRT_INTERFACES)
934514f5e3Sopenharmony_ci    {
944514f5e3Sopenharmony_ci        LockHolder lock(synchronizationMutex_);
954514f5e3Sopenharmony_ci        profilerMap_[tid_] = taskInfo;
964514f5e3Sopenharmony_ci    }
974514f5e3Sopenharmony_ci
984514f5e3Sopenharmony_ci    JSPandaFileManager *pandaFileManager = JSPandaFileManager::GetInstance();
994514f5e3Sopenharmony_ci    pandaFileManager->EnumerateJSPandaFiles([&](const std::shared_ptr<JSPandaFile> &file) -> bool {
1004514f5e3Sopenharmony_ci        pandaFileManager->CpuProfilerGetJSPtExtractor(file.get());
1014514f5e3Sopenharmony_ci        return true;
1024514f5e3Sopenharmony_ci    });
1034514f5e3Sopenharmony_ci
1044514f5e3Sopenharmony_ci    generator_->SetTimeDeltaThreshold(interval_ * THRESHOLD_GROWTH_FACTORY + THRESHOLD_FIXED_INCREMENT);
1054514f5e3Sopenharmony_ci    generator_->SetIsStart(true);
1064514f5e3Sopenharmony_ci    uint64_t startTime = SamplingProcessor::GetMicrosecondsTimeStamp();
1074514f5e3Sopenharmony_ci    generator_->SetThreadStartTime(startTime);
1084514f5e3Sopenharmony_ci    params_ = new RunParams(generator_, static_cast<uint32_t>(interval_), pthread_self(), taskHandle);
1094514f5e3Sopenharmony_ci    if (pthread_create(&tid_, nullptr, SamplingProcessor::Run, params_) != 0) {
1104514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StartCpuProfilerForInfo, pthread_create failed, errno = " << errno;
1114514f5e3Sopenharmony_ci        return false;
1124514f5e3Sopenharmony_ci    }
1134514f5e3Sopenharmony_ci    isProfiling_ = true;
1144514f5e3Sopenharmony_ci    vm_->GetJSThread()->SetIsProfiling(true);
1154514f5e3Sopenharmony_ci    outToFile_ = false;
1164514f5e3Sopenharmony_ci    return true;
1174514f5e3Sopenharmony_ci}
1184514f5e3Sopenharmony_ci
1194514f5e3Sopenharmony_cibool CpuProfiler::StartCpuProfilerForFile(const std::string &fileName)
1204514f5e3Sopenharmony_ci{
1214514f5e3Sopenharmony_ci    LOG_ECMA(INFO) << "CpuProfiler::StartCpuProfilerForFile, sampling interval = " << interval_;
1224514f5e3Sopenharmony_ci    if (isProfiling_) {
1234514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StartCpuProfilerForFile, can not start when CpuProfiler is Profiling";
1244514f5e3Sopenharmony_ci        return false;
1254514f5e3Sopenharmony_ci    }
1264514f5e3Sopenharmony_ci    std::string absoluteFilePath("");
1274514f5e3Sopenharmony_ci    if (!CheckFileName(fileName, absoluteFilePath)) {
1284514f5e3Sopenharmony_ci        return false;
1294514f5e3Sopenharmony_ci    }
1304514f5e3Sopenharmony_ci    fileName_ = absoluteFilePath;
1314514f5e3Sopenharmony_ci    generator_->SetFileName(fileName_);
1324514f5e3Sopenharmony_ci    generator_->fileHandle_.open(fileName_.c_str());
1334514f5e3Sopenharmony_ci    if (generator_->fileHandle_.fail()) {
1344514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StartCpuProfilerForFile, fileHandle_ open failed";
1354514f5e3Sopenharmony_ci        return false;
1364514f5e3Sopenharmony_ci    }
1374514f5e3Sopenharmony_ci    if (!RegisterGetStackSignal()) {
1384514f5e3Sopenharmony_ci        return false;
1394514f5e3Sopenharmony_ci    }
1404514f5e3Sopenharmony_ci    // when the ffrt is enabled for the thread, the tid_ will be task id
1414514f5e3Sopenharmony_ci    tid_ = static_cast<pthread_t>(JSThread::GetCurrentThreadId());
1424514f5e3Sopenharmony_ci    void *taskHandle = nullptr;
1434514f5e3Sopenharmony_ci    // when the ffrt is enabled for the thread,
1444514f5e3Sopenharmony_ci    // we record the task handle, which can be used to obtain the running thread id in the task
1454514f5e3Sopenharmony_ci#if defined(ENABLE_FFRT_INTERFACES)
1464514f5e3Sopenharmony_ci    taskHandle = ffrt_get_cur_task();
1474514f5e3Sopenharmony_ci    TaskInfo taskInfo { vm_, taskHandle };
1484514f5e3Sopenharmony_ci#else
1494514f5e3Sopenharmony_ci    TaskInfo taskInfo { vm_, nullptr };
1504514f5e3Sopenharmony_ci#endif // defined(ENABLE_FFRT_INTERFACES)
1514514f5e3Sopenharmony_ci    {
1524514f5e3Sopenharmony_ci        LockHolder lock(synchronizationMutex_);
1534514f5e3Sopenharmony_ci        profilerMap_[tid_] = taskInfo;
1544514f5e3Sopenharmony_ci    }
1554514f5e3Sopenharmony_ci
1564514f5e3Sopenharmony_ci    JSPandaFileManager *pandaFileManager = JSPandaFileManager::GetInstance();
1574514f5e3Sopenharmony_ci    pandaFileManager->EnumerateJSPandaFiles([&](const std::shared_ptr<JSPandaFile> &file) -> bool {
1584514f5e3Sopenharmony_ci        pandaFileManager->CpuProfilerGetJSPtExtractor(file.get());
1594514f5e3Sopenharmony_ci        return true;
1604514f5e3Sopenharmony_ci    });
1614514f5e3Sopenharmony_ci
1624514f5e3Sopenharmony_ci    generator_->SetTimeDeltaThreshold(interval_ * THRESHOLD_GROWTH_FACTORY + THRESHOLD_FIXED_INCREMENT);
1634514f5e3Sopenharmony_ci    generator_->SetIsStart(true);
1644514f5e3Sopenharmony_ci    uint64_t startTime = SamplingProcessor::GetMicrosecondsTimeStamp();
1654514f5e3Sopenharmony_ci    generator_->SetThreadStartTime(startTime);
1664514f5e3Sopenharmony_ci    params_ = new RunParams(generator_, static_cast<uint32_t>(interval_), pthread_self(), taskHandle);
1674514f5e3Sopenharmony_ci    if (pthread_create(&tid_, nullptr, SamplingProcessor::Run, params_) != 0) {
1684514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StartCpuProfilerForFile, pthread_create failed, errno = " << errno;
1694514f5e3Sopenharmony_ci        return false;
1704514f5e3Sopenharmony_ci    }
1714514f5e3Sopenharmony_ci    isProfiling_ = true;
1724514f5e3Sopenharmony_ci    vm_->GetJSThread()->SetIsProfiling(true);
1734514f5e3Sopenharmony_ci    outToFile_ = true;
1744514f5e3Sopenharmony_ci    return true;
1754514f5e3Sopenharmony_ci}
1764514f5e3Sopenharmony_ci
1774514f5e3Sopenharmony_cibool CpuProfiler::StopCpuProfilerForInfo(std::unique_ptr<struct ProfileInfo> &profileInfo)
1784514f5e3Sopenharmony_ci{
1794514f5e3Sopenharmony_ci    LOG_ECMA(INFO) << "CpuProfiler::StopCpuProfilerForInfo enter";
1804514f5e3Sopenharmony_ci    if (!isProfiling_) {
1814514f5e3Sopenharmony_ci        LOG_ECMA(WARN) << "CpuProfiler::StopCpuProfilerForInfo, not isProfiling_";
1824514f5e3Sopenharmony_ci        return true;
1834514f5e3Sopenharmony_ci    }
1844514f5e3Sopenharmony_ci    if (outToFile_) {
1854514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForInfo, is outToFile_";
1864514f5e3Sopenharmony_ci        return false;
1874514f5e3Sopenharmony_ci    }
1884514f5e3Sopenharmony_ci    generator_->SetIsStart(false);
1894514f5e3Sopenharmony_ci    if (generator_->SemPost(0) != 0) {
1904514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForInfo, sem_[0] post failed, errno = " << errno;
1914514f5e3Sopenharmony_ci        return false;
1924514f5e3Sopenharmony_ci    }
1934514f5e3Sopenharmony_ci    if (generator_->SemWait(1) != 0) {
1944514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForInfo, sem_[1] wait failed, errno = " << errno;
1954514f5e3Sopenharmony_ci        return false;
1964514f5e3Sopenharmony_ci    }
1974514f5e3Sopenharmony_ci    isProfiling_ = false;
1984514f5e3Sopenharmony_ci    vm_->GetJSThread()->SetIsProfiling(false);
1994514f5e3Sopenharmony_ci    profileInfo = generator_->GetProfileInfo();
2004514f5e3Sopenharmony_ci    return true;
2014514f5e3Sopenharmony_ci}
2024514f5e3Sopenharmony_ci
2034514f5e3Sopenharmony_civoid CpuProfiler::SetCpuSamplingInterval(int interval)
2044514f5e3Sopenharmony_ci{
2054514f5e3Sopenharmony_ci    interval_ = static_cast<uint32_t>(interval);
2064514f5e3Sopenharmony_ci}
2074514f5e3Sopenharmony_ci
2084514f5e3Sopenharmony_cibool CpuProfiler::StopCpuProfilerForFile()
2094514f5e3Sopenharmony_ci{
2104514f5e3Sopenharmony_ci    LOG_ECMA(INFO) << "CpuProfiler::StopCpuProfilerForFile enter";
2114514f5e3Sopenharmony_ci    if (!isProfiling_) {
2124514f5e3Sopenharmony_ci        LOG_ECMA(WARN) << "CpuProfiler::StopCpuProfilerForFile, not isProfiling_";
2134514f5e3Sopenharmony_ci        return true;
2144514f5e3Sopenharmony_ci    }
2154514f5e3Sopenharmony_ci    if (!outToFile_) {
2164514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForFile, not outToFile_";
2174514f5e3Sopenharmony_ci        return false;
2184514f5e3Sopenharmony_ci    }
2194514f5e3Sopenharmony_ci    generator_->SetIsStart(false);
2204514f5e3Sopenharmony_ci    if (generator_->SemPost(0) != 0) {
2214514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForFile, sem_[0] post failed, errno = " << errno;
2224514f5e3Sopenharmony_ci        return false;
2234514f5e3Sopenharmony_ci    }
2244514f5e3Sopenharmony_ci    if (generator_->SemWait(1) != 0) {
2254514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::StopCpuProfilerForFile, sem_[1] wait failed, errno = " << errno;
2264514f5e3Sopenharmony_ci        return false;
2274514f5e3Sopenharmony_ci    }
2284514f5e3Sopenharmony_ci    isProfiling_ = false;
2294514f5e3Sopenharmony_ci    vm_->GetJSThread()->SetIsProfiling(false);
2304514f5e3Sopenharmony_ci    generator_->StringifySampleData();
2314514f5e3Sopenharmony_ci    std::string fileData = generator_->GetSampleData();
2324514f5e3Sopenharmony_ci    generator_->fileHandle_ << fileData;
2334514f5e3Sopenharmony_ci    return true;
2344514f5e3Sopenharmony_ci}
2354514f5e3Sopenharmony_ci
2364514f5e3Sopenharmony_ciCpuProfiler::~CpuProfiler()
2374514f5e3Sopenharmony_ci{
2384514f5e3Sopenharmony_ci    if (generator_->SemDestroy(0) != 0) {
2394514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "sem_[0] destroy failed";
2404514f5e3Sopenharmony_ci    }
2414514f5e3Sopenharmony_ci    if (generator_->SemDestroy(1) != 0) {
2424514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "sem_[1] destroy failed";
2434514f5e3Sopenharmony_ci    }
2444514f5e3Sopenharmony_ci    if (generator_->SemDestroy(2) != 0) { // 2: signal 2
2454514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "sem_[2] destroy failed";
2464514f5e3Sopenharmony_ci    }
2474514f5e3Sopenharmony_ci    if (generator_ != nullptr) {
2484514f5e3Sopenharmony_ci        delete generator_;
2494514f5e3Sopenharmony_ci        generator_ = nullptr;
2504514f5e3Sopenharmony_ci    }
2514514f5e3Sopenharmony_ci    if (params_ != nullptr) {
2524514f5e3Sopenharmony_ci        delete params_;
2534514f5e3Sopenharmony_ci        params_ = nullptr;
2544514f5e3Sopenharmony_ci    }
2554514f5e3Sopenharmony_ci}
2564514f5e3Sopenharmony_ci
2574514f5e3Sopenharmony_civoid CpuProfiler::GetStack(FrameIterator &it)
2584514f5e3Sopenharmony_ci{
2594514f5e3Sopenharmony_ci    const CMap<struct MethodKey, struct FrameInfo> &stackInfo = generator_->GetStackInfo();
2604514f5e3Sopenharmony_ci    bool topFrame = true;
2614514f5e3Sopenharmony_ci    generator_->ResetFrameLength();
2624514f5e3Sopenharmony_ci    for (; !it.Done(); it.Advance<>()) {
2634514f5e3Sopenharmony_ci        auto method = it.CheckAndGetMethod();
2644514f5e3Sopenharmony_ci        if (method == nullptr || !JSTaggedValue(method).IsMethod()) {
2654514f5e3Sopenharmony_ci            continue;
2664514f5e3Sopenharmony_ci        }
2674514f5e3Sopenharmony_ci        bool isNative = method->IsNativeWithCallField();
2684514f5e3Sopenharmony_ci        struct MethodKey methodKey;
2694514f5e3Sopenharmony_ci        methodKey.deoptType = method->GetDeoptType();
2704514f5e3Sopenharmony_ci        if (topFrame) {
2714514f5e3Sopenharmony_ci            methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, true, enableVMTag_);
2724514f5e3Sopenharmony_ci            topFrame = false;
2734514f5e3Sopenharmony_ci        } else {
2744514f5e3Sopenharmony_ci            methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, false, enableVMTag_);
2754514f5e3Sopenharmony_ci        }
2764514f5e3Sopenharmony_ci        if (isNative) {
2774514f5e3Sopenharmony_ci            JsStackGetter::GetCallLineNumber(it, methodKey.lineNumber);
2784514f5e3Sopenharmony_ci        }
2794514f5e3Sopenharmony_ci        void *methodIdentifier = JsStackGetter::GetMethodIdentifier(method, it);
2804514f5e3Sopenharmony_ci        if (methodIdentifier == nullptr) {
2814514f5e3Sopenharmony_ci            continue;
2824514f5e3Sopenharmony_ci        }
2834514f5e3Sopenharmony_ci        methodKey.methodIdentifier = methodIdentifier;
2844514f5e3Sopenharmony_ci        if (stackInfo.count(methodKey) == 0) {
2854514f5e3Sopenharmony_ci            struct FrameInfoTemp codeEntry;
2864514f5e3Sopenharmony_ci            if (UNLIKELY(!JsStackGetter::ParseMethodInfo(methodKey, it, vm_, codeEntry, true))) {
2874514f5e3Sopenharmony_ci                continue;
2884514f5e3Sopenharmony_ci            }
2894514f5e3Sopenharmony_ci            if (UNLIKELY(!generator_->PushStackInfo(codeEntry))) {
2904514f5e3Sopenharmony_ci                return;
2914514f5e3Sopenharmony_ci            }
2924514f5e3Sopenharmony_ci        }
2934514f5e3Sopenharmony_ci        if (UNLIKELY(!generator_->PushFrameStack(methodKey))) {
2944514f5e3Sopenharmony_ci            return;
2954514f5e3Sopenharmony_ci        }
2964514f5e3Sopenharmony_ci    }
2974514f5e3Sopenharmony_ci    generator_->PostFrame();
2984514f5e3Sopenharmony_ci}
2994514f5e3Sopenharmony_ci
3004514f5e3Sopenharmony_cibool CpuProfiler::GetStackBeforeCallNapi(JSThread *thread)
3014514f5e3Sopenharmony_ci{
3024514f5e3Sopenharmony_ci    uint64_t tempTimeStamp = SamplingProcessor::GetMicrosecondsTimeStamp();
3034514f5e3Sopenharmony_ci    if (tempTimeStamp - beforeCallNapiTimeStamp_ < interval_) {
3044514f5e3Sopenharmony_ci        return false;
3054514f5e3Sopenharmony_ci    }
3064514f5e3Sopenharmony_ci
3074514f5e3Sopenharmony_ci    if (GetStackCallNapi(thread, true)) {
3084514f5e3Sopenharmony_ci        beforeCallNapiTimeStamp_ = tempTimeStamp;
3094514f5e3Sopenharmony_ci        return true;
3104514f5e3Sopenharmony_ci    }
3114514f5e3Sopenharmony_ci    return false;
3124514f5e3Sopenharmony_ci}
3134514f5e3Sopenharmony_ci
3144514f5e3Sopenharmony_civoid CpuProfiler::GetStackAfterCallNapi(JSThread *thread)
3154514f5e3Sopenharmony_ci{
3164514f5e3Sopenharmony_ci    GetStackCallNapi(thread, false);
3174514f5e3Sopenharmony_ci}
3184514f5e3Sopenharmony_ci
3194514f5e3Sopenharmony_cibool CpuProfiler::GetStackCallNapi(JSThread *thread, bool beforeCallNapi)
3204514f5e3Sopenharmony_ci{
3214514f5e3Sopenharmony_ci    [[maybe_unused]] CallNapiScope scope(this);
3224514f5e3Sopenharmony_ci    const CMap<struct MethodKey, struct FrameInfo> &stackInfo = generator_->GetStackInfo();
3234514f5e3Sopenharmony_ci    generator_->ClearNapiStack();
3244514f5e3Sopenharmony_ci    bool topFrame = true;
3254514f5e3Sopenharmony_ci    auto currentFrame = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
3264514f5e3Sopenharmony_ci    FrameIterator it(currentFrame, thread);
3274514f5e3Sopenharmony_ci    if (!beforeCallNapi) {
3284514f5e3Sopenharmony_ci        it.Advance<GCVisitedFlag::IGNORED>();
3294514f5e3Sopenharmony_ci    }
3304514f5e3Sopenharmony_ci    for (; !it.Done(); it.Advance<GCVisitedFlag::IGNORED>()) {
3314514f5e3Sopenharmony_ci        auto method = it.CheckAndGetMethod();
3324514f5e3Sopenharmony_ci        if (method == nullptr || !JSTaggedValue(method).IsMethod()) {
3334514f5e3Sopenharmony_ci            continue;
3344514f5e3Sopenharmony_ci        }
3354514f5e3Sopenharmony_ci
3364514f5e3Sopenharmony_ci        bool isNative = method->IsNativeWithCallField();
3374514f5e3Sopenharmony_ci        struct MethodKey methodKey;
3384514f5e3Sopenharmony_ci        methodKey.deoptType = method->GetDeoptType();
3394514f5e3Sopenharmony_ci        if (topFrame) {
3404514f5e3Sopenharmony_ci            if (beforeCallNapi) {
3414514f5e3Sopenharmony_ci                methodKey.state = RunningState::NAPI;
3424514f5e3Sopenharmony_ci            } else {
3434514f5e3Sopenharmony_ci                methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, true, enableVMTag_);
3444514f5e3Sopenharmony_ci            }
3454514f5e3Sopenharmony_ci            topFrame = false;
3464514f5e3Sopenharmony_ci        } else {
3474514f5e3Sopenharmony_ci            methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, false, enableVMTag_);
3484514f5e3Sopenharmony_ci        }
3494514f5e3Sopenharmony_ci        if (isNative) {
3504514f5e3Sopenharmony_ci            JsStackGetter::GetCallLineNumber(it, methodKey.lineNumber);
3514514f5e3Sopenharmony_ci        }
3524514f5e3Sopenharmony_ci        void *methodIdentifier = JsStackGetter::GetMethodIdentifier(method, it);
3534514f5e3Sopenharmony_ci        if (methodIdentifier == nullptr) {
3544514f5e3Sopenharmony_ci            continue;
3554514f5e3Sopenharmony_ci        }
3564514f5e3Sopenharmony_ci        methodKey.methodIdentifier = methodIdentifier;
3574514f5e3Sopenharmony_ci        if (stackInfo.count(methodKey) == 0) {
3584514f5e3Sopenharmony_ci            struct FrameInfoTemp codeEntry;
3594514f5e3Sopenharmony_ci            if (UNLIKELY(!JsStackGetter::ParseMethodInfo(methodKey, it, vm_, codeEntry, true))) {
3604514f5e3Sopenharmony_ci                continue;
3614514f5e3Sopenharmony_ci            }
3624514f5e3Sopenharmony_ci            if (UNLIKELY(!generator_->PushNapiStackInfo(codeEntry))) {
3634514f5e3Sopenharmony_ci                return false;
3644514f5e3Sopenharmony_ci            }
3654514f5e3Sopenharmony_ci        }
3664514f5e3Sopenharmony_ci        if (UNLIKELY(!generator_->PushNapiFrameStack(methodKey))) {
3674514f5e3Sopenharmony_ci            return false;
3684514f5e3Sopenharmony_ci        }
3694514f5e3Sopenharmony_ci    }
3704514f5e3Sopenharmony_ci    generator_->PostNapiFrame();
3714514f5e3Sopenharmony_ci    return true;
3724514f5e3Sopenharmony_ci}
3734514f5e3Sopenharmony_ci
3744514f5e3Sopenharmony_civoid CpuProfiler::GetStackSignalHandler(int signal, [[maybe_unused]] siginfo_t *siginfo, void *context)
3754514f5e3Sopenharmony_ci{
3764514f5e3Sopenharmony_ci    if (signal != SIGPROF) {
3774514f5e3Sopenharmony_ci        return;
3784514f5e3Sopenharmony_ci    }
3794514f5e3Sopenharmony_ci    CpuProfiler *profiler = nullptr;
3804514f5e3Sopenharmony_ci    JSThread *thread = nullptr;
3814514f5e3Sopenharmony_ci    {
3824514f5e3Sopenharmony_ci        LockHolder lock(synchronizationMutex_);
3834514f5e3Sopenharmony_ci        // If no task running in this thread, we get the id of the last task that ran in this thread
3844514f5e3Sopenharmony_ci        pthread_t tid = static_cast<pthread_t>(GetThreadIdOrCachedTaskId());
3854514f5e3Sopenharmony_ci        const EcmaVM *vm = profilerMap_[tid].vm_;
3864514f5e3Sopenharmony_ci        if (vm == nullptr) {
3874514f5e3Sopenharmony_ci            LOG_ECMA(ERROR) << "CpuProfiler GetStackSignalHandler vm is nullptr";
3884514f5e3Sopenharmony_ci            return;
3894514f5e3Sopenharmony_ci        }
3904514f5e3Sopenharmony_ci        profiler = vm->GetProfiler();
3914514f5e3Sopenharmony_ci        thread = vm->GetAssociatedJSThread();
3924514f5e3Sopenharmony_ci        if (profiler == nullptr) {
3934514f5e3Sopenharmony_ci            LOG_ECMA(ERROR) << "CpuProfiler GetStackSignalHandler profiler is nullptr";
3944514f5e3Sopenharmony_ci            return;
3954514f5e3Sopenharmony_ci        }
3964514f5e3Sopenharmony_ci    }
3974514f5e3Sopenharmony_ci    [[maybe_unused]] SignalStateScope scope(thread->GetEcmaVM()->GetJsDebuggerManager());
3984514f5e3Sopenharmony_ci
3994514f5e3Sopenharmony_ci    if (profiler->GetBuildNapiStack() || thread->GetGcState()) {
4004514f5e3Sopenharmony_ci        if (profiler->generator_->SemPost(0) != 0) {
4014514f5e3Sopenharmony_ci            LOG_ECMA(ERROR) << "sem_[0] post failed";
4024514f5e3Sopenharmony_ci        }
4034514f5e3Sopenharmony_ci        return;
4044514f5e3Sopenharmony_ci    }
4054514f5e3Sopenharmony_ci
4064514f5e3Sopenharmony_ci    uint64_t pc = 0;
4074514f5e3Sopenharmony_ci    if (thread->IsAsmInterpreter()) {
4084514f5e3Sopenharmony_ci        // If the attempt fails, the callback will be terminated directly to avoid the reentrancy deadlock,
4094514f5e3Sopenharmony_ci        // and a sampling will be abandoned. Failures are rare, so the impact on the overall sampling results
4104514f5e3Sopenharmony_ci        // is very limited.
4114514f5e3Sopenharmony_ci        if (!thread->GetEcmaVM()->GetAOTFileManager()->TryReadLock()) {
4124514f5e3Sopenharmony_ci            if (profiler->generator_->SemPost(0) != 0) {
4134514f5e3Sopenharmony_ci                LOG_ECMA(ERROR) << "sem_[0] post failed";
4144514f5e3Sopenharmony_ci            }
4154514f5e3Sopenharmony_ci            return;
4164514f5e3Sopenharmony_ci        }
4174514f5e3Sopenharmony_ci        pc = GetPcFromContext(context);
4184514f5e3Sopenharmony_ci    }
4194514f5e3Sopenharmony_ci    if (thread->IsAsmInterpreter() && profiler->IsAddrAtStubOrAot(pc) &&
4204514f5e3Sopenharmony_ci        !profiler->IsEntryFrameHeaderOrTail(thread, pc)) {
4214514f5e3Sopenharmony_ci        [[maybe_unused]] ucontext_t *ucontext = reinterpret_cast<ucontext_t*>(context);
4224514f5e3Sopenharmony_ci        [[maybe_unused]] mcontext_t &mcontext = ucontext->uc_mcontext;
4234514f5e3Sopenharmony_ci        [[maybe_unused]] void *fp = nullptr;
4244514f5e3Sopenharmony_ci        [[maybe_unused]] void *sp = nullptr;
4254514f5e3Sopenharmony_ci#if defined(PANDA_TARGET_AMD64)
4264514f5e3Sopenharmony_ci        fp = reinterpret_cast<void*>(mcontext.gregs[REG_RBP]);
4274514f5e3Sopenharmony_ci        sp = reinterpret_cast<void*>(mcontext.gregs[REG_RSP]);
4284514f5e3Sopenharmony_ci#elif defined(PANDA_TARGET_ARM64)
4294514f5e3Sopenharmony_ci        fp = reinterpret_cast<void*>(mcontext.regs[29]); // FP is an alias for x29.
4304514f5e3Sopenharmony_ci        sp = reinterpret_cast<void*>(mcontext.sp);
4314514f5e3Sopenharmony_ci#else
4324514f5e3Sopenharmony_ci        LOG_FULL(FATAL) << "AsmInterpreter does not currently support other platforms, please run on x64 and arm64";
4334514f5e3Sopenharmony_ci        return;
4344514f5e3Sopenharmony_ci#endif
4354514f5e3Sopenharmony_ci        if (reinterpret_cast<uint64_t*>(sp) > reinterpret_cast<uint64_t*>(fp)) {
4364514f5e3Sopenharmony_ci            LOG_ECMA(ERROR) << "sp > fp, stack frame exception";
4374514f5e3Sopenharmony_ci            if (profiler->generator_->SemPost(0) != 0) {
4384514f5e3Sopenharmony_ci                LOG_ECMA(ERROR) << "sem_[0] post failed";
4394514f5e3Sopenharmony_ci            }
4404514f5e3Sopenharmony_ci            return;
4414514f5e3Sopenharmony_ci        }
4424514f5e3Sopenharmony_ci        if (JsStackGetter::CheckFrameType(thread, reinterpret_cast<JSTaggedType *>(fp))) {
4434514f5e3Sopenharmony_ci            FrameIterator it(reinterpret_cast<JSTaggedType *>(fp), thread);
4444514f5e3Sopenharmony_ci            profiler->GetStack(it);
4454514f5e3Sopenharmony_ci        }
4464514f5e3Sopenharmony_ci    } else if (thread->IsAsmInterpreter()) {
4474514f5e3Sopenharmony_ci        if (thread->GetLastLeaveFrame() != nullptr) {
4484514f5e3Sopenharmony_ci            JSTaggedType *leaveFrame = const_cast<JSTaggedType *>(thread->GetLastLeaveFrame());
4494514f5e3Sopenharmony_ci            if (JsStackGetter::CheckFrameType(thread, leaveFrame)) {
4504514f5e3Sopenharmony_ci                FrameIterator it(leaveFrame, thread);
4514514f5e3Sopenharmony_ci                profiler->GetStack(it);
4524514f5e3Sopenharmony_ci            }
4534514f5e3Sopenharmony_ci        }
4544514f5e3Sopenharmony_ci    } else {
4554514f5e3Sopenharmony_ci        if (thread->GetCurrentFrame() != nullptr) {
4564514f5e3Sopenharmony_ci            if (JsStackGetter::CheckFrameType(thread, const_cast<JSTaggedType *>(thread->GetCurrentFrame()))) {
4574514f5e3Sopenharmony_ci                FrameHandler frameHandler(thread);
4584514f5e3Sopenharmony_ci                FrameIterator it(frameHandler.GetSp(), thread);
4594514f5e3Sopenharmony_ci                profiler->GetStack(it);
4604514f5e3Sopenharmony_ci            }
4614514f5e3Sopenharmony_ci        }
4624514f5e3Sopenharmony_ci    }
4634514f5e3Sopenharmony_ci    if (profiler->generator_->SemPost(0) != 0) {
4644514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "sem_[0] post failed";
4654514f5e3Sopenharmony_ci        return;
4664514f5e3Sopenharmony_ci    }
4674514f5e3Sopenharmony_ci}
4684514f5e3Sopenharmony_ci
4694514f5e3Sopenharmony_cibool CpuProfiler::InHeaderOrTail(uint64_t pc, uint64_t entryBegin, uint64_t entryDuration, uint64_t headerSize,
4704514f5e3Sopenharmony_ci                                 uint64_t tailSize) const
4714514f5e3Sopenharmony_ci{
4724514f5e3Sopenharmony_ci    uintptr_t entryEnd = entryBegin + entryDuration;
4734514f5e3Sopenharmony_ci    if (pc >= entryBegin && pc <= (entryBegin + headerSize)) {
4744514f5e3Sopenharmony_ci        return true;
4754514f5e3Sopenharmony_ci    }
4764514f5e3Sopenharmony_ci    if (pc <= entryEnd && pc >= (entryEnd - tailSize)) {
4774514f5e3Sopenharmony_ci        return true;
4784514f5e3Sopenharmony_ci    }
4794514f5e3Sopenharmony_ci    return false;
4804514f5e3Sopenharmony_ci}
4814514f5e3Sopenharmony_ci
4824514f5e3Sopenharmony_cibool CpuProfiler::IsEntryFrameHeaderOrTail(JSThread *thread, uint64_t pc) const
4834514f5e3Sopenharmony_ci{
4844514f5e3Sopenharmony_ci    uint64_t headerSize = 0;
4854514f5e3Sopenharmony_ci    uint64_t tailSize = 0;
4864514f5e3Sopenharmony_ci    uint64_t entryDuration = 0;
4874514f5e3Sopenharmony_ci    Assembler::GetFrameCompletionPos(headerSize, tailSize, entryDuration);
4884514f5e3Sopenharmony_ci    uintptr_t entryBegin = thread->GetRTInterface(kungfu::RuntimeStubCSigns::ID_AsmInterpreterEntry);
4894514f5e3Sopenharmony_ci    bool inAsmInterpreterEntry = InHeaderOrTail(pc, entryBegin, entryDuration, headerSize, tailSize);
4904514f5e3Sopenharmony_ci    entryBegin = thread->GetRTInterface(kungfu::RuntimeStubCSigns::ID_GeneratorReEnterAsmInterp);
4914514f5e3Sopenharmony_ci    bool inGeneratorReEnterAsmInterp = InHeaderOrTail(pc, entryBegin, entryDuration, headerSize, tailSize);
4924514f5e3Sopenharmony_ci    return (inAsmInterpreterEntry || inGeneratorReEnterAsmInterp);
4934514f5e3Sopenharmony_ci}
4944514f5e3Sopenharmony_ci
4954514f5e3Sopenharmony_ciuint64_t CpuProfiler::GetPcFromContext(void *context)
4964514f5e3Sopenharmony_ci{
4974514f5e3Sopenharmony_ci    [[maybe_unused]] ucontext_t *ucontext = reinterpret_cast<ucontext_t*>(context);
4984514f5e3Sopenharmony_ci    [[maybe_unused]] mcontext_t &mcontext = ucontext->uc_mcontext;
4994514f5e3Sopenharmony_ci    uint64_t pc = 0;
5004514f5e3Sopenharmony_ci#if defined(PANDA_TARGET_AMD64)
5014514f5e3Sopenharmony_ci    pc = static_cast<uint64_t>(mcontext.gregs[REG_RIP]);
5024514f5e3Sopenharmony_ci#elif defined(PANDA_TARGET_ARM64)
5034514f5e3Sopenharmony_ci    pc = static_cast<uint64_t>(mcontext.pc);
5044514f5e3Sopenharmony_ci#else
5054514f5e3Sopenharmony_ci    LOG_FULL(FATAL) << "AsmInterpreter does not currently support other platforms, please run on x64 and arm64";
5064514f5e3Sopenharmony_ci    pc = 0;
5074514f5e3Sopenharmony_ci#endif
5084514f5e3Sopenharmony_ci    return pc;
5094514f5e3Sopenharmony_ci}
5104514f5e3Sopenharmony_ci
5114514f5e3Sopenharmony_cibool CpuProfiler::IsAddrAtStubOrAot(uint64_t pc) const
5124514f5e3Sopenharmony_ci{
5134514f5e3Sopenharmony_ci    AOTFileManager *loader = vm_->GetAOTFileManager();
5144514f5e3Sopenharmony_ci    return loader->InsideStub(pc) || loader->InsideAOT(pc);
5154514f5e3Sopenharmony_ci}
5164514f5e3Sopenharmony_ci
5174514f5e3Sopenharmony_cibool CpuProfiler::CheckFileName(const std::string &fileName, std::string &absoluteFilePath) const
5184514f5e3Sopenharmony_ci{
5194514f5e3Sopenharmony_ci    if (fileName.empty()) {
5204514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::CheckFileName, fileName is empty";
5214514f5e3Sopenharmony_ci        return false;
5224514f5e3Sopenharmony_ci    }
5234514f5e3Sopenharmony_ci
5244514f5e3Sopenharmony_ci    if (fileName.size() > PATH_MAX) {
5254514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::CheckFileName, fileName exceed PATH_MAX";
5264514f5e3Sopenharmony_ci        return false;
5274514f5e3Sopenharmony_ci    }
5284514f5e3Sopenharmony_ci
5294514f5e3Sopenharmony_ci    CVector<char> resolvedPath(PATH_MAX);
5304514f5e3Sopenharmony_ci    auto result = realpath(fileName.c_str(), resolvedPath.data());
5314514f5e3Sopenharmony_ci    if (result == nullptr) {
5324514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::CheckFileName, realpath fail, errno = " << errno;
5334514f5e3Sopenharmony_ci        return false;
5344514f5e3Sopenharmony_ci    }
5354514f5e3Sopenharmony_ci    std::ofstream file(resolvedPath.data());
5364514f5e3Sopenharmony_ci    if (!file.good()) {
5374514f5e3Sopenharmony_ci        LOG_ECMA(ERROR) << "CpuProfiler::CheckFileName, file is not good, errno = " << errno;
5384514f5e3Sopenharmony_ci        return false;
5394514f5e3Sopenharmony_ci    }
5404514f5e3Sopenharmony_ci    file.close();
5414514f5e3Sopenharmony_ci    absoluteFilePath = resolvedPath.data();
5424514f5e3Sopenharmony_ci    return true;
5434514f5e3Sopenharmony_ci}
5444514f5e3Sopenharmony_ci
5454514f5e3Sopenharmony_civoid CpuProfiler::SetBuildNapiStack(bool flag)
5464514f5e3Sopenharmony_ci{
5474514f5e3Sopenharmony_ci    isBuildNapiStack_.store(flag);
5484514f5e3Sopenharmony_ci}
5494514f5e3Sopenharmony_ci
5504514f5e3Sopenharmony_cibool CpuProfiler::GetBuildNapiStack()
5514514f5e3Sopenharmony_ci{
5524514f5e3Sopenharmony_ci    return isBuildNapiStack_.load();
5534514f5e3Sopenharmony_ci}
5544514f5e3Sopenharmony_ci
5554514f5e3Sopenharmony_cibool CpuProfiler::GetOutToFile()
5564514f5e3Sopenharmony_ci{
5574514f5e3Sopenharmony_ci    return outToFile_;
5584514f5e3Sopenharmony_ci}
5594514f5e3Sopenharmony_ci
5604514f5e3Sopenharmony_ciEcmaVM* CpuProfiler::GetVmbyTid(pthread_t tid)
5614514f5e3Sopenharmony_ci{
5624514f5e3Sopenharmony_ci    LockHolder lock(synchronizationMutex_);
5634514f5e3Sopenharmony_ci    return const_cast<EcmaVM *>(profilerMap_[tid].vm_);
5644514f5e3Sopenharmony_ci}
5654514f5e3Sopenharmony_ci} // namespace panda::ecmascript
566