current commit
260924.8 · test: enable trace_events tests with perfetto
import { isWindows, skipIfPerfettoEnabled } from '../common/index.mjs';
import assert from 'node:assert';
import { writeFileSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import { it } from 'node:test';
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();

it('should print the timing information for cjs', () => {
  const result = spawnSyncAndAssert(process.execPath, ['--eval', 'require("url");'], {
    cwd: tmpdir.path,
    env: {
      ...process.env,
      NODE_DEBUG: 'module_timer',
    },
  }, {
    stdout: '',
    stderr: /MODULE_TIMER/g,
  });

  const firstLine = result.stderr.split('\n').find((line) => line.includes('[url]'));

  assert.notStrictEqual(firstLine, undefined);
  assert.ok(firstLine.includes('MODULE_TIMER'), `Not found MODULE_TIMER on ${firstLine}`);
  assert.ok(firstLine.includes('[url]:'), `Not found [url]: on ${firstLine}`);
  assert.ok(firstLine.endsWith('ms'), `Not found ms on ${firstLine}`);
});

it('should write tracing information for cjs', async () => {
  const outputFile = tmpdir.resolve('output-trace.log');

  spawnSyncAndAssert(process.execPath, [
    '--trace-event-categories',
    'node.module_timer',
    '--trace-event-file-pattern',
    outputFile,
    '--eval',
    'require("url");',
  ], {
    cwd: tmpdir.path,
  }, {
    stdout: '',
    stderr: '',
  });

  const expectedMimeTypes = ['b', 'e'];
  const outputFileContent = await readFile(outputFile, 'utf-8');
  const outputFileJson = JSON.parse(outputFileContent).traceEvents;
  const urlTraces = outputFileJson.filter((trace) => trace.name === "require('url')");

  assert.strictEqual(urlTraces.length, 2);

  for (const trace of urlTraces) {
    assert.strictEqual(trace.ph, expectedMimeTypes.shift());
  }
});

it('should write tracing & print logs for cjs', async () => {
  const outputFile = tmpdir.resolve('output-trace-and-log.log');

  const result = spawnSyncAndAssert(process.execPath, [
    '--trace-event-categories',
    'node.module_timer',
    '--trace-event-file-pattern',
    outputFile,
    '--eval',
    'require("url");',
  ], {
    cwd: tmpdir.path,
    env: {
      ...process.env,
      NODE_DEBUG: 'module_timer',
    },
  }, {
    stdout: '',
    stderr: /MODULE_TIMER/g,
  });

  const firstLine = result.stderr.split('\n').find((line) => line.includes('[url]'));

  assert.notStrictEqual(firstLine, undefined);
  assert.ok(firstLine.includes('MODULE_TIMER'), `Not found MODULE_TIMER on ${firstLine}`);
  assert.ok(firstLine.includes('[url]:'), `Not found [url]: on ${firstLine}`);
  assert.ok(firstLine.endsWith('ms'), `Not found ms on ${firstLine}`);

  const expectedMimeTypes = ['b', 'e'];
  const outputFileContent = await readFile(outputFile, 'utf-8');
  const outputFileJson = JSON.parse(outputFileContent).traceEvents;
  const urlTraces = outputFileJson.filter((trace) => trace.name === "require('url')");

  assert.ok(urlTraces.length > 0, 'Not found url traces');

  for (const trace of urlTraces) {
    assert.strictEqual(trace.ph, expectedMimeTypes.shift());
  }
});

it('should support enable tracing dynamically', async () => {
  try {
    spawnSyncAndAssert(process.execPath, [
      '--eval',
      'require("trace_events")',
    ], {
      stdout: '',
      stderr: '',
    });
  } catch {
    // Skip this test if the trace_events module is not available
    return;
  }


  const outputFile = tmpdir.resolve('output-dynamic-trace.log');
  let requireFileWithDoubleQuote = '';
  if (!isWindows) {
    // Double quotes are not valid char for a path on Windows.
    const fileWithDoubleQuote = tmpdir.resolve('filename-with-"double"-quotes.cjs');
    writeFileSync(fileWithDoubleQuote, ';\n');
    requireFileWithDoubleQuote = `require(${JSON.stringify(fileWithDoubleQuote)});`;
  }
  const jsScript = `
  const traceEvents = require("trace_events");
  const tracing = traceEvents.createTracing({ categories: ["node.module_timer"] });

  tracing.enable();
  ${requireFileWithDoubleQuote}
  require("http");
  tracing.disable();

  require("vm");
  `;

  spawnSyncAndAssert(process.execPath, [
    '--trace-event-file-pattern',
    outputFile,
    '--eval',
    jsScript,
  ], {
    cwd: tmpdir.path,
    env: {
      ...process.env,
    },
  }, {
    stdout: '',
    stderr: '',
  });

  const expectedMimeTypes = ['b', 'e'];
  const outputFileContent = await readFile(outputFile, 'utf-8');

  const outputFileJson = JSON.parse(outputFileContent).traceEvents;
  const httpTraces = outputFileJson.filter((trace) => trace.name === "require('http')");

  assert.ok(httpTraces.length > 0, 'Not found http traces');

  for (const trace of httpTraces) {
    assert.strictEqual(trace.ph, expectedMimeTypes.shift());
  }

  const vmTraces = outputFileJson.filter((trace) => trace.name === "require('vm')");
  assert.strictEqual(vmTraces.length, 0);
});

it('should not print when is disabled and found duplicated labels (GH-54265)', () => {
  const testFile = fixtures.path('GH-54265/index.js');

  spawnSyncAndAssert(process.execPath, [
    testFile,
  ], {
    cwd: tmpdir.path,
    env: {
      ...process.env,
    },
  }, {
    stdout: '',
    stderr: '',
  });
});