diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index fc6d9ccf4f517b..eafd1327bf0ffe 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -8,6 +8,14 @@ const { ObjectGetOwnPropertyDescriptor, SafeMap, StringPrototypeStartsWith, + Date, + DatePrototypeGetFullYear, + DatePrototypeGetMonth, + DatePrototypeGetDate, + DatePrototypeGetHours, + DatePrototypeGetMinutes, + DatePrototypeGetSeconds, + String, globalThis, } = primordials; @@ -365,6 +373,7 @@ function initializeReportSignalHandlers() { function initializeHeapSnapshotSignalHandlers() { const signal = getOptionValue('--heapsnapshot-signal'); + const diagnosticDir = getOptionValue('--diagnostic-dir'); if (!signal) return; @@ -373,7 +382,8 @@ function initializeHeapSnapshotSignalHandlers() { const { writeHeapSnapshot } = require('v8'); function doWriteHeapSnapshot() { - writeHeapSnapshot(); + const heapSnapshotFilename = getHeapSnapshotFilename(diagnosticDir); + writeHeapSnapshot(heapSnapshotFilename); } process.on(signal, doWriteHeapSnapshot); @@ -650,6 +660,31 @@ function markBootstrapComplete() { internalBinding('performance').markBootstrapComplete(); } +// Sequence number for diagnostic filenames +let sequenceNumOfheapSnapshot = 0; + +// To get the HeapSnapshotFilename while using custom diagnosticDir +function getHeapSnapshotFilename(diagnosticDir) { + if (!diagnosticDir) return undefined; + + const date = new Date(); + + const year = DatePrototypeGetFullYear(date); + const month = String(DatePrototypeGetMonth(date) + 1).padStart(2, '0'); + const day = String(DatePrototypeGetDate(date)).padStart(2, '0'); + const hours = String(DatePrototypeGetHours(date)).padStart(2, '0'); + const minutes = String(DatePrototypeGetMinutes(date)).padStart(2, '0'); + const seconds = String(DatePrototypeGetSeconds(date)).padStart(2, '0'); + + const dateString = `${year}${month}${day}`; + const timeString = `${hours}${minutes}${seconds}`; + const pid = process.pid; + const threadId = internalBinding('worker').threadId; + const fileSequence = (++sequenceNumOfheapSnapshot).toString().padStart(3, '0'); + + return `${diagnosticDir}/Heap.${dateString}.${timeString}.${pid}.${threadId}.${fileSequence}.heapsnapshot`; +} + module.exports = { setupUserModules, prepareMainThreadExecution, diff --git a/test/sequential/test-heapdump-flag-custom-dir.js b/test/sequential/test-heapdump-flag-custom-dir.js new file mode 100644 index 00000000000000..573664a1b37f22 --- /dev/null +++ b/test/sequential/test-heapdump-flag-custom-dir.js @@ -0,0 +1,64 @@ +'use strict'; +const common = require('../common'); + + +if (common.isWindows) + common.skip('test not supported on Windows'); + +const assert = require('assert'); + +const validateDateStringInHeapSnapshotFile = (fileName) => { + const currentDate = new Date(); + const currentYear = currentDate.getFullYear(); + const currentMonth = (currentDate.getMonth() + 1).toString().padStart(2, '0'); + const currentDateValue = currentDate.getDate().toString().padStart(2, '0'); + + const expectedDateString = `${currentYear}${currentMonth}${currentDateValue}`; + const fileDateString = fileName.split('.')[1]; + assert.deepStrictEqual(fileDateString, expectedDateString); +}; + +const validateHeapSnapshotFile = () => { + const fs = require('fs'); + + assert.strictEqual(process.listenerCount('SIGUSR2'), 1); + process.kill(process.pid, 'SIGUSR2'); + process.kill(process.pid, 'SIGUSR2'); + + // Asynchronously wait for the snapshot. Use an async loop to be a bit more + // robust in case platform or machine differences throw off the timing. + (function validate() { + const files = fs.readdirSync(process.cwd()); + + if (files.length === 0) + return setImmediate(validate); + + assert.strictEqual(files.length, 2); + + for (let i = 0; i < files.length; i++) { + assert.match(files[i], /^Heap\..+\.heapsnapshot$/); + + // Check the heapsnapshot file contains the date as YYYYMMDD format + validateDateStringInHeapSnapshotFile(files[i]); + + // Check the file is parsable as JSON + JSON.parse(fs.readFileSync(files[i])); + } + })(); +}; + +if (process.argv[2] === 'child') { + validateHeapSnapshotFile(); +} else { + // Modify the timezone. So we can check the file date string still returning correctly. + process.env.TZ = 'America/New_York'; + const { spawnSync } = require('child_process'); + const tmpdir = require('../common/tmpdir'); + + tmpdir.refresh(); + const args = ['--heapsnapshot-signal', 'SIGUSR2', '--diagnostic-dir', tmpdir.path, __filename, 'child']; + const child = spawnSync(process.execPath, args, { cwd: tmpdir.path }); + + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); +}