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