├── assets └── mixed.png ├── .gitignore ├── .travis.yml ├── .npmignore ├── examples ├── http.js ├── setInterval.js ├── setTimeout.js ├── net.js └── mixed.js ├── LICENSE ├── package.json ├── test ├── opts.js ├── single-timeout.js └── single-interval.js ├── core.js ├── index.js └── README.md /assets/mixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thlorenz/active-handles/HEAD/assets/mixed.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.12" 5 | # Travis currently has problems with node-gyp for io.js 6 | # - "iojs" 7 | 8 | before_install: 9 | - npm install -g npm@~1.4.6 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | assets/ 17 | .travis.yml 18 | spikes 19 | examples 20 | -------------------------------------------------------------------------------- /examples/http.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var activeHandles = require('../'); 4 | var http = require('http'); 5 | var PORT = 3000; 6 | 7 | function onrequest(req, res) { 8 | res.writeHead(200, { 'Content-Type': 'text/plain' }); 9 | res.end('hello world\r\n'); 10 | activeHandles.print(); 11 | } 12 | 13 | function onlistening() { 14 | console.error('Listening on localhost %d', PORT); 15 | } 16 | 17 | var server = http.createServer(); 18 | server 19 | .on('error', console.error) 20 | .on('request', onrequest) 21 | .on('listening', onlistening) 22 | .listen(PORT); 23 | 24 | 25 | function onclientResponse(res) { 26 | console.log('\n\n--------------\nStatus: %d', res.statusCode); 27 | server.close(); 28 | } 29 | 30 | http 31 | .get('http://localhost:' + PORT) 32 | .on('error', console.error) 33 | .on('response', onclientResponse) 34 | -------------------------------------------------------------------------------- /examples/setInterval.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var activeHandles = require('../'); 4 | // only needed for <= v1.6.2 5 | activeHandles.hookSetInterval(); 6 | var iv1, iv2, iv3 7 | 8 | function onTimeout() { 9 | // here the function declaration is named, easy enough 10 | clearInterval(iv1); 11 | } 12 | 13 | global.onOtherTimeout = function () { 14 | // global functions are bad, even for timeouts ;) 15 | // let's fix that ;) 16 | delete global.onOtherTimeout; 17 | clearInterval(iv2); 18 | } 19 | 20 | function Me() {} 21 | Me.prototype.timeout = function () { 22 | // assigning to prototype property results in 23 | // the function name being derived from the 24 | // prototype name and the property name 25 | clearInterval(iv3); 26 | } 27 | 28 | iv1 = setInterval(onTimeout, 20); 29 | iv2 = setInterval(global.onOtherTimeout, 10); 30 | iv3 = setInterval(new Me().timeout, 30); 31 | 32 | activeHandles.print(); 33 | -------------------------------------------------------------------------------- /examples/setTimeout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var activeHandles = require('../'); 4 | 5 | function onTimeout() { 6 | // here the function declaration is named, easy enough 7 | } 8 | 9 | global.onOtherTimeout = function () { 10 | // global functions are bad, even for timeouts ;) 11 | // let's fix that ;) 12 | delete global.onOtherTimeout; 13 | } 14 | 15 | function Me() {} 16 | Me.prototype.timeout = function () { 17 | // assigning to prototype property results in 18 | // the function name being derived from the 19 | // prototype name and the property name 20 | } 21 | 22 | var assignedTimeout = function () { 23 | // here the name the function was assigned to 24 | // is returned as the function name 25 | } 26 | 27 | setTimeout(onTimeout, 10); 28 | setTimeout(global.onOtherTimeout, 10); 29 | setTimeout(new Me().timeout, 20); 30 | setTimeout(assignedTimeout, 5); 31 | setTimeout(function () { 32 | // inline anonymous function 33 | }, 10); 34 | 35 | activeHandles.print(); 36 | -------------------------------------------------------------------------------- /examples/net.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var activeHandles = require('../'); 4 | var net = require('net'); 5 | var PORT = 3000; 6 | 7 | function onconnect(c) { 8 | c.write('hello world\r\n'); 9 | c.pipe(c); 10 | activeHandles.print(); 11 | } 12 | 13 | function onlistening() { 14 | console.error('Listening on localhost %d', PORT); 15 | } 16 | 17 | var server = net.createServer() 18 | server 19 | .on('connection', onconnect) 20 | .on('error', console.error) 21 | .on('listening', onlistening) 22 | .listen(PORT) 23 | 24 | function onclientConnect() { 25 | client.write('hola mundo\r\n'); 26 | } 27 | 28 | function onclientData(data) { 29 | console.log('\n\n--------------\n%s', data.toString()); 30 | client.end(); 31 | } 32 | 33 | function onclientEnd() { 34 | console.log('disconnected from server'); 35 | server.close(); 36 | } 37 | 38 | var client = net 39 | .connect({ port: PORT }) 40 | .on('error', console.error) 41 | .on('connection', onclientConnect) 42 | .on('data', onclientData) 43 | .on('end', onclientEnd); 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 Thorsten Lorenz. 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation 6 | files (the "Software"), to deal in the Software without 7 | restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /examples/mixed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var activeHandles = require('../'); 4 | var http = require('http'); 5 | var PORT = 3000; 6 | var iv; 7 | 8 | // setInterval 9 | function Me() {} 10 | Me.prototype.timeout = function () { 11 | // assigning to prototype property results in 12 | // the function name being derived from the 13 | // prototype name and the property name 14 | clearInterval(iv); 15 | } 16 | iv = setInterval(new Me().timeout, 100); 17 | 18 | function onrequest(req, res) { 19 | res.writeHead(200, { 'Content-Type': 'text/plain' }); 20 | res.end('hello world\r\n'); 21 | activeHandles.print(); 22 | } 23 | 24 | // setTimeout 25 | var assignedTimeout = function () { 26 | // here the name the function was assigned to 27 | // is returned as the function name 28 | } 29 | 30 | setTimeout(assignedTimeout, 50); 31 | 32 | // http connection 33 | function onlistening() { 34 | // do nothing, we'll log active handles 35 | // when we get a request 36 | } 37 | 38 | var server = http.createServer(); 39 | server 40 | .on('error', console.error) 41 | .on('request', onrequest) 42 | .on('listening', onlistening) 43 | .listen(PORT); 44 | 45 | 46 | function onclientResponse(res) { 47 | server.close(); 48 | } 49 | 50 | http 51 | .get('http://localhost:' + PORT) 52 | .on('error', console.error) 53 | .on('response', onclientResponse) 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "active-handles", 3 | "version": "1.1.0", 4 | "description": "Prints out information about the process's active handles, including function source and location", 5 | "main": "index.js", 6 | "scripts": { 7 | "test-main": "tape test/*.js", 8 | "test-0.10": " (cd ./node_modules/function-origin && nave use 0.10 node-gyp rebuild) && nave use 0.10 npm run test-main", 9 | "test-0.12": " (cd ./node_modules/function-origin && nave use 0.12 node-gyp rebuild) && nave use 0.12 npm run test-main", 10 | "test-iojs": " (cd ./node_modules/function-origin && nave use latest node-gyp rebuild) && nave use latest npm run test-main", 11 | "test-all": "npm run test-main && npm run test-0.10 && npm run test-0.12 && npm run test-iojs", 12 | "test": "if [ -e $TRAVIS ]; then (cd ./node_modules/function-origin && node-gyp rebuild) && npm run test-all; else npm run test-main; fi" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/thlorenz/active-handles.git" 17 | }, 18 | "homepage": "https://github.com/thlorenz/active-handles", 19 | "dependencies": { 20 | "ansicolors": "~0.3.2", 21 | "cardinal": "~0.5.0", 22 | "function-origin": "~1.1.0", 23 | "xtend": "~4.0.0" 24 | }, 25 | "devDependencies": { 26 | "nave": "~0.5.1", 27 | "tape": "~4.0.0" 28 | }, 29 | "keywords": [ 30 | "active", 31 | "handles", 32 | "debug", 33 | "profile", 34 | "leak" 35 | ], 36 | "author": { 37 | "name": "Thorsten Lorenz", 38 | "email": "thlorenz@gmx.de", 39 | "url": "http://thlorenz.com" 40 | }, 41 | "license": { 42 | "type": "MIT", 43 | "url": "https://github.com/thlorenz/active-handles/blob/master/LICENSE" 44 | }, 45 | "engine": { 46 | "node": ">=0.10" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/opts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | var activeHandles = require('../') 5 | var core = require('../core') 6 | var xtend = require('xtend') 7 | var TIMEOUT = 20 8 | 9 | function inspect(obj, depth) { 10 | console.error(require('util').inspect(obj, false, depth || 5, true)); 11 | } 12 | 13 | function insp(obj, depth) { 14 | return require('util').inspect(obj, false, depth || 5, true); 15 | } 16 | 17 | inspect(process.versions); 18 | 19 | function checkOne(t, opts) { 20 | function timeout() { t.end() } 21 | setTimeout(timeout, TIMEOUT); 22 | 23 | var fn = timeout 24 | , name = 'timeout' 25 | , line = 20 26 | 27 | var firstHandle = process._getActiveHandles()[0]; 28 | var handles = activeHandles(opts); 29 | var h = handles[0] 30 | , l = h.location; 31 | 32 | // xtend after the call to ensure it is xtended in impl as well 33 | opts = xtend(core.defaultOpts, opts) 34 | 35 | t.equal(handles.length, 1, 'returns one handle') 36 | t.equal(h.msecs, TIMEOUT, 'reports correct timeout') 37 | t.equal(h.name, name, 'resolves function name correctly') 38 | t.equal(h.type, 'setTimeout', 'identifies type as setTimeout') 39 | t.equal(l.file, __filename, 'location has correct filename') 40 | t.equal(l.line, line, 'location has correct line') 41 | 42 | if(opts.source || opts.highlight) 43 | t.equal(h.source, fn.toString(), 'includes function source') 44 | else 45 | t.ok(!h.source, 'does not include function source') 46 | 47 | if (opts.highlight) t.ok(h.highlighted, 'includes highlighted function source') 48 | else t.ok(!h.highlighted, 'does not include highlighted function source') 49 | 50 | if (opts.attachHandle) t.equal(h.handle && h.handle.msecs, firstHandle.msecs, 'attaches handle to result') 51 | else t.ok(!h.handle, 'does not attach handle to result') 52 | } 53 | 54 | var opts1 = { highlight: false } 55 | test('\nsetting timeout with named handle, options:' + insp(opts1), function (t) { 56 | checkOne(t, opts1) 57 | }) 58 | 59 | var opts2 = { source: false } 60 | test('\nsetting timeout with named handle, options:' + insp(opts2), function (t) { 61 | checkOne(t, opts2) 62 | }) 63 | 64 | var opts3 = { source: false, highlight: false } 65 | test('\nsetting timeout with named handle, options:' + insp(opts3), function (t) { 66 | checkOne(t, opts3) 67 | }) 68 | 69 | var opts4 = { attachHandle: true } 70 | test('\nsetting timeout with named handle, options:' + insp(opts4), function (t) { 71 | checkOne(t, opts4) 72 | }) 73 | -------------------------------------------------------------------------------- /test/single-timeout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | var activeHandles = require('../') 5 | var TIMEOUT = 20 6 | 7 | function inspect(obj, depth) { 8 | console.error(require('util').inspect(obj, false, depth || 5, true)); 9 | } 10 | 11 | inspect(process.versions); 12 | 13 | function checkOne(t, fn, name, line) { 14 | 15 | var handles = activeHandles(); 16 | var h = handles[0] 17 | , l = h.location; 18 | 19 | t.equal(handles.length, 1, 'returns one handle') 20 | t.equal(h.msecs, TIMEOUT, 'reports correct timeout') 21 | t.equal(h.name, name, 'resolves function name correctly') 22 | t.equal(h.source, fn.toString(), 'includes function source') 23 | t.equal(h.type, 'setTimeout', 'identifies type as setTimeout') 24 | t.equal(l.file, __filename, 'location has correct filename') 25 | t.equal(l.line, line, 'location has correct line') 26 | } 27 | 28 | test('\nsetting timeout with named handle', function (t) { 29 | function timeout() { t.end() } 30 | 31 | t.equal(activeHandles().length, 0, 'initially no handles are active') 32 | 33 | setTimeout(timeout, TIMEOUT); 34 | checkOne(t, timeout, 'timeout', 29) 35 | }) 36 | 37 | test('\nsetting timeout with handle assigned to var', function (t) { 38 | var timeout = function () { t.end() } 39 | 40 | t.equal(activeHandles().length, 0, 'initially no handles are active') 41 | 42 | setTimeout(timeout, TIMEOUT); 43 | checkOne(t, timeout, 'timeout', 38) 44 | }) 45 | 46 | test('\nsetting timeout with handle assigned to global', function (t) { 47 | global.timeout = function () { t.end() } 48 | 49 | t.equal(activeHandles().length, 0, 'initially no handles are active') 50 | 51 | setTimeout(global.timeout, TIMEOUT); 52 | checkOne(t, global.timeout, 'global.timeout', 47) 53 | }) 54 | 55 | test('\nsetting timeout with handle assigned to a prototype', function (t) { 56 | function Me() {} 57 | Me.prototype.timeout = function () { t.end() } 58 | 59 | t.equal(activeHandles().length, 0, 'initially no handles are active') 60 | 61 | setTimeout(Me.prototype.timeout, TIMEOUT); 62 | checkOne(t, Me.prototype.timeout, 'Me.timeout', 57) 63 | }) 64 | 65 | test('\nsetting timeout with inlined handle unnamed', function (t) { 66 | t.equal(activeHandles().length, 0, 'initially no handles are active') 67 | 68 | setTimeout(function () { /* name your functions ;) */ t.end() }, TIMEOUT); 69 | checkOne(t, function () { /* name your functions ;) */ t.end() }, '__unknown_function_name__', 68) 70 | }) 71 | 72 | test('\nsetting timeout with inlined handle named', function (t) { 73 | t.equal(activeHandles().length, 0, 'initially no handles are active') 74 | 75 | setTimeout(function foo() { t.end() }, TIMEOUT); 76 | checkOne(t, function foo() { t.end() }, 'foo', 75) 77 | }) 78 | -------------------------------------------------------------------------------- /test/single-interval.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | var activeHandles = require('../') 5 | var TIMEOUT = 20 6 | 7 | function inspect(obj, depth) { 8 | console.error(require('util').inspect(obj, false, depth || 5, true)); 9 | } 10 | 11 | inspect(process.versions); 12 | 13 | activeHandles.hookSetInterval(); 14 | 15 | function checkOne(t, fn, name, line) { 16 | 17 | var handles = activeHandles(); 18 | var h = handles[0] 19 | , l = h.location; 20 | 21 | t.equal(handles.length, 1, 'returns one handle') 22 | t.equal(h.msecs, TIMEOUT, 'reports correct timeout') 23 | t.equal(h.name, name, 'resolves function name correctly') 24 | t.equal(h.source, fn.toString(), 'includes function source') 25 | t.equal(h.type, 'setInterval', 'identifies type as setInterval') 26 | t.equal(l.file, __filename, 'location has correct filename') 27 | t.equal(l.line, line, 'location has correct line') 28 | } 29 | 30 | test('\nsetting interval with named handle', function (t) { 31 | function timeout() { clearInterval(iv); t.end() } 32 | 33 | t.equal(activeHandles().length, 0, 'initially no handles are active') 34 | 35 | var iv = setInterval(timeout, TIMEOUT); 36 | checkOne(t, timeout, 'timeout', 31) 37 | }) 38 | 39 | test('\nsetting interval with handle assigned to var', function (t) { 40 | var timeout = function () { clearInterval(iv); t.end() } 41 | 42 | t.equal(activeHandles().length, 0, 'initially no handles are active') 43 | 44 | var iv = setInterval(timeout, TIMEOUT); 45 | checkOne(t, timeout, 'timeout', 40) 46 | }) 47 | 48 | test('\nsetting interval with handle assigned to global', function (t) { 49 | global.timeout = function () { clearInterval(iv); t.end() } 50 | 51 | t.equal(activeHandles().length, 0, 'initially no handles are active') 52 | 53 | var iv = setInterval(global.timeout, TIMEOUT); 54 | checkOne(t, global.timeout, 'global.timeout', 49) 55 | }) 56 | 57 | test('\nsetting interval with handle assigned to a prototype', function (t) { 58 | function Me() {} 59 | Me.prototype.timeout = function () { clearInterval(iv); t.end() } 60 | 61 | t.equal(activeHandles().length, 0, 'initially no handles are active') 62 | 63 | var iv = setInterval(Me.prototype.timeout, TIMEOUT); 64 | checkOne(t, Me.prototype.timeout, 'Me.timeout', 59) 65 | }) 66 | 67 | test('\nsetting interval with inlined handle unnamed', function (t) { 68 | t.equal(activeHandles().length, 0, 'initially no handles are active') 69 | 70 | var iv = setInterval(function () { /* name your functions ;) */ clearInterval(iv); t.end() }, TIMEOUT); 71 | checkOne(t, function () { /* name your functions ;) */ clearInterval(iv); t.end() }, '__unknown_function_name__', 70) 72 | }) 73 | 74 | test('\nsetting interval with inlined handle named', function (t) { 75 | t.equal(activeHandles().length, 0, 'initially no handles are active') 76 | 77 | var iv = setInterval(function foo() { clearInterval(iv); t.end() }, TIMEOUT); 78 | checkOne(t, function foo() { clearInterval(iv); t.end() }, 'foo', 77) 79 | }) 80 | -------------------------------------------------------------------------------- /core.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var getFunctionLocation = require('function-origin') 4 | 5 | function functionInfo(fn, handle, opts) { 6 | var name; 7 | var src, highlighted = null; 8 | 9 | var location = getFunctionLocation(fn) 10 | // v8 zero bases lines 11 | if (location) location.line++; 12 | 13 | // handle anonymous functions and try to figure out a meaningful function name 14 | var anonymous = false; 15 | if (!fn.name || !fn.name.length) { 16 | name = location.inferredName && location.inferredName.length 17 | ? location.inferredName 18 | : '__unknown_function_name__'; 19 | 20 | anonymous = true; 21 | } else { 22 | name = fn.name; 23 | } 24 | 25 | if (opts.highlight || opts.source) src = fn.toString(); 26 | 27 | var ret = { 28 | fn : fn 29 | , name : name 30 | , location : location 31 | , anonymous : anonymous 32 | } 33 | 34 | if (opts.highlight || opts.source) ret.source = fn.toString(); 35 | if (opts.attachHandle) ret.handle = handle; 36 | 37 | return ret; 38 | } 39 | 40 | function resolveHandle(handle, opts) { 41 | var visited = {} 42 | , resolvedFns = {} 43 | , resolved = [] 44 | , fn 45 | , addedInfo 46 | 47 | function addInfo(fn) { 48 | if (resolvedFns[fn]) return; 49 | resolvedFns[fn] = true; 50 | 51 | var info = functionInfo(fn, handle, opts); 52 | resolved.push(info); 53 | return info; 54 | } 55 | 56 | // timer handles created via setTimeout or setInterval 57 | for (var next = handle._idleNext; !!next && !visited[next]; next = next._idleNext) { 58 | visited[next] = true; 59 | var repeatIsFn = typeof next._repeat === 'function'; 60 | var hasWrappedCallback = typeof next._wrappedCallback === 'function'; 61 | 62 | if (!repeatIsFn && !hasWrappedCallback && !next.hasOwnProperty('_onTimeout')) continue; 63 | 64 | // starting with io.js 1.6.2 when using setInterval the timer handle's 65 | // _repeat property references the wrapped function so we prefer that 66 | fn = repeatIsFn 67 | ? next._repeat 68 | : hasWrappedCallback ? next._wrappedCallback : next._onTimeout; 69 | 70 | addedInfo = addInfo(fn, next); 71 | addedInfo.msecs = next._idleTimeout; 72 | addedInfo.type = hasWrappedCallback || repeatIsFn ? 'setInterval' : 'setTimeout'; 73 | } 74 | 75 | function addHandleFn(key) { 76 | var value = handle._handle[key]; 77 | if (typeof value !== 'function') return; 78 | var addedInfo = addInfo(value, handle._handle); 79 | if (handle._handle.fd) { 80 | addedInfo.fd = handle._handle.fd; 81 | 82 | switch (key) { 83 | case 'onconnection': 84 | addedInfo.type = 'net connection'; 85 | break; 86 | case 'onread': 87 | addedInfo.type = 'net client connection'; 88 | break; 89 | default: 90 | addInfo.type = 'unknown'; 91 | 92 | } 93 | } 94 | } 95 | // handles created by the net module via direct use or of http/https 96 | if (handle._handle) 97 | Object.keys(handle._handle).forEach(addHandleFn); 98 | 99 | return resolved; 100 | } 101 | 102 | function resolveHandles(opts) { 103 | var tasks = opts.handles.length; 104 | var resolvedHandles = []; 105 | 106 | function pushHandle(h) { 107 | resolvedHandles.push(h); 108 | } 109 | 110 | function resolveCurrentHandle(handle) { 111 | var resolved = resolveHandle(handle, opts); 112 | resolved.forEach(pushHandle); 113 | } 114 | 115 | opts.handles.forEach(resolveCurrentHandle); 116 | return resolvedHandles; 117 | } 118 | 119 | var defaultOpts = { 120 | source: true, highlight: true, attachHandle: false 121 | } 122 | 123 | var exports = module.exports = function coreActiveHandles(opts) { 124 | opts = opts || defaultOpts; 125 | opts.handles = opts.handles || process._getActiveHandles(); 126 | if (!opts.handles.length) return []; 127 | return resolveHandles(opts); 128 | } 129 | 130 | exports.defaultOpts = defaultOpts; 131 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var cardinal = require('cardinal') 4 | , xtend = require('xtend') 5 | , colors = require('ansicolors') 6 | , format = require('util').format 7 | , core = require('./core') 8 | , indexes 9 | 10 | function highlightSource(s) { 11 | try { 12 | return cardinal.highlight(s, { lineno: true }); 13 | } catch (e) { 14 | return s; 15 | } 16 | } 17 | 18 | function addHighlight(info) { 19 | // function () { ... is not by itself parsable 20 | // x = function () { .. is 21 | info.highlighted = info.anonymous 22 | ? highlightSource(info.name + ' = ' + info.source) 23 | : highlightSource(info.source); 24 | } 25 | 26 | function versionGreaterEqualOneSixTwo(v) { 27 | var digits = v.slice(1).split('.'); 28 | if (digits.length !== 3) return false; // can't be sure 29 | if (digits[0] < 1 || digits[1] < 6 || digits[2] < 2) return false; 30 | return true; 31 | } 32 | 33 | /** 34 | * Gathers information about all currently active handles. 35 | * Active handles are obtained via `process._getActiveHandles` 36 | * and location and name of each is resolved. 37 | * 38 | * @name activeHandles 39 | * @function 40 | * @param {Object} options 41 | * @param {Array.=} opts.handles handles to get info for (default: `process._getActiveHandles()`) 42 | * @param {Boolean=} opts.source include source (default: `true`), included either way if `highlight=true` 43 | * @param {Boolean=} opts.highlight include highlighted source (default: `true`) 44 | * @param {Boolean=} opts.attachHandle attaches inspected handle for further inspection (default: `false`) 45 | * @return {Array.} handles each with the following properties 46 | * @return {Number} handle.msecs timeout specified for the handle 47 | * @return {Function} handle.fn the handle itself 48 | * @return {String} handle.name the name of the function, for anonymous functions this is the name it was assigned to 49 | * @return {Boolean} handle.anonymous true if the function was anonymous 50 | * @return {String} handle.source the raw function source 51 | * @return {String} handle.highlighted the highlighted source 52 | * @return {Object} handle.location location information about the handle 53 | * @return {String} handle.location.file full path to the file in which the handle was defined 54 | * @return {Number} handle.location.line line where the handle was defined 55 | * @return {Number} handle.location.column column where the handle was defined 56 | * @return {String} handle.location.inferredName name that is used when function declaration is anonymous 57 | */ 58 | exports = module.exports = function activeHandles(opts) { 59 | opts = xtend(core.defaultOpts, opts); 60 | var infos = core(opts); 61 | 62 | if (opts.highlight) infos.forEach(addHighlight); 63 | return infos; 64 | } 65 | 66 | /** 67 | * Convenience function that first calls @see activeHandles and 68 | * prints the information to stdout. 69 | * 70 | * @name activeHandles::print 71 | * @param {Object} options 72 | * @param {Boolean=} opts.highlight print highlighted source (default: `true`) 73 | * @function 74 | */ 75 | exports.print = function print(opts) { 76 | var h, loc, locString, fdString, printed = {}; 77 | var highlightString = ''; 78 | 79 | opts = xtend(core.defaultOpts, opts); 80 | opts.source = false; 81 | 82 | var handles = exports(opts); 83 | for (var i = 0, len = handles.length; i < len; i++) { 84 | h = handles[i]; 85 | loc = h.location; 86 | 87 | locString = loc 88 | ? format('%s:%d:%d', loc.file, loc.line, loc.column) 89 | : 'Unknown location'; 90 | 91 | if (opts.highlight) { 92 | highlightString = printed[locString] 93 | ? 'Count: ' + (printed[locString] + 1) + '. Source printed above' 94 | : h.highlighted; 95 | } 96 | 97 | fdString = h.fd ? ', fd = ' + h.fd : ''; 98 | 99 | console.log('\n%s %s (%s%s)\n%s' 100 | , colors.green(h.name + ':') 101 | , colors.brightBlack(locString) 102 | , h.type || 'unknown type' 103 | , fdString 104 | , highlightString); 105 | 106 | printed[locString] = (printed[locString] || 0) + 1; 107 | } 108 | } 109 | 110 | /** 111 | * Hooks `setInterval` calls in order to expose the passed handle. 112 | * NOTE: not needed in `io.js >=v1.6.2` and will not hook for those versions. 113 | * 114 | * The handle is wrapped. In older node versions it is not exposed. 115 | * The hooked version of `setInterval` will expose the wrapped callback 116 | * so its information can be retrieved later. 117 | * 118 | * @name activeHandles::hookSetInterval 119 | * @function 120 | */ 121 | exports.hookSetInterval = function () { 122 | // no need to hook things starting with io.js 1.6.2 (see resolveHandle) 123 | if (versionGreaterEqualOneSixTwo(process.version)) return; 124 | var timers = require('timers'); 125 | var setInterval_ = timers.setInterval; 126 | 127 | function setIntervalHook() { 128 | var t = setInterval_.apply(timers, arguments); 129 | t._wrappedCallback = arguments[0]; 130 | return t; 131 | } 132 | 133 | timers.setInterval = setIntervalHook; 134 | } 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # active-handles [![build status](https://secure.travis-ci.org/thlorenz/active-handles.png)](http://travis-ci.org/thlorenz/active-handles) 2 | 3 | Prints out information about the process's active handles, including function source and location. 4 | 5 | ```js 6 | var activeHandles = require('active-handles'); 7 | 8 | // run your program, launch a server, call setTimeout or similar 9 | // in order to create some handles and then call the below 10 | 11 | activeHandles.print(); 12 | ``` 13 | 14 | ![assets/mixed.png](assets/mixed.png) 15 | 16 | *Shows handles of multiple types created via 17 | [examples/mixed.js](https://github.com/thlorenz/active-handles/blob/master/examples/mixed.js)* 18 | 19 | ## Installation 20 | 21 | npm install active-handles 22 | 23 | ## Caveats 24 | 25 | For any version `<= v1.6.2` the `setInterval` function has to be hooked in order for this to work for `setInterval` 26 | created handles. 27 | 28 | In order to do that call `activeHandles.hookSetInterval()` **before** any calls to `setInterval` occurr. This is 29 | demonstrated in [this example](https://github.com/thlorenz/active-handles/blob/master/examples/setInterval.js#L5). 30 | 31 | Handles of `net` and `http` modules log handles from inside core *instead of your handle*, leaving you to hunt down 32 | where you created these connections. 33 | 34 | ## API 35 | 36 | 37 | 38 | 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |

