diff options
-rw-r--r-- | doc/api/perf_hooks.md | 4 | ||||
-rw-r--r-- | doc/api/tracing.md | 18 | ||||
-rw-r--r-- | src/node_perf.cc | 20 | ||||
-rw-r--r-- | src/node_perf.h | 1 | ||||
-rw-r--r-- | src/tracing/trace_event.h | 17 | ||||
-rw-r--r-- | test/parallel/test-trace-events-perf.js | 82 |
6 files changed, 131 insertions, 11 deletions
diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 5bef91eafb..4e31a24785 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -268,8 +268,8 @@ added: v8.5.0 * {string} -The type of the performance entry. Current it may be one of: `'node'`, `'mark'`, -`'measure'`, `'gc'`, or `'function'`. +The type of the performance entry. Currently it may be one of: `'node'`, +`'mark'`, `'measure'`, `'gc'`, or `'function'`. ### performanceEntry.kind <!-- YAML diff --git a/doc/api/tracing.md b/doc/api/tracing.md index 67b3394d55..3a534177a3 100644 --- a/doc/api/tracing.md +++ b/doc/api/tracing.md @@ -10,8 +10,20 @@ starting a Node.js application. The set of categories for which traces are recorded can be specified using the `--trace-event-categories` flag followed by a list of comma separated category -names. By default the `node`, `node.async_hooks`, and `v8` categories are -enabled. +names. + +The available categories are: + +* `node` +* `node.async_hooks` - Enables capture of detailed async_hooks trace data. +* `node.perf` - Enables capture of [Performance API] measurements. + * `node.perf.usertiming` - Enables capture of only Performance API User Timing + measures and marks. + * `node.perf.timerify` - Enables capture of only Performance API timerify + measurements. +* `v8` + +By default the `node`, `node.async_hooks`, and `v8` categories are enabled. ```txt node --trace-events-enabled --trace-event-categories v8,node,node.async_hooks server.js @@ -24,3 +36,5 @@ tab of Chrome. Starting with Node 10.0.0, the tracing system uses the same time source as the one used by `process.hrtime()` however the trace-event timestamps are expressed in microseconds, unlike `process.hrtime()` which returns nanoseconds. + +[Performance API]: perf_hooks.html diff --git a/src/node_perf.cc b/src/node_perf.cc index a4297c21f0..db4eff5353 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -105,8 +105,8 @@ void Mark(const FunctionCallbackInfo<Value>& args) { auto marks = env->performance_marks(); (*marks)[*name] = now; - // TODO(jasnell): Once Tracing API is fully implemented, this should - // record a trace event also. + TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP( + "node.perf,node.perf.usertiming", *name, now / 1000); PerformanceEntry entry(env, *name, "mark", now, now); Local<Object> obj = entry.ToObject(); @@ -153,8 +153,10 @@ void Measure(const FunctionCallbackInfo<Value>& args) { if (endTimestamp < startTimestamp) endTimestamp = startTimestamp; - // TODO(jasnell): Once Tracing API is fully implemented, this should - // record a trace event also. + TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( + "node.perf,node.perf.usertiming", *name, *name, startTimestamp / 1000); + TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( + "node.perf,node.perf.usertiming", *name, *name, endTimestamp / 1000); PerformanceEntry entry(env, *name, "measure", startTimestamp, endTimestamp); Local<Object> obj = entry.ToObject(); @@ -269,10 +271,15 @@ void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) { v8::TryCatch try_catch(isolate); if (args.IsConstructCall()) { start = PERFORMANCE_NOW(); + TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( + "node.perf,node.perf.timerify", *name, *name, start / 1000); v8::MaybeLocal<Object> ret = fn->NewInstance(context, call_args.size(), call_args.data()); end = PERFORMANCE_NOW(); + TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( + "node.perf,node.perf.timerify", *name, *name, end / 1000); + if (ret.IsEmpty()) { try_catch.ReThrow(); return; @@ -280,11 +287,16 @@ void TimerFunctionCall(const FunctionCallbackInfo<Value>& args) { args.GetReturnValue().Set(ret.ToLocalChecked()); } else { start = PERFORMANCE_NOW(); + TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( + "node.perf,node.perf.timerify", *name, *name, start / 1000); v8::MaybeLocal<Value> ret = fn->Call(context, args.This(), call_args.size(), call_args.data()); end = PERFORMANCE_NOW(); + TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( + "node.perf,node.perf.timerify", *name, *name, end / 1000); + if (ret.IsEmpty()) { try_catch.ReThrow(); return; diff --git a/src/node_perf.h b/src/node_perf.h index f1b182b4e3..82e3f46eb8 100644 --- a/src/node_perf.h +++ b/src/node_perf.h @@ -4,6 +4,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "node.h" +#include "node_internals.h" #include "node_perf_common.h" #include "env.h" #include "base_object-inl.h" diff --git a/src/tracing/trace_event.h b/src/tracing/trace_event.h index 4aac9e5543..3fbe1b0a1f 100644 --- a/src/tracing/trace_event.h +++ b/src/tracing/trace_event.h @@ -244,9 +244,20 @@ enum CategoryGroupEnabledFlags { // Adds a trace event with a given id, thread_id, and timestamp. Not // Implemented. -#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ - phase, category_group, name, id, thread_id, timestamp, flags, ...) \ - UNIMPLEMENTED() +#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + phase, category_group, name, id, thread_id, timestamp, flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ + unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \ + node::tracing::TraceID trace_event_trace_id(id, \ + &trace_event_flags); \ + node::tracing::AddTraceEventWithTimestamp( \ + phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ + trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \ + node::tracing::kNoId, trace_event_flags, timestamp, ##__VA_ARGS__);\ + } \ + } while (0) // Enter and leave a context based on the current scope. #define INTERNAL_TRACE_EVENT_SCOPED_CONTEXT(category_group, name, context) \ diff --git a/test/parallel/test-trace-events-perf.js b/test/parallel/test-trace-events-perf.js new file mode 100644 index 0000000000..e73579f87e --- /dev/null +++ b/test/parallel/test-trace-events-perf.js @@ -0,0 +1,82 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +if (process.argv[2] === 'child') { + const { performance } = require('perf_hooks'); + + // Will emit mark and measure trace events + performance.mark('A'); + setTimeout(() => { + performance.mark('B'); + performance.measure('A to B', 'A', 'B'); + }, 1); + + // Intentional non-op, part of the test + function f() {} + const ff = performance.timerify(f); + ff(); // Will emit a timerify trace event +} else { + tmpdir.refresh(); + process.chdir(tmpdir.path); + + const expectedMarks = ['A', 'B']; + const expectedBegins = [ + { cat: 'node.perf,node.perf.timerify', name: 'f' }, + { cat: 'node.perf,node.perf.usertiming', name: 'A to B' } + ]; + const expectedEnds = [ + { cat: 'node.perf,node.perf.timerify', name: 'f' }, + { cat: 'node.perf,node.perf.usertiming', name: 'A to B' } + ]; + + const proc = cp.fork(__filename, + [ + 'child' + ], { + execArgv: [ + '--trace-events-enabled', + '--trace-event-categories', + 'node.perf' + ] + }); + + proc.once('exit', common.mustCall(() => { + const file = path.join(tmpdir.path, 'node_trace.1.log'); + + assert(common.fileExists(file)); + fs.readFile(file, common.mustCall((err, data) => { + const traces = JSON.parse(data.toString()).traceEvents; + assert.strictEqual(traces.length, + expectedMarks.length + + expectedBegins.length + + expectedEnds.length); + + traces.forEach((trace) => { + assert.strictEqual(trace.pid, proc.pid); + switch (trace.ph) { + case 'R': + assert.strictEqual(trace.cat, 'node.perf,node.perf.usertiming'); + assert.strictEqual(trace.name, expectedMarks.shift()); + break; + case 'b': + const expectedBegin = expectedBegins.shift(); + assert.strictEqual(trace.cat, expectedBegin.cat); + assert.strictEqual(trace.name, expectedBegin.name); + break; + case 'e': + const expectedEnd = expectedEnds.shift(); + assert.strictEqual(trace.cat, expectedEnd.cat); + assert.strictEqual(trace.name, expectedEnd.name); + break; + default: + assert.fail('Unexpected trace event phase'); + } + }); + })); + })); +} |