Skip to content

Commit a35dbac

Browse files
bnoordhuiscodebytere
authored andcommitted
lib,src: remove cpu profiler idle notifier
I added it in commit 57231d5 ("src: notify V8 profiler when we're idle") from October 2013 as a stop-gap measure to measure CPU time rather than wall clock time, otherwise processes that spend a lot of time sleeping in system calls give a false impression of being very busy. That fix is not without drawbacks because the idle flag is set before libuv makes I/O callbacks and cleared again after. I/O callbacks can result into calls into JS code and executing JS code is as non-idle as you can get. In commit 96ffcb9 ("src: reduce cpu profiler overhead") from January 2015, I made Node.js block off the SIGPROF signal that V8's CPU profiler uses before Node.js goes to sleep. The goal of that commit is to reduce the overhead from EINTR system call wakeups but it also has the pleasant side effect of fixing what the idle notifier tried to fix. This commit removes the idle notifier and turns the JS process object methods into no-ops. Fixes: #19009 Refs: #33138 PR-URL: #34010 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 2510ffc commit a35dbac

File tree

9 files changed

+10
-86
lines changed

9 files changed

+10
-86
lines changed

lib/internal/bootstrap/switches/is_main_thread.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ const rawMethods = internalBinding('process_methods');
66
// TODO(joyeecheung): deprecate and remove these underscore methods
77
process._debugProcess = rawMethods._debugProcess;
88
process._debugEnd = rawMethods._debugEnd;
9-
process._startProfilerIdleNotifier = rawMethods._startProfilerIdleNotifier;
10-
process._stopProfilerIdleNotifier = rawMethods._stopProfilerIdleNotifier;
9+
10+
// See the discussion in https://github.com/nodejs/node/issues/19009 and
11+
// https://github.com/nodejs/node/pull/34010 for why these are no-ops.
12+
// Five word summary: they were broken beyond repair.
13+
process._startProfilerIdleNotifier = () => {};
14+
process._stopProfilerIdleNotifier = () => {};
1115

