@@ -17,9 +17,8 @@ using v8::Integer; |
17 | 17 | using v8::Isolate; |
18 | 18 | using v8::Local; |
19 | 19 | using v8::Name; |
| 20 | +using v8::Number; |
20 | 21 | using v8::Object; |
21 | | -using v8::ObjectTemplate; |
22 | | -using v8::Signature; |
23 | 22 | using v8::String; |
24 | 23 | using v8::Value; |
25 | 24 |
|
@@ -30,63 +29,107 @@ uint64_t performance_v8_start; |
30 | 29 | uint64_t performance_last_gc_start_mark_ = 0; |
31 | 30 | v8::GCType performance_last_gc_type_ = v8::GCType::kGCTypeAll; |
32 | 31 |
|
| 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 |
33 | 71 | void PerformanceEntry::New(const FunctionCallbackInfo<Value>& args) { |
34 | 72 | Environment* env = Environment::GetCurrent(args); |
35 | 73 | Isolate* isolate = env->isolate(); |
36 | 74 | Utf8Value name(isolate, args[0]); |
37 | 75 | Utf8Value type(isolate, args[1]); |
38 | 76 | 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); |
40 | 81 | } |
41 | 82 |
|
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()); |
44 | 88 | AliasedBuffer<uint32_t, v8::Uint32Array>& observers = |
45 | 89 | 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); |
50 | 97 | } |
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(); |
57 | 98 | } |
58 | 99 |
|
| 100 | +// Create a User Timing Mark |
59 | 101 | void Mark(const FunctionCallbackInfo<Value>& args) { |
60 | 102 | 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]); |
64 | 105 | uint64_t now = PERFORMANCE_NOW(); |
65 | 106 | auto marks = env->performance_marks(); |
66 | 107 | (*marks)[*name] = now; |
67 | 108 |
|
68 | 109 | // TODO(jasnell): Once Tracing API is fully implemented, this should |
69 | 110 | // record a trace event also. |
70 | 111 |
|
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); |
74 | 115 | args.GetReturnValue().Set(obj); |
75 | 116 | } |
76 | 117 |
|
| 118 | + |
77 | 119 | inline uint64_t GetPerformanceMark(Environment* env, std::string name) { |
78 | 120 | auto marks = env->performance_marks(); |
79 | 121 | auto res = marks->find(name); |
80 | 122 | return res != marks->end() ? res->second : 0; |
81 | 123 | } |
82 | 124 |
|
| 125 | +// Create a User Timing Measure. A Measure is a PerformanceEntry that |
| 126 | +// measures the duration between two distinct user timing marks |
83 | 127 | void Measure(const FunctionCallbackInfo<Value>& args) { |
84 | 128 | 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]); |
90 | 133 |
|
91 | 134 | AliasedBuffer<double, v8::Float64Array>& milestones = |
92 | 135 | env->performance_state()->milestones; |
@@ -114,41 +157,13 @@ void Measure(const FunctionCallbackInfo<Value>& args) { |
114 | 157 | // TODO(jasnell): Once Tracing API is fully implemented, this should |
115 | 158 | // record a trace event also. |
116 | 159 |
|
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); |
121 | 163 | args.GetReturnValue().Set(obj); |
122 | 164 | } |
123 | 165 |
|
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 |
152 | 167 | void MarkMilestone(const FunctionCallbackInfo<Value>& args) { |
153 | 168 | Environment* env = Environment::GetCurrent(args); |
154 | 169 | Local<Context> context = env->context(); |
@@ -162,74 +177,64 @@ void MarkMilestone(const FunctionCallbackInfo<Value>& args) { |
162 | 177 | } |
163 | 178 | } |
164 | 179 |
|
| 180 | + |
165 | 181 | void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) { |
166 | 182 | Environment* env = Environment::GetCurrent(args); |
167 | 183 | CHECK(args[0]->IsFunction()); |
168 | 184 | env->set_performance_entry_callback(args[0].As<Function>()); |
169 | 185 | } |
170 | 186 |
|
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()); |
177 | 191 | 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()); |
182 | 192 |
|
183 | 193 | AliasedBuffer<uint32_t, v8::Uint32Array>& observers = |
184 | 194 | 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); |
187 | 204 | } |
188 | 205 |
|
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; |
202 | 207 | } |
203 | 208 |
|
| 209 | +// Marks the start of a GC cycle |
204 | 210 | void MarkGarbageCollectionStart(Isolate* isolate, |
205 | 211 | v8::GCType type, |
206 | 212 | v8::GCCallbackFlags flags) { |
207 | 213 | performance_last_gc_start_mark_ = PERFORMANCE_NOW(); |
208 | 214 | performance_last_gc_type_ = type; |
209 | 215 | } |
210 | 216 |
|
| 217 | +// Marks the end of a GC cycle |
211 | 218 | void MarkGarbageCollectionEnd(Isolate* isolate, |
212 | 219 | v8::GCType type, |
213 | 220 | v8::GCCallbackFlags flags, |
214 | 221 | void* data) { |
215 | 222 | 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())); |
225 | 228 | } |
226 | 229 |
|
| 230 | + |
227 | 231 | inline void SetupGarbageCollectionTracking(Environment* env) { |
228 | 232 | env->isolate()->AddGCPrologueCallback(MarkGarbageCollectionStart); |
229 | 233 | env->isolate()->AddGCEpilogueCallback(MarkGarbageCollectionEnd, |
230 | 234 | static_cast<void*>(env)); |
231 | 235 | } |
232 | 236 |
|
| 237 | +// Gets the name of a function |
233 | 238 | inline Local<Value> GetName(Local<Function> fn) { |
234 | 239 | Local<Value> val = fn->GetDebugName(); |
235 | 240 | if (val.IsEmpty() || val->IsUndefined()) { |
@@ -241,6 +246,9 @@ inline Local<Value> GetName(Local<Function> fn) { |
241 | 246 | return val; |
242 | 247 | } |
243 | 248 |
|
| 249 | +// Executes a wrapped Function and captures timing information, causing a |
| 250 | +// Function PerformanceEntry to be emitted to PerformanceObservers after |
| 251 | +// execution. |
244 | 252 | void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) { |
245 | 253 | Isolate* isolate = args.GetIsolate(); |
246 | 254 | HandleScope scope(isolate); |
@@ -250,9 +258,8 @@ void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) { |
250 | 258 | size_t count = args.Length(); |
251 | 259 | size_t idx; |
252 | 260 | std::vector<Local<Value>> call_args; |
253 | | - for (size_t i = 0; i < count; ++i) { |
| 261 | + for (size_t i = 0; i < count; ++i) |
254 | 262 | call_args.push_back(args[i]); |
255 | | - } |
256 | 263 |
|
257 | 264 | Utf8Value name(isolate, GetName(fn)); |
258 | 265 |
|
@@ -289,15 +296,14 @@ void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) { |
289 | 296 | if (!observers[NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION]) |
290 | 297 | return; |
291 | 298 |
|
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); |
299 | 304 | } |
300 | 305 |
|
| 306 | +// Wraps a Function in a TimerFunctionCall |
301 | 307 | void Timerify(const FunctionCallbackInfo<Value>& args) { |
302 | 308 | Environment* env = Environment::GetCurrent(args); |
303 | 309 | Local<Context> context = env->context(); |
@@ -310,6 +316,7 @@ void Timerify(const FunctionCallbackInfo<Value>& args) { |
310 | 316 | args.GetReturnValue().Set(wrap); |
311 | 317 | } |
312 | 318 |
|
| 319 | + |
313 | 320 | void Init(Local<Object> target, |
314 | 321 | Local<Value> unused, |
315 | 322 | Local<Context> context) { |
@@ -328,55 +335,10 @@ void Init(Local<Object> target, |
328 | 335 | Local<String> performanceEntryString = |
329 | 336 | FIXED_ONE_BYTE_STRING(isolate, "PerformanceEntry"); |
330 | 337 |
|
331 | | - Local<FunctionTemplate> pe = env->NewFunctionTemplate(PerformanceEntry::New); |
332 | | - pe->InstanceTemplate()->SetInternalFieldCount(1); |
| 338 | + Local<FunctionTemplate> pe = FunctionTemplate::New(isolate); |
333 | 339 | 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 | | - |
378 | 340 | Local<Function> fn = pe->GetFunction(); |
379 | | - target->Set(performanceEntryString, fn); |
| 341 | + target->Set(context, performanceEntryString, fn).FromJust(); |
380 | 342 | env->set_performance_entry_template(fn); |
381 | 343 |
|
382 | 344 | env->SetMethod(target, "mark", Mark); |
|