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