1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22#include "node_v8.h" 23#include "aliased_buffer-inl.h" 24#include "base_object-inl.h" 25#include "env-inl.h" 26#include "memory_tracker-inl.h" 27#include "node.h" 28#include "node_external_reference.h" 29#include "util-inl.h" 30#include "v8.h" 31 32namespace node { 33namespace v8_utils { 34using v8::Array; 35using v8::Context; 36using v8::FunctionCallbackInfo; 37using v8::FunctionTemplate; 38using v8::HandleScope; 39using v8::HeapCodeStatistics; 40using v8::HeapSpaceStatistics; 41using v8::HeapStatistics; 42using v8::Integer; 43using v8::Isolate; 44using v8::Local; 45using v8::Object; 46using v8::ScriptCompiler; 47using v8::String; 48using v8::Uint32; 49using v8::V8; 50using v8::Value; 51 52#define HEAP_STATISTICS_PROPERTIES(V) \ 53 V(0, total_heap_size, kTotalHeapSizeIndex) \ 54 V(1, total_heap_size_executable, kTotalHeapSizeExecutableIndex) \ 55 V(2, total_physical_size, kTotalPhysicalSizeIndex) \ 56 V(3, total_available_size, kTotalAvailableSize) \ 57 V(4, used_heap_size, kUsedHeapSizeIndex) \ 58 V(5, heap_size_limit, kHeapSizeLimitIndex) \ 59 V(6, malloced_memory, kMallocedMemoryIndex) \ 60 V(7, peak_malloced_memory, kPeakMallocedMemoryIndex) \ 61 V(8, does_zap_garbage, kDoesZapGarbageIndex) \ 62 V(9, number_of_native_contexts, kNumberOfNativeContextsIndex) \ 63 V(10, number_of_detached_contexts, kNumberOfDetachedContextsIndex) \ 64 V(11, total_global_handles_size, kTotalGlobalHandlesSizeIndex) \ 65 V(12, used_global_handles_size, kUsedGlobalHandlesSizeIndex) \ 66 V(13, external_memory, kExternalMemoryIndex) 67 68#define V(a, b, c) +1 69static constexpr size_t kHeapStatisticsPropertiesCount = 70 HEAP_STATISTICS_PROPERTIES(V); 71#undef V 72 73#define HEAP_SPACE_STATISTICS_PROPERTIES(V) \ 74 V(0, space_size, kSpaceSizeIndex) \ 75 V(1, space_used_size, kSpaceUsedSizeIndex) \ 76 V(2, space_available_size, kSpaceAvailableSizeIndex) \ 77 V(3, physical_space_size, kPhysicalSpaceSizeIndex) 78 79#define V(a, b, c) +1 80static constexpr size_t kHeapSpaceStatisticsPropertiesCount = 81 HEAP_SPACE_STATISTICS_PROPERTIES(V); 82#undef V 83 84#define HEAP_CODE_STATISTICS_PROPERTIES(V) \ 85 V(0, code_and_metadata_size, kCodeAndMetadataSizeIndex) \ 86 V(1, bytecode_and_metadata_size, kBytecodeAndMetadataSizeIndex) \ 87 V(2, external_script_source_size, kExternalScriptSourceSizeIndex) \ 88 V(3, cpu_profiler_metadata_size, kCPUProfilerMetaDataSizeIndex) 89 90#define V(a, b, c) +1 91static const size_t kHeapCodeStatisticsPropertiesCount = 92 HEAP_CODE_STATISTICS_PROPERTIES(V); 93#undef V 94 95BindingData::BindingData(Realm* realm, Local<Object> obj) 96 : SnapshotableObject(realm, obj, type_int), 97 heap_statistics_buffer(realm->isolate(), kHeapStatisticsPropertiesCount), 98 heap_space_statistics_buffer(realm->isolate(), 99 kHeapSpaceStatisticsPropertiesCount), 100 heap_code_statistics_buffer(realm->isolate(), 101 kHeapCodeStatisticsPropertiesCount) { 102 Local<Context> context = realm->context(); 103 obj->Set(context, 104 FIXED_ONE_BYTE_STRING(realm->isolate(), "heapStatisticsBuffer"), 105 heap_statistics_buffer.GetJSArray()) 106 .Check(); 107 obj->Set(context, 108 FIXED_ONE_BYTE_STRING(realm->isolate(), "heapCodeStatisticsBuffer"), 109 heap_code_statistics_buffer.GetJSArray()) 110 .Check(); 111 obj->Set(context, 112 FIXED_ONE_BYTE_STRING(realm->isolate(), "heapSpaceStatisticsBuffer"), 113 heap_space_statistics_buffer.GetJSArray()) 114 .Check(); 115} 116 117bool BindingData::PrepareForSerialization(Local<Context> context, 118 v8::SnapshotCreator* creator) { 119 // We'll just re-initialize the buffers in the constructor since their 120 // contents can be thrown away once consumed in the previous call. 121 heap_statistics_buffer.Release(); 122 heap_space_statistics_buffer.Release(); 123 heap_code_statistics_buffer.Release(); 124 // Return true because we need to maintain the reference to the binding from 125 // JS land. 126 return true; 127} 128 129void BindingData::Deserialize(Local<Context> context, 130 Local<Object> holder, 131 int index, 132 InternalFieldInfoBase* info) { 133 DCHECK_EQ(index, BaseObject::kEmbedderType); 134 HandleScope scope(context->GetIsolate()); 135 Realm* realm = Realm::GetCurrent(context); 136 BindingData* binding = realm->AddBindingData<BindingData>(context, holder); 137 CHECK_NOT_NULL(binding); 138} 139 140InternalFieldInfoBase* BindingData::Serialize(int index) { 141 DCHECK_EQ(index, BaseObject::kEmbedderType); 142 InternalFieldInfo* info = 143 InternalFieldInfoBase::New<InternalFieldInfo>(type()); 144 return info; 145} 146 147void BindingData::MemoryInfo(MemoryTracker* tracker) const { 148 tracker->TrackField("heap_statistics_buffer", heap_statistics_buffer); 149 tracker->TrackField("heap_space_statistics_buffer", 150 heap_space_statistics_buffer); 151 tracker->TrackField("heap_code_statistics_buffer", 152 heap_code_statistics_buffer); 153} 154 155void CachedDataVersionTag(const FunctionCallbackInfo<Value>& args) { 156 Environment* env = Environment::GetCurrent(args); 157 Local<Integer> result = 158 Integer::NewFromUnsigned(env->isolate(), 159 ScriptCompiler::CachedDataVersionTag()); 160 args.GetReturnValue().Set(result); 161} 162 163void SetHeapSnapshotNearHeapLimit(const FunctionCallbackInfo<Value>& args) { 164 CHECK(args[0]->IsUint32()); 165 Environment* env = Environment::GetCurrent(args); 166 uint32_t limit = args[0].As<v8::Uint32>()->Value(); 167 CHECK_GT(limit, 0); 168 env->AddHeapSnapshotNearHeapLimitCallback(); 169 env->set_heap_snapshot_near_heap_limit(limit); 170} 171 172void UpdateHeapStatisticsBuffer(const FunctionCallbackInfo<Value>& args) { 173 BindingData* data = Realm::GetBindingData<BindingData>(args); 174 HeapStatistics s; 175 args.GetIsolate()->GetHeapStatistics(&s); 176 AliasedFloat64Array& buffer = data->heap_statistics_buffer; 177#define V(index, name, _) buffer[index] = static_cast<double>(s.name()); 178 HEAP_STATISTICS_PROPERTIES(V) 179#undef V 180} 181 182 183void UpdateHeapSpaceStatisticsBuffer(const FunctionCallbackInfo<Value>& args) { 184 BindingData* data = Realm::GetBindingData<BindingData>(args); 185 HeapSpaceStatistics s; 186 Isolate* const isolate = args.GetIsolate(); 187 CHECK(args[0]->IsUint32()); 188 size_t space_index = static_cast<size_t>(args[0].As<v8::Uint32>()->Value()); 189 isolate->GetHeapSpaceStatistics(&s, space_index); 190 191 AliasedFloat64Array& buffer = data->heap_space_statistics_buffer; 192 193#define V(index, name, _) buffer[index] = static_cast<double>(s.name()); 194 HEAP_SPACE_STATISTICS_PROPERTIES(V) 195#undef V 196} 197 198void UpdateHeapCodeStatisticsBuffer(const FunctionCallbackInfo<Value>& args) { 199 BindingData* data = Realm::GetBindingData<BindingData>(args); 200 HeapCodeStatistics s; 201 args.GetIsolate()->GetHeapCodeAndMetadataStatistics(&s); 202 AliasedFloat64Array& buffer = data->heap_code_statistics_buffer; 203 204#define V(index, name, _) buffer[index] = static_cast<double>(s.name()); 205 HEAP_CODE_STATISTICS_PROPERTIES(V) 206#undef V 207} 208 209 210void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) { 211 CHECK(args[0]->IsString()); 212 String::Utf8Value flags(args.GetIsolate(), args[0]); 213 V8::SetFlagsFromString(*flags, static_cast<size_t>(flags.length())); 214} 215 216static const char* GetGCTypeName(v8::GCType gc_type) { 217 switch (gc_type) { 218 case v8::GCType::kGCTypeScavenge: 219 return "Scavenge"; 220 case v8::GCType::kGCTypeMarkSweepCompact: 221 return "MarkSweepCompact"; 222 case v8::GCType::kGCTypeIncrementalMarking: 223 return "IncrementalMarking"; 224 case v8::GCType::kGCTypeProcessWeakCallbacks: 225 return "ProcessWeakCallbacks"; 226 default: 227 return "Unknown"; 228 } 229} 230 231static void SetHeapStatistics(JSONWriter* writer, Isolate* isolate) { 232 HeapStatistics heap_statistics; 233 isolate->GetHeapStatistics(&heap_statistics); 234 writer->json_objectstart("heapStatistics"); 235 writer->json_keyvalue("totalHeapSize", heap_statistics.total_heap_size()); 236 writer->json_keyvalue("totalHeapSizeExecutable", 237 heap_statistics.total_heap_size_executable()); 238 writer->json_keyvalue("totalPhysicalSize", 239 heap_statistics.total_physical_size()); 240 writer->json_keyvalue("totalAvailableSize", 241 heap_statistics.total_available_size()); 242 writer->json_keyvalue("totalGlobalHandlesSize", 243 heap_statistics.total_global_handles_size()); 244 writer->json_keyvalue("usedGlobalHandlesSize", 245 heap_statistics.used_global_handles_size()); 246 writer->json_keyvalue("usedHeapSize", heap_statistics.used_heap_size()); 247 writer->json_keyvalue("heapSizeLimit", heap_statistics.heap_size_limit()); 248 writer->json_keyvalue("mallocedMemory", heap_statistics.malloced_memory()); 249 writer->json_keyvalue("externalMemory", heap_statistics.external_memory()); 250 writer->json_keyvalue("peakMallocedMemory", 251 heap_statistics.peak_malloced_memory()); 252 writer->json_objectend(); 253 254 int space_count = isolate->NumberOfHeapSpaces(); 255 writer->json_arraystart("heapSpaceStatistics"); 256 for (int i = 0; i < space_count; i++) { 257 HeapSpaceStatistics heap_space_statistics; 258 isolate->GetHeapSpaceStatistics(&heap_space_statistics, i); 259 writer->json_start(); 260 writer->json_keyvalue("spaceName", heap_space_statistics.space_name()); 261 writer->json_keyvalue("spaceSize", heap_space_statistics.space_size()); 262 writer->json_keyvalue("spaceUsedSize", 263 heap_space_statistics.space_used_size()); 264 writer->json_keyvalue("spaceAvailableSize", 265 heap_space_statistics.space_available_size()); 266 writer->json_keyvalue("physicalSpaceSize", 267 heap_space_statistics.physical_space_size()); 268 writer->json_end(); 269 } 270 writer->json_arrayend(); 271} 272 273static void BeforeGCCallback(Isolate* isolate, 274 v8::GCType gc_type, 275 v8::GCCallbackFlags flags, 276 void* data) { 277 GCProfiler* profiler = static_cast<GCProfiler*>(data); 278 if (profiler->current_gc_type != 0) { 279 return; 280 } 281 JSONWriter* writer = profiler->writer(); 282 writer->json_start(); 283 writer->json_keyvalue("gcType", GetGCTypeName(gc_type)); 284 writer->json_objectstart("beforeGC"); 285 SetHeapStatistics(writer, isolate); 286 writer->json_objectend(); 287 profiler->current_gc_type = gc_type; 288 profiler->start_time = uv_hrtime(); 289} 290 291static void AfterGCCallback(Isolate* isolate, 292 v8::GCType gc_type, 293 v8::GCCallbackFlags flags, 294 void* data) { 295 GCProfiler* profiler = static_cast<GCProfiler*>(data); 296 if (profiler->current_gc_type != gc_type) { 297 return; 298 } 299 JSONWriter* writer = profiler->writer(); 300 profiler->current_gc_type = 0; 301 writer->json_keyvalue("cost", (uv_hrtime() - profiler->start_time) / 1e3); 302 profiler->start_time = 0; 303 writer->json_objectstart("afterGC"); 304 SetHeapStatistics(writer, isolate); 305 writer->json_objectend(); 306 writer->json_end(); 307} 308 309GCProfiler::GCProfiler(Environment* env, Local<Object> object) 310 : BaseObject(env, object), 311 start_time(0), 312 current_gc_type(0), 313 state(GCProfilerState::kInitialized), 314 writer_(out_stream_, false) { 315 MakeWeak(); 316} 317 318// This function will be called when 319// 1. StartGCProfile and StopGCProfile are called and 320// JS land does not keep the object anymore. 321// 2. StartGCProfile is called then the env exits before 322// StopGCProfile is called. 323GCProfiler::~GCProfiler() { 324 if (state != GCProfiler::GCProfilerState::kInitialized) { 325 env()->isolate()->RemoveGCPrologueCallback(BeforeGCCallback, this); 326 env()->isolate()->RemoveGCEpilogueCallback(AfterGCCallback, this); 327 } 328} 329 330JSONWriter* GCProfiler::writer() { 331 return &writer_; 332} 333 334std::ostringstream* GCProfiler::out_stream() { 335 return &out_stream_; 336} 337 338void GCProfiler::New(const FunctionCallbackInfo<Value>& args) { 339 CHECK(args.IsConstructCall()); 340 Environment* env = Environment::GetCurrent(args); 341 new GCProfiler(env, args.This()); 342} 343 344void GCProfiler::Start(const FunctionCallbackInfo<Value>& args) { 345 Environment* env = Environment::GetCurrent(args); 346 GCProfiler* profiler; 347 ASSIGN_OR_RETURN_UNWRAP(&profiler, args.Holder()); 348 if (profiler->state != GCProfiler::GCProfilerState::kInitialized) { 349 return; 350 } 351 profiler->writer()->json_start(); 352 profiler->writer()->json_keyvalue("version", 1); 353 354 uv_timeval64_t ts; 355 if (uv_gettimeofday(&ts) == 0) { 356 profiler->writer()->json_keyvalue("startTime", 357 ts.tv_sec * 1000 + ts.tv_usec / 1000); 358 } else { 359 profiler->writer()->json_keyvalue("startTime", 0); 360 } 361 profiler->writer()->json_arraystart("statistics"); 362 env->isolate()->AddGCPrologueCallback(BeforeGCCallback, 363 static_cast<void*>(profiler)); 364 env->isolate()->AddGCEpilogueCallback(AfterGCCallback, 365 static_cast<void*>(profiler)); 366 profiler->state = GCProfiler::GCProfilerState::kStarted; 367} 368 369void GCProfiler::Stop(const FunctionCallbackInfo<v8::Value>& args) { 370 Environment* env = Environment::GetCurrent(args); 371 GCProfiler* profiler; 372 ASSIGN_OR_RETURN_UNWRAP(&profiler, args.Holder()); 373 if (profiler->state != GCProfiler::GCProfilerState::kStarted) { 374 return; 375 } 376 profiler->writer()->json_arrayend(); 377 uv_timeval64_t ts; 378 if (uv_gettimeofday(&ts) == 0) { 379 profiler->writer()->json_keyvalue("endTime", 380 ts.tv_sec * 1000 + ts.tv_usec / 1000); 381 } else { 382 profiler->writer()->json_keyvalue("endTime", 0); 383 } 384 profiler->writer()->json_end(); 385 profiler->state = GCProfiler::GCProfilerState::kStopped; 386 auto string = profiler->out_stream()->str(); 387 args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(), 388 string.data(), 389 v8::NewStringType::kNormal, 390 string.size()) 391 .ToLocalChecked()); 392} 393 394void Initialize(Local<Object> target, 395 Local<Value> unused, 396 Local<Context> context, 397 void* priv) { 398 Realm* realm = Realm::GetCurrent(context); 399 Environment* env = realm->env(); 400 BindingData* const binding_data = 401 realm->AddBindingData<BindingData>(context, target); 402 if (binding_data == nullptr) return; 403 404 SetMethodNoSideEffect( 405 context, target, "cachedDataVersionTag", CachedDataVersionTag); 406 SetMethodNoSideEffect(context, 407 target, 408 "setHeapSnapshotNearHeapLimit", 409 SetHeapSnapshotNearHeapLimit); 410 SetMethod(context, 411 target, 412 "updateHeapStatisticsBuffer", 413 UpdateHeapStatisticsBuffer); 414 415 SetMethod(context, 416 target, 417 "updateHeapCodeStatisticsBuffer", 418 UpdateHeapCodeStatisticsBuffer); 419 420 size_t number_of_heap_spaces = env->isolate()->NumberOfHeapSpaces(); 421 422 // Heap space names are extracted once and exposed to JavaScript to 423 // avoid excessive creation of heap space name Strings. 424 HeapSpaceStatistics s; 425 MaybeStackBuffer<Local<Value>, 16> heap_spaces(number_of_heap_spaces); 426 for (size_t i = 0; i < number_of_heap_spaces; i++) { 427 env->isolate()->GetHeapSpaceStatistics(&s, i); 428 heap_spaces[i] = String::NewFromUtf8(env->isolate(), s.space_name()) 429 .ToLocalChecked(); 430 } 431 target 432 ->Set( 433 context, 434 FIXED_ONE_BYTE_STRING(env->isolate(), "kHeapSpaces"), 435 Array::New(env->isolate(), heap_spaces.out(), number_of_heap_spaces)) 436 .Check(); 437 438 SetMethod(context, 439 target, 440 "updateHeapSpaceStatisticsBuffer", 441 UpdateHeapSpaceStatisticsBuffer); 442 443#define V(i, _, name) \ 444 target \ 445 ->Set(context, \ 446 FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ 447 Uint32::NewFromUnsigned(env->isolate(), i)) \ 448 .Check(); 449 450 HEAP_STATISTICS_PROPERTIES(V) 451 HEAP_CODE_STATISTICS_PROPERTIES(V) 452 HEAP_SPACE_STATISTICS_PROPERTIES(V) 453#undef V 454 455 // Export symbols used by v8.setFlagsFromString() 456 SetMethod(context, target, "setFlagsFromString", SetFlagsFromString); 457 458 // GCProfiler 459 Local<FunctionTemplate> t = 460 NewFunctionTemplate(env->isolate(), GCProfiler::New); 461 t->InstanceTemplate()->SetInternalFieldCount(BaseObject::kInternalFieldCount); 462 SetProtoMethod(env->isolate(), t, "start", GCProfiler::Start); 463 SetProtoMethod(env->isolate(), t, "stop", GCProfiler::Stop); 464 SetConstructorFunction(context, target, "GCProfiler", t); 465} 466 467void RegisterExternalReferences(ExternalReferenceRegistry* registry) { 468 registry->Register(CachedDataVersionTag); 469 registry->Register(UpdateHeapStatisticsBuffer); 470 registry->Register(UpdateHeapCodeStatisticsBuffer); 471 registry->Register(UpdateHeapSpaceStatisticsBuffer); 472 registry->Register(SetFlagsFromString); 473 registry->Register(SetHeapSnapshotNearHeapLimit); 474 registry->Register(GCProfiler::New); 475 registry->Register(GCProfiler::Start); 476 registry->Register(GCProfiler::Stop); 477} 478 479} // namespace v8_utils 480} // namespace node 481 482NODE_BINDING_CONTEXT_AWARE_INTERNAL(v8, node::v8_utils::Initialize) 483NODE_BINDING_EXTERNAL_REFERENCE(v8, node::v8_utils::RegisterExternalReferences) 484