1 #include "env.h"
2 #include "async_wrap.h"
3 #include "base_object-inl.h"
4 #include "debug_utils-inl.h"
5 #include "diagnosticfilename-inl.h"
6 #include "memory_tracker-inl.h"
7 #include "node_buffer.h"
8 #include "node_context_data.h"
9 #include "node_contextify.h"
10 #include "node_errors.h"
11 #include "node_internals.h"
12 #include "node_options-inl.h"
13 #include "node_process-inl.h"
14 #include "node_v8_platform-inl.h"
15 #include "node_worker.h"
16 #include "req_wrap-inl.h"
17 #include "stream_base.h"
18 #include "tracing/agent.h"
19 #include "tracing/traced_value.h"
20 #include "util-inl.h"
21 #include "v8-profiler.h"
22
23 #include <algorithm>
24 #include <atomic>
25 #include <cinttypes>
26 #include <cstdio>
27 #include <iostream>
28 #include <limits>
29 #include <memory>
30
31 namespace node {
32
33 using errors::TryCatchScope;
34 using v8::Array;
35 using v8::Boolean;
36 using v8::Context;
37 using v8::EmbedderGraph;
38 using v8::EscapableHandleScope;
39 using v8::Function;
40 using v8::FunctionTemplate;
41 using v8::HandleScope;
42 using v8::HeapSpaceStatistics;
43 using v8::Integer;
44 using v8::Isolate;
45 using v8::Local;
46 using v8::MaybeLocal;
47 using v8::NewStringType;
48 using v8::Number;
49 using v8::Object;
50 using v8::Private;
51 using v8::Script;
52 using v8::SnapshotCreator;
53 using v8::StackTrace;
54 using v8::String;
55 using v8::Symbol;
56 using v8::TracingController;
57 using v8::TryCatch;
58 using v8::Undefined;
59 using v8::Value;
60 using worker::Worker;
61
62 int const ContextEmbedderTag::kNodeContextTag = 0x6e6f64;
63 void* const ContextEmbedderTag::kNodeContextTagPtr = const_cast<void*>(
64 static_cast<const void*>(&ContextEmbedderTag::kNodeContextTag));
65
ResetPromiseHooks(Local<Function> init,Local<Function> before,Local<Function> after,Local<Function> resolve)66 void AsyncHooks::ResetPromiseHooks(Local<Function> init,
67 Local<Function> before,
68 Local<Function> after,
69 Local<Function> resolve) {
70 js_promise_hooks_[0].Reset(env()->isolate(), init);
71 js_promise_hooks_[1].Reset(env()->isolate(), before);
72 js_promise_hooks_[2].Reset(env()->isolate(), after);
73 js_promise_hooks_[3].Reset(env()->isolate(), resolve);
74 }
75
ResetPromiseHooks(Local<Function> init,Local<Function> before,Local<Function> after,Local<Function> resolve)76 void Environment::ResetPromiseHooks(Local<Function> init,
77 Local<Function> before,
78 Local<Function> after,
79 Local<Function> resolve) {
80 async_hooks()->ResetPromiseHooks(init, before, after, resolve);
81
82 for (auto it = contexts_.begin(); it != contexts_.end(); it++) {
83 if (it->IsEmpty()) {
84 contexts_.erase(it--);
85 continue;
86 }
87 PersistentToLocal::Weak(isolate_, *it)
88 ->SetPromiseHooks(init, before, after, resolve);
89 }
90 }
91
92 // Remember to keep this code aligned with pushAsyncContext() in JS.
push_async_context(double async_id,double trigger_async_id,Local<Object> resource)93 void AsyncHooks::push_async_context(double async_id,
94 double trigger_async_id,
95 Local<Object> resource) {
96 // Since async_hooks is experimental, do only perform the check
97 // when async_hooks is enabled.
98 if (fields_[kCheck] > 0) {
99 CHECK_GE(async_id, -1);
100 CHECK_GE(trigger_async_id, -1);
101 }
102
103 uint32_t offset = fields_[kStackLength];
104 if (offset * 2 >= async_ids_stack_.Length()) grow_async_ids_stack();
105 async_ids_stack_[2 * offset] = async_id_fields_[kExecutionAsyncId];
106 async_ids_stack_[2 * offset + 1] = async_id_fields_[kTriggerAsyncId];
107 fields_[kStackLength] += 1;
108 async_id_fields_[kExecutionAsyncId] = async_id;
109 async_id_fields_[kTriggerAsyncId] = trigger_async_id;
110
111 #ifdef DEBUG
112 for (uint32_t i = offset; i < native_execution_async_resources_.size(); i++)
113 CHECK(native_execution_async_resources_[i].IsEmpty());
114 #endif
115
116 // When this call comes from JS (as a way of increasing the stack size),
117 // `resource` will be empty, because JS caches these values anyway.
118 if (!resource.IsEmpty()) {
119 native_execution_async_resources_.resize(offset + 1);
120 // Caveat: This is a v8::Local<> assignment, we do not keep a v8::Global<>!
121 native_execution_async_resources_[offset] = resource;
122 }
123 }
124
125 // Remember to keep this code aligned with popAsyncContext() in JS.
pop_async_context(double async_id)126 bool AsyncHooks::pop_async_context(double async_id) {
127 // In case of an exception then this may have already been reset, if the
128 // stack was multiple MakeCallback()'s deep.
129 if (UNLIKELY(fields_[kStackLength] == 0)) return false;
130
131 // Ask for the async_id to be restored as a check that the stack
132 // hasn't been corrupted.
133 if (UNLIKELY(fields_[kCheck] > 0 &&
134 async_id_fields_[kExecutionAsyncId] != async_id)) {
135 FailWithCorruptedAsyncStack(async_id);
136 }
137
138 uint32_t offset = fields_[kStackLength] - 1;
139 async_id_fields_[kExecutionAsyncId] = async_ids_stack_[2 * offset];
140 async_id_fields_[kTriggerAsyncId] = async_ids_stack_[2 * offset + 1];
141 fields_[kStackLength] = offset;
142
143 if (LIKELY(offset < native_execution_async_resources_.size() &&
144 !native_execution_async_resources_[offset].IsEmpty())) {
145 #ifdef DEBUG
146 for (uint32_t i = offset + 1; i < native_execution_async_resources_.size();
147 i++) {
148 CHECK(native_execution_async_resources_[i].IsEmpty());
149 }
150 #endif
151 native_execution_async_resources_.resize(offset);
152 if (native_execution_async_resources_.size() <
153 native_execution_async_resources_.capacity() / 2 &&
154 native_execution_async_resources_.size() > 16) {
155 native_execution_async_resources_.shrink_to_fit();
156 }
157 }
158
159 if (UNLIKELY(js_execution_async_resources()->Length() > offset)) {
160 HandleScope handle_scope(env()->isolate());
161 USE(js_execution_async_resources()->Set(
162 env()->context(),
163 env()->length_string(),
164 Integer::NewFromUnsigned(env()->isolate(), offset)));
165 }
166
167 return fields_[kStackLength] > 0;
168 }
169
clear_async_id_stack()170 void AsyncHooks::clear_async_id_stack() {
171 if (!js_execution_async_resources_.IsEmpty() && env()->can_call_into_js()) {
172 Isolate* isolate = env()->isolate();
173 HandleScope handle_scope(isolate);
174 USE(PersistentToLocal::Strong(js_execution_async_resources_)
175 ->Set(env()->context(),
176 env()->length_string(),
177 Integer::NewFromUnsigned(isolate, 0)));
178 }
179
180 native_execution_async_resources_.clear();
181 native_execution_async_resources_.shrink_to_fit();
182
183 async_id_fields_[kExecutionAsyncId] = 0;
184 async_id_fields_[kTriggerAsyncId] = 0;
185 fields_[kStackLength] = 0;
186 }
187
InstallPromiseHooks(Local<Context> ctx)188 void AsyncHooks::InstallPromiseHooks(Local<Context> ctx) {
189 ctx->SetPromiseHooks(js_promise_hooks_[0].IsEmpty()
190 ? Local<Function>()
191 : PersistentToLocal::Strong(js_promise_hooks_[0]),
192 js_promise_hooks_[1].IsEmpty()
193 ? Local<Function>()
194 : PersistentToLocal::Strong(js_promise_hooks_[1]),
195 js_promise_hooks_[2].IsEmpty()
196 ? Local<Function>()
197 : PersistentToLocal::Strong(js_promise_hooks_[2]),
198 js_promise_hooks_[3].IsEmpty()
199 ? Local<Function>()
200 : PersistentToLocal::Strong(js_promise_hooks_[3]));
201 }
202
TrackContext(Local<Context> context)203 void Environment::TrackContext(Local<Context> context) {
204 size_t id = contexts_.size();
205 contexts_.resize(id + 1);
206 contexts_[id].Reset(isolate_, context);
207 contexts_[id].SetWeak();
208 }
209
UntrackContext(Local<Context> context)210 void Environment::UntrackContext(Local<Context> context) {
211 HandleScope handle_scope(isolate_);
212 contexts_.erase(std::remove_if(contexts_.begin(),
213 contexts_.end(),
214 [&](auto&& el) { return el.IsEmpty(); }),
215 contexts_.end());
216 for (auto it = contexts_.begin(); it != contexts_.end(); it++) {
217 Local<Context> saved_context = PersistentToLocal::Weak(isolate_, *it);
218 if (saved_context == context) {
219 it->Reset();
220 contexts_.erase(it);
221 break;
222 }
223 }
224 }
225
DefaultTriggerAsyncIdScope(Environment * env,double default_trigger_async_id)226 AsyncHooks::DefaultTriggerAsyncIdScope::DefaultTriggerAsyncIdScope(
227 Environment* env, double default_trigger_async_id)
228 : async_hooks_(env->async_hooks()) {
229 if (env->async_hooks()->fields()[AsyncHooks::kCheck] > 0) {
230 CHECK_GE(default_trigger_async_id, 0);
231 }
232
233 old_default_trigger_async_id_ =
234 async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId];
235 async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId] =
236 default_trigger_async_id;
237 }
238
~DefaultTriggerAsyncIdScope()239 AsyncHooks::DefaultTriggerAsyncIdScope::~DefaultTriggerAsyncIdScope() {
240 async_hooks_->async_id_fields()[AsyncHooks::kDefaultTriggerAsyncId] =
241 old_default_trigger_async_id_;
242 }
243
DefaultTriggerAsyncIdScope(AsyncWrap * async_wrap)244 AsyncHooks::DefaultTriggerAsyncIdScope::DefaultTriggerAsyncIdScope(
245 AsyncWrap* async_wrap)
246 : DefaultTriggerAsyncIdScope(async_wrap->env(),
247 async_wrap->get_async_id()) {}
248
operator <<(std::ostream & output,const std::vector<SnapshotIndex> & v)249 std::ostream& operator<<(std::ostream& output,
250 const std::vector<SnapshotIndex>& v) {
251 output << "{ ";
252 for (const SnapshotIndex i : v) {
253 output << i << ", ";
254 }
255 output << " }";
256 return output;
257 }
258
operator <<(std::ostream & output,const IsolateDataSerializeInfo & i)259 std::ostream& operator<<(std::ostream& output,
260 const IsolateDataSerializeInfo& i) {
261 output << "{\n"
262 << "// -- primitive begins --\n"
263 << i.primitive_values << ",\n"
264 << "// -- primitive ends --\n"
265 << "// -- template_values begins --\n"
266 << i.template_values << ",\n"
267 << "// -- template_values ends --\n"
268 << "}";
269 return output;
270 }
271
operator <<(std::ostream & output,const SnapshotMetadata & i)272 std::ostream& operator<<(std::ostream& output, const SnapshotMetadata& i) {
273 output << "{\n"
274 << " "
275 << (i.type == SnapshotMetadata::Type::kDefault
276 ? "SnapshotMetadata::Type::kDefault"
277 : "SnapshotMetadata::Type::kFullyCustomized")
278 << ", // type\n"
279 << " \"" << i.node_version << "\", // node_version\n"
280 << " \"" << i.node_arch << "\", // node_arch\n"
281 << " \"" << i.node_platform << "\", // node_platform\n"
282 << " " << i.v8_cache_version_tag << ", // v8_cache_version_tag\n"
283 << "}";
284 return output;
285 }
286
Serialize(SnapshotCreator * creator)287 IsolateDataSerializeInfo IsolateData::Serialize(SnapshotCreator* creator) {
288 Isolate* isolate = creator->GetIsolate();
289 IsolateDataSerializeInfo info;
290 HandleScope handle_scope(isolate);
291 // XXX(joyeecheung): technically speaking, the indexes here should be
292 // consecutive and we could just return a range instead of an array,
293 // but that's not part of the V8 API contract so we use an array
294 // just to be safe.
295
296 #define VP(PropertyName, StringValue) V(Private, PropertyName)
297 #define VY(PropertyName, StringValue) V(Symbol, PropertyName)
298 #define VS(PropertyName, StringValue) V(String, PropertyName)
299 #define V(TypeName, PropertyName) \
300 info.primitive_values.push_back( \
301 creator->AddData(PropertyName##_.Get(isolate)));
302 PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
303 PER_ISOLATE_SYMBOL_PROPERTIES(VY)
304 PER_ISOLATE_STRING_PROPERTIES(VS)
305 #undef V
306 #undef VY
307 #undef VS
308 #undef VP
309
310 for (size_t i = 0; i < AsyncWrap::PROVIDERS_LENGTH; i++)
311 info.primitive_values.push_back(creator->AddData(async_wrap_provider(i)));
312
313 uint32_t id = 0;
314 #define V(PropertyName, TypeName) \
315 do { \
316 Local<TypeName> field = PropertyName(); \
317 if (!field.IsEmpty()) { \
318 size_t index = creator->AddData(field); \
319 info.template_values.push_back({#PropertyName, id, index}); \
320 } \
321 id++; \
322 } while (0);
323 PER_ISOLATE_TEMPLATE_PROPERTIES(V)
324 #undef V
325
326 return info;
327 }
328
DeserializeProperties(const IsolateDataSerializeInfo * info)329 void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) {
330 size_t i = 0;
331 HandleScope handle_scope(isolate_);
332
333 #define VP(PropertyName, StringValue) V(Private, PropertyName)
334 #define VY(PropertyName, StringValue) V(Symbol, PropertyName)
335 #define VS(PropertyName, StringValue) V(String, PropertyName)
336 #define V(TypeName, PropertyName) \
337 do { \
338 MaybeLocal<TypeName> maybe_field = \
339 isolate_->GetDataFromSnapshotOnce<TypeName>( \
340 info->primitive_values[i++]); \
341 Local<TypeName> field; \
342 if (!maybe_field.ToLocal(&field)) { \
343 fprintf(stderr, "Failed to deserialize " #PropertyName "\n"); \
344 } \
345 PropertyName##_.Set(isolate_, field); \
346 } while (0);
347 PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
348 PER_ISOLATE_SYMBOL_PROPERTIES(VY)
349 PER_ISOLATE_STRING_PROPERTIES(VS)
350 #undef V
351 #undef VY
352 #undef VS
353 #undef VP
354
355 for (size_t j = 0; j < AsyncWrap::PROVIDERS_LENGTH; j++) {
356 MaybeLocal<String> maybe_field =
357 isolate_->GetDataFromSnapshotOnce<String>(info->primitive_values[i++]);
358 Local<String> field;
359 if (!maybe_field.ToLocal(&field)) {
360 fprintf(stderr, "Failed to deserialize AsyncWrap provider %zu\n", j);
361 }
362 async_wrap_providers_[j].Set(isolate_, field);
363 }
364
365 const std::vector<PropInfo>& values = info->template_values;
366 i = 0; // index to the array
367 uint32_t id = 0;
368 #define V(PropertyName, TypeName) \
369 do { \
370 if (values.size() > i && id == values[i].id) { \
371 const PropInfo& d = values[i]; \
372 DCHECK_EQ(d.name, #PropertyName); \
373 MaybeLocal<TypeName> maybe_field = \
374 isolate_->GetDataFromSnapshotOnce<TypeName>(d.index); \
375 Local<TypeName> field; \
376 if (!maybe_field.ToLocal(&field)) { \
377 fprintf(stderr, \
378 "Failed to deserialize isolate data template " #PropertyName \
379 "\n"); \
380 } \
381 set_##PropertyName(field); \
382 i++; \
383 } \
384 id++; \
385 } while (0);
386
387 PER_ISOLATE_TEMPLATE_PROPERTIES(V);
388 #undef V
389 }
390
CreateProperties()391 void IsolateData::CreateProperties() {
392 // Create string and private symbol properties as internalized one byte
393 // strings after the platform is properly initialized.
394 //
395 // Internalized because it makes property lookups a little faster and
396 // because the string is created in the old space straight away. It's going
397 // to end up in the old space sooner or later anyway but now it doesn't go
398 // through v8::Eternal's new space handling first.
399 //
400 // One byte because our strings are ASCII and we can safely skip V8's UTF-8
401 // decoding step.
402
403 HandleScope handle_scope(isolate_);
404
405 #define V(PropertyName, StringValue) \
406 PropertyName##_.Set( \
407 isolate_, \
408 Private::New(isolate_, \
409 String::NewFromOneByte( \
410 isolate_, \
411 reinterpret_cast<const uint8_t*>(StringValue), \
412 NewStringType::kInternalized, \
413 sizeof(StringValue) - 1) \
414 .ToLocalChecked()));
415 PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
416 #undef V
417 #define V(PropertyName, StringValue) \
418 PropertyName##_.Set( \
419 isolate_, \
420 Symbol::New(isolate_, \
421 String::NewFromOneByte( \
422 isolate_, \
423 reinterpret_cast<const uint8_t*>(StringValue), \
424 NewStringType::kInternalized, \
425 sizeof(StringValue) - 1) \
426 .ToLocalChecked()));
427 PER_ISOLATE_SYMBOL_PROPERTIES(V)
428 #undef V
429 #define V(PropertyName, StringValue) \
430 PropertyName##_.Set( \
431 isolate_, \
432 String::NewFromOneByte(isolate_, \
433 reinterpret_cast<const uint8_t*>(StringValue), \
434 NewStringType::kInternalized, \
435 sizeof(StringValue) - 1) \
436 .ToLocalChecked());
437 PER_ISOLATE_STRING_PROPERTIES(V)
438 #undef V
439
440 // Create all the provider strings that will be passed to JS. Place them in
441 // an array so the array index matches the PROVIDER id offset. This way the
442 // strings can be retrieved quickly.
443 #define V(Provider) \
444 async_wrap_providers_[AsyncWrap::PROVIDER_ ## Provider].Set( \
445 isolate_, \
446 String::NewFromOneByte( \
447 isolate_, \
448 reinterpret_cast<const uint8_t*>(#Provider), \
449 NewStringType::kInternalized, \
450 sizeof(#Provider) - 1).ToLocalChecked());
451 NODE_ASYNC_PROVIDER_TYPES(V)
452 #undef V
453
454 // TODO(legendecas): eagerly create per isolate templates.
455 Local<FunctionTemplate> templ = FunctionTemplate::New(isolate());
456 templ->InstanceTemplate()->SetInternalFieldCount(
457 BaseObject::kInternalFieldCount);
458 templ->Inherit(BaseObject::GetConstructorTemplate(this));
459 set_binding_data_ctor_template(templ);
460
461 contextify::ContextifyContext::InitializeGlobalTemplates(this);
462 }
463
IsolateData(Isolate * isolate,uv_loop_t * event_loop,MultiIsolatePlatform * platform,ArrayBufferAllocator * node_allocator,const IsolateDataSerializeInfo * isolate_data_info)464 IsolateData::IsolateData(Isolate* isolate,
465 uv_loop_t* event_loop,
466 MultiIsolatePlatform* platform,
467 ArrayBufferAllocator* node_allocator,
468 const IsolateDataSerializeInfo* isolate_data_info)
469 : isolate_(isolate),
470 event_loop_(event_loop),
471 node_allocator_(node_allocator == nullptr ? nullptr
472 : node_allocator->GetImpl()),
473 platform_(platform) {
474 options_.reset(
475 new PerIsolateOptions(*(per_process::cli_options->per_isolate)));
476
477 if (isolate_data_info == nullptr) {
478 CreateProperties();
479 } else {
480 DeserializeProperties(isolate_data_info);
481 }
482 }
483
MemoryInfo(MemoryTracker * tracker) const484 void IsolateData::MemoryInfo(MemoryTracker* tracker) const {
485 #define V(PropertyName, StringValue) \
486 tracker->TrackField(#PropertyName, PropertyName());
487 PER_ISOLATE_SYMBOL_PROPERTIES(V)
488
489 PER_ISOLATE_STRING_PROPERTIES(V)
490 #undef V
491
492 tracker->TrackField("async_wrap_providers", async_wrap_providers_);
493
494 if (node_allocator_ != nullptr) {
495 tracker->TrackFieldWithSize(
496 "node_allocator", sizeof(*node_allocator_), "NodeArrayBufferAllocator");
497 }
498 tracker->TrackFieldWithSize(
499 "platform", sizeof(*platform_), "MultiIsolatePlatform");
500 // TODO(joyeecheung): implement MemoryRetainer in the option classes.
501 }
502
UpdateTraceCategoryState()503 void TrackingTraceStateObserver::UpdateTraceCategoryState() {
504 if (!env_->owns_process_state() || !env_->can_call_into_js()) {
505 // Ideally, we’d have a consistent story that treats all threads/Environment
506 // instances equally here. However, tracing is essentially global, and this
507 // callback is called from whichever thread calls `StartTracing()` or
508 // `StopTracing()`. The only way to do this in a threadsafe fashion
509 // seems to be only tracking this from the main thread, and only allowing
510 // these state modifications from the main thread.
511 return;
512 }
513
514 if (env_->principal_realm() == nullptr) {
515 return;
516 }
517
518 bool async_hooks_enabled = (*(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
519 TRACING_CATEGORY_NODE1(async_hooks)))) != 0;
520
521 Isolate* isolate = env_->isolate();
522 HandleScope handle_scope(isolate);
523 Local<Function> cb = env_->trace_category_state_function();
524 if (cb.IsEmpty())
525 return;
526 TryCatchScope try_catch(env_);
527 try_catch.SetVerbose(true);
528 Local<Value> args[] = {Boolean::New(isolate, async_hooks_enabled)};
529 USE(cb->Call(env_->context(), Undefined(isolate), arraysize(args), args));
530 }
531
AssignToContext(Local<v8::Context> context,Realm * realm,const ContextInfo & info)532 void Environment::AssignToContext(Local<v8::Context> context,
533 Realm* realm,
534 const ContextInfo& info) {
535 context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment,
536 this);
537 context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kRealm, realm);
538 // Used to retrieve bindings
539 context->SetAlignedPointerInEmbedderData(
540 ContextEmbedderIndex::kBindingDataStoreIndex,
541 realm->binding_data_store());
542
543 // ContextifyContexts will update this to a pointer to the native object.
544 context->SetAlignedPointerInEmbedderData(
545 ContextEmbedderIndex::kContextifyContext, nullptr);
546
547 // This must not be done before other context fields are initialized.
548 ContextEmbedderTag::TagNodeContext(context);
549
550 #if HAVE_INSPECTOR
551 inspector_agent()->ContextCreated(context, info);
552 #endif // HAVE_INSPECTOR
553
554 this->async_hooks()->InstallPromiseHooks(context);
555 TrackContext(context);
556 }
557
TryLoadAddon(const char * filename,int flags,const std::function<bool (binding::DLib *)> & was_loaded)558 void Environment::TryLoadAddon(
559 const char* filename,
560 int flags,
561 const std::function<bool(binding::DLib*)>& was_loaded) {
562 loaded_addons_.emplace_back(filename, flags);
563 if (!was_loaded(&loaded_addons_.back())) {
564 loaded_addons_.pop_back();
565 }
566 }
567
GetCwd()568 std::string Environment::GetCwd() {
569 char cwd[PATH_MAX_BYTES];
570 size_t size = PATH_MAX_BYTES;
571 const int err = uv_cwd(cwd, &size);
572
573 if (err == 0) {
574 CHECK_GT(size, 0);
575 return cwd;
576 }
577
578 // This can fail if the cwd is deleted. In that case, fall back to
579 // exec_path.
580 const std::string& exec_path = exec_path_;
581 return exec_path.substr(0, exec_path.find_last_of(kPathSeparator));
582 }
583
add_refs(int64_t diff)584 void Environment::add_refs(int64_t diff) {
585 task_queues_async_refs_ += diff;
586 CHECK_GE(task_queues_async_refs_, 0);
587 if (task_queues_async_refs_ == 0)
588 uv_unref(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
589 else
590 uv_ref(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
591 }
592
allocate_managed_buffer(const size_t suggested_size)593 uv_buf_t Environment::allocate_managed_buffer(const size_t suggested_size) {
594 NoArrayBufferZeroFillScope no_zero_fill_scope(isolate_data());
595 std::unique_ptr<v8::BackingStore> bs =
596 v8::ArrayBuffer::NewBackingStore(isolate(), suggested_size);
597 uv_buf_t buf = uv_buf_init(static_cast<char*>(bs->Data()), bs->ByteLength());
598 released_allocated_buffers_.emplace(buf.base, std::move(bs));
599 return buf;
600 }
601
release_managed_buffer(const uv_buf_t & buf)602 std::unique_ptr<v8::BackingStore> Environment::release_managed_buffer(
603 const uv_buf_t& buf) {
604 std::unique_ptr<v8::BackingStore> bs;
605 if (buf.base != nullptr) {
606 auto it = released_allocated_buffers_.find(buf.base);
607 CHECK_NE(it, released_allocated_buffers_.end());
608 bs = std::move(it->second);
609 released_allocated_buffers_.erase(it);
610 }
611 return bs;
612 }
613
GetExecPath(const std::vector<std::string> & argv)614 std::string GetExecPath(const std::vector<std::string>& argv) {
615 char exec_path_buf[2 * PATH_MAX];
616 size_t exec_path_len = sizeof(exec_path_buf);
617 std::string exec_path;
618 if (uv_exepath(exec_path_buf, &exec_path_len) == 0) {
619 exec_path = std::string(exec_path_buf, exec_path_len);
620 } else if (argv.size() > 0) {
621 exec_path = argv[0];
622 }
623
624 // On OpenBSD process.execPath will be relative unless we
625 // get the full path before process.execPath is used.
626 #if defined(__OpenBSD__)
627 uv_fs_t req;
628 req.ptr = nullptr;
629 if (0 ==
630 uv_fs_realpath(nullptr, &req, exec_path.c_str(), nullptr)) {
631 CHECK_NOT_NULL(req.ptr);
632 exec_path = std::string(static_cast<char*>(req.ptr));
633 }
634 uv_fs_req_cleanup(&req);
635 #endif
636
637 return exec_path;
638 }
639
Environment(IsolateData * isolate_data,Isolate * isolate,const std::vector<std::string> & args,const std::vector<std::string> & exec_args,const EnvSerializeInfo * env_info,EnvironmentFlags::Flags flags,ThreadId thread_id)640 Environment::Environment(IsolateData* isolate_data,
641 Isolate* isolate,
642 const std::vector<std::string>& args,
643 const std::vector<std::string>& exec_args,
644 const EnvSerializeInfo* env_info,
645 EnvironmentFlags::Flags flags,
646 ThreadId thread_id)
647 : isolate_(isolate),
648 isolate_data_(isolate_data),
649 async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)),
650 immediate_info_(isolate, MAYBE_FIELD_PTR(env_info, immediate_info)),
651 timeout_info_(isolate_, 1, MAYBE_FIELD_PTR(env_info, timeout_info)),
652 tick_info_(isolate, MAYBE_FIELD_PTR(env_info, tick_info)),
653 timer_base_(uv_now(isolate_data->event_loop())),
654 exec_argv_(exec_args),
655 argv_(args),
656 exec_path_(GetExecPath(args)),
657 exiting_(isolate_, 1, MAYBE_FIELD_PTR(env_info, exiting)),
658 should_abort_on_uncaught_toggle_(
659 isolate_,
660 1,
661 MAYBE_FIELD_PTR(env_info, should_abort_on_uncaught_toggle)),
662 stream_base_state_(isolate_,
663 StreamBase::kNumStreamBaseStateFields,
664 MAYBE_FIELD_PTR(env_info, stream_base_state)),
665 environment_start_time_(PERFORMANCE_NOW()),
666 flags_(flags),
667 thread_id_(thread_id.id == static_cast<uint64_t>(-1)
668 ? AllocateEnvironmentThreadId().id
669 : thread_id.id) {
670 // We'll be creating new objects so make sure we've entered the context.
671 HandleScope handle_scope(isolate);
672
673 // Set some flags if only kDefaultFlags was passed. This can make API version
674 // transitions easier for embedders.
675 if (flags_ & EnvironmentFlags::kDefaultFlags) {
676 flags_ = flags_ |
677 EnvironmentFlags::kOwnsProcessState |
678 EnvironmentFlags::kOwnsInspector;
679 }
680
681 set_env_vars(per_process::system_environment);
682 enabled_debug_list_.Parse(env_vars(), isolate);
683
684 // We create new copies of the per-Environment option sets, so that it is
685 // easier to modify them after Environment creation. The defaults are
686 // part of the per-Isolate option set, for which in turn the defaults are
687 // part of the per-process option set.
688 options_ = std::make_shared<EnvironmentOptions>(
689 *isolate_data->options()->per_env);
690 inspector_host_port_ = std::make_shared<ExclusiveAccess<HostPort>>(
691 options_->debug_options().host_port);
692
693 heap_snapshot_near_heap_limit_ =
694 static_cast<uint32_t>(options_->heap_snapshot_near_heap_limit);
695
696 if (!(flags_ & EnvironmentFlags::kOwnsProcessState)) {
697 set_abort_on_uncaught_exception(false);
698 }
699
700 #if HAVE_INSPECTOR
701 // We can only create the inspector agent after having cloned the options.
702 inspector_agent_ = std::make_unique<inspector::Agent>(this);
703 #endif
704
705 if (tracing::AgentWriterHandle* writer = GetTracingAgentWriter()) {
706 trace_state_observer_ = std::make_unique<TrackingTraceStateObserver>(this);
707 if (TracingController* tracing_controller = writer->GetTracingController())
708 tracing_controller->AddTraceStateObserver(trace_state_observer_.get());
709 }
710
711 destroy_async_id_list_.reserve(512);
712
713 performance_state_ = std::make_unique<performance::PerformanceState>(
714 isolate, MAYBE_FIELD_PTR(env_info, performance_state));
715
716 if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
717 TRACING_CATEGORY_NODE1(environment)) != 0) {
718 auto traced_value = tracing::TracedValue::Create();
719 traced_value->BeginArray("args");
720 for (const std::string& arg : args) traced_value->AppendString(arg);
721 traced_value->EndArray();
722 traced_value->BeginArray("exec_args");
723 for (const std::string& arg : exec_args) traced_value->AppendString(arg);
724 traced_value->EndArray();
725 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(environment),
726 "Environment",
727 this,
728 "args",
729 std::move(traced_value));
730 }
731 }
732
Environment(IsolateData * isolate_data,Local<Context> context,const std::vector<std::string> & args,const std::vector<std::string> & exec_args,const EnvSerializeInfo * env_info,EnvironmentFlags::Flags flags,ThreadId thread_id)733 Environment::Environment(IsolateData* isolate_data,
734 Local<Context> context,
735 const std::vector<std::string>& args,
736 const std::vector<std::string>& exec_args,
737 const EnvSerializeInfo* env_info,
738 EnvironmentFlags::Flags flags,
739 ThreadId thread_id)
740 : Environment(isolate_data,
741 context->GetIsolate(),
742 args,
743 exec_args,
744 env_info,
745 flags,
746 thread_id) {
747 InitializeMainContext(context, env_info);
748 }
749
InitializeMainContext(Local<Context> context,const EnvSerializeInfo * env_info)750 void Environment::InitializeMainContext(Local<Context> context,
751 const EnvSerializeInfo* env_info) {
752 principal_realm_ = std::make_unique<Realm>(
753 this, context, MAYBE_FIELD_PTR(env_info, principal_realm));
754 AssignToContext(context, principal_realm_.get(), ContextInfo(""));
755 if (env_info != nullptr) {
756 DeserializeProperties(env_info);
757 }
758
759 if (!options_->force_async_hooks_checks) {
760 async_hooks_.no_force_checks();
761 }
762
763 // By default, always abort when --abort-on-uncaught-exception was passed.
764 should_abort_on_uncaught_toggle_[0] = 1;
765
766 // The process is not exiting by default.
767 set_exiting(false);
768
769 performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_ENVIRONMENT,
770 environment_start_time_);
771 performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_NODE_START,
772 per_process::node_start_time);
773
774 if (per_process::v8_initialized) {
775 performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_V8_START,
776 performance::performance_v8_start);
777 }
778 }
779
~Environment()780 Environment::~Environment() {
781 HandleScope handle_scope(isolate());
782 Local<Context> ctx = context();
783
784 if (Environment** interrupt_data = interrupt_data_.load()) {
785 // There are pending RequestInterrupt() callbacks. Tell them not to run,
786 // then force V8 to run interrupts by compiling and running an empty script
787 // so as not to leak memory.
788 *interrupt_data = nullptr;
789
790 Isolate::AllowJavascriptExecutionScope allow_js_here(isolate());
791 TryCatch try_catch(isolate());
792 Context::Scope context_scope(ctx);
793
794 #ifdef DEBUG
795 bool consistency_check = false;
796 isolate()->RequestInterrupt([](Isolate*, void* data) {
797 *static_cast<bool*>(data) = true;
798 }, &consistency_check);
799 #endif
800
801 Local<Script> script;
802 if (Script::Compile(ctx, String::Empty(isolate())).ToLocal(&script))
803 USE(script->Run(ctx));
804
805 DCHECK(consistency_check);
806 }
807
808 // FreeEnvironment() should have set this.
809 CHECK(is_stopping());
810
811 if (heapsnapshot_near_heap_limit_callback_added_) {
812 RemoveHeapSnapshotNearHeapLimitCallback(0);
813 }
814
815 isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback(
816 BuildEmbedderGraph, this);
817
818 #if HAVE_INSPECTOR
819 // Destroy inspector agent before erasing the context. The inspector
820 // destructor depends on the context still being accessible.
821 inspector_agent_.reset();
822 #endif
823
824 ctx->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment,
825 nullptr);
826 ctx->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kRealm, nullptr);
827
828 if (trace_state_observer_) {
829 tracing::AgentWriterHandle* writer = GetTracingAgentWriter();
830 CHECK_NOT_NULL(writer);
831 if (TracingController* tracing_controller = writer->GetTracingController())
832 tracing_controller->RemoveTraceStateObserver(trace_state_observer_.get());
833 }
834
835 TRACE_EVENT_NESTABLE_ASYNC_END0(
836 TRACING_CATEGORY_NODE1(environment), "Environment", this);
837
838 // Do not unload addons on the main thread. Some addons need to retain memory
839 // beyond the Environment's lifetime, and unloading them early would break
840 // them; with Worker threads, we have the opportunity to be stricter.
841 // Also, since the main thread usually stops just before the process exits,
842 // this is far less relevant here.
843 if (!is_main_thread()) {
844 // Dereference all addons that were loaded into this environment.
845 for (binding::DLib& addon : loaded_addons_) {
846 addon.Close();
847 }
848 }
849 }
850
InitializeLibuv()851 void Environment::InitializeLibuv() {
852 HandleScope handle_scope(isolate());
853 Context::Scope context_scope(context());
854
855 CHECK_EQ(0, uv_timer_init(event_loop(), timer_handle()));
856 uv_unref(reinterpret_cast<uv_handle_t*>(timer_handle()));
857
858 CHECK_EQ(0, uv_check_init(event_loop(), immediate_check_handle()));
859 uv_unref(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));
860
861 CHECK_EQ(0, uv_idle_init(event_loop(), immediate_idle_handle()));
862
863 CHECK_EQ(0, uv_check_start(immediate_check_handle(), CheckImmediate));
864
865 // Inform V8's CPU profiler when we're idle. The profiler is sampling-based
866 // but not all samples are created equal; mark the wall clock time spent in
867 // epoll_wait() and friends so profiling tools can filter it out. The samples
868 // still end up in v8.log but with state=IDLE rather than state=EXTERNAL.
869 CHECK_EQ(0, uv_prepare_init(event_loop(), &idle_prepare_handle_));
870 CHECK_EQ(0, uv_check_init(event_loop(), &idle_check_handle_));
871
872 CHECK_EQ(0, uv_async_init(
873 event_loop(),
874 &task_queues_async_,
875 [](uv_async_t* async) {
876 Environment* env = ContainerOf(
877 &Environment::task_queues_async_, async);
878 HandleScope handle_scope(env->isolate());
879 Context::Scope context_scope(env->context());
880 env->RunAndClearNativeImmediates();
881 }));
882 uv_unref(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
883 uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
884 uv_unref(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
885
886 {
887 Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
888 task_queues_async_initialized_ = true;
889 if (native_immediates_threadsafe_.size() > 0 ||
890 native_immediates_interrupts_.size() > 0) {
891 uv_async_send(&task_queues_async_);
892 }
893 }
894
895 // Register clean-up cb to be called to clean up the handles
896 // when the environment is freed, note that they are not cleaned in
897 // the one environment per process setup, but will be called in
898 // FreeEnvironment.
899 RegisterHandleCleanups();
900
901 StartProfilerIdleNotifier();
902 }
903
ExitEnv(StopFlags::Flags flags)904 void Environment::ExitEnv(StopFlags::Flags flags) {
905 // Should not access non-thread-safe methods here.
906 set_stopping(true);
907 if ((flags & StopFlags::kDoNotTerminateIsolate) == 0)
908 isolate_->TerminateExecution();
909 SetImmediateThreadsafe([](Environment* env) {
910 env->set_can_call_into_js(false);
911 uv_stop(env->event_loop());
912 });
913 }
914
RegisterHandleCleanups()915 void Environment::RegisterHandleCleanups() {
916 HandleCleanupCb close_and_finish = [](Environment* env, uv_handle_t* handle,
917 void* arg) {
918 handle->data = env;
919
920 env->CloseHandle(handle, [](uv_handle_t* handle) {
921 #ifdef DEBUG
922 memset(handle, 0xab, uv_handle_size(handle->type));
923 #endif
924 });
925 };
926
927 auto register_handle = [&](uv_handle_t* handle) {
928 RegisterHandleCleanup(handle, close_and_finish, nullptr);
929 };
930 register_handle(reinterpret_cast<uv_handle_t*>(timer_handle()));
931 register_handle(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));
932 register_handle(reinterpret_cast<uv_handle_t*>(immediate_idle_handle()));
933 register_handle(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
934 register_handle(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
935 register_handle(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
936 }
937
CleanupHandles()938 void Environment::CleanupHandles() {
939 {
940 Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
941 task_queues_async_initialized_ = false;
942 }
943
944 Isolate::DisallowJavascriptExecutionScope disallow_js(isolate(),
945 Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
946
947 RunAndClearNativeImmediates(true /* skip unrefed SetImmediate()s */);
948
949 for (ReqWrapBase* request : req_wrap_queue_)
950 request->Cancel();
951
952 for (HandleWrap* handle : handle_wrap_queue_)
953 handle->Close();
954
955 for (HandleCleanup& hc : handle_cleanup_queue_)
956 hc.cb_(this, hc.handle_, hc.arg_);
957 handle_cleanup_queue_.clear();
958
959 while (handle_cleanup_waiting_ != 0 ||
960 request_waiting_ != 0 ||
961 !handle_wrap_queue_.IsEmpty()) {
962 uv_run(event_loop(), UV_RUN_ONCE);
963 }
964 }
965
StartProfilerIdleNotifier()966 void Environment::StartProfilerIdleNotifier() {
967 uv_prepare_start(&idle_prepare_handle_, [](uv_prepare_t* handle) {
968 Environment* env = ContainerOf(&Environment::idle_prepare_handle_, handle);
969 env->isolate()->SetIdle(true);
970 });
971 uv_check_start(&idle_check_handle_, [](uv_check_t* handle) {
972 Environment* env = ContainerOf(&Environment::idle_check_handle_, handle);
973 env->isolate()->SetIdle(false);
974 });
975 }
976
PrintSyncTrace() const977 void Environment::PrintSyncTrace() const {
978 if (!trace_sync_io_) return;
979
980 HandleScope handle_scope(isolate());
981
982 fprintf(
983 stderr, "(node:%d) WARNING: Detected use of sync API\n", uv_os_getpid());
984 PrintStackTrace(isolate(),
985 StackTrace::CurrentStackTrace(
986 isolate(), stack_trace_limit(), StackTrace::kDetailed));
987 }
988
RunSnapshotSerializeCallback() const989 MaybeLocal<Value> Environment::RunSnapshotSerializeCallback() const {
990 EscapableHandleScope handle_scope(isolate());
991 if (!snapshot_serialize_callback().IsEmpty()) {
992 Context::Scope context_scope(context());
993 return handle_scope.EscapeMaybe(snapshot_serialize_callback()->Call(
994 context(), v8::Undefined(isolate()), 0, nullptr));
995 }
996 return handle_scope.Escape(Undefined(isolate()));
997 }
998
RunSnapshotDeserializeMain() const999 MaybeLocal<Value> Environment::RunSnapshotDeserializeMain() const {
1000 EscapableHandleScope handle_scope(isolate());
1001 if (!snapshot_deserialize_main().IsEmpty()) {
1002 Context::Scope context_scope(context());
1003 return handle_scope.EscapeMaybe(snapshot_deserialize_main()->Call(
1004 context(), v8::Undefined(isolate()), 0, nullptr));
1005 }
1006 return handle_scope.Escape(Undefined(isolate()));
1007 }
1008
RunCleanup()1009 void Environment::RunCleanup() {
1010 started_cleanup_ = true;
1011 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "RunCleanup");
1012 // Only BaseObject's cleanups are registered as per-realm cleanup hooks now.
1013 // Defer the BaseObject cleanup after handles are cleaned up.
1014 CleanupHandles();
1015
1016 while (!cleanup_queue_.empty() || principal_realm_->HasCleanupHooks() ||
1017 native_immediates_.size() > 0 ||
1018 native_immediates_threadsafe_.size() > 0 ||
1019 native_immediates_interrupts_.size() > 0) {
1020 // TODO(legendecas): cleanup handles in per-realm cleanup hooks as well.
1021 principal_realm_->RunCleanup();
1022 cleanup_queue_.Drain();
1023 CleanupHandles();
1024 }
1025
1026 for (const int fd : unmanaged_fds_) {
1027 uv_fs_t close_req;
1028 uv_fs_close(nullptr, &close_req, fd, nullptr);
1029 uv_fs_req_cleanup(&close_req);
1030 }
1031 }
1032
RunAtExitCallbacks()1033 void Environment::RunAtExitCallbacks() {
1034 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "AtExit");
1035 for (ExitCallback at_exit : at_exit_functions_) {
1036 at_exit.cb_(at_exit.arg_);
1037 }
1038 at_exit_functions_.clear();
1039 }
1040
AtExit(void (* cb)(void * arg),void * arg)1041 void Environment::AtExit(void (*cb)(void* arg), void* arg) {
1042 at_exit_functions_.push_front(ExitCallback{cb, arg});
1043 }
1044
RunAndClearInterrupts()1045 void Environment::RunAndClearInterrupts() {
1046 while (native_immediates_interrupts_.size() > 0) {
1047 NativeImmediateQueue queue;
1048 {
1049 Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
1050 queue.ConcatMove(std::move(native_immediates_interrupts_));
1051 }
1052 DebugSealHandleScope seal_handle_scope(isolate());
1053
1054 while (auto head = queue.Shift())
1055 head->Call(this);
1056 }
1057 }
1058
RunAndClearNativeImmediates(bool only_refed)1059 void Environment::RunAndClearNativeImmediates(bool only_refed) {
1060 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment),
1061 "RunAndClearNativeImmediates");
1062 HandleScope handle_scope(isolate_);
1063 // In case the Isolate is no longer accessible just use an empty Local. This
1064 // is not an issue for InternalCallbackScope as this case is already handled
1065 // in its constructor but we avoid calls into v8 which can crash the process
1066 // in debug builds.
1067 Local<Object> obj =
1068 can_call_into_js() ? Object::New(isolate_) : Local<Object>();
1069 InternalCallbackScope cb_scope(this, obj, {0, 0});
1070
1071 size_t ref_count = 0;
1072
1073 // Handle interrupts first. These functions are not allowed to throw
1074 // exceptions, so we do not need to handle that.
1075 RunAndClearInterrupts();
1076
1077 auto drain_list = [&](NativeImmediateQueue* queue) {
1078 TryCatchScope try_catch(this);
1079 DebugSealHandleScope seal_handle_scope(isolate());
1080 while (auto head = queue->Shift()) {
1081 bool is_refed = head->flags() & CallbackFlags::kRefed;
1082 if (is_refed)
1083 ref_count++;
1084
1085 if (is_refed || !only_refed)
1086 head->Call(this);
1087
1088 head.reset(); // Destroy now so that this is also observed by try_catch.
1089
1090 if (UNLIKELY(try_catch.HasCaught())) {
1091 if (!try_catch.HasTerminated() && can_call_into_js())
1092 errors::TriggerUncaughtException(isolate(), try_catch);
1093
1094 return true;
1095 }
1096 }
1097 return false;
1098 };
1099 while (drain_list(&native_immediates_)) {}
1100
1101 immediate_info()->ref_count_dec(ref_count);
1102
1103 if (immediate_info()->ref_count() == 0)
1104 ToggleImmediateRef(false);
1105
1106 // It is safe to check .size() first, because there is a causal relationship
1107 // between pushes to the threadsafe immediate list and this function being
1108 // called. For the common case, it's worth checking the size first before
1109 // establishing a mutex lock.
1110 // This is intentionally placed after the `ref_count` handling, because when
1111 // refed threadsafe immediates are created, they are not counted towards the
1112 // count in immediate_info() either.
1113 NativeImmediateQueue threadsafe_immediates;
1114 if (native_immediates_threadsafe_.size() > 0) {
1115 Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
1116 threadsafe_immediates.ConcatMove(std::move(native_immediates_threadsafe_));
1117 }
1118 while (drain_list(&threadsafe_immediates)) {}
1119 }
1120
RequestInterruptFromV8()1121 void Environment::RequestInterruptFromV8() {
1122 // The Isolate may outlive the Environment, so some logic to handle the
1123 // situation in which the Environment is destroyed before the handler runs
1124 // is required.
1125
1126 // We allocate a new pointer to a pointer to this Environment instance, and
1127 // try to set it as interrupt_data_. If interrupt_data_ was already set, then
1128 // callbacks are already scheduled to run and we can delete our own pointer
1129 // and just return. If it was nullptr previously, the Environment** is stored;
1130 // ~Environment sets the Environment* contained in it to nullptr, so that
1131 // the callback can check whether ~Environment has already run and it is thus
1132 // not safe to access the Environment instance itself.
1133 Environment** interrupt_data = new Environment*(this);
1134 Environment** dummy = nullptr;
1135 if (!interrupt_data_.compare_exchange_strong(dummy, interrupt_data)) {
1136 delete interrupt_data;
1137 return; // Already scheduled.
1138 }
1139
1140 isolate()->RequestInterrupt([](Isolate* isolate, void* data) {
1141 std::unique_ptr<Environment*> env_ptr { static_cast<Environment**>(data) };
1142 Environment* env = *env_ptr;
1143 if (env == nullptr) {
1144 // The Environment has already been destroyed. That should be okay; any
1145 // callback added before the Environment shuts down would have been
1146 // handled during cleanup.
1147 return;
1148 }
1149 env->interrupt_data_.store(nullptr);
1150 env->RunAndClearInterrupts();
1151 }, interrupt_data);
1152 }
1153
ScheduleTimer(int64_t duration_ms)1154 void Environment::ScheduleTimer(int64_t duration_ms) {
1155 if (started_cleanup_) return;
1156 uv_timer_start(timer_handle(), RunTimers, duration_ms, 0);
1157 }
1158
ToggleTimerRef(bool ref)1159 void Environment::ToggleTimerRef(bool ref) {
1160 if (started_cleanup_) return;
1161
1162 if (ref) {
1163 uv_ref(reinterpret_cast<uv_handle_t*>(timer_handle()));
1164 } else {
1165 uv_unref(reinterpret_cast<uv_handle_t*>(timer_handle()));
1166 }
1167 }
1168
RunTimers(uv_timer_t * handle)1169 void Environment::RunTimers(uv_timer_t* handle) {
1170 Environment* env = Environment::from_timer_handle(handle);
1171 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "RunTimers");
1172
1173 if (!env->can_call_into_js())
1174 return;
1175
1176 HandleScope handle_scope(env->isolate());
1177 Context::Scope context_scope(env->context());
1178
1179 Local<Object> process = env->process_object();
1180 InternalCallbackScope scope(env, process, {0, 0});
1181
1182 Local<Function> cb = env->timers_callback_function();
1183 MaybeLocal<Value> ret;
1184 Local<Value> arg = env->GetNow();
1185 // This code will loop until all currently due timers will process. It is
1186 // impossible for us to end up in an infinite loop due to how the JS-side
1187 // is structured.
1188 do {
1189 TryCatchScope try_catch(env);
1190 try_catch.SetVerbose(true);
1191 ret = cb->Call(env->context(), process, 1, &arg);
1192 } while (ret.IsEmpty() && env->can_call_into_js());
1193
1194 // NOTE(apapirovski): If it ever becomes possible that `call_into_js` above
1195 // is reset back to `true` after being previously set to `false` then this
1196 // code becomes invalid and needs to be rewritten. Otherwise catastrophic
1197 // timers corruption will occur and all timers behaviour will become
1198 // entirely unpredictable.
1199 if (ret.IsEmpty())
1200 return;
1201
1202 // To allow for less JS-C++ boundary crossing, the value returned from JS
1203 // serves a few purposes:
1204 // 1. If it's 0, no more timers exist and the handle should be unrefed
1205 // 2. If it's > 0, the value represents the next timer's expiry and there
1206 // is at least one timer remaining that is refed.
1207 // 3. If it's < 0, the absolute value represents the next timer's expiry
1208 // and there are no timers that are refed.
1209 int64_t expiry_ms =
1210 ret.ToLocalChecked()->IntegerValue(env->context()).FromJust();
1211
1212 uv_handle_t* h = reinterpret_cast<uv_handle_t*>(handle);
1213
1214 if (expiry_ms != 0) {
1215 int64_t duration_ms =
1216 llabs(expiry_ms) - (uv_now(env->event_loop()) - env->timer_base());
1217
1218 env->ScheduleTimer(duration_ms > 0 ? duration_ms : 1);
1219
1220 if (expiry_ms > 0)
1221 uv_ref(h);
1222 else
1223 uv_unref(h);
1224 } else {
1225 uv_unref(h);
1226 }
1227 }
1228
1229
CheckImmediate(uv_check_t * handle)1230 void Environment::CheckImmediate(uv_check_t* handle) {
1231 Environment* env = Environment::from_immediate_check_handle(handle);
1232 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "CheckImmediate");
1233
1234 HandleScope scope(env->isolate());
1235 Context::Scope context_scope(env->context());
1236
1237 env->RunAndClearNativeImmediates();
1238
1239 if (env->immediate_info()->count() == 0 || !env->can_call_into_js())
1240 return;
1241
1242 do {
1243 MakeCallback(env->isolate(),
1244 env->process_object(),
1245 env->immediate_callback_function(),
1246 0,
1247 nullptr,
1248 {0, 0}).ToLocalChecked();
1249 } while (env->immediate_info()->has_outstanding() && env->can_call_into_js());
1250
1251 if (env->immediate_info()->ref_count() == 0)
1252 env->ToggleImmediateRef(false);
1253 }
1254
ToggleImmediateRef(bool ref)1255 void Environment::ToggleImmediateRef(bool ref) {
1256 if (started_cleanup_) return;
1257
1258 if (ref) {
1259 // Idle handle is needed only to stop the event loop from blocking in poll.
1260 uv_idle_start(immediate_idle_handle(), [](uv_idle_t*){ });
1261 } else {
1262 uv_idle_stop(immediate_idle_handle());
1263 }
1264 }
1265
1266
GetNow()1267 Local<Value> Environment::GetNow() {
1268 uv_update_time(event_loop());
1269 uint64_t now = uv_now(event_loop());
1270 CHECK_GE(now, timer_base());
1271 now -= timer_base();
1272 if (now <= 0xffffffff)
1273 return Integer::NewFromUnsigned(isolate(), static_cast<uint32_t>(now));
1274 else
1275 return Number::New(isolate(), static_cast<double>(now));
1276 }
1277
CollectExceptionInfo(Environment * env,Local<Object> obj,int errorno,const char * err_string,const char * syscall,const char * message,const char * path,const char * dest)1278 void CollectExceptionInfo(Environment* env,
1279 Local<Object> obj,
1280 int errorno,
1281 const char* err_string,
1282 const char* syscall,
1283 const char* message,
1284 const char* path,
1285 const char* dest) {
1286 obj->Set(env->context(),
1287 env->errno_string(),
1288 Integer::New(env->isolate(), errorno)).Check();
1289
1290 obj->Set(env->context(), env->code_string(),
1291 OneByteString(env->isolate(), err_string)).Check();
1292
1293 if (message != nullptr) {
1294 obj->Set(env->context(), env->message_string(),
1295 OneByteString(env->isolate(), message)).Check();
1296 }
1297
1298 Local<Value> path_buffer;
1299 if (path != nullptr) {
1300 path_buffer =
1301 Buffer::Copy(env->isolate(), path, strlen(path)).ToLocalChecked();
1302 obj->Set(env->context(), env->path_string(), path_buffer).Check();
1303 }
1304
1305 Local<Value> dest_buffer;
1306 if (dest != nullptr) {
1307 dest_buffer =
1308 Buffer::Copy(env->isolate(), dest, strlen(dest)).ToLocalChecked();
1309 obj->Set(env->context(), env->dest_string(), dest_buffer).Check();
1310 }
1311
1312 if (syscall != nullptr) {
1313 obj->Set(env->context(), env->syscall_string(),
1314 OneByteString(env->isolate(), syscall)).Check();
1315 }
1316 }
1317
CollectUVExceptionInfo(Local<Value> object,int errorno,const char * syscall,const char * message,const char * path,const char * dest)1318 void Environment::CollectUVExceptionInfo(Local<Value> object,
1319 int errorno,
1320 const char* syscall,
1321 const char* message,
1322 const char* path,
1323 const char* dest) {
1324 if (!object->IsObject() || errorno == 0)
1325 return;
1326
1327 Local<Object> obj = object.As<Object>();
1328 const char* err_string = uv_err_name(errorno);
1329
1330 if (message == nullptr || message[0] == '\0') {
1331 message = uv_strerror(errorno);
1332 }
1333
1334 node::CollectExceptionInfo(this, obj, errorno, err_string,
1335 syscall, message, path, dest);
1336 }
1337
ImmediateInfo(Isolate * isolate,const SerializeInfo * info)1338 ImmediateInfo::ImmediateInfo(Isolate* isolate, const SerializeInfo* info)
1339 : fields_(isolate, kFieldsCount, MAYBE_FIELD_PTR(info, fields)) {}
1340
Serialize(Local<Context> context,SnapshotCreator * creator)1341 ImmediateInfo::SerializeInfo ImmediateInfo::Serialize(
1342 Local<Context> context, SnapshotCreator* creator) {
1343 return {fields_.Serialize(context, creator)};
1344 }
1345
Deserialize(Local<Context> context)1346 void ImmediateInfo::Deserialize(Local<Context> context) {
1347 fields_.Deserialize(context);
1348 }
1349
operator <<(std::ostream & output,const ImmediateInfo::SerializeInfo & i)1350 std::ostream& operator<<(std::ostream& output,
1351 const ImmediateInfo::SerializeInfo& i) {
1352 output << "{ " << i.fields << " }";
1353 return output;
1354 }
1355
MemoryInfo(MemoryTracker * tracker) const1356 void ImmediateInfo::MemoryInfo(MemoryTracker* tracker) const {
1357 tracker->TrackField("fields", fields_);
1358 }
1359
Serialize(Local<Context> context,SnapshotCreator * creator)1360 TickInfo::SerializeInfo TickInfo::Serialize(Local<Context> context,
1361 SnapshotCreator* creator) {
1362 return {fields_.Serialize(context, creator)};
1363 }
1364
Deserialize(Local<Context> context)1365 void TickInfo::Deserialize(Local<Context> context) {
1366 fields_.Deserialize(context);
1367 }
1368
operator <<(std::ostream & output,const TickInfo::SerializeInfo & i)1369 std::ostream& operator<<(std::ostream& output,
1370 const TickInfo::SerializeInfo& i) {
1371 output << "{ " << i.fields << " }";
1372 return output;
1373 }
1374
MemoryInfo(MemoryTracker * tracker) const1375 void TickInfo::MemoryInfo(MemoryTracker* tracker) const {
1376 tracker->TrackField("fields", fields_);
1377 }
1378
TickInfo(Isolate * isolate,const SerializeInfo * info)1379 TickInfo::TickInfo(Isolate* isolate, const SerializeInfo* info)
1380 : fields_(
1381 isolate, kFieldsCount, info == nullptr ? nullptr : &(info->fields)) {}
1382
AsyncHooks(Isolate * isolate,const SerializeInfo * info)1383 AsyncHooks::AsyncHooks(Isolate* isolate, const SerializeInfo* info)
1384 : async_ids_stack_(isolate, 16 * 2, MAYBE_FIELD_PTR(info, async_ids_stack)),
1385 fields_(isolate, kFieldsCount, MAYBE_FIELD_PTR(info, fields)),
1386 async_id_fields_(
1387 isolate, kUidFieldsCount, MAYBE_FIELD_PTR(info, async_id_fields)),
1388 info_(info) {
1389 HandleScope handle_scope(isolate);
1390 if (info == nullptr) {
1391 clear_async_id_stack();
1392
1393 // Always perform async_hooks checks, not just when async_hooks is enabled.
1394 // TODO(AndreasMadsen): Consider removing this for LTS releases.
1395 // See discussion in https://github.com/nodejs/node/pull/15454
1396 // When removing this, do it by reverting the commit. Otherwise the test
1397 // and flag changes won't be included.
1398 fields_[kCheck] = 1;
1399
1400 // kDefaultTriggerAsyncId should be -1, this indicates that there is no
1401 // specified default value and it should fallback to the executionAsyncId.
1402 // 0 is not used as the magic value, because that indicates a missing
1403 // context which is different from a default context.
1404 async_id_fields_[AsyncHooks::kDefaultTriggerAsyncId] = -1;
1405
1406 // kAsyncIdCounter should start at 1 because that'll be the id the execution
1407 // context during bootstrap (code that runs before entering uv_run()).
1408 async_id_fields_[AsyncHooks::kAsyncIdCounter] = 1;
1409 }
1410 }
1411
Deserialize(Local<Context> context)1412 void AsyncHooks::Deserialize(Local<Context> context) {
1413 async_ids_stack_.Deserialize(context);
1414 fields_.Deserialize(context);
1415 async_id_fields_.Deserialize(context);
1416
1417 Local<Array> js_execution_async_resources;
1418 if (info_->js_execution_async_resources != 0) {
1419 js_execution_async_resources =
1420 context->GetDataFromSnapshotOnce<Array>(
1421 info_->js_execution_async_resources).ToLocalChecked();
1422 } else {
1423 js_execution_async_resources = Array::New(context->GetIsolate());
1424 }
1425 js_execution_async_resources_.Reset(
1426 context->GetIsolate(), js_execution_async_resources);
1427
1428 // The native_execution_async_resources_ field requires v8::Local<> instances
1429 // for async calls whose resources were on the stack as JS objects when they
1430 // were entered. We cannot recreate this here; however, storing these values
1431 // on the JS equivalent gives the same result, so we do that instead.
1432 for (size_t i = 0; i < info_->native_execution_async_resources.size(); ++i) {
1433 if (info_->native_execution_async_resources[i] == SIZE_MAX)
1434 continue;
1435 Local<Object> obj = context->GetDataFromSnapshotOnce<Object>(
1436 info_->native_execution_async_resources[i])
1437 .ToLocalChecked();
1438 js_execution_async_resources->Set(context, i, obj).Check();
1439 }
1440 info_ = nullptr;
1441 }
1442
operator <<(std::ostream & output,const AsyncHooks::SerializeInfo & i)1443 std::ostream& operator<<(std::ostream& output,
1444 const AsyncHooks::SerializeInfo& i) {
1445 output << "{\n"
1446 << " " << i.async_ids_stack << ", // async_ids_stack\n"
1447 << " " << i.fields << ", // fields\n"
1448 << " " << i.async_id_fields << ", // async_id_fields\n"
1449 << " " << i.js_execution_async_resources
1450 << ", // js_execution_async_resources\n"
1451 << " " << i.native_execution_async_resources
1452 << ", // native_execution_async_resources\n"
1453 << "}";
1454 return output;
1455 }
1456
Serialize(Local<Context> context,SnapshotCreator * creator)1457 AsyncHooks::SerializeInfo AsyncHooks::Serialize(Local<Context> context,
1458 SnapshotCreator* creator) {
1459 SerializeInfo info;
1460 // TODO(joyeecheung): some of these probably don't need to be serialized.
1461 info.async_ids_stack = async_ids_stack_.Serialize(context, creator);
1462 info.fields = fields_.Serialize(context, creator);
1463 info.async_id_fields = async_id_fields_.Serialize(context, creator);
1464 if (!js_execution_async_resources_.IsEmpty()) {
1465 info.js_execution_async_resources = creator->AddData(
1466 context, js_execution_async_resources_.Get(context->GetIsolate()));
1467 CHECK_NE(info.js_execution_async_resources, 0);
1468 } else {
1469 info.js_execution_async_resources = 0;
1470 }
1471
1472 info.native_execution_async_resources.resize(
1473 native_execution_async_resources_.size());
1474 for (size_t i = 0; i < native_execution_async_resources_.size(); i++) {
1475 info.native_execution_async_resources[i] =
1476 native_execution_async_resources_[i].IsEmpty() ? SIZE_MAX :
1477 creator->AddData(
1478 context,
1479 native_execution_async_resources_[i]);
1480 }
1481
1482 // At the moment, promise hooks are not supported in the startup snapshot.
1483 // TODO(joyeecheung): support promise hooks in the startup snapshot.
1484 CHECK(js_promise_hooks_[0].IsEmpty());
1485 CHECK(js_promise_hooks_[1].IsEmpty());
1486 CHECK(js_promise_hooks_[2].IsEmpty());
1487 CHECK(js_promise_hooks_[3].IsEmpty());
1488
1489 return info;
1490 }
1491
MemoryInfo(MemoryTracker * tracker) const1492 void AsyncHooks::MemoryInfo(MemoryTracker* tracker) const {
1493 tracker->TrackField("async_ids_stack", async_ids_stack_);
1494 tracker->TrackField("fields", fields_);
1495 tracker->TrackField("async_id_fields", async_id_fields_);
1496 tracker->TrackField("js_promise_hooks", js_promise_hooks_);
1497 }
1498
grow_async_ids_stack()1499 void AsyncHooks::grow_async_ids_stack() {
1500 async_ids_stack_.reserve(async_ids_stack_.Length() * 3);
1501
1502 env()->async_hooks_binding()->Set(
1503 env()->context(),
1504 env()->async_ids_stack_string(),
1505 async_ids_stack_.GetJSArray()).Check();
1506 }
1507
FailWithCorruptedAsyncStack(double expected_async_id)1508 void AsyncHooks::FailWithCorruptedAsyncStack(double expected_async_id) {
1509 fprintf(stderr,
1510 "Error: async hook stack has become corrupted ("
1511 "actual: %.f, expected: %.f)\n",
1512 async_id_fields_.GetValue(kExecutionAsyncId),
1513 expected_async_id);
1514 DumpBacktrace(stderr);
1515 fflush(stderr);
1516 if (!env()->abort_on_uncaught_exception())
1517 exit(1);
1518 fprintf(stderr, "\n");
1519 fflush(stderr);
1520 ABORT_NO_BACKTRACE();
1521 }
1522
Exit(int exit_code)1523 void Environment::Exit(int exit_code) {
1524 if (options()->trace_exit) {
1525 HandleScope handle_scope(isolate());
1526 Isolate::DisallowJavascriptExecutionScope disallow_js(
1527 isolate(), Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE);
1528
1529 if (is_main_thread()) {
1530 fprintf(stderr, "(node:%d) ", uv_os_getpid());
1531 } else {
1532 fprintf(stderr, "(node:%d, thread:%" PRIu64 ") ",
1533 uv_os_getpid(), thread_id());
1534 }
1535
1536 fprintf(
1537 stderr, "WARNING: Exited the environment with code %d\n", exit_code);
1538 PrintStackTrace(isolate(),
1539 StackTrace::CurrentStackTrace(
1540 isolate(), stack_trace_limit(), StackTrace::kDetailed));
1541 }
1542 process_exit_handler_(this, exit_code);
1543 }
1544
stop_sub_worker_contexts()1545 void Environment::stop_sub_worker_contexts() {
1546 DCHECK_EQ(Isolate::GetCurrent(), isolate());
1547
1548 while (!sub_worker_contexts_.empty()) {
1549 Worker* w = *sub_worker_contexts_.begin();
1550 remove_sub_worker_context(w);
1551 w->Exit(1);
1552 w->JoinThread();
1553 }
1554 }
1555
worker_parent_env() const1556 Environment* Environment::worker_parent_env() const {
1557 if (worker_context() == nullptr) return nullptr;
1558 return worker_context()->env();
1559 }
1560
AddUnmanagedFd(int fd)1561 void Environment::AddUnmanagedFd(int fd) {
1562 if (!tracks_unmanaged_fds()) return;
1563 auto result = unmanaged_fds_.insert(fd);
1564 if (!result.second) {
1565 ProcessEmitWarning(
1566 this, "File descriptor %d opened in unmanaged mode twice", fd);
1567 }
1568 }
1569
RemoveUnmanagedFd(int fd)1570 void Environment::RemoveUnmanagedFd(int fd) {
1571 if (!tracks_unmanaged_fds()) return;
1572 size_t removed_count = unmanaged_fds_.erase(fd);
1573 if (removed_count == 0) {
1574 ProcessEmitWarning(
1575 this, "File descriptor %d closed but not opened in unmanaged mode", fd);
1576 }
1577 }
1578
PrintInfoForSnapshotIfDebug()1579 void Environment::PrintInfoForSnapshotIfDebug() {
1580 if (enabled_debug_list()->enabled(DebugCategory::MKSNAPSHOT)) {
1581 fprintf(stderr, "At the exit of the Environment:\n");
1582 principal_realm()->PrintInfoForSnapshot();
1583 fprintf(stderr, "\nBuiltins without cache:\n");
1584 for (const auto& s : builtins_without_cache) {
1585 fprintf(stderr, "%s\n", s.c_str());
1586 }
1587 fprintf(stderr, "\nBuiltins with cache:\n");
1588 for (const auto& s : builtins_with_cache) {
1589 fprintf(stderr, "%s\n", s.c_str());
1590 }
1591 fprintf(stderr, "\nStatic bindings (need to be registered):\n");
1592 for (const auto mod : internal_bindings) {
1593 fprintf(stderr, "%s:%s\n", mod->nm_filename, mod->nm_modname);
1594 }
1595 }
1596 }
1597
Serialize(SnapshotCreator * creator)1598 EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
1599 EnvSerializeInfo info;
1600 Local<Context> ctx = context();
1601
1602 // Currently all modules are compiled without cache in builtin snapshot
1603 // builder.
1604 info.builtins = std::vector<std::string>(builtins_without_cache.begin(),
1605 builtins_without_cache.end());
1606
1607 info.async_hooks = async_hooks_.Serialize(ctx, creator);
1608 info.immediate_info = immediate_info_.Serialize(ctx, creator);
1609 info.timeout_info = timeout_info_.Serialize(ctx, creator);
1610 info.tick_info = tick_info_.Serialize(ctx, creator);
1611 info.performance_state = performance_state_->Serialize(ctx, creator);
1612 info.exiting = exiting_.Serialize(ctx, creator);
1613 info.stream_base_state = stream_base_state_.Serialize(ctx, creator);
1614 info.should_abort_on_uncaught_toggle =
1615 should_abort_on_uncaught_toggle_.Serialize(ctx, creator);
1616
1617 info.principal_realm = principal_realm_->Serialize(creator);
1618 // For now we only support serialization of the main context.
1619 // TODO(joyeecheung): support de/serialization of vm contexts.
1620 CHECK_EQ(contexts_.size(), 1);
1621 CHECK_EQ(contexts_[0], context());
1622 return info;
1623 }
1624
EnqueueDeserializeRequest(DeserializeRequestCallback cb,Local<Object> holder,int index,InternalFieldInfoBase * info)1625 void Environment::EnqueueDeserializeRequest(DeserializeRequestCallback cb,
1626 Local<Object> holder,
1627 int index,
1628 InternalFieldInfoBase* info) {
1629 DCHECK_EQ(index, BaseObject::kEmbedderType);
1630 DeserializeRequest request{cb, {isolate(), holder}, index, info};
1631 deserialize_requests_.push_back(std::move(request));
1632 }
1633
RunDeserializeRequests()1634 void Environment::RunDeserializeRequests() {
1635 HandleScope scope(isolate());
1636 Local<Context> ctx = context();
1637 Isolate* is = isolate();
1638 while (!deserialize_requests_.empty()) {
1639 DeserializeRequest request(std::move(deserialize_requests_.front()));
1640 deserialize_requests_.pop_front();
1641 Local<Object> holder = request.holder.Get(is);
1642 request.cb(ctx, holder, request.index, request.info);
1643 request.holder.Reset();
1644 request.info->Delete();
1645 }
1646 }
1647
DeserializeProperties(const EnvSerializeInfo * info)1648 void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
1649 Local<Context> ctx = context();
1650
1651 RunDeserializeRequests();
1652
1653 builtins_in_snapshot = info->builtins;
1654 async_hooks_.Deserialize(ctx);
1655 immediate_info_.Deserialize(ctx);
1656 timeout_info_.Deserialize(ctx);
1657 tick_info_.Deserialize(ctx);
1658 performance_state_->Deserialize(ctx);
1659 exiting_.Deserialize(ctx);
1660 stream_base_state_.Deserialize(ctx);
1661 should_abort_on_uncaught_toggle_.Deserialize(ctx);
1662
1663 principal_realm_->DeserializeProperties(&info->principal_realm);
1664
1665 if (enabled_debug_list_.enabled(DebugCategory::MKSNAPSHOT)) {
1666 fprintf(stderr, "deserializing...\n");
1667 std::cerr << *info << "\n";
1668 }
1669 }
1670
GuessMemoryAvailableToTheProcess()1671 uint64_t GuessMemoryAvailableToTheProcess() {
1672 uint64_t free_in_system = uv_get_free_memory();
1673 size_t allowed = uv_get_constrained_memory();
1674 if (allowed == 0) {
1675 return free_in_system;
1676 }
1677 size_t rss;
1678 int err = uv_resident_set_memory(&rss);
1679 if (err) {
1680 return free_in_system;
1681 }
1682 if (allowed < rss) {
1683 // Something is probably wrong. Fallback to the free memory.
1684 return free_in_system;
1685 }
1686 // There may still be room for swap, but we will just leave it here.
1687 return allowed - rss;
1688 }
1689
BuildEmbedderGraph(Isolate * isolate,EmbedderGraph * graph,void * data)1690 void Environment::BuildEmbedderGraph(Isolate* isolate,
1691 EmbedderGraph* graph,
1692 void* data) {
1693 MemoryTracker tracker(isolate, graph);
1694 Environment* env = static_cast<Environment*>(data);
1695 // Start traversing embedder objects from the root Environment object.
1696 tracker.Track(env);
1697 }
1698
NearHeapLimitCallback(void * data,size_t current_heap_limit,size_t initial_heap_limit)1699 size_t Environment::NearHeapLimitCallback(void* data,
1700 size_t current_heap_limit,
1701 size_t initial_heap_limit) {
1702 Environment* env = static_cast<Environment*>(data);
1703
1704 Debug(env,
1705 DebugCategory::DIAGNOSTICS,
1706 "Invoked NearHeapLimitCallback, processing=%d, "
1707 "current_limit=%" PRIu64 ", "
1708 "initial_limit=%" PRIu64 "\n",
1709 env->is_in_heapsnapshot_heap_limit_callback_,
1710 static_cast<uint64_t>(current_heap_limit),
1711 static_cast<uint64_t>(initial_heap_limit));
1712
1713 size_t max_young_gen_size = env->isolate_data()->max_young_gen_size;
1714 size_t young_gen_size = 0;
1715 size_t old_gen_size = 0;
1716
1717 HeapSpaceStatistics stats;
1718 size_t num_heap_spaces = env->isolate()->NumberOfHeapSpaces();
1719 for (size_t i = 0; i < num_heap_spaces; ++i) {
1720 env->isolate()->GetHeapSpaceStatistics(&stats, i);
1721 if (strcmp(stats.space_name(), "new_space") == 0 ||
1722 strcmp(stats.space_name(), "new_large_object_space") == 0) {
1723 young_gen_size += stats.space_used_size();
1724 } else {
1725 old_gen_size += stats.space_used_size();
1726 }
1727 }
1728
1729 Debug(env,
1730 DebugCategory::DIAGNOSTICS,
1731 "max_young_gen_size=%" PRIu64 ", "
1732 "young_gen_size=%" PRIu64 ", "
1733 "old_gen_size=%" PRIu64 ", "
1734 "total_size=%" PRIu64 "\n",
1735 static_cast<uint64_t>(max_young_gen_size),
1736 static_cast<uint64_t>(young_gen_size),
1737 static_cast<uint64_t>(old_gen_size),
1738 static_cast<uint64_t>(young_gen_size + old_gen_size));
1739
1740 uint64_t available = GuessMemoryAvailableToTheProcess();
1741 // TODO(joyeecheung): get a better estimate about the native memory
1742 // usage into the overhead, e.g. based on the count of objects.
1743 uint64_t estimated_overhead = max_young_gen_size;
1744 Debug(env,
1745 DebugCategory::DIAGNOSTICS,
1746 "Estimated available memory=%" PRIu64 ", "
1747 "estimated overhead=%" PRIu64 "\n",
1748 static_cast<uint64_t>(available),
1749 static_cast<uint64_t>(estimated_overhead));
1750
1751 // This might be hit when the snapshot is being taken in another
1752 // NearHeapLimitCallback invocation.
1753 // When taking the snapshot, objects in the young generation may be
1754 // promoted to the old generation, result in increased heap usage,
1755 // but it should be no more than the young generation size.
1756 // Ideally, this should be as small as possible - the heap limit
1757 // can only be restored when the heap usage falls down below the
1758 // new limit, so in a heap with unbounded growth the isolate
1759 // may eventually crash with this new limit - effectively raising
1760 // the heap limit to the new one.
1761 size_t new_limit = current_heap_limit + max_young_gen_size;
1762 if (env->is_in_heapsnapshot_heap_limit_callback_) {
1763 Debug(env,
1764 DebugCategory::DIAGNOSTICS,
1765 "Not generating snapshots in nested callback. "
1766 "new_limit=%" PRIu64 "\n",
1767 static_cast<uint64_t>(new_limit));
1768 return new_limit;
1769 }
1770
1771 // Estimate whether the snapshot is going to use up all the memory
1772 // available to the process. If so, just give up to prevent the system
1773 // from killing the process for a system OOM.
1774 if (estimated_overhead > available) {
1775 Debug(env,
1776 DebugCategory::DIAGNOSTICS,
1777 "Not generating snapshots because it's too risky.\n");
1778 env->RemoveHeapSnapshotNearHeapLimitCallback(0);
1779 // The new limit must be higher than current_heap_limit or V8 might
1780 // crash.
1781 return new_limit;
1782 }
1783
1784 // Take the snapshot synchronously.
1785 env->is_in_heapsnapshot_heap_limit_callback_ = true;
1786
1787 std::string dir = env->options()->diagnostic_dir;
1788 if (dir.empty()) {
1789 dir = env->GetCwd();
1790 }
1791 DiagnosticFilename name(env, "Heap", "heapsnapshot");
1792 std::string filename = dir + kPathSeparator + (*name);
1793
1794 Debug(env, DebugCategory::DIAGNOSTICS, "Start generating %s...\n", *name);
1795
1796 heap::WriteSnapshot(env, filename.c_str());
1797 env->heap_limit_snapshot_taken_ += 1;
1798
1799 Debug(env,
1800 DebugCategory::DIAGNOSTICS,
1801 "%" PRIu32 "/%" PRIu32 " snapshots taken.\n",
1802 env->heap_limit_snapshot_taken_,
1803 env->heap_snapshot_near_heap_limit_);
1804
1805 // Don't take more snapshots than the limit specified.
1806 if (env->heap_limit_snapshot_taken_ == env->heap_snapshot_near_heap_limit_) {
1807 Debug(env,
1808 DebugCategory::DIAGNOSTICS,
1809 "Removing the near heap limit callback");
1810 env->RemoveHeapSnapshotNearHeapLimitCallback(0);
1811 }
1812
1813 FPrintF(stderr, "Wrote snapshot to %s\n", filename.c_str());
1814 // Tell V8 to reset the heap limit once the heap usage falls down to
1815 // 95% of the initial limit.
1816 env->isolate()->AutomaticallyRestoreInitialHeapLimit(0.95);
1817
1818 env->is_in_heapsnapshot_heap_limit_callback_ = false;
1819
1820 // The new limit must be higher than current_heap_limit or V8 might
1821 // crash.
1822 return new_limit;
1823 }
1824
SelfSize() const1825 inline size_t Environment::SelfSize() const {
1826 size_t size = sizeof(*this);
1827 // Remove non pointer fields that will be tracked in MemoryInfo()
1828 // TODO(joyeecheung): refactor the MemoryTracker interface so
1829 // this can be done for common types within the Track* calls automatically
1830 // if a certain scope is entered.
1831 size -= sizeof(async_hooks_);
1832 size -= sizeof(cleanup_queue_);
1833 size -= sizeof(tick_info_);
1834 size -= sizeof(immediate_info_);
1835 return size;
1836 }
1837
MemoryInfo(MemoryTracker * tracker) const1838 void Environment::MemoryInfo(MemoryTracker* tracker) const {
1839 // Iteratable STLs have their own sizes subtracted from the parent
1840 // by default.
1841 tracker->TrackField("isolate_data", isolate_data_);
1842 tracker->TrackField("builtins_with_cache", builtins_with_cache);
1843 tracker->TrackField("builtins_without_cache", builtins_without_cache);
1844 tracker->TrackField("destroy_async_id_list", destroy_async_id_list_);
1845 tracker->TrackField("exec_argv", exec_argv_);
1846 tracker->TrackField("exiting", exiting_);
1847 tracker->TrackField("should_abort_on_uncaught_toggle",
1848 should_abort_on_uncaught_toggle_);
1849 tracker->TrackField("stream_base_state", stream_base_state_);
1850 tracker->TrackField("cleanup_queue", cleanup_queue_);
1851 tracker->TrackField("async_hooks", async_hooks_);
1852 tracker->TrackField("immediate_info", immediate_info_);
1853 tracker->TrackField("timeout_info", timeout_info_);
1854 tracker->TrackField("tick_info", tick_info_);
1855 tracker->TrackField("principal_realm", principal_realm_);
1856
1857 // FIXME(joyeecheung): track other fields in Environment.
1858 // Currently MemoryTracker is unable to track these
1859 // correctly:
1860 // - Internal types that do not implement MemoryRetainer yet
1861 // - STL containers with MemoryRetainer* inside
1862 // - STL containers with numeric types inside that should not have their
1863 // nodes elided e.g. numeric keys in maps.
1864 // We also need to make sure that when we add a non-pointer field as its own
1865 // node, we shift its sizeof() size out of the Environment node.
1866 }
1867
RunWeakRefCleanup()1868 void Environment::RunWeakRefCleanup() {
1869 isolate()->ClearKeptObjects();
1870 }
1871 } // namespace node
1872