@@ -23,6 +23,9 @@ using v8::Array;
2323using v8::ArrayBuffer;
2424using v8::Boolean;
2525using v8::Context;
26+ using v8::CpuProfile;
27+ using v8::CpuProfiler;
28+ using v8::CpuProfilingStatus;
2629using v8::Float64Array;
2730using v8::FunctionCallbackInfo;
2831using v8::FunctionTemplate;
@@ -495,7 +498,10 @@ Worker::~Worker() {
495498 CHECK (stopped_);
496499 CHECK_NULL (env_);
497500 CHECK (!tid_.has_value ());
498-
501+ if (!cpu_profiler_) {
502+ cpu_profiler_->Dispose ();
503+ cpu_profiler_ = nullptr ;
504+ }
499505 Debug (this , " Worker %llu destroyed" , thread_id_.id );
500506}
501507
@@ -897,6 +903,164 @@ void Worker::CpuUsage(const FunctionCallbackInfo<Value>& args) {
897903 }
898904}
899905
906+ class WorkerCpuProfileTaker : public AsyncWrap {
907+ public:
908+ WorkerCpuProfileTaker (Environment* env, Local<Object> obj)
909+ : AsyncWrap(env, obj, AsyncWrap::PROVIDER_WORKERCPUPROFILE) {}
910+
911+ SET_NO_MEMORY_INFO ()
912+ SET_MEMORY_INFO_NAME (WorkerCpuProfileTaker)
913+ SET_SELF_SIZE (WorkerCpuProfileTaker)
914+ };
915+
916+ void Worker::StartCpuProfile (const FunctionCallbackInfo<Value>& args) {
917+ Worker* w;
918+ ASSIGN_OR_RETURN_UNWRAP (&w, args.This ());
919+ Environment* env = w->env ();
920+
921+ CHECK (args[0 ]->IsString ());
922+ node::Utf8Value name (env->isolate (), args[0 ]);
923+
924+ AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope (w);
925+ Local<Object> wrap;
926+ if (!env->worker_cpu_profile_taker_template ()
927+ ->NewInstance (env->context ())
928+ .ToLocal (&wrap)) {
929+ return ;
930+ }
931+
932+ BaseObjectPtr<WorkerCpuProfileTaker> taker =
933+ MakeDetachedBaseObject<WorkerCpuProfileTaker>(env, wrap);
934+
935+ bool scheduled = w->RequestInterrupt ([taker = std::move (taker),
936+ name = name.ToString (),
937+ env,
938+ w](Environment* worker_env) mutable {
939+ Isolate* isolate = worker_env->isolate ();
940+ if (!w->cpu_profiler_ ) {
941+ w->cpu_profiler_ = CpuProfiler::New (isolate);
942+ }
943+ Local<String> title = String::NewFromUtf8 (isolate,
944+ name.data (),
945+ NewStringType::kNormal ,
946+ name.size ()).ToLocalChecked ();
947+ CpuProfilingStatus status = w->cpu_profiler_ ->StartProfiling (title, true );
948+ env->SetImmediateThreadsafe (
949+ [taker = std::move (taker),
950+ status
951+ ](Environment* env) mutable {
952+ Isolate* isolate = env->isolate ();
953+ HandleScope handle_scope (isolate);
954+ Context::Scope context_scope (env->context ());
955+ AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope (taker.get ());
956+ Local<Value> argv[] = {
957+ Number::New (isolate, static_cast <double >(status)) // status
958+ };
959+ taker->MakeCallback (env->ondone_string (), arraysize (argv), argv);
960+ },
961+ CallbackFlags::kUnrefed );
962+ });
963+
964+ if (scheduled) {
965+ args.GetReturnValue ().Set (wrap);
966+ }
967+ }
968+
969+ class JSONOutputStream : public v8 ::OutputStream {
970+ public:
971+ JSONOutputStream () {}
972+
973+ int GetChunkSize () override {
974+ return 65536 ;
975+ }
976+
977+ void EndOfStream () override {}
978+
979+ WriteResult WriteAsciiChunk (char * data, const int size) override {
980+ out_stream_.write (data, size);
981+ return kContinue ;
982+ }
983+
984+ std::ostringstream& out_stream () { return out_stream_; }
985+
986+ private:
987+ std::ostringstream out_stream_;
988+ };
989+
990+ void Worker::StopCpuProfile (const FunctionCallbackInfo<Value>& args) {
991+ Worker* w;
992+ ASSIGN_OR_RETURN_UNWRAP (&w, args.This ());
993+
994+ Environment* env = w->env ();
995+ CHECK (args[0 ]->IsString ());
996+ node::Utf8Value name (env->isolate (), args[0 ]);
997+
998+ AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope (w);
999+ Local<Object> wrap;
1000+ if (!env->worker_cpu_profile_taker_template ()
1001+ ->NewInstance (env->context ())
1002+ .ToLocal (&wrap)) {
1003+ return ;
1004+ }
1005+
1006+ BaseObjectPtr<WorkerCpuProfileTaker> taker =
1007+ MakeDetachedBaseObject<WorkerCpuProfileTaker>(env, wrap);
1008+
1009+ bool scheduled = w->RequestInterrupt ([taker = std::move (taker),
1010+ name = name.ToString (),
1011+ env,
1012+ w](Environment* worker_env) mutable {
1013+ Local<String> title = String::NewFromUtf8 (worker_env->isolate (),
1014+ name.data (),
1015+ NewStringType::kNormal ,
1016+ name.size ()).ToLocalChecked ();
1017+ bool found = false ;
1018+ auto json_out_stream = std::make_unique<JSONOutputStream>();
1019+ if (w->cpu_profiler_ ) {
1020+ CpuProfile* profile = w->cpu_profiler_ ->StopProfiling (title);
1021+ if (profile) {
1022+ profile->Serialize (json_out_stream.get (),
1023+ CpuProfile::SerializationFormat::kJSON );
1024+ profile->Delete ();
1025+ found = true ;
1026+ }
1027+ }
1028+ env->SetImmediateThreadsafe (
1029+ [taker = std::move (taker),
1030+ json_out_stream = std::move (json_out_stream),
1031+ found
1032+ ](Environment* env) mutable {
1033+ Isolate* isolate = env->isolate ();
1034+ HandleScope handle_scope (isolate);
1035+ Context::Scope context_scope (env->context ());
1036+ AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope (taker.get ());
1037+ Local<Value> argv[] = {
1038+ Undefined (isolate), // status
1039+ Undefined (isolate), // profile
1040+ };
1041+ if (found) {
1042+ argv[0 ] = Number::New (isolate, 0 );
1043+ Local<Value> result;
1044+ if (ToV8Value (env->context (),
1045+ json_out_stream->out_stream ().str (),
1046+ isolate).ToLocal (&result)) {
1047+ argv[1 ] = result;
1048+ } else {
1049+ argv[1 ] = FIXED_ONE_BYTE_STRING (isolate, " {}" );
1050+ }
1051+ } else {
1052+ argv[0 ] = Number::New (isolate, 1 );
1053+ }
1054+ taker->MakeCallback (env->ondone_string (), arraysize (argv), argv);
1055+ },
1056+ CallbackFlags::kUnrefed );
1057+ });
1058+
1059+ if (scheduled) {
1060+ args.GetReturnValue ().Set (wrap);
1061+ }
1062+ }
1063+
9001064class WorkerHeapStatisticsTaker : public AsyncWrap {
9011065 public:
9021066 WorkerHeapStatisticsTaker (Environment* env, Local<Object> obj)
@@ -1189,6 +1353,8 @@ void CreateWorkerPerIsolateProperties(IsolateData* isolate_data,
11891353 SetProtoMethod (isolate, w, " loopStartTime" , Worker::LoopStartTime);
11901354 SetProtoMethod (isolate, w, " getHeapStatistics" , Worker::GetHeapStatistics);
11911355 SetProtoMethod (isolate, w, " cpuUsage" , Worker::CpuUsage);
1356+ SetProtoMethod (isolate, w, " startCpuProfile" , Worker::StartCpuProfile);
1357+ SetProtoMethod (isolate, w, " stopCpuProfile" , Worker::StopCpuProfile);
11921358
11931359 SetConstructorFunction (isolate, target, " Worker" , w);
11941360 }
@@ -1234,6 +1400,20 @@ void CreateWorkerPerIsolateProperties(IsolateData* isolate_data,
12341400 isolate_data->set_worker_cpu_usage_taker_template (wst->InstanceTemplate ());
12351401 }
12361402
1403+ {
1404+ Local<FunctionTemplate> wst = NewFunctionTemplate (isolate, nullptr );
1405+
1406+ wst->InstanceTemplate ()->SetInternalFieldCount (
1407+ WorkerCpuProfileTaker::kInternalFieldCount );
1408+ wst->Inherit (AsyncWrap::GetConstructorTemplate (isolate_data));
1409+
1410+ Local<String> wst_string =
1411+ FIXED_ONE_BYTE_STRING (isolate, " WorkerCpuProfileTaker" );
1412+ wst->SetClassName (wst_string);
1413+ isolate_data->set_worker_cpu_profile_taker_template (
1414+ wst->InstanceTemplate ());
1415+ }
1416+
12371417 SetMethod (isolate, target, " getEnvMessagePort" , GetEnvMessagePort);
12381418}
12391419
@@ -1311,6 +1491,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
13111491 registry->Register (Worker::LoopStartTime);
13121492 registry->Register (Worker::GetHeapStatistics);
13131493 registry->Register (Worker::CpuUsage);
1494+ registry->Register (Worker::StartCpuProfile);
1495+ registry->Register (Worker::StopCpuProfile);
13141496}
13151497
13161498} // anonymous namespace
0 commit comments