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;
@@ -616,6 +623,28 @@ class NodeInspectorClient : public V8InspectorClient {
616623 timers_.erase (data);
617624 }
618625
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+
619648 private:
620649 node::Environment* env_;
621650 v8::Platform* platform_;
@@ -676,9 +705,21 @@ bool Agent::StartIoThread(bool wait_for_connect) {
676705 }
677706
678707 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+ }
679721
680722 // Send message to enable debug in workers
681- HandleScope handle_scope (isolate);
682723 Local<Object> process_object = parent_env_->process_object ();
683724 Local<Value> emit_fn =
684725 process_object->Get (FIXED_ONE_BYTE_STRING (isolate, " emit" ));
@@ -717,10 +758,40 @@ void Agent::Stop() {
717758 if (io_ != nullptr ) {
718759 io_->Stop ();
719760 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+ }
720777 }
721778}
722779
723780void 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+
724795 enabled_ = true ;
725796 client_->connectFrontend (delegate);
726797}
@@ -773,6 +844,34 @@ void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
773844 channel->schedulePauseOnNextStatement (reason);
774845}
775846
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+
776875void Open (const FunctionCallbackInfo<Value>& args) {
777876 Environment* env = Environment::GetCurrent (args);
778877 inspector::Agent* agent = env->inspector_agent ();
@@ -810,6 +909,59 @@ void Url(const FunctionCallbackInfo<Value>& args) {
810909 args.GetReturnValue ().Set (OneByteString (env->isolate (), url.c_str ()));
811910}
812911
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+ }
813965
814966// static
815967void Agent::InitInspector (Local<Object> target, Local<Value> unused,
@@ -830,6 +982,17 @@ void Agent::InitInspector(Local<Object> target, Local<Value> unused,
830982 env->SetMethod (target, " connect" , ConnectJSBindingsSession);
831983 env->SetMethod (target, " open" , Open);
832984 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);
833996}
834997
835998void Agent::RequestIoThreadStart () {
0 commit comments