From 1c9e4ed2bd1feef4cf9803bf5c3acb00765bda95 Mon Sep 17 00:00:00 2001 From: ishabi Date: Tue, 28 Apr 2026 11:52:54 +0200 Subject: [PATCH] src: split profiling helpers from util Signed-off-by: ishabi --- node.gyp | 2 + src/env.h | 1 + src/node_profiling.cc | 130 ++++++++++++++++++++++++++++++++++++++++++ src/node_profiling.h | 59 +++++++++++++++++++ src/node_v8.cc | 1 + src/node_worker.cc | 1 + src/util.cc | 96 ------------------------------- src/util.h | 20 ------- 8 files changed, 194 insertions(+), 116 deletions(-) create mode 100644 src/node_profiling.cc create mode 100644 src/node_profiling.h diff --git a/node.gyp b/node.gyp index 77acf529698db1..b129c3db8d88c1 100644 --- a/node.gyp +++ b/node.gyp @@ -141,6 +141,7 @@ 'src/node_os.cc', 'src/node_perf.cc', 'src/node_platform.cc', + 'src/node_profiling.cc', 'src/node_postmortem_metadata.cc', 'src/node_process_events.cc', 'src/node_process_methods.cc', @@ -283,6 +284,7 @@ 'src/node_perf.h', 'src/node_perf_common.h', 'src/node_platform.h', + 'src/node_profiling.h', 'src/node_process.h', 'src/node_process-inl.h', 'src/node_realm.h', diff --git a/src/env.h b/src/env.h index 5414edb1aea8ef..616f6e7d04109a 100644 --- a/src/env.h +++ b/src/env.h @@ -42,6 +42,7 @@ #include "node_main_instance.h" #include "node_options.h" #include "node_perf_common.h" +#include "node_profiling.h" #include "node_realm.h" #include "node_snapshotable.h" #include "permission/permission.h" diff --git a/src/node_profiling.cc b/src/node_profiling.cc new file mode 100644 index 00000000000000..bc403776c33f8e --- /dev/null +++ b/src/node_profiling.cc @@ -0,0 +1,130 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +#include "node_profiling.h" + +#include "json_utils.h" +#include "util.h" + +#include + +namespace node { + +using v8::AllocationProfile; +using v8::HandleScope; +using v8::HeapProfiler; +using v8::Isolate; +using v8::Value; + +static void BuildHeapProfileNode(Isolate* isolate, + const AllocationProfile::Node* node, + JSONWriter* writer) { + size_t selfSize = 0; + for (const auto& allocation : node->allocations) + selfSize += allocation.size * allocation.count; + + writer->json_keyvalue("selfSize", selfSize); + writer->json_keyvalue("id", node->node_id); + writer->json_objectstart("callFrame"); + writer->json_keyvalue("scriptId", node->script_id); + writer->json_keyvalue("lineNumber", node->line_number - 1); + writer->json_keyvalue("columnNumber", node->column_number - 1); + Utf8Value name(isolate, node->name); + Utf8Value script_name(isolate, node->script_name); + writer->json_keyvalue("functionName", *name); + writer->json_keyvalue("url", *script_name); + writer->json_objectend(); + + writer->json_arraystart("children"); + for (const auto* child : node->children) { + writer->json_start(); + BuildHeapProfileNode(isolate, child, writer); + writer->json_end(); + } + writer->json_arrayend(); +} + +bool SerializeHeapProfile(Isolate* isolate, std::ostringstream& out_stream) { + HandleScope scope(isolate); + HeapProfiler* profiler = isolate->GetHeapProfiler(); + std::unique_ptr profile(profiler->GetAllocationProfile()); + if (!profile) { + return false; + } + profiler->StopSamplingHeapProfiler(); + JSONWriter writer(out_stream, true); + writer.json_start(); + + writer.json_arraystart("samples"); + for (const auto& sample : profile->GetSamples()) { + writer.json_start(); + writer.json_keyvalue("size", sample.size * sample.count); + writer.json_keyvalue("nodeId", sample.node_id); + writer.json_keyvalue("ordinal", static_cast(sample.sample_id)); + writer.json_end(); + } + writer.json_arrayend(); + + writer.json_objectstart("head"); + BuildHeapProfileNode(isolate, profile->GetRootNode(), &writer); + writer.json_objectend(); + + writer.json_end(); + return true; +} + +HeapProfileOptions ParseHeapProfileOptions( + const v8::FunctionCallbackInfo& args) { + HeapProfileOptions options; + CHECK_LE(args.Length(), 3); + if (args.Length() > 0) { + CHECK(args[0]->IsNumber()); + options.sample_interval = + static_cast(args[0].As()->Value()); + } + if (args.Length() > 1) { + CHECK(args[1]->IsInt32()); + options.stack_depth = args[1].As()->Value(); + } + if (args.Length() > 2) { + CHECK(args[2]->IsUint32()); + options.flags = static_cast( + args[2].As()->Value()); + } + return options; +} + +CpuProfileOptions ParseCpuProfileOptions( + const v8::FunctionCallbackInfo& args) { + CpuProfileOptions options; + CHECK_LE(args.Length(), 2); + if (args.Length() > 0) { + CHECK(args[0]->IsInt32()); + options.sampling_interval_us = args[0].As()->Value(); + } + if (args.Length() > 1) { + CHECK(args[1]->IsUint32()); + options.max_samples = args[1].As()->Value(); + } + return options; +} + +} // namespace node diff --git a/src/node_profiling.h b/src/node_profiling.h new file mode 100644 index 00000000000000..c6fc396cbc6ab1 --- /dev/null +++ b/src/node_profiling.h @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SRC_NODE_PROFILING_H_ +#define SRC_NODE_PROFILING_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "v8-profiler.h" +#include "v8.h" + +#include +#include + +namespace node { + +struct HeapProfileOptions { + uint64_t sample_interval = 512 * 1024; + int stack_depth = 16; + v8::HeapProfiler::SamplingFlags flags = + v8::HeapProfiler::SamplingFlags::kSamplingNoFlags; +}; + +HeapProfileOptions ParseHeapProfileOptions( + const v8::FunctionCallbackInfo& args); + +bool SerializeHeapProfile(v8::Isolate* isolate, std::ostringstream& out_stream); + +struct CpuProfileOptions { + int sampling_interval_us = 0; + uint32_t max_samples = v8::CpuProfilingOptions::kNoSampleLimit; +}; + +CpuProfileOptions ParseCpuProfileOptions( + const v8::FunctionCallbackInfo& args); + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_PROFILING_H_ diff --git a/src/node_v8.cc b/src/node_v8.cc index fc0f16d340bcd5..b49c29443a4287 100644 --- a/src/node_v8.cc +++ b/src/node_v8.cc @@ -26,6 +26,7 @@ #include "memory_tracker-inl.h" #include "node.h" #include "node_external_reference.h" +#include "node_profiling.h" #include "util-inl.h" #include "v8-profiler.h" #include "v8.h" diff --git a/src/node_worker.cc b/src/node_worker.cc index 7d131f61cf97a2..edc21e7e556157 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -8,6 +8,7 @@ #include "node_external_reference.h" #include "node_options-inl.h" #include "node_perf.h" +#include "node_profiling.h" #include "node_snapshot_builder.h" #include "permission/permission.h" #include "util-inl.h" diff --git a/src/util.cc b/src/util.cc index 4bc7ddea92905a..1ea51cf7012963 100644 --- a/src/util.cc +++ b/src/util.cc @@ -26,7 +26,6 @@ #include "debug_utils-inl.h" #include "env-inl.h" -#include "json_utils.h" #include "node_buffer.h" #include "node_errors.h" #include "node_internals.h" @@ -86,13 +85,10 @@ constexpr int kMaximumCopyMode = namespace node { -using v8::AllocationProfile; using v8::ArrayBuffer; using v8::ArrayBufferView; using v8::Context; using v8::FunctionTemplate; -using v8::HandleScope; -using v8::HeapProfiler; using v8::Isolate; using v8::Local; using v8::Object; @@ -816,96 +812,4 @@ v8::Maybe GetValidFileMode(Environment* env, return v8::Just(mode); } -static void BuildHeapProfileNode(Isolate* isolate, - const AllocationProfile::Node* node, - JSONWriter* writer) { - size_t selfSize = 0; - for (const auto& allocation : node->allocations) - selfSize += allocation.size * allocation.count; - - writer->json_keyvalue("selfSize", selfSize); - writer->json_keyvalue("id", node->node_id); - writer->json_objectstart("callFrame"); - writer->json_keyvalue("scriptId", node->script_id); - writer->json_keyvalue("lineNumber", node->line_number - 1); - writer->json_keyvalue("columnNumber", node->column_number - 1); - Utf8Value name(isolate, node->name); - Utf8Value script_name(isolate, node->script_name); - writer->json_keyvalue("functionName", *name); - writer->json_keyvalue("url", *script_name); - writer->json_objectend(); - - writer->json_arraystart("children"); - for (const auto* child : node->children) { - writer->json_start(); - BuildHeapProfileNode(isolate, child, writer); - writer->json_end(); - } - writer->json_arrayend(); -} - -bool SerializeHeapProfile(Isolate* isolate, std::ostringstream& out_stream) { - HandleScope scope(isolate); - HeapProfiler* profiler = isolate->GetHeapProfiler(); - std::unique_ptr profile(profiler->GetAllocationProfile()); - if (!profile) { - return false; - } - profiler->StopSamplingHeapProfiler(); - JSONWriter writer(out_stream, true); - writer.json_start(); - - writer.json_arraystart("samples"); - for (const auto& sample : profile->GetSamples()) { - writer.json_start(); - writer.json_keyvalue("size", sample.size * sample.count); - writer.json_keyvalue("nodeId", sample.node_id); - writer.json_keyvalue("ordinal", static_cast(sample.sample_id)); - writer.json_end(); - } - writer.json_arrayend(); - - writer.json_objectstart("head"); - BuildHeapProfileNode(isolate, profile->GetRootNode(), &writer); - writer.json_objectend(); - - writer.json_end(); - return true; -} - -HeapProfileOptions ParseHeapProfileOptions( - const v8::FunctionCallbackInfo& args) { - HeapProfileOptions options; - CHECK_LE(args.Length(), 3); - if (args.Length() > 0) { - CHECK(args[0]->IsNumber()); - options.sample_interval = - static_cast(args[0].As()->Value()); - } - if (args.Length() > 1) { - CHECK(args[1]->IsInt32()); - options.stack_depth = args[1].As()->Value(); - } - if (args.Length() > 2) { - CHECK(args[2]->IsUint32()); - options.flags = static_cast( - args[2].As()->Value()); - } - return options; -} - -CpuProfileOptions ParseCpuProfileOptions( - const v8::FunctionCallbackInfo& args) { - CpuProfileOptions options; - CHECK_LE(args.Length(), 2); - if (args.Length() > 0) { - CHECK(args[0]->IsInt32()); - options.sampling_interval_us = args[0].As()->Value(); - } - if (args.Length() > 1) { - CHECK(args[1]->IsUint32()); - options.max_samples = args[1].As()->Value(); - } - return options; -} } // namespace node diff --git a/src/util.h b/src/util.h index 28b69491a141a9..3dedeca4d227e9 100644 --- a/src/util.h +++ b/src/util.h @@ -1078,26 +1078,6 @@ inline v8::Local Uint32ToString(v8::Local context, ->ToString(context) .ToLocalChecked(); } -bool SerializeHeapProfile(v8::Isolate* isolate, std::ostringstream& out_stream); - -struct HeapProfileOptions { - uint64_t sample_interval = 512 * 1024; - int stack_depth = 16; - v8::HeapProfiler::SamplingFlags flags = - v8::HeapProfiler::SamplingFlags::kSamplingNoFlags; -}; - -HeapProfileOptions ParseHeapProfileOptions( - const v8::FunctionCallbackInfo& args); - -struct CpuProfileOptions { - int sampling_interval_us = 0; - uint32_t max_samples = v8::CpuProfilingOptions::kNoSampleLimit; -}; - -CpuProfileOptions ParseCpuProfileOptions( - const v8::FunctionCallbackInfo& args); - } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS