• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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