Skip to content

Commit 2da532c

Browse files
Stephen Belangerruyadorno
Stephen Belanger
authored andcommitted
src: report idle time correctly
With this change, the V8 profiler will attribute any time between prepare and check cycles, except any entrances to InternalCallbackScope, to be "idle" time. All callbacks, microtasks, and timers will be marked as not idle. The one exception is native modules which don't use the MakeCallback helper, but those are already broken anyway for async context tracking so we should just encourage broken modules to be fixed. PR-URL: #37868 Reviewed-By: Gerhard Stöbich <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
1 parent 50fc6b9 commit 2da532c

File tree

4 files changed

+75
-0
lines changed

4 files changed

+75
-0
lines changed

src/api/callback.cc

+4
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ InternalCallbackScope::InternalCallbackScope(Environment* env,
6060
// If you hit this assertion, you forgot to enter the v8::Context first.
6161
CHECK_EQ(Environment::GetCurrent(env->isolate()), env);
6262

63+
env->isolate()->SetIdle(false);
64+
6365
env->async_hooks()->push_async_context(
6466
async_context_.async_id, async_context_.trigger_async_id, object);
6567

@@ -81,6 +83,8 @@ void InternalCallbackScope::Close() {
8183
if (closed_) return;
8284
closed_ = true;
8385

86+
auto idle = OnScopeLeave([&]() { env_->isolate()->SetIdle(true); });
87+
8488
if (!env_->can_call_into_js()) return;
8589
auto perform_stopping_check = [&]() {
8690
if (env_->is_stopping()) {

src/env.cc

+24
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,13 @@ void Environment::InitializeLibuv() {
555555

556556
uv_check_start(immediate_check_handle(), CheckImmediate);
557557

558+
// Inform V8's CPU profiler when we're idle. The profiler is sampling-based
559+
// but not all samples are created equal; mark the wall clock time spent in
560+
// epoll_wait() and friends so profiling tools can filter it out. The samples
561+
// still end up in v8.log but with state=IDLE rather than state=EXTERNAL.
562+
uv_prepare_init(event_loop(), &idle_prepare_handle_);
563+
uv_check_init(event_loop(), &idle_check_handle_);
564+
558565
uv_async_init(
559566
event_loop(),
560567
&task_queues_async_,
@@ -565,6 +572,8 @@ void Environment::InitializeLibuv() {
565572
Context::Scope context_scope(env->context());
566573
env->RunAndClearNativeImmediates();
567574
});
575+
uv_unref(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
576+
uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
568577
uv_unref(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
569578

570579
{
@@ -581,6 +590,8 @@ void Environment::InitializeLibuv() {
581590
// the one environment per process setup, but will be called in
582591
// FreeEnvironment.
583592
RegisterHandleCleanups();
593+
594+
StartProfilerIdleNotifier();
584595
}
585596

586597
void Environment::ExitEnv() {
@@ -608,6 +619,8 @@ void Environment::RegisterHandleCleanups() {
608619
register_handle(reinterpret_cast<uv_handle_t*>(timer_handle()));
609620
register_handle(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));
610621
register_handle(reinterpret_cast<uv_handle_t*>(immediate_idle_handle()));
622+
register_handle(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
623+
register_handle(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
611624
register_handle(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
612625
}
613626

@@ -639,6 +652,17 @@ void Environment::CleanupHandles() {
639652
}
640653
}
641654

655+
void Environment::StartProfilerIdleNotifier() {
656+
uv_prepare_start(&idle_prepare_handle_, [](uv_prepare_t* handle) {
657+
Environment* env = ContainerOf(&Environment::idle_prepare_handle_, handle);
658+
env->isolate()->SetIdle(true);
659+
});
660+
uv_check_start(&idle_check_handle_, [](uv_check_t* handle) {
661+
Environment* env = ContainerOf(&Environment::idle_check_handle_, handle);
662+
env->isolate()->SetIdle(false);
663+
});
664+
}
665+
642666
void Environment::PrintSyncTrace() const {
643667
if (!trace_sync_io_) return;
644668

src/env.h

+4
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,8 @@ class Environment : public MemoryRetainer {
10571057
inline void AssignToContext(v8::Local<v8::Context> context,
10581058
const ContextInfo& info);
10591059

1060+
void StartProfilerIdleNotifier();
1061+
10601062
inline v8::Isolate* isolate() const;
10611063
inline uv_loop_t* event_loop() const;
10621064
inline void TryLoadAddon(
@@ -1408,6 +1410,8 @@ class Environment : public MemoryRetainer {
14081410
uv_timer_t timer_handle_;
14091411
uv_check_t immediate_check_handle_;
14101412
uv_idle_t immediate_idle_handle_;
1413+
uv_prepare_t idle_prepare_handle_;
1414+
uv_check_t idle_check_handle_;
14111415
uv_async_t task_queues_async_;
14121416
int64_t task_queues_async_refs_ = 0;
14131417

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
const common = require('../common');
3+
4+
common.skipIfInspectorDisabled();
5+
6+
const assert = require('assert');
7+
const { Session } = require('inspector');
8+
const { promisify } = require('util');
9+
10+
const sleep = promisify(setTimeout);
11+
12+
async function test() {
13+
const inspector = new Session();
14+
inspector.connect();
15+
16+
inspector.post('Profiler.enable');
17+
inspector.post('Profiler.start');
18+
19+
await sleep(1000);
20+
21+
const { profile } = await new Promise((resolve, reject) => {
22+
inspector.post('Profiler.stop', (err, params) => {
23+
if (err) return reject(err);
24+
resolve(params);
25+
});
26+
});
27+
28+
let hasIdle = false;
29+
for (const node of profile.nodes) {
30+
if (node.callFrame.functionName === '(idle)') {
31+
hasIdle = true;
32+
break;
33+
}
34+
}
35+
assert(hasIdle);
36+
37+
inspector.post('Profiler.disable');
38+
inspector.disconnect();
39+
}
40+
41+
test().then(common.mustCall(() => {
42+
console.log('Done!');
43+
}));

0 commit comments

Comments
 (0)