UNPKG

@nearform/heap-profiler

Version:

Heap dump, sample profiler and allocation timeline generator for Node.

117 lines (89 loc) 3.91 kB
'use strict' const generateHeapSnapshot = require('./snapshot') const generateHeapSamplingProfile = require('./profile') const recordAllocationTimeline = require('./timeline') const AbortController = require('abort-controller') function benchmarkGeneration(logger, type, report, options, cb) { const start = process.hrtime.bigint() report(options, (err, file) => { if (err) { logger.error(`[@nearform/heap-profiler] Heap ${type} generation failed`, err) return cb(err) } const end = Number(process.hrtime.bigint() - start) logger.info(`[@nearform/heap-profiler] Generated heap ${type} file ${file} in ${end / 1e6} ms`) cb(file) }) } module.exports = function installPreloader(logger) { function runTools() { logger.info('[@nearform/heap-profiler] Received SIGUSR2. Starting tools ...') const takeSnapshot = process.env.HEAP_PROFILER_SNAPSHOT !== 'false' const takeProfile = process.env.HEAP_PROFILER_PROFILE !== 'false' const recordTimeline = process.env.HEAP_PROFILER_TIMELINE !== 'false' const snapshotOptions = {} const profilerOptions = {} const timelineOptions = {} if ('HEAP_PROFILER_SNAPSHOT_DESTINATION' in process.env) { snapshotOptions.destination = process.env.HEAP_PROFILER_SNAPSHOT_DESTINATION } if ('HEAP_PROFILER_SNAPSHOT_RUN_GC' in process.env) { profilerOptions.runGC = process.env.HEAP_PROFILER_SNAPSHOT_RUN_GC === 'true' } if ('HEAP_PROFILER_PROFILE_DESTINATION' in process.env) { profilerOptions.destination = process.env.HEAP_PROFILER_PROFILE_DESTINATION } if ('HEAP_PROFILER_PROFILE_INTERVAL' in process.env) { profilerOptions.interval = parseInt(process.env.HEAP_PROFILER_PROFILE_INTERVAL, 10) } if ('HEAP_PROFILER_TIMELINE_DESTINATION' in process.env) { timelineOptions.destination = process.env.HEAP_PROFILER_TIMELINE_DESTINATION } if ('HEAP_PROFILER_TIMELINE_RUN_GC' in process.env) { timelineOptions.runGC = process.env.HEAP_PROFILER_TIMELINE_RUN_GC === 'true' } let toInvoke = takeSnapshot + takeProfile + recordTimeline function onToolEnd() { toInvoke-- if (toInvoke > 0) { return } logger.info('[@nearform/heap-profiler] All tools have completed.') // Resume awaiting on the next start signal process.once('SIGUSR2', runTools) } if (toInvoke === 0) { logger.info('[@nearform/heap-profiler] All tools were disabled.') // Resume awaiting on the next start signal process.once('SIGUSR2', runTools) return } if (takeSnapshot) { benchmarkGeneration(logger, 'snapshot', generateHeapSnapshot, snapshotOptions, onToolEnd) } if (takeProfile) { const controller = new AbortController() profilerOptions.signal = controller.signal const abort = controller.abort.bind(controller) process.once('SIGUSR2', abort) logger.info('[@nearform/heap-profiler] Sampling profiler started. Awaiting SIGUSR2 to stop ...') benchmarkGeneration(logger, 'sampling profile', generateHeapSamplingProfile, profilerOptions, function(err) { process.removeListener('SIGUSR2', abort) onToolEnd(err) }) } if (recordTimeline) { const controller = new AbortController() timelineOptions.signal = controller.signal const abort = controller.abort.bind(controller) process.once('SIGUSR2', abort) logger.info('[@nearform/heap-profiler] Allocation timeline started. Awaiting SIGUSR2 to stop ...') benchmarkGeneration(logger, 'allocation timeline', recordAllocationTimeline, timelineOptions, function(err) { process.removeListener('SIGUSR2', abort) onToolEnd(err) }) } } process.once('SIGUSR2', runTools) logger.info(`[@nearform/heap-profiler] Listening for SIGUSR2 signal on process ${process.pid}.`) }