2323namespace node {
2424namespace inspector {
2525namespace {
26+
27+ using node::FatalError;
28+
2629using v8::Array;
30+ using v8::Boolean;
2731using v8::Context;
2832using v8::External;
2933using v8::Function;
3034using v8::FunctionCallbackInfo;
3135using v8::HandleScope;
36+ using v8::Integer;
3237using v8::Isolate;
3338using v8::Local;
3439using v8::Maybe;
3540using v8::MaybeLocal;
41+ using v8::Name;
3642using v8::NewStringType;
3743using v8::Object;
3844using v8::Persistent;
3945using v8::String;
46+ using v8::Undefined;
4047using v8::Value;
4148
4249using v8_inspector::StringBuffer;
@@ -613,6 +620,28 @@ class NodeInspectorClient : public V8InspectorClient {
613620 timers_.erase (data);
614621 }
615622
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+
616645 private:
617646 node::Environment* env_;
618647 node::NodePlatform* platform_;
@@ -673,9 +702,21 @@ bool Agent::StartIoThread(bool wait_for_connect) {
673702 }
674703
675704 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+ }
676718
677719 // Send message to enable debug in workers
678- HandleScope handle_scope (isolate);
679720 Local<Object> process_object = parent_env_->process_object ();
680721 Local<Value> emit_fn =
681722 process_object->Get (FIXED_ONE_BYTE_STRING (isolate, " emit" ));
@@ -714,10 +755,40 @@ void Agent::Stop() {
714755 if (io_ != nullptr ) {
715756 io_->Stop ();
716757 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+ }
717774 }
718775}
719776
720777void 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+
721792 enabled_ = true ;
722793 client_->connectFrontend (delegate);
723794}
@@ -770,6 +841,34 @@ void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
770841 channel->schedulePauseOnNextStatement (reason);
771842}
772843
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+
773872void Open (const FunctionCallbackInfo<Value>& args) {
774873 Environment* env = Environment::GetCurrent (args);
775874 inspector::Agent* agent = env->inspector_agent ();
@@ -807,6 +906,59 @@ void Url(const FunctionCallbackInfo<Value>& args) {
807906 args.GetReturnValue ().Set (OneByteString (env->isolate (), url.c_str ()));
808907}
809908
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+ }
810962
811963// static
812964void Agent::InitInspector (Local<Object> target, Local<Value> unused,
@@ -827,6 +979,17 @@ void Agent::InitInspector(Local<Object> target, Local<Value> unused,
827979 env->SetMethod (target, " connect" , ConnectJSBindingsSession);
828980 env->SetMethod (target, " open" , Open);
829981 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);
830993}
831994
832995void Agent::RequestIoThreadStart () {
0 commit comments