From ab9cad8107d35a41303af1eec12c6df8330d50b5 Mon Sep 17 00:00:00 2001 From: Luke Albao Date: Wed, 25 Oct 2023 16:37:54 -0700 Subject: [PATCH] test: v8: Add test-linux-perf-logger test suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/50352 Reviewed-By: Michael Dawson Reviewed-By: Richard Lau Reviewed-By: Vinícius Lourenço Claro Cardoso --- test/fixtures/linux-perf-logger.js | 17 +++ test/v8-updates/test-linux-perf-logger.js | 148 ++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 test/fixtures/linux-perf-logger.js create mode 100644 test/v8-updates/test-linux-perf-logger.js diff --git a/test/fixtures/linux-perf-logger.js b/test/fixtures/linux-perf-logger.js new file mode 100644 index 00000000000000..d39f9e0cc45b05 --- /dev/null +++ b/test/fixtures/linux-perf-logger.js @@ -0,0 +1,17 @@ +'use strict'; + +process.stdout.write(`${process.pid}`); + +const testRegex = /test-regex/gi; + +function functionOne() { + for (let i = 0; i < 100; i++) { + const match = testRegex.exec(Math.random().toString()); + } +} + +function functionTwo() { + functionOne(); +} + +functionTwo(); diff --git a/test/v8-updates/test-linux-perf-logger.js b/test/v8-updates/test-linux-perf-logger.js new file mode 100644 index 00000000000000..8b00d332585121 --- /dev/null +++ b/test/v8-updates/test-linux-perf-logger.js @@ -0,0 +1,148 @@ +'use strict'; + +// --- About this test suite +// +// JIT support for perf(1) was added in 2009 (see https://lkml.org/lkml/2009/6/8/499). +// It works by looking for a perf map file in /tmp/perf-.map, where is the +// PID of the target process. +// +// The structure of this file is stable. Perf expects each line to specify a symbol +// in the form: +// +// +// +// where is the hex representation of the instruction pointer for the beginning +// of the function, is the byte length of the function, and is the +// readable JIT name used for reporting. +// +// This file asserts that a node script run with the appropriate flags will produce +// a compliant perf map. +// +// NOTE: This test runs only on linux, as that is the only platform supported by perf, and +// accordingly the only platform where `perf-basic-prof*` v8 flags are available. + + +const common = require('../common'); +if (!common.isLinux) { + common.skip('--perf-basic-prof* is statically defined as linux-only'); +} + +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const { readFileSync } = require('fs'); + +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const testCases = [ + { + title: '--perf-basic-prof interpreted', + nodeFlags: ['--perf-basic-prof', '--no-turbo-inlining', '--no-opt'], + matches: [ + '~functionOne .+/linux-perf-logger.js', + '~functionTwo .+/linux-perf-logger.js', + 'test-regex', + ], + noMatches: ['\\*functionOne', '\\*functionTwo'], + }, + { + title: '--perf-basic-prof compiled', + nodeFlags: ['--perf-basic-prof', '--no-turbo-inlining', '--always-opt'], + matches: [ + 'test-regex', + '~functionOne .+/linux-perf-logger.js', + '~functionTwo .+/linux-perf-logger.js', + '\\*functionOne .+/linux-perf-logger.js', + '\\*functionTwo .+/linux-perf-logger.js', + ], + noMatches: [], + }, + { + title: '--perf-basic-prof-only-functions interpreted', + nodeFlags: ['--perf-basic-prof-only-functions', '--no-turbo-inlining', '--no-opt'], + matches: ['~functionOne .+/linux-perf-logger.js', '~functionTwo .+/linux-perf-logger.js'], + noMatches: ['\\*functionOne', '\\*functionTwo', 'test-regex'], + }, + { + title: '--perf-basic-prof-only-functions compiled', + nodeFlags: ['--perf-basic-prof-only-functions', '--no-turbo-inlining', '--always-opt'], + matches: [ + '~functionOne .+/linux-perf-logger.js', + '~functionTwo .+/linux-perf-logger.js', + '\\*functionOne .+/linux-perf-logger.js', + '\\*functionTwo .+/linux-perf-logger.js', + ], + noMatches: ['test-regex'], + }, +]; + +function runTest(test) { + const report = { + title: test.title, + perfMap: '[uninitialized]', + errors: [], + }; + + const args = test.nodeFlags.concat(fixtures.path('linux-perf-logger.js')); + const run = spawnSync(process.execPath, args, { cwd: tmpdir.path, encoding: 'utf8' }); + if (run.error) { + report.errors.push(run.error.stack); + return report; + } + if (run.status !== 0) { + report.errors.push(`running script:\n${run.stderr}`); + return report; + } + + try { + report.perfMap = readFileSync(`/tmp/perf-${run.pid}.map`, 'utf8'); + } catch (err) { + report.errors.push(`reading perf map: ${err.stack}`); + return report; + } + + const hexRegex = '[a-fA-F0-9]+'; + for (const testRegex of test.matches) { + const lineRegex = new RegExp(`${hexRegex} ${hexRegex}.*:${testRegex}`); + if (!lineRegex.test(report.perfMap)) { + report.errors.push(`Expected to match ${lineRegex}`); + } + } + + for (const regex of test.noMatches) { + const noMatch = new RegExp(regex); + if (noMatch.test(report.perfMap)) { + report.errors.push(`Expected not to match ${noMatch}`); + } + } + + return report; +} + +function serializeError(report, index) { + return `[ERROR ${index + 1}] ${report.title} +Errors: +${report.errors.map((err, i) => `${i + 1}. ${err}`).join('\n')} +Perf map content: +${report.perfMap} + +`; +} + +function runSuite() { + const failures = []; + + for (const tc of testCases) { + const report = runTest(tc); + if (report.errors.length > 0) { + failures.push(report); + } + } + + const errorsToReport = failures.map(serializeError).join('\n--------\n'); + + assert.strictEqual(failures.length, 0, `${failures.length} tests failed\n\n${errorsToReport}`); +} + +runSuite();