From 8d83c1c60144878f19929e9678186c2313400a2f Mon Sep 17 00:00:00 2001 From: Mathias Buus Date: Wed, 14 Feb 2018 12:01:26 +0100 Subject: [PATCH 1/2] use async_hooks --- README.md | 2 +- cli.js | 1 - core.js | 115 ------------------------------------------------- example.js | 2 +- index.js | 123 ++++++++++++++--------------------------------------- 5 files changed, 34 insertions(+), 209 deletions(-) delete mode 100644 core.js diff --git a/README.md b/README.md index 9284cf7..3fa44e4 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ setTimeout(function () { }, 100) ``` -Run the above script like this: `node --expose-internals example.js`. It will print the following: +Run the above script like this: `node example.js`. It will print the following: ``` There are 4 known handle(s) keeping the process running and 0 unknown diff --git a/cli.js b/cli.js index 54a1525..a5815cb 100755 --- a/cli.js +++ b/cli.js @@ -9,7 +9,6 @@ console.log('probing program', prog) console.log('kill -SIGUSR1', process.pid, 'for logging') var nodeArgs = [ - '--expose-internals', '-r', path.join(__dirname, 'include.js') ] diff --git a/core.js b/core.js deleted file mode 100644 index 424502b..0000000 --- a/core.js +++ /dev/null @@ -1,115 +0,0 @@ -// lifted from node core, https://github.com/nodejs/node/blob/88307974e60346bc98c4e9f70a2b6918ccb6844f/src/node.js -// needed to instrument timers - -exports.NativeModule = NativeModule -exports.globalTimeouts = globalTimeouts - -var ContextifyScript = process.binding('contextify').ContextifyScript; -function runInThisContext(code, options) { - var script = new ContextifyScript(code, options); - return script.runInThisContext(); -} - -function globalTimeouts () { - const timers = NativeModule.require('timers'); - global.clearImmediate = timers.clearImmediate; - global.clearInterval = timers.clearInterval; - global.clearTimeout = timers.clearTimeout; - global.setImmediate = timers.setImmediate; - global.setInterval = timers.setInterval; - global.setTimeout = timers.setTimeout; -}; - -function NativeModule(id) { - this.filename = id + '.js'; - this.id = id; - this.exports = {}; - this.loaded = false; -} - -NativeModule._source = process.binding('natives'); -NativeModule._cache = {}; - -NativeModule.require = function(id) { - if (id == 'native_module') { - return NativeModule; - } - - var cached = NativeModule.getCached(id); - if (cached) { - return cached.exports; - } - - if (!NativeModule.exists(id)) { - throw new Error('No such native module ' + id); - } - - process.moduleLoadList.push('NativeModule ' + id); - - var nativeModule = new NativeModule(id); - - nativeModule.cache(); - nativeModule.compile(); - - return nativeModule.exports; -}; - -NativeModule.getCached = function(id) { - return NativeModule._cache[id]; -}; - -NativeModule.exists = function(id) { - return NativeModule._source.hasOwnProperty(id); -}; - -const EXPOSE_INTERNALS = process.execArgv.some(function(arg) { - return arg.match(/^--expose[-_]internals$/); -}); - -if (EXPOSE_INTERNALS) { - NativeModule.nonInternalExists = NativeModule.exists; - - NativeModule.isInternal = function(id) { - return false; - }; -} else { - NativeModule.nonInternalExists = function(id) { - return NativeModule.exists(id) && !NativeModule.isInternal(id); - }; - - NativeModule.isInternal = function(id) { - return id.startsWith('internal/'); - }; -} - - -NativeModule.getSource = function(id) { - return NativeModule._source[id]; -}; - -NativeModule.wrap = function(script) { - return NativeModule.wrapper[0] + script + NativeModule.wrapper[1]; -}; - -NativeModule.wrapper = [ - '(function (exports, require, module, __filename, __dirname) { ', - '\n});' -]; - -NativeModule.prototype.compile = function() { - var source = NativeModule.getSource(this.id); - source = NativeModule.wrap(source); - - var fn = runInThisContext(source, { - filename: this.filename, - lineOffset: 0, - displayErrors: true - }); - fn(this.exports, require, this, this.filename); - - this.loaded = true; -}; - -NativeModule.prototype.cache = function() { - NativeModule._cache[this.id] = this; -}; diff --git a/example.js b/example.js index 4adb416..4584d4b 100644 --- a/example.js +++ b/example.js @@ -1,4 +1,4 @@ -var log = require('why-is-node-running') +var log = require('./') var net = require('net') function createServer () { diff --git a/index.js b/index.js index bbbb363..8177556 100644 --- a/index.js +++ b/index.js @@ -1,78 +1,40 @@ -var core = require('./core') - -var b = process.binding - -process.binding = function (name) { - var loaded = b(name) - var cpy = {} - - Object.keys(loaded).forEach(function (prop) { - if (typeof loaded[prop] === 'function' && loaded[prop].prototype) { - var wrap = function () { - var handle - - if (arguments.length === 4) handle = new loaded[prop](arguments[0], arguments[1], arguments[2], arguments[3]) - else if (arguments.length === 3) handle = new loaded[prop](arguments[0], arguments[1], arguments[2]) - else if (arguments.length === 2) handle = new loaded[prop](arguments[0], arguments[1]) - else if (arguments.length === 1) handle = new loaded[prop](arguments[0]) - else handle = new loaded[prop]() - - var e = new Error('whatevs') - var stacks = require('stackback')(e) - - handle.__WHY_IS_NODE_RUNNING__ = {stacks: [], wrapped: loaded[prop]} - - for (var i = 1; i < stacks.length; i++) { - handle.__WHY_IS_NODE_RUNNING__.stacks.push(stacks[i]) - } - - return handle - } - - Object.keys(loaded[prop]).forEach(function (name) { - wrap[name] = loaded[prop][name] - }) - - if (/^[A-Z]/.test(prop)) { - cpy[prop] = wrap - } else { - cpy[prop] = loaded[prop] - } - } else { - cpy[prop] = loaded[prop] - } - }) +var asyncHooks = require('async_hooks') +var stackback = require('stackback') +var path = require('path') +var fs = require('fs') +var sep = path.sep + +var active = new Map() +var hook = asyncHooks.createHook({ + init (asyncId, type) { + if (type === 'TIMERWRAP') return + var err = new Error('whatevs') + var stacks = stackback(err) + active.set(asyncId, {type, stacks}) + }, + destroy (asyncId) { + active.delete(asyncId) + } +}) - return cpy -} +hook.enable() +module.exports = whyIsNodeRunning -core.globalTimeouts() +function whyIsNodeRunning (logger) { + if (!logger) logger = console -module.exports = function (logger) { - logger = logger || console - var handles = process._getActiveHandles() - var unknown = [] - var known = [] + hook.disable() + logger.error('There are %d handle(s) keeping the process running', active.size) + for (const o of active.values()) printStacks(o) - handles.forEach(function (handle) { - var stacks = findStacks(handle, 0) - if (stacks) { - known.push(stacks) - } else { - unknown.push(handle) - } - }) - - logger.error('There are %d known handle(s) keeping the process running and %d unknown', known.length, unknown.length) - logger.error('Known handles:\n') - known.forEach(function (obj, i) { - var stacks = obj.stacks - - stacks = stacks.filter(function (s) { - return s.getFileName().indexOf(require('path').sep) > -1 + function printStacks (o) { + var stacks = o.stacks.slice(1).filter(function (s) { + var filename = s.getFileName() + return filename.indexOf(sep) > -1 && filename.indexOf('internal' + sep) !== 0 }) - logger.error('# %s', obj.wrapped.name) + logger.error('') + logger.error('# %s', o.type) if (!stacks[0]) { logger.error('(unknown stack trace)') @@ -85,33 +47,12 @@ module.exports = function (logger) { stacks.forEach(function (s) { var prefix = s.getFileName() + ':' + s.getLineNumber() try { - var src = require('fs').readFileSync(s.getFileName(), 'utf-8').split(/\n|\r\n/) + var src = fs.readFileSync(s.getFileName(), 'utf-8').split(/\n|\r\n/) logger.error(prefix + padding.slice(prefix.length) + ' - ' + src[s.getLineNumber() - 1].trim()) } catch (e) { logger.error(prefix + padding.slice(prefix.length)) } }) } - - logger.error('\nUnknown handles:\n') - - unknown.forEach(function (stack) { - logger.error(stack) - logger.error() - }) - }) -} - -function findStacks (obj, depth) { - if (depth === 10) return null - - var keys = Object.keys(obj) - for (var i = 0; i < keys.length; i++) { - var val = obj[keys[i]] - if (keys[i] === '__WHY_IS_NODE_RUNNING__') return val - if (typeof val === 'object' && val) { - val = findStacks(val, depth + 1) - if (val) return val - } } } From 6b2f32d93a7ca5829caa6d1ee0c4f91f57fe0a74 Mon Sep 17 00:00:00 2001 From: Mathias Buus Date: Wed, 14 Feb 2018 16:20:22 +0100 Subject: [PATCH 2/2] updated docs --- README.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 3fa44e4..8708def 100644 --- a/README.md +++ b/README.md @@ -29,23 +29,26 @@ setTimeout(function () { Run the above script like this: `node example.js`. It will print the following: ``` -There are 4 known handle(s) keeping the process running and 0 unknown -Known handles: +There are 5 handle(s) keeping the process running -# Timer -/Users/maf/dev/node_modules/why-is-node-running/example.js:6 - setInterval(function () {}, 1000) -/Users/maf/dev/node_modules/why-is-node-running/example.js:10 - createServer() +# Timeout +/home/maf/dev/node_modules/why-is-node-running/example.js:6 - setInterval(function () {}, 1000) +/home/maf/dev/node_modules/why-is-node-running/example.js:10 - createServer() -# TCP -/Users/maf/dev/node_modules/why-is-node-running/example.js:7 - server.listen(0) -/Users/maf/dev/node_modules/why-is-node-running/example.js:10 - createServer() +# TCPSERVERWRAP +/home/maf/dev/node_modules/why-is-node-running/example.js:7 - server.listen(0) +/home/maf/dev/node_modules/why-is-node-running/example.js:10 - createServer() -# TCP -/Users/maf/dev/node_modules/why-is-node-running/example.js:7 - server.listen(0) -/Users/maf/dev/node_modules/why-is-node-running/example.js:11 - createServer() +# Timeout +/home/maf/dev/node_modules/why-is-node-running/example.js:6 - setInterval(function () {}, 1000) +/home/maf/dev/node_modules/why-is-node-running/example.js:11 - createServer() -# Timer -/Users/maf/dev/node_modules/why-is-node-running/example.js:13 - setTimeout(function () { +# TCPSERVERWRAP +/home/maf/dev/node_modules/why-is-node-running/example.js:7 - server.listen(0) +/home/maf/dev/node_modules/why-is-node-running/example.js:11 - createServer() + +# Timeout +/home/maf/dev/node_modules/why-is-node-running/example.js:13 - setTimeout(function () { ``` ## CLI