| 'use strict'; |
| |
| // This test verifies that JavaScript functions are being correctly sampled by |
| // Linux perf. The test runs a JavaScript script, sampling the execution with |
| // Linux perf. It then uses `perf script` to generate a human-readable output, |
| // and uses regular expressions to find samples of the functions defined in |
| // `fixtures/linux-perf.js`. |
| |
| // NOTE (mmarchini): this test is meant to run only on Linux machines with Linux |
| // perf installed. It will skip if those criteria are not met. |
| |
| const common = require('../common'); |
| if (!common.hasCrypto) |
| common.skip('missing crypto'); |
| |
| const assert = require('assert'); |
| const { spawnSync } = require('child_process'); |
| const fixtures = require('../common/fixtures'); |
| const tmpdir = require('../common/tmpdir'); |
| tmpdir.refresh(); |
| |
| if (process.config.variables.node_shared) |
| common.skip("can't test Linux perf with shared libraries yet"); |
| |
| if (!common.isLinux) |
| common.skip('only testing Linux for now'); |
| |
| const frequency = 99; |
| |
| const repeat = 5; |
| |
| // Expected number of samples we'll capture per repeat |
| const sampleCount = 10; |
| const sleepTime = sampleCount * (1.0 / frequency); |
| |
| const perfFlags = [ |
| 'record', |
| `-F${frequency}`, |
| '-g', |
| ]; |
| |
| const nodeCommonFlags = [ |
| '--perf-basic-prof', |
| '--interpreted-frames-native-stack', |
| '--no-turbo-inlining', // Otherwise simple functions might get inlined. |
| ]; |
| |
| const perfInterpretedFramesArgs = [ |
| ...perfFlags, |
| '--', |
| process.execPath, |
| ...nodeCommonFlags, |
| '--no-opt', |
| fixtures.path('linux-perf.js'), |
| `${sleepTime}`, |
| `${repeat}`, |
| ]; |
| |
| const perfCompiledFramesArgs = [ |
| ...perfFlags, |
| '--', |
| process.execPath, |
| ...nodeCommonFlags, |
| '--always-opt', |
| fixtures.path('linux-perf.js'), |
| `${sleepTime}`, |
| `${repeat}`, |
| ]; |
| |
| const perfArgsList = [ |
| perfInterpretedFramesArgs, perfCompiledFramesArgs |
| ]; |
| |
| const perfScriptArgs = [ |
| 'script', |
| ]; |
| |
| const options = { |
| cwd: tmpdir.path, |
| encoding: 'utf-8', |
| }; |
| |
| let output = ''; |
| |
| for (const perfArgs of perfArgsList) { |
| const perf = spawnSync('perf', perfArgs, options); |
| assert.ifError(perf.error); |
| if (perf.status !== 0) |
| throw new Error(`Failed to execute 'perf': ${perf.stderr}`); |
| |
| const perfScript = spawnSync('perf', perfScriptArgs, options); |
| assert.ifError(perfScript.error); |
| if (perfScript.status !== 0) |
| throw new Error(`Failed to execute perf script: ${perfScript.stderr}`); |
| |
| output += perfScript.stdout; |
| } |
| |
| const interpretedFunctionOneRe = /InterpretedFunction:functionOne/; |
| const compiledFunctionOneRe = /LazyCompile:\*functionOne/; |
| const interpretedFunctionTwoRe = /InterpretedFunction:functionTwo/; |
| const compiledFunctionTwoRe = /LazyCompile:\*functionTwo/; |
| |
| |
| assert.ok(output.match(interpretedFunctionOneRe), |
| "Couldn't find interpreted functionOne()"); |
| assert.ok(output.match(compiledFunctionOneRe), |
| "Couldn't find compiled functionOne()"); |
| assert.ok(output.match(interpretedFunctionTwoRe), |
| "Couldn't find interpreted functionTwo()"); |
| assert.ok(output.match(compiledFunctionTwoRe), |
| "Couldn't find compiled functionTwo"); |