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
32 namespace node {
33 namespace v8_utils {
34 using v8::Array;
35 using v8::Context;
36 using v8::FunctionCallbackInfo;
37 using v8::FunctionTemplate;
38 using v8::HandleScope;
39 using v8::HeapCodeStatistics;
40 using v8::HeapSpaceStatistics;
41 using v8::HeapStatistics;
42 using v8::Integer;
43 using v8::Isolate;
44 using v8::Local;
45 using v8::Object;
46 using v8::ScriptCompiler;
47 using v8::String;
48 using v8::Uint32;
49 using v8::V8;
50 using 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
69 static 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
80 static 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
91 static const size_t kHeapCodeStatisticsPropertiesCount =
92 HEAP_CODE_STATISTICS_PROPERTIES(V);
93 #undef V
94
BindingData(Realm * realm,Local<Object> obj)95 BindingData::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
PrepareForSerialization(Local<Context> context,v8::SnapshotCreator * creator)117 bool 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
Deserialize(Local<Context> context,Local<Object> holder,int index,InternalFieldInfoBase * info)129 void 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
Serialize(int index)140 InternalFieldInfoBase* BindingData::Serialize(int index) {
141 DCHECK_EQ(index, BaseObject::kEmbedderType);
142 InternalFieldInfo* info =
143 InternalFieldInfoBase::New<InternalFieldInfo>(type());
144 return info;
145 }
146
MemoryInfo(MemoryTracker * tracker) const147 void 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
CachedDataVersionTag(const FunctionCallbackInfo<Value> & args)155 void 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
SetHeapSnapshotNearHeapLimit(const FunctionCallbackInfo<Value> & args)163 void 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
UpdateHeapStatisticsBuffer(const FunctionCallbackInfo<Value> & args)172 void 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
UpdateHeapSpaceStatisticsBuffer(const FunctionCallbackInfo<Value> & args)183 void 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
UpdateHeapCodeStatisticsBuffer(const FunctionCallbackInfo<Value> & args)198 void 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
SetFlagsFromString(const FunctionCallbackInfo<Value> & args)210 void 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
GetGCTypeName(v8::GCType gc_type)216 static 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
SetHeapStatistics(JSONWriter * writer,Isolate * isolate)231 static 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
BeforeGCCallback(Isolate * isolate,v8::GCType gc_type,v8::GCCallbackFlags flags,void * data)273 static 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
AfterGCCallback(Isolate * isolate,v8::GCType gc_type,v8::GCCallbackFlags flags,void * data)291 static 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
GCProfiler(Environment * env,Local<Object> object)309 GCProfiler::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.
~GCProfiler()323 GCProfiler::~GCProfiler() {
324 if (state != GCProfiler::GCProfilerState::kInitialized) {
325 env()->isolate()->RemoveGCPrologueCallback(BeforeGCCallback, this);
326 env()->isolate()->RemoveGCEpilogueCallback(AfterGCCallback, this);
327 }
328 }
329
writer()330 JSONWriter* GCProfiler::writer() {
331 return &writer_;
332 }
333
out_stream()334 std::ostringstream* GCProfiler::out_stream() {
335 return &out_stream_;
336 }
337
New(const FunctionCallbackInfo<Value> & args)338 void GCProfiler::New(const FunctionCallbackInfo<Value>& args) {
339 CHECK(args.IsConstructCall());
340 Environment* env = Environment::GetCurrent(args);
341 new GCProfiler(env, args.This());
342 }
343
Start(const FunctionCallbackInfo<Value> & args)344 void 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
Stop(const FunctionCallbackInfo<v8::Value> & args)369 void 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
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)394 void 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
RegisterExternalReferences(ExternalReferenceRegistry * registry)467 void 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
482 NODE_BINDING_CONTEXT_AWARE_INTERNAL(v8, node::v8_utils::Initialize)
483 NODE_BINDING_EXTERNAL_REFERENCE(v8, node::v8_utils::RegisterExternalReferences)
484