mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
6.8 KiB
221 lines
6.8 KiB
#include "env.h"
|
|
#include "env-inl.h"
|
|
#include "async-wrap.h"
|
|
#include "v8.h"
|
|
#include "v8-profiler.h"
|
|
|
|
#if defined(_MSC_VER)
|
|
#define getpid GetCurrentProcessId
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <algorithm>
|
|
|
|
namespace node {
|
|
|
|
using v8::Context;
|
|
using v8::FunctionTemplate;
|
|
using v8::HandleScope;
|
|
using v8::Local;
|
|
using v8::Message;
|
|
using v8::StackFrame;
|
|
using v8::StackTrace;
|
|
|
|
void Environment::Start(int argc,
|
|
const char* const* argv,
|
|
int exec_argc,
|
|
const char* const* exec_argv,
|
|
bool start_profiler_idle_notifier) {
|
|
HandleScope handle_scope(isolate());
|
|
Context::Scope context_scope(context());
|
|
|
|
uv_check_init(event_loop(), immediate_check_handle());
|
|
uv_unref(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));
|
|
|
|
uv_idle_init(event_loop(), immediate_idle_handle());
|
|
|
|
// Inform V8's CPU profiler when we're idle. The profiler is sampling-based
|
|
// but not all samples are created equal; mark the wall clock time spent in
|
|
// epoll_wait() and friends so profiling tools can filter it out. The samples
|
|
// still end up in v8.log but with state=IDLE rather than state=EXTERNAL.
|
|
// TODO(bnoordhuis) Depends on a libuv implementation detail that we should
|
|
// probably fortify in the API contract, namely that the last started prepare
|
|
// or check watcher runs first. It's not 100% foolproof; if an add-on starts
|
|
// a prepare or check watcher after us, any samples attributed to its callback
|
|
// will be recorded with state=IDLE.
|
|
uv_prepare_init(event_loop(), &idle_prepare_handle_);
|
|
uv_check_init(event_loop(), &idle_check_handle_);
|
|
uv_unref(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
|
|
uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
|
|
|
|
uv_timer_init(event_loop(), destroy_ids_timer_handle());
|
|
|
|
auto close_and_finish = [](Environment* env, uv_handle_t* handle, void* arg) {
|
|
handle->data = env;
|
|
|
|
uv_close(handle, [](uv_handle_t* handle) {
|
|
static_cast<Environment*>(handle->data)->FinishHandleCleanup(handle);
|
|
});
|
|
};
|
|
|
|
RegisterHandleCleanup(
|
|
reinterpret_cast<uv_handle_t*>(immediate_check_handle()),
|
|
close_and_finish,
|
|
nullptr);
|
|
RegisterHandleCleanup(
|
|
reinterpret_cast<uv_handle_t*>(immediate_idle_handle()),
|
|
close_and_finish,
|
|
nullptr);
|
|
RegisterHandleCleanup(
|
|
reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_),
|
|
close_and_finish,
|
|
nullptr);
|
|
RegisterHandleCleanup(
|
|
reinterpret_cast<uv_handle_t*>(&idle_check_handle_),
|
|
close_and_finish,
|
|
nullptr);
|
|
|
|
if (start_profiler_idle_notifier) {
|
|
StartProfilerIdleNotifier();
|
|
}
|
|
|
|
auto process_template = FunctionTemplate::New(isolate());
|
|
process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate(), "process"));
|
|
|
|
auto process_object =
|
|
process_template->GetFunction()->NewInstance(context()).ToLocalChecked();
|
|
set_process_object(process_object);
|
|
|
|
SetupProcessObject(this, argc, argv, exec_argc, exec_argv);
|
|
LoadAsyncWrapperInfo(this);
|
|
}
|
|
|
|
void Environment::CleanupHandles() {
|
|
while (HandleCleanup* hc = handle_cleanup_queue_.PopFront()) {
|
|
handle_cleanup_waiting_++;
|
|
hc->cb_(this, hc->handle_, hc->arg_);
|
|
delete hc;
|
|
}
|
|
|
|
while (handle_cleanup_waiting_ != 0)
|
|
uv_run(event_loop(), UV_RUN_ONCE);
|
|
|
|
while (handle_cleanup_waiting_ != 0)
|
|
uv_run(event_loop(), UV_RUN_ONCE);
|
|
}
|
|
|
|
void Environment::StartProfilerIdleNotifier() {
|
|
uv_prepare_start(&idle_prepare_handle_, [](uv_prepare_t* handle) {
|
|
Environment* env = ContainerOf(&Environment::idle_prepare_handle_, handle);
|
|
env->isolate()->GetCpuProfiler()->SetIdle(true);
|
|
});
|
|
|
|
uv_check_start(&idle_check_handle_, [](uv_check_t* handle) {
|
|
Environment* env = ContainerOf(&Environment::idle_check_handle_, handle);
|
|
env->isolate()->GetCpuProfiler()->SetIdle(false);
|
|
});
|
|
}
|
|
|
|
void Environment::StopProfilerIdleNotifier() {
|
|
uv_prepare_stop(&idle_prepare_handle_);
|
|
uv_check_stop(&idle_check_handle_);
|
|
}
|
|
|
|
void Environment::PrintSyncTrace() const {
|
|
if (!trace_sync_io_)
|
|
return;
|
|
|
|
HandleScope handle_scope(isolate());
|
|
Local<v8::StackTrace> stack =
|
|
StackTrace::CurrentStackTrace(isolate(), 10, StackTrace::kDetailed);
|
|
|
|
fprintf(stderr, "(node:%d) WARNING: Detected use of sync API\n", getpid());
|
|
|
|
for (int i = 0; i < stack->GetFrameCount() - 1; i++) {
|
|
Local<StackFrame> stack_frame = stack->GetFrame(i);
|
|
node::Utf8Value fn_name_s(isolate(), stack_frame->GetFunctionName());
|
|
node::Utf8Value script_name(isolate(), stack_frame->GetScriptName());
|
|
const int line_number = stack_frame->GetLineNumber();
|
|
const int column = stack_frame->GetColumn();
|
|
|
|
if (stack_frame->IsEval()) {
|
|
if (stack_frame->GetScriptId() == Message::kNoScriptIdInfo) {
|
|
fprintf(stderr, " at [eval]:%i:%i\n", line_number, column);
|
|
} else {
|
|
fprintf(stderr,
|
|
" at [eval] (%s:%i:%i)\n",
|
|
*script_name,
|
|
line_number,
|
|
column);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (fn_name_s.length() == 0) {
|
|
fprintf(stderr, " at %s:%i:%i\n", *script_name, line_number, column);
|
|
} else {
|
|
fprintf(stderr,
|
|
" at %s (%s:%i:%i)\n",
|
|
*fn_name_s,
|
|
*script_name,
|
|
line_number,
|
|
column);
|
|
}
|
|
}
|
|
fflush(stderr);
|
|
}
|
|
|
|
void Environment::RunAtExitCallbacks() {
|
|
for (AtExitCallback at_exit : at_exit_functions_) {
|
|
at_exit.cb_(at_exit.arg_);
|
|
}
|
|
at_exit_functions_.clear();
|
|
}
|
|
|
|
void Environment::AtExit(void (*cb)(void* arg), void* arg) {
|
|
at_exit_functions_.push_back(AtExitCallback{cb, arg});
|
|
}
|
|
|
|
void Environment::AddPromiseHook(promise_hook_func fn, void* arg) {
|
|
auto it = std::find_if(
|
|
promise_hooks_.begin(), promise_hooks_.end(),
|
|
[&](const PromiseHookCallback& hook) {
|
|
return hook.cb_ == fn && hook.arg_ == arg;
|
|
});
|
|
CHECK_EQ(it, promise_hooks_.end());
|
|
promise_hooks_.push_back(PromiseHookCallback{fn, arg});
|
|
|
|
if (promise_hooks_.size() == 1) {
|
|
isolate_->SetPromiseHook(EnvPromiseHook);
|
|
}
|
|
}
|
|
|
|
bool Environment::RemovePromiseHook(promise_hook_func fn, void* arg) {
|
|
auto it = std::find_if(
|
|
promise_hooks_.begin(), promise_hooks_.end(),
|
|
[&](const PromiseHookCallback& hook) {
|
|
return hook.cb_ == fn && hook.arg_ == arg;
|
|
});
|
|
|
|
if (it == promise_hooks_.end()) return false;
|
|
|
|
promise_hooks_.erase(it);
|
|
if (promise_hooks_.empty()) {
|
|
isolate_->SetPromiseHook(nullptr);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Environment::EnvPromiseHook(v8::PromiseHookType type,
|
|
v8::Local<v8::Promise> promise,
|
|
v8::Local<v8::Value> parent) {
|
|
Environment* env = Environment::GetCurrent(promise->CreationContext());
|
|
for (const PromiseHookCallback& hook : env->promise_hooks_) {
|
|
hook.cb_(type, promise, parent, hook.arg_);
|
|
}
|
|
}
|
|
|
|
} // namespace node
|
|
|