Skip to content

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

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

  1. For each benchmark file I require coverage.js to set up the monkey patch of Module.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.

  1. 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 as AsyncLocalStorage (or function classes) aren't covered as the constructor (called with new ..) isn't patched. I think using Proxy 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

Merge request reports

Loading