diff --git a/injects/sampler.js b/injects/sampler.js index 1d3633da..7459fa14 100644 --- a/injects/sampler.js +++ b/injects/sampler.js @@ -1,22 +1,36 @@ 'use strict' +// Important to keep in mind that every Node.js worker thread +// will run it's own instance of the sample.js. + const fs = require('fs') const makeDir = require('mkdirp') const systemInfo = require('../collect/system-info.js') const ProcessStat = require('../collect/process-stat.js') const getLoggingPaths = require('@nearform/clinic-common').getLoggingPaths('doctor') const ProcessStatEncoder = require('../format/process-stat-encoder.js') +const { isMainThread, threadId } = require('worker_threads') // create encoding files and directory const paths = getLoggingPaths({ path: process.env.NODE_CLINIC_DOCTOR_DATA_PATH, identifier: process.pid }) makeDir.sync(paths['/']) -// write system file -fs.writeFileSync(paths['/systeminfo'], JSON.stringify(systemInfo(), null, 2)) +// write system file only on the main thread +if (isMainThread) { + fs.writeFileSync(paths['/systeminfo'], JSON.stringify(systemInfo(), null, 2)) +} const processStatEncoder = new ProcessStatEncoder() -const out = processStatEncoder.pipe(fs.createWriteStream(paths['/processstat'])) + +// If sampling in a worker thread, we have to write samples +// out to a separate processstat file. +let outpath = paths['/processstat'] +if (!isMainThread) { + outpath += `-${threadId}` +} + +const out = processStatEncoder.pipe(fs.createWriteStream(outpath)) // sample every 10ms const processStat = new ProcessStat(parseInt( diff --git a/test/cmd-collect-analysing-worker.test.js b/test/cmd-collect-analysing-worker.test.js new file mode 100644 index 00000000..07d645f0 --- /dev/null +++ b/test/cmd-collect-analysing-worker.test.js @@ -0,0 +1,37 @@ +'use strict' + +const { test } = require('tap') +const rimraf = require('rimraf') +const ClinicDoctor = require('../index.js') + +test('cmd - test collect - emits "analysing" event', function (t) { + const tool = new ClinicDoctor() + + function cleanup (err, dirname) { + t.ifError(err) + t.match(dirname, /^[0-9]+\.clinic-doctor/) + rimraf(dirname, (err) => { + t.ifError(err) + t.end() + }) + } + + let seenAnalysing = false + tool.on('analysing', () => { + seenAnalysing = true + }) + + const expr = 'setTimeout(() => {}, 123);' + + 'const { Worker } = require("worker_threads");' + + 'new Worker("setTimeout(() => {}, 123);", { eval: true });' + + tool.collect( + [process.execPath, '-e', expr], + function (err, dirname) { + if (err) return cleanup(err, dirname) + + t.ok(seenAnalysing) // should've happened before this callback + cleanup(null, dirname) + } + ) +})