* Copyright (c) 2021-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/napi/include/dfx_jsnapi.h"
#include "ecmascript/base/block_hook_scope.h"
#include "ecmascript/base/config.h"
#include "ecmascript/base/json_helper.h"
#include "ecmascript/builtins/builtins_ark_tools.h"
#include "ecmascript/checkpoint/thread_state_transition.h"
#include "common_components/mutator/mutator_manager.h"
#include "ecmascript/debugger/debugger_api.h"
#include "ecmascript/debugger/js_debugger_manager.h"
#include "ecmascript/dfx/hprof/heap_profiler.h"
#if defined(PANDA_JS_ETS_HYBRID_MODE)
#include "ecmascript/dfx/hprof/hybrid/hybrid_heap_profiler.h"
#endif
#include "ecmascript/dfx/stackinfo/async_stack_trace.h"
#include "ecmascript/dfx/stackinfo/js_stackinfo.h"
#include "ecmascript/dfx/tracing/tracing.h"
#include "ecmascript/dfx/vm_thread_control.h"
#include "ecmascript/jit/jit.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/jspandafile/js_pandafile_executor.h"
#include "ecmascript/mem/heap-inl.h"
#include "ecmascript/ohos/ohos_constants.h"
#include "ecmascript/platform/backtrace.h"
#include "jsnapi_expo.h"
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
#include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
#include "ecmascript/dfx/cpu_profiler/samples_record.h"
#endif
#if defined(ENABLE_DUMP_IN_FAULTLOG)
#include "faultloggerd_client.h"
#include "uv.h"
#endif
namespace panda {
using ecmascript::CString;
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
using BuiltinsArkTools = ecmascript::builtins::BuiltinsArkTools;
using ecmascript::CpuProfiler;
#endif
using ecmascript::EcmaString;
using ecmascript::JSTaggedValue;
using ecmascript::GCStatisticData;
using ecmascript::GCStats;
template<typename T>
using JSHandle = ecmascript::JSHandle<T>;
using ecmascript::FileStream;
using ecmascript::FileDescriptorStream;
using ecmascript::CMap;
using ecmascript::Tracing;
using ecmascript::DumpSnapShotOption;
using ecmascript::g_isEnableCMCGC;
using ecmascript::JSFunction;
using ecmascript::JSPandaFileExecutor;
using ecmascript::SourceTextModule;
using Runtime = ecmascript::Runtime;
using LanguageEnv = ecmascript::LanguageEnv;
#if defined(PANDA_JS_ETS_HYBRID_MODE)
using HybridHeapProfiler = ecmascript::HybridHeapProfiler;
#endif
sem_t g_heapdumpCnt;
void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] const std::string &path,
[[maybe_unused]] const DumpSnapShotOption &dumpOption,
const std::function<void(uint8_t)> &callback)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
FileStream stream(path);
if (dumpOption.languageEnv == LanguageEnv::DYNAMIC) {
DumpHeapSnapshot(vm, &stream, dumpOption, nullptr, callback);
} else {
PerformHybridHeapDump(vm, &stream, dumpOption);
}
#else
if (callback) {
callback(static_cast<uint8_t>(ecmascript::DumpHeapSnapshotStatus::FORK_FAILED));
}
LOG_ECMA(ERROR) << "Not support arkcompiler heap snapshot";
#endif
}
void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] Stream *stream,
[[maybe_unused]] const DumpSnapShotOption &dumpOption,
[[maybe_unused]] Progress *progress,
[[maybe_unused]] std::function<void(uint8_t)> callback)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
const_cast<EcmaVM *>(vm));
#if defined (ENABLE_LOCAL_HANDLE_LEAK_DETECT)
#if defined (ENABLE_DUMP_IN_FAULTLOG)
auto heapProfiler = reinterpret_cast<ecmascript::HeapProfiler *>(heapProfile);
heapProfiler->SwitchStartLocalHandleLeakDetect();
if (heapProfiler->IsStartLocalHandleLeakDetect()) {
int32_t fd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::JS_STACKTRACE));
if (fd < 0) {
LOG_ECMA(ERROR) << "[LocalHandleLeakDetect] Failed on request file descriptor";
} else {
heapProfiler->SetLeakStackTraceFd(fd);
}
}
#endif
#endif
ecmascript::NodeIdCacheClearScope guard(const_cast<EcmaVM *>(vm), dumpOption);
heapProfile->DumpHeapSnapshot(stream, dumpOption, progress, callback);
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap snapshot";
#endif
}
[[maybe_unused]] static uint8_t killCount = 0;
void DFXJSNApi::DumpCpuProfile([[maybe_unused]] const EcmaVM *vm)
{
#if ECMASCRIPT_ENABLE_MEGA_PROFILER
vm->GetJSThread()->PrintMegaICStat();
#endif
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
#if defined(ENABLE_DUMP_IN_FAULTLOG)
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
if (DFXJSNApi::StopCpuProfilerForColdStart(vm)) {
return;
}
(void)killCount;
if (DFXJSNApi::CpuProfilerSamplingAnyTime(vm)) {
killCount++;
return;
}
#endif
#endif
#endif
}
void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm,
[[maybe_unused]] const DumpSnapShotOption &dumpOption)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
#if defined(ENABLE_DUMP_IN_FAULTLOG)
sem_wait(&g_heapdumpCnt);
auto &options = const_cast<EcmaVM *>(vm)->GetJSOptions();
options.SwitchStartGlobalLeakCheck();
if (options.EnableGlobalLeakCheck() && options.IsStartGlobalLeakCheck()) {
int32_t stackTraceFd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::JS_STACKTRACE));
if (stackTraceFd < 0) {
options.SwitchStartGlobalLeakCheck();
} else {
vm->GetJSThread()->SetStackTraceFd(stackTraceFd);
}
}
int32_t fd;
if (dumpOption.isDumpOOM && dumpOption.dumpFormat == DumpFormat::BINARY) {
fd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::JS_RAW_SNAPSHOT));
} else {
fd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::JS_HEAP_SNAPSHOT));
}
if (fd < 0) {
LOG_ECMA(ERROR) << "Write FD failed, fd" << fd;
return;
}
FileDescriptorStream stream(fd);
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
const_cast<EcmaVM *>(vm));
ecmascript::NodeIdCacheClearScope guard(const_cast<EcmaVM *>(vm), dumpOption);
heapProfile->DumpHeapSnapshot(&stream, dumpOption);
sem_post(&g_heapdumpCnt);
#endif
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap snapshot";
#endif
}
void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm,
[[maybe_unused]] const DumpSnapShotOption &dumpOption, [[maybe_unused]] uint32_t tid)
{
const int THREAD_COUNT = 1;
if (vm->IsWorkerThread()) {
LOG_ECMA(ERROR) << "this is a workthread!";
return;
}
sem_init(&g_heapdumpCnt, 0, THREAD_COUNT);
uint32_t curTid = vm->GetTid();
LOG_ECMA(INFO) << "DumpHeapSnapshot tid " << tid << " curTid " << curTid;
if (dumpOption.languageEnv == LanguageEnv::DYNAMIC) {
DumpHeapSnapshotWithVm(vm, dumpOption, tid);
} else {
ScheduleHybridHeapDump(vm, dumpOption, tid);
}
}
void DFXJSNApi::DumpHeapSnapshotWithVm([[maybe_unused]] const EcmaVM *vm,
[[maybe_unused]] const DumpSnapShotOption &dumpOption,
[[maybe_unused]] uint32_t tid)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
#if defined(ENABLE_DUMP_IN_FAULTLOG)
uv_loop_t *loop = reinterpret_cast<uv_loop_t *>(vm->GetLoop());
if (loop == nullptr || uv_loop_alive(loop) == 0) {
LOG_ECMA(ERROR) << "loop nullptr or uv_loop_alive dead";
return;
}
struct DumpForSnapShotStruct *dumpStruct = new DumpForSnapShotStruct();
dumpStruct->vm = vm;
dumpStruct->dumpOption = dumpOption;
uv_work_t *work = new(std::nothrow) uv_work_t;
if (work == nullptr) {
LOG_ECMA(ERROR) << "work nullptr";
delete dumpStruct;
return;
}
work->data = static_cast<void *>(dumpStruct);
uint32_t curTid = vm->GetTid();
int ret = 0;
if (tid == 0 || tid == curTid) {
ret = uv_queue_work(loop, work, [](uv_work_t *) {}, [](uv_work_t *work, int32_t) {
struct DumpForSnapShotStruct *dump = static_cast<struct DumpForSnapShotStruct *>(work->data);
DFXJSNApi::GetHeapPrepare(dump->vm);
DumpSnapShotOption dumpOption = dump->dumpOption;
DumpHeapSnapshot(dump->vm, dumpOption);
delete dump;
delete work;
});
} else {
delete dumpStruct;
delete work;
}
const_cast<EcmaVM *>(vm)->EnumerateWorkerVm([&](const EcmaVM *workerVm) -> void {
uint32_t curTid = workerVm->GetTid();
LOG_ECMA(INFO) << "DumpHeapSnapshot workthread curTid " << curTid;
DumpHeapSnapshotWithVm(workerVm, dumpOption, tid);
return;
});
if (ret != 0) {
LOG_ECMA(ERROR) << "uv_queue_work fail ret " << ret;
delete dumpStruct;
delete work;
}
#endif
#endif
}
bool DFXJSNApi::PerformHybridHeapDump([[maybe_unused]] const EcmaVM *vm,
[[maybe_unused]] const DumpSnapShotOption &dumpOption)
{
#if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) && defined(PANDA_JS_ETS_HYBRID_MODE)
auto *hybridProfiler = HybridHeapProfiler::GetInstance();
if (hybridProfiler == nullptr) {
LOG_ECMA(ERROR) << "PerformHybridHeapDump: HybridHeapProfiler not available";
return false;
}
return hybridProfiler->Dump(const_cast<EcmaVM *>(vm), nullptr,
const_cast<DumpSnapShotOption &>(dumpOption));
#endif
return false;
}
bool DFXJSNApi::PerformHybridHeapDump([[maybe_unused]] const EcmaVM *vm,
[[maybe_unused]] Stream *stream,
[[maybe_unused]] const DumpSnapShotOption &dumpOption)
{
#if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) && defined(PANDA_JS_ETS_HYBRID_MODE)
auto *hybridProfiler = HybridHeapProfiler::GetInstance();
if (hybridProfiler == nullptr) {
LOG_ECMA(ERROR) << "PerformHybridHeapDump: HybridHeapProfiler not available";
return false;
}
return hybridProfiler->Dump(const_cast<EcmaVM *>(vm), stream,
const_cast<DumpSnapShotOption &>(dumpOption));
#endif
return false;
}
void DFXJSNApi::ScheduleHybridHeapDump([[maybe_unused]] const EcmaVM *vm,
[[maybe_unused]] const DumpSnapShotOption &dumpOption,
[[maybe_unused]] uint32_t tid)
{
#if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) && defined(PANDA_JS_ETS_HYBRID_MODE)
uint32_t mainTid = vm->GetTid();
if (tid == 0) {
ScheduleHybridDumpOnLoop(vm, dumpOption, mainTid);
DumpSnapShotOption dynamicOption = dumpOption;
const_cast<EcmaVM *>(vm)->EnumerateWorkerVm([&](const EcmaVM *workerVm) {
DumpHeapSnapshotWithVm(workerVm, dynamicOption, workerVm->GetTid());
});
} else if (tid == mainTid) {
ScheduleHybridDumpOnLoop(vm, dumpOption, mainTid);
} else {
EcmaVM *workerVm = const_cast<EcmaVM *>(vm)->GetWorkerVm(tid);
if (workerVm != nullptr) {
ScheduleHybridDumpOnLoop(workerVm, dumpOption, tid);
} else {
ScheduleHybridDumpOnLoop(vm, dumpOption, tid);
}
}
#endif
}
void DFXJSNApi::ScheduleHybridDumpOnLoop([[maybe_unused]] const EcmaVM *vm,
[[maybe_unused]] const DumpSnapShotOption &dumpOption,
[[maybe_unused]] uint32_t tid)
{
#if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) && defined(PANDA_JS_ETS_HYBRID_MODE) && defined(ENABLE_DUMP_IN_FAULTLOG)
uv_loop_t *loop = reinterpret_cast<uv_loop_t *>(vm->GetLoop());
if (loop == nullptr || uv_loop_alive(loop) == 0) {
LOG_ECMA(ERROR) << "[HybridHeapDump] DFXJSNApi::ScheduleHybridDumpOnLoop: loop nullptr or dead";
return;
}
struct DumpForSnapShotStruct *dumpStruct = new DumpForSnapShotStruct();
dumpStruct->vm = vm;
dumpStruct->dumpOption = dumpOption;
dumpStruct->tid = tid;
uv_work_t *work = new(std::nothrow) uv_work_t;
if (work == nullptr) {
LOG_ECMA(ERROR) << "[HybridHeapDump] DFXJSNApi::ScheduleHybridDumpOnLoop: work alloc failed";
delete dumpStruct;
return;
}
work->data = static_cast<void *>(dumpStruct);
int ret = uv_queue_work(loop, work, [](uv_work_t *) {},
[](uv_work_t *work, int32_t) {
struct DumpForSnapShotStruct *dump = static_cast<struct DumpForSnapShotStruct *>(work->data);
const EcmaVM *targetVm = (dump->vm->GetTid() == dump->tid) ? dump->vm : nullptr;
PerformHybridHeapDump(targetVm, dump->dumpOption);
delete dump;
delete work;
});
if (ret != 0) {
LOG_ECMA(ERROR) << "[HybridHeapDump] DFXJSNApi::ScheduleHybridDumpOnLoop: uv_queue_work fail, ret " << ret;
delete dumpStruct;
delete work;
}
#endif
}
void DFXJSNApi::TriggerGC([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] uint32_t tid)
{
if (vm->IsWorkerThread()) {
LOG_ECMA(ERROR) << "this is a workthread!";
return;
}
uint32_t curTid = vm->GetTid();
LOG_ECMA(INFO) << "TriggerGC tid " << tid << " curTid " << curTid;
if (tid == 0 || tid == curTid) {
TriggerGCWithVm(vm);
}
const_cast<EcmaVM *>(vm)->EnumerateWorkerVm([&](const EcmaVM *workerVm) -> void {
curTid = workerVm->GetTid();
LOG_ECMA(INFO) << "TriggerGC tid " << tid << " curTid " << curTid;
if (tid == 0 || tid == curTid) {
TriggerGCWithVm(workerVm);
return;
}
});
TriggerSharedGCWithVm(vm);
}
void DFXJSNApi::TriggerSharedGCWithVm([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
#if defined(ENABLE_DUMP_IN_FAULTLOG)
uv_loop_t *loop = reinterpret_cast<uv_loop_t *>(vm->GetLoop());
if (loop == nullptr) {
LOG_ECMA(ERROR) << "loop nullptr";
return;
}
if (uv_loop_alive(loop) == 0) {
LOG_ECMA(ERROR) << "uv_loop_alive dead";
return;
}
uv_work_t *work = new(std::nothrow) uv_work_t;
if (work == nullptr) {
LOG_ECMA(FATAL) << "DFXJSNApi::TriggerGCWithVm:work is nullptr";
return;
}
work->data = static_cast<void *>(const_cast<EcmaVM *>(vm));
int ret = uv_queue_work(loop, work, [](uv_work_t *) {}, [](uv_work_t *work, int32_t) {
EcmaVM *vm = static_cast<EcmaVM *>(work->data);
ecmascript::SharedHeap* sHeap = ecmascript::SharedHeap::GetInstance();
JSThread *thread = vm->GetJSThread();
ecmascript::ThreadManagedScope managedScope(thread);
sHeap->CollectGarbage<ecmascript::TriggerGCType::SHARED_FULL_GC,
ecmascript::GCReason::TRIGGER_BY_MEM_TOOLS>(thread);
delete work;
});
if (ret != 0) {
LOG_ECMA(ERROR) << "uv_queue_work fail ret " << ret;
delete work;
}
#endif
#endif
}
void DFXJSNApi::TriggerGCWithVm([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
#if defined(ENABLE_DUMP_IN_FAULTLOG)
uv_loop_t *loop = reinterpret_cast<uv_loop_t *>(vm->GetLoop());
if (loop == nullptr) {
LOG_ECMA(ERROR) << "loop nullptr";
return;
}
if (uv_loop_alive(loop) == 0) {
LOG_ECMA(ERROR) << "uv_loop_alive dead";
return;
}
uv_work_t *work = new(std::nothrow) uv_work_t;
if (work == nullptr) {
LOG_ECMA(FATAL) << "DFXJSNApi::TriggerGCWithVm:work is nullptr";
return;
}
work->data = static_cast<void *>(const_cast<EcmaVM *>(vm));
int ret = uv_queue_work(loop, work, [](uv_work_t *) {}, [](uv_work_t *work, int32_t) {
EcmaVM *vm = static_cast<EcmaVM *>(work->data);
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
vm->CollectGarbage(ecmascript::TriggerGCType::FULL_GC, ecmascript::GCReason::TRIGGER_BY_MEM_TOOLS);
delete work;
});
if (ret != 0) {
LOG_ECMA(ERROR) << "uv_queue_work fail ret " << ret;
delete work;
}
#endif
#endif
}
void DFXJSNApi::TriggerLocalCCWithVmForTest([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
#if defined(ENABLE_DUMP_IN_FAULTLOG)
uv_loop_t *loop = reinterpret_cast<uv_loop_t *>(vm->GetLoop());
if (loop == nullptr) {
LOG_ECMA(ERROR) << "loop nullptr";
return;
}
if (uv_loop_alive(loop) == 0) {
LOG_ECMA(ERROR) << "uv_loop_alive dead";
return;
}
uv_work_t *work = new(std::nothrow) uv_work_t;
if (work == nullptr) {
LOG_ECMA(FATAL) << "DFXJSNApi::TriggerLocalCCWithVm:work is nullptr";
return;
}
work->data = static_cast<void *>(const_cast<EcmaVM *>(vm));
int ret = uv_queue_work(loop, work, [](uv_work_t *) {}, [](uv_work_t *work, int32_t) {
const EcmaVM *vm = static_cast<const EcmaVM *>(work->data);
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
const_cast<ecmascript::Heap*>(vm->GetHeap())->TryTriggerCCMarking(ecmascript::MarkReason::OTHER);
delete work;
});
if (ret != 0) {
LOG_ECMA(ERROR) << "uv_queue_work fail ret " << ret;
delete work;
}
#endif
#endif
}
void DFXJSNApi::DestroyHeapProfiler([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
ecmascript::HeapProfilerInterface::Destroy(const_cast<EcmaVM *>(vm));
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap snapshot";
#endif
}
bool DFXJSNApi::BuildNativeAndJsStackTrace(const EcmaVM *vm, std::string &stackTraceStr)
{
stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace(vm->GetAssociatedJSThread(), true);
if (stackTraceStr.empty()) {
return false;
}
return true;
}
bool DFXJSNApi::BuildJsStackTrace(const EcmaVM *vm, std::string &stackTraceStr)
{
JSThread *thread = vm->GetAssociatedJSThread();
ecmascript::ThreadManagedScope managedScope(thread);
bool needUnbindMutator = false;
if (g_isEnableCMCGC) {
common::Mutator *mutator = reinterpret_cast<common::Mutator *>(thread->GetThreadHolder()->GetMutator());
needUnbindMutator = common::BaseRuntime::GetInstance()->GetMutatorManager().BindMutatorOnly(mutator);
}
stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace(thread, false);
if (needUnbindMutator) {
common::BaseRuntime::GetInstance()->GetMutatorManager().UnbindMutatorOnly();
}
if (stackTraceStr.empty()) {
return false;
}
return true;
}
bool DFXJSNApi::StartHeapTracking([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] double timeInterval,
[[maybe_unused]] bool isVmMode, [[maybe_unused]] Stream *stream,
[[maybe_unused]] bool traceAllocation, [[maybe_unused]] bool newThread)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
ecmascript::base::BlockHookScope blockScope;
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
const_cast<EcmaVM *>(vm));
return heapProfile->StartHeapTracking(timeInterval, isVmMode, stream, traceAllocation, newThread);
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap tracking";
return false;
#endif
}
bool DFXJSNApi::UpdateHeapTracking([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] Stream *stream)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
ecmascript::base::BlockHookScope blockScope;
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
const_cast<EcmaVM *>(vm));
return heapProfile->UpdateHeapTracking(stream);
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap tracking";
return false;
#endif
}
bool DFXJSNApi::StopHeapTracking([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] const std::string &filePath,
[[maybe_unused]] bool newThread)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
FileStream stream(filePath);
return StopHeapTracking(vm, &stream, nullptr, newThread);
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap tracking";
return false;
#endif
}
bool DFXJSNApi::StopHeapTracking([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] Stream* stream,
[[maybe_unused]] Progress *progress, [[maybe_unused]] bool newThread)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
ecmascript::base::BlockHookScope blockScope;
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
bool result = false;
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
const_cast<EcmaVM *>(vm));
result = heapProfile->StopHeapTracking(stream, progress, newThread);
ecmascript::HeapProfilerInterface::Destroy(const_cast<EcmaVM *>(vm));
return result;
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap tracking";
return false;
#endif
}
void DFXJSNApi::PrintStatisticResult(const EcmaVM *vm)
{
ecmascript::GCStats gcstats(vm->GetHeap());
gcstats.PrintStatisticResult();
}
void DFXJSNApi::StartRuntimeStat(EcmaVM *vm)
{
vm->GetJSThread()->GetEcmaVM()->SetRuntimeStatEnable(true);
}
void DFXJSNApi::StopRuntimeStat(EcmaVM *vm)
{
vm->GetJSThread()->GetEcmaVM()->SetRuntimeStatEnable(false);
}
size_t GetArrayBufferSizeForCMCGC()
{
size_t result = 0;
auto visitor = [&result](common::BaseObject *obj) {
panda::ecmascript::JSTaggedValue entry = panda::ecmascript::JSTaggedValue(
reinterpret_cast<panda::ecmascript::JSTaggedType>(obj));
panda::ecmascript::TaggedObject *taggedObj = entry.GetTaggedObject();
panda::ecmascript::JSHClass* jsClass = taggedObj->GetClass();
result += jsClass->IsArrayBuffer() ? jsClass->GetObjectSize() : 0;
};
common::Heap::GetHeap().ForEachObject(visitor, true);
return result;
}
size_t DFXJSNApi::GetArrayBufferSize(const EcmaVM *vm)
{
if (g_isEnableCMCGC) {
return GetArrayBufferSizeForCMCGC();
}
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
return vm->GetHeap()->GetArrayBufferSize();
}
size_t DFXJSNApi::GetHeapTotalSize(const EcmaVM *vm)
{
if (g_isEnableCMCGC) {
return common::Heap::GetHeap().GetCurrentCapacity();
}
return vm->GetHeap()->GetCommittedSize();
}
size_t DFXJSNApi::GetHeapUsedSize(const EcmaVM *vm)
{
if (g_isEnableCMCGC) {
return common::Heap::GetHeap().GetSurvivedSize();
}
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
return vm->GetHeap()->GetLiveObjectSize();
}
size_t DFXJSNApi::GetHeapObjectSize(const EcmaVM *vm)
{
if (g_isEnableCMCGC) {
return common::Heap::GetHeap().GetAllocatedSize();
}
return vm->GetHeap()->GetHeapObjectSize();
}
size_t DFXJSNApi::GetSharedHeapSize()
{
if (g_isEnableCMCGC) {
return 0;
}
return ecmascript::SharedHeap::GetInstance()->GetHeapObjectSize();
}
size_t DFXJSNApi::GetHeapLimitSize(const EcmaVM *vm)
{
if (g_isEnableCMCGC) {
return common::Heap::GetHeap().GetMaxCapacity();
}
return vm->GetHeap()->GetHeapLimitSize();
}
size_t DFXJSNApi::GetProcessHeapLimitSize()
{
if (g_isEnableCMCGC) {
return common::Heap::GetHeap().GetMaxCapacity();
}
return ecmascript::MemMapAllocator::GetInstance()->GetCapacity();
}
size_t DFXJSNApi::GetGCCount(const EcmaVM *vm)
{
if (vm->IsWorkerThread()) {
return vm->GetEcmaGCStats()->GetGCCount();
}
return vm->GetEcmaGCStats()->GetGCCount() +
ecmascript::SharedHeap::GetInstance()->GetEcmaGCStats()->GetGCCount();
}
size_t DFXJSNApi::GetGCDuration(const EcmaVM *vm)
{
if (vm->IsWorkerThread()) {
return vm->GetEcmaGCStats()->GetGCDuration();
}
return vm->GetEcmaGCStats()->GetGCDuration() +
ecmascript::SharedHeap::GetInstance()->GetEcmaGCStats()->GetGCDuration();
}
size_t DFXJSNApi::GetAccumulatedAllocateSize(const EcmaVM *vm)
{
if (g_isEnableCMCGC) {
return common::Heap::GetHeap().GetAccumulatedAllocateSize();
}
if (vm->IsWorkerThread()) {
return vm->GetEcmaGCStats()->GetAccumulatedAllocateSize();
}
return vm->GetEcmaGCStats()->GetAccumulatedAllocateSize() +
ecmascript::SharedHeap::GetInstance()->GetEcmaGCStats()->GetAccumulatedAllocateSize();
}
size_t DFXJSNApi::GetAccumulatedFreeSize(const EcmaVM *vm)
{
if (g_isEnableCMCGC) {
return common::Heap::GetHeap().GetAccumulatedFreeSize();
}
if (vm->IsWorkerThread()) {
return vm->GetEcmaGCStats()->GetAccumulatedFreeSize();
}
return vm->GetEcmaGCStats()->GetAccumulatedFreeSize() +
ecmascript::SharedHeap::GetInstance()->GetEcmaGCStats()->GetAccumulatedFreeSize();
}
size_t DFXJSNApi::GetFullGCLongTimeCount(const EcmaVM *vm)
{
return vm->GetEcmaGCStats()->GetFullGCLongTimeCount();
}
GCStatistic DFXJSNApi::GetGCStatistic(const EcmaVM *vm)
{
auto *thread = vm->GetAssociatedJSThread();
if (!thread->IsMainThreadFast()) {
LOG_ECMA(FATAL) << "GetGCStatistic only supports the main thread VM";
return {};
}
GCStatisticData gcStatisticData = vm->GetEcmaGCStats()->GetGCStatistic();
gcStatisticData = GCStats::MergeGCStatistic(gcStatisticData,
ecmascript::SharedHeap::GetInstance()->GetEcmaGCStats()->GetGCStatistic());
GCStatistic statistic;
statistic.count = gcStatisticData.count;
statistic.maxPause = gcStatisticData.maxPause;
statistic.minPause = gcStatisticData.minPause;
statistic.averagePause = gcStatisticData.count == 0 ? 0.0f :
(gcStatisticData.totalPause / gcStatisticData.count);
statistic.lastStartTime = gcStatisticData.lastStartTime;
statistic.lastEndTime = gcStatisticData.lastEndTime;
statistic.lastType = gcStatisticData.lastType;
LOG_ECMA(INFO) << "GC statistic, count: " << statistic.count
<< ", maxPause: " << statistic.maxPause
<< ", minPause: " << statistic.minPause
<< ", averagePause: " << statistic.averagePause
<< ", lastStartTime: " << statistic.lastStartTime
<< ", lastEndTime: " << statistic.lastEndTime
<< ", lastType: " << statistic.lastType;
return statistic;
}
void DFXJSNApi::GetHeapPrepare(const EcmaVM *vm)
{
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
const_cast<ecmascript::Heap *>(vm->GetHeap())->GetHeapPrepare(vm->GetJSThread());
}
void DFXJSNApi::SetJsDumpThresholds([[maybe_unused]] EcmaVM *vm, [[maybe_unused]] size_t thresholds)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT) && defined(PANDA_TARGET_OHOS) && defined(ENABLE_HISYSEVENT)
vm->GetHeap()->SetJsDumpThresholds(thresholds);
#else
LOG_ECMA(ERROR) << "Not support set jsdump thresholds";
#endif
}
void DFXJSNApi::SetAppFreezeFilterCallback([[maybe_unused]] const EcmaVM *vm, AppFreezeFilterCallback cb)
{
if (ecmascript::Runtime::GetInstance()->GetAppFreezeFilterCallback() == nullptr && cb != nullptr) {
ecmascript::Runtime::GetInstance()->SetAppFreezeFilterCallback(cb);
}
}
void DFXJSNApi::NotifyApplicationState(EcmaVM *vm, bool inBackground)
{
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
const_cast<ecmascript::Heap *>(vm->GetHeap())->ChangeGCParams(inBackground);
ecmascript::Jit::GetInstance()->ChangeTaskPoolState(inBackground);
ecmascript::Runtime::GetInstance()->SetInBackground(inBackground);
}
void DFXJSNApi::NotifyIdleStatusControl(const EcmaVM *vm, std::function<void(bool)> callback)
{
const_cast<ecmascript::Heap *>(vm->GetHeap())->InitializeIdleStatusControl(callback);
}
void DFXJSNApi::NotifyIdleTime(const EcmaVM *vm, int idleMicroSec)
{
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
}
void DFXJSNApi::NotifyMemoryPressure(EcmaVM *vm, bool inHighMemoryPressure)
{
const_cast<ecmascript::Heap *>(vm->GetHeap())->NotifyMemoryPressure(inHighMemoryPressure);
}
void DFXJSNApi::NotifyFinishColdStart(EcmaVM *vm, [[maybe_unused]] bool isConvinced)
{
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
}
void DFXJSNApi::NotifyHighSensitive(EcmaVM *vm, bool isStart)
{
const_cast<ecmascript::Heap *>(vm->GetHeap())->NotifyHighSensitive(isStart);
}
void DFXJSNApi::NotifyWarmStart(EcmaVM *vm)
{
LOG_ECMA(INFO) << "SmartGC: app warm start gc";
ecmascript::ThreadManagedScope managedScope(vm->GetJSThread());
const_cast<ecmascript::Heap *>(vm->GetHeap())->NotifyWarmStartup();
}
bool DFXJSNApi::StopCpuProfilerForColdStart([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
bool success = false;
auto &options = const_cast<EcmaVM *>(vm)->GetJSOptions();
if (options.EnableCpuProfilerColdStartMainThread()) {
success = true;
DFXJSNApi::StopCpuProfilerForFile(vm);
}
if (options.EnableCpuProfilerColdStartWorkerThread()) {
success = true;
const_cast<EcmaVM *>(vm)->EnumerateWorkerVm([&](const EcmaVM *workerVm) -> void {
if (workerVm->GetJSThread()->GetIsProfiling()) {
DFXJSNApi::StopCpuProfilerForFile(workerVm);
}
});
}
return success;
#else
LOG_ECMA(ERROR) << "Not support arkcompiler cpu profiler";
return false;
#endif
}
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
void DFXJSNApi::CpuProfilerAnyTimeMainThread(const EcmaVM *vm)
{
const uint8_t KILL_COUNT_FACTOR = 2;
if (killCount % KILL_COUNT_FACTOR == 0) {
uint8_t fileCount = killCount / KILL_COUNT_FACTOR + 1;
LOG_ECMA(INFO) << "Start CpuProfiler Any Time Main Thread, killCount = " << killCount;
std::string fileName = ConvertToStdString(const_cast<EcmaVM *>(vm)->GetBundleName())
+ "_" + std::to_string(fileCount) + ".cpuprofile";
if (!BuiltinsArkTools::CreateFile(fileName)) {
LOG_ECMA(ERROR) << "createFile failed " << fileName;
} else {
DFXJSNApi::StartCpuProfilerForFile(vm, fileName, CpuProfiler::INTERVAL_OF_INNER_START);
}
} else {
LOG_ECMA(INFO) << "Stop CpuProfiler Any Time Main Thread, killCount = " << killCount;
if (vm->GetJSThread()->GetIsProfiling()) {
DFXJSNApi::StopCpuProfilerForFile(vm);
}
}
}
#endif
bool DFXJSNApi::CpuProfilerSamplingAnyTime([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
(void)killCount;
bool success = false;
const uint8_t KILL_COUNT_FACTOR = 2;
auto &options = const_cast<EcmaVM *>(vm)->GetJSOptions();
if (options.EnableCpuProfilerAnyTimeMainThread()) {
success = true;
CpuProfilerAnyTimeMainThread(vm);
}
if (options.EnableCpuProfilerAnyTimeWorkerThread()) {
success = true;
if (killCount % KILL_COUNT_FACTOR == 0) {
uint8_t fileCount = killCount / KILL_COUNT_FACTOR + 1;
LOG_ECMA(INFO) << "Start CpuProfiler Any Time Worker Thread, killCount = " << killCount;
const_cast<EcmaVM *>(vm)->EnumerateWorkerVm([&](const EcmaVM *workerVm) -> void {
auto *thread = workerVm->GetAssociatedJSThread();
std::string fileName = ConvertToStdString(workerVm->GetBundleName()) + "_"
+ std::to_string(thread->GetThreadId()) + "_"
+ std::to_string(fileCount) + ".cpuprofile";
if (!BuiltinsArkTools::CreateFile(fileName)) {
LOG_ECMA(ERROR) << "createFile failed " << fileName;
} else {
thread->SetCpuProfileName(fileName);
thread->SetNeedProfiling(true);
}
});
} else {
LOG_ECMA(INFO) << "Stop CpuProfiler Any Time Worker Thread, killCount = " << killCount;
const_cast<EcmaVM *>(vm)->EnumerateWorkerVm([&](const EcmaVM *workerVm) -> void {
auto *thread = workerVm->GetAssociatedJSThread();
if (thread->GetIsProfiling()) {
DFXJSNApi::StopCpuProfilerForFile(workerVm);
}
thread->SetNeedProfiling(false);
});
}
}
return success;
#else
LOG_ECMA(ERROR) << "Not support arkcompiler cpu profiler";
return false;
#endif
}
bool DFXJSNApi::StartCpuProfilerForFile([[maybe_unused]] const EcmaVM *vm,
[[maybe_unused]] const std::string &fileName,
[[maybe_unused]] int interval)
{
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
LOG_ECMA(INFO) << "DFXJSNApi::StartCpuProfilerForFile, vm = " << vm;
if (interval <= 0) {
LOG_ECMA(ERROR) << "DFXJSNApi::StartCpuProfilerForFile, interval <= 0";
return false;
}
if (vm == nullptr) {
LOG_ECMA(ERROR) << "DFXJSNApi::StartCpuProfilerForFile, vm == nullptr";
return false;
}
CpuProfiler *profiler = vm->GetProfiler();
if (profiler == nullptr) {
profiler = new CpuProfiler(vm, interval);
const_cast<EcmaVM *>(vm)->SetProfiler(profiler);
}
return profiler->StartCpuProfilerForFile(fileName);
#else
LOG_ECMA(ERROR) << "DFXJSNApi::StartCpuProfilerForFile, not support cpu profiler";
return false;
#endif
}
void DFXJSNApi::StopCpuProfilerForFile([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
LOG_ECMA(INFO) << "DFXJSNApi::StopCpuProfilerForFile, vm = " << vm;
if (vm == nullptr) {
LOG_ECMA(ERROR) << "DFXJSNApi::StopCpuProfilerForFile, vm == nullptr";
return;
}
CpuProfiler *profiler = vm->GetProfiler();
if (profiler == nullptr) {
LOG_ECMA(ERROR) << "DFXJSNApi::StopCpuProfilerForFile, profiler == nullptr";
return;
}
bool result = profiler->StopCpuProfilerForFile();
if (!result) {
LOG_ECMA(ERROR) << "DFXJSNApi::StopCpuProfilerForFile failed";
return;
}
delete profiler;
profiler = nullptr;
const_cast<EcmaVM *>(vm)->SetProfiler(nullptr);
#else
LOG_ECMA(ERROR) << "Not support arkcompiler cpu profiler";
#endif
}
bool DFXJSNApi::StartCpuProfilerForInfo([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int interval)
{
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
LOG_ECMA(INFO) << "DFXJSNApi::StartCpuProfilerForInfo, vm = " << vm;
if (interval <= 0) {
LOG_ECMA(ERROR) << "DFXJSNApi::StartCpuProfilerForInfo, interval <= 0";
return false;
}
if (vm == nullptr) {
LOG_ECMA(ERROR) << "DFXJSNApi::StartCpuProfilerForInfo, vm == nullptr";
return false;
}
CpuProfiler *profiler = vm->GetProfiler();
if (profiler == nullptr) {
profiler = new CpuProfiler(vm, interval);
const_cast<EcmaVM *>(vm)->SetProfiler(profiler);
}
return profiler->StartCpuProfilerForInfo();
#else
LOG_ECMA(ERROR) << "DFXJSNApi::StartCpuProfilerForInfo, not support cpu profiler";
return false;
#endif
}
std::unique_ptr<ProfileInfo> DFXJSNApi::StopCpuProfilerForInfo([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
LOG_ECMA(INFO) << "DFXJSNApi::StopCpuProfilerForInfo, vm = " << vm;
if (vm == nullptr) {
LOG_ECMA(ERROR) << "DFXJSNApi::StopCpuProfilerForInfo, vm == nullptr";
return nullptr;
}
CpuProfiler *profiler = vm->GetProfiler();
if (profiler == nullptr) {
LOG_ECMA(ERROR) << "DFXJSNApi::StopCpuProfilerForInfo, profiler == nullptr";
return nullptr;
}
std::unique_ptr<ProfileInfo> profileInfo;
bool result = profiler->StopCpuProfilerForInfo(profileInfo);
if (!result) {
LOG_ECMA(ERROR) << "DFXJSNApi::StopCpuProfilerForInfo failed";
return nullptr;
}
delete profiler;
profiler = nullptr;
const_cast<EcmaVM *>(vm)->SetProfiler(nullptr);
return profileInfo;
#else
LOG_ECMA(ERROR) << "Not support arkcompiler cpu profiler";
return nullptr;
#endif
}
void DFXJSNApi::SetCpuSamplingInterval([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int interval)
{
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
if (interval < 0) {
LOG_ECMA(ERROR) << "Sampling interval is illegal";
return;
}
LOG_ECMA(INFO) << "SetCpuProfilerSamplingInterval, Sampling interval is: " << interval;
if (vm == nullptr) {
return;
}
CpuProfiler *profiler = vm->GetProfiler();
if (profiler == nullptr) {
profiler = new CpuProfiler(vm, interval);
const_cast<EcmaVM *>(vm)->SetProfiler(profiler);
return;
}
profiler->SetCpuSamplingInterval(interval);
#else
LOG_ECMA(ERROR) << "Not support arkcompiler cpu profiler";
#endif
}
void DFXJSNApi::EnableSeriliazationTimeoutCheck(const EcmaVM *ecmaVM, int32_t threshold)
{
ecmaVM->GetJsDebuggerManager()->EnableSerializationTimeoutCheck();
ecmaVM->GetJsDebuggerManager()->SetSerializationCheckThreshold(threshold);
}
void DFXJSNApi::DisableSeriliazationTimeoutCheck(const EcmaVM *ecmaVM)
{
ecmaVM->GetJsDebuggerManager()->DisableSerializationTimeoutCheck();
}
bool DFXJSNApi::SuspendVM([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
ecmascript::VmThreadControl* vmThreadControl = vm->GetAssociatedJSThread()->GetVmThreadControl();
return vmThreadControl->NotifyVMThreadSuspension();
#else
LOG_ECMA(ERROR) << "Not support arkcompiler snapshot";
return false;
#endif
}
void DFXJSNApi::ResumeVM([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
ecmascript::VmThreadControl* vmThreadControl = vm->GetAssociatedJSThread()->GetVmThreadControl();
vmThreadControl->ResumeVM();
#else
LOG_ECMA(ERROR) << "Not support arkcompiler snapshot";
#endif
}
bool DFXJSNApi::IsSuspended([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
ecmascript::VmThreadControl* vmThreadControl = vm->GetAssociatedJSThread()->GetVmThreadControl();
return vmThreadControl->IsSuspended();
#else
LOG_ECMA(ERROR) << "Not support arkcompiler snapshot";
return false;
#endif
}
void DFXJSNApi::TerminateExecution(const EcmaVM *vm)
{
ecmascript::VmThreadControl* vmThreadControl = vm->GetAssociatedJSThread()->GetVmThreadControl();
vmThreadControl->RequestTerminateExecution();
}
bool DFXJSNApi::CheckSafepoint([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
ecmascript::JSThread* thread = vm->GetJSThread();
return thread->CheckSafepoint();
#else
LOG_ECMA(ERROR) << "Not support arkcompiler snapshot";
return false;
#endif
}
bool DFXJSNApi::BuildJsStackInfoList(const EcmaVM *hostVm, uint32_t tid, std::vector<JsFrameInfo>& jsFrames)
{
EcmaVM *vm;
if (hostVm->GetAssociatedJSThread()->GetThreadId() == tid) {
vm = const_cast<EcmaVM*>(hostVm);
} else {
vm = const_cast<EcmaVM*>(hostVm)->GetWorkerVm(tid);
if (vm == nullptr) {
return false;
}
}
jsFrames = ecmascript::JsStackInfo::BuildJsStackInfo(vm->GetAssociatedJSThread());
if (jsFrames.size() > 0) {
return true;
}
return false;
}
std::pair<std::string, std::uint32_t> DFXJSNApi::GetAnonymizeExtraErrorMessage(const EcmaVM *vm, uint32_t width)
{
JSThread *thread = vm->GetJSThread();
JSHandle<JSTaggedValue> string(thread, thread->GetExtraErrorMessage());
if (string->IsHole()) {
return {"", 0};
}
int32_t position = thread->GetJsonErrorPosition();
return ecmascript::base::JsonHelper::AnonymizeJsonString(thread, string, position, width);
}
void DFXJSNApi::ClearExtraErrorMessage(const EcmaVM *vm)
{
JSThread *thread = vm->GetJSThread();
thread->SetExtraErrorMessage(JSTaggedValue::Hole());
}
std::string DFXJSNApi::GetExtraJSCrashMessage(const EcmaVM *vm)
{
return vm->GetExtraJSCrashMessage();
}
int32_t DFXJSNApi::GetObjectHash(const EcmaVM *vm, Local<JSValueRef> nativeObject)
{
#if defined(CROSS_PLATFORM) && !defined(ECMASCRIPT_SUPPORT_DEBUGGER)
LOG_ECMA(INFO) << "GetObjectHash is not supported in cross-platform mode without debugger";
return 0;
#else
JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(nativeObject);
return ecmascript::tooling::DebuggerApi::GetObjectHash(vm, obj);
#endif
}
int32_t DFXJSNApi::GetObjectHashCode(const EcmaVM *vm, Local<JSValueRef> nativeObject)
{
#if defined(CROSS_PLATFORM) && !defined(ECMASCRIPT_SUPPORT_DEBUGGER)
LOG_ECMA(INFO) << "GetObjectHashCode is not supported in cross-platform mode without debugger";
return 0;
#else
JSHandle<JSTaggedValue> obj = JSNApiHelper::ToJSHandle(nativeObject);
return ecmascript::tooling::DebuggerApi::GetObjectHashCode(vm, obj);
#endif
}
bool DFXJSNApi::StartSampling([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] uint64_t samplingInterval)
{
#if defined(ECMASCRIPT_SUPPORT_HEAPSAMPLING)
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
const_cast<EcmaVM *>(vm));
return heapProfile->StartHeapSampling(samplingInterval);
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap sampling";
return false;
#endif
}
const SamplingInfo *DFXJSNApi::GetAllocationProfile([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_HEAPSAMPLING)
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
const_cast<EcmaVM *>(vm));
return heapProfile->GetAllocationProfile();
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap sampling";
return nullptr;
#endif
}
void DFXJSNApi::StopSampling([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_HEAPSAMPLING)
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
const_cast<EcmaVM *>(vm));
heapProfile->StopHeapSampling();
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap sampling";
#endif
}
bool DFXJSNApi::StartProfiler(EcmaVM *vm, const ProfilerOption &option, int tid,
int32_t instanceId, const DebuggerPostTask &debuggerPostTask, bool isDebugApp)
{
LOG_ECMA(INFO) << "DFXJSNApi::StartProfiler, type = " << (int)option.profilerType
<< ", tid = " << tid << ", isDebugApp = " << isDebugApp;
JSNApi::DebugOption debugOption;
debugOption.libraryPath = option.libraryPath;
if (option.profilerType == ProfilerType::CPU_PROFILER) {
debugOption.isDebugMode = false;
if (JSNApi::NotifyDebugMode(tid, vm, debugOption, instanceId, debuggerPostTask, isDebugApp)) {
return StartCpuProfilerForInfo(vm, option.interval);
} else {
LOG_ECMA(ERROR) << "DFXJSNApi::StartProfiler, NotifyDebugMode failed";
return false;
}
} else {
debugOption.isDebugMode = true;
return JSNApi::NotifyDebugMode(tid, vm, debugOption, instanceId, debuggerPostTask, isDebugApp);
}
}
void DFXJSNApi::ResumeVMById(EcmaVM *hostVm, uint32_t tid)
{
if (hostVm->GetAssociatedJSThread()->GetThreadId() == tid) {
ResumeVM(hostVm);
} else {
hostVm->ResumeWorkerVm(tid);
}
}
bool DFXJSNApi::SuspendVMById(EcmaVM *hostVm, uint32_t tid)
{
bool success = false;
if (hostVm->GetAssociatedJSThread()->GetThreadId() == tid) {
success = SuspendVM(hostVm);
LOG_ECMA(INFO) << "The main thread, SuspendVMById succeeded: " << success;
return success;
} else {
success = hostVm->SuspendWorkerVm(tid);
LOG_ECMA(INFO) << "The worker thread, SuspendVMById succeeded: " << success;
return success;
}
}
bool DFXJSNApi::StartTracing([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] std::string &categories)
{
#if defined(ECMASCRIPT_SUPPORT_TRACING)
if (vm == nullptr) {
return false;
}
Tracing *tracing = vm->GetTracing();
if (tracing == nullptr) {
tracing = new Tracing(vm);
const_cast<EcmaVM *>(vm)->SetTracing(tracing);
}
tracing->StartTracing(categories);
return true;
#else
LOG_ECMA(ERROR) << "Not support arkcompiler tracing";
return false;
#endif
}
std::unique_ptr<std::vector<TraceEvent>> DFXJSNApi::StopTracing([[maybe_unused]] const EcmaVM *vm)
{
#if defined(ECMASCRIPT_SUPPORT_TRACING)
if (vm == nullptr) {
return nullptr;
}
Tracing *tracing = vm->GetTracing();
if (tracing == nullptr) {
LOG_ECMA(ERROR) << "StopTracing tracing is nullptr";
return nullptr;
}
auto traceEvents = tracing->StopTracing();
if (traceEvents == nullptr) {
LOG_ECMA(ERROR) << "trace events is nullptr";
}
delete tracing;
tracing = nullptr;
const_cast<EcmaVM *>(vm)->SetTracing(nullptr);
return traceEvents;
#else
LOG_ECMA(ERROR) << "Not support arkcompiler tracing";
return nullptr;
#endif
}
void DFXJSNApi::GetTracingBufferUseage([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] double &percentFull,
[[maybe_unused]] uint32_t &eventCount, [[maybe_unused]] double &value)
{
#if defined(ECMASCRIPT_SUPPORT_TRACING)
if (vm == nullptr) {
return;
}
ecmascript::Tracing *tracing = vm->GetTracing();
if (tracing == nullptr) {
LOG_ECMA(ERROR) << "GetTracingBufferUseage tracing is nullptr";
} else {
tracing->GetBufferUseage(percentFull, eventCount, value);
}
#else
LOG_ECMA(ERROR) << "Not support arkcompiler tracing";
#endif
}
void DFXJSNApi::TranslateJSStackInfo(const EcmaVM *vm, std::string &url, int32_t &line, int32_t &column,
std::string &packageName)
{
auto cb = vm->GetSourceMapTranslateCallback();
if (cb == nullptr) {
LOG_ECMA(ERROR) << "Translate failed, callback function is nullptr.";
} else if (!cb(url, line, column, packageName)) {
LOG_ECMA(ERROR) << "Translate failed, url: " << url;
}
}
uint32_t DFXJSNApi::GetCurrentThreadId()
{
return JSThread::GetCurrentThreadId();
}
void DFXJSNApi::RegisterAsyncDetectCallBack(const EcmaVM *vm)
{
vm->GetAsyncStackTrace()->RegisterAsyncDetectCallBack();
}
void DFXJSNApi::GetMainThreadStackTrace(const EcmaVM *vm, std::string &stackTraceStr)
{
auto thread = vm->GetJSThread();
if (thread->IsMainThread()) {
stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace(
thread, false, false, ecmascript::JS_STACK_TRACE_DEPTH_MAX);
} else {
auto mainThread = ecmascript::Runtime::GetInstance()->GetMainThread();
ecmascript::ThreadManagedScope runningScope(thread);
if (g_isEnableCMCGC) {
ecmascript::SuspendAllScope suspendAllScope(thread);
stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace(
mainThread, false, false, ecmascript::JS_STACK_TRACE_DEPTH_MAX);
} else {
ecmascript::SuspendOtherScope suspendOtherScope(thread, mainThread);
stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace(
mainThread, false, false, ecmascript::JS_STACK_TRACE_DEPTH_MAX);
}
}
auto sourceMapcb = vm->GetSourceMapCallback();
if (sourceMapcb != nullptr && !stackTraceStr.empty()) {
stackTraceStr = sourceMapcb(stackTraceStr);
}
}
void DFXJSNApi::SetMultithreadingDetectionEnabled(const EcmaVM *vm, bool enabled,
const MultithreadingDetectionOptions& options)
{
EcmaVM::SetMultiThreadCheck(enabled);
EcmaVM::SetCheckCountApi(enabled);
if (enabled) {
EcmaVM::SetDetectionConfig(options);
}
}
const DFXJSNApi::MultithreadingDetectionOptions &DFXJSNApi::GetDetectionConfig()
{
return EcmaVM::GetDetectionConfig();
}
bool DFXJSNApi::OnVMHeapMemoryPressure(const EcmaVM *vm, HeapMemoryPressureOptions options,
Local<FunctionRef> callback)
{
LOG_ECMA(INFO) << "OnVMHeapMemoryPressure called: localThreshold=" << options.localHeapThreshold
<< ", sharedThreshold=" << options.sharedHeapThreshold
<< ", processThreshold=" << options.processHeapThreshold;
auto ecmaVm = const_cast<EcmaVM *>(vm);
return ecmaVm->SetHeapMemoryPressure(options, callback);
}
void DFXJSNApi::OffVMHeapMemoryPressure(const EcmaVM *vm)
{
auto ecmaVm = const_cast<EcmaVM *>(vm);
ecmaVm->ResetMemoryPressure();
LOG_ECMA(INFO) << "OffVMHeapMemoryPressure called. Memory pressure listener cleared, thresholds reset to 0";
}
void DFXJSNApi::GetHybridStackTrace(const EcmaVM *vm, std::string &stackTraceStr)
{
stackTraceStr = ecmascript::SymbolicAddress(vm->GetPcVectorData(), vm->GetPcVectorSize(), vm);
}
void DFXJSNApi::SetJsRawHeapCropLevel(CropLevel level)
{
ecmascript::Runtime::GetInstance()->SetRawHeapDumpCropLevel(level);
LOG_ECMA(INFO) << "Set raw heap dump level " << static_cast<int>(level);
}
void DFXJSNApi::SetProcDumpInSharedOOM(bool enable)
{
ecmascript::Runtime::GetInstance()->SetProcDumpInSharedOOM(enable);
LOG_ECMA(INFO) << "SetProcDumpInSharedOOM " << enable;
}
JSHandle<JSTaggedValue> DFXJSNApi::FindFunctionForHook(const EcmaVM *vm, const std::string &recordName,
const std::string &namespaceName, const std::string &className,
const std::string &funcName)
{
JSThread *thread = vm->GetJSThread();
return SourceTextModule::FindFuncInModuleForHook(thread, recordName, namespaceName, className, funcName);
}
void DFXJSNApi::ReplaceFunctionForHook(const EcmaVM *vm, JSHandle<JSTaggedValue> &target,
JSHandle<JSTaggedValue> &hook, JSHandle<JSTaggedValue> &backup)
{
if (!target->IsJSFunction() || !hook->IsJSFunction() || !backup->IsJSFunction()) {
return;
}
JSThread *thread = vm->GetJSThread();
JSHandle<JSFunction> targetFunc = JSHandle<JSFunction>::Cast(target);
JSHandle<JSFunction> hookFunc = JSHandle<JSFunction>::Cast(hook);
JSHandle<JSFunction> backupFunc = JSHandle<JSFunction>::Cast(backup);
JSFunction::ReplaceFunctionForHook(thread, backupFunc, targetFunc);
JSFunction::ReplaceFunctionForHook(thread, targetFunc, hookFunc);
}
bool DFXJSNApi::LoadHookModule(const EcmaVM *vm)
{
const CString path(ecmascript::ohos::OhosConstants::HOOK_FILE_PATH);
return JSPandaFileExecutor::ExecuteInsecureAbcFile(vm->GetJSThread(), path);
}
void DFXJSNApi::SetEnableRuntimeAsyncStack(EcmaVM *vm, bool state)
{
#if defined(ENABLE_ASYNC_STACK)
vm->SetEnableRuntimeAsyncStack(state);
#else
LOG_ECMA(ERROR) << "Not support runtime async stack trace";
#endif
}
bool DFXJSNApi::GetEnableRuntimeAsyncStack(const EcmaVM *vm)
{
#if defined(ENABLE_ASYNC_STACK)
return vm->IsEnableRuntimeAsyncStack();
#else
LOG_ECMA(ERROR) << "Not support runtime async stack trace";
return false;
#endif
}
}