Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/build-shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ on:
required: false
type: boolean
default: false
perfetto:
description: Whether the build links perfetto, which the trace event tests need trace_processor_shell for.
required: false
type: boolean
default: false
secrets:
CACHIX_AUTH_TOKEN:
description: Cachix auth token for nodejs.cachix.org.
Expand Down Expand Up @@ -78,6 +83,11 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
core.exportVariable('NIX_SCCACHE', '(import <nixpkgs> {}).sccache');

- name: Get trace_processor
if: inputs.perfetto
shell: bash
run: make -C "$TAR_DIR" trace-processor

- name: Build Node.js and run tests
shell: bash
run: |
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test-linux-perfetto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ jobs:
- name: Build
working-directory: node
run: make build-ci -j4 V=1 CONFIG_FLAGS="--error-on-warn --v8-enable-temporal-support --with-perfetto"
- name: Get trace_processor
working-directory: node
run: make trace-processor
- name: Test
working-directory: node
run: make test-ci -j1 V=1 TEST_CI_ARGS="-p actions --measure-flakiness 9"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test-shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ jobs:
with:
runner: ${{ matrix.runner }}
with-sccache: ${{ github.base_ref == 'main' || github.ref_name == 'main' }}
perfetto: ${{ matrix.perfetto || false }}
extra-nix-flags: |
--arg useSeparateDerivationForV8 true \
${{ matrix.perfetto && '--arg withPerfetto true \' || '\' }}
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ tools/*/*.i.tmp
/tools/eslint/node_modules
/tools/lint-md/node_modules

# === Rules for tools/perfetto ===
/tools/perfetto/trace_processor_shell
/tools/perfetto/.version

# === Rules for test artifacts ===
/*.tap
/*.xml
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ distclean: ## Remove all build and test artifacts.
$(RM) -r node_modules
$(RM) -r deps/icu
$(RM) -r deps/icu4c*.tgz deps/icu4c*.zip deps/icu-tmp
$(RM) tools/perfetto/trace_processor_shell tools/perfetto/.version
$(RM) $(BINARYTAR).* $(TARBALL).*

.PHONY: check
Expand Down Expand Up @@ -338,6 +339,10 @@ coverage-run-js: ## Run JavaScript tests with coverage.
TEST_CI_ARGS="$(TEST_CI_ARGS) --type=coverage" $(MAKE) jstest
$(MAKE) coverage-report-js

.PHONY: trace-processor
trace-processor: ## Download perfetto's trace_processor_shell.
@tools/perfetto/get_trace_processor

.PHONY: test
# This does not run tests of third-party libraries inside deps.
test: all ## Run default tests and build docs.
Expand Down Expand Up @@ -1319,7 +1324,7 @@ ifeq ($(SKIP_SHARED_DEPS), 1)
$(RM) -r $(TARNAME)/deps/ngtcp2
find $(TARNAME)/deps/openssl -maxdepth 1 -type f ! -name 'nodejs-openssl.cnf' -exec $(RM) {} +
find $(TARNAME)/deps/openssl -mindepth 1 -maxdepth 1 -type d -exec $(RM) -r {} +
$(RM) -r $(TARNAME)/deps/perfetto
find $(TARNAME)/deps/perfetto -mindepth 1 -maxdepth 1 ! -name 'VERSION' -exec $(RM) -r {} +
$(RM) -r $(TARNAME)/deps/simdjson
$(RM) -r $(TARNAME)/deps/sqlite
$(RM) -r $(TARNAME)/deps/uv
Expand Down
1 change: 1 addition & 0 deletions test/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,7 @@ const common = {
hasSQLite,
hasFFI,
hasLocalStorage,
hasPerfetto,
invalidArgTypeHelper,
isAlive,
isASan,
Expand Down
64 changes: 64 additions & 0 deletions test/common/trace_events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use strict';

// Helpers to deal with both Chrome legacy JSON trace format, and Perfetto
// binary format.
// This depends on perfetto's `trace_processor_shell` converts to pftrace format
// to JSON format. Run `make trace-processor` to download it.

const assert = require('assert');
const { spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const common = require('./');

const traceProcessor = path.resolve(
__dirname, '..', '..', 'tools', 'perfetto', 'trace_processor_shell');

// The JSON form of a trace runs about three times the size of the trace it was
// converted from, and the traces these tests produce are a few hundred KiB at
// most. This is an assumed MAX size of a JSON conversion size limit for tests.
const kMaxTraceJsonBytes = 64 * 1024 * 1024;

const traceFileExt = common.hasPerfetto ? 'pftrace' : 'log';
const defaultTraceFileName = `node_trace.1.${traceFileExt}`;

// Only perfetto traces need converting, so a missing `trace_processor_shell`
// does not stop anything on a legacy build.
function checkTraceProcessor() {
if (common.hasPerfetto && !fs.existsSync(traceProcessor)) {
assert.fail('trace_processor_shell is missing, ' +
'run `make trace-processor` to download it');
}
}

function readTraceEvents(file) {
if (!common.hasPerfetto) {
return JSON.parse(fs.readFileSync(file, 'utf8')).traceEvents;
}

const converted = spawnSync(traceProcessor, ['convert', 'json', file],
{ maxBuffer: kMaxTraceJsonBytes });
assert.ifError(converted.error);
assert.strictEqual(
converted.status, 0,
`trace_processor_shell failed: ${converted.stderr}`);
return JSON.parse(converted.stdout.toString()).traceEvents;
}

// A perfetto trace event carries the single category it was emitted with. The
// legacy backend instead groups it with every ancestor category, so
// `node.net.native` is recorded as `node,node.net,node.net.native`.
function traceCategory(name) {
if (common.hasPerfetto) {
return name;
}
const parts = name.split('.');
return parts.map((_, i) => parts.slice(0, i + 1).join('.')).join(',');
}

module.exports = {
defaultTraceFileName,
readTraceEvents,
checkTraceProcessor,
traceCategory,
};
3 changes: 3 additions & 0 deletions test/parallel/test-inspector-tracing-domain.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
const common = require('../common');

common.skipIfInspectorDisabled();
// The inspector NodeTracing domain is not wired up on a perfetto build, see
// src/inspector_agent.cc, so every command here fails with
// ERR_INSPECTOR_COMMAND.
common.skipIfPerfettoEnabled();

const { isMainThread } = require('worker_threads');
Expand Down
2 changes: 2 additions & 0 deletions test/parallel/test-module-print-timing.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import tmpdir from '../common/tmpdir.js';
import { spawnSyncAndAssert } from '../common/child_process.js';
import fixtures from '../common/fixtures.js';

// The dynamic tracing case below records nothing on a perfetto build: a
// category enabled after the tracing session started stays off.
skipIfPerfettoEnabled();
tmpdir.refresh();

Expand Down
7 changes: 3 additions & 4 deletions test/parallel/test-permission-fs-write-trace-events.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
// Flags: --expose-internals
'use strict';

const common = require('../common');
const { spawnSyncAndExitWithoutError } = require('../common/child_process');
const { isMainThread } = require('worker_threads');

common.skipIfPerfettoEnabled();
if (!isMainThread) {
common.skip('This test only works on a main thread');
}

const assert = require('assert');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');
const { defaultTraceFileName } = require('../common/trace_events');

try {
require('trace_events');
Expand Down Expand Up @@ -56,7 +55,7 @@ assert.throws(() => {
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemWrite',
resource: 'node_trace.1.log',
resource: defaultTraceFileName,
}));

assert.strictEqual(fs.existsSync('node_trace.1.log'), false);
assert.strictEqual(fs.existsSync(defaultTraceFileName), false);
57 changes: 0 additions & 57 deletions test/parallel/test-trace-events-all.js

This file was deleted.

2 changes: 2 additions & 0 deletions test/parallel/test-trace-events-async-hooks-dynamic.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ try {
common.skip('missing trace events');
}

// Perfetto records nothing for a category enabled after the tracing session
// started, so there is no dynamic enabling to test there yet.
common.skipIfPerfettoEnabled();

const assert = require('assert');
Expand Down
67 changes: 0 additions & 67 deletions test/parallel/test-trace-events-async-hooks.js

This file was deleted.

2 changes: 2 additions & 0 deletions test/parallel/test-trace-events-binding.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const assert = require('assert');
const cp = require('child_process');
const fs = require('fs');

// V8's trace() builtin only accepts the begin, end and instant phases on a
// perfetto build, so the nestable async phase used here throws a TypeError.
common.skipIfPerfettoEnabled();

const CODE = `
Expand Down
3 changes: 3 additions & 0 deletions test/parallel/test-trace-events-console.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const cp = require('child_process');
const fs = require('fs');
const tmpdir = require('../common/tmpdir');

// console.count() throws on a perfetto build once node.console is enabled:
// internal/trace_events picks the nestable async instant phase for counters,
// which V8's trace() builtin rejects there.
common.skipIfPerfettoEnabled();

// Tests that node.console trace events for counters and time methods are
Expand Down
2 changes: 2 additions & 0 deletions test/parallel/test-trace-events-dynamic-enable.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
const common = require('../common');

common.skipIfInspectorDisabled();
// Needs two things a perfetto build does not have: the inspector NodeTracing
// domain, and recording for a category enabled after the session started.
common.skipIfPerfettoEnabled();

const { isMainThread } = require('worker_threads');
Expand Down
5 changes: 5 additions & 0 deletions test/parallel/test-trace-events-fs-async.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ const cp = require('child_process');
const fs = require('fs');
const util = require('util');

// MKDir passes UV_FS_UNLINK to FS_ASYNC_TRACE_BEGIN1 (src/node_file.cc), so
// the begin event is named `unlink` while the end event is named `mkdir`. The
// legacy backend records both names, and this test passes off the end event.
// Perfetto matches an async pair by name and drops the unmatched end, leaving
// no `mkdir` event at all.
common.skipIfPerfettoEnabled();

const tests = { __proto__: null };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ try {
common.skip('missing trace events');
}

// Perfetto aborts on a category that is not in its static registry, and its
// enabled flag does not follow createTracing().enable().
common.skipIfPerfettoEnabled();

const { createTracing, getEnabledCategories } = require('trace_events');
Expand Down
2 changes: 2 additions & 0 deletions test/parallel/test-trace-events-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const assert = require('assert');
const cp = require('child_process');
const fs = require('fs');

// A perfetto build drops the legacy metadata events, so none of the
// process_name, version or node entries checked here are recorded.
common.skipIfPerfettoEnabled();

const CODE =
Expand Down
Loading
Loading