Skip to content

Commit

Permalink
test: add test for Linux perf
Browse files Browse the repository at this point in the history
This commit adds a test to validate if Linux perf is working correctly
on Node.js. The test is marked as flaky because its intention is to let
us know when changes on V8 potentially broke Linux perf, so we can fix
it before a new version of V8 lands on Node.js master.

PR-URL: #20783
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Joyee Cheung <[email protected]>
  • Loading branch information
Matheus Marchini authored and targos committed Jun 13, 2018
1 parent e16036c commit a4ad989
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
26 changes: 26 additions & 0 deletions test/fixtures/linux-perf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict';

const crypto = require('crypto');

// Functions should be complex enough for V8 to run them a few times before
// compiling, but not complex enough to always stay in interpreted mode. They
// should also take some time to run, otherwise Linux perf might miss them
// entirely even when sampling at a high frequency.
function functionOne(i) {
for (let j=i; j > 0; j--) {
crypto.createHash('md5').update(functionTwo(i, j)).digest("hex");
}
}

function functionTwo(x, y) {
let data = ((((x * y) + (x / y)) * y) ** (x + 1)).toString();
if (x % 2 == 0) {
return crypto.createHash('md5').update(data.repeat((x % 100) + 1)).digest("hex");
} else {
return crypto.createHash('md5').update(data.repeat((y % 100) + 1)).digest("hex");
}
}

for (let i = 0; i < 1000; i++) {
functionOne(i);
}
81 changes: 81 additions & 0 deletions test/v8-updates/test-linux-perf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
'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");

const perfArgs = [
'record',
'-F500',
'-g',
'--',
process.execPath,
'--perf-basic-prof',
'--interpreted-frames-native-stack',
'--no-turbo-inlining', // Otherwise simple functions might get inlined.
fixtures.path('linux-perf.js'),
];

const perfScriptArgs = [
'script',
];

const options = {
cwd: tmpdir.path,
encoding: 'utf-8',
};

if (!common.isLinux)
common.skip('only testing Linux for now');

const perf = spawnSync('perf', perfArgs, options);

if (perf.error && perf.error.errno === 'ENOENT')
common.skip('perf not found on system');

if (perf.status !== 0) {
common.skip(`Failed to execute perf: ${perf.stderr}`);
}

const perfScript = spawnSync('perf', perfScriptArgs, options);

if (perf.error)
common.skip(`perf script aborted: ${perf.error.errno}`);

if (perfScript.status !== 0) {
common.skip(`Failed to execute perf script: ${perfScript.stderr}`);
}

const interpretedFunctionOneRe = /InterpretedFunction:functionOne/;
const compiledFunctionOneRe = /LazyCompile:\*functionOne/;
const interpretedFunctionTwoRe = /InterpretedFunction:functionTwo/;
const compiledFunctionTwoRe = /LazyCompile:\*functionTwo/;

const output = perfScript.stdout;

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");

0 comments on commit a4ad989

Please sign in to comment.