diff --git a/node.gyp b/node.gyp index c7107f02953c8a..c89f862c4269ca 100644 --- a/node.gyp +++ b/node.gyp @@ -363,14 +363,19 @@ ], 'sources': [ + 'src/api/callback.cc', + 'src/api/encoding.cc', + 'src/api/environment.cc', + 'src/api/exceptions.cc', + 'src/api/hooks.cc', + 'src/api/utils.cc', + 'src/async_wrap.cc', - 'src/callback_scope.cc', 'src/cares_wrap.cc', 'src/connect_wrap.cc', 'src/connection_wrap.cc', 'src/debug_utils.cc', 'src/env.cc', - 'src/exceptions.cc', 'src/fs_event_wrap.cc', 'src/handle_wrap.cc', 'src/heap_utils.cc', @@ -390,7 +395,6 @@ 'src/node_contextify.cc', 'src/node_credentials.cc', 'src/node_domain.cc', - 'src/node_encoding.cc', 'src/node_env_var.cc', 'src/node_errors.cc', 'src/node_file.cc', @@ -493,6 +497,7 @@ 'src/node_union_bytes.h', 'src/node_url.h', 'src/node_version.h', + 'src/node_v8_platform-inl.h', 'src/node_watchdog.h', 'src/node_worker.h', 'src/pipe_wrap.h', diff --git a/src/callback_scope.cc b/src/api/callback.cc similarity index 100% rename from src/callback_scope.cc rename to src/api/callback.cc diff --git a/src/node_encoding.cc b/src/api/encoding.cc similarity index 100% rename from src/node_encoding.cc rename to src/api/encoding.cc diff --git a/src/api/environment.cc b/src/api/environment.cc new file mode 100644 index 00000000000000..9480fb2b96144c --- /dev/null +++ b/src/api/environment.cc @@ -0,0 +1,214 @@ +#include "env.h" +#include "node.h" +#include "node_context_data.h" +#include "node_errors.h" +#include "node_internals.h" +#include "node_native_module.h" +#include "node_platform.h" +#include "node_process.h" +#include "node_v8_platform-inl.h" +#include "uv.h" + +namespace node { +using v8::Context; +using v8::Function; +using v8::HandleScope; +using v8::Isolate; +using v8::Local; +using v8::MaybeLocal; +using v8::Message; +using v8::MicrotasksPolicy; +using v8::ObjectTemplate; +using v8::String; +using v8::Value; + +static bool AllowWasmCodeGenerationCallback(Local context, + Local) { + Local wasm_code_gen = + context->GetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration); + return wasm_code_gen->IsUndefined() || wasm_code_gen->IsTrue(); +} + +static bool ShouldAbortOnUncaughtException(Isolate* isolate) { + HandleScope scope(isolate); + Environment* env = Environment::GetCurrent(isolate); + return env != nullptr && env->should_abort_on_uncaught_toggle()[0] && + !env->inside_should_not_abort_on_uncaught_scope(); +} + +static void OnMessage(Local message, Local error) { + Isolate* isolate = message->GetIsolate(); + switch (message->ErrorLevel()) { + case Isolate::MessageErrorLevel::kMessageWarning: { + Environment* env = Environment::GetCurrent(isolate); + if (!env) { + break; + } + Utf8Value filename(isolate, message->GetScriptOrigin().ResourceName()); + // (filename):(line) (message) + std::stringstream warning; + warning << *filename; + warning << ":"; + warning << message->GetLineNumber(env->context()).FromMaybe(-1); + warning << " "; + v8::String::Utf8Value msg(isolate, message->Get()); + warning << *msg; + USE(ProcessEmitWarningGeneric(env, warning.str().c_str(), "V8")); + break; + } + case Isolate::MessageErrorLevel::kMessageError: + FatalException(isolate, error, message); + break; + } +} + +void* ArrayBufferAllocator::Allocate(size_t size) { + if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers) + return UncheckedCalloc(size); + else + return UncheckedMalloc(size); +} + +ArrayBufferAllocator* CreateArrayBufferAllocator() { + return new ArrayBufferAllocator(); +} + +void FreeArrayBufferAllocator(ArrayBufferAllocator* allocator) { + delete allocator; +} + +Isolate* NewIsolate(ArrayBufferAllocator* allocator, uv_loop_t* event_loop) { + Isolate::CreateParams params; + params.array_buffer_allocator = allocator; +#ifdef NODE_ENABLE_VTUNE_PROFILING + params.code_event_handler = vTune::GetVtuneCodeEventHandler(); +#endif + + Isolate* isolate = Isolate::Allocate(); + if (isolate == nullptr) return nullptr; + + // Register the isolate on the platform before the isolate gets initialized, + // so that the isolate can access the platform during initialization. + per_process::v8_platform.Platform()->RegisterIsolate(isolate, event_loop); + Isolate::Initialize(isolate, params); + + isolate->AddMessageListenerWithErrorLevel( + OnMessage, + Isolate::MessageErrorLevel::kMessageError | + Isolate::MessageErrorLevel::kMessageWarning); + isolate->SetAbortOnUncaughtExceptionCallback(ShouldAbortOnUncaughtException); + isolate->SetMicrotasksPolicy(MicrotasksPolicy::kExplicit); + isolate->SetFatalErrorHandler(OnFatalError); + isolate->SetAllowWasmCodeGenerationCallback(AllowWasmCodeGenerationCallback); + v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate); + + return isolate; +} + +IsolateData* CreateIsolateData(Isolate* isolate, + uv_loop_t* loop, + MultiIsolatePlatform* platform, + ArrayBufferAllocator* allocator) { + return new IsolateData( + isolate, + loop, + platform, + allocator != nullptr ? allocator->zero_fill_field() : nullptr); +} + +void FreeIsolateData(IsolateData* isolate_data) { + delete isolate_data; +} + +Environment* CreateEnvironment(IsolateData* isolate_data, + Local context, + int argc, + const char* const* argv, + int exec_argc, + const char* const* exec_argv) { + Isolate* isolate = context->GetIsolate(); + HandleScope handle_scope(isolate); + Context::Scope context_scope(context); + // TODO(addaleax): This is a much better place for parsing per-Environment + // options than the global parse call. + std::vector args(argv, argv + argc); + std::vector exec_args(exec_argv, exec_argv + exec_argc); + Environment* env = new Environment(isolate_data, context); + env->Start(per_process::v8_is_profiling); + env->ProcessCliArgs(args, exec_args); + return env; +} + +void FreeEnvironment(Environment* env) { + env->RunCleanup(); + delete env; +} + +Environment* GetCurrentEnvironment(Local context) { + return Environment::GetCurrent(context); +} + +MultiIsolatePlatform* GetMainThreadMultiIsolatePlatform() { + return per_process::v8_platform.Platform(); +} + +MultiIsolatePlatform* CreatePlatform( + int thread_pool_size, + node::tracing::TracingController* tracing_controller) { + return new NodePlatform(thread_pool_size, tracing_controller); +} + +MultiIsolatePlatform* InitializeV8Platform(int thread_pool_size) { + per_process::v8_platform.Initialize(thread_pool_size); + return per_process::v8_platform.Platform(); +} + +void FreePlatform(MultiIsolatePlatform* platform) { + delete platform; +} + +Local NewContext(Isolate* isolate, + Local object_template) { + auto context = Context::New(isolate, nullptr, object_template); + if (context.IsEmpty()) return context; + HandleScope handle_scope(isolate); + + context->SetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration, + True(isolate)); + + { + // Run lib/internal/per_context.js + Context::Scope context_scope(context); + + std::vector> parameters = { + FIXED_ONE_BYTE_STRING(isolate, "global")}; + Local arguments[] = {context->Global()}; + MaybeLocal maybe_fn = + per_process::native_module_loader.LookupAndCompile( + context, "internal/per_context", ¶meters, nullptr); + if (maybe_fn.IsEmpty()) { + return Local(); + } + Local fn = maybe_fn.ToLocalChecked(); + MaybeLocal result = + fn->Call(context, Undefined(isolate), arraysize(arguments), arguments); + // Execution failed during context creation. + // TODO(joyeecheung): deprecate this signature and return a MaybeLocal. + if (result.IsEmpty()) { + return Local(); + } + } + + return context; +} + +uv_loop_t* GetCurrentEventLoop(Isolate* isolate) { + HandleScope handle_scope(isolate); + Local context = isolate->GetCurrentContext(); + if (context.IsEmpty()) return nullptr; + Environment* env = Environment::GetCurrent(context); + if (env == nullptr) return nullptr; + return env->event_loop(); +} + +} // namespace node diff --git a/src/exceptions.cc b/src/api/exceptions.cc similarity index 92% rename from src/exceptions.cc rename to src/api/exceptions.cc index d5c05fdf420926..4d1cca8b6512b4 100644 --- a/src/exceptions.cc +++ b/src/api/exceptions.cc @@ -12,6 +12,7 @@ namespace node { using v8::Exception; +using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; @@ -228,4 +229,17 @@ Local WinapiErrnoException(Isolate* isolate, } #endif +void FatalException(Isolate* isolate, const v8::TryCatch& try_catch) { + // If we try to print out a termination exception, we'd just get 'null', + // so just crashing here with that information seems like a better idea, + // and in particular it seems like we should handle terminations at the call + // site for this function rather than by printing them out somewhere. + CHECK(!try_catch.HasTerminated()); + + HandleScope scope(isolate); + if (!try_catch.IsVerbose()) { + FatalException(isolate, try_catch.Exception(), try_catch.Message()); + } +} + } // namespace node diff --git a/src/api/hooks.cc b/src/api/hooks.cc new file mode 100644 index 00000000000000..b54292638ddf95 --- /dev/null +++ b/src/api/hooks.cc @@ -0,0 +1,149 @@ +#include "env-inl.h" +#include "node.h" +#include "node_process.h" +#include "async_wrap.h" + +namespace node { + +using v8::Context; +using v8::HandleScope; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Object; +using v8::String; +using v8::Value; +using v8::NewStringType; + +void RunAtExit(Environment* env) { + env->RunAtExitCallbacks(); +} + +void AtExit(void (*cb)(void* arg), void* arg) { + auto env = Environment::GetThreadLocalEnv(); + AtExit(env, cb, arg); +} + +void AtExit(Environment* env, void (*cb)(void* arg), void* arg) { + CHECK_NOT_NULL(env); + env->AtExit(cb, arg); +} + +void EmitBeforeExit(Environment* env) { + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local exit_code = env->process_object() + ->Get(env->context(), env->exit_code_string()) + .ToLocalChecked() + ->ToInteger(env->context()) + .ToLocalChecked(); + ProcessEmit(env, "beforeExit", exit_code).ToLocalChecked(); +} + +int EmitExit(Environment* env) { + // process.emit('exit') + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local process_object = env->process_object(); + process_object + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "_exiting"), + True(env->isolate())) + .FromJust(); + + Local exit_code = env->exit_code_string(); + int code = process_object->Get(env->context(), exit_code) + .ToLocalChecked() + ->Int32Value(env->context()) + .ToChecked(); + ProcessEmit(env, "exit", Integer::New(env->isolate(), code)); + + // Reload exit code, it may be changed by `emit('exit')` + return process_object->Get(env->context(), exit_code) + .ToLocalChecked() + ->Int32Value(env->context()) + .ToChecked(); +} + +void AddPromiseHook(Isolate* isolate, promise_hook_func fn, void* arg) { + Environment* env = Environment::GetCurrent(isolate); + CHECK_NOT_NULL(env); + env->AddPromiseHook(fn, arg); +} + +void AddEnvironmentCleanupHook(Isolate* isolate, + void (*fun)(void* arg), + void* arg) { + Environment* env = Environment::GetCurrent(isolate); + CHECK_NOT_NULL(env); + env->AddCleanupHook(fun, arg); +} + +void RemoveEnvironmentCleanupHook(Isolate* isolate, + void (*fun)(void* arg), + void* arg) { + Environment* env = Environment::GetCurrent(isolate); + CHECK_NOT_NULL(env); + env->RemoveCleanupHook(fun, arg); +} + +async_id AsyncHooksGetExecutionAsyncId(Isolate* isolate) { + // Environment::GetCurrent() allocates a Local<> handle. + HandleScope handle_scope(isolate); + Environment* env = Environment::GetCurrent(isolate); + if (env == nullptr) return -1; + return env->execution_async_id(); +} + +async_id AsyncHooksGetTriggerAsyncId(Isolate* isolate) { + // Environment::GetCurrent() allocates a Local<> handle. + HandleScope handle_scope(isolate); + Environment* env = Environment::GetCurrent(isolate); + if (env == nullptr) return -1; + return env->trigger_async_id(); +} + + +async_context EmitAsyncInit(Isolate* isolate, + Local resource, + const char* name, + async_id trigger_async_id) { + HandleScope handle_scope(isolate); + Local type = + String::NewFromUtf8(isolate, name, NewStringType::kInternalized) + .ToLocalChecked(); + return EmitAsyncInit(isolate, resource, type, trigger_async_id); +} + +async_context EmitAsyncInit(Isolate* isolate, + Local resource, + Local name, + async_id trigger_async_id) { + HandleScope handle_scope(isolate); + Environment* env = Environment::GetCurrent(isolate); + CHECK_NOT_NULL(env); + + // Initialize async context struct + if (trigger_async_id == -1) + trigger_async_id = env->get_default_trigger_async_id(); + + async_context context = { + env->new_async_id(), // async_id_ + trigger_async_id // trigger_async_id_ + }; + + // Run init hooks + AsyncWrap::EmitAsyncInit(env, resource, name, context.async_id, + context.trigger_async_id); + + return context; +} + +void EmitAsyncDestroy(Isolate* isolate, async_context asyncContext) { + // Environment::GetCurrent() allocates a Local<> handle. + HandleScope handle_scope(isolate); + AsyncWrap::EmitDestroy( + Environment::GetCurrent(isolate), asyncContext.async_id); +} + +} // namespace node diff --git a/src/api/utils.cc b/src/api/utils.cc new file mode 100644 index 00000000000000..e6993f33b00647 --- /dev/null +++ b/src/api/utils.cc @@ -0,0 +1,170 @@ +#include "node.h" +#include "node_internals.h" + +#include + +namespace node { + +const char* signo_string(int signo) { +#define SIGNO_CASE(e) \ + case e: \ + return #e; + switch (signo) { +#ifdef SIGHUP + SIGNO_CASE(SIGHUP); +#endif + +#ifdef SIGINT + SIGNO_CASE(SIGINT); +#endif + +#ifdef SIGQUIT + SIGNO_CASE(SIGQUIT); +#endif + +#ifdef SIGILL + SIGNO_CASE(SIGILL); +#endif + +#ifdef SIGTRAP + SIGNO_CASE(SIGTRAP); +#endif + +#ifdef SIGABRT + SIGNO_CASE(SIGABRT); +#endif + +#ifdef SIGIOT +#if SIGABRT != SIGIOT + SIGNO_CASE(SIGIOT); +#endif +#endif + +#ifdef SIGBUS + SIGNO_CASE(SIGBUS); +#endif + +#ifdef SIGFPE + SIGNO_CASE(SIGFPE); +#endif + +#ifdef SIGKILL + SIGNO_CASE(SIGKILL); +#endif + +#ifdef SIGUSR1 + SIGNO_CASE(SIGUSR1); +#endif + +#ifdef SIGSEGV + SIGNO_CASE(SIGSEGV); +#endif + +#ifdef SIGUSR2 + SIGNO_CASE(SIGUSR2); +#endif + +#ifdef SIGPIPE + SIGNO_CASE(SIGPIPE); +#endif + +#ifdef SIGALRM + SIGNO_CASE(SIGALRM); +#endif + + SIGNO_CASE(SIGTERM); + +#ifdef SIGCHLD + SIGNO_CASE(SIGCHLD); +#endif + +#ifdef SIGSTKFLT + SIGNO_CASE(SIGSTKFLT); +#endif + +#ifdef SIGCONT + SIGNO_CASE(SIGCONT); +#endif + +#ifdef SIGSTOP + SIGNO_CASE(SIGSTOP); +#endif + +#ifdef SIGTSTP + SIGNO_CASE(SIGTSTP); +#endif + +#ifdef SIGBREAK + SIGNO_CASE(SIGBREAK); +#endif + +#ifdef SIGTTIN + SIGNO_CASE(SIGTTIN); +#endif + +#ifdef SIGTTOU + SIGNO_CASE(SIGTTOU); +#endif + +#ifdef SIGURG + SIGNO_CASE(SIGURG); +#endif + +#ifdef SIGXCPU + SIGNO_CASE(SIGXCPU); +#endif + +#ifdef SIGXFSZ + SIGNO_CASE(SIGXFSZ); +#endif + +#ifdef SIGVTALRM + SIGNO_CASE(SIGVTALRM); +#endif + +#ifdef SIGPROF + SIGNO_CASE(SIGPROF); +#endif + +#ifdef SIGWINCH + SIGNO_CASE(SIGWINCH); +#endif + +#ifdef SIGIO + SIGNO_CASE(SIGIO); +#endif + +#ifdef SIGPOLL +#if SIGPOLL != SIGIO + SIGNO_CASE(SIGPOLL); +#endif +#endif + +#ifdef SIGLOST +#if SIGLOST != SIGABRT + SIGNO_CASE(SIGLOST); +#endif +#endif + +#ifdef SIGPWR +#if SIGPWR != SIGLOST + SIGNO_CASE(SIGPWR); +#endif +#endif + +#ifdef SIGINFO +#if !defined(SIGPWR) || SIGINFO != SIGPWR + SIGNO_CASE(SIGINFO); +#endif +#endif + +#ifdef SIGSYS + SIGNO_CASE(SIGSYS); +#endif + + default: + return ""; + } +} + +} // namespace node diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 193fec7ca7087a..8f20d4f83705e3 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -39,7 +39,6 @@ using v8::Integer; using v8::Isolate; using v8::Local; using v8::MaybeLocal; -using v8::NewStringType; using v8::Number; using v8::Object; using v8::ObjectTemplate; @@ -688,70 +687,6 @@ MaybeLocal AsyncWrap::MakeCallback(const Local cb, return ret; } - -/* Public C++ embedder API */ - - -async_id AsyncHooksGetExecutionAsyncId(Isolate* isolate) { - // Environment::GetCurrent() allocates a Local<> handle. - HandleScope handle_scope(isolate); - Environment* env = Environment::GetCurrent(isolate); - if (env == nullptr) return -1; - return env->execution_async_id(); -} - - -async_id AsyncHooksGetTriggerAsyncId(Isolate* isolate) { - // Environment::GetCurrent() allocates a Local<> handle. - HandleScope handle_scope(isolate); - Environment* env = Environment::GetCurrent(isolate); - if (env == nullptr) return -1; - return env->trigger_async_id(); -} - - -async_context EmitAsyncInit(Isolate* isolate, - Local resource, - const char* name, - async_id trigger_async_id) { - HandleScope handle_scope(isolate); - Local type = - String::NewFromUtf8(isolate, name, NewStringType::kInternalized) - .ToLocalChecked(); - return EmitAsyncInit(isolate, resource, type, trigger_async_id); -} - -async_context EmitAsyncInit(Isolate* isolate, - Local resource, - Local name, - async_id trigger_async_id) { - HandleScope handle_scope(isolate); - Environment* env = Environment::GetCurrent(isolate); - CHECK_NOT_NULL(env); - - // Initialize async context struct - if (trigger_async_id == -1) - trigger_async_id = env->get_default_trigger_async_id(); - - async_context context = { - env->new_async_id(), // async_id_ - trigger_async_id // trigger_async_id_ - }; - - // Run init hooks - AsyncWrap::EmitAsyncInit(env, resource, name, context.async_id, - context.trigger_async_id); - - return context; -} - -void EmitAsyncDestroy(Isolate* isolate, async_context asyncContext) { - // Environment::GetCurrent() allocates a Local<> handle. - HandleScope handle_scope(isolate); - AsyncWrap::EmitDestroy( - Environment::GetCurrent(isolate), asyncContext.async_id); -} - std::string AsyncWrap::MemoryInfoName() const { return provider_names[provider_type()]; } diff --git a/src/env.cc b/src/env.cc index 8ecab92df73f1f..137dc83b76a7ec 100644 --- a/src/env.cc +++ b/src/env.cc @@ -8,6 +8,7 @@ #include "node_options-inl.h" #include "node_platform.h" #include "node_process.h" +#include "node_v8_platform-inl.h" #include "node_worker.h" #include "tracing/agent.h" #include "tracing/traced_value.h" diff --git a/src/inspector/tracing_agent.cc b/src/inspector/tracing_agent.cc index 79fccbf8aa47ac..fb8467a1b9d3e0 100644 --- a/src/inspector/tracing_agent.cc +++ b/src/inspector/tracing_agent.cc @@ -1,6 +1,7 @@ #include "tracing_agent.h" #include "main_thread_interface.h" #include "node_internals.h" +#include "node_v8_platform-inl.h" #include "env-inl.h" #include "v8.h" diff --git a/src/node.cc b/src/node.cc index e67b16af83f0df..745ed83917885e 100644 --- a/src/node.cc +++ b/src/node.cc @@ -32,8 +32,8 @@ #include "node_platform.h" #include "node_process.h" #include "node_revert.h" +#include "node_v8_platform-inl.h" #include "node_version.h" -#include "tracing/traced_value.h" #if HAVE_OPENSSL #include "node_crypto.h" @@ -56,8 +56,6 @@ #include "handle_wrap.h" #include "req_wrap-inl.h" #include "string_bytes.h" -#include "tracing/agent.h" -#include "tracing/node_trace_writer.h" #include "util.h" #include "uv.h" #if NODE_USE_V8_PLATFORM @@ -121,32 +119,21 @@ using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Int32; -using v8::Integer; using v8::Isolate; using v8::Just; using v8::Local; using v8::Locker; using v8::Maybe; using v8::MaybeLocal; -using v8::Message; -using v8::MicrotasksPolicy; using v8::Object; -using v8::ObjectTemplate; using v8::Script; -using v8::ScriptOrigin; using v8::SealHandleScope; using v8::String; -using v8::TracingController; using v8::Undefined; using v8::V8; using v8::Value; namespace per_process { -// Tells whether --prof is passed. -// TODO(joyeecheung): move env->options()->prof_process to -// per_process::cli_options.prof_process and use that instead. -static bool v8_is_profiling = false; - // TODO(joyeecheung): these are no longer necessary. Remove them. // See: https://github.com/nodejs/node/pull/25302#discussion_r244924196 // Isolate on the main thread @@ -165,377 +152,17 @@ bool v8_initialized = false; // node_internals.h // process-relative uptime base, initialized at start-up double prog_start_time; -} // namespace per_process - -// Ensures that __metadata trace events are only emitted -// when tracing is enabled. -class NodeTraceStateObserver : - public TracingController::TraceStateObserver { - public: - void OnTraceEnabled() override { - char name_buffer[512]; - if (uv_get_process_title(name_buffer, sizeof(name_buffer)) == 0) { - // Only emit the metadata event if the title can be retrieved - // successfully. Ignore it otherwise. - TRACE_EVENT_METADATA1("__metadata", "process_name", - "name", TRACE_STR_COPY(name_buffer)); - } - TRACE_EVENT_METADATA1("__metadata", - "version", - "node", - per_process::metadata.versions.node.c_str()); - TRACE_EVENT_METADATA1("__metadata", "thread_name", - "name", "JavaScriptMainThread"); - - auto trace_process = tracing::TracedValue::Create(); - trace_process->BeginDictionary("versions"); - -#define V(key) \ - trace_process->SetString(#key, per_process::metadata.versions.key.c_str()); - - NODE_VERSIONS_KEYS(V) -#undef V - - trace_process->EndDictionary(); - - trace_process->SetString("arch", per_process::metadata.arch.c_str()); - trace_process->SetString("platform", - per_process::metadata.platform.c_str()); - - trace_process->BeginDictionary("release"); - trace_process->SetString("name", - per_process::metadata.release.name.c_str()); -#if NODE_VERSION_IS_LTS - trace_process->SetString("lts", per_process::metadata.release.lts.c_str()); -#endif - trace_process->EndDictionary(); - TRACE_EVENT_METADATA1("__metadata", "node", - "process", std::move(trace_process)); - - // This only runs the first time tracing is enabled - controller_->RemoveTraceStateObserver(this); - } - - void OnTraceDisabled() override { - // Do nothing here. This should never be called because the - // observer removes itself when OnTraceEnabled() is called. - UNREACHABLE(); - } - - explicit NodeTraceStateObserver(TracingController* controller) : - controller_(controller) {} - ~NodeTraceStateObserver() override {} - - private: - TracingController* controller_; -}; - -static struct { -#if NODE_USE_V8_PLATFORM - void Initialize(int thread_pool_size) { - tracing_agent_.reset(new tracing::Agent()); - node::tracing::TraceEventHelper::SetAgent(tracing_agent_.get()); - node::tracing::TracingController* controller = - tracing_agent_->GetTracingController(); - trace_state_observer_.reset(new NodeTraceStateObserver(controller)); - controller->AddTraceStateObserver(trace_state_observer_.get()); - StartTracingAgent(); - // Tracing must be initialized before platform threads are created. - platform_ = new NodePlatform(thread_pool_size, controller); - V8::InitializePlatform(platform_); - } - - void Dispose() { - StopTracingAgent(); - platform_->Shutdown(); - delete platform_; - platform_ = nullptr; - // Destroy tracing after the platform (and platform threads) have been - // stopped. - tracing_agent_.reset(nullptr); - trace_state_observer_.reset(nullptr); - } - - void DrainVMTasks(Isolate* isolate) { - platform_->DrainTasks(isolate); - } - - void CancelVMTasks(Isolate* isolate) { - platform_->CancelPendingDelayedTasks(isolate); - } - - void StartTracingAgent() { - if (per_process::cli_options->trace_event_categories.empty()) { - tracing_file_writer_ = tracing_agent_->DefaultHandle(); - } else { - std::vector categories = - SplitString(per_process::cli_options->trace_event_categories, ','); - - tracing_file_writer_ = tracing_agent_->AddClient( - std::set(std::make_move_iterator(categories.begin()), - std::make_move_iterator(categories.end())), - std::unique_ptr( - new tracing::NodeTraceWriter( - per_process::cli_options->trace_event_file_pattern)), - tracing::Agent::kUseDefaultCategories); - } - } - - void StopTracingAgent() { - tracing_file_writer_.reset(); - } - - tracing::AgentWriterHandle* GetTracingAgentWriter() { - return &tracing_file_writer_; - } - - NodePlatform* Platform() { - return platform_; - } - - std::unique_ptr trace_state_observer_; - std::unique_ptr tracing_agent_; - tracing::AgentWriterHandle tracing_file_writer_; - NodePlatform* platform_; -#else // !NODE_USE_V8_PLATFORM - void Initialize(int thread_pool_size) {} - void Dispose() {} - void DrainVMTasks(Isolate* isolate) {} - void CancelVMTasks(Isolate* isolate) {} - - void StartTracingAgent() { - if (!trace_enabled_categories.empty()) { - fprintf(stderr, "Node compiled with NODE_USE_V8_PLATFORM=0, " - "so event tracing is not available.\n"); - } - } - void StopTracingAgent() {} - - tracing::AgentWriterHandle* GetTracingAgentWriter() { - return nullptr; - } - - NodePlatform* Platform() { - return nullptr; - } -#endif // !NODE_USE_V8_PLATFORM -} v8_platform; - -tracing::AgentWriterHandle* GetTracingAgentWriter() { - return v8_platform.GetTracingAgentWriter(); -} +// Tells whether --prof is passed. +bool v8_is_profiling = false; -void DisposePlatform() { - v8_platform.Dispose(); -} +// node_v8_platform-inl.h +struct V8Platform v8_platform; +} // namespace per_process #ifdef __POSIX__ static const unsigned kMaxSignal = 32; #endif -const char* signo_string(int signo) { -#define SIGNO_CASE(e) case e: return #e; - switch (signo) { -#ifdef SIGHUP - SIGNO_CASE(SIGHUP); -#endif - -#ifdef SIGINT - SIGNO_CASE(SIGINT); -#endif - -#ifdef SIGQUIT - SIGNO_CASE(SIGQUIT); -#endif - -#ifdef SIGILL - SIGNO_CASE(SIGILL); -#endif - -#ifdef SIGTRAP - SIGNO_CASE(SIGTRAP); -#endif - -#ifdef SIGABRT - SIGNO_CASE(SIGABRT); -#endif - -#ifdef SIGIOT -# if SIGABRT != SIGIOT - SIGNO_CASE(SIGIOT); -# endif -#endif - -#ifdef SIGBUS - SIGNO_CASE(SIGBUS); -#endif - -#ifdef SIGFPE - SIGNO_CASE(SIGFPE); -#endif - -#ifdef SIGKILL - SIGNO_CASE(SIGKILL); -#endif - -#ifdef SIGUSR1 - SIGNO_CASE(SIGUSR1); -#endif - -#ifdef SIGSEGV - SIGNO_CASE(SIGSEGV); -#endif - -#ifdef SIGUSR2 - SIGNO_CASE(SIGUSR2); -#endif - -#ifdef SIGPIPE - SIGNO_CASE(SIGPIPE); -#endif - -#ifdef SIGALRM - SIGNO_CASE(SIGALRM); -#endif - - SIGNO_CASE(SIGTERM); - -#ifdef SIGCHLD - SIGNO_CASE(SIGCHLD); -#endif - -#ifdef SIGSTKFLT - SIGNO_CASE(SIGSTKFLT); -#endif - - -#ifdef SIGCONT - SIGNO_CASE(SIGCONT); -#endif - -#ifdef SIGSTOP - SIGNO_CASE(SIGSTOP); -#endif - -#ifdef SIGTSTP - SIGNO_CASE(SIGTSTP); -#endif - -#ifdef SIGBREAK - SIGNO_CASE(SIGBREAK); -#endif - -#ifdef SIGTTIN - SIGNO_CASE(SIGTTIN); -#endif - -#ifdef SIGTTOU - SIGNO_CASE(SIGTTOU); -#endif - -#ifdef SIGURG - SIGNO_CASE(SIGURG); -#endif - -#ifdef SIGXCPU - SIGNO_CASE(SIGXCPU); -#endif - -#ifdef SIGXFSZ - SIGNO_CASE(SIGXFSZ); -#endif - -#ifdef SIGVTALRM - SIGNO_CASE(SIGVTALRM); -#endif - -#ifdef SIGPROF - SIGNO_CASE(SIGPROF); -#endif - -#ifdef SIGWINCH - SIGNO_CASE(SIGWINCH); -#endif - -#ifdef SIGIO - SIGNO_CASE(SIGIO); -#endif - -#ifdef SIGPOLL -# if SIGPOLL != SIGIO - SIGNO_CASE(SIGPOLL); -# endif -#endif - -#ifdef SIGLOST -# if SIGLOST != SIGABRT - SIGNO_CASE(SIGLOST); -# endif -#endif - -#ifdef SIGPWR -# if SIGPWR != SIGLOST - SIGNO_CASE(SIGPWR); -# endif -#endif - -#ifdef SIGINFO -# if !defined(SIGPWR) || SIGINFO != SIGPWR - SIGNO_CASE(SIGINFO); -# endif -#endif - -#ifdef SIGSYS - SIGNO_CASE(SIGSYS); -#endif - - default: return ""; - } -} - -void* ArrayBufferAllocator::Allocate(size_t size) { - if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers) - return UncheckedCalloc(size); - else - return UncheckedMalloc(size); -} - -namespace { - -bool ShouldAbortOnUncaughtException(Isolate* isolate) { - HandleScope scope(isolate); - Environment* env = Environment::GetCurrent(isolate); - return env != nullptr && - env->should_abort_on_uncaught_toggle()[0] && - !env->inside_should_not_abort_on_uncaught_scope(); -} - -} // anonymous namespace - - -void AddPromiseHook(Isolate* isolate, promise_hook_func fn, void* arg) { - Environment* env = Environment::GetCurrent(isolate); - CHECK_NOT_NULL(env); - env->AddPromiseHook(fn, arg); -} - -void AddEnvironmentCleanupHook(Isolate* isolate, - void (*fun)(void* arg), - void* arg) { - Environment* env = Environment::GetCurrent(isolate); - CHECK_NOT_NULL(env); - env->AddCleanupHook(fun, arg); -} - - -void RemoveEnvironmentCleanupHook(Isolate* isolate, - void (*fun)(void* arg), - void* arg) { - Environment* env = Environment::GetCurrent(isolate); - CHECK_NOT_NULL(env); - env->RemoveCleanupHook(fun, arg); -} - static void WaitForInspectorDisconnect(Environment* env) { #if HAVE_INSPECTOR if (env->inspector_agent()->IsActive()) { @@ -563,33 +190,6 @@ void Exit(const FunctionCallbackInfo& args) { env->Exit(code); } -static void OnMessage(Local message, Local error) { - Isolate* isolate = message->GetIsolate(); - switch (message->ErrorLevel()) { - case Isolate::MessageErrorLevel::kMessageWarning: { - Environment* env = Environment::GetCurrent(isolate); - if (!env) { - break; - } - Utf8Value filename(isolate, - message->GetScriptOrigin().ResourceName()); - // (filename):(line) (message) - std::stringstream warning; - warning << *filename; - warning << ":"; - warning << message->GetLineNumber(env->context()).FromMaybe(-1); - warning << " "; - v8::String::Utf8Value msg(isolate, message->Get()); - warning << *msg; - USE(ProcessEmitWarningGeneric(env, warning.str().c_str(), "V8")); - break; - } - case Isolate::MessageErrorLevel::kMessageError: - FatalException(isolate, error, message); - break; - } -} - void SignalExit(int signo) { uv_tty_reset_mode(); #ifdef __FreeBSD__ @@ -1130,35 +730,6 @@ void Init(int* argc, argv[i] = strdup(argv_[i].c_str()); } -void RunAtExit(Environment* env) { - env->RunAtExitCallbacks(); -} - - -uv_loop_t* GetCurrentEventLoop(Isolate* isolate) { - HandleScope handle_scope(isolate); - Local context = isolate->GetCurrentContext(); - if (context.IsEmpty()) - return nullptr; - Environment* env = Environment::GetCurrent(context); - if (env == nullptr) - return nullptr; - return env->event_loop(); -} - - -void AtExit(void (*cb)(void* arg), void* arg) { - auto env = Environment::GetThreadLocalEnv(); - AtExit(env, cb, arg); -} - - -void AtExit(Environment* env, void (*cb)(void* arg), void* arg) { - CHECK_NOT_NULL(env); - env->AtExit(cb, arg); -} - - void RunBeforeExit(Environment* env) { env->RunBeforeExitCallbacks(); @@ -1166,155 +737,6 @@ void RunBeforeExit(Environment* env) { EmitBeforeExit(env); } - -void EmitBeforeExit(Environment* env) { - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - Local exit_code = env->process_object() - ->Get(env->context(), env->exit_code_string()) - .ToLocalChecked() - ->ToInteger(env->context()) - .ToLocalChecked(); - ProcessEmit(env, "beforeExit", exit_code).ToLocalChecked(); -} - -int EmitExit(Environment* env) { - // process.emit('exit') - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - Local process_object = env->process_object(); - process_object->Set(env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "_exiting"), - True(env->isolate())).FromJust(); - - Local exit_code = env->exit_code_string(); - int code = process_object->Get(env->context(), exit_code).ToLocalChecked() - ->Int32Value(env->context()).ToChecked(); - ProcessEmit(env, "exit", Integer::New(env->isolate(), code)); - - // Reload exit code, it may be changed by `emit('exit')` - return process_object->Get(env->context(), exit_code).ToLocalChecked() - ->Int32Value(env->context()).ToChecked(); -} - - -ArrayBufferAllocator* CreateArrayBufferAllocator() { - return new ArrayBufferAllocator(); -} - - -void FreeArrayBufferAllocator(ArrayBufferAllocator* allocator) { - delete allocator; -} - - -IsolateData* CreateIsolateData( - Isolate* isolate, - uv_loop_t* loop, - MultiIsolatePlatform* platform, - ArrayBufferAllocator* allocator) { - return new IsolateData( - isolate, - loop, - platform, - allocator != nullptr ? allocator->zero_fill_field() : nullptr); -} - - -void FreeIsolateData(IsolateData* isolate_data) { - delete isolate_data; -} - - -Environment* CreateEnvironment(IsolateData* isolate_data, - Local context, - int argc, - const char* const* argv, - int exec_argc, - const char* const* exec_argv) { - Isolate* isolate = context->GetIsolate(); - HandleScope handle_scope(isolate); - Context::Scope context_scope(context); - // TODO(addaleax): This is a much better place for parsing per-Environment - // options than the global parse call. - std::vector args(argv, argv + argc); - std::vector exec_args(exec_argv, exec_argv + exec_argc); - Environment* env = new Environment(isolate_data, context); - env->Start(per_process::v8_is_profiling); - env->ProcessCliArgs(args, exec_args); - return env; -} - - -void FreeEnvironment(Environment* env) { - env->RunCleanup(); - delete env; -} - - -Environment* GetCurrentEnvironment(Local context) { - return Environment::GetCurrent(context); -} - - -MultiIsolatePlatform* GetMainThreadMultiIsolatePlatform() { - return v8_platform.Platform(); -} - - -MultiIsolatePlatform* CreatePlatform( - int thread_pool_size, - node::tracing::TracingController* tracing_controller) { - return new NodePlatform(thread_pool_size, tracing_controller); -} - - -MultiIsolatePlatform* InitializeV8Platform(int thread_pool_size) { - v8_platform.Initialize(thread_pool_size); - return v8_platform.Platform(); -} - - -void FreePlatform(MultiIsolatePlatform* platform) { - delete platform; -} - -Local NewContext(Isolate* isolate, - Local object_template) { - Local context = Context::New(isolate, nullptr, object_template); - if (context.IsEmpty()) return context; - HandleScope handle_scope(isolate); - - context->SetEmbedderData( - ContextEmbedderIndex::kAllowWasmCodeGeneration, True(isolate)); - - { - // Run lib/internal/per_context.js - Context::Scope context_scope(context); - - std::vector> parameters = { - FIXED_ONE_BYTE_STRING(isolate, "global")}; - Local arguments[] = {context->Global()}; - MaybeLocal maybe_fn = - per_process::native_module_loader.LookupAndCompile( - context, "internal/per_context", ¶meters, nullptr); - if (maybe_fn.IsEmpty()) { - return Local(); - } - Local fn = maybe_fn.ToLocalChecked(); - MaybeLocal result = - fn->Call(context, Undefined(isolate), arraysize(arguments), arguments); - // Execution failed during context creation. - // TODO(joyeecheung): deprecate this signature and return a MaybeLocal. - if (result.IsEmpty()) { - return Local(); - } - } - - return context; -} - - inline int Start(Isolate* isolate, IsolateData* isolate_data, const std::vector& args, const std::vector& exec_args) { @@ -1359,7 +781,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, do { uv_run(env.event_loop(), UV_RUN_DEFAULT); - v8_platform.DrainVMTasks(isolate); + per_process::v8_platform.DrainVMTasks(isolate); more = uv_loop_alive(env.event_loop()); if (more) @@ -1387,8 +809,8 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, env.RunCleanup(); RunAtExit(&env); - v8_platform.DrainVMTasks(isolate); - v8_platform.CancelVMTasks(isolate); + per_process::v8_platform.DrainVMTasks(isolate); + per_process::v8_platform.CancelVMTasks(isolate); #if defined(LEAK_SANITIZER) __lsan_do_leak_check(); #endif @@ -1396,41 +818,6 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, return exit_code; } -bool AllowWasmCodeGenerationCallback( - Local context, Local) { - Local wasm_code_gen = - context->GetEmbedderData(ContextEmbedderIndex::kAllowWasmCodeGeneration); - return wasm_code_gen->IsUndefined() || wasm_code_gen->IsTrue(); -} - -Isolate* NewIsolate(ArrayBufferAllocator* allocator, uv_loop_t* event_loop) { - Isolate::CreateParams params; - params.array_buffer_allocator = allocator; -#ifdef NODE_ENABLE_VTUNE_PROFILING - params.code_event_handler = vTune::GetVtuneCodeEventHandler(); -#endif - - Isolate* isolate = Isolate::Allocate(); - if (isolate == nullptr) - return nullptr; - - // Register the isolate on the platform before the isolate gets initialized, - // so that the isolate can access the platform during initialization. - v8_platform.Platform()->RegisterIsolate(isolate, event_loop); - Isolate::Initialize(isolate, params); - - isolate->AddMessageListenerWithErrorLevel(OnMessage, - Isolate::MessageErrorLevel::kMessageError | - Isolate::MessageErrorLevel::kMessageWarning); - isolate->SetAbortOnUncaughtExceptionCallback(ShouldAbortOnUncaughtException); - isolate->SetMicrotasksPolicy(MicrotasksPolicy::kExplicit); - isolate->SetFatalErrorHandler(OnFatalError); - isolate->SetAllowWasmCodeGenerationCallback(AllowWasmCodeGenerationCallback); - v8::CpuProfiler::UseDetailedSourcePositionsForProfiling(isolate); - - return isolate; -} - inline int Start(uv_loop_t* event_loop, const std::vector& args, const std::vector& exec_args) { @@ -1462,11 +849,10 @@ inline int Start(uv_loop_t* event_loop, Isolate::Scope isolate_scope(isolate); HandleScope handle_scope(isolate); std::unique_ptr isolate_data( - CreateIsolateData( - isolate, - event_loop, - v8_platform.Platform(), - allocator.get()), + CreateIsolateData(isolate, + event_loop, + per_process::v8_platform.Platform(), + allocator.get()), &FreeIsolateData); // TODO(addaleax): This should load a real per-Isolate option, currently // this is still effectively per-process. @@ -1484,7 +870,7 @@ inline int Start(uv_loop_t* event_loop, } isolate->Dispose(); - v8_platform.Platform()->UnregisterIsolate(isolate); + per_process::v8_platform.Platform()->UnregisterIsolate(isolate); return exit_code; } @@ -1549,7 +935,7 @@ int Start(int argc, char** argv) { // that happen to terminate during shutdown from being run unsafely. // Since uv_run cannot be called, uv_async handles held by the platform // will never be fully cleaned up. - v8_platform.Dispose(); + per_process::v8_platform.Dispose(); return exit_code; } diff --git a/src/node_errors.cc b/src/node_errors.cc index 23a65bd2251d62..5a9f08839ceb52 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -763,19 +763,6 @@ void FatalException(Isolate* isolate, } } -void FatalException(Isolate* isolate, const v8::TryCatch& try_catch) { - // If we try to print out a termination exception, we'd just get 'null', - // so just crashing here with that information seems like a better idea, - // and in particular it seems like we should handle terminations at the call - // site for this function rather than by printing them out somewhere. - CHECK(!try_catch.HasTerminated()); - - HandleScope scope(isolate); - if (!try_catch.IsVerbose()) { - FatalException(isolate, try_catch.Exception(), try_catch.Message()); - } -} - void FatalException(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); Environment* env = Environment::GetCurrent(isolate); diff --git a/src/node_internals.h b/src/node_internals.h index bf66c77e6f043c..b34e6f90e7d28c 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -56,6 +56,7 @@ class NativeModuleLoader; namespace per_process { extern Mutex env_var_mutex; extern double prog_start_time; +extern bool v8_is_profiling; } // namespace per_process // Forward declaration @@ -244,9 +245,6 @@ int ThreadPoolWork::CancelWork() { return uv_cancel(reinterpret_cast(&work_req_)); } -tracing::AgentWriterHandle* GetTracingAgentWriter(); -void DisposePlatform(); - #define TRACING_CATEGORY_NODE "node" #define TRACING_CATEGORY_NODE1(one) \ TRACING_CATEGORY_NODE "," \ diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc index 3c0f1cf68f1db8..9538e75d2c54ed 100644 --- a/src/node_trace_events.cc +++ b/src/node_trace_events.cc @@ -1,8 +1,8 @@ +#include "base_object-inl.h" +#include "env.h" #include "node.h" -#include "node_internals.h" +#include "node_v8_platform-inl.h" #include "tracing/agent.h" -#include "env.h" -#include "base_object-inl.h" #include #include diff --git a/src/node_v8_platform-inl.h b/src/node_v8_platform-inl.h new file mode 100644 index 00000000000000..ed91ee3a022551 --- /dev/null +++ b/src/node_v8_platform-inl.h @@ -0,0 +1,178 @@ +#ifndef SRC_NODE_V8_PLATFORM_INL_H_ +#define SRC_NODE_V8_PLATFORM_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "env-inl.h" +#include "node.h" +#include "node_metadata.h" +#include "node_options.h" +#include "tracing/node_trace_writer.h" +#include "tracing/trace_event.h" +#include "tracing/traced_value.h" + +namespace node { + +// Ensures that __metadata trace events are only emitted +// when tracing is enabled. +class NodeTraceStateObserver + : public v8::TracingController::TraceStateObserver { + public: + inline void OnTraceEnabled() override { + char name_buffer[512]; + if (uv_get_process_title(name_buffer, sizeof(name_buffer)) == 0) { + // Only emit the metadata event if the title can be retrieved + // successfully. Ignore it otherwise. + TRACE_EVENT_METADATA1( + "__metadata", "process_name", "name", TRACE_STR_COPY(name_buffer)); + } + TRACE_EVENT_METADATA1("__metadata", + "version", + "node", + per_process::metadata.versions.node.c_str()); + TRACE_EVENT_METADATA1( + "__metadata", "thread_name", "name", "JavaScriptMainThread"); + + auto trace_process = tracing::TracedValue::Create(); + trace_process->BeginDictionary("versions"); + +#define V(key) \ + trace_process->SetString(#key, per_process::metadata.versions.key.c_str()); + + NODE_VERSIONS_KEYS(V) +#undef V + + trace_process->EndDictionary(); + + trace_process->SetString("arch", per_process::metadata.arch.c_str()); + trace_process->SetString("platform", + per_process::metadata.platform.c_str()); + + trace_process->BeginDictionary("release"); + trace_process->SetString("name", + per_process::metadata.release.name.c_str()); +#if NODE_VERSION_IS_LTS + trace_process->SetString("lts", per_process::metadata.release.lts.c_str()); +#endif + trace_process->EndDictionary(); + TRACE_EVENT_METADATA1( + "__metadata", "node", "process", std::move(trace_process)); + + // This only runs the first time tracing is enabled + controller_->RemoveTraceStateObserver(this); + } + + inline void OnTraceDisabled() override { + // Do nothing here. This should never be called because the + // observer removes itself when OnTraceEnabled() is called. + UNREACHABLE(); + } + + explicit NodeTraceStateObserver(v8::TracingController* controller) + : controller_(controller) {} + ~NodeTraceStateObserver() override {} + + private: + v8::TracingController* controller_; +}; + +struct V8Platform { +#if NODE_USE_V8_PLATFORM + inline void Initialize(int thread_pool_size) { + tracing_agent_.reset(new tracing::Agent()); + node::tracing::TraceEventHelper::SetAgent(tracing_agent_.get()); + node::tracing::TracingController* controller = + tracing_agent_->GetTracingController(); + trace_state_observer_.reset(new NodeTraceStateObserver(controller)); + controller->AddTraceStateObserver(trace_state_observer_.get()); + StartTracingAgent(); + // Tracing must be initialized before platform threads are created. + platform_ = new NodePlatform(thread_pool_size, controller); + v8::V8::InitializePlatform(platform_); + } + + inline void Dispose() { + StopTracingAgent(); + platform_->Shutdown(); + delete platform_; + platform_ = nullptr; + // Destroy tracing after the platform (and platform threads) have been + // stopped. + tracing_agent_.reset(nullptr); + trace_state_observer_.reset(nullptr); + } + + inline void DrainVMTasks(v8::Isolate* isolate) { + platform_->DrainTasks(isolate); + } + + inline void CancelVMTasks(v8::Isolate* isolate) { + platform_->CancelPendingDelayedTasks(isolate); + } + + inline void StartTracingAgent() { + if (per_process::cli_options->trace_event_categories.empty()) { + tracing_file_writer_ = tracing_agent_->DefaultHandle(); + } else { + std::vector categories = + SplitString(per_process::cli_options->trace_event_categories, ','); + + tracing_file_writer_ = tracing_agent_->AddClient( + std::set(std::make_move_iterator(categories.begin()), + std::make_move_iterator(categories.end())), + std::unique_ptr( + new tracing::NodeTraceWriter( + per_process::cli_options->trace_event_file_pattern)), + tracing::Agent::kUseDefaultCategories); + } + } + + inline void StopTracingAgent() { tracing_file_writer_.reset(); } + + inline tracing::AgentWriterHandle* GetTracingAgentWriter() { + return &tracing_file_writer_; + } + + inline NodePlatform* Platform() { return platform_; } + + std::unique_ptr trace_state_observer_; + std::unique_ptr tracing_agent_; + tracing::AgentWriterHandle tracing_file_writer_; + NodePlatform* platform_; +#else // !NODE_USE_V8_PLATFORM + inline void Initialize(int thread_pool_size) {} + inline void Dispose() {} + inline void DrainVMTasks(v8::Isolate* isolate) {} + inline void CancelVMTasks(v8::Isolate* isolate) {} + inline void StartTracingAgent() { + if (!trace_enabled_categories.empty()) { + fprintf(stderr, + "Node compiled with NODE_USE_V8_PLATFORM=0, " + "so event tracing is not available.\n"); + } + } + inline void StopTracingAgent() {} + + inline tracing::AgentWriterHandle* GetTracingAgentWriter() { return nullptr; } + + inline NodePlatform* Platform() { return nullptr; } +#endif // !NODE_USE_V8_PLATFORM +}; + +namespace per_process { +extern struct V8Platform v8_platform; +} + +inline tracing::AgentWriterHandle* GetTracingAgentWriter() { + return per_process::v8_platform.GetTracingAgentWriter(); +} + +inline void DisposePlatform() { + per_process::v8_platform.Dispose(); +} + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_V8_PLATFORM_INL_H_