@nearform/heap-profiler
Version: 
Heap dump, sample profiler and allocation timeline generator for Node.
90 lines (72 loc) • 2.05 kB
JavaScript
const { Session } = require('inspector')
const SonicBoom = require('sonic-boom')
const { ensurePromiseCallback, destinationFile, validateDestinationFile } = require('./utils')
module.exports = function generateHeapSnapshot(options, cb) {
  /* istanbul ignore if */
  if (typeof options === 'function') {
    cb = options
    options = null
  }
  // Prepare the context
  const [callback, promise] = ensurePromiseCallback(cb)
  const { destination, runGC } = Object.assign({ destination: destinationFile('heapsnapshot'), runGC: false }, options)
  const session = new Session()
  let error = null
  let handled = false
  if (typeof destination !== 'string' || destination.length === 0) {
    throw new Error('The destination option must be a non empty string')
  }
  validateDestinationFile(destination, err => {
    if (err) {
      return callback(err)
    }
    const writer = new SonicBoom({ dest: destination })
    function onWriterEnd(err) {
      /* istanbul ignore if */
      if (handled) {
        return
      }
      handled = true
      session.disconnect()
      callback(err || error, destination)
    }
    writer.on('error', onWriterEnd)
    writer.on('close', onWriterEnd)
    if (runGC && typeof global.gc === 'function') {
      try {
        global.gc()
      } catch (e) {
        error = e
        writer.end()
        return promise
      }
    }
    // Start the session
    session.connect()
    // Prepare chunk appending
    session.on('HeapProfiler.addHeapSnapshotChunk', m => {
      // A write failed, discard all the rest
      /* istanbul ignore if */
      if (error) {
        return
      }
      try {
        writer.write(m.params.chunk)
      } catch (e) {
        /* istanbul ignore next */
        error = e
      }
    })
    // Request heap snapshot
    session.post('HeapProfiler.takeHeapSnapshot', null, (err, r) => {
      /* istanbul ignore next */
      if (err && !error) {
        error = err
      }
      // Cleanup
      writer.end()
    })
  })
  return promise
}