From 4ceaf52668f3513b89e1f79447d7b73b6c5d3436 Mon Sep 17 00:00:00 2001 From: Kris Reeves Date: Sat, 17 Feb 2018 11:08:15 -0800 Subject: [PATCH] Fixes for eval, sourcemaps (#25) * Don't fail on empty file component of a stack trace * Capture source of eval * Don't break sourcemaps * Clean things up a bit, get more organized --- .gitignore | 15 ++----- index.js | 42 +++++++++++--------- package.json | 17 ++++++-- test-all.sh | 32 +++++++++++++++ key-cert.pem => tests/key-cert.pem | 0 key.pem => tests/key.pem | 0 kitchensink.js => tests/kitchensink.js | 2 +- clustertest.js => tests/test-cluster.js | 2 +- tests/test-eval.js | 7 ++++ test-promisify.js => tests/test-promisify.js | 2 +- tests/test-sourcemaps.coffee | 14 +++++++ test.js => tests/test.js | 2 +- 12 files changed, 98 insertions(+), 37 deletions(-) create mode 100755 test-all.sh rename key-cert.pem => tests/key-cert.pem (100%) rename key.pem => tests/key.pem (100%) rename kitchensink.js => tests/kitchensink.js (98%) rename clustertest.js => tests/test-cluster.js (85%) create mode 100644 tests/test-eval.js rename test-promisify.js => tests/test-promisify.js (87%) create mode 100644 tests/test-sourcemaps.coffee rename test.js => tests/test.js (98%) diff --git a/.gitignore b/.gitignore index de1a1c2..bebf4da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,3 @@ -* -!.gitignore -!package.json -!index.js -!kitchensink.js -!clustertest.js -!test.js -!test-promisify.js -!README.md -!key.pem -!key-cert.pem -!proxy.js +/node_modules +/tests/test-sourcemaps.js +/tests/test-sourcemaps.js.map diff --git a/index.js b/index.js index 079353d..70ed54e 100644 --- a/index.js +++ b/index.js @@ -36,14 +36,23 @@ function setPrototypeOf(obj, proto) { // hook stuff (function () { - var _Error_prepareStackTrace = Error.prepareStackTrace; var hooked = function (_, stack) { return stack; }; function getStack() { - Error.prepareStackTrace = hooked + var _Error_prepareStackTrace = Error.prepareStackTrace; + Error.prepareStackTrace = hooked; var err = new Error(); var stack = err.stack.map(function (item) { + if (item.isEval()) { + var matched = item.getEvalOrigin().match(/\((.*):(\d*):(\d*)\)/) || {}; + return { + name: '', + file: matched[1], + line: matched[2] + }; + } return { + name: item.getFunctionName(), file: item.getFileName(), line: item.getLineNumber() }; @@ -55,11 +64,13 @@ function setPrototypeOf(obj, proto) { function findCallsite(stack) { for (var i = 0; i < stack.length; i++) { // Ignore frames from: + // - null/undefined values // - wtfnode by excluding __filename // - builtins by excluding files with no path separator // - internal builtins by excluding files beginning with 'internal/' // (even on windows, the stack trace uses unix separators for these) - if (stack[i].file !== __filename && + if (stack[i].file && + stack[i].file !== __filename && stack[i].file.indexOf(path.sep) !== -1 && stack[i].file.slice(0, 9) !== 'internal/' ) { @@ -112,6 +123,7 @@ function setPrototypeOf(obj, proto) { var stack = getStack(); // this should inherit 'name' and 'length' and any other properties that have been assigned + // also inherits prototype, and symbols like the promisify value! copyProperties(fn, wrapped); // we use these later to identify the source information about an open handle @@ -310,11 +322,11 @@ function formatTime(t) { var count = 0; function getCallsite(thing) { if (!thing.__callSite) { - var name = ((thing.name ? thing.name : thing.constructor.name) || 'unknown').trim(); + var name = ((thing.name ? thing.name : thing.constructor.name) || '(anonymous)').trim(); if (!DONT_INSTRUMENT[name]) { console.warn('Unable to determine callsite for "'+name+'". Did you require `wtfnode` at the top of your entry point?'); } - return { file: 'unknown', line: 0 }; + return { name: '(anonymous)', file: 'unknown', line: 0 }; } return thing.__callSite; }; @@ -359,7 +371,7 @@ function dump() { console.log(' - Listeners:'); keypressListeners.forEach(function (fn) { var callSite = getCallsite(fn); - console.log(' - %s: %s @ %s:%d', 'keypress', fn.name || '(anonymous)', callSite.file, callSite.line); + console.log(' - %s: %s @ %s:%d', 'keypress', fn.name || fn.__name || callSite.name || '(anonymous)', callSite.file, callSite.line); }); } }); @@ -425,7 +437,7 @@ function dump() { 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 || fn.__name || callSite.name || '(anonymous)', callSite.file, callSite.line); }); } }); @@ -470,7 +482,7 @@ function dump() { console.log(' - Listeners:'); listeners.forEach(function (fn) { var callSite = getCallsite(fn); - console.log(' - %s: %s @ %s:%d', eventType, fn.__name || fn.name || '(anonymous)', callSite.file, callSite.line); + console.log(' - %s: %s @ %s:%d', eventType, fn.name || fn.__name || callSite.name || '(anonymous)', callSite.file, callSite.line); }); } }); @@ -522,11 +534,8 @@ function dump() { timers.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); - } + + console.log(' - (%d ~ %s) %s @ %s:%d', t._idleTimeout, formatTime(t._idleTimeout), fn.name || fn.__name || callSite.name || '(anonymous)', callSite.file, callSite.line); }); } @@ -536,11 +545,8 @@ function dump() { 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); - } + + console.log(' - (%d ~ %s) %s @ %s:%d', t._idleTimeout, formatTime(t._idleTimeout), fn.name || fn.__name || callSite.name, callSite.file, callSite.line); }); } diff --git a/package.json b/package.json index 0620edf..4d5ecb1 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,29 @@ { "name": "wtfnode", - "version": "0.5.7", + "version": "0.6.0", "description": "Utility to help find out why Node isn't exiting", "repository": { "type": "git", "url": "https://github.com/myndzi/wtfnode" }, "main": "index.js", + "files": [ + "index.js", + "proxy.js", + "README.md" + ], "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "(cd tests && node test && node test-eval && node test-promisify)", + "test-sourcemaps": "(cd tests && coffee --map --compile test-sourcemaps.coffee && node test-sourcemaps.js || exit 0)", + "kitchensink": "(cd tests && node kitchensink)" }, "author": "Kris Reeves", "bin": { "wtfnode": "proxy.js" }, - "license": "ISC" + "license": "ISC", + "devDependencies": { + "coffee-script": "^1.12.7", + "source-map-support": "^0.5.3" + } } diff --git a/test-all.sh b/test-all.sh new file mode 100755 index 0000000..61a2347 --- /dev/null +++ b/test-all.sh @@ -0,0 +1,32 @@ +#!/bin/bash -e + +. ~/.nvm/nvm.sh + +CURRENT_VERSION="$(node --version)" + +declare -a versions=( + "0.10" + "0.12" + "4" + "5" + "6" + "7" + "8" + "9" +) + +test_version () { + nvm install "$1" >/dev/null 2>/dev/null + nvm use "$1" >/dev/null + npm test >/dev/null +} + +for ver in "${versions[@]}"; do + if test_version "$ver"; then + echo "$ver: ok" + else + echo "$ver: failed" + fi +done + +nvm use "$CURRENT_VERSION" \ No newline at end of file diff --git a/key-cert.pem b/tests/key-cert.pem similarity index 100% rename from key-cert.pem rename to tests/key-cert.pem diff --git a/key.pem b/tests/key.pem similarity index 100% rename from key.pem rename to tests/key.pem diff --git a/kitchensink.js b/tests/kitchensink.js similarity index 98% rename from kitchensink.js rename to tests/kitchensink.js index ccad213..d397c7d 100644 --- a/kitchensink.js +++ b/tests/kitchensink.js @@ -1,4 +1,4 @@ -var wtf = require('./index'); +var wtf = require('../index'); var assert = require('assert'); cp = require('child_process'), diff --git a/clustertest.js b/tests/test-cluster.js similarity index 85% rename from clustertest.js rename to tests/test-cluster.js index ecb9364..db45b5b 100644 --- a/clustertest.js +++ b/tests/test-cluster.js @@ -1,6 +1,6 @@ 'use strict'; -var wtf = require('./index'), +var wtf = require('../index'), cluster = require('cluster'); if (cluster.isMaster) { diff --git a/tests/test-eval.js b/tests/test-eval.js new file mode 100644 index 0000000..951e5c0 --- /dev/null +++ b/tests/test-eval.js @@ -0,0 +1,7 @@ +'use strict'; + +var wtf = require('../index'); + +var foo = new Function('setTimeout(function evaled() {}, 100);'); +foo(); +wtf.dump(); \ No newline at end of file diff --git a/test-promisify.js b/tests/test-promisify.js similarity index 87% rename from test-promisify.js rename to tests/test-promisify.js index 0c8285b..9be7e0a 100644 --- a/test-promisify.js +++ b/tests/test-promisify.js @@ -1,6 +1,6 @@ 'use strict'; -var wtf = require('./index'), +var wtf = require('../index'), util = require('util'); if (util.promisify) { diff --git a/tests/test-sourcemaps.coffee b/tests/test-sourcemaps.coffee new file mode 100644 index 0000000..1eb3cd3 --- /dev/null +++ b/tests/test-sourcemaps.coffee @@ -0,0 +1,14 @@ +wtf = require('../index') +require('source-map-support').install() + +noop = () -> + undefined + +setTimeout(noop, 1000) + +wtf.dump() + +foo = -> + bar = -> throw new Error 'this is a demo' + bar() +foo() \ No newline at end of file diff --git a/test.js b/tests/test.js similarity index 98% rename from test.js rename to tests/test.js index 07810f0..4375842 100644 --- a/test.js +++ b/tests/test.js @@ -1,6 +1,6 @@ 'use strict'; -var wtf = require('./index'); +var wtf = require('../index'); var EventEmitter = require('events').EventEmitter, assert = require('assert');