Skip to content

Commit 54442ef

Browse files
jasnellMylesBorins
authored andcommitted
perf_hooks: refactor internals
Refactor and simplify the perf_hooks native internals. Backport-PR-URL: #20456 PR-URL: #17822 Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 7eba62e commit 54442ef

File tree

2 files changed

+150
-242
lines changed

2 files changed

+150
-242
lines changed

src/node_perf.cc

+110-148
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ using v8::Integer;
1717
using v8::Isolate;
1818
using v8::Local;
1919
using v8::Name;
20+
using v8::Number;
2021
using v8::Object;
21-
using v8::ObjectTemplate;
22-
using v8::Signature;
2322
using v8::String;
2423
using v8::Value;
2524

@@ -30,63 +29,107 @@ uint64_t performance_v8_start;
3029
uint64_t performance_last_gc_start_mark_ = 0;
3130
v8::GCType performance_last_gc_type_ = v8::GCType::kGCTypeAll;
3231

32+
// Initialize the performance entry object properties
33+
inline void InitObject(const PerformanceEntry& entry, Local<Object> obj) {
34+
Environment* env = entry.env();
35+
Isolate* isolate = env->isolate();
36+
Local<Context> context = env->context();
37+
v8::PropertyAttribute attr =
38+
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
39+
obj->DefineOwnProperty(context,
40+
env->name_string(),
41+
String::NewFromUtf8(isolate,
42+
entry.name().c_str(),
43+
String::kNormalString),
44+
attr).FromJust();
45+
obj->DefineOwnProperty(context,
46+
FIXED_ONE_BYTE_STRING(isolate, "entryType"),
47+
String::NewFromUtf8(isolate,
48+
entry.type().c_str(),
49+
String::kNormalString),
50+
attr).FromJust();
51+
obj->DefineOwnProperty(context,
52+
FIXED_ONE_BYTE_STRING(isolate, "startTime"),
53+
Number::New(isolate, entry.startTime()),
54+
attr).FromJust();
55+
obj->DefineOwnProperty(context,
56+
FIXED_ONE_BYTE_STRING(isolate, "duration"),
57+
Number::New(isolate, entry.duration()),
58+
attr).FromJust();
59+
}
60+
61+
// Create a new PerformanceEntry object
62+
const Local<Object> PerformanceEntry::ToObject() const {
63+
Local<Object> obj =
64+
env_->performance_entry_template()
65+
->NewInstance(env_->context()).ToLocalChecked();
66+
InitObject(*this, obj);
67+
return obj;
68+
}
69+
70+
// Allow creating a PerformanceEntry object from JavaScript
3371
void PerformanceEntry::New(const FunctionCallbackInfo<Value>& args) {
3472
Environment* env = Environment::GetCurrent(args);
3573
Isolate* isolate = env->isolate();
3674
Utf8Value name(isolate, args[0]);
3775
Utf8Value type(isolate, args[1]);
3876
uint64_t now = PERFORMANCE_NOW();
39-
new PerformanceEntry(env, args.This(), *name, *type, now, now);
77+
PerformanceEntry entry(env, *name, *type, now, now);
78+
Local<Object> obj = args.This();
79+
InitObject(entry, obj);
80+
PerformanceEntry::Notify(env, entry.kind(), obj);
4081
}
4182

