• 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 "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