diff options
author | Joyee Cheung <joyeec9h3@gmail.com> | 2019-04-21 07:47:32 +0300 |
---|---|---|
committer | Joyee Cheung <joyeec9h3@gmail.com> | 2020-07-18 06:15:57 +0300 |
commit | 1faf6f459f220bb67866f12b30626ef7856876ee (patch) | |
tree | d1308396c07e8289f8d987178c294141f4687889 /src/env.cc | |
parent | f04538761f5bb3c334d3c8d16d093ac0916ff3bc (diff) |
src: snapshot Environment upon instantiation
This includes the initial Environment (without running bootstrap
scripts) into the builtin snapshot
PR-URL: https://github.com/nodejs/node/pull/32984
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Diffstat (limited to 'src/env.cc')
-rw-r--r-- | src/env.cc | 376 |
1 files changed, 350 insertions, 26 deletions
diff --git a/src/env.cc b/src/env.cc index 494c1877f44..e30e7d6e1da 100644 --- a/src/env.cc +++ b/src/env.cc @@ -21,7 +21,9 @@ #include <algorithm> #include <atomic> +#include <cinttypes> #include <cstdio> +#include <iostream> #include <memory> namespace node { @@ -309,18 +311,26 @@ Environment::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) : isolate_(isolate), isolate_data_(isolate_data), - immediate_info_(isolate), - tick_info_(isolate), + async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)), + immediate_info_(isolate, MAYBE_FIELD_PTR(env_info, immediate_info)), + tick_info_(isolate, MAYBE_FIELD_PTR(env_info, tick_info)), timer_base_(uv_now(isolate_data->event_loop())), exec_argv_(exec_args), argv_(args), exec_path_(GetExecPath(args)), - should_abort_on_uncaught_toggle_(isolate_, 1), - stream_base_state_(isolate_, StreamBase::kNumStreamBaseStateFields), + should_abort_on_uncaught_toggle_( + isolate_, + 1, + MAYBE_FIELD_PTR(env_info, should_abort_on_uncaught_toggle)), + stream_base_state_(isolate_, + StreamBase::kNumStreamBaseStateFields, + MAYBE_FIELD_PTR(env_info, stream_base_state)), + environment_start_time_(PERFORMANCE_NOW()), flags_(flags), thread_id_(thread_id.id == static_cast<uint64_t>(-1) ? AllocateEnvironmentThreadId().id @@ -370,14 +380,8 @@ Environment::Environment(IsolateData* isolate_data, destroy_async_id_list_.reserve(512); - performance_state_ = std::make_unique<performance::PerformanceState>(isolate); - performance_state_->Mark( - performance::NODE_PERFORMANCE_MILESTONE_ENVIRONMENT); - performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_NODE_START, - per_process::node_start_time); - performance_state_->Mark( - performance::NODE_PERFORMANCE_MILESTONE_V8_START, - performance::performance_v8_start); + performance_state_ = std::make_unique<performance::PerformanceState>( + isolate, MAYBE_FIELD_PTR(env_info, performance_state)); if (*TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( TRACING_CATEGORY_NODE1(environment)) != 0) { @@ -395,13 +399,6 @@ Environment::Environment(IsolateData* isolate_data, std::move(traced_value)); } - // By default, always abort when --abort-on-uncaught-exception was passed. - should_abort_on_uncaught_toggle_[0] = 1; - - if (options_->no_force_async_hooks_checks) { - async_hooks_.no_force_checks(); - } - // This adjusts the return value of base_object_count() so that tests that // check the count do not have to account for internally created BaseObjects. initial_base_object_count_ = base_object_count(); @@ -411,23 +408,42 @@ Environment::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) : Environment(isolate_data, context->GetIsolate(), args, exec_args, + env_info, flags, thread_id) { - InitializeMainContext(context); + InitializeMainContext(context, env_info); } -void Environment::InitializeMainContext(Local<Context> context) { +void Environment::InitializeMainContext(Local<Context> context, + const EnvSerializeInfo* env_info) { context_.Reset(context->GetIsolate(), context); AssignToContext(context, ContextInfo("")); - // TODO(joyeecheung): deserialize when the snapshot covers the environment - // properties. - CreateProperties(); + if (env_info != nullptr) { + DeserializeProperties(env_info); + } else { + CreateProperties(); + } + + if (options_->no_force_async_hooks_checks) { + async_hooks_.no_force_checks(); + } + + // By default, always abort when --abort-on-uncaught-exception was passed. + should_abort_on_uncaught_toggle_[0] = 1; + + performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_ENVIRONMENT, + environment_start_time_); + performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_NODE_START, + per_process::node_start_time); + performance_state_->Mark(performance::NODE_PERFORMANCE_MILESTONE_V8_START, + performance::performance_v8_start); // This adjusts the return value of base_object_count() so that tests that // check the count do not have to account for internally created BaseObjects. @@ -474,8 +490,8 @@ Environment::~Environment() { inspector_agent_.reset(); #endif - context()->SetAlignedPointerInEmbedderData( - ContextEmbedderIndex::kEnvironment, nullptr); + context()->SetAlignedPointerInEmbedderData(ContextEmbedderIndex::kEnvironment, + nullptr); if (trace_state_observer_) { tracing::AgentWriterHandle* writer = GetTracingAgentWriter(); @@ -951,14 +967,154 @@ void Environment::CollectUVExceptionInfo(Local<Value> object, syscall, message, path, dest); } +ImmediateInfo::ImmediateInfo(v8::Isolate* isolate, const SerializeInfo* info) + : fields_(isolate, kFieldsCount, MAYBE_FIELD_PTR(info, fields)) {} + +ImmediateInfo::SerializeInfo ImmediateInfo::Serialize( + v8::Local<v8::Context> context, v8::SnapshotCreator* creator) { + return {fields_.Serialize(context, creator)}; +} + +void ImmediateInfo::Deserialize(Local<Context> context) { + fields_.Deserialize(context); +} + +std::ostream& operator<<(std::ostream& output, + const ImmediateInfo::SerializeInfo& i) { + output << "{ " << i.fields << " }"; + return output; +} + void ImmediateInfo::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("fields", fields_); } +TickInfo::SerializeInfo TickInfo::Serialize(v8::Local<v8::Context> context, + v8::SnapshotCreator* creator) { + return {fields_.Serialize(context, creator)}; +} + +void TickInfo::Deserialize(Local<Context> context) { + fields_.Deserialize(context); +} + +std::ostream& operator<<(std::ostream& output, + const TickInfo::SerializeInfo& i) { + output << "{ " << i.fields << " }"; + return output; +} + void TickInfo::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("fields", fields_); } +TickInfo::TickInfo(v8::Isolate* isolate, const SerializeInfo* info) + : fields_( + isolate, kFieldsCount, info == nullptr ? nullptr : &(info->fields)) {} + +AsyncHooks::AsyncHooks(v8::Isolate* isolate, const SerializeInfo* info) + : async_ids_stack_(isolate, 16 * 2, MAYBE_FIELD_PTR(info, async_ids_stack)), + fields_(isolate, kFieldsCount, MAYBE_FIELD_PTR(info, fields)), + async_id_fields_( + isolate, kUidFieldsCount, MAYBE_FIELD_PTR(info, async_id_fields)), + info_(info) { + v8::HandleScope handle_scope(isolate); + if (info == nullptr) { + clear_async_id_stack(); + + // Always perform async_hooks checks, not just when async_hooks is enabled. + // TODO(AndreasMadsen): Consider removing this for LTS releases. + // See discussion in https://github.com/nodejs/node/pull/15454 + // When removing this, do it by reverting the commit. Otherwise the test + // and flag changes won't be included. + fields_[kCheck] = 1; + + // kDefaultTriggerAsyncId should be -1, this indicates that there is no + // specified default value and it should fallback to the executionAsyncId. + // 0 is not used as the magic value, because that indicates a missing + // context which is different from a default context. + async_id_fields_[AsyncHooks::kDefaultTriggerAsyncId] = -1; + + // kAsyncIdCounter should start at 1 because that'll be the id the execution + // context during bootstrap (code that runs before entering uv_run()). + async_id_fields_[AsyncHooks::kAsyncIdCounter] = 1; + } +} + +void AsyncHooks::Deserialize(Local<Context> context) { + async_ids_stack_.Deserialize(context); + fields_.Deserialize(context); + async_id_fields_.Deserialize(context); + if (info_->js_execution_async_resources != 0) { + v8::Local<v8::Array> arr = context + ->GetDataFromSnapshotOnce<v8::Array>( + info_->js_execution_async_resources) + .ToLocalChecked(); + js_execution_async_resources_.Reset(context->GetIsolate(), arr); + } + + native_execution_async_resources_.resize( + info_->native_execution_async_resources.size()); + for (size_t i = 0; i < info_->native_execution_async_resources.size(); ++i) { + v8::Local<v8::Object> obj = + context + ->GetDataFromSnapshotOnce<v8::Object>( + info_->native_execution_async_resources[i]) + .ToLocalChecked(); + native_execution_async_resources_[i].Reset(context->GetIsolate(), obj); + } + info_ = nullptr; +} + +std::ostream& operator<<(std::ostream& output, + const std::vector<SnapshotIndex>& v) { + output << "{ "; + for (const SnapshotIndex i : v) { + output << i << ", "; + } + output << " }"; + return output; +} + +std::ostream& operator<<(std::ostream& output, + const AsyncHooks::SerializeInfo& i) { + output << "{\n" + << " " << i.async_ids_stack << ", // async_ids_stack\n" + << " " << i.fields << ", // fields\n" + << " " << i.async_id_fields << ", // async_id_fields\n" + << " " << i.js_execution_async_resources + << ", // js_execution_async_resources\n" + << " " << i.native_execution_async_resources + << ", // native_execution_async_resources\n" + << "}"; + return output; +} + +AsyncHooks::SerializeInfo AsyncHooks::Serialize(Local<Context> context, + SnapshotCreator* creator) { + SerializeInfo info; + info.async_ids_stack = async_ids_stack_.Serialize(context, creator); + info.fields = fields_.Serialize(context, creator); + info.async_id_fields = async_id_fields_.Serialize(context, creator); + if (!js_execution_async_resources_.IsEmpty()) { + info.js_execution_async_resources = creator->AddData( + context, js_execution_async_resources_.Get(context->GetIsolate())); + CHECK_NE(info.js_execution_async_resources, 0); + } else { + info.js_execution_async_resources = 0; + } + + info.native_execution_async_resources.resize( + native_execution_async_resources_.size()); + for (size_t i = 0; i < native_execution_async_resources_.size(); i++) { + info.native_execution_async_resources[i] = creator->AddData( + context, + native_execution_async_resources_[i].Get(context->GetIsolate())); + } + + return info; +} + void AsyncHooks::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("async_ids_stack", async_ids_stack_); tracker->TrackField("fields", fields_); @@ -1030,6 +1186,174 @@ void Environment::RemoveUnmanagedFd(int fd) { } } +void Environment::ForEachBaseObject(BaseObjectIterator iterator) { + size_t i = 0; + for (const auto& hook : cleanup_hooks_) { + BaseObject* obj = hook.GetBaseObject(); + if (obj != nullptr) iterator(i, obj); + i++; + } +} + +void PrintBaseObject(size_t i, BaseObject* obj) { + std::cout << "#" << i << " " << obj << ": " << obj->MemoryInfoName() << "\n"; +} + +void Environment::PrintAllBaseObjects() { + std::cout << "BaseObjects\n"; + ForEachBaseObject(PrintBaseObject); +} + +EnvSerializeInfo Environment::Serialize(SnapshotCreator* creator) { + EnvSerializeInfo info; + Local<Context> ctx = context(); + + info.async_hooks = async_hooks_.Serialize(ctx, creator); + info.immediate_info = immediate_info_.Serialize(ctx, creator); + info.tick_info = tick_info_.Serialize(ctx, creator); + info.performance_state = performance_state_->Serialize(ctx, creator); + info.stream_base_state = stream_base_state_.Serialize(ctx, creator); + info.should_abort_on_uncaught_toggle = + should_abort_on_uncaught_toggle_.Serialize(ctx, creator); + + size_t id = 0; +#define V(PropertyName, TypeName) \ + do { \ + Local<TypeName> field = PropertyName(); \ + if (!field.IsEmpty()) { \ + size_t index = creator->AddData(field); \ + info.persistent_templates.push_back({#PropertyName, id, index}); \ + } \ + id++; \ + } while (0); + ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) +#undef V + + id = 0; +#define V(PropertyName, TypeName) \ + do { \ + Local<TypeName> field = PropertyName(); \ + if (!field.IsEmpty()) { \ + size_t index = creator->AddData(ctx, field); \ + info.persistent_values.push_back({#PropertyName, id, index}); \ + } \ + id++; \ + } while (0); + ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) +#undef V + + info.context = creator->AddData(ctx, context()); + return info; +} + +std::ostream& operator<<(std::ostream& output, + const std::vector<PropInfo>& vec) { + output << "{\n"; + for (const auto& info : vec) { + output << " { \"" << info.name << "\", " << std::to_string(info.id) << ", " + << std::to_string(info.index) << " },\n"; + } + output << "}"; + return output; +} + +std::ostream& operator<<(std::ostream& output, const EnvSerializeInfo& i) { + output << "{\n" + << "// -- async_hooks begins --\n" + << i.async_hooks << ",\n" + << "// -- async_hooks begins --\n" + << i.tick_info << ", // tick_info\n" + << i.immediate_info << ", // immediate_info\n" + << "// -- performance_state begins --\n" + << i.performance_state << ",\n" + << "// -- performance_state ends --\n" + << i.stream_base_state << ", // stream_base_state\n" + << i.should_abort_on_uncaught_toggle + << ", // should_abort_on_uncaught_toggle\n" + << "// -- persistent_templates begins --\n" + << i.persistent_templates << ",\n" + << "// persistent_templates ends --\n" + << "// -- persistent_values begins --\n" + << i.persistent_values << ",\n" + << "// -- persistent_values ends --\n" + << i.context << ", // context\n" + << "}"; + return output; +} + +void Environment::DeserializeProperties(const EnvSerializeInfo* info) { + Local<Context> ctx = context(); + + async_hooks_.Deserialize(ctx); + immediate_info_.Deserialize(ctx); + tick_info_.Deserialize(ctx); + performance_state_->Deserialize(ctx); + stream_base_state_.Deserialize(ctx); + should_abort_on_uncaught_toggle_.Deserialize(ctx); + + if (enabled_debug_list_.enabled(DebugCategory::MKSNAPSHOT)) { + fprintf(stderr, "deserializing...\n"); + std::cerr << *info << "\n"; + } + + const std::vector<PropInfo>& templates = info->persistent_templates; + size_t i = 0; // index to the array + size_t id = 0; +#define V(PropertyName, TypeName) \ + do { \ + if (templates.size() > i && id == templates[i].id) { \ + const PropInfo& d = templates[i]; \ + DCHECK_EQ(d.name, #PropertyName); \ + MaybeLocal<TypeName> maybe_field = \ + isolate_->GetDataFromSnapshotOnce<TypeName>(d.index); \ + Local<TypeName> field; \ + if (!maybe_field.ToLocal(&field)) { \ + fprintf(stderr, \ + "Failed to deserialize environment template " #PropertyName \ + "\n"); \ + } \ + set_##PropertyName(field); \ + i++; \ + } \ + } while (0); \ + id++; + ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V); +#undef V + + i = 0; // index to the array + id = 0; + const std::vector<PropInfo>& values = info->persistent_values; +#define V(PropertyName, TypeName) \ + do { \ + if (values.size() > i && id == values[i].id) { \ + const PropInfo& d = values[i]; \ + DCHECK_EQ(d.name, #PropertyName); \ + MaybeLocal<TypeName> maybe_field = \ + ctx->GetDataFromSnapshotOnce<TypeName>(d.index); \ + Local<TypeName> field; \ + if (!maybe_field.ToLocal(&field)) { \ + fprintf(stderr, \ + "Failed to deserialize environment value " #PropertyName \ + "\n"); \ + } \ + set_##PropertyName(field); \ + i++; \ + } \ + } while (0); \ + id++; + ENVIRONMENT_STRONG_PERSISTENT_VALUES(V); +#undef V + + MaybeLocal<Context> maybe_ctx_from_snapshot = + ctx->GetDataFromSnapshotOnce<Context>(info->context); + Local<Context> ctx_from_snapshot; + if (!maybe_ctx_from_snapshot.ToLocal(&ctx_from_snapshot)) { + fprintf(stderr, + "Failed to deserialize context back reference from the snapshot\n"); + } + CHECK_EQ(ctx_from_snapshot, ctx); +} + void Environment::BuildEmbedderGraph(Isolate* isolate, EmbedderGraph* graph, void* data) { |