Skip to content

Commit 40bc309

Browse files
bnoordhuisjasnell
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 cb2c810 commit 40bc309

File tree

9 files changed

+10
-86
lines changed

9 files changed

+10
-86
lines changed

lib/internal/bootstrap/switches/is_main_thread.js

Lines changed: 6 additions & 2 deletions
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

Lines changed: 0 additions & 2 deletions
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ MaybeLocal<Value> LoadEnvironment(
426426
Environment* env,
427427
StartExecutionCallback cb,
428428
std::unique_ptr<InspectorParentHandle> removeme) {
429-
env->InitializeLibuv(per_process::v8_is_profiling);
429+
env->InitializeLibuv();
430430
env->InitializeDiagnostics();
431431

432432
return StartExecution(env, cb);

src/env-inl.h

Lines changed: 0 additions & 4 deletions
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

Lines changed: 1 addition & 43 deletions
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

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ class Environment : public MemoryRetainer {
925925
ThreadId thread_id);
926926
~Environment() override;
927927

928-
void InitializeLibuv(bool start_profiler_idle_notifier);
928+
void InitializeLibuv();
929929
inline const std::vector<std::string>& exec_argv();
930930
inline const std::vector<std::string>& argv();
931931
const std::string& exec_path() const;
@@ -955,10 +955,6 @@ class Environment : public MemoryRetainer {
955955
inline void AssignToContext(v8::Local<v8::Context> context,
956956
const ContextInfo& info);
957957

958-
void StartProfilerIdleNotifier();
959-
void StopProfilerIdleNotifier();
960-
inline bool profiler_idle_notifier_started() const;
961-
962958
inline v8::Isolate* isolate() const;
963959
inline uv_loop_t* event_loop() const;
964960
inline void TryLoadAddon(
@@ -1285,11 +1281,8 @@ class Environment : public MemoryRetainer {
12851281
uv_timer_t timer_handle_;
12861282
uv_check_t immediate_check_handle_;
12871283
uv_idle_t immediate_idle_handle_;
1288-
uv_prepare_t idle_prepare_handle_;
1289-
uv_check_t idle_check_handle_;
12901284
uv_async_t task_queues_async_;
12911285
int64_t task_queues_async_refs_ = 0;
1292-
bool profiler_idle_notifier_started_ = false;
12931286

12941287
AsyncHooks async_hooks_;
12951288
ImmediateInfo immediate_info_;

src/node.cc

Lines changed: 1 addition & 11 deletions
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;
@@ -782,19 +780,11 @@ int ProcessGlobalArgs(std::vector<std::string>* args,
782780
env_opts->abort_on_uncaught_exception = true;
783781
}
784782

785-
// TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler
786-
// manually? That would give us a little more control over its runtime
787-
// behavior but it could also interfere with the user's intentions in ways
788-
// we fail to anticipate. Dillema.
789-
if (std::find(v8_args.begin(), v8_args.end(), "--prof") != v8_args.end()) {
790-
per_process::v8_is_profiling = true;
791-
}
792-
793783
#ifdef __POSIX__
794784
// Block SIGPROF signals when sleeping in epoll_wait/kevent/etc. Avoids the
795785
// performance penalty of frequent EINTR wakeups when the profiler is running.
796786
// Only do this for v8.log profiling, as it breaks v8::CpuProfiler users.
797-
if (per_process::v8_is_profiling) {
787+
if (std::find(v8_args.begin(), v8_args.end(), "--prof") != v8_args.end()) {
798788
uv_loop_configure(uv_default_loop(), UV_LOOP_BLOCK_SIGNAL, SIGPROF);
799789
}
800790
#endif

src/node_internals.h

Lines changed: 0 additions & 1 deletion
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

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,6 @@ void RawDebug(const FunctionCallbackInfo<Value>& args) {
196196
fflush(stderr);
197197
}
198198

199-
static void StartProfilerIdleNotifier(const FunctionCallbackInfo<Value>& args) {
200-
Environment* env = Environment::GetCurrent(args);
201-
env->StartProfilerIdleNotifier();
202-
}
203-
204-
static void StopProfilerIdleNotifier(const FunctionCallbackInfo<Value>& args) {
205-
Environment* env = Environment::GetCurrent(args);
206-
env->StopProfilerIdleNotifier();
207-
}
208-
209199
static void Umask(const FunctionCallbackInfo<Value>& args) {
210200
Environment* env = Environment::GetCurrent(args);
211201
CHECK(env->has_run_bootstrapping_code());
@@ -529,10 +519,6 @@ static void InitializeProcessMethods(Local<Object> target,
529519
env->SetMethod(target, "chdir", Chdir);
530520
}
531521

532-
env->SetMethod(
533-
target, "_startProfilerIdleNotifier", StartProfilerIdleNotifier);
534-
env->SetMethod(target, "_stopProfilerIdleNotifier", StopProfilerIdleNotifier);
535-
536522
env->SetMethod(target, "umask", Umask);
537523
env->SetMethod(target, "_rawDebug", RawDebug);
538524
env->SetMethod(target, "memoryUsage", MemoryUsage);

0 commit comments

Comments
 (0)