Comparing version 0.2.3 to 0.3.0
262
index.js
@@ -5,5 +5,6 @@ #!/usr/bin/env node | ||
var Socket, Server, Timer; | ||
var Socket, dgramSocket, Server, TlsServer, HttpServer, HttpsServer, Timer, ChildProcess; | ||
function timerCallback(thing) { | ||
if (typeof thing._repeat === 'function') { return '_repeat'; } | ||
if (typeof thing._onTimeout === 'function') { return '_onTimeout'; } | ||
@@ -33,21 +34,84 @@ if (typeof thing._onImmediate === 'function') { return '_onImmediate'; } | ||
// timers call this | ||
var L = require('_linklist'); | ||
var _L_append = L.append; | ||
L.append = function (list, item) { | ||
if (list instanceof Timer || (typeof list === 'object' && list.hasOwnProperty('_idleNext'))) { | ||
if (item && timerCallback(item)) { | ||
var stack = __stack; | ||
for (var i = 5; i < stack.length; i++) { | ||
if (/\//.test(stack[i].file)) { | ||
item[timerCallback(item)].__callSite = stack[i]; | ||
break; | ||
} | ||
} | ||
} else { | ||
console.log('Uncertain what to do with item:', item); | ||
function findCallsite(stack) { | ||
for (var i = 0; i < stack.length; i++) { | ||
if (stack[i].file !== __filename && /\//.test(stack[i].file)) { | ||
return stack[i]; | ||
} | ||
} | ||
return _L_append.apply(this, arguments); | ||
return null; | ||
} | ||
// wraps a function with a proxy function holding the first userland call | ||
// site in the stack and some other information, for later display | ||
// this will probably screw up any code that depends on the callbacks having | ||
// a 'name' or 'length' property that is accurate, but there doesn't appear | ||
// to be a way around that :( | ||
var consolelog = console.log.bind(console); | ||
function wrapFn(fn, name, isInterval) { | ||
if (typeof fn !== 'function') { return fn; } | ||
var wrapped = function () { | ||
return fn.apply(this, arguments); | ||
}; | ||
var stack = __stack; | ||
// this should inherit 'name' and 'length' and any other properties that have been assigned | ||
Object.getOwnPropertyNames(fn).forEach(function (key) { | ||
try { | ||
Object.defineProperty(wrapped, key, Object.getOwnPropertyDescriptor(fn, key)); | ||
} catch (e) { | ||
// some properties cannot be redefined, not much we can do about it | ||
} | ||
}); | ||
// we use these later to identify the source information about an open handle | ||
Object.defineProperties(wrapped, { | ||
__fullStack: { | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
value: stack | ||
}, | ||
__name: { | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
value: name || '(anonymous)' | ||
}, | ||
__callSite: { | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
value: findCallsite(stack) | ||
}, | ||
__isInterval: { | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
value: isInterval | ||
} | ||
}); | ||
return wrapped; | ||
} | ||
var GLOBALS = { }; | ||
function wrapTimer(type, isInterval) { | ||
GLOBALS[type] = global[type]; | ||
global[type] = function () { | ||
var args = [ ], i = arguments.length; | ||
while (i--) { args[i] = arguments[i]; } | ||
var ret = GLOBALS[type].apply(this, args); | ||
var cbkey = timerCallback(ret); | ||
if (ret[cbkey]) { | ||
ret[cbkey] = wrapFn(ret[cbkey], args[0].name, isInterval); | ||
} | ||
return ret; | ||
}; | ||
}; | ||
wrapTimer('setTimeout', false); | ||
wrapTimer('setInterval', true); | ||
@@ -58,19 +122,13 @@ var EventEmitter = require('events').EventEmitter; | ||
EventEmitter.prototype.on = | ||
EventEmitter.prototype.addListener = function (type, listener) { | ||
EventEmitter.prototype.addListener = function (/*type, listener*/) { | ||
var stack = __stack; | ||
listener.__fullStack = stack; | ||
if (stack[3] && stack[3].file === 'events.js') { | ||
listener.__callSite = stack[6]; // inline listener binding on net.connect | ||
var args = [ ], i = arguments.length; | ||
while (i--) { args[i] = arguments[i]; } | ||
if (typeof args[1] === 'function') { | ||
args[1] = wrapFn(args[1], args[1].name, null); | ||
} | ||
else if (stack[4] && /express\/lib\/application\.js$/.test(stack[4].file)) { | ||
listener.__callSite = stack[5]; // express 4 | ||
} | ||
else if (stack[3] && stack[3].file === 'http.js') { | ||
listener.__callSite = stack[4]; // http.createServer(fn) | ||
} | ||
else { | ||
listener.__callSite = stack[3]; | ||
} | ||
return _EventEmitter_addListener.apply(this, arguments); | ||
return _EventEmitter_addListener.apply(this, args); | ||
}; | ||
@@ -81,4 +139,20 @@ })(); | ||
Server = require('net').Server; | ||
TlsServer = require('tls').Server; | ||
HttpServer = require('http').Server; | ||
HttpsServer = require('https').Server; | ||
Timer = process.binding('timer_wrap').Timer; | ||
dgramSocket = require('dgram').Socket; | ||
ChildProcess = (function () { | ||
var ChildProcess = require('child_process').ChildProcess; | ||
if (typeof ChildProcess !== 'function') { | ||
// node 0.10 doesn't expose the ChildProcess constructor, so we have to get it on the sly | ||
var cp = require('child_process').spawn('true', { stdio: 'ignore' }); | ||
ChildProcess = cp.constructor; | ||
} | ||
return ChildProcess; | ||
})(); | ||
function formatTime(t) { | ||
@@ -103,11 +177,18 @@ var labels = ['ms', 's', 'min', 'hr'], | ||
console.log('[WTF Node?] open handles:'); | ||
var sockets = [ ], fds = [ ], servers = [ ], _timers = [ ], other = [ ]; | ||
// sort the active handles into different types for logging | ||
var sockets = [ ], fds = [ ], servers = [ ], _timers = [ ], processes = [ ], other = [ ]; | ||
process._getActiveHandles().forEach(function (h) { | ||
if (h instanceof Socket) { | ||
if (h.fd) { fds.push(h); } | ||
else { sockets.push(h); } | ||
// stdin, stdout, etc. are now instances of socket and get listed in open handles | ||
// todo: a more specific/better way to identify them than the 'fd' property | ||
if (h.fd) { fds.push(h); } | ||
else { sockets.push(h); } | ||
} | ||
else if (h instanceof Server) { servers.push(h); } | ||
else if (h instanceof dgramSocket) { servers.push(h); } | ||
else if (h instanceof Timer) { _timers.push(h); } | ||
else if (h instanceof ChildProcess) { processes.push(h); } | ||
// catchall | ||
else { other.push(h); } | ||
@@ -126,2 +207,21 @@ }); | ||
} | ||
if (processes.length) { | ||
console.log('- Child processes'); | ||
processes.forEach(function (cp) { | ||
var fds = [ ]; | ||
console.log(' - PID %s', cp.pid); | ||
if (cp.stdio && cp.stdio.length) { | ||
cp.stdio.forEach(function (s) { | ||
if (s._handle && s._handle.fd) { fds.push(s._handle.fd); } | ||
var idx = sockets.indexOf(s); | ||
if (idx > -1) { | ||
sockets.splice(idx, 1); | ||
} | ||
}); | ||
console.log(' - STDIO file descriptors:', fds.join(', ')); | ||
} | ||
}); | ||
} | ||
@@ -133,11 +233,15 @@ if (sockets.length) { | ||
console.log(' - (?:?) -> %s:? (destroyed)', s._host); | ||
} else if (s.localAddress) { | ||
console.log(' - %s:%s -> %s:%s', s.localAddress, s.localPort, s.remoteAddress, s.remotePort); | ||
} else if (s._handle && s._handle.fd) { | ||
console.log(' - fd %s', s._handle.fd); | ||
} else { | ||
console.log(' - %s:%s -> %s:%s', s.localAddress, s.localPort, s.remoteAddress, s.remotePort); | ||
console.log(' - unknown socket'); | ||
} | ||
var connectListeners = s.listeners('connect'); | ||
if (connectListeners) { | ||
if (connectListeners && connectListeners.length) { | ||
console.log(' - Listeners:'); | ||
connectListeners.forEach(function (fn) { | ||
var callSite = getCallsite(fn); | ||
console.log(' - %s: %s @ %s:%d', 'connect', fn.name || 'anonymous', callSite.file, callSite.line); | ||
console.log(' - %s: %s @ %s:%d', 'connect', fn.name || '(anonymous)', callSite.file, callSite.line); | ||
}); | ||
@@ -151,11 +255,35 @@ } | ||
servers.forEach(function (s) { | ||
var a = s.address(); | ||
console.log(' - %s:%s', a.address, a.port); | ||
var type = 'unknown type'; | ||
if (s instanceof HttpServer) { type = 'HTTP'; } | ||
else if (s instanceof HttpsServer) { type = 'HTTPS'; } | ||
else if (s instanceof TlsServer) { type = 'TLS'; } | ||
else if (s instanceof Server) { type = 'TCP'; } | ||
else if (s instanceof dgramSocket) { type = 'UDP'; } | ||
var connectListeners = s.listeners('connection'); | ||
if (connectListeners) { | ||
try { | ||
var a = s.address(); | ||
} catch (e) { | ||
if (type === 'UDP') { | ||
// udp sockets that haven't been bound will throw, but won't prevent exit | ||
return; | ||
} | ||
throw e; | ||
} | ||
console.log(' - %s:%s (%s)', a.address, a.port, type); | ||
var eventType = ( | ||
type === 'HTTP' || type === 'HTTPS' ? 'request' : | ||
type === 'TCP' || type === 'TLS' ? 'connection' : | ||
type === 'UDP' ? 'message' : | ||
'connection' | ||
); | ||
var listeners = s.listeners(eventType); | ||
if (listeners && listeners.length) { | ||
console.log(' - Listeners:'); | ||
connectListeners.forEach(function (fn) { | ||
listeners.forEach(function (fn) { | ||
var callSite = getCallsite(fn); | ||
console.log(' - %s: %s @ %s:%d', 'connection', fn.name || 'anonymous', callSite.file, callSite.line); | ||
console.log(' - %s: %s @ %s:%d', eventType, fn.__name || fn.name || '(anonymous)', callSite.file, callSite.line); | ||
}); | ||
@@ -166,10 +294,16 @@ } | ||
var timers = [ ]; | ||
var timers = [ ], intervals = [ ]; | ||
_timers.forEach(function (t) { | ||
var timer = t._list, cb, cbkey; | ||
if (t._list) { | ||
// node v5ish behavior | ||
var timer = t._list; | ||
do { | ||
if (timerCallback(timer) && timers.indexOf(timer) === -1) { | ||
timers.push(timer); | ||
cbkey = timerCallback(timer); | ||
if (cbkey && timers.indexOf(timer) === -1) { | ||
cb = timer[cbkey]; | ||
if (cb.__isInterval || cbkey === '_repeat') { | ||
intervals.push(timer); | ||
} else { | ||
timers.push(timer); | ||
} | ||
} | ||
@@ -186,4 +320,10 @@ timer = timer._idleNext; | ||
} | ||
if (timerCallback(timer) && timers.indexOf(timer) === -1) { | ||
timers.push(timer); | ||
cbkey = timerCallback(timer); | ||
if (cbkey && timers.indexOf(timer) === -1) { | ||
cb = timer[cbkey] | ||
if (cb.__isInterval) { | ||
intervals.push(timer); | ||
} else { | ||
timers.push(timer); | ||
} | ||
} | ||
@@ -200,8 +340,26 @@ } | ||
timers.forEach(function (t) { | ||
var fn = t._onTimeout, | ||
var fn = t[timerCallback(t)], | ||
callSite = getCallsite(fn); | ||
console.log(' - (%d ~ %s) %s @ %s:%d', t._idleTimeout, formatTime(t._idleTimeout), fn.name, callSite.file, callSite.line); | ||
if (fn.__name) { | ||
console.log(' - (%d ~ %s) %s @ %s:%d', t._idleTimeout, formatTime(t._idleTimeout), fn.__name, callSite.file, callSite.line); | ||
} else { | ||
console.log(' - (%d ~ %s) %s @ %s:%d', t._idleTimeout, formatTime(t._idleTimeout), fn.name, callSite.file, callSite.line); | ||
} | ||
}); | ||
} | ||
if (intervals.length) { | ||
console.log('- Intervals:'); | ||
intervals.forEach(function (t) { | ||
var fn = t[timerCallback(t)], | ||
callSite = getCallsite(fn); | ||
if (fn.__name) { | ||
console.log(' - (%d ~ %s) %s @ %s:%d', t._idleTimeout, formatTime(t._idleTimeout), fn.__name, callSite.file, callSite.line); | ||
} else { | ||
console.log(' - (%d ~ %s) %s @ %s:%d', t._idleTimeout, formatTime(t._idleTimeout), fn.name, callSite.file, callSite.line); | ||
} | ||
}); | ||
} | ||
if (other.length) { | ||
@@ -208,0 +366,0 @@ console.log('- Others:'); |
{ | ||
"name": "wtfnode", | ||
"version": "0.2.3", | ||
"version": "0.3.0", | ||
"description": "Utility to help find out why Node isn't exiting", | ||
@@ -5,0 +5,0 @@ "repository": { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 4 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
20409
7
377
2
14