@@ -816,6 +816,116 @@ void Worker::Unref(const FunctionCallbackInfo<Value>& args) { |
816 | 816 | } |
817 | 817 | } |
818 | 818 |
|
| 819 | +class WorkerHeapStatisticsTaker : public AsyncWrap { |
| 820 | + public: |
| 821 | + WorkerHeapStatisticsTaker(Environment* env, Local<Object> obj) |
| 822 | + : AsyncWrap(env, obj, AsyncWrap::PROVIDER_WORKERHEAPSTATISTICS) {} |
| 823 | + |
| 824 | + SET_NO_MEMORY_INFO() |
| 825 | + SET_MEMORY_INFO_NAME(WorkerHeapStatisticsTaker) |
| 826 | + SET_SELF_SIZE(WorkerHeapStatisticsTaker) |
| 827 | +}; |
| 828 | + |
| 829 | +void Worker::GetHeapStatistics(const FunctionCallbackInfo<Value>& args) { |
| 830 | + Worker* w; |
| 831 | + ASSIGN_OR_RETURN_UNWRAP(&w, args.This()); |
| 832 | + |
| 833 | + Environment* env = w->env(); |
| 834 | + AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w); |
| 835 | + Local<Object> wrap; |
| 836 | + if (!env->worker_heap_statistics_taker_template() |
| 837 | + ->NewInstance(env->context()) |
| 838 | + .ToLocal(&wrap)) { |
| 839 | + return; |
| 840 | + } |
| 841 | + |
| 842 | + // The created WorkerHeapStatisticsTaker is an object owned by main |
| 843 | + // thread's Isolate, it can not be accessed by worker thread |
| 844 | + std::unique_ptr<BaseObjectPtr<WorkerHeapStatisticsTaker>> taker = |
| 845 | + std::make_unique<BaseObjectPtr<WorkerHeapStatisticsTaker>>( |
| 846 | + MakeDetachedBaseObject<WorkerHeapStatisticsTaker>(env, wrap)); |
| 847 | + |
| 848 | + // Interrupt the worker thread and take a snapshot, then schedule a call |
| 849 | + // on the parent thread that turns that snapshot into a readable stream. |
| 850 | + bool scheduled = w->RequestInterrupt([taker = std::move(taker), |
| 851 | + env](Environment* worker_env) mutable { |
| 852 | + // We create a unique pointer to HeapStatistics so that the actual object |
| 853 | + // it's not copied in the lambda, but only the pointer is. |
| 854 | + auto heap_stats = std::make_unique<v8::HeapStatistics>(); |
| 855 | + worker_env->isolate()->GetHeapStatistics(heap_stats.get()); |
| 856 | + |
| 857 | + // Here, the worker thread temporarily owns the WorkerHeapStatisticsTaker |
| 858 | + // object. |
| 859 | + |
| 860 | + env->SetImmediateThreadsafe( |
| 861 | + [taker = std::move(taker), |
| 862 | + heap_stats = std::move(heap_stats)](Environment* env) mutable { |
| 863 | + Isolate* isolate = env->isolate(); |
| 864 | + HandleScope handle_scope(isolate); |
| 865 | + Context::Scope context_scope(env->context()); |
| 866 | + |
| 867 | + AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker->get()); |
| 868 | + |
| 869 | + Local<v8::Name> heap_stats_names[] = { |
| 870 | + FIXED_ONE_BYTE_STRING(isolate, "total_heap_size"), |
| 871 | + FIXED_ONE_BYTE_STRING(isolate, "total_heap_size_executable"), |
| 872 | + FIXED_ONE_BYTE_STRING(isolate, "total_physical_size"), |
| 873 | + FIXED_ONE_BYTE_STRING(isolate, "total_available_size"), |
| 874 | + FIXED_ONE_BYTE_STRING(isolate, "used_heap_size"), |
| 875 | + FIXED_ONE_BYTE_STRING(isolate, "heap_size_limit"), |
| 876 | + FIXED_ONE_BYTE_STRING(isolate, "malloced_memory"), |
| 877 | + FIXED_ONE_BYTE_STRING(isolate, "peak_malloced_memory"), |
| 878 | + FIXED_ONE_BYTE_STRING(isolate, "does_zap_garbage"), |
| 879 | + FIXED_ONE_BYTE_STRING(isolate, "number_of_native_contexts"), |
| 880 | + FIXED_ONE_BYTE_STRING(isolate, "number_of_detached_contexts"), |
| 881 | + FIXED_ONE_BYTE_STRING(isolate, "total_global_handles_size"), |
| 882 | + FIXED_ONE_BYTE_STRING(isolate, "used_global_handles_size"), |
| 883 | + FIXED_ONE_BYTE_STRING(isolate, "external_memory")}; |
| 884 | + |
| 885 | + // Define an array of property values |
| 886 | + Local<Value> heap_stats_values[] = { |
| 887 | + Number::New(isolate, heap_stats->total_heap_size()), |
| 888 | + Number::New(isolate, heap_stats->total_heap_size_executable()), |
| 889 | + Number::New(isolate, heap_stats->total_physical_size()), |
| 890 | + Number::New(isolate, heap_stats->total_available_size()), |
| 891 | + Number::New(isolate, heap_stats->used_heap_size()), |
| 892 | + Number::New(isolate, heap_stats->heap_size_limit()), |
| 893 | + Number::New(isolate, heap_stats->malloced_memory()), |
| 894 | + Number::New(isolate, heap_stats->peak_malloced_memory()), |
| 895 | + Boolean::New(isolate, heap_stats->does_zap_garbage()), |
| 896 | + Number::New(isolate, heap_stats->number_of_native_contexts()), |
| 897 | + Number::New(isolate, heap_stats->number_of_detached_contexts()), |
| 898 | + Number::New(isolate, heap_stats->total_global_handles_size()), |
| 899 | + Number::New(isolate, heap_stats->used_global_handles_size()), |
| 900 | + Number::New(isolate, heap_stats->external_memory())}; |
| 901 | + |
| 902 | + DCHECK_EQ(arraysize(heap_stats_names), arraysize(heap_stats_values)); |
| 903 | + |
| 904 | + // Create the object with the property names and values |
| 905 | + Local<Object> stats = Object::New(isolate, |
| 906 | + Null(isolate), |
| 907 | + heap_stats_names, |
| 908 | + heap_stats_values, |
| 909 | + arraysize(heap_stats_names)); |
| 910 | + |
| 911 | + Local<Value> args[] = {stats}; |
| 912 | + taker->get()->MakeCallback( |
| 913 | + env->ondone_string(), arraysize(args), args); |
| 914 | + // implicitly delete `taker` |
| 915 | + }, |
| 916 | + CallbackFlags::kUnrefed); |
| 917 | + |
| 918 | + // Now, the lambda is delivered to the main thread, as a result, the |
| 919 | + // WorkerHeapStatisticsTaker object is delivered to the main thread, too. |
| 920 | + }); |
| 921 | + |
| 922 | + if (scheduled) { |
| 923 | + args.GetReturnValue().Set(wrap); |
| 924 | + } else { |
| 925 | + args.GetReturnValue().Set(Local<Object>()); |
| 926 | + } |
| 927 | +} |
| 928 | + |
819 | 929 | void Worker::GetResourceLimits(const FunctionCallbackInfo<Value>& args) { |
820 | 930 | Worker* w; |
821 | 931 | ASSIGN_OR_RETURN_UNWRAP(&w, args.This()); |
@@ -996,6 +1106,7 @@ void CreateWorkerPerIsolateProperties(IsolateData* isolate_data, |
996 | 1106 | SetProtoMethod(isolate, w, "takeHeapSnapshot", Worker::TakeHeapSnapshot); |
997 | 1107 | SetProtoMethod(isolate, w, "loopIdleTime", Worker::LoopIdleTime); |
998 | 1108 | SetProtoMethod(isolate, w, "loopStartTime", Worker::LoopStartTime); |
| 1109 | + SetProtoMethod(isolate, w, "getHeapStatistics", Worker::GetHeapStatistics); |
999 | 1110 |
|
1000 | 1111 | SetConstructorFunction(isolate, target, "Worker", w); |
1001 | 1112 | } |
@@ -1014,6 +1125,20 @@ void CreateWorkerPerIsolateProperties(IsolateData* isolate_data, |
1014 | 1125 | wst->InstanceTemplate()); |
1015 | 1126 | } |
1016 | 1127 |
|
| 1128 | + { |
| 1129 | + Local<FunctionTemplate> wst = NewFunctionTemplate(isolate, nullptr); |
| 1130 | + |
| 1131 | + wst->InstanceTemplate()->SetInternalFieldCount( |
| 1132 | + WorkerHeapSnapshotTaker::kInternalFieldCount); |
| 1133 | + wst->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data)); |
| 1134 | + |
| 1135 | + Local<String> wst_string = |
| 1136 | + FIXED_ONE_BYTE_STRING(isolate, "WorkerHeapStatisticsTaker"); |
| 1137 | + wst->SetClassName(wst_string); |
| 1138 | + isolate_data->set_worker_heap_statistics_taker_template( |
| 1139 | + wst->InstanceTemplate()); |
| 1140 | + } |
| 1141 | + |
1017 | 1142 | SetMethod(isolate, target, "getEnvMessagePort", GetEnvMessagePort); |
1018 | 1143 | } |
1019 | 1144 |
|
@@ -1079,6 +1204,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
1079 | 1204 | registry->Register(Worker::TakeHeapSnapshot); |
1080 | 1205 | registry->Register(Worker::LoopIdleTime); |
1081 | 1206 | registry->Register(Worker::LoopStartTime); |
| 1207 | + registry->Register(Worker::GetHeapStatistics); |
1082 | 1208 | } |
1083 | 1209 |
|
1084 | 1210 | } // anonymous namespace |
|