xref: /third_party/node/src/node_v8.cc (revision 1cb0ef41)
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