activeHandles(options) → {Array.<Object>}

50 |
51 |
52 |
53 |

Gathers information about all currently active handles. 54 | Active handles are obtained via process._getActiveHandles 55 | and location and name of each is resolved.

56 |
57 |
Parameters:
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 73 | 75 | 76 | 77 | 78 | 79 | 82 | 85 | 86 | 87 | 88 | 89 | 92 | 95 | 96 | 97 | 98 | 99 | 102 | 105 | 106 | 107 | 108 | 109 | 112 | 115 | 116 | 117 | 118 |
NameTypeArgumentDescription
options 71 | Object 72 | 74 |
opts.handles 80 | Array.<Object>= 81 | 83 | <optional>
84 |

handles to get info for (default: process._getActiveHandles())

opts.source 90 | Boolean 91 | 93 | <optional>
94 |

include source (default: true), included either way if highlight=true

opts.highlight 100 | Boolean 101 | 103 | <optional>
104 |

include highlighted source (default: true)

opts.attachHandle 110 | Boolean 111 | 113 | <optional>
114 |

attaches inspected handle for further inspection (default: false)

119 |
120 |
Source:
121 |
128 |
129 |
Returns:
130 |
    131 |
  • 132 |
    133 |

    handles each with the following properties

    134 |
    135 |
    136 |
    137 | Type 138 |
    139 |
    140 | Array.<Object> 141 |
    142 |
    143 |
  • 144 |
  • 145 |
    146 |

    handle.msecs timeout specified for the handle

    147 |
    148 |
    149 |
    150 | Type 151 |
    152 |
    153 | Number 154 |
    155 |
    156 |
  • 157 |
  • 158 |
    159 |

    handle.fn the handle itself

    160 |
    161 |
    162 |
    163 | Type 164 |
    165 |
    166 | function 167 |
    168 |
    169 |
  • 170 |
  • 171 |
    172 |

    handle.name the name of the function, for anonymous functions this is the name it was assigned to

    173 |
    174 |
    175 |
    176 | Type 177 |
    178 |
    179 | String 180 |
    181 |
    182 |
  • 183 |
  • 184 |
    185 |

    handle.anonymous true if the function was anonymous

    186 |
    187 |
    188 |
    189 | Type 190 |
    191 |
    192 | Boolean 193 |
    194 |
    195 |
  • 196 |
  • 197 |
    198 |

    handle.source the raw function source

    199 |
    200 |
    201 |
    202 | Type 203 |
    204 |
    205 | String 206 |
    207 |
    208 |
  • 209 |
  • 210 |
    211 |

    handle.highlighted the highlighted source

    212 |
    213 |
    214 |
    215 | Type 216 |
    217 |
    218 | String 219 |
    220 |
    221 |
  • 222 |
  • 223 |
    224 |

    handle.location location information about the handle

    225 |
    226 |
    227 |
    228 | Type 229 |
    230 |
    231 | Object 232 |
    233 |
    234 |
  • 235 |
  • 236 |
    237 |

    handle.location.file full path to the file in which the handle was defined

    238 |
    239 |
    240 |
    241 | Type 242 |
    243 |
    244 | String 245 |
    246 |
    247 |
  • 248 |
  • 249 |
    250 |

    handle.location.line line where the handle was defined

    251 |
    252 |
    253 |
    254 | Type 255 |
    256 |
    257 | Number 258 |
    259 |
    260 |
  • 261 |
  • 262 |
    263 |

    handle.location.column column where the handle was defined

    264 |
    265 |
    266 |
    267 | Type 268 |
    269 |
    270 | Number 271 |
    272 |
    273 |
  • 274 |
  • 275 |
    276 |

    handle.location.inferredName name that is used when function declaration is anonymous

    277 |
    278 |
    279 |
    280 | Type 281 |
    282 |
    283 | String 284 |
    285 |
    286 |
  • 287 |
