/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v26.5.1
src/node_v8.cc
847 строк
33 KB
Ilyas Shabi
src: split profiling helpers from util
05 май 2026, 17:19
Не верифицирован
05 май 2026, 17:19
56917af
Код
Авторство
О чём код?
// 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_v8.h" #include "aliased_buffer-inl.h" #include "base_object-inl.h" #include "env-inl.h" #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" namespace node { namespace v8_utils { using v8::Array; using v8::BigInt; using v8::CFunction; using v8::Context; using v8::CpuProfile; using v8::CpuProfilingResult; using v8::CpuProfilingStatus; using v8::DictionaryTemplate; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::HeapCodeStatistics; using v8::HeapSpaceStatistics; using v8::HeapStatistics; using v8::Integer; using v8::Isolate; using v8::Local; using v8::LocalVector; using v8::MaybeLocal; using v8::Number; using v8::Object; using v8::ScriptCompiler; using v8::String; using v8::Uint32; using v8::V8; using v8::Value; #define HEAP_STATISTICS_PROPERTIES(V) \ V(0, total_heap_size, kTotalHeapSizeIndex) \ V(1, total_heap_size_executable, kTotalHeapSizeExecutableIndex) \ V(2, total_physical_size, kTotalPhysicalSizeIndex) \ V(3, total_available_size, kTotalAvailableSize) \ V(4, used_heap_size, kUsedHeapSizeIndex) \ V(5, heap_size_limit, kHeapSizeLimitIndex) \ V(6, malloced_memory, kMallocedMemoryIndex) \ V(7, peak_malloced_memory, kPeakMallocedMemoryIndex) \ V(8, does_zap_garbage, kDoesZapGarbageIndex) \ V(9, number_of_native_contexts, kNumberOfNativeContextsIndex) \ V(10, number_of_detached_contexts, kNumberOfDetachedContextsIndex) \ V(11, total_global_handles_size, kTotalGlobalHandlesSizeIndex) \ V(12, used_global_handles_size, kUsedGlobalHandlesSizeIndex) \ V(13, external_memory, kExternalMemoryIndex) \ V(14, total_allocated_bytes, kTotalAllocatedBytes) #define V(a, b, c) +1 static constexpr size_t kHeapStatisticsPropertiesCount = HEAP_STATISTICS_PROPERTIES(V); #undef V #define HEAP_SPACE_STATISTICS_PROPERTIES(V) \ V(0, space_size, kSpaceSizeIndex) \ V(1, space_used_size, kSpaceUsedSizeIndex) \ V(2, space_available_size, kSpaceAvailableSizeIndex) \ V(3, physical_space_size, kPhysicalSpaceSizeIndex) #define V(a, b, c) +1 static constexpr size_t kHeapSpaceStatisticsPropertiesCount = HEAP_SPACE_STATISTICS_PROPERTIES(V); #undef V #define HEAP_CODE_STATISTICS_PROPERTIES(V) \ V(0, code_and_metadata_size, kCodeAndMetadataSizeIndex) \ V(1, bytecode_and_metadata_size, kBytecodeAndMetadataSizeIndex) \ V(2, external_script_source_size, kExternalScriptSourceSizeIndex) \ V(3, cpu_profiler_metadata_size, kCPUProfilerMetaDataSizeIndex) #define V(a, b, c) +1 static const size_t kHeapCodeStatisticsPropertiesCount = HEAP_CODE_STATISTICS_PROPERTIES(V); #undef V BindingData::BindingData(Realm* realm, Local<Object> obj, InternalFieldInfo* info) : SnapshotableObject(realm, obj, type_int), heap_statistics_buffer(realm->isolate(), kHeapStatisticsPropertiesCount, MAYBE_FIELD_PTR(info, heap_statistics_buffer)), heap_space_statistics_buffer( realm->isolate(), kHeapSpaceStatisticsPropertiesCount, MAYBE_FIELD_PTR(info, heap_space_statistics_buffer)), heap_code_statistics_buffer( realm->isolate(), kHeapCodeStatisticsPropertiesCount, MAYBE_FIELD_PTR(info, heap_code_statistics_buffer)) { Local<Context> context = realm->context(); if (info == nullptr) { obj->Set(context, FIXED_ONE_BYTE_STRING(realm->isolate(), "heapStatisticsBuffer"), heap_statistics_buffer.GetJSArray()) .Check(); obj->Set( context, FIXED_ONE_BYTE_STRING(realm->isolate(), "heapCodeStatisticsBuffer"), heap_code_statistics_buffer.GetJSArray()) .Check(); obj->Set( context, FIXED_ONE_BYTE_STRING(realm->isolate(), "heapSpaceStatisticsBuffer"), heap_space_statistics_buffer.GetJSArray()) .Check(); } else { heap_statistics_buffer.Deserialize(realm->context()); heap_code_statistics_buffer.Deserialize(realm->context()); heap_space_statistics_buffer.Deserialize(realm->context()); } heap_statistics_buffer.MakeWeak(); heap_space_statistics_buffer.MakeWeak(); heap_code_statistics_buffer.MakeWeak(); } bool BindingData::PrepareForSerialization(Local<Context> context, v8::SnapshotCreator* creator) { DCHECK_NULL(internal_field_info_); internal_field_info_ = InternalFieldInfoBase::New<InternalFieldInfo>(type()); internal_field_info_->heap_statistics_buffer = heap_statistics_buffer.Serialize(context, creator); internal_field_info_->heap_space_statistics_buffer = heap_space_statistics_buffer.Serialize(context, creator); internal_field_info_->heap_code_statistics_buffer = heap_code_statistics_buffer.Serialize(context, creator); // Return true because we need to maintain the reference to the binding from // JS land. return true; } void BindingData::Deserialize(Local<Context> context, Local<Object> holder, int index, InternalFieldInfoBase* info) { DCHECK_IS_SNAPSHOT_SLOT(index); HandleScope scope(Isolate::GetCurrent()); Realm* realm = Realm::GetCurrent(context); // Recreate the buffer in the constructor. InternalFieldInfo* casted_info = static_cast<InternalFieldInfo*>(info); BindingData* binding = realm->AddBindingData<BindingData>(holder, casted_info); CHECK_NOT_NULL(binding); } InternalFieldInfoBase* BindingData::Serialize(int index) { DCHECK_IS_SNAPSHOT_SLOT(index); InternalFieldInfo* info = internal_field_info_; internal_field_info_ = nullptr; return info; } void BindingData::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("heap_statistics_buffer", heap_statistics_buffer); tracker->TrackField("heap_space_statistics_buffer", heap_space_statistics_buffer); tracker->TrackField("heap_code_statistics_buffer", heap_code_statistics_buffer); } void CachedDataVersionTag(const FunctionCallbackInfo<Value>& args) { Local<Integer> result = Integer::NewFromUnsigned( args.GetIsolate(), ScriptCompiler::CachedDataVersionTag()); args.GetReturnValue().Set(result); } void SetHeapSnapshotNearHeapLimit(const FunctionCallbackInfo<Value>& args) { CHECK(args[0]->IsUint32()); Environment* env = Environment::GetCurrent(args); uint32_t limit = args[0].As<v8::Uint32>()->Value(); CHECK_GT(limit, 0); env->AddHeapSnapshotNearHeapLimitCallback(); env->set_heap_snapshot_near_heap_limit(limit); } void UpdateHeapStatisticsBuffer(const FunctionCallbackInfo<Value>& args) { BindingData* data = Realm::GetBindingData<BindingData>(args); HeapStatistics s; args.GetIsolate()->GetHeapStatistics(&s); AliasedFloat64Array& buffer = data->heap_statistics_buffer; #define V(index, name, _) buffer[index] = static_cast<double>(s.name()); HEAP_STATISTICS_PROPERTIES(V) #undef V } void UpdateHeapSpaceStatisticsBuffer(const FunctionCallbackInfo<Value>& args) { BindingData* data = Realm::GetBindingData<BindingData>(args); HeapSpaceStatistics s; Isolate* const isolate = args.GetIsolate(); CHECK(args[0]->IsUint32()); size_t space_index = static_cast<size_t>(args[0].As<v8::Uint32>()->Value()); isolate->GetHeapSpaceStatistics(&s, space_index); AliasedFloat64Array& buffer = data->heap_space_statistics_buffer; #define V(index, name, _) buffer[index] = static_cast<double>(s.name()); HEAP_SPACE_STATISTICS_PROPERTIES(V) #undef V } void UpdateHeapCodeStatisticsBuffer(const FunctionCallbackInfo<Value>& args) { BindingData* data = Realm::GetBindingData<BindingData>(args); HeapCodeStatistics s; args.GetIsolate()->GetHeapCodeAndMetadataStatistics(&s); AliasedFloat64Array& buffer = data->heap_code_statistics_buffer; #define V(index, name, _) buffer[index] = static_cast<double>(s.name()); HEAP_CODE_STATISTICS_PROPERTIES(V) #undef V } void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) { CHECK(args[0]->IsString()); Utf8Value flags(args.GetIsolate(), args[0]); V8::SetFlagsFromString(flags.out(), flags.length()); } void StartCpuProfile(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); CpuProfileOptions options = ParseCpuProfileOptions(args); CpuProfilingResult result = env->StartCpuProfile(options); if (result.status == CpuProfilingStatus::kErrorTooManyProfilers) { return THROW_ERR_CPU_PROFILE_TOO_MANY(isolate, "There are too many CPU profiles"); } else if (result.status == CpuProfilingStatus::kStarted) { args.GetReturnValue().Set(Number::New(isolate, result.id)); } } void StopCpuProfile(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); CHECK(args[0]->IsUint32()); uint32_t profile_id = args[0]->Uint32Value(env->context()).FromJust(); CpuProfile* profile = env->StopCpuProfile(profile_id); if (!profile) { return THROW_ERR_CPU_PROFILE_NOT_STARTED(isolate, "CPU profile not started"); } auto json_out_stream = std::make_unique<node::JSONOutputStream>(); profile->Serialize(json_out_stream.get(), CpuProfile::SerializationFormat::kJSON); profile->Delete(); Local<Value> ret; if (ToV8Value(env->context(), json_out_stream->out_stream().str(), isolate) .ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void StartHeapProfile(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); auto options = ParseHeapProfileOptions(args); if (isolate->GetHeapProfiler()->StartSamplingHeapProfiler( options.sample_interval, options.stack_depth, options.flags)) { return; } THROW_ERR_HEAP_PROFILE_HAVE_BEEN_STARTED(isolate, "Heap profile has been started"); } void StopHeapProfile(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); std::ostringstream out_stream; bool success = node::SerializeHeapProfile(isolate, out_stream); if (success) { Local<Value> result; if (ToV8Value(env->context(), out_stream.str(), isolate).ToLocal(&result)) { args.GetReturnValue().Set(result); } } else { THROW_ERR_HEAP_PROFILE_NOT_STARTED(isolate, "heap profile not started"); } } static void IsStringOneByteRepresentation( const FunctionCallbackInfo<Value>& args) { CHECK_EQ(args.Length(), 1); CHECK(args[0]->IsString()); bool is_one_byte = args[0].As<String>()->IsOneByte(); args.GetReturnValue().Set(is_one_byte); } static bool FastIsStringOneByteRepresentation(Local<Value> receiver, const Local<Value> target) { CHECK(target->IsString()); return target.As<String>()->IsOneByte(); } CFunction fast_is_string_one_byte_representation_( CFunction::Make(FastIsStringOneByteRepresentation)); void GetHashSeed(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); uint64_t hash_seed = isolate->GetHashSeed(); args.GetReturnValue().Set(BigInt::NewFromUnsigned(isolate, hash_seed)); } static const char* GetGCTypeName(v8::GCType gc_type) { switch (gc_type) { case v8::GCType::kGCTypeScavenge: return "Scavenge"; case v8::GCType::kGCTypeMarkSweepCompact: return "MarkSweepCompact"; case v8::GCType::kGCTypeIncrementalMarking: return "IncrementalMarking"; case v8::GCType::kGCTypeProcessWeakCallbacks: return "ProcessWeakCallbacks"; default: return "Unknown"; } } static void SetHeapStatistics(JSONWriter* writer, Isolate* isolate) { HeapStatistics heap_statistics; isolate->GetHeapStatistics(&heap_statistics); writer->json_objectstart("heapStatistics"); writer->json_keyvalue("totalHeapSize", heap_statistics.total_heap_size()); writer->json_keyvalue("totalHeapSizeExecutable", heap_statistics.total_heap_size_executable()); writer->json_keyvalue("totalPhysicalSize", heap_statistics.total_physical_size()); writer->json_keyvalue("totalAvailableSize", heap_statistics.total_available_size()); writer->json_keyvalue("totalGlobalHandlesSize", heap_statistics.total_global_handles_size()); writer->json_keyvalue("usedGlobalHandlesSize", heap_statistics.used_global_handles_size()); writer->json_keyvalue("usedHeapSize", heap_statistics.used_heap_size()); writer->json_keyvalue("heapSizeLimit", heap_statistics.heap_size_limit()); writer->json_keyvalue("mallocedMemory", heap_statistics.malloced_memory()); writer->json_keyvalue("externalMemory", heap_statistics.external_memory()); writer->json_keyvalue("peakMallocedMemory", heap_statistics.peak_malloced_memory()); writer->json_objectend(); int space_count = isolate->NumberOfHeapSpaces(); writer->json_arraystart("heapSpaceStatistics"); for (int i = 0; i < space_count; i++) { HeapSpaceStatistics heap_space_statistics; isolate->GetHeapSpaceStatistics(&heap_space_statistics, i); writer->json_start(); writer->json_keyvalue("spaceName", heap_space_statistics.space_name()); writer->json_keyvalue("spaceSize", heap_space_statistics.space_size()); writer->json_keyvalue("spaceUsedSize", heap_space_statistics.space_used_size()); writer->json_keyvalue("spaceAvailableSize", heap_space_statistics.space_available_size()); writer->json_keyvalue("physicalSpaceSize", heap_space_statistics.physical_space_size()); writer->json_end(); } writer->json_arrayend(); } static MaybeLocal<Object> ConvertHeapStatsToJSObject( Isolate* isolate, const cppgc::HeapStatistics& stats) { Local<Context> context = isolate->GetCurrentContext(); Environment* env = Environment::GetCurrent(isolate); // Space Statistics LocalVector<Value> space_statistics_array(isolate); space_statistics_array.reserve(stats.space_stats.size()); auto object_stats_template = env->object_stats_template(); auto page_stats_tmpl = env->page_stats_template(); auto free_list_statistics_template = env->free_list_statistics_template(); auto space_stats_tmpl = env->space_stats_template(); auto heap_stats_tmpl = env->v8_heap_statistics_template(); if (object_stats_template.IsEmpty()) { static constexpr std::string_view object_stats_names[] = {"allocated_bytes", "object_count"}; object_stats_template = DictionaryTemplate::New(isolate, object_stats_names); env->set_object_stats_template(object_stats_template); } if (page_stats_tmpl.IsEmpty()) { static constexpr std::string_view page_stats_names[] = { "committed_size_bytes", "resident_size_bytes", "used_size_bytes", "object_statistics"}; page_stats_tmpl = DictionaryTemplate::New(isolate, page_stats_names); env->set_page_stats_template(page_stats_tmpl); } if (free_list_statistics_template.IsEmpty()) { std::string_view free_list_statistics_names[] = { "bucket_size", "free_count", "free_size"}; free_list_statistics_template = DictionaryTemplate::New(isolate, free_list_statistics_names); env->set_free_list_statistics_template(free_list_statistics_template); } if (space_stats_tmpl.IsEmpty()) { static constexpr std::string_view space_stats_names[] = { "name", "committed_size_bytes", "resident_size_bytes", "used_size_bytes", "page_stats", "free_list_stats"}; space_stats_tmpl = DictionaryTemplate::New(isolate, space_stats_names); env->set_space_stats_template(space_stats_tmpl); } if (heap_stats_tmpl.IsEmpty()) { static constexpr std::string_view heap_statistics_names[] = { "committed_size_bytes", "resident_size_bytes", "used_size_bytes", "space_statistics", "type_names"}; heap_stats_tmpl = DictionaryTemplate::New(isolate, heap_statistics_names); env->set_v8_heap_statistics_template(heap_stats_tmpl); } for (size_t i = 0; i < stats.space_stats.size(); i++) { const cppgc::HeapStatistics::SpaceStatistics& space_stats = stats.space_stats[i]; // Page Statistics LocalVector<Value> page_statistics_array(isolate); page_statistics_array.reserve(space_stats.page_stats.size()); for (size_t j = 0; j < space_stats.page_stats.size(); j++) { const cppgc::HeapStatistics::PageStatistics& page_stats = space_stats.page_stats[j]; // Object Statistics LocalVector<Value> object_statistics_array(isolate); object_statistics_array.reserve(page_stats.object_statistics.size()); for (size_t k = 0; k < page_stats.object_statistics.size(); k++) { const cppgc::HeapStatistics::ObjectStatsEntry& object_stats = page_stats.object_statistics[k]; MaybeLocal<Value> object_stats_values[] = { Uint32::NewFromUnsigned( isolate, static_cast<uint32_t>(object_stats.allocated_bytes)), Uint32::NewFromUnsigned( isolate, static_cast<uint32_t>(object_stats.object_count))}; Local<Object> object_stats_object; if (!NewDictionaryInstanceNullProto( context, object_stats_template, object_stats_values) .ToLocal(&object_stats_object)) { return MaybeLocal<Object>(); } object_statistics_array.emplace_back(object_stats_object); } // Set page statistics MaybeLocal<Value> page_stats_values[] = { Uint32::NewFromUnsigned( isolate, static_cast<uint32_t>(page_stats.committed_size_bytes)), Uint32::NewFromUnsigned( isolate, static_cast<uint32_t>(page_stats.resident_size_bytes)), Uint32::NewFromUnsigned( isolate, static_cast<uint32_t>(page_stats.used_size_bytes)), Array::New(isolate, object_statistics_array.data(), object_statistics_array.size())}; Local<Object> page_stats_object; if (!NewDictionaryInstanceNullProto( context, page_stats_tmpl, page_stats_values) .ToLocal(&page_stats_object)) { return MaybeLocal<Object>(); } page_statistics_array.emplace_back(page_stats_object); } // Free List Statistics MaybeLocal<Value> free_list_statistics_values[] = { ToV8ValuePrimitiveArray( context, space_stats.free_list_stats.bucket_size, isolate), ToV8ValuePrimitiveArray( context, space_stats.free_list_stats.free_count, isolate), ToV8ValuePrimitiveArray( context, space_stats.free_list_stats.free_size, isolate)}; Local<Object> free_list_statistics_obj; if (!NewDictionaryInstanceNullProto(context, free_list_statistics_template, free_list_statistics_values) .ToLocal(&free_list_statistics_obj)) { return MaybeLocal<Object>(); } // Set Space Statistics Local<Value> name_value; if (!ToV8Value(context, stats.space_stats[i].name, isolate) .ToLocal(&name_value)) { return MaybeLocal<Object>(); } MaybeLocal<Value> space_stats_values[] = { name_value, Uint32::NewFromUnsigned( isolate, static_cast<uint32_t>(stats.space_stats[i].committed_size_bytes)), Uint32::NewFromUnsigned( isolate, static_cast<uint32_t>(stats.space_stats[i].resident_size_bytes)), Uint32::NewFromUnsigned( isolate, static_cast<uint32_t>(stats.space_stats[i].used_size_bytes)), Array::New(isolate, page_statistics_array.data(), page_statistics_array.size()), free_list_statistics_obj, }; Local<Object> space_stats_object; if (!NewDictionaryInstanceNullProto( context, space_stats_tmpl, space_stats_values) .ToLocal(&space_stats_object)) { return MaybeLocal<Object>(); } space_statistics_array.emplace_back(space_stats_object); } Local<Value> type_names_value; if (!ToV8Value(context, stats.type_names, isolate) .ToLocal(&type_names_value)) { return MaybeLocal<Object>(); } MaybeLocal<Value> heap_statistics_values[] = { Uint32::NewFromUnsigned( isolate, static_cast<uint32_t>(stats.committed_size_bytes)), Uint32::NewFromUnsigned(isolate, static_cast<uint32_t>(stats.resident_size_bytes)), Uint32::NewFromUnsigned(isolate, static_cast<uint32_t>(stats.used_size_bytes)), Array::New(isolate, space_statistics_array.data(), space_statistics_array.size()), type_names_value}; return NewDictionaryInstanceNullProto( context, heap_stats_tmpl, heap_statistics_values); } static void GetCppHeapStatistics(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); HandleScope handle_scope(isolate); CHECK_EQ(args.Length(), 1); CHECK(args[0]->IsInt32()); cppgc::HeapStatistics stats = isolate->GetCppHeap()->CollectStatistics( FromV8Value<cppgc::HeapStatistics::DetailLevel>(args[0])); Local<Object> result; if (!ConvertHeapStatsToJSObject(isolate, stats).ToLocal(&result)) { return; } args.GetReturnValue().Set(result); } static void BeforeGCCallback(Isolate* isolate, v8::GCType gc_type, v8::GCCallbackFlags flags, void* data) { GCProfiler* profiler = static_cast<GCProfiler*>(data); if (profiler->current_gc_type != 0) { return; } JSONWriter* writer = profiler->writer(); writer->json_start(); writer->json_keyvalue("gcType", GetGCTypeName(gc_type)); writer->json_objectstart("beforeGC"); SetHeapStatistics(writer, isolate); writer->json_objectend(); profiler->current_gc_type = gc_type; profiler->start_time = uv_hrtime(); } static void AfterGCCallback(Isolate* isolate, v8::GCType gc_type, v8::GCCallbackFlags flags, void* data) { GCProfiler* profiler = static_cast<GCProfiler*>(data); if (profiler->current_gc_type != gc_type) { return; } JSONWriter* writer = profiler->writer(); profiler->current_gc_type = 0; writer->json_keyvalue("cost", (uv_hrtime() - profiler->start_time) / 1e3); profiler->start_time = 0; writer->json_objectstart("afterGC"); SetHeapStatistics(writer, isolate); writer->json_objectend(); writer->json_end(); } GCProfiler::GCProfiler(Environment* env, Local<Object> object) : BaseObject(env, object), start_time(0), current_gc_type(0), state(GCProfilerState::kInitialized), writer_(out_stream_, false) { MakeWeak(); } // This function will be called when // 1. StartGCProfile and StopGCProfile are called and // JS land does not keep the object anymore. // 2. StartGCProfile is called then the env exits before // StopGCProfile is called. GCProfiler::~GCProfiler() { if (state != GCProfiler::GCProfilerState::kInitialized) { env()->isolate()->RemoveGCPrologueCallback(BeforeGCCallback, this); env()->isolate()->RemoveGCEpilogueCallback(AfterGCCallback, this); } } JSONWriter* GCProfiler::writer() { return &writer_; } std::ostringstream* GCProfiler::out_stream() { return &out_stream_; } void GCProfiler::New(const FunctionCallbackInfo<Value>& args) { CHECK(args.IsConstructCall()); Environment* env = Environment::GetCurrent(args); new GCProfiler(env, args.This()); } void GCProfiler::Start(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); GCProfiler* profiler; ASSIGN_OR_RETURN_UNWRAP(&profiler, args.This()); if (profiler->state != GCProfiler::GCProfilerState::kInitialized) { return; } profiler->writer()->json_start(); profiler->writer()->json_keyvalue("version", 1); uv_timeval64_t ts; if (uv_gettimeofday(&ts) == 0) { profiler->writer()->json_keyvalue("startTime", ts.tv_sec * 1000 + ts.tv_usec / 1000); } else { profiler->writer()->json_keyvalue("startTime", 0); } profiler->writer()->json_arraystart("statistics"); env->isolate()->AddGCPrologueCallback(BeforeGCCallback, static_cast<void*>(profiler)); env->isolate()->AddGCEpilogueCallback(AfterGCCallback, static_cast<void*>(profiler)); profiler->state = GCProfiler::GCProfilerState::kStarted; } void GCProfiler::Stop(const FunctionCallbackInfo<v8::Value>& args) { Environment* env = Environment::GetCurrent(args); GCProfiler* profiler; ASSIGN_OR_RETURN_UNWRAP(&profiler, args.This()); if (profiler->state != GCProfiler::GCProfilerState::kStarted) { return; } profiler->writer()->json_arrayend(); uv_timeval64_t ts; if (uv_gettimeofday(&ts) == 0) { profiler->writer()->json_keyvalue("endTime", ts.tv_sec * 1000 + ts.tv_usec / 1000); } else { profiler->writer()->json_keyvalue("endTime", 0); } profiler->writer()->json_end(); profiler->state = GCProfiler::GCProfilerState::kStopped; auto string = profiler->out_stream()->str(); Local<Value> ret; if (ToV8Value(env->context(), string, env->isolate()).ToLocal(&ret)) { args.GetReturnValue().Set(ret); } } void Initialize(Local<Object> target, Local<Value> unused, Local<Context> context, void* priv) { Realm* realm = Realm::GetCurrent(context); Environment* env = realm->env(); BindingData* const binding_data = realm->AddBindingData<BindingData>(target); if (binding_data == nullptr) return; SetMethodNoSideEffect( context, target, "cachedDataVersionTag", CachedDataVersionTag); SetMethodNoSideEffect(context, target, "setHeapSnapshotNearHeapLimit", SetHeapSnapshotNearHeapLimit); SetMethod(context, target, "updateHeapStatisticsBuffer", UpdateHeapStatisticsBuffer); SetMethod(context, target, "updateHeapCodeStatisticsBuffer", UpdateHeapCodeStatisticsBuffer); SetMethodNoSideEffect( context, target, "getCppHeapStatistics", GetCppHeapStatistics); size_t number_of_heap_spaces = env->isolate()->NumberOfHeapSpaces(); // Heap space names are extracted once and exposed to JavaScript to // avoid excessive creation of heap space name Strings. HeapSpaceStatistics s; MaybeStackBuffer<Local<Value>, 16> heap_spaces(number_of_heap_spaces); for (size_t i = 0; i < number_of_heap_spaces; i++) { env->isolate()->GetHeapSpaceStatistics(&s, i); heap_spaces[i] = String::NewFromUtf8(env->isolate(), s.space_name()) .ToLocalChecked(); } target ->Set( context, FIXED_ONE_BYTE_STRING(env->isolate(), "kHeapSpaces"), Array::New(env->isolate(), heap_spaces.out(), number_of_heap_spaces)) .Check(); SetMethod(context, target, "updateHeapSpaceStatisticsBuffer", UpdateHeapSpaceStatisticsBuffer); #define V(i, _, name) \ target \ ->Set(context, \ FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ Uint32::NewFromUnsigned(env->isolate(), i)) \ .Check(); HEAP_STATISTICS_PROPERTIES(V) HEAP_CODE_STATISTICS_PROPERTIES(V) HEAP_SPACE_STATISTICS_PROPERTIES(V) #undef V // Export symbols used by v8.setFlagsFromString() SetMethod(context, target, "setFlagsFromString", SetFlagsFromString); SetMethod(context, target, "startCpuProfile", StartCpuProfile); SetMethod(context, target, "stopCpuProfile", StopCpuProfile); SetMethod(context, target, "startHeapProfile", StartHeapProfile); SetMethod(context, target, "stopHeapProfile", StopHeapProfile); { constexpr uint32_t kSamplingNoFlags = static_cast<uint32_t>( v8::HeapProfiler::SamplingFlags::kSamplingNoFlags); constexpr uint32_t kSamplingForceGC = static_cast<uint32_t>( v8::HeapProfiler::SamplingFlags::kSamplingForceGC); constexpr uint32_t kSamplingIncludeObjectsCollectedByMajorGC = static_cast<uint32_t>(v8::HeapProfiler::SamplingFlags:: kSamplingIncludeObjectsCollectedByMajorGC); constexpr uint32_t kSamplingIncludeObjectsCollectedByMinorGC = static_cast<uint32_t>(v8::HeapProfiler::SamplingFlags:: kSamplingIncludeObjectsCollectedByMinorGC); NODE_DEFINE_CONSTANT(target, kSamplingNoFlags); NODE_DEFINE_CONSTANT(target, kSamplingForceGC); NODE_DEFINE_CONSTANT(target, kSamplingIncludeObjectsCollectedByMajorGC); NODE_DEFINE_CONSTANT(target, kSamplingIncludeObjectsCollectedByMinorGC); } // Export symbols used by v8.isStringOneByteRepresentation() SetFastMethodNoSideEffect(context, target, "isStringOneByteRepresentation", IsStringOneByteRepresentation, &fast_is_string_one_byte_representation_); SetMethodNoSideEffect(context, target, "getHashSeed", GetHashSeed); // GCProfiler Local<FunctionTemplate> t = NewFunctionTemplate(env->isolate(), GCProfiler::New); t->InstanceTemplate()->SetInternalFieldCount(GCProfiler::kInternalFieldCount); SetProtoMethod(env->isolate(), t, "start", GCProfiler::Start); SetProtoMethod(env->isolate(), t, "stop", GCProfiler::Stop); SetConstructorFunction(context, target, "GCProfiler", t); { Isolate* isolate = env->isolate(); Local<Object> detail_level = Object::New(isolate); cppgc::HeapStatistics::DetailLevel DETAILED = cppgc::HeapStatistics::DetailLevel::kDetailed; cppgc::HeapStatistics::DetailLevel BRIEF = cppgc::HeapStatistics::DetailLevel::kBrief; NODE_DEFINE_CONSTANT(detail_level, DETAILED); NODE_DEFINE_CONSTANT(detail_level, BRIEF); READONLY_PROPERTY(target, "detailLevel", detail_level); } } void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(CachedDataVersionTag); registry->Register(UpdateHeapStatisticsBuffer); registry->Register(UpdateHeapCodeStatisticsBuffer); registry->Register(UpdateHeapSpaceStatisticsBuffer); registry->Register(SetFlagsFromString); registry->Register(GetHashSeed); registry->Register(SetHeapSnapshotNearHeapLimit); registry->Register(GCProfiler::New); registry->Register(GCProfiler::Start); registry->Register(GCProfiler::Stop); registry->Register(GetCppHeapStatistics); registry->Register(IsStringOneByteRepresentation); registry->Register(fast_is_string_one_byte_representation_); registry->Register(StartCpuProfile); registry->Register(StopCpuProfile); registry->Register(StartHeapProfile); registry->Register(StopHeapProfile); } } // namespace v8_utils } // namespace node NODE_BINDING_CONTEXT_AWARE_INTERNAL(v8, node::v8_utils::Initialize) NODE_BINDING_EXTERNAL_REFERENCE(v8, node::v8_utils::RegisterExternalReferences)