1216
function defineStream(name, getter) {
1317
ObjectDefineProperty(process, name, {

lib/internal/bootstrap/switches/is_not_main_thread.js

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ const { ObjectDefineProperty } = primordials;
44

55
delete process._debugProcess;
66
delete process._debugEnd;
7-
delete process._startProfilerIdleNotifier;
8-
delete process._stopProfilerIdleNotifier;
97

108
function defineStream(name, getter) {
119
ObjectDefineProperty(process, name, {

src/api/environment.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ MaybeLocal<Value> LoadEnvironment(
420420
Environment* env,
421421
StartExecutionCallback cb,
422422
std::unique_ptr<InspectorParentHandle> removeme) {
423-
env->InitializeLibuv(per_process::v8_is_profiling);
423+
env->InitializeLibuv();
424424
env->InitializeDiagnostics();
425425

426426
return StartExecution(env, cb);

src/env-inl.h

-4
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,6 @@ inline Environment* Environment::GetThreadLocalEnv() {
378378
return static_cast<Environment*>(uv_key_get(&thread_local_env));
379379
}
380380

381-
inline bool Environment::profiler_idle_notifier_started() const {
382-
return profiler_idle_notifier_started_;
383-
}
384-
385381
inline v8::Isolate* Environment::isolate() const {
386382
return isolate_;
387383
}

src/env.cc

+1-43
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ Environment::~Environment() {
485485
CHECK_EQ(base_object_count_, 0);
486486
}
487487

488-
void Environment::InitializeLibuv(bool start_profiler_idle_notifier) {
488+
void Environment::InitializeLibuv() {
489489
HandleScope handle_scope(isolate());
490490
Context::Scope context_scope(context());
491491

@@ -499,17 +499,6 @@ void Environment::InitializeLibuv(bool start_profiler_idle_notifier) {
499499

500500
uv_check_start(immediate_check_handle(), CheckImmediate);
501501

502-
// Inform V8's CPU profiler when we're idle. The profiler is sampling-based
503-
// but not all samples are created equal; mark the wall clock time spent in
504-
// epoll_wait() and friends so profiling tools can filter it out. The samples
505-
// still end up in v8.log but with state=IDLE rather than state=EXTERNAL.
506-
// TODO(bnoordhuis) Depends on a libuv implementation detail that we should
507-
// probably fortify in the API contract, namely that the last started prepare
508-
// or check watcher runs first. It's not 100% foolproof; if an add-on starts
509-
// a prepare or check watcher after us, any samples attributed to its callback
510-
// will be recorded with state=IDLE.
511-
uv_prepare_init(event_loop(), &idle_prepare_handle_);
512-
uv_check_init(event_loop(), &idle_check_handle_);
513502
uv_async_init(
514503
event_loop(),
515504
&task_queues_async_,
@@ -518,8 +507,6 @@ void Environment::InitializeLibuv(bool start_profiler_idle_notifier) {
518507
&Environment::task_queues_async_, async);
519508
env->RunAndClearNativeImmediates();
520509
});
521-
uv_unref(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
522-
uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
523510
uv_unref(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
524511

525512
{
@@ -536,10 +523,6 @@ void Environment::InitializeLibuv(bool start_profiler_idle_notifier) {
536523
// the one environment per process setup, but will be called in
537524
// FreeEnvironment.
538525
RegisterHandleCleanups();
539-
540-
if (start_profiler_idle_notifier) {
541-
StartProfilerIdleNotifier();
542-
}
543526
}
544527

545528
void Environment::ExitEnv() {
@@ -567,8 +550,6 @@ void Environment::RegisterHandleCleanups() {
567550
register_handle(reinterpret_cast<uv_handle_t*>(timer_handle()));
568551
register_handle(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));
569552
register_handle(reinterpret_cast<uv_handle_t*>(immediate_idle_handle()));
570-
register_handle(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
571-
register_handle(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
572553
register_handle(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
573554
}
574555

@@ -600,29 +581,6 @@ void Environment::CleanupHandles() {
600581
}
601582
}
602583

603-
void Environment::StartProfilerIdleNotifier() {
604-
if (profiler_idle_notifier_started_)
605-
return;
606-
607-
profiler_idle_notifier_started_ = true;
608-
609-
uv_prepare_start(&idle_prepare_handle_, [](uv_prepare_t* handle) {
610-
Environment* env = ContainerOf(&Environment::idle_prepare_handle_, handle);
611-
env->isolate()->SetIdle(true);
612-
});
613-
614-
uv_check_start(&idle_check_handle_, [](uv_check_t* handle) {
615-
Environment* env = ContainerOf(&Environment::idle_check_handle_, handle);
616-
env->isolate()->SetIdle(false);
617-
});
618-
}
619-
620-
void Environment::StopProfilerIdleNotifier() {
621-
profiler_idle_notifier_started_ = false;
622-
uv_prepare_stop(&idle_prepare_handle_);
623-
uv_check_stop(&idle_check_handle_);
624-
}
625-
626584
void Environment::PrintSyncTrace() const {
627585
if (!trace_sync_io_) return;
628586

src/env.h

+1-8
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,7 @@ class Environment : public MemoryRetainer {
879879
ThreadId thread_id);
880880
~Environment() override;
881881

882-
void InitializeLibuv(bool start_profiler_idle_notifier);
882+
void InitializeLibuv();
883883
inline const std::vector<std::string>& exec_argv();
884884
inline const std::vector<std::string>& argv();
885885
const std::string& exec_path() const;
@@ -909,10 +909,6 @@ class Environment : public MemoryRetainer {
909909
inline void AssignToContext(v8::Local<v8::Context> context,
910910
const ContextInfo& info);
911911

912-
void StartProfilerIdleNotifier();
913-
void StopProfilerIdleNotifier();
914-
inline bool profiler_idle_notifier_started() const;
915-
916912
inline v8::Isolate* isolate() const;
917913
inline uv_loop_t* event_loop() const;
918914
inline void TryLoadAddon(
@@ -1234,11 +1230,8 @@ class Environment : public MemoryRetainer {
12341230
uv_timer_t timer_handle_;
12351231
uv_check_t immediate_check_handle_;
12361232
uv_idle_t immediate_idle_handle_;
1237-
uv_prepare_t idle_prepare_handle_;
1238-
uv_check_t idle_check_handle_;
12391233
uv_async_t task_queues_async_;
12401234
int64_t task_queues_async_refs_ = 0;
1241-
bool profiler_idle_notifier_started_ = false;
12421235

12431236
AsyncHooks async_hooks_;
12441237
ImmediateInfo immediate_info_;

src/node.cc

+1-11
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,6 @@ bool v8_initialized = false;
148148
// node_internals.h
149149
// process-relative uptime base in nanoseconds, initialized in node::Start()
150150
uint64_t node_start_time;
151-
// Tells whether --prof is passed.
152-
bool v8_is_profiling = false;
153151

154152
// node_v8_platform-inl.h
155153
struct V8Platform v8_platform;
@@ -789,19 +787,11 @@ int ProcessGlobalArgs(std::vector<std::string>* args,
789787
env_opts->abort_on_uncaught_exception = true;
790788
}
791789

792-
// TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler
793-
// manually? That would give us a little more control over its runtime
794-
// behavior but it could also interfere with the user's intentions in ways
795-
// we fail to anticipate. Dillema.
796-
if (std::find(v8_args.begin(), v8_args.end(), "--prof") != v8_args.end()) {
797-
per_process::v8_is_profiling = true;
798-
}
799-
800790
#ifdef __POSIX__
801791
// Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the
802792
// performance penalty of frequent EINTR wakeups when the profiler is running.
803793
// Only do this for v8.log profiling, as it breaks v8::CpuProfiler users.
804-
if (per_process::v8_is_profiling) {
794+
if (std::find(v8_args.begin(), v8_args.end(), "--prof") != v8_args.end()) {
805795
uv_loop_configure(uv_default_loop(), UV_LOOP_BLOCK_SIGNAL, SIGPROF);
806796
}
807797
#endif

src/node_internals.h

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ class NativeModuleLoader;
5555
namespace per_process {
5656
extern Mutex env_var_mutex;
5757
extern uint64_t node_start_time;
58-
extern bool v8_is_profiling;
5958
} // namespace per_process
6059

6160
// Forward declaration

src/node_process_methods.cc

-14
Original file line numberDiff line numberDiff line change
@@ -224,16 +224,6 @@ void RawDebug(const FunctionCallbackInfo<Value>& args) {
224224
fflush(stderr);
225225
}
226226

227-
static void StartProfilerIdleNotifier(const FunctionCallbackInfo<Value>& args) {
228-
Environment* env = Environment::GetCurrent(args);
229-
env->StartProfilerIdleNotifier();
230-
}
231-
232-
static void StopProfilerIdleNotifier(const FunctionCallbackInfo<Value>& args) {
233-
Environment* env = Environment::GetCurrent(args);
234-
env->StopProfilerIdleNotifier();
235-
}
236-
237227
static void Umask(const FunctionCallbackInfo<Value>& args) {
238228
Environment* env = Environment::GetCurrent(args);
239229
CHECK(env->has_run_bootstrapping_code());
@@ -456,10 +446,6 @@ static void InitializeProcessMethods(Local<Object> target,
456446
env->SetMethod(target, "chdir", Chdir);
457447
}
458448

459-
env->SetMethod(
460-
target, "_startProfilerIdleNotifier", StartProfilerIdleNotifier);
461-
env->SetMethod(target, "_stopProfilerIdleNotifier", StopProfilerIdleNotifier);
462-
463449
env->SetMethod(target, "umask", Umask);
464450
env->SetMethod(target, "_rawDebug", RawDebug);
465451
env->SetMethod(target, "memoryUsage", MemoryUsage);

0 commit comments

Comments
 (0)