42-
void PerformanceEntry::NotifyObservers(Environment* env,
43-
PerformanceEntry* entry) {
83+
// Pass the PerformanceEntry object to the PerformanceObservers
84+
inline void PerformanceEntry::Notify(Environment* env,
85+
PerformanceEntryType type,
86+
Local<Value> object) {
87+
Context::Scope scope(env->context());
4488
AliasedBuffer<uint32_t, v8::Uint32Array>& observers =
4589
env->performance_state()->observers;
46-
PerformanceEntryType type = ToPerformanceEntryTypeEnum(entry->type().c_str());
47-
if (type == NODE_PERFORMANCE_ENTRY_TYPE_INVALID ||
48-
!observers[type]) {
49-
return;
90+
if (observers != nullptr &&
91+
type != NODE_PERFORMANCE_ENTRY_TYPE_INVALID &&
92+
observers[type]) {
93+
node::MakeCallback(env->isolate(),
94+
env->process_object(),
95+
env->performance_entry_callback(),
96+
1, &object);
5097
}
51-
Local<Context> context = env->context();
52-
Isolate* isolate = env->isolate();
53-
Local<Value> argv = entry->object();
54-
env->performance_entry_callback()->Call(context,
55-
v8::Undefined(isolate),
56-
1, &argv).ToLocalChecked();
5798
}
5899

100+
// Create a User Timing Mark
59101
void Mark(const FunctionCallbackInfo<Value>& args) {
60102
Environment* env = Environment::GetCurrent(args);
61-
Local<Context> context = env->context();
62-
Isolate* isolate = env->isolate();
63-
Utf8Value name(isolate, args[0]);
103+
HandleScope scope(env->isolate());
104+
Utf8Value name(env->isolate(), args[0]);
64105
uint64_t now = PERFORMANCE_NOW();
65106
auto marks = env->performance_marks();
66107
(*marks)[*name] = now;
67108

68109
// TODO(jasnell): Once Tracing API is fully implemented, this should
69110
// record a trace event also.
70111

71-
Local<Function> fn = env->performance_entry_template();
72-
Local<Object> obj = fn->NewInstance(context).ToLocalChecked();
73-
new PerformanceEntry(env, obj, *name, "mark", now, now);
112+
PerformanceEntry entry(env, *name, "mark", now, now);
113+
Local<Object> obj = entry.ToObject();
114+
PerformanceEntry::Notify(env, entry.kind(), obj);
74115
args.GetReturnValue().Set(obj);
75116
}
76117

118+
77119
inline uint64_t GetPerformanceMark(Environment* env, std::string name) {
78120
auto marks = env->performance_marks();
79121
auto res = marks->find(name);
80122
return res != marks->end() ? res->second : 0;
81123
}
82124

125+
// Create a User Timing Measure. A Measure is a PerformanceEntry that
126+
// measures the duration between two distinct user timing marks
83127
void Measure(const FunctionCallbackInfo<Value>& args) {
84128
Environment* env = Environment::GetCurrent(args);
85-
Local<Context> context = env->context();
86-
Isolate* isolate = env->isolate();
87-
Utf8Value name(isolate, args[0]);
88-
Utf8Value startMark(isolate, args[1]);
89-
Utf8Value endMark(isolate, args[2]);
129+
HandleScope scope(env->isolate());
130+
Utf8Value name(env->isolate(), args[0]);
131+
Utf8Value startMark(env->isolate(), args[1]);
132+
Utf8Value endMark(env->isolate(), args[2]);
90133

91134
AliasedBuffer<double, v8::Float64Array>& milestones =
92135
env->performance_state()->milestones;
@@ -114,41 +157,13 @@ void Measure(const FunctionCallbackInfo<Value>& args) {
114157
// TODO(jasnell): Once Tracing API is fully implemented, this should
115158
// record a trace event also.
116159

117-
Local<Function> fn = env->performance_entry_template();
118-
Local<Object> obj = fn->NewInstance(context).ToLocalChecked();
119-
new PerformanceEntry(env, obj, *name, "measure",
120-
startTimestamp, endTimestamp);
160+
PerformanceEntry entry(env, *name, "measure", startTimestamp, endTimestamp);
161+
Local<Object> obj = entry.ToObject();
162+
PerformanceEntry::Notify(env, entry.kind(), obj);
121163
args.GetReturnValue().Set(obj);
122164
}
123165

124-
void GetPerformanceEntryName(const FunctionCallbackInfo<Value>& info) {
125-
Isolate* isolate = info.GetIsolate();
126-
PerformanceEntry* entry;
127-
ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder());
128-
info.GetReturnValue().Set(
129-
String::NewFromUtf8(isolate, entry->name().c_str(), String::kNormalString));
130-
}
131-
132-
void GetPerformanceEntryType(const FunctionCallbackInfo<Value>& info) {
133-
Isolate* isolate = info.GetIsolate();
134-
PerformanceEntry* entry;
135-
ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder());
136-
info.GetReturnValue().Set(
137-
String::NewFromUtf8(isolate, entry->type().c_str(), String::kNormalString));
138-
}
139-
140-
void GetPerformanceEntryStartTime(const FunctionCallbackInfo<Value>& info) {
141-
PerformanceEntry* entry;
142-
ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder());
143-
info.GetReturnValue().Set(entry->startTime());
144-
}
145-
146-
void GetPerformanceEntryDuration(const FunctionCallbackInfo<Value>& info) {
147-
PerformanceEntry* entry;
148-
ASSIGN_OR_RETURN_UNWRAP(&entry, info.Holder());
149-
info.GetReturnValue().Set(entry->duration());
150-
}
151-
166+
// Allows specific Node.js lifecycle milestones to be set from JavaScript
152167
void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
153168
Environment* env = Environment::GetCurrent(args);
154169
Local<Context> context = env->context();
@@ -162,74 +177,64 @@ void MarkMilestone(const FunctionCallbackInfo<Value>& args) {
162177
}
163178
}
164179