288 |
289 |
290 |

activeHandles::hookSetInterval()

291 |
292 |
293 |
294 |

Hooks setInterval calls in order to expose the passed handle. 295 | NOTE: not needed in io.js >=v1.6.2 and will not hook for those versions.

296 |

The handle is wrapped. In older node versions it is not exposed. 297 | The hooked version of setInterval will expose the wrapped callback 298 | so its information can be retrieved later.

299 |
300 |
301 |
Source:
302 |
309 |
310 |
311 |
312 |

activeHandles::print(options)

313 |
314 |
315 |
316 |

Convenience function that first calls @see activeHandles and 317 | prints the information to stdout.

318 |
319 |
Parameters:
320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 335 | 337 | 338 | 339 | 340 | 341 | 344 | 347 | 348 | 349 | 350 |
NameTypeArgumentDescription
options 333 | Object 334 | 336 |
opts.highlight 342 | Boolean 343 | 345 | <optional>
346 |

print highlighted source (default: true)

351 |
352 |
Source:
353 |
360 |
361 |
362 |
363 |
364 |
365 |
366 | 367 | *generated with [docme](https://github.com/thlorenz/docme)* 368 |
369 | 370 | 371 | ## core 372 | 373 | A core module which depends only on `function-origin` can be used. It behaves exactly like the main module except 374 | that it doesn't merge `opts` with default options and provides **no highlighting** even if `highlight` is set in the options. 375 | Additionally it does not include the `print` feature. 376 | 377 | You can use it as follows: 378 | 379 | ```js 380 | var activeHandles = require('active-handles/core'); 381 | activeHandles(); 382 | ``` 383 | 384 | ## License 385 | 386 | MIT 387 | --------------------------------------------------------------------------------