diff --git a/binding.gyp b/binding.gyp index d059d3c0..db93a543 100644 --- a/binding.gyp +++ b/binding.gyp @@ -15,6 +15,7 @@ "bindings/profilers/heap.cc", "bindings/profilers/wall.cc", "bindings/per-isolate-data.cc", + "bindings/profile-translator.cc", "bindings/thread-cpu-clock.cc", "bindings/translate-heap-profile.cc", "bindings/translate-time-profile.cc", diff --git a/bindings/general-regs-only.hh b/bindings/general-regs-only.hh new file mode 100644 index 00000000..cdcf8166 --- /dev/null +++ b/bindings/general-regs-only.hh @@ -0,0 +1,23 @@ +/* + * Copyright 2024 Datadog, Inc + * + * 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. + */ + +#pragma once + +#if defined(__linux__) && defined(__aarch64__) +#define GENERAL_REGS_ONLY __attribute__((target("general-regs-only"))) +#else +#define GENERAL_REGS_ONLY +#endif diff --git a/bindings/profile-translator.cc b/bindings/profile-translator.cc new file mode 100644 index 00000000..de2c18b4 --- /dev/null +++ b/bindings/profile-translator.cc @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Datadog, Inc + * + * 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 "profile-translator.hh" +#include + +v8::Local dd::ProfileTranslator::NewNumber(int64_t x) { + return v8::Number::New(isolate, static_cast(x)); +} diff --git a/bindings/profile-translator.hh b/bindings/profile-translator.hh index 78ce070f..54429c72 100644 --- a/bindings/profile-translator.hh +++ b/bindings/profile-translator.hh @@ -33,10 +33,7 @@ class ProfileTranslator { return v8::Boolean::New(isolate, x); } - template - v8::Local NewNumber(T x) { - return v8::Number::New(isolate, x); - } + v8::Local NewNumber(int64_t x); v8::Local NewArray(int length) { return length == 0 ? emptyArray : v8::Array::New(isolate, length); diff --git a/bindings/profilers/heap.hh b/bindings/profilers/heap.hh index 5badc46c..01c897e3 100644 --- a/bindings/profilers/heap.hh +++ b/bindings/profilers/heap.hh @@ -17,6 +17,7 @@ #pragma once #include +#include "general-regs-only.hh" namespace dd { @@ -34,7 +35,7 @@ class HeapProfiler { // getAllocationProfile(): AllocationProfileNode static NAN_METHOD(GetAllocationProfile); - static NAN_METHOD(MonitorOutOfMemory); + static NAN_METHOD(MonitorOutOfMemory) GENERAL_REGS_ONLY; static NAN_MODULE_INIT(Init); }; diff --git a/bindings/profilers/wall.cc b/bindings/profilers/wall.cc index 1c760c95..2fabac00 100644 --- a/bindings/profilers/wall.cc +++ b/bindings/profilers/wall.cc @@ -321,7 +321,8 @@ void SignalHandler::HandleProfilerSignal(int sig, auto time_from = Now(); old_handler(sig, info, context); auto time_to = Now(); - double async_id = node::AsyncHooksGetExecutionAsyncId(isolate); + int64_t async_id = + static_cast(node::AsyncHooksGetExecutionAsyncId(isolate)); prof->PushContext(time_from, time_to, cpu_time, async_id); } #else @@ -369,10 +370,17 @@ static int64_t GetV8ToEpochOffset() { return V8toEpochOffset; } -ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile, - ContextBuffer& contexts, - int64_t startCpuTime) { - ContextsByNode contextsByNode; +Local NewNumberFromInt64(Isolate* isolate, int64_t value) { + return Number::New(isolate, static_cast(value)); +} + +std::shared_ptr CreateContextsByNode() { + return std::make_shared(); +} + +std::shared_ptr WallProfiler::GetContextsByNode( + CpuProfile* profile, ContextBuffer& contexts, int64_t startCpuTime) { + auto contextsByNode = CreateContextsByNode(); auto sampleCount = profile->GetSamplesCount(); if (contexts.empty() || sampleCount == 0) { @@ -432,11 +440,11 @@ ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile, break; } else { // This sample context is the closest to this sample. - auto it = contextsByNode.find(sample); + auto it = contextsByNode->find(sample); Local array; - if (it == contextsByNode.end()) { + if (it == contextsByNode->end()) { array = Array::New(isolate); - contextsByNode[sample] = {array, 1}; + (*contextsByNode)[sample] = {array, 1}; } else { array = it->second.contexts; ++it->second.hitcount; @@ -459,17 +467,17 @@ ContextsByNode WallProfiler::GetContextsByNode(CpuProfile* profile, // sample if (collectCpuTime_ && !isIdleOrProgram(sample)) { timedContext - ->Set( - v8Context, - cpuTimeKey, - Number::New(isolate, sampleContext.cpu_time - lastCpuTime)) + ->Set(v8Context, + cpuTimeKey, + NewNumberFromInt64(isolate, + sampleContext.cpu_time - lastCpuTime)) .Check(); lastCpuTime = sampleContext.cpu_time; } timedContext ->Set(v8Context, asyncIdKey, - Number::New(isolate, sampleContext.async_id)) + NewNumberFromInt64(isolate, sampleContext.async_id)) .Check(); array->Set(v8Context, array->Length(), timedContext).Check(); } @@ -881,7 +889,7 @@ Result WallProfiler::StopImpl(bool restart, v8::Local& profile) { profile = TranslateTimeProfile(v8_profile, includeLines_, - &contextsByNode, + contextsByNode, collectCpuTime_, nonJSThreadsCpuTime); @@ -1018,7 +1026,7 @@ NAN_METHOD(WallProfiler::Dispose) { void WallProfiler::PushContext(int64_t time_from, int64_t time_to, int64_t cpu_time, - double async_id) { + int64_t async_id) { // Be careful this is called in a signal handler context therefore all // operations must be async signal safe (in particular no allocations). // Our ring buffer avoids allocations. diff --git a/bindings/profilers/wall.hh b/bindings/profilers/wall.hh index dd2ebe77..0f1f3322 100644 --- a/bindings/profilers/wall.hh +++ b/bindings/profilers/wall.hh @@ -17,6 +17,7 @@ #pragma once #include "contexts.hh" +#include "general-regs-only.hh" #include "thread-cpu-clock.hh" #include @@ -82,7 +83,7 @@ class WallProfiler : public Nan::ObjectWrap { int64_t time_from; int64_t time_to; int64_t cpu_time; - double async_id; + int64_t async_id; }; using ContextBuffer = std::vector; @@ -95,9 +96,10 @@ class WallProfiler : public Nan::ObjectWrap { // to work around https://bugs.chromium.org/p/v8/issues/detail?id=11051. v8::CpuProfiler* CreateV8CpuProfiler(); - ContextsByNode GetContextsByNode(v8::CpuProfile* profile, - ContextBuffer& contexts, - int64_t startCpuTime); + std::shared_ptr GetContextsByNode(v8::CpuProfile* profile, + ContextBuffer& contexts, + int64_t startCpuTime) + GENERAL_REGS_ONLY; bool waitForSignal(uint64_t targetCallCount = 0); @@ -122,10 +124,11 @@ class WallProfiler : public Nan::ObjectWrap { void PushContext(int64_t time_from, int64_t time_to, int64_t cpu_time, - double async_id); + int64_t async_id); Result StartImpl(); std::string StartInternal(); - Result StopImpl(bool restart, v8::Local& profile); + Result StopImpl(bool restart, + v8::Local& profile) GENERAL_REGS_ONLY; CollectionMode collectionMode() { auto res = collectionMode_.load(std::memory_order_relaxed); @@ -146,12 +149,12 @@ class WallProfiler : public Nan::ObjectWrap { return threadCpuStopWatch_.GetAndReset(); } - static NAN_METHOD(New); + static NAN_METHOD(New) GENERAL_REGS_ONLY; static NAN_METHOD(Start); static NAN_METHOD(Stop); static NAN_METHOD(V8ProfilerStuckEventLoopDetected); static NAN_METHOD(Dispose); - static NAN_MODULE_INIT(Init); + static NAN_MODULE_INIT(Init) GENERAL_REGS_ONLY; static NAN_GETTER(GetContext); static NAN_SETTER(SetContext); static NAN_GETTER(SharedArrayGetter); diff --git a/bindings/translate-heap-profile.hh b/bindings/translate-heap-profile.hh index dc5c7aa6..5493fba2 100644 --- a/bindings/translate-heap-profile.hh +++ b/bindings/translate-heap-profile.hh @@ -17,10 +17,11 @@ #pragma once #include +#include "general-regs-only.hh" namespace dd { v8::Local TranslateAllocationProfile( - v8::AllocationProfile::Node* node); + v8::AllocationProfile::Node* node) GENERAL_REGS_ONLY; } // namespace dd diff --git a/bindings/translate-time-profile.cc b/bindings/translate-time-profile.cc index 6495d3f0..41315648 100644 --- a/bindings/translate-time-profile.cc +++ b/bindings/translate-time-profile.cc @@ -15,6 +15,7 @@ */ #include "translate-time-profile.hh" +#include "general-regs-only.hh" #include "profile-translator.hh" namespace dd { @@ -22,7 +23,7 @@ namespace dd { namespace { class TimeProfileTranslator : ProfileTranslator { private: - ContextsByNode* contextsByNode; + std::shared_ptr contextsByNode; v8::Local emptyArray = NewArray(0); v8::Local zero = NewInteger(0); @@ -80,7 +81,7 @@ class TimeProfileTranslator : ProfileTranslator { } v8::Local GetLineNumberTimeProfileChildren( - const v8::CpuProfileNode* node) { + const v8::CpuProfileNode* node) GENERAL_REGS_ONLY { unsigned int index = 0; v8::Local children; int32_t count = node->GetChildrenCount(); @@ -200,7 +201,7 @@ class TimeProfileTranslator : ProfileTranslator { } public: - explicit TimeProfileTranslator(ContextsByNode* nls = nullptr) + explicit TimeProfileTranslator(std::shared_ptr nls = nullptr) : contextsByNode(nls) {} v8::Local TranslateTimeProfile(const v8::CpuProfile* profile, @@ -230,11 +231,12 @@ class TimeProfileTranslator : ProfileTranslator { }; } // namespace -v8::Local TranslateTimeProfile(const v8::CpuProfile* profile, - bool includeLineInfo, - ContextsByNode* contextsByNode, - bool hasCpuTime, - int64_t nonJSThreadsCpuTime) { +v8::Local TranslateTimeProfile( + const v8::CpuProfile* profile, + bool includeLineInfo, + std::shared_ptr contextsByNode, + bool hasCpuTime, + int64_t nonJSThreadsCpuTime) { return TimeProfileTranslator(contextsByNode) .TranslateTimeProfile( profile, includeLineInfo, hasCpuTime, nonJSThreadsCpuTime); diff --git a/bindings/translate-time-profile.hh b/bindings/translate-time-profile.hh index c83115d5..6ba28c60 100644 --- a/bindings/translate-time-profile.hh +++ b/bindings/translate-time-profile.hh @@ -24,7 +24,7 @@ namespace dd { v8::Local TranslateTimeProfile( const v8::CpuProfile* profile, bool includeLineInfo, - ContextsByNode* contextsByNode = nullptr, + std::shared_ptr contextsByNode = nullptr, bool hasCpuTime = false, int64_t nonJSThreadsCpuTime = 0);