Skip to content

Commit 2da532c

Browse files
Stephen Belangerruyadorno
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‎

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

Lines changed: 24 additions & 0 deletions
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
voidEnvironment::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+
voidEnvironment::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
voidEnvironment::PrintSyncTrace() const{
643667
if (!trace_sync_io_) return;
644668

‎src/env.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,8 @@ class Environment : public MemoryRetainer{
10571057
inlinevoidAssignToContext(v8::Local<v8::Context> context,
10581058
const ContextInfo& info);
10591059

1060+
voidStartProfilerIdleNotifier();
1061+
10601062
inline v8::Isolate* isolate() const;
10611063
inlineuv_loop_t* event_loop() const;
10621064
inlinevoidTryLoadAddon(
@@ -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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
constcommon=require('../common');
3+
4+
common.skipIfInspectorDisabled();
5+
6+
constassert=require('assert');
7+
const{ Session }=require('inspector');
8+
const{ promisify }=require('util');
9+
10+
constsleep=promisify(setTimeout);
11+
12+
asyncfunctiontest(){
13+
constinspector=newSession();
14+
inspector.connect();
15+
16+
inspector.post('Profiler.enable');
17+
inspector.post('Profiler.start');
18+
19+
awaitsleep(1000);
20+
21+
const{ profile }=awaitnewPromise((resolve,reject)=>{
22+
inspector.post('Profiler.stop',(err,params)=>{
23+
if(err)returnreject(err);
24+
resolve(params);
25+
});
26+
});
27+
28+
lethasIdle=false;
29+
for(constnodeofprofile.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)