180+
165181
void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
166182
Environment* env = Environment::GetCurrent(args);
167183
CHECK(args[0]->IsFunction());
168184
env->set_performance_entry_callback(args[0].As<Function>());
169185
}
170186

171-
void PerformanceGCCallback(uv_async_t* handle) {
172-
PerformanceEntry::Data* data =
173-
static_cast<PerformanceEntry::Data*>(handle->data);
174-
Environment* env = data->env();
175-
Isolate* isolate = env->isolate();
176-
HandleScope scope(isolate);
187+
// Creates a GC Performance Entry and passes it to observers
188+
void PerformanceGCCallback(Environment* env, void* ptr) {
189+
GCPerformanceEntry* entry = static_cast<GCPerformanceEntry*>(ptr);
190+
HandleScope scope(env->isolate());
177191
Local<Context> context = env->context();
178-
Context::Scope context_scope(context);
179-
Local<Function> fn;
180-
Local<Object> obj;
181-
PerformanceGCKind kind = static_cast<PerformanceGCKind>(data->data());
182192

183193
AliasedBuffer<uint32_t, v8::Uint32Array>& observers =
184194
env->performance_state()->observers;
185-
if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) {
186-
goto cleanup;
195+
if (observers[NODE_PERFORMANCE_ENTRY_TYPE_GC]) {
196+
Local<Object> obj = entry->ToObject();
197+
v8::PropertyAttribute attr =
198+
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
199+
obj->DefineOwnProperty(context,
200+
FIXED_ONE_BYTE_STRING(env->isolate(), "kind"),
201+
Integer::New(env->isolate(), entry->gckind()),
202+
attr).FromJust();
203+
PerformanceEntry::Notify(env, entry->kind(), obj);
187204
}
188205

189-
fn = env->performance_entry_template();
190-
obj = fn->NewInstance(context).ToLocalChecked();
191-
obj->Set(context,
192-
FIXED_ONE_BYTE_STRING(isolate, "kind"),
193-
Integer::New(isolate, kind)).FromJust();
194-
new PerformanceEntry(env, obj, data);
195-
196-
cleanup:
197-
delete data;
198-
auto closeCB = [](uv_handle_t* handle) {
199-
delete reinterpret_cast<uv_async_t*>(handle);
200-
};
201-
uv_close(reinterpret_cast<uv_handle_t*>(handle), closeCB);
206+
delete entry;
202207
}
203208

209+
// Marks the start of a GC cycle
204210
void MarkGarbageCollectionStart(Isolate* isolate,
205211
v8::GCType type,
206212
v8::GCCallbackFlags flags) {
207213
performance_last_gc_start_mark_ = PERFORMANCE_NOW();
208214
performance_last_gc_type_ = type;
209215
}
210216

217+
// Marks the end of a GC cycle
211218
void MarkGarbageCollectionEnd(Isolate* isolate,
212219
v8::GCType type,
213220
v8::GCCallbackFlags flags,
214221
void* data) {
215222
Environment* env = static_cast<Environment*>(data);
216-
uv_async_t* async = new uv_async_t(); // coverity[leaked_storage]
217-
if (uv_async_init(env->event_loop(), async, PerformanceGCCallback))
218-
return delete async;
219-
uv_unref(reinterpret_cast<uv_handle_t*>(async));
220-
async->data =
221-
new PerformanceEntry::Data(env, "gc", "gc",
222-
performance_last_gc_start_mark_,
223-
PERFORMANCE_NOW(), type);
224-
CHECK_EQ(0, uv_async_send(async));
223+
env->SetImmediate(PerformanceGCCallback,
224+
new GCPerformanceEntry(env,
225+
static_cast<PerformanceGCKind>(type),
226+
performance_last_gc_start_mark_,
227+
PERFORMANCE_NOW()));
225228
}
226229

230+
227231
inline void SetupGarbageCollectionTracking(Environment* env) {
228232
env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart);
229233
env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd,
230234
static_cast<void*>(env));
231235
}
232236

237+
// Gets the name of a function
233238
inline Local<Value> GetName(Local<Function> fn) {
234239
Local<Value> val = fn->GetDebugName();
235240
if (val.IsEmpty() || val->IsUndefined()) {
@@ -241,6 +246,9 @@ inline Local<Value> GetName(Local<Function> fn) {
241246
return val;
242247
}
243248

249+
// Executes a wrapped Function and captures timing information, causing a
250+
// Function PerformanceEntry to be emitted to PerformanceObservers after
251+
// execution.
244252
void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
245253
Isolate* isolate = args.GetIsolate();
246254
HandleScope scope(isolate);
@@ -250,9 +258,8 @@ void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
250258
size_t count = args.Length();
251259
size_t idx;
252260
std::vector<Local<Value>> call_args;
253-
for (size_t i = 0; i < count; ++i) {
261+
for (size_t i = 0; i < count; ++i)
254262
call_args.push_back(args[i]);
255-
}
256263

257264
Utf8Value name(isolate, GetName(fn));
258265

@@ -289,15 +296,14 @@ void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) {
289296
if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION])
290297
return;
291298

292-
Local<Function> ctor = env->performance_entry_template();
293-
v8::MaybeLocal<Object> instance = ctor->NewInstance(context);
294-
Local<Object> obj = instance.ToLocalChecked();
295-
for (idx = 0; idx < count; idx++) {
296-
obj->Set(context, idx, args[idx]).ToChecked();
297-
}
298-
new PerformanceEntry(env, obj, *name, "function", start, end);
299+
PerformanceEntry entry(env, *name, "function", start, end);
300+
Local<Object> obj = entry.ToObject();
301+
for (idx = 0; idx < count; idx++)
302+
obj->Set(context, idx, args[idx]).FromJust();
303+
PerformanceEntry::Notify(env, entry.kind(), obj);
299304
}
300305

306+
// Wraps a Function in a TimerFunctionCall
301307
void Timerify(const FunctionCallbackInfo<Value>& args) {
302308
Environment* env = Environment::GetCurrent(args);
303309
Local<Context> context = env->context();
@@ -310,6 +316,7 @@ void Timerify(const FunctionCallbackInfo<Value>& args) {
310316
args.GetReturnValue().Set(wrap);
311317
}
312318

319+
313320
void Init(Local<Object> target,
314321
Local<Value> unused,
315322
Local<Context> context) {
@@ -328,55 +335,10 @@ void Init(Local<Object> target,
328335
Local<String> performanceEntryString =
329336
FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry");
330337

331-
Local<FunctionTemplate> pe = env->NewFunctionTemplate(PerformanceEntry::New);
332-
pe->InstanceTemplate()->SetInternalFieldCount(1);
338+
Local<FunctionTemplate> pe = FunctionTemplate::New(isolate);
333339
pe->SetClassName(performanceEntryString);
334-
335-
Local<Signature> signature = Signature::New(env->isolate(), pe);
336-
337-
Local<FunctionTemplate> get_performance_entry_name_templ =
338-
FunctionTemplate::New(env->isolate(),
339-
GetPerformanceEntryName,
340-
env->as_external(),
341-
signature);
342-
343-
Local<FunctionTemplate> get_performance_entry_type_templ =
344-
FunctionTemplate::New(env->isolate(),
345-
GetPerformanceEntryType,
346-
env->as_external(),
347-
signature);
348-
349-
Local<FunctionTemplate> get_performance_entry_start_time_templ =
350-
FunctionTemplate::New(env->isolate(),
351-
GetPerformanceEntryStartTime,
352-
env->as_external(),
353-
signature);
354-
355-
Local<FunctionTemplate> get_performance_entry_duration_templ =
356-
FunctionTemplate::New(env->isolate(),
357-
GetPerformanceEntryDuration,
358-
env->as_external(),
359-
signature);
360-
361-
Local<ObjectTemplate> ot = pe->InstanceTemplate();
362-
ot->SetAccessorProperty(env->name_string(),
363-
get_performance_entry_name_templ,
364-
Local<FunctionTemplate>());
365-
366-
ot->SetAccessorProperty(FIXED_ONE_BYTE_STRING(isolate, "entryType"),
367-
get_performance_entry_type_templ,
368-
Local<FunctionTemplate>());
369-
370-
ot->SetAccessorProperty(FIXED_ONE_BYTE_STRING(isolate, "startTime"),
371-
get_performance_entry_start_time_templ,
372-
Local<FunctionTemplate>());
373-
374-
ot->SetAccessorProperty(FIXED_ONE_BYTE_STRING(isolate, "duration"),
375-
get_performance_entry_duration_templ,
376-
Local<FunctionTemplate>());
377-
378340
Local<Function> fn = pe->GetFunction();
379-
target->Set(performanceEntryString, fn);
341+
target->Set(context, performanceEntryString, fn).FromJust();
380342
env->set_performance_entry_template(fn);
381343

382344
env->SetMethod(target, "mark", Mark);

0 commit comments

Comments
 (0)