worker: add heap profile API · nodejs/node@49747a5

@@ -20,6 +20,7 @@

20202121

using node::kAllowedInEnvvar;

2222

using node::kDisallowedInEnvvar;

23+

using v8::AllocationProfile;

2324

using v8::Array;

2425

using v8::ArrayBuffer;

2526

using v8::Boolean;

@@ -32,6 +33,7 @@ using v8::Float64Array;

3233

using v8::FunctionCallbackInfo;

3334

using v8::FunctionTemplate;

3435

using v8::HandleScope;

36+

using v8::HeapProfiler;

3537

using v8::HeapStatistics;

3638

using v8::Integer;

3739

using 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+

}

10341199

class WorkerHeapStatisticsTaker : public AsyncWrap {

10351200

public:

10361201

WorkerHeapStatisticsTaker(Environment* env, Local<Object> obj)

@@ -1328,6 +1493,8 @@ void CreateWorkerPerIsolateProperties(IsolateData* isolate_data,

13281493

SetProtoMethod(isolate, w, "cpuUsage", Worker::CpuUsage);

13291494

SetProtoMethod(isolate, w, "startCpuProfile", Worker::StartCpuProfile);

13301495

SetProtoMethod(isolate, w, "stopCpuProfile", Worker::StopCpuProfile);

1496+

SetProtoMethod(isolate, w, "startHeapProfile", Worker::StartHeapProfile);

1497+

SetProtoMethod(isolate, w, "stopHeapProfile", Worker::StopHeapProfile);

1331149813321499

SetConstructorFunction(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+13901571

SetMethod(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