23
23
namespace node {
24
24
namespace inspector {
25
25
namespace {
26
+
27
+ using node::FatalError;
28
+
26
29
using v8::Array;
30
+ using v8::Boolean ;
27
31
using v8::Context;
28
32
using v8::External;
29
33
using v8::Function;
30
34
using v8::FunctionCallbackInfo;
31
35
using v8::HandleScope;
36
+ using v8::Integer;
32
37
using v8::Isolate;
33
38
using v8::Local;
34
39
using v8::Maybe;
35
40
using v8::MaybeLocal;
41
+ using v8::Name;
36
42
using v8::NewStringType;
37
43
using v8::Object;
38
44
using v8::Persistent;
39
45
using v8::String;
46
+ using v8::Undefined;
40
47
using v8::Value;
41
48
42
49
using v8_inspector::StringBuffer;
@@ -616,6 +623,28 @@ class NodeInspectorClient : public V8InspectorClient {
616
623
timers_.erase (data);
617
624
}
618
625
626
+ // Async stack traces instrumentation.
627
+ void AsyncTaskScheduled (const StringView& task_name, void * task,
628
+ bool recurring) {
629
+ client_->asyncTaskScheduled (task_name, task, recurring);
630
+ }
631
+
632
+ void AsyncTaskCanceled (void * task) {
633
+ client_->asyncTaskCanceled (task);
634
+ }
635
+
636
+ void AsyncTaskStarted (void * task) {
637
+ client_->asyncTaskStarted (task);
638
+ }
639
+
640
+ void AsyncTaskFinished (void * task) {
641
+ client_->asyncTaskFinished (task);
642
+ }
643
+
644
+ void AllAsyncTasksCanceled () {
645
+ client_->allAsyncTasksCanceled ();
646
+ }
647
+
619
648
private:
620
649
node::Environment* env_;
621
650
v8::Platform* platform_;
@@ -676,9 +705,21 @@ bool Agent::StartIoThread(bool wait_for_connect) {
676
705
}
677
706
678
707
v8::Isolate* isolate = parent_env_->isolate ();
708
+ HandleScope handle_scope (isolate);
709
+
710
+ // Enable tracking of async stack traces
711
+ if (!enable_async_hook_function_.IsEmpty ()) {
712
+ Local<Function> enable_fn = enable_async_hook_function_.Get (isolate);
713
+ auto context = parent_env_->context ();
714
+ auto result = enable_fn->Call (context, Undefined (isolate), 0 , nullptr );
715
+ if (result.IsEmpty ()) {
716
+ FatalError (
717
+ " node::InspectorAgent::StartIoThread" ,
718
+ " Cannot enable Inspector's AsyncHook, please report this." );
719
+ }
720
+ }
679
721
680
722
// Send message to enable debug in workers
681
- HandleScope handle_scope (isolate);
682
723
Local<Object> process_object = parent_env_->process_object ();
683
724
Local<Value> emit_fn =
684
725
process_object->Get (FIXED_ONE_BYTE_STRING (isolate, " emit" ));
@@ -717,10 +758,40 @@ void Agent::Stop() {
717
758
if (io_ != nullptr ) {
718
759
io_->Stop ();
719
760
io_.reset ();
761
+ enabled_ = false ;
762
+ }
763
+
764
+ v8::Isolate* isolate = parent_env_->isolate ();
765
+ HandleScope handle_scope (isolate);
766
+
767
+ // Disable tracking of async stack traces
768
+ if (!disable_async_hook_function_.IsEmpty ()) {
769
+ Local<Function> disable_fn = disable_async_hook_function_.Get (isolate);
770
+ auto result = disable_fn->Call (parent_env_->context (),
771
+ Undefined (parent_env_->isolate ()), 0 , nullptr );
772
+ if (result.IsEmpty ()) {
773
+ FatalError (
774
+ " node::InspectorAgent::Stop" ,
775
+ " Cannot disable Inspector's AsyncHook, please report this." );
776
+ }
720
777
}
721
778
}
722
779
723
780
void Agent::Connect (InspectorSessionDelegate* delegate) {
781
+ if (!enabled_) {
782
+ // Enable tracking of async stack traces
783
+ v8::Isolate* isolate = parent_env_->isolate ();
784
+ HandleScope handle_scope (isolate);
785
+ auto context = parent_env_->context ();
786
+ Local<Function> enable_fn = enable_async_hook_function_.Get (isolate);
787
+ auto result = enable_fn->Call (context, Undefined (isolate), 0 , nullptr );
788
+ if (result.IsEmpty ()) {
789
+ FatalError (
790
+ " node::InspectorAgent::Connect" ,
791
+ " Cannot enable Inspector's AsyncHook, please report this." );
792
+ }
793
+ }
794
+
724
795
enabled_ = true ;
725
796
client_->connectFrontend (delegate);
726
797
}
@@ -773,6 +844,34 @@ void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
773
844
channel->schedulePauseOnNextStatement (reason);
774
845
}
775
846
847
+ void Agent::RegisterAsyncHook (Isolate* isolate,
848
+ v8::Local<v8::Function> enable_function,
849
+ v8::Local<v8::Function> disable_function) {
850
+ enable_async_hook_function_.Reset (isolate, enable_function);
851
+ disable_async_hook_function_.Reset (isolate, disable_function);
852
+ }
853
+
854
+ void Agent::AsyncTaskScheduled (const StringView& task_name, void * task,
855
+ bool recurring) {
856
+ client_->AsyncTaskScheduled (task_name, task, recurring);
857
+ }
858
+
859
+ void Agent::AsyncTaskCanceled (void * task) {
860
+ client_->AsyncTaskCanceled (task);
861
+ }
862
+
863
+ void Agent::AsyncTaskStarted (void * task) {
864
+ client_->AsyncTaskStarted (task);
865
+ }
866
+
867
+ void Agent::AsyncTaskFinished (void * task) {
868
+ client_->AsyncTaskFinished (task);
869
+ }
870
+
871
+ void Agent::AllAsyncTasksCanceled () {
872
+ client_->AllAsyncTasksCanceled ();
873
+ }
874
+
776
875
void Open (const FunctionCallbackInfo<Value>& args) {
777
876
Environment* env = Environment::GetCurrent (args);
778
877
inspector::Agent* agent = env->inspector_agent ();
@@ -810,6 +909,59 @@ void Url(const FunctionCallbackInfo<Value>& args) {
810
909
args.GetReturnValue ().Set (OneByteString (env->isolate (), url.c_str ()));
811
910
}
812
911
912
+ static void * GetAsyncTask (int64_t asyncId) {
913
+ // The inspector assumes that when other clients use its asyncTask* API,
914
+ // they use real pointers, or at least something aligned like real pointer.
915
+ // In general it means that our task_id should always be even.
916
+ //
917
+ // On 32bit platforms, the 64bit asyncId would get truncated when converted
918
+ // to a 32bit pointer. However, the javascript part will never enable
919
+ // the async_hook on 32bit platforms, therefore the truncation will never
920
+ // happen in practice.
921
+ return reinterpret_cast <void *>(asyncId << 1 );
922
+ }
923
+
924
+ template <void (Agent::*asyncTaskFn)(void *)>
925
+ static void InvokeAsyncTaskFnWithId (const FunctionCallbackInfo<Value>& args) {
926
+ Environment* env = Environment::GetCurrent (args);
927
+ CHECK (args[0 ]->IsNumber ());
928
+ int64_t task_id = args[0 ]->IntegerValue (env->context ()).FromJust ();
929
+ (env->inspector_agent ()->*asyncTaskFn)(GetAsyncTask (task_id));
930
+ }
931
+
932
+ static void AsyncTaskScheduledWrapper (const FunctionCallbackInfo<Value>& args) {
933
+ Environment* env = Environment::GetCurrent (args);
934
+
935
+ CHECK (args[0 ]->IsString ());
936
+ Local<String> task_name = args[0 ].As <String>();
937
+ String::Value task_name_value (task_name);
938
+ StringView task_name_view (*task_name_value, task_name_value.length ());
939
+
940
+ CHECK (args[1 ]->IsNumber ());
941
+ int64_t task_id = args[1 ]->IntegerValue (env->context ()).FromJust ();
942
+ void * task = GetAsyncTask (task_id);
943
+
944
+ CHECK (args[2 ]->IsBoolean ());
945
+ bool recurring = args[2 ]->BooleanValue (env->context ()).FromJust ();
946
+
947
+ env->inspector_agent ()->AsyncTaskScheduled (task_name_view, task, recurring);
948
+ }
949
+
950
+ static void RegisterAsyncHookWrapper (const FunctionCallbackInfo<Value>& args) {
951
+ Environment* env = Environment::GetCurrent (args);
952
+
953
+ CHECK (args[0 ]->IsFunction ());
954
+ v8::Local<v8::Function> enable_function = args[0 ].As <Function>();
955
+ CHECK (args[1 ]->IsFunction ());
956
+ v8::Local<v8::Function> disable_function = args[1 ].As <Function>();
957
+ env->inspector_agent ()->RegisterAsyncHook (env->isolate (),
958
+ enable_function, disable_function);
959
+ }
960
+
961
+ static void IsEnabled (const FunctionCallbackInfo<Value>& args) {
962
+ Environment* env = Environment::GetCurrent (args);
963
+ args.GetReturnValue ().Set (env->inspector_agent ()->enabled ());
964
+ }
813
965
814
966
// static
815
967
void Agent::InitInspector (Local<Object> target, Local<Value> unused,
@@ -830,6 +982,17 @@ void Agent::InitInspector(Local<Object> target, Local<Value> unused,
830
982
env->SetMethod (target, " connect" , ConnectJSBindingsSession);
831
983
env->SetMethod (target, " open" , Open);
832
984
env->SetMethod (target, " url" , Url);
985
+
986
+ env->SetMethod (target, " asyncTaskScheduled" , AsyncTaskScheduledWrapper);
987
+ env->SetMethod (target, " asyncTaskCanceled" ,
988
+ InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>);
989
+ env->SetMethod (target, " asyncTaskStarted" ,
990
+ InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>);
991
+ env->SetMethod (target, " asyncTaskFinished" ,
992
+ InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>);
993
+
994
+ env->SetMethod (target, " registerAsyncHook" , RegisterAsyncHookWrapper);
995
+ env->SetMethod (target, " isEnabled" , IsEnabled);
833
996
}
834
997
835
998
void Agent::RequestIoThreadStart () {
0 commit comments