worker: add heap profile API · nodejs/node@04013ee
@@ -20,6 +20,7 @@
20202121using node::kAllowedInEnvvar;
2222using node::kDisallowedInEnvvar;
23+using v8::AllocationProfile;
2324using v8::Array;
2425using v8::ArrayBuffer;
2526using v8::Boolean;
@@ -32,6 +33,7 @@ using v8::Float64Array;
3233using v8::FunctionCallbackInfo;
3334using v8::FunctionTemplate;
3435using v8::HandleScope;
36+using v8::HeapProfiler;
3537using v8::HeapStatistics;
3638using v8::Integer;
3739using v8::Isolate;
@@ -1031,6 +1033,169 @@ void Worker::StopCpuProfile(const FunctionCallbackInfo<Value>& args) {
10311033 }
10321034}
103310351036+class WorkerHeapProfileTaker final : public AsyncWrap {
1037+public:
1038+WorkerHeapProfileTaker(Environment* env, Local<Object> obj)
1039+ : AsyncWrap(env, obj, AsyncWrap::PROVIDER_WORKERHEAPPROFILE) {}
1040+1041+SET_NO_MEMORY_INFO()
1042+SET_MEMORY_INFO_NAME(WorkerHeapProfileTaker)
1043+SET_SELF_SIZE(WorkerHeapProfileTaker)
1044+};
1045+1046+void Worker::StartHeapProfile(const FunctionCallbackInfo<Value>& args) {
1047+ Worker* w;
1048+ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
1049+ Environment* env = w->env();
1050+1051+ AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w);
1052+ Local<Object> wrap;
1053+if (!env->worker_heap_profile_taker_template()
1054+ ->NewInstance(env->context())
1055+ .ToLocal(&wrap)) {
1056+return;
1057+ }
1058+1059+ BaseObjectPtr<WorkerHeapProfileTaker> taker =
1060+ MakeDetachedBaseObject<WorkerHeapProfileTaker>(env, wrap);
1061+1062+bool scheduled = w->RequestInterrupt([taker = std::move(taker),
1063+ env](Environment* worker_env) mutable {
1064+ v8::HeapProfiler* profiler = worker_env->isolate()->GetHeapProfiler();
1065+bool success = profiler->StartSamplingHeapProfiler();
1066+ env->SetImmediateThreadsafe(
1067+ [taker = std::move(taker),
1068+ success = success](Environment* env) mutable {
1069+ Isolate* isolate = env->isolate();
1070+ HandleScope handle_scope(isolate);
1071+ Context::Scope context_scope(env->context());
1072+ AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker.get());
1073+ Local<Value> argv[] = {
1074+Null(isolate), // error
1075+ };
1076+if (!success) {
1077+ argv[0] = ERR_HEAP_PROFILE_HAVE_BEEN_STARTED(
1078+ isolate, "heap profiler have been started");
1079+ }
1080+ taker->MakeCallback(env->ondone_string(), arraysize(argv), argv);
1081+ },
1082+ CallbackFlags::kUnrefed);
1083+ });
1084+1085+if (scheduled) {
1086+ args.GetReturnValue().Set(wrap);
1087+ }
1088+}
1089+1090+static void buildHeapProfileNode(Isolate* isolate,
1091+const AllocationProfile::Node* node,
1092+ JSONWriter* writer) {
1093+size_t selfSize = 0;
1094+for (const auto& allocation : node->allocations)
1095+ selfSize += allocation.size * allocation.count;
1096+1097+ writer->json_keyvalue("selfSize", selfSize);
1098+ writer->json_keyvalue("id", node->node_id);
1099+ writer->json_objectstart("callFrame");
1100+ writer->json_keyvalue("scriptId", node->script_id);
1101+ writer->json_keyvalue("lineNumber", node->line_number - 1);
1102+ writer->json_keyvalue("columnNumber", node->column_number - 1);
1103+ node::Utf8Value name(isolate, node->name);
1104+ node::Utf8Value script_name(isolate, node->script_name);
1105+ writer->json_keyvalue("functionName", *name);
1106+ writer->json_keyvalue("url", *script_name);
1107+ writer->json_objectend();
1108+1109+ writer->json_arraystart("children");
1110+for (const auto* child : node->children) {
1111+ writer->json_start();
1112+buildHeapProfileNode(isolate, child, writer);
1113+ writer->json_end();
1114+ }
1115+ writer->json_arrayend();
1116+}
1117+1118+static bool serializeProfile(Isolate* isolate, std::ostringstream& out_stream) {
1119+ HandleScope scope(isolate);
1120+ HeapProfiler* profiler = isolate->GetHeapProfiler();
1121+ std::unique_ptr<AllocationProfile> profile(profiler->GetAllocationProfile());
1122+if (!profile) {
1123+return false;
1124+ }
1125+ JSONWriter writer(out_stream, false);
1126+ writer.json_start();
1127+1128+ writer.json_arraystart("samples");
1129+for (const auto& sample : profile->GetSamples()) {
1130+ writer.json_start();
1131+ writer.json_keyvalue("size", sample.size * sample.count);
1132+ writer.json_keyvalue("nodeId", sample.node_id);
1133+ writer.json_keyvalue("ordinal", static_cast<double>(sample.sample_id));
1134+ writer.json_end();
1135+ }
1136+ writer.json_arrayend();
1137+1138+ writer.json_objectstart("head");
1139+buildHeapProfileNode(isolate, profile->GetRootNode(), &writer);
1140+ writer.json_objectend();
1141+1142+ writer.json_end();
1143+ profiler->StopSamplingHeapProfiler();
1144+return true;
1145+}
1146+1147+void Worker::StopHeapProfile(const FunctionCallbackInfo<Value>& args) {
1148+ Worker* w;
1149+ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
1150+1151+ Environment* env = w->env();
1152+ AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w);
1153+ Local<Object> wrap;
1154+if (!env->worker_heap_profile_taker_template()
1155+ ->NewInstance(env->context())
1156+ .ToLocal(&wrap)) {
1157+return;
1158+ }
1159+1160+ BaseObjectPtr<WorkerHeapProfileTaker> taker =
1161+ MakeDetachedBaseObject<WorkerHeapProfileTaker>(env, wrap);
1162+1163+bool scheduled = w->RequestInterrupt([taker = std::move(taker),
1164+ env](Environment* worker_env) mutable {
1165+ std::ostringstream out_stream;
1166+bool success = serializeProfile(worker_env->isolate(), out_stream);
1167+ env->SetImmediateThreadsafe(
1168+ [taker = std::move(taker),
1169+ out_stream = std::move(out_stream),
1170+ success = success](Environment* env) mutable {
1171+ Isolate* isolate = env->isolate();
1172+ HandleScope handle_scope(isolate);
1173+ Context::Scope context_scope(env->context());
1174+ AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker.get());
1175+ Local<Value> argv[] = {
1176+Null(isolate), // error
1177+Undefined(isolate), // profile
1178+ };
1179+if (success) {
1180+ Local<Value> result;
1181+if (!ToV8Value(env->context(), out_stream.str(), isolate)
1182+ .ToLocal(&result)) {
1183+return;
1184+ }
1185+ argv[1] = result;
1186+ } else {
1187+ argv[0] = ERR_HEAP_PROFILE_NOT_STARTED(isolate,
1188+"heap profile not started");
1189+ }
1190+ taker->MakeCallback(env->ondone_string(), arraysize(argv), argv);
1191+ },
1192+ CallbackFlags::kUnrefed);
1193+ });
1194+1195+if (scheduled) {
1196+ args.GetReturnValue().Set(wrap);
1197+ }
1198+}
10341199class WorkerHeapStatisticsTaker : public AsyncWrap {
10351200public:
10361201WorkerHeapStatisticsTaker(Environment* env, Local<Object> obj)
@@ -1328,6 +1493,8 @@ void CreateWorkerPerIsolateProperties(IsolateData* isolate_data,
13281493SetProtoMethod(isolate, w, "cpuUsage", Worker::CpuUsage);
13291494SetProtoMethod(isolate, w, "startCpuProfile", Worker::StartCpuProfile);
13301495SetProtoMethod(isolate, w, "stopCpuProfile", Worker::StopCpuProfile);
1496+SetProtoMethod(isolate, w, "startHeapProfile", Worker::StartHeapProfile);
1497+SetProtoMethod(isolate, w, "stopHeapProfile", Worker::StopHeapProfile);
1331149813321499SetConstructorFunction(isolate, target, "Worker", w);
13331500 }
@@ -1387,6 +1554,20 @@ void CreateWorkerPerIsolateProperties(IsolateData* isolate_data,
13871554 wst->InstanceTemplate());
13881555 }
138915561557+ {
1558+ Local<FunctionTemplate> wst = NewFunctionTemplate(isolate, nullptr);
1559+1560+ wst->InstanceTemplate()->SetInternalFieldCount(
1561+ WorkerHeapProfileTaker::kInternalFieldCount);
1562+ wst->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
1563+1564+ Local<String> wst_string =
1565+FIXED_ONE_BYTE_STRING(isolate, "WorkerHeapProfileTaker");
1566+ wst->SetClassName(wst_string);
1567+ isolate_data->set_worker_heap_profile_taker_template(
1568+ wst->InstanceTemplate());
1569+ }
1570+13901571SetMethod(isolate, target, "getEnvMessagePort", GetEnvMessagePort);
13911572}
13921573@@ -1466,6 +1647,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
14661647 registry->Register(Worker::CpuUsage);
14671648 registry->Register(Worker::StartCpuProfile);
14681649 registry->Register(Worker::StopCpuProfile);
1650+ registry->Register(Worker::StartHeapProfile);
1651+ registry->Register(Worker::StopHeapProfile);
14691652}
1470165314711654} // anonymous namespace