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 VM(PropertyName) V(PropertyName##_binding, FunctionTemplate)
315 #define V(PropertyName, TypeName) \
316 do { \
317 Local<TypeName> field = PropertyName(); \
318 if (!field.IsEmpty()) { \
319 size_t index = creator->AddData(field); \
320 info.template_values.push_back({#PropertyName, id, index}); \
321 } \
322 id++; \
323 } while (0);
324 PER_ISOLATE_TEMPLATE_PROPERTIES(V)
325 NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM)
326 #undef V
327
328 return info;
329 }
330
DeserializeProperties(const IsolateDataSerializeInfo * info)331 void IsolateData::DeserializeProperties(const IsolateDataSerializeInfo* info) {
332 size_t i = 0;
333 HandleScope handle_scope(isolate_);
334
335 if (per_process::enabled_debug_list.enabled(DebugCategory::MKSNAPSHOT)) {
336 fprintf(stderr, "deserializing IsolateDataSerializeInfo...\n");
337 std::cerr << *info << "\n";
338 }
339
340 #define VP(PropertyName, StringValue) V(Private, PropertyName)
341 #define VY(PropertyName, StringValue) V(Symbol, PropertyName)
342 #define VS(PropertyName, StringValue) V(String, PropertyName)
343 #define V(TypeName, PropertyName) \
344 do { \
345 MaybeLocal<TypeName> maybe_field = \
346 isolate_->GetDataFromSnapshotOnce<TypeName>( \
347 info->primitive_values[i++]); \
348 Local<TypeName> field; \
349 if (!maybe_field.ToLocal(&field)) { \
350 fprintf(stderr, "Failed to deserialize " #PropertyName "\n"); \
351 } \
352 PropertyName##_.Set(isolate_, field); \
353 } while (0);
354 PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
355 PER_ISOLATE_SYMBOL_PROPERTIES(VY)
356 PER_ISOLATE_STRING_PROPERTIES(VS)
357 #undef V
358 #undef VY
359 #undef VS
360 #undef VP
361
362 for (size_t j = 0; j < AsyncWrap::PROVIDERS_LENGTH; j++) {
363 MaybeLocal<String> maybe_field =
364 isolate_->GetDataFromSnapshotOnce<String>(info->primitive_values[i++]);
365 Local<String> field;
366 if (!maybe_field.ToLocal(&field)) {
367 fprintf(stderr, "Failed to deserialize AsyncWrap provider %zu\n", j);
368 }
369 async_wrap_providers_[j].Set(isolate_, field);
370 }
371
372 const std::vector<PropInfo>& values = info->template_values;
373 i = 0; // index to the array
374 uint32_t id = 0;
375 #define VM(PropertyName) V(PropertyName##_binding, FunctionTemplate)
376 #define V(PropertyName, TypeName) \
377 do { \
378 if (values.size() > i && id == values[i].id) { \
379 const PropInfo& d = values[i]; \
380 DCHECK_EQ(d.name, #PropertyName); \
381 MaybeLocal<TypeName> maybe_field = \
382 isolate_->GetDataFromSnapshotOnce<TypeName>(d.index); \
383 Local<TypeName> field; \
384 if (!maybe_field.ToLocal(&field)) { \
385 fprintf(stderr, \
386 "Failed to deserialize isolate data template " #PropertyName \
387 "\n"); \
388 } \
389 set_##PropertyName(field); \
390 i++; \
391 } \
392 id++; \
393 } while (0);
394
395 PER_ISOLATE_TEMPLATE_PROPERTIES(V);
396 NODE_BINDINGS_WITH_PER_ISOLATE_INIT(VM);
397 #undef V
398 }
399
CreateProperties()400 void IsolateData::CreateProperties() {
401 // Create string and private symbol properties as internalized one byte
402 // strings after the platform is properly initialized.
403 //
404 // Internalized because it makes property lookups a little faster and
405 // because the string is created in the old space straight away. It's going
406 // to end up in the old space sooner or later anyway but now it doesn't go
407 // through v8::Eternal's new space handling first.
408 //
409 // One byte because our strings are ASCII and we can safely skip V8's UTF-8
410 // decoding step.
411
412 HandleScope handle_scope(isolate_);
413
414 #define V(PropertyName, StringValue) \
415 PropertyName##_.Set( \
416 isolate_, \
417 Private::New(isolate_, \
418 String::NewFromOneByte( \
419 isolate_, \
420 reinterpret_cast<const uint8_t*>(StringValue), \
421 NewStringType::kInternalized, \
422 sizeof(StringValue) - 1) \
423 .ToLocalChecked()));
424 PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
425 #undef V
426 #define V(PropertyName, StringValue) \
427 PropertyName##_.Set( \
428 isolate_, \
429 Symbol::New(isolate_, \
430 String::NewFromOneByte( \
431 isolate_, \
432 reinterpret_cast<const uint8_t*>(StringValue), \
433 NewStringType::kInternalized, \
434 sizeof(StringValue) - 1) \
435 .ToLocalChecked()));
436 PER_ISOLATE_SYMBOL_PROPERTIES(V)
437 #undef V
438 #define V(PropertyName, StringValue) \
439 PropertyName##_.Set( \
440 isolate_, \
441 String::NewFromOneByte(isolate_, \
442 reinterpret_cast<const uint8_t*>(StringValue), \
443 NewStringType::kInternalized, \
444 sizeof(StringValue) - 1) \
445 .ToLocalChecked());
446 PER_ISOLATE_STRING_PROPERTIES(V)
447 #undef V
448
449 // Create all the provider strings that will be passed to JS. Place them in
450 // an array so the array index matches the PROVIDER id offset. This way the
451 // strings can be retrieved quickly.
452 #define V(Provider) \
453 async_wrap_providers_[AsyncWrap::PROVIDER_ ## Provider].Set( \
454 isolate_, \
455 String::NewFromOneByte( \
456 isolate_, \
457 reinterpret_cast<const uint8_t*>(#Provider), \
458 NewStringType::kInternalized, \
459 sizeof(#Provider) - 1).ToLocalChecked());
460 NODE_ASYNC_PROVIDER_TYPES(V)
461 #undef V
462
463 Local<FunctionTemplate> templ = FunctionTemplate::New(isolate());
464 templ->InstanceTemplate()->SetInternalFieldCount(
465 BaseObject::kInternalFieldCount);
466 templ->Inherit(BaseObject::GetConstructorTemplate(this));
467 set_binding_data_ctor_template(templ);
468 binding::CreateInternalBindingTemplates(this);
469
470 contextify::ContextifyContext::InitializeGlobalTemplates(this);
471 }
472
IsolateData(Isolate * isolate,uv_loop_t * event_loop,MultiIsolatePlatform * platform,ArrayBufferAllocator * node_allocator,const IsolateDataSerializeInfo * isolate_data_info)473 IsolateData::IsolateData(Isolate* isolate,
474 uv_loop_t* event_loop,
475 MultiIsolatePlatform* platform,
476 ArrayBufferAllocator* node_allocator,
477 const IsolateDataSerializeInfo* isolate_data_info)
478 : isolate_(isolate),
479 event_loop_(event_loop),
480 node_allocator_(node_allocator == nullptr ? nullptr
481 : node_allocator->GetImpl()),
482 platform_(platform) {
483 options_.reset(
484 new PerIsolateOptions(*(per_process::cli_options->per_isolate)));
485
486 if (isolate_data_info == nullptr) {
487 CreateProperties();
488 } else {
489 DeserializeProperties(isolate_data_info);
490 }
491 }
492
MemoryInfo(MemoryTracker * tracker) const493 void IsolateData::MemoryInfo(MemoryTracker* tracker) const {
494 #define V(PropertyName, StringValue) \
495 tracker->TrackField(#PropertyName, PropertyName());
496 PER_ISOLATE_SYMBOL_PROPERTIES(V)
497
498 PER_ISOLATE_STRING_PROPERTIES(V)
499 #undef V
500
501 tracker->TrackField("async_wrap_providers", async_wrap_providers_);
502
503 if (node_allocator_ != nullptr) {
504 tracker->TrackFieldWithSize(
505 "node_allocator", sizeof(*node_allocator_), "NodeArrayBufferAllocator");
506 }
507 tracker->TrackFieldWithSize(
508 "platform", sizeof(*platform_), "MultiIsolatePlatform");
509 // TODO(joyeecheung): implement MemoryRetainer in the option classes.
510 }
511
UpdateTraceCategoryState()512 void TrackingTraceStateObserver::UpdateTraceCategoryState() {
513 if (!env_->owns_process_state() || !env_->can_call_into_js()) {
514 // Ideally, we’d have a consistent story that treats all threads/Environment
515 // instances equally here. However, tracing is essentially global, and this
516 // callback is called from whichever thread calls `StartTracing()` or
517 // `StopTracing()`. The only way to do this in a threadsafe fashion
518 // seems to be only tracking this from the main thread, and only allowing
519 // these state modifications from the main thread.
520 return;
521 }
522
523 if (env_->principal_realm() == nullptr) {
524 return;
525 }
526
527 bool async_hooks_enabled = (*(TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
528 TRACING_CATEGORY_NODE1(async_hooks)))) != 0;
529
530 Isolate* isolate = env_->isolate();
531 HandleScope handle_scope(isolate);
532 Local<Function> cb = env_->trace_category_state_function();
533 if (cb.IsEmpty())
534 return;
535 TryCatchScope try_catch(env_);
536 try_catch.SetVerbose(true);
537 Local<Value> args[] = {Boolean::New(isolate, async_hooks_enabled)};
538 USE(cb->Call(env_->context(), Undefined(isolate), arraysize(args), args));
539 }
540
AssignToContext(Local<v8::Context> context,Realm * realm,const ContextInfo & info)541 void Environment::AssignToContext(Local<v8::Context> context,
542 Realm* realm,
543 const ContextInfo& info) {
544 context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment,
545 this);
546 context->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kRealm, realm);
547 // Used to retrieve bindings
548 context->SetAlignedPointerInEmbedderData(
549 ContextEmbedderIndex::kBindingDataStoreIndex,
550 realm != nullptr ? realm->binding_data_store() : nullptr);
551
552 // ContextifyContexts will update this to a pointer to the native object.
553 context->SetAlignedPointerInEmbedderData(
554 ContextEmbedderIndex::kContextifyContext, nullptr);
555
556 // This must not be done before other context fields are initialized.
557 ContextEmbedderTag::TagNodeContext(context);
558
559 #if HAVE_INSPECTOR
560 inspector_agent()->ContextCreated(context, info);
561 #endif // HAVE_INSPECTOR
562
563 this->async_hooks()->InstallPromiseHooks(context);
564 TrackContext(context);
565 }
566
TryLoadAddon(const char * filename,int flags,const std::function<bool (binding::DLib *)> & was_loaded)567 void Environment::TryLoadAddon(
568 const char* filename,
569 int flags,
570 const std::function<bool(binding::DLib*)>& was_loaded) {
571 loaded_addons_.emplace_back(filename, flags);
572 if (!was_loaded(&loaded_addons_.back())) {
573 loaded_addons_.pop_back();
574 }
575 }
576
GetCwd()577 std::string Environment::GetCwd() {
578 char cwd[PATH_MAX_BYTES];
579 size_t size = PATH_MAX_BYTES;
580 const int err = uv_cwd(cwd, &size);
581
582 if (err == 0) {
583 CHECK_GT(size, 0);
584 return cwd;
585 }
586
587 // This can fail if the cwd is deleted. In that case, fall back to
588 // exec_path.
589 const std::string& exec_path = exec_path_;
590 return exec_path.substr(0, exec_path.find_last_of(kPathSeparator));
591 }
592
add_refs(int64_t diff)593 void Environment::add_refs(int64_t diff) {
594 task_queues_async_refs_ += diff;
595 CHECK_GE(task_queues_async_refs_, 0);
596 if (task_queues_async_refs_ == 0)
597 uv_unref(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
598 else
599 uv_ref(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
600 }
601
allocate_managed_buffer(const size_t suggested_size)602 uv_buf_t Environment::allocate_managed_buffer(const size_t suggested_size) {
603 NoArrayBufferZeroFillScope no_zero_fill_scope(isolate_data());
604 std::unique_ptr<v8::BackingStore> bs =
605 v8::ArrayBuffer::NewBackingStore(isolate(), suggested_size);
606 uv_buf_t buf = uv_buf_init(static_cast<char*>(bs->Data()), bs->ByteLength());
607 released_allocated_buffers_.emplace(buf.base, std::move(bs));
608 return buf;
609 }
610
release_managed_buffer(const uv_buf_t & buf)611 std::unique_ptr<v8::BackingStore> Environment::release_managed_buffer(
612 const uv_buf_t& buf) {
613 std::unique_ptr<v8::BackingStore> bs;
614 if (buf.base != nullptr) {
615 auto it = released_allocated_buffers_.find(buf.base);
616 CHECK_NE(it, released_allocated_buffers_.end());
617 bs = std::move(it->second);
618 released_allocated_buffers_.erase(it);
619 }
620 return bs;
621 }
622
GetExecPath(const std::vector<std::string> & argv)623 std::string GetExecPath(const std::vector<std::string>& argv) {
624 char exec_path_buf[2 * PATH_MAX];
625 size_t exec_path_len = sizeof(exec_path_buf);
626 std::string exec_path;
627 if (uv_exepath(exec_path_buf, &exec_path_len) == 0) {
628 exec_path = std::string(exec_path_buf, exec_path_len);
629 } else if (argv.size() > 0) {
630 exec_path = argv[0];
631 }
632
633 // On OpenBSD process.execPath will be relative unless we
634 // get the full path before process.execPath is used.
635 #if defined(__OpenBSD__)
636 uv_fs_t req;
637 req.ptr = nullptr;
638 if (0 ==
639 uv_fs_realpath(nullptr, &req, exec_path.c_str(), nullptr)) {
640 CHECK_NOT_NULL(req.ptr);
641 exec_path = std::string(static_cast<char*>(req.ptr));
642 }
643 uv_fs_req_cleanup(&req);
644 #endif
645
646 return exec_path;
647 }
648
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)649 Environment::Environment(IsolateData* isolate_data,
650 Isolate* isolate,
651 const std::vector<std::string>& args,
652 const std::vector<std::string>& exec_args,
653 const EnvSerializeInfo* env_info,
654 EnvironmentFlags::Flags flags,
655 ThreadId thread_id)
656 : isolate_(isolate),
657 isolate_data_(isolate_data),
658 async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)),
659 immediate_info_(isolate, MAYBE_FIELD_PTR(env_info, immediate_info)),
660 timeout_info_(isolate_, 1, MAYBE_FIELD_PTR(env_info, timeout_info)),
661 tick_info_(isolate, MAYBE_FIELD_PTR(env_info, tick_info)),
662 timer_base_(uv_now(isolate_data->event_loop())),
663 exec_argv_(exec_args),
664 argv_(args),
665 exec_path_(GetExecPath(args)),
666 exiting_(isolate_, 1, MAYBE_FIELD_PTR(env_info, exiting)),
667 should_abort_on_uncaught_toggle_(
668 isolate_,
669 1,
670 MAYBE_FIELD_PTR(env_info, should_abort_on_uncaught_toggle)),
671 stream_base_state_(isolate_,
672 StreamBase::kNumStreamBaseStateFields,
673 MAYBE_FIELD_PTR(env_info, stream_base_state)),
674 environment_start_time_(PERFORMANCE_NOW()),
675 flags_(flags),
676 thread_id_(thread_id.id == static_cast<uint64_t>(-1)
677 ? AllocateEnvironmentThreadId().id
678 : thread_id.id) {
679 #ifdef NODE_V8_SHARED_RO_HEAP
680 if (!is_main_thread()) {
681 CHECK_NOT_NULL(isolate_data->worker_context());
682 // TODO(addaleax): Adjust for the embedder API snapshot support changes
683 builtin_loader()->CopySourceAndCodeCacheReferenceFrom(
684 isolate_data->worker_context()->env()->builtin_loader());
685 }
686 #endif
687
688 // We'll be creating new objects so make sure we've entered the context.
689 HandleScope handle_scope(isolate);
690
691 // Set some flags if only kDefaultFlags was passed. This can make API version
692 // transitions easier for embedders.
693 if (flags_ & EnvironmentFlags::kDefaultFlags) {
694 flags_ = flags_ |
695 EnvironmentFlags::kOwnsProcessState |
696 EnvironmentFlags::kOwnsInspector;
697 }
698
699 set_env_vars(per_process::system_environment);
700 enabled_debug_list_.Parse(env_vars(), isolate);
701
702 // We create new copies of the per-Environment option sets, so that it is
703 // easier to modify them after Environment creation. The defaults are
704 // part of the per-Isolate option set, for which in turn the defaults are
705 // part of the per-process option set.
706 options_ = std::make_shared<EnvironmentOptions>(
707 *isolate_data->options()->per_env);
708 inspector_host_port_ = std::make_shared<ExclusiveAccess<HostPort>>(
709 options_->debug_options().host_port);
710
711 heap_snapshot_near_heap_limit_ =
712 static_cast<uint32_t>(options_->heap_snapshot_near_heap_limit);
713
714 if (!(flags_ & EnvironmentFlags::kOwnsProcessState)) {
715 set_abort_on_uncaught_exception(false);
716 }
717
718 #if HAVE_INSPECTOR
719 // We can only create the inspector agent after having cloned the options.
720 inspector_agent_ = std::make_unique<inspector::Agent>(this);
721 #endif
722
723 if (tracing::AgentWriterHandle* writer = GetTracingAgentWriter()) {
724 trace_state_observer_ = std::make_unique<TrackingTraceStateObserver>(this);
725 if (TracingController* tracing_controller = writer->GetTracingController())
726 tracing_controller->AddTraceStateObserver(trace_state_observer_.get());
727 }
728
729 destroy_async_id_list_.reserve(512);
730
731 performance_state_ = std::make_unique<performance::PerformanceState>(
732 isolate, MAYBE_FIELD_PTR(env_info, performance_state));
733
734 if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
735 TRACING_CATEGORY_NODE1(environment)) != 0) {
736 auto traced_value = tracing::TracedValue::Create();
737 traced_value->BeginArray("args");
738 for (const std::string& arg : args) traced_value->AppendString(arg);
739 traced_value->EndArray();
740 traced_value->BeginArray("exec_args");
741 for (const std::string& arg : exec_args) traced_value->AppendString(arg);
742 traced_value->EndArray();
743 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(TRACING_CATEGORY_NODE1(environment),
744 "Environment",
745 this,
746 "args",
747 std::move(traced_value));
748 }
749 }
750
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)751 Environment::Environment(IsolateData* isolate_data,
752 Local<Context> context,
753 const std::vector<std::string>& args,
754 const std::vector<std::string>& exec_args,
755 const EnvSerializeInfo* env_info,
756 EnvironmentFlags::Flags flags,
757 ThreadId thread_id)
758 : Environment(isolate_data,
759 context->GetIsolate(),
760 args,
761 exec_args,
762 env_info,
763 flags,
764 thread_id) {
765 InitializeMainContext(context, env_info);
766 }
767
InitializeMainContext(Local<Context> context,const EnvSerializeInfo * env_info)768 void Environment::InitializeMainContext(Local<Context> context,
769 const EnvSerializeInfo* env_info) {
770 principal_realm_ = std::make_unique<Realm>(
771 this, context, MAYBE_FIELD_PTR(env_info, principal_realm));
772 AssignToContext(context, principal_realm_.get(), ContextInfo(""));
773 if (env_info != nullptr) {
774 DeserializeProperties(env_info);
775 }
776
777 if (!options_->force_async_hooks_checks) {
778 async_hooks_.no_force_checks();
779 }
780
781 // By default, always abort when --abort-on-uncaught-exception was passed.
782 should_abort_on_uncaught_toggle_[0] = 1;
783
784 // The process is not exiting by default.
785 set_exiting(false);
786
787 performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_ENVIRONMENT,
788 environment_start_time_);
789 performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_NODE_START,
790 per_process::node_start_time);
791
792 if (per_process::v8_initialized) {
793 performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_V8_START,
794 performance::performance_v8_start);
795 }
796 }
797
~Environment()798 Environment::~Environment() {
799 HandleScope handle_scope(isolate());
800 Local<Context> ctx = context();
801
802 if (Environment** interrupt_data = interrupt_data_.load()) {
803 // There are pending RequestInterrupt() callbacks. Tell them not to run,
804 // then force V8 to run interrupts by compiling and running an empty script
805 // so as not to leak memory.
806 *interrupt_data = nullptr;
807
808 Isolate::AllowJavascriptExecutionScope allow_js_here(isolate());
809 TryCatch try_catch(isolate());
810 Context::Scope context_scope(ctx);
811
812 #ifdef DEBUG
813 bool consistency_check = false;
814 isolate()->RequestInterrupt([](Isolate*, void* data) {
815 *static_cast<bool*>(data) = true;
816 }, &consistency_check);
817 #endif
818
819 Local<Script> script;
820 if (Script::Compile(ctx, String::Empty(isolate())).ToLocal(&script))
821 USE(script->Run(ctx));
822
823 DCHECK(consistency_check);
824 }
825
826 // FreeEnvironment() should have set this.
827 CHECK(is_stopping());
828
829 if (heapsnapshot_near_heap_limit_callback_added_) {
830 RemoveHeapSnapshotNearHeapLimitCallback(0);
831 }
832
833 isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback(
834 BuildEmbedderGraph, this);
835
836 #if HAVE_INSPECTOR
837 // Destroy inspector agent before erasing the context. The inspector
838 // destructor depends on the context still being accessible.
839 inspector_agent_.reset();
840 #endif
841
842 ctx->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment,
843 nullptr);
844 ctx->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kRealm, nullptr);
845
846 if (trace_state_observer_) {
847 tracing::AgentWriterHandle* writer = GetTracingAgentWriter();
848 CHECK_NOT_NULL(writer);
849 if (TracingController* tracing_controller = writer->GetTracingController())
850 tracing_controller->RemoveTraceStateObserver(trace_state_observer_.get());
851 }
852
853 TRACE_EVENT_NESTABLE_ASYNC_END0(
854 TRACING_CATEGORY_NODE1(environment), "Environment", this);
855
856 // Do not unload addons on the main thread. Some addons need to retain memory
857 // beyond the Environment's lifetime, and unloading them early would break
858 // them; with Worker threads, we have the opportunity to be stricter.
859 // Also, since the main thread usually stops just before the process exits,
860 // this is far less relevant here.
861 if (!is_main_thread()) {
862 // Dereference all addons that were loaded into this environment.
863 for (binding::DLib& addon : loaded_addons_) {
864 addon.Close();
865 }
866 }
867 }
868
InitializeLibuv()869 void Environment::InitializeLibuv() {
870 HandleScope handle_scope(isolate());
871 Context::Scope context_scope(context());
872
873 CHECK_EQ(0, uv_timer_init(event_loop(), timer_handle()));
874 uv_unref(reinterpret_cast<uv_handle_t*>(timer_handle()));
875
876 CHECK_EQ(0, uv_check_init(event_loop(), immediate_check_handle()));
877 uv_unref(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));
878
879 CHECK_EQ(0, uv_idle_init(event_loop(), immediate_idle_handle()));
880
881 CHECK_EQ(0, uv_check_start(immediate_check_handle(), CheckImmediate));
882
883 // Inform V8's CPU profiler when we're idle. The profiler is sampling-based
884 // but not all samples are created equal; mark the wall clock time spent in
885 // epoll_wait() and friends so profiling tools can filter it out. The samples
886 // still end up in v8.log but with state=IDLE rather than state=EXTERNAL.
887 CHECK_EQ(0, uv_prepare_init(event_loop(), &idle_prepare_handle_));
888 CHECK_EQ(0, uv_check_init(event_loop(), &idle_check_handle_));
889
890 CHECK_EQ(0, uv_async_init(
891 event_loop(),
892 &task_queues_async_,
893 [](uv_async_t* async) {
894 Environment* env = ContainerOf(
895 &Environment::task_queues_async_, async);
896 HandleScope handle_scope(env->isolate());
897 Context::Scope context_scope(env->context());
898 env->RunAndClearNativeImmediates();
899 }));
900 uv_unref(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
901 uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
902 uv_unref(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
903
904 {
905 Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
906 task_queues_async_initialized_ = true;
907 if (native_immediates_threadsafe_.size() > 0 ||
908 native_immediates_interrupts_.size() > 0) {
909 uv_async_send(&task_queues_async_);
910 }
911 }
912
913 // Register clean-up cb to be called to clean up the handles
914 // when the environment is freed, note that they are not cleaned in
915 // the one environment per process setup, but will be called in
916 // FreeEnvironment.
917 RegisterHandleCleanups();
918
919 StartProfilerIdleNotifier();
920 }
921
ExitEnv(StopFlags::Flags flags)922 void Environment::ExitEnv(StopFlags::Flags flags) {
923 // Should not access non-thread-safe methods here.
924 set_stopping(true);
925 if ((flags & StopFlags::kDoNotTerminateIsolate) == 0)
926 isolate_->TerminateExecution();
927 SetImmediateThreadsafe([](Environment* env) {
928 env->set_can_call_into_js(false);
929 uv_stop(env->event_loop());
930 });
931 }
932
RegisterHandleCleanups()933 void Environment::RegisterHandleCleanups() {
934 HandleCleanupCb close_and_finish = [](Environment* env, uv_handle_t* handle,
935 void* arg) {
936 handle->data = env;
937
938 env->CloseHandle(handle, [](uv_handle_t* handle) {
939 #ifdef DEBUG
940 memset(handle, 0xab, uv_handle_size(handle->type));
941 #endif
942 });
943 };
944
945 auto register_handle = [&](uv_handle_t* handle) {
946 RegisterHandleCleanup(handle, close_and_finish, nullptr);
947 };
948 register_handle(reinterpret_cast<uv_handle_t*>(timer_handle()));
949 register_handle(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));
950 register_handle(reinterpret_cast<uv_handle_t*>(immediate_idle_handle()));
951 register_handle(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
952 register_handle(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
953 register_handle(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
954 }
955
CleanupHandles()956 void Environment::CleanupHandles() {
957 {
958 Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
959 task_queues_async_initialized_ = false;
960 }
961
962 Isolate::DisallowJavascriptExecutionScope disallow_js(isolate(),
963 Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
964
965 RunAndClearNativeImmediates(true /* skip unrefed SetImmediate()s */);
966
967 for (ReqWrapBase* request : req_wrap_queue_)
968 request->Cancel();
969
970 for (HandleWrap* handle : handle_wrap_queue_)
971 handle->Close();
972
973 for (HandleCleanup& hc : handle_cleanup_queue_)
974 hc.cb_(this, hc.handle_, hc.arg_);
975 handle_cleanup_queue_.clear();
976
977 while (handle_cleanup_waiting_ != 0 ||
978 request_waiting_ != 0 ||
979 !handle_wrap_queue_.IsEmpty()) {
980 uv_run(event_loop(), UV_RUN_ONCE);
981 }
982 }
983
StartProfilerIdleNotifier()984 void Environment::StartProfilerIdleNotifier() {
985 uv_prepare_start(&idle_prepare_handle_, [](uv_prepare_t* handle) {
986 Environment* env = ContainerOf(&Environment::idle_prepare_handle_, handle);
987 env->isolate()->SetIdle(true);
988 });
989 uv_check_start(&idle_check_handle_, [](uv_check_t* handle) {
990 Environment* env = ContainerOf(&Environment::idle_check_handle_, handle);
991 env->isolate()->SetIdle(false);
992 });
993 }
994
PrintSyncTrace() const995 void Environment::PrintSyncTrace() const {
996 if (!trace_sync_io_) return;
997
998 HandleScope handle_scope(isolate());
999
1000 fprintf(
1001 stderr, "(node:%d) WARNING: Detected use of sync API\n", uv_os_getpid());
1002 PrintStackTrace(isolate(),
1003 StackTrace::CurrentStackTrace(
1004 isolate(), stack_trace_limit(), StackTrace::kDetailed));
1005 }
1006
RunSnapshotSerializeCallback() const1007 MaybeLocal<Value> Environment::RunSnapshotSerializeCallback() const {
1008 EscapableHandleScope handle_scope(isolate());
1009 if (!snapshot_serialize_callback().IsEmpty()) {
1010 Context::Scope context_scope(context());
1011 return handle_scope.EscapeMaybe(snapshot_serialize_callback()->Call(
1012 context(), v8::Undefined(isolate()), 0, nullptr));
1013 }
1014 return handle_scope.Escape(Undefined(isolate()));
1015 }
1016
RunSnapshotDeserializeMain() const1017 MaybeLocal<Value> Environment::RunSnapshotDeserializeMain() const {
1018 EscapableHandleScope handle_scope(isolate());
1019 if (!snapshot_deserialize_main().IsEmpty()) {
1020 Context::Scope context_scope(context());
1021 return handle_scope.EscapeMaybe(snapshot_deserialize_main()->Call(
1022 context(), v8::Undefined(isolate()), 0, nullptr));
1023 }
1024 return handle_scope.Escape(Undefined(isolate()));
1025 }
1026
RunCleanup()1027 void Environment::RunCleanup() {
1028 started_cleanup_ = true;
1029 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "RunCleanup");
1030 // Only BaseObject's cleanups are registered as per-realm cleanup hooks now.
1031 // Defer the BaseObject cleanup after handles are cleaned up.
1032 CleanupHandles();
1033
1034 while (!cleanup_queue_.empty() || principal_realm_->HasCleanupHooks() ||
1035 native_immediates_.size() > 0 ||
1036 native_immediates_threadsafe_.size() > 0 ||
1037 native_immediates_interrupts_.size() > 0) {
1038 // TODO(legendecas): cleanup handles in per-realm cleanup hooks as well.
1039 principal_realm_->RunCleanup();
1040 cleanup_queue_.Drain();
1041 CleanupHandles();
1042 }
1043
1044 for (const int fd : unmanaged_fds_) {
1045 uv_fs_t close_req;
1046 uv_fs_close(nullptr, &close_req, fd, nullptr);
1047 uv_fs_req_cleanup(&close_req);
1048 }
1049 }
1050
RunAtExitCallbacks()1051 void Environment::RunAtExitCallbacks() {
1052 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "AtExit");
1053 for (ExitCallback at_exit : at_exit_functions_) {
1054 at_exit.cb_(at_exit.arg_);
1055 }
1056 at_exit_functions_.clear();
1057 }
1058
AtExit(void (* cb)(void * arg),void * arg)1059 void Environment::AtExit(void (*cb)(void* arg), void* arg) {
1060 at_exit_functions_.push_front(ExitCallback{cb, arg});
1061 }
1062
RunAndClearInterrupts()1063 void Environment::RunAndClearInterrupts() {
1064 while (native_immediates_interrupts_.size() > 0) {
1065 NativeImmediateQueue queue;
1066 {
1067 Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
1068 queue.ConcatMove(std::move(native_immediates_interrupts_));
1069 }
1070 DebugSealHandleScope seal_handle_scope(isolate());
1071
1072 while (auto head = queue.Shift())
1073 head->Call(this);
1074 }
1075 }
1076
RunAndClearNativeImmediates(bool only_refed)1077 void Environment::RunAndClearNativeImmediates(bool only_refed) {
1078 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment),
1079 "RunAndClearNativeImmediates");
1080 HandleScope handle_scope(isolate_);
1081 // In case the Isolate is no longer accessible just use an empty Local. This
1082 // is not an issue for InternalCallbackScope as this case is already handled
1083 // in its constructor but we avoid calls into v8 which can crash the process
1084 // in debug builds.
1085 Local<Object> obj =
1086 can_call_into_js() ? Object::New(isolate_) : Local<Object>();
1087 InternalCallbackScope cb_scope(this, obj, {0, 0});
1088
1089 size_t ref_count = 0;
1090
1091 // Handle interrupts first. These functions are not allowed to throw
1092 // exceptions, so we do not need to handle that.
1093 RunAndClearInterrupts();
1094
1095 auto drain_list = [&](NativeImmediateQueue* queue) {
1096 TryCatchScope try_catch(this);
1097 DebugSealHandleScope seal_handle_scope(isolate());
1098 while (auto head = queue->Shift()) {
1099 bool is_refed = head->flags() & CallbackFlags::kRefed;
1100 if (is_refed)
1101 ref_count++;
1102
1103 if (is_refed || !only_refed)
1104 head->Call(this);
1105
1106 head.reset(); // Destroy now so that this is also observed by try_catch.
1107
1108 if (UNLIKELY(try_catch.HasCaught())) {
1109 if (!try_catch.HasTerminated() && can_call_into_js())
1110 errors::TriggerUncaughtException(isolate(), try_catch);
1111
1112 return true;
1113 }
1114 }
1115 return false;
1116 };
1117 while (drain_list(&native_immediates_)) {}
1118
1119 immediate_info()->ref_count_dec(ref_count);
1120
1121 if (immediate_info()->ref_count() == 0)
1122 ToggleImmediateRef(false);
1123
1124 // It is safe to check .size() first, because there is a causal relationship
1125 // between pushes to the threadsafe immediate list and this function being
1126 // called. For the common case, it's worth checking the size first before
1127 // establishing a mutex lock.
1128 // This is intentionally placed after the `ref_count` handling, because when
1129 // refed threadsafe immediates are created, they are not counted towards the
1130 // count in immediate_info() either.
1131 NativeImmediateQueue threadsafe_immediates;
1132 if (native_immediates_threadsafe_.size() > 0) {
1133 Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
1134 threadsafe_immediates.ConcatMove(std::move(native_immediates_threadsafe_));
1135 }
1136 while (drain_list(&threadsafe_immediates)) {}
1137 }
1138
RequestInterruptFromV8()1139 void Environment::RequestInterruptFromV8() {
1140 // The Isolate may outlive the Environment, so some logic to handle the
1141 // situation in which the Environment is destroyed before the handler runs
1142 // is required.
1143
1144 // We allocate a new pointer to a pointer to this Environment instance, and
1145 // try to set it as interrupt_data_. If interrupt_data_ was already set, then
1146 // callbacks are already scheduled to run and we can delete our own pointer
1147 // and just return. If it was nullptr previously, the Environment** is stored;
1148 // ~Environment sets the Environment* contained in it to nullptr, so that
1149 // the callback can check whether ~Environment has already run and it is thus
1150 // not safe to access the Environment instance itself.
1151 Environment** interrupt_data = new Environment*(this);
1152 Environment** dummy = nullptr;
1153 if (!interrupt_data_.compare_exchange_strong(dummy, interrupt_data)) {
1154 delete interrupt_data;
1155 return; // Already scheduled.
1156 }
1157
1158 isolate()->RequestInterrupt([](Isolate* isolate, void* data) {
1159 std::unique_ptr<Environment*> env_ptr { static_cast<Environment**>(data) };
1160 Environment* env = *env_ptr;
1161 if (env == nullptr) {
1162 // The Environment has already been destroyed. That should be okay; any
1163 // callback added before the Environment shuts down would have been
1164 // handled during cleanup.
1165 return;
1166 }
1167 env->interrupt_data_.store(nullptr);
1168 env->RunAndClearInterrupts();
1169 }, interrupt_data);
1170 }
1171
ScheduleTimer(int64_t duration_ms)1172 void Environment::ScheduleTimer(int64_t duration_ms) {
1173 if (started_cleanup_) return;
1174 uv_timer_start(timer_handle(), RunTimers, duration_ms, 0);
1175 }
1176
ToggleTimerRef(bool ref)1177 void Environment::ToggleTimerRef(bool ref) {
1178 if (started_cleanup_) return;
1179
1180 if (ref) {
1181 uv_ref(reinterpret_cast<uv_handle_t*>(timer_handle()));
1182 } else {
1183 uv_unref(reinterpret_cast<uv_handle_t*>(timer_handle()));
1184 }
1185 }
1186
RunTimers(uv_timer_t * handle)1187 void Environment::RunTimers(uv_timer_t* handle) {
1188 Environment* env = Environment::from_timer_handle(handle);
1189 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "RunTimers");
1190
1191 if (!env->can_call_into_js())
1192 return;
1193
1194 HandleScope handle_scope(env->isolate());
1195 Context::Scope context_scope(env->context());
1196
1197 Local<Object> process = env->process_object();
1198 InternalCallbackScope scope(env, process, {0, 0});
1199
1200 Local<Function> cb = env->timers_callback_function();
1201 MaybeLocal<Value> ret;
1202 Local<Value> arg = env->GetNow();
1203 // This code will loop until all currently due timers will process. It is
1204 // impossible for us to end up in an infinite loop due to how the JS-side
1205 // is structured.
1206 do {
1207 TryCatchScope try_catch(env);
1208 try_catch.SetVerbose(true);
1209 ret = cb->Call(env->context(), process, 1, &arg);
1210 } while (ret.IsEmpty() && env->can_call_into_js());
1211
1212 // NOTE(apapirovski): If it ever becomes possible that `call_into_js` above
1213 // is reset back to `true` after being previously set to `false` then this
1214 // code becomes invalid and needs to be rewritten. Otherwise catastrophic
1215 // timers corruption will occur and all timers behaviour will become
1216 // entirely unpredictable.
1217 if (ret.IsEmpty())
1218 return;
1219
1220 // To allow for less JS-C++ boundary crossing, the value returned from JS
1221 // serves a few purposes:
1222 // 1. If it's 0, no more timers exist and the handle should be unrefed
1223 // 2. If it's > 0, the value represents the next timer's expiry and there
1224 // is at least one timer remaining that is refed.
1225 // 3. If it's < 0, the absolute value represents the next timer's expiry
1226 // and there are no timers that are refed.
1227 int64_t expiry_ms =
1228 ret.ToLocalChecked()->IntegerValue(env->context()).FromJust();
1229
1230 uv_handle_t* h = reinterpret_cast<uv_handle_t*>(handle);
1231
1232 if (expiry_ms != 0) {
1233 int64_t duration_ms =
1234 llabs(expiry_ms) - (uv_now(env->event_loop()) - env->timer_base());
1235
1236 env->ScheduleTimer(duration_ms > 0 ? duration_ms : 1);
1237
1238 if (expiry_ms > 0)
1239 uv_ref(h);
1240 else
1241 uv_unref(h);
1242 } else {
1243 uv_unref(h);
1244 }
1245 }
1246
1247
CheckImmediate(uv_check_t * handle)1248 void Environment::CheckImmediate(uv_check_t* handle) {
1249 Environment* env = Environment::from_immediate_check_handle(handle);
1250 TRACE_EVENT0(TRACING_CATEGORY_NODE1(environment), "CheckImmediate");
1251
1252 HandleScope scope(env->isolate());
1253 Context::Scope context_scope(env->context());
1254
1255 env->RunAndClearNativeImmediates();
1256
1257 if (env->immediate_info()->count() == 0 || !env->can_call_into_js())
1258 return;
1259
1260 do {
1261 MakeCallback(env->isolate(),
1262 env->process_object(),
1263 env->immediate_callback_function(),
1264 0,
1265 nullptr,
1266 {0, 0}).ToLocalChecked();
1267 } while (env->immediate_info()->has_outstanding() && env->can_call_into_js());
1268
1269 if (env->immediate_info()->ref_count() == 0)
1270 env->ToggleImmediateRef(false);
1271 }
1272
ToggleImmediateRef(bool ref)1273 void Environment::ToggleImmediateRef(bool ref) {
1274 if (started_cleanup_) return;
1275
1276 if (ref) {
1277 // Idle handle is needed only to stop the event loop from blocking in poll.
1278 uv_idle_start(immediate_idle_handle(), [](uv_idle_t*){ });
1279 } else {
1280 uv_idle_stop(immediate_idle_handle());
1281 }
1282 }
1283
1284
GetNow()1285 Local<Value> Environment::GetNow() {
1286 uv_update_time(event_loop());
1287 uint64_t now = uv_now(event_loop());
1288 CHECK_GE(now, timer_base());
1289 now -= timer_base();
1290 if (now <= 0xffffffff)
1291 return Integer::NewFromUnsigned(isolate(), static_cast<uint32_t>(now));
1292 else
1293 return Number::New(isolate(), static_cast<double>(now));
1294 }
1295
CollectExceptionInfo(Environment * env,Local<Object> obj,int errorno,const char * err_string,const char * syscall,const char * message,const char * path,const char * dest)1296 void CollectExceptionInfo(Environment* env,
1297 Local<Object> obj,
1298 int errorno,
1299 const char* err_string,
1300 const char* syscall,
1301 const char* message,
1302 const char* path,
1303 const char* dest) {
1304 obj->Set(env->context(),
1305 env->errno_string(),
1306 Integer::New(env->isolate(), errorno)).Check();
1307
1308 obj->Set(env->context(), env->code_string(),
1309 OneByteString(env->isolate(), err_string)).Check();
1310
1311 if (message != nullptr) {
1312 obj->Set(env->context(), env->message_string(),
1313 OneByteString(env->isolate(), message)).Check();
1314 }
1315
1316 Local<Value> path_buffer;
1317 if (path != nullptr) {
1318 path_buffer =
1319 Buffer::Copy(env->isolate(), path, strlen(path)).ToLocalChecked();
1320 obj->Set(env->context(), env->path_string(), path_buffer).Check();
1321 }
1322
1323 Local<Value> dest_buffer;
1324 if (dest != nullptr) {
1325 dest_buffer =
1326 Buffer::Copy(env->isolate(), dest, strlen(dest)).ToLocalChecked();
1327 obj->Set(env->context(), env->dest_string(), dest_buffer).Check();
1328 }
1329
1330 if (syscall != nullptr) {
1331 obj->Set(env->context(), env->syscall_string(),
1332 OneByteString(env->isolate(), syscall)).Check();
1333 }
1334 }
1335
CollectUVExceptionInfo(Local<Value> object,int errorno,const char * syscall,const char * message,const char * path,const char * dest)1336 void Environment::CollectUVExceptionInfo(Local<Value> object,
1337 int errorno,
1338 const char* syscall,
1339 const char* message,
1340 const char* path,
1341 const char* dest) {
1342 if (!object->IsObject() || errorno == 0)
1343 return;
1344
1345 Local<Object> obj = object.As<Object>();
1346 const char* err_string = uv_err_name(errorno);
1347
1348 if (message == nullptr || message[0] == '\0') {
1349 message = uv_strerror(errorno);
1350 }
1351
1352 node::CollectExceptionInfo(this, obj, errorno, err_string,
1353 syscall, message, path, dest);
1354 }
1355
ImmediateInfo(Isolate * isolate,const SerializeInfo * info)1356 ImmediateInfo::ImmediateInfo(Isolate* isolate, const SerializeInfo* info)
1357 : fields_(isolate, kFieldsCount, MAYBE_FIELD_PTR(info, fields)) {}
1358
Serialize(Local<Context> context,SnapshotCreator * creator)1359 ImmediateInfo::SerializeInfo ImmediateInfo::Serialize(
1360 Local<Context> context, SnapshotCreator* creator) {
1361 return {fields_.Serialize(context, creator)};
1362 }
1363
Deserialize(Local<Context> context)1364 void ImmediateInfo::Deserialize(Local<Context> context) {
1365 fields_.Deserialize(context);
1366 }
1367
operator <<(std::ostream & output,const ImmediateInfo::SerializeInfo & i)1368 std::ostream& operator<<(std::ostream& output,
1369 const ImmediateInfo::SerializeInfo& i) {
1370 output << "{ " << i.fields << " }";
1371 return output;
1372 }
1373
MemoryInfo(MemoryTracker * tracker) const1374 void ImmediateInfo::MemoryInfo(MemoryTracker* tracker) const {
1375 tracker->TrackField("fields", fields_);
1376 }
1377
Serialize(Local<Context> context,SnapshotCreator * creator)1378 TickInfo::SerializeInfo TickInfo::Serialize(Local<Context> context,
1379 SnapshotCreator* creator) {
1380 return {fields_.Serialize(context, creator)};
1381 }
1382
Deserialize(Local<Context> context)1383 void TickInfo::Deserialize(Local<Context> context) {
1384 fields_.Deserialize(context);
1385 }
1386
operator <<(std::ostream & output,const TickInfo::SerializeInfo & i)1387 std::ostream& operator<<(std::ostream& output,
1388 const TickInfo::SerializeInfo& i) {
1389 output << "{ " << i.fields << " }";
1390 return output;
1391 }
1392
MemoryInfo(MemoryTracker * tracker) const1393 void TickInfo::MemoryInfo(MemoryTracker* tracker) const {
1394 tracker->TrackField("fields", fields_);
1395 }
1396
TickInfo(Isolate * isolate,const SerializeInfo * info)1397 TickInfo::TickInfo(Isolate* isolate, const SerializeInfo* info)
1398 : fields_(
1399 isolate, kFieldsCount, info == nullptr ? nullptr : &(info->fields)) {}
1400
AsyncHooks(Isolate * isolate,const SerializeInfo * info)1401 AsyncHooks::AsyncHooks(Isolate* isolate, const SerializeInfo* info)
1402 : async_ids_stack_(isolate, 16 * 2, MAYBE_FIELD_PTR(info, async_ids_stack)),
1403 fields_(isolate, kFieldsCount, MAYBE_FIELD_PTR(info, fields)),
1404 async_id_fields_(
1405 isolate, kUidFieldsCount, MAYBE_FIELD_PTR(info, async_id_fields)),
1406 info_(info) {
1407 HandleScope handle_scope(isolate);
1408 if (info == nullptr) {
1409 clear_async_id_stack();
1410
1411 // Always perform async_hooks checks, not just when async_hooks is enabled.
1412 // TODO(AndreasMadsen): Consider removing this for LTS releases.
1413 // See discussion in https://github.com/nodejs/node/pull/15454
1414 // When removing this, do it by reverting the commit. Otherwise the test
1415 // and flag changes won't be included.
1416 fields_[kCheck] = 1;
1417
1418 // kDefaultTriggerAsyncId should be -1, this indicates that there is no
1419 // specified default value and it should fallback to the executionAsyncId.
1420 // 0 is not used as the magic value, because that indicates a missing
1421 // context which is different from a default context.
1422 async_id_fields_[AsyncHooks::kDefaultTriggerAsyncId] = -1;
1423
1424 // kAsyncIdCounter should start at 1 because that'll be the id the execution
1425 // context during bootstrap (code that runs before entering uv_run()).
1426 async_id_fields_[AsyncHooks::kAsyncIdCounter] = 1;
1427 }
1428 }
1429
Deserialize(Local<Context> context)1430 void AsyncHooks::Deserialize(Local<Context> context) {
1431 async_ids_stack_.Deserialize(context);
1432 fields_.Deserialize(context);
1433 async_id_fields_.Deserialize(context);
1434
1435 Local<Array> js_execution_async_resources;
1436 if (info_->js_execution_async_resources != 0) {
1437 js_execution_async_resources =
1438 context->GetDataFromSnapshotOnce<Array>(
1439 info_->js_execution_async_resources).ToLocalChecked();
1440 } else {
1441 js_execution_async_resources = Array::New(context->GetIsolate());
1442 }
1443 js_execution_async_resources_.Reset(
1444 context->GetIsolate(), js_execution_async_resources);
1445
1446 // The native_execution_async_resources_ field requires v8::Local<> instances
1447 // for async calls whose resources were on the stack as JS objects when they
1448 // were entered. We cannot recreate this here; however, storing these values
1449 // on the JS equivalent gives the same result, so we do that instead.
1450 for (size_t i = 0; i < info_->native_execution_async_resources.size(); ++i) {
1451 if (info_->native_execution_async_resources[i] == SIZE_MAX)
1452 continue;
1453 Local<Object> obj = context->GetDataFromSnapshotOnce<Object>(
1454 info_->native_execution_async_resources[i])
1455 .ToLocalChecked();
1456 js_execution_async_resources->Set(context, i, obj).Check();
1457 }
1458 info_ = nullptr;
1459 }
1460
operator <<(std::ostream & output,const AsyncHooks::SerializeInfo & i)1461 std::ostream& operator<<(std::ostream& output,
1462 const AsyncHooks::SerializeInfo& i) {
1463 output << "{\n"
1464 << " " << i.async_ids_stack << ", // async_ids_stack\n"
1465 << " " << i.fields << ", // fields\n"
1466 << " " << i.async_id_fields << ", // async_id_fields\n"
1467 << " " << i.js_execution_async_resources
1468 << ", // js_execution_async_resources\n"
1469 << " " << i.native_execution_async_resources
1470 << ", // native_execution_async_resources\n"
1471 << "}";
1472 return output;
1473 }
1474
Serialize(Local<Context> context,SnapshotCreator * creator)1475 AsyncHooks::SerializeInfo AsyncHooks::Serialize(Local<Context> context,
1476 SnapshotCreator* creator) {
1477 SerializeInfo info;
1478 // TODO(joyeecheung): some of these probably don't need to be serialized.
1479 info.async_ids_stack = async_ids_stack_.Serialize(context, creator);
1480 info.fields = fields_.Serialize(context, creator);
1481 info.async_id_fields = async_id_fields_.Serialize(context, creator);
1482 if (!js_execution_async_resources_.IsEmpty()) {
1483 info.js_execution_async_resources = creator->AddData(
1484 context, js_execution_async_resources_.Get(context->GetIsolate()));
1485 CHECK_NE(info.js_execution_async_resources, 0);
1486 } else {
1487 info.js_execution_async_resources = 0;
1488 }
1489
1490 info.native_execution_async_resources.resize(
1491 native_execution_async_resources_.size());
1492 for (size_t i = 0; i < native_execution_async_resources_.size(); i++) {
1493 info.native_execution_async_resources[i] =
1494 native_execution_async_resources_[i].IsEmpty() ? SIZE_MAX :
1495 creator->AddData(
1496 context,
1497 native_execution_async_resources_[i]);
1498 }
1499
1500 // At the moment, promise hooks are not supported in the startup snapshot.
1501 // TODO(joyeecheung): support promise hooks in the startup snapshot.
1502 CHECK(js_promise_hooks_[0].IsEmpty());
1503 CHECK(js_promise_hooks_[1].IsEmpty());
1504 CHECK(js_promise_hooks_[2].IsEmpty());
1505 CHECK(js_promise_hooks_[3].IsEmpty());
1506
1507 return info;
1508 }
1509
MemoryInfo(MemoryTracker * tracker) const1510 void AsyncHooks::MemoryInfo(MemoryTracker* tracker) const {
1511 tracker->TrackField("async_ids_stack", async_ids_stack_);
1512 tracker->TrackField("fields", fields_);
1513 tracker->TrackField("async_id_fields", async_id_fields_);
1514 tracker->TrackField("js_promise_hooks", js_promise_hooks_);
1515 }
1516
grow_async_ids_stack()1517 void AsyncHooks::grow_async_ids_stack() {
1518 async_ids_stack_.reserve(async_ids_stack_.Length() * 3);
1519
1520 env()->async_hooks_binding()->Set(
1521 env()->context(),
1522 env()->async_ids_stack_string(),
1523 async_ids_stack_.GetJSArray()).Check();
1524 }
1525
FailWithCorruptedAsyncStack(double expected_async_id)1526 void AsyncHooks::FailWithCorruptedAsyncStack(double expected_async_id) {
1527 fprintf(stderr,
1528 "Error: async hook stack has become corrupted ("
1529 "actual: %.f, expected: %.f)\n",
1530 async_id_fields_.GetValue(kExecutionAsyncId),
1531 expected_async_id);
1532 DumpBacktrace(stderr);
1533 fflush(stderr);
1534 if (!env()->abort_on_uncaught_exception())
1535 exit(1);
1536 fprintf(stderr, "\n");
1537 fflush(stderr);
1538 ABORT_NO_BACKTRACE();
1539 }
1540
Exit(int exit_code)1541 void Environment::Exit(int exit_code) {
1542 if (options()->trace_exit) {
1543 HandleScope handle_scope(isolate());
1544 Isolate::DisallowJavascriptExecutionScope disallow_js(
1545 isolate(), Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE);
1546
1547 if (is_main_thread()) {
1548 fprintf(stderr, "(node:%d) ", uv_os_getpid());
1549 } else {
1550 fprintf(stderr, "(node:%d, thread:%" PRIu64 ") ",
1551 uv_os_getpid(), thread_id());
1552 }
1553
1554 fprintf(
1555 stderr, "WARNING: Exited the environment with code %d\n", exit_code);
1556 PrintStackTrace(isolate(),
1557 StackTrace::CurrentStackTrace(
1558 isolate(), stack_trace_limit(), StackTrace::kDetailed));
1559 }
1560 process_exit_handler_(this, exit_code);
1561 }
1562
stop_sub_worker_contexts()1563 void Environment::stop_sub_worker_contexts() {
1564 DCHECK_EQ(Isolate::GetCurrent(), isolate());
1565
1566 while (!sub_worker_contexts_.empty()) {
1567 Worker* w = *sub_worker_contexts_.begin();
1568 remove_sub_worker_context(w);
1569 w->Exit(1);
1570 w->JoinThread();
1571 }
1572 }
1573
worker_parent_env() const1574 Environment* Environment::worker_parent_env() const {
1575 if (worker_context() == nullptr) return nullptr;
1576 return worker_context()->env();
1577 }
1578
AddUnmanagedFd(int fd)1579 void Environment::AddUnmanagedFd(int fd) {
1580 if (!tracks_unmanaged_fds()) return;
1581 auto result = unmanaged_fds_.insert(fd);
1582 if (!result.second) {
1583 ProcessEmitWarning(
1584 this, "File descriptor %d opened in unmanaged mode twice", fd);
1585 }
1586 }
1587
RemoveUnmanagedFd(int fd)1588 void Environment::RemoveUnmanagedFd(int fd) {
1589 if (!tracks_unmanaged_fds()) return;
1590 size_t removed_count = unmanaged_fds_.erase(fd);
1591 if (removed_count == 0) {
1592 ProcessEmitWarning(
1593 this, "File descriptor %d closed but not opened in unmanaged mode", fd);
1594 }
1595 }
1596
PrintInfoForSnapshotIfDebug()1597 void Environment::PrintInfoForSnapshotIfDebug() {
1598 if (enabled_debug_list()->enabled(DebugCategory::MKSNAPSHOT)) {
1599 fprintf(stderr, "At the exit of the Environment:\n");
1600 principal_realm()->PrintInfoForSnapshot();
1601 }
1602 }
1603
Serialize(SnapshotCreator * creator)1604 EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) {
1605 EnvSerializeInfo info;
1606 Local<Context> ctx = context();
1607
1608 info.async_hooks = async_hooks_.Serialize(ctx, creator);
1609 info.immediate_info = immediate_info_.Serialize(ctx, creator);
1610 info.timeout_info = timeout_info_.Serialize(ctx, creator);
1611 info.tick_info = tick_info_.Serialize(ctx, creator);
1612 info.performance_state = performance_state_->Serialize(ctx, creator);
1613 info.exiting = exiting_.Serialize(ctx, creator);
1614 info.stream_base_state = stream_base_state_.Serialize(ctx, creator);
1615 info.should_abort_on_uncaught_toggle =
1616 should_abort_on_uncaught_toggle_.Serialize(ctx, creator);
1617
1618 info.principal_realm = principal_realm_->Serialize(creator);
1619 // For now we only support serialization of the main context.
1620 // TODO(joyeecheung): support de/serialization of vm contexts.
1621 CHECK_EQ(contexts_.size(), 1);
1622 CHECK_EQ(contexts_[0], context());
1623 return info;
1624 }
1625
EnqueueDeserializeRequest(DeserializeRequestCallback cb,Local<Object> holder,int index,InternalFieldInfoBase * info)1626 void Environment::EnqueueDeserializeRequest(DeserializeRequestCallback cb,
1627 Local<Object> holder,
1628 int index,
1629 InternalFieldInfoBase* info) {
1630 DCHECK_EQ(index, BaseObject::kEmbedderType);
1631 DeserializeRequest request{cb, {isolate(), holder}, index, info};
1632 deserialize_requests_.push_back(std::move(request));
1633 }
1634
RunDeserializeRequests()1635 void Environment::RunDeserializeRequests() {
1636 HandleScope scope(isolate());
1637 Local<Context> ctx = context();
1638 Isolate* is = isolate();
1639 while (!deserialize_requests_.empty()) {
1640 DeserializeRequest request(std::move(deserialize_requests_.front()));
1641 deserialize_requests_.pop_front();
1642 Local<Object> holder = request.holder.Get(is);
1643 request.cb(ctx, holder, request.index, request.info);
1644 request.holder.Reset();
1645 request.info->Delete();
1646 }
1647 }
1648
DeserializeProperties(const EnvSerializeInfo * info)1649 void Environment::DeserializeProperties(const EnvSerializeInfo* info) {
1650 Local<Context> ctx = context();
1651
1652 if (enabled_debug_list_.enabled(DebugCategory::MKSNAPSHOT)) {
1653 fprintf(stderr, "deserializing EnvSerializeInfo...\n");
1654 std::cerr << *info << "\n";
1655 }
1656
1657 RunDeserializeRequests();
1658
1659 async_hooks_.Deserialize(ctx);
1660 immediate_info_.Deserialize(ctx);
1661 timeout_info_.Deserialize(ctx);
1662 tick_info_.Deserialize(ctx);
1663 performance_state_->Deserialize(ctx);
1664 exiting_.Deserialize(ctx);
1665 stream_base_state_.Deserialize(ctx);
1666 should_abort_on_uncaught_toggle_.Deserialize(ctx);
1667
1668 principal_realm_->DeserializeProperties(&info->principal_realm);
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("destroy_async_id_list", destroy_async_id_list_);
1843 tracker->TrackField("exec_argv", exec_argv_);
1844 tracker->TrackField("exiting", exiting_);
1845 tracker->TrackField("should_abort_on_uncaught_toggle",
1846 should_abort_on_uncaught_toggle_);
1847 tracker->TrackField("stream_base_state", stream_base_state_);
1848 tracker->TrackField("cleanup_queue", cleanup_queue_);
1849 tracker->TrackField("async_hooks", async_hooks_);
1850 tracker->TrackField("immediate_info", immediate_info_);
1851 tracker->TrackField("timeout_info", timeout_info_);
1852 tracker->TrackField("tick_info", tick_info_);
1853 tracker->TrackField("principal_realm", principal_realm_);
1854
1855 // FIXME(joyeecheung): track other fields in Environment.
1856 // Currently MemoryTracker is unable to track these
1857 // correctly:
1858 // - Internal types that do not implement MemoryRetainer yet
1859 // - STL containers with MemoryRetainer* inside
1860 // - STL containers with numeric types inside that should not have their
1861 // nodes elided e.g. numeric keys in maps.
1862 // We also need to make sure that when we add a non-pointer field as its own
1863 // node, we shift its sizeof() size out of the Environment node.
1864 }
1865
RunWeakRefCleanup()1866 void Environment::RunWeakRefCleanup() {
1867 isolate()->ClearKeptObjects();
1868 }
1869 } // namespace node
1870