benchmark: add initial support for benchmark coverage
Hey,
I'm opening it as a draft as there are a lot of loose ends to solve before considering such usage. This is an experiment related to research I'm conducting.
The idea is to generate a benchmark coverage for all of our exported modules. For instance, check if the functions exposed by require('node:fs')
are covered in our benchmark suite (benchmark/fs/*.js
). To achieve an intermediary goal I had to monkey-patch the Module.prototype.require
before executing each benchmark (we run each one in a separate process) to return a singleton that basically manages the state of how many times that function was called.
I have tried to use our test_runner
coverage for that, but it couldn't identify the location of built-in modules (require('node:*')
) which is expected by the nature of a coverage tool. The other reason it didn't fit was the need to cover only the exported modules, not the lib/internal/*
functions. The result I wanted to have was: "I need to know which functions/classes that are exposed to users do not contain a benchmark".
It's also worth it to mention that, a nested call is ignored in the benchmark report. Example:
- benchmark/foo/foo.js
const bench = common.createBenchmark(main, { ... }});
const fs = require('node:fs')
function main({ ... }) {
bench.start()
fs.exists(__filename, () => {
bench.stop(n)
});
}
-
fs.exists
callsfs.access
behind the scenes https://github.com/nodejs/node/blob/b8a2550ec0a7fd6ce1f9a6b6b7cf2bfbf96fb484/lib/fs.js#L260 - I didn't want this benchmark to consider both
fs.exists
andfs.access
as "covered" in the benchmark report. It should only countfs.exists
as covered. - To achieve that I'm getting the last callSite and checking if the path is
benchmark/**/*.js
Results
Please note that this is not accurate (in terms of Classes) for the reason described in the limitations section
┌─────────┬──────────────────┬────────┐
│ (index) │ node:assert │ Values │
├─────────┼──────────────────┼────────┤
│ 0 │ 'fail' │ 0 │
│ 1 │ 'AssertionError' │ 0 │
│ 2 │ 'equal' │ 0 │
│ 3 │ 'notEqual' │ 0 │
│ 4 │ 'notStrictEqual' │ 0 │
│ 5 │ 'throws' │ 0 │
│ 6 │ 'rejects' │ 0 │
│ 7 │ 'doesNotThrow' │ 0 │
│ 8 │ 'doesNotReject' │ 0 │
│ 9 │ 'match' │ 0 │
│ 10 │ 'doesNotMatch' │ 0 │
│ 11 │ 'CallTracker' │ 0 │
│ 12 │ 'strict' │ 0 │
└─────────┴──────────────────┴────────┘
┌─────────┬─────────────┬────────┐
│ (index) │ node:buffer │ Values │
├─────────┼─────────────┼────────┤
│ 0 │ 'Buffer' │ 0 │
│ 1 │ 'transcode' │ 0 │
│ 2 │ 'isUtf8' │ 0 │
│ 3 │ 'isAscii' │ 0 │
│ 4 │ 'btoa' │ 0 │
│ 5 │ 'atob' │ 0 │
│ 6 │ 'Blob' │ 0 │
│ 7 │ 'File' │ 0 │
└─────────┴─────────────┴────────┘
┌─────────┬────────────────────┬────────┐
│ (index) │ node:child_process │ Values │
├─────────┼────────────────────┼────────┤
│ 0 │ 'ChildProcess' │ 0 │
│ 1 │ 'fork' │ 0 │
└─────────┴────────────────────┴────────┘
┌─────────┬──────────────────┬────────┐
│ (index) │ node:console │ Values │
├─────────┼──────────────────┼────────┤
│ 0 │ 'log' │ 0 │
│ 1 │ 'warn' │ 0 │
│ 2 │ 'error' │ 0 │
│ 3 │ 'dir' │ 0 │
│ 4 │ 'time' │ 0 │
│ 5 │ 'timeEnd' │ 0 │
│ 6 │ 'timeLog' │ 0 │
│ 7 │ 'trace' │ 0 │
│ 8 │ 'assert' │ 0 │
│ 9 │ 'clear' │ 0 │
│ 10 │ 'count' │ 0 │
│ 11 │ 'countReset' │ 0 │
│ 12 │ 'group' │ 0 │
│ 13 │ 'groupEnd' │ 0 │
│ 14 │ 'table' │ 0 │
│ 15 │ 'debug' │ 0 │
│ 16 │ 'info' │ 0 │
│ 17 │ 'dirxml' │ 0 │
│ 18 │ 'groupCollapsed' │ 0 │
│ 19 │ 'Console' │ 0 │
│ 20 │ 'profile' │ 0 │
│ 21 │ 'profileEnd' │ 0 │
│ 22 │ 'timeStamp' │ 0 │
│ 23 │ 'context' │ 0 │
│ 24 │ 'createTask' │ 0 │
└─────────┴──────────────────┴────────┘
┌─────────┬────────────────────────────┬────────┐
│ (index) │ node:crypto │ Values │
├─────────┼────────────────────────────┼────────┤
│ 0 │ 'checkPrime' │ 0 │
│ 1 │ 'checkPrimeSync' │ 0 │
│ 2 │ 'createDiffieHellman' │ 0 │
│ 3 │ 'createDiffieHellmanGroup' │ 0 │
│ 4 │ 'createECDH' │ 0 │
│ 5 │ 'createHmac' │ 0 │
│ 6 │ 'createSecretKey' │ 0 │
│ 7 │ 'diffieHellman' │ 0 │
│ 8 │ 'generatePrime' │ 0 │
│ 9 │ 'generatePrimeSync' │ 0 │
│ 10 │ 'getCipherInfo' │ 0 │
│ 11 │ 'getCurves' │ 0 │
│ 12 │ 'getDiffieHellman' │ 0 │
│ 13 │ 'getHashes' │ 0 │
│ 14 │ 'pbkdf2' │ 0 │
│ 15 │ 'pbkdf2Sync' │ 0 │
│ 16 │ 'generateKey' │ 0 │
│ 17 │ 'generateKeySync' │ 0 │
│ 18 │ 'privateDecrypt' │ 0 │
│ 19 │ 'publicEncrypt' │ 0 │
│ 20 │ 'randomFill' │ 0 │
│ 21 │ 'randomFillSync' │ 0 │
│ 22 │ 'scrypt' │ 0 │
│ 23 │ 'scryptSync' │ 0 │
│ 24 │ 'setEngine' │ 0 │
│ 25 │ 'getFips' │ 0 │
│ 26 │ 'setFips' │ 0 │
│ 27 │ 'Certificate' │ 0 │
│ 28 │ 'Cipheriv' │ 0 │
│ 29 │ 'Decipheriv' │ 0 │
│ 30 │ 'DiffieHellman' │ 0 │
│ 31 │ 'DiffieHellmanGroup' │ 0 │
│ 32 │ 'ECDH' │ 0 │
│ 33 │ 'Hash' │ 0 │
│ 34 │ 'Hmac' │ 0 │
│ 35 │ 'KeyObject' │ 0 │
│ 36 │ 'Sign' │ 0 │
│ 37 │ 'Verify' │ 0 │
│ 38 │ 'X509Certificate' │ 0 │
│ 39 │ 'secureHeapUsed' │ 0 │
│ 40 │ 'getRandomValues' │ 0 │
└─────────┴────────────────────────────┴────────┘
┌─────────┬────────────┬────────┐
│ (index) │ node:dgram │ Values │
├─────────┼────────────┼────────┤
│ 0 │ 'Socket' │ 0 │
└─────────┴────────────┴────────┘
┌─────────┬──────────────────────────┬────────┐
│ (index) │ node:diagnostics_channel │ Values │
├─────────┼──────────────────────────┼────────┤
│ 0 │ 'hasSubscribers' │ 0 │
│ 1 │ 'subscribe' │ 0 │
│ 2 │ 'tracingChannel' │ 0 │
│ 3 │ 'unsubscribe' │ 0 │
│ 4 │ 'Channel' │ 0 │
└─────────┴──────────────────────────┴────────┘
┌─────────┬─────────────────────────┬────────┐
│ (index) │ node:dns │ Values │
├─────────┼─────────────────────────┼────────┤
│ 0 │ 'lookupService' │ 0 │
│ 1 │ 'Resolver' │ 0 │
│ 2 │ 'getDefaultResultOrder' │ 0 │
│ 3 │ 'setDefaultResultOrder' │ 0 │
│ 4 │ 'setServers' │ 0 │
│ 5 │ 'getServers' │ 0 │
│ 6 │ 'resolve' │ 0 │
│ 7 │ 'resolve4' │ 0 │
│ 8 │ 'resolve6' │ 0 │
│ 9 │ 'resolveAny' │ 0 │
│ 10 │ 'resolveCaa' │ 0 │
│ 11 │ 'resolveCname' │ 0 │
│ 12 │ 'resolveMx' │ 0 │
│ 13 │ 'resolveNaptr' │ 0 │
│ 14 │ 'resolveNs' │ 0 │
│ 15 │ 'resolvePtr' │ 0 │
│ 16 │ 'resolveSoa' │ 0 │
│ 17 │ 'resolveSrv' │ 0 │
│ 18 │ 'resolveTxt' │ 0 │
│ 19 │ 'reverse' │ 0 │
└─────────┴─────────────────────────┴────────┘
┌─────────┬────────────────┬────────┐
│ (index) │ node:domain │ Values │
├─────────┼────────────────┼────────┤
│ 0 │ 'Domain' │ 0 │
│ 1 │ 'createDomain' │ 0 │
└─────────┴────────────────┴────────┘
┌─────────┬─────────────────────────────┬────────┐
│ (index) │ node:events │ Values │
├─────────┼─────────────────────────────┼────────┤
│ 0 │ 'addAbortListener' │ 0 │
│ 1 │ 'once' │ 0 │
│ 2 │ 'on' │ 0 │
│ 3 │ 'getEventListeners' │ 0 │
│ 4 │ 'getMaxListeners' │ 0 │
│ 5 │ 'EventEmitterAsyncResource' │ 0 │
│ 6 │ 'setMaxListeners' │ 0 │
│ 7 │ 'init' │ 0 │
│ 8 │ 'listenerCount' │ 0 │
└─────────┴─────────────────────────────┴────────┘
┌─────────┬───────────────────┬────────┐
│ (index) │ node:fs │ Values │
├─────────┼───────────────────┼────────┤
│ 0 │ 'appendFile' │ 0 │
│ 1 │ 'access' │ 0 │
│ 2 │ 'chown' │ 0 │
│ 3 │ 'chmod' │ 0 │
│ 4 │ 'close' │ 0 │
│ 5 │ 'copyFile' │ 0 │
│ 6 │ 'cp' │ 0 │
│ 7 │ 'exists' │ 0 │
│ 8 │ 'fchown' │ 0 │
│ 9 │ 'fchownSync' │ 0 │
│ 10 │ 'fchmod' │ 0 │
│ 11 │ 'fdatasync' │ 0 │
│ 12 │ 'fsync' │ 0 │
│ 13 │ 'ftruncate' │ 0 │
│ 14 │ 'futimes' │ 0 │
│ 15 │ 'glob' │ 0 │
│ 16 │ 'globSync' │ 0 │
│ 17 │ 'lchown' │ 0 │
│ 18 │ 'link' │ 0 │
│ 19 │ 'lutimes' │ 0 │
│ 20 │ 'mkdtemp' │ 0 │
│ 21 │ 'openAsBlob' │ 0 │
│ 22 │ 'read' │ 0 │
│ 23 │ 'readv' │ 0 │
│ 24 │ 'readlink' │ 0 │
│ 25 │ 'rename' │ 0 │
│ 26 │ 'rm' │ 0 │
│ 27 │ 'rmdir' │ 0 │
│ 28 │ 'statfs' │ 0 │
│ 29 │ 'statfsSync' │ 0 │
│ 30 │ 'symlink' │ 0 │
│ 31 │ 'truncate' │ 0 │
│ 32 │ 'truncateSync' │ 0 │
│ 33 │ 'unwatchFile' │ 0 │
│ 34 │ 'unlink' │ 0 │
│ 35 │ 'utimes' │ 0 │
│ 36 │ 'watch' │ 0 │
│ 37 │ 'watchFile' │ 0 │
│ 38 │ 'writeFile' │ 0 │
│ 39 │ 'write' │ 0 │
│ 40 │ 'writeSync' │ 0 │
│ 41 │ 'writev' │ 0 │
│ 42 │ 'Dirent' │ 0 │
│ 43 │ 'Stats' │ 0 │
│ 44 │ 'ReadStream' │ 0 │
│ 45 │ 'WriteStream' │ 0 │
│ 46 │ 'FileReadStream' │ 0 │
│ 47 │ 'FileWriteStream' │ 0 │
│ 48 │ 'Dir' │ 0 │
│ 49 │ 'opendir' │ 0 │
└─────────┴───────────────────┴────────┘
┌─────────┬─────────────────────────┬────────┐
│ (index) │ node:http │ Values │
├─────────┼─────────────────────────┼────────┤
│ 0 │ 'Agent' │ 0 │
│ 1 │ 'IncomingMessage' │ 0 │
│ 2 │ 'OutgoingMessage' │ 0 │
│ 3 │ 'Server' │ 0 │
│ 4 │ 'ServerResponse' │ 0 │
│ 5 │ 'validateHeaderName' │ 0 │
│ 6 │ 'validateHeaderValue' │ 0 │
│ 7 │ 'get' │ 0 │
│ 8 │ 'request' │ 0 │
│ 9 │ 'setMaxIdleHTTPParsers' │ 0 │
│ 10 │ 'WebSocket' │ 0 │
│ 11 │ 'CloseEvent' │ 0 │
│ 12 │ 'MessageEvent' │ 0 │
└─────────┴─────────────────────────┴────────┘
┌─────────┬──────────────────────────┬────────┐
│ (index) │ node:http2 │ Values │
├─────────┼──────────────────────────┼────────┤
│ 0 │ 'createSecureServer' │ 0 │
│ 1 │ 'getDefaultSettings' │ 0 │
│ 2 │ 'getPackedSettings' │ 0 │
│ 3 │ 'getUnpackedSettings' │ 0 │
│ 4 │ 'performServerHandshake' │ 0 │
│ 5 │ 'Http2ServerRequest' │ 0 │
│ 6 │ 'Http2ServerResponse' │ 0 │
└─────────┴──────────────────────────┴────────┘
┌─────────┬────────────┬────────┐
│ (index) │ node:https │ Values │
├─────────┼────────────┼────────┤
│ 0 │ 'Agent' │ 0 │
│ 1 │ 'Server' │ 0 │
│ 2 │ 'get' │ 0 │
│ 3 │ 'request' │ 0 │
└─────────┴────────────┴────────┘
┌─────────┬───────────────────┬────────┐
│ (index) │ node:inspector │ Values │
├─────────┼───────────────────┼────────┤
│ 0 │ 'open' │ 0 │
│ 1 │ 'close' │ 0 │
│ 2 │ 'url' │ 0 │
│ 3 │ 'waitForDebugger' │ 0 │
│ 4 │ 'Session' │ 0 │
└─────────┴───────────────────┴────────┘
┌─────────┬─────────────────────────┬────────┐
│ (index) │ node:module │ Values │
├─────────┼─────────────────────────┼────────┤
│ 0 │ 'isBuiltin' │ 0 │
│ 1 │ 'createRequire' │ 0 │
│ 2 │ 'syncBuiltinESMExports' │ 0 │
│ 3 │ 'Module' │ 0 │
│ 4 │ 'runMain' │ 0 │
│ 5 │ 'findSourceMap' │ 0 │
│ 6 │ 'register' │ 0 │
│ 7 │ 'SourceMap' │ 0 │
└─────────┴─────────────────────────┴────────┘
┌─────────┬────────────────────────────────────────────┬────────┐
│ (index) │ node:net │ Values │
├─────────┼────────────────────────────────────────────┼────────┤
│ 0 │ 'BlockList' │ 0 │
│ 1 │ 'SocketAddress' │ 0 │
│ 2 │ 'createConnection' │ 0 │
│ 3 │ 'isIP' │ 0 │
│ 4 │ 'Server' │ 0 │
│ 5 │ 'Socket' │ 0 │
│ 6 │ 'Stream' │ 0 │
│ 7 │ 'getDefaultAutoSelectFamily' │ 0 │
│ 8 │ 'setDefaultAutoSelectFamily' │ 0 │
│ 9 │ 'getDefaultAutoSelectFamilyAttemptTimeout' │ 0 │
│ 10 │ 'setDefaultAutoSelectFamilyAttemptTimeout' │ 0 │
└─────────┴────────────────────────────────────────────┴────────┘
┌─────────┬────────────────────────┬────────┐
│ (index) │ node:os │ Values │
├─────────┼────────────────────────┼────────┤
│ 0 │ 'arch' │ 0 │
│ 1 │ 'availableParallelism' │ 0 │
│ 2 │ 'endianness' │ 0 │
│ 3 │ 'freemem' │ 0 │
│ 4 │ 'getPriority' │ 0 │
│ 5 │ 'release' │ 0 │
│ 6 │ 'setPriority' │ 0 │
│ 7 │ 'tmpdir' │ 0 │
│ 8 │ 'totalmem' │ 0 │
│ 9 │ 'userInfo' │ 0 │
│ 10 │ 'version' │ 0 │
│ 11 │ 'machine' │ 0 │
└─────────┴────────────────────────┴────────┘
┌─────────┬────────────────────┬────────┐
│ (index) │ node:path │ Values │
├─────────┼────────────────────┼────────┤
│ 0 │ 'normalize' │ 0 │
│ 1 │ 'isAbsolute' │ 0 │
│ 2 │ 'toNamespacedPath' │ 0 │
│ 3 │ 'dirname' │ 0 │
│ 4 │ 'basename' │ 0 │
│ 5 │ 'extname' │ 0 │
│ 6 │ 'format' │ 0 │
│ 7 │ 'parse' │ 0 │
│ 8 │ 'matchesGlob' │ 0 │
└─────────┴────────────────────┴────────┘
┌─────────┬────────────────────────────────┬────────┐
│ (index) │ node:perf_hooks │ Values │
├─────────┼────────────────────────────────┼────────┤
│ 0 │ 'Performance' │ 0 │
│ 1 │ 'PerformanceEntry' │ 0 │
│ 2 │ 'PerformanceMark' │ 0 │
│ 3 │ 'PerformanceMeasure' │ 0 │
│ 4 │ 'PerformanceObserver' │ 0 │
│ 5 │ 'PerformanceObserverEntryList' │ 0 │
│ 6 │ 'PerformanceResourceTiming' │ 0 │
│ 7 │ 'monitorEventLoopDelay' │ 0 │
└─────────┴────────────────────────────────┴────────┘
┌─────────┬───────────────────────────────────────┬────────┐
│ (index) │ node:process │ Values │
├─────────┼───────────────────────────────────────┼────────┤
│ 0 │ 'binding' │ 0 │
│ 1 │ 'dlopen' │ 0 │
│ 2 │ 'uptime' │ 0 │
│ 3 │ 'getActiveResourcesInfo' │ 0 │
│ 4 │ 'reallyExit' │ 0 │
│ 5 │ 'loadEnvFile' │ 0 │
│ 6 │ 'cpuUsage' │ 0 │
│ 7 │ 'resourceUsage' │ 0 │
│ 8 │ 'memoryUsage' │ 0 │
│ 9 │ 'constrainedMemory' │ 0 │
│ 10 │ 'availableMemory' │ 0 │
│ 11 │ 'kill' │ 0 │
│ 12 │ 'exit' │ 0 │
│ 13 │ 'hrtime' │ 0 │
│ 14 │ 'openStdin' │ 0 │
│ 15 │ 'getuid' │ 0 │
│ 16 │ 'geteuid' │ 0 │
│ 17 │ 'getgid' │ 0 │
│ 18 │ 'getegid' │ 0 │
│ 19 │ 'getgroups' │ 0 │
│ 20 │ 'assert' │ 0 │
│ 21 │ 'setUncaughtExceptionCaptureCallback' │ 0 │
│ 22 │ 'hasUncaughtExceptionCaptureCallback' │ 0 │
│ 23 │ 'emitWarning' │ 0 │
│ 24 │ 'nextTick' │ 0 │
│ 25 │ 'setSourceMapsEnabled' │ 0 │
│ 26 │ 'getBuiltinModule' │ 0 │
│ 27 │ 'abort' │ 0 │
│ 28 │ 'umask' │ 0 │
│ 29 │ 'chdir' │ 0 │
│ 30 │ 'cwd' │ 0 │
│ 31 │ 'initgroups' │ 0 │
│ 32 │ 'setgroups' │ 0 │
│ 33 │ 'setegid' │ 0 │
│ 34 │ 'seteuid' │ 0 │
│ 35 │ 'setgid' │ 0 │
│ 36 │ 'setuid' │ 0 │
└─────────┴───────────────────────────────────────┴────────┘
┌─────────┬───────────────┬────────┐
│ (index) │ node:punycode │ Values │
├─────────┼───────────────┼────────┤
│ 0 │ 'decode' │ 0 │
│ 1 │ 'encode' │ 0 │
└─────────┴───────────────┴────────┘
┌─────────┬──────────────────┬────────┐
│ (index) │ node:querystring │ Values │
├─────────┼──────────────────┼────────┤
│ 0 │ 'unescape' │ 0 │
│ 1 │ 'escape' │ 0 │
│ 2 │ 'encode' │ 0 │
│ 3 │ 'decode' │ 0 │
└─────────┴──────────────────┴────────┘
┌─────────┬──────────────────────┬────────┐
│ (index) │ node:readline │ Values │
├─────────┼──────────────────────┼────────┤
│ 0 │ 'Interface' │ 0 │
│ 1 │ 'clearLine' │ 0 │
│ 2 │ 'clearScreenDown' │ 0 │
│ 3 │ 'cursorTo' │ 0 │
│ 4 │ 'emitKeypressEvents' │ 0 │
│ 5 │ 'moveCursor' │ 0 │
└─────────┴──────────────────────┴────────┘
┌─────────┬───────────────┬────────┐
│ (index) │ node:repl │ Values │
├─────────┼───────────────┼────────┤
│ 0 │ 'start' │ 0 │
│ 1 │ 'writer' │ 0 │
│ 2 │ 'REPLServer' │ 0 │
│ 3 │ 'Recoverable' │ 0 │
└─────────┴───────────────┴────────┘
┌─────────┬──────────────────┬────────┐
│ (index) │ node:sea │ Values │
├─────────┼──────────────────┼────────┤
│ 0 │ 'isSea' │ 0 │
│ 1 │ 'getAsset' │ 0 │
│ 2 │ 'getRawAsset' │ 0 │
│ 3 │ 'getAssetAsBlob' │ 0 │
└─────────┴──────────────────┴────────┘
┌─────────┬─────────────────┬────────┐
│ (index) │ node:sqlite │ Values │
├─────────┼─────────────────┼────────┤
│ 0 │ 'DatabaseSync' │ 0 │
│ 1 │ 'StatementSync' │ 0 │
└─────────┴─────────────────┴────────┘
┌─────────┬───────────────────────────┬────────┐
│ (index) │ node:stream │ Values │
├─────────┼───────────────────────────┼────────┤
│ 0 │ 'isDestroyed' │ 0 │
│ 1 │ 'isDisturbed' │ 0 │
│ 2 │ 'isErrored' │ 0 │
│ 3 │ 'isReadable' │ 0 │
│ 4 │ 'isWritable' │ 0 │
│ 5 │ 'duplexPair' │ 0 │
│ 6 │ 'pipeline' │ 0 │
│ 7 │ 'addAbortSignal' │ 0 │
│ 8 │ 'finished' │ 0 │
│ 9 │ 'destroy' │ 0 │
│ 10 │ 'compose' │ 0 │
│ 11 │ 'setDefaultHighWaterMark' │ 0 │
│ 12 │ 'getDefaultHighWaterMark' │ 0 │
│ 13 │ 'Stream' │ 0 │
└─────────┴───────────────────────────┴────────┘
┌─────────┬───────────────────────────────┬────────┐
│ (index) │ node:sys │ Values │
├─────────┼───────────────────────────────┼────────┤
│ 0 │ 'callbackify' │ 0 │
│ 1 │ 'debug' │ 0 │
│ 2 │ 'debuglog' │ 0 │
│ 3 │ 'deprecate' │ 0 │
│ 4 │ 'format' │ 0 │
│ 5 │ 'styleText' │ 0 │
│ 6 │ 'formatWithOptions' │ 0 │
│ 7 │ 'getSystemErrorMap' │ 0 │
│ 8 │ 'getSystemErrorName' │ 0 │
│ 9 │ 'inherits' │ 0 │
│ 10 │ 'inspect' │ 0 │
│ 11 │ 'isArray' │ 0 │
│ 12 │ 'isBoolean' │ 0 │
│ 13 │ 'isBuffer' │ 0 │
│ 14 │ 'isDeepStrictEqual' │ 0 │
│ 15 │ 'isNull' │ 0 │
│ 16 │ 'isNullOrUndefined' │ 0 │
│ 17 │ 'isNumber' │ 0 │
│ 18 │ 'isString' │ 0 │
│ 19 │ 'isSymbol' │ 0 │
│ 20 │ 'isUndefined' │ 0 │
│ 21 │ 'isRegExp' │ 0 │
│ 22 │ 'isObject' │ 0 │
│ 23 │ 'isDate' │ 0 │
│ 24 │ 'isError' │ 0 │
│ 25 │ 'isFunction' │ 0 │
│ 26 │ 'isPrimitive' │ 0 │
│ 27 │ 'log' │ 0 │
│ 28 │ 'promisify' │ 0 │
│ 29 │ 'stripVTControlCharacters' │ 0 │
│ 30 │ 'toUSVString' │ 0 │
│ 31 │ 'transferableAbortSignal' │ 0 │
│ 32 │ 'transferableAbortController' │ 0 │
│ 33 │ 'aborted' │ 0 │
│ 34 │ 'parseEnv' │ 0 │
│ 35 │ 'parseArgs' │ 0 │
│ 36 │ 'TextDecoder' │ 0 │
│ 37 │ 'TextEncoder' │ 0 │
│ 38 │ 'MIMEType' │ 0 │
│ 39 │ 'MIMEParams' │ 0 │
└─────────┴───────────────────────────────┴────────┘
┌─────────┬──────────────┬────────┐
│ (index) │ node:test │ Values │
├─────────┼──────────────┼────────┤
│ 0 │ 'skip' │ 0 │
│ 1 │ 'todo' │ 0 │
│ 2 │ 'only' │ 0 │
│ 3 │ 'after' │ 0 │
│ 4 │ 'afterEach' │ 0 │
│ 5 │ 'before' │ 0 │
│ 6 │ 'beforeEach' │ 0 │
│ 7 │ 'run' │ 0 │
│ 8 │ 'suite' │ 0 │
│ 9 │ 'test' │ 0 │
└─────────┴──────────────┴────────┘
┌─────────┬──────────────────┬────────┐
│ (index) │ node:timers │ Values │
├─────────┼──────────────────┼────────┤
│ 0 │ 'setTimeout' │ 0 │
│ 1 │ 'clearTimeout' │ 0 │
│ 2 │ 'setImmediate' │ 0 │
│ 3 │ 'clearImmediate' │ 0 │
│ 4 │ 'setInterval' │ 0 │
│ 5 │ 'clearInterval' │ 0 │
│ 6 │ 'active' │ 0 │
│ 7 │ 'unenroll' │ 0 │
│ 8 │ 'enroll' │ 0 │
└─────────┴──────────────────┴────────┘
┌─────────┬───────────────────────┬────────┐
│ (index) │ node:tls │ Values │
├─────────┼───────────────────────┼────────┤
│ 0 │ 'checkServerIdentity' │ 0 │
│ 1 │ 'SecureContext' │ 0 │
│ 2 │ 'Server' │ 0 │
└─────────┴───────────────────────┴────────┘
┌─────────┬───────────────┬────────┐
│ (index) │ node:tty │ Values │
├─────────┼───────────────┼────────┤
│ 0 │ 'isatty' │ 0 │
│ 1 │ 'ReadStream' │ 0 │
│ 2 │ 'WriteStream' │ 0 │
└─────────┴───────────────┴────────┘
┌─────────┬────────────────────┬────────┐
│ (index) │ node:url │ Values │
├─────────┼────────────────────┼────────┤
│ 0 │ 'Url' │ 0 │
│ 1 │ 'resolveObject' │ 0 │
│ 2 │ 'URL' │ 0 │
│ 3 │ 'URLSearchParams' │ 0 │
│ 4 │ 'urlToHttpOptions' │ 0 │
└─────────┴────────────────────┴────────┘
┌─────────┬───────────────────────────────┬────────┐
│ (index) │ node:util │ Values │
├─────────┼───────────────────────────────┼────────┤
│ 0 │ 'callbackify' │ 0 │
│ 1 │ 'debug' │ 0 │
│ 2 │ 'debuglog' │ 0 │
│ 3 │ 'deprecate' │ 0 │
│ 4 │ 'formatWithOptions' │ 0 │
│ 5 │ 'getSystemErrorMap' │ 0 │
│ 6 │ 'getSystemErrorName' │ 0 │
│ 7 │ 'inherits' │ 0 │
│ 8 │ 'isArray' │ 0 │
│ 9 │ 'isBoolean' │ 0 │
│ 10 │ 'isBuffer' │ 0 │
│ 11 │ 'isDeepStrictEqual' │ 0 │
│ 12 │ 'isNull' │ 0 │
│ 13 │ 'isNullOrUndefined' │ 0 │
│ 14 │ 'isNumber' │ 0 │
│ 15 │ 'isString' │ 0 │
│ 16 │ 'isSymbol' │ 0 │
│ 17 │ 'isUndefined' │ 0 │
│ 18 │ 'isRegExp' │ 0 │
│ 19 │ 'isObject' │ 0 │
│ 20 │ 'isDate' │ 0 │
│ 21 │ 'isError' │ 0 │
│ 22 │ 'isFunction' │ 0 │
│ 23 │ 'isPrimitive' │ 0 │
│ 24 │ 'log' │ 0 │
│ 25 │ 'stripVTControlCharacters' │ 0 │
│ 26 │ 'toUSVString' │ 0 │
│ 27 │ 'transferableAbortSignal' │ 0 │
│ 28 │ 'transferableAbortController' │ 0 │
│ 29 │ 'aborted' │ 0 │
│ 30 │ 'parseEnv' │ 0 │
│ 31 │ 'parseArgs' │ 0 │
│ 32 │ 'TextDecoder' │ 0 │
│ 33 │ 'TextEncoder' │ 0 │
│ 34 │ 'MIMEType' │ 0 │
│ 35 │ 'MIMEParams' │ 0 │
└─────────┴───────────────────────────────┴────────┘
┌─────────┬────────────────────────────────┬────────┐
│ (index) │ node:v8 │ Values │
├─────────┼────────────────────────────────┼────────┤
│ 0 │ 'cachedDataVersionTag' │ 0 │
│ 1 │ 'getHeapSnapshot' │ 0 │
│ 2 │ 'getHeapCodeStatistics' │ 0 │
│ 3 │ 'setFlagsFromString' │ 0 │
│ 4 │ 'Serializer' │ 0 │
│ 5 │ 'Deserializer' │ 0 │
│ 6 │ 'DefaultSerializer' │ 0 │
│ 7 │ 'DefaultDeserializer' │ 0 │
│ 8 │ 'deserialize' │ 0 │
│ 9 │ 'takeCoverage' │ 0 │
│ 10 │ 'stopCoverage' │ 0 │
│ 11 │ 'writeHeapSnapshot' │ 0 │
│ 12 │ 'queryObjects' │ 0 │
│ 13 │ 'setHeapSnapshotNearHeapLimit' │ 0 │
│ 14 │ 'GCProfiler' │ 0 │
└─────────┴────────────────────────────────┴────────┘
┌─────────┬───────────────────┬────────┐
│ (index) │ node:vm │ Values │
├─────────┼───────────────────┼────────┤
│ 0 │ 'Script' │ 0 │
│ 1 │ 'createScript' │ 0 │
│ 2 │ 'runInNewContext' │ 0 │
│ 3 │ 'isContext' │ 0 │
│ 4 │ 'compileFunction' │ 0 │
│ 5 │ 'measureMemory' │ 0 │
└─────────┴───────────────────┴────────┘
┌─────────┬───────────┬────────┐
│ (index) │ node:wasi │ Values │
├─────────┼───────────┼────────┤
│ 0 │ 'WASI' │ 0 │
└─────────┴───────────┴────────┘
┌─────────┬────────────────────────────┬────────┐
│ (index) │ node:worker_threads │ Values │
├─────────┼────────────────────────────┼────────┤
│ 0 │ 'MessagePort' │ 0 │
│ 1 │ 'markAsUntransferable' │ 0 │
│ 2 │ 'isMarkedAsUntransferable' │ 0 │
│ 3 │ 'moveMessagePortToContext' │ 0 │
│ 4 │ 'receiveMessageOnPort' │ 0 │
│ 5 │ 'postMessageToThread' │ 0 │
│ 6 │ 'BroadcastChannel' │ 0 │
│ 7 │ 'setEnvironmentData' │ 0 │
│ 8 │ 'getEnvironmentData' │ 0 │
└─────────┴────────────────────────────┴────────┘
┌─────────┬────────────────────────┬────────┐
│ (index) │ node:zlib │ Values │
├─────────┼────────────────────────┼────────┤
│ 0 │ 'crc32' │ 0 │
│ 1 │ 'Deflate' │ 0 │
│ 2 │ 'Inflate' │ 0 │
│ 3 │ 'Gzip' │ 0 │
│ 4 │ 'Gunzip' │ 0 │
│ 5 │ 'DeflateRaw' │ 0 │
│ 6 │ 'InflateRaw' │ 0 │
│ 7 │ 'Unzip' │ 0 │
│ 8 │ 'BrotliCompress' │ 0 │
│ 9 │ 'BrotliDecompress' │ 0 │
│ 10 │ 'gzip' │ 0 │
│ 11 │ 'gzipSync' │ 0 │
│ 12 │ 'deflateRaw' │ 0 │
│ 13 │ 'deflateRawSync' │ 0 │
│ 14 │ 'unzip' │ 0 │
│ 15 │ 'unzipSync' │ 0 │
│ 16 │ 'gunzip' │ 0 │
│ 17 │ 'gunzipSync' │ 0 │
│ 18 │ 'inflateRaw' │ 0 │
│ 19 │ 'inflateRawSync' │ 0 │
│ 20 │ 'brotliCompress' │ 0 │
│ 21 │ 'brotliCompressSync' │ 0 │
│ 22 │ 'brotliDecompress' │ 0 │
│ 23 │ 'brotliDecompressSync' │ 0 │
└─────────┴────────────────────────┴────────┘
Full result: https://gist.github.com/RafaelGSS/66d33091560d61932b26d74af2fa8b82
Current limitations
- For each benchmark file I require
coverage.js
to set up the monkey patch ofModule.require
(-r ./coverage.js
). However, any benchmark setup would impact the report. For instance:
const bench = common.createBenchmark(main, { ... }});
const fs = require('node:fs')
const files = fs.readdir(...) // readdir and collect files
function main({ ... }) {
bench.start()
for (const file of files) {
fs.existsSync(file)
}
bench.end(files.length)
}
This will result in:
-
fs.existsSync
status "covered" -
fs.readdir
status "covered"
This is unfortunate, as in reality only fs.existsSync
is being measured. One theory that came to my mind to fix that is to intercept the bench.start
calls and only then return the patched modules. I might try it later.
- For simplicity, I'm assuming that all modules export an object containing functions. However, some modules default exports a function, for instance,
typeof require('node:assert') === 'function'
. This also means that classes such asAsyncLocalStorage
(or function classes) aren't covered as the constructor (called withnew ..
) isn't patched. I think usingProxy
might solve it, but I haven't tested yet.
I'm certain there's a better way to approach this goal and fix the limitations. Hence, I'm opening it as a draft.
cc: @nodejs/benchmarking @nodejs/test_runner @lemire