Skip to content

Instantly share code, notes, and snippets.

@subfuzion
Forked from boneskull/README.md
Created November 7, 2017 18:33
Show Gist options
  • Save subfuzion/54a9413d02c6d9ba223076bebd49e38f to your computer and use it in GitHub Desktop.
Save subfuzion/54a9413d02c6d9ba223076bebd49e38f to your computer and use it in GitHub Desktop.

Revisions

  1. @boneskull boneskull revised this gist Oct 4, 2017. 1 changed file with 0 additions and 7 deletions.
    7 changes: 0 additions & 7 deletions async-dump.js
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,6 @@
    const {createHook} = require('async_hooks');
    const {stackTraceFilter} = require('mocha/lib/utils');
    const allResources = new Map();
    const resourceActivity = new Set();

    // this will pull Mocha internals out of the stacks
    const filterStack = stackTraceFilter();
    @@ -12,12 +11,6 @@ const hook = createHook({
    init(asyncId, type, triggerAsyncId) {
    allResources.set(asyncId, {type, triggerAsyncId, stack: (new Error()).stack});
    },
    before(asyncId) {
    resourceActivity.add(asyncId);
    },
    after(asyncId) {
    resourceActivity.delete(asyncId);
    },
    destroy(asyncId) {
    allResources.delete(asyncId);
    }
  2. @boneskull boneskull created this gist Oct 4, 2017.
    119 changes: 119 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,119 @@
    Here's an example of how to debug Mocha v4 if it hangs.

    Ensure you're using a Node.js 8 or newer (or any version with [async_hooks](https://nodejs.org/api/async_hooks.html#async_hooks_async_hooks) support).

    If you run your test, you'll notice it hangs:

    ```bash
    $ mocha test.js


    how to debug Mocha when it hangs
    ✓ should complete, but Mocha should not exit


    1 passing (25ms)

    ```

    You can include a module like `async-dump.js` to print information about what's happening. First, add this hook somewhere in the `describe` block:

    ```js
    after(function () {
    global.asyncDump();
    });
    ```

    Then, you can `--require` `async-dump.js` with Mocha:

    ```bash
    $ mocha --require async-dump test.js


    how to debug Mocha when it hangs
    ✓ should complete, but Mocha should not exit

    STUFF STILL IN THE EVENT LOOP:
    Type: SIGNALWRAP
    Error
    at AsyncHook.init (async-dump.js:12:62)
    at Signal.emitInitNative (async_hooks.js:458:43)
    at process.<anonymous> (internal/process.js:209:20)
    at startup (bootstrap_node.js:201:16)
    at bootstrap_node.js:626:3


    Type: TCPWRAP
    Error
    at AsyncHook.init (async-dump.js:12:62)
    at TCP.emitInitNative (async_hooks.js:458:43)
    at createServerHandle (net.js:1267:14)
    at Server.setupListenHandle [as _listen2] (net.js:1310:14)
    at listenInCluster (net.js:1391:12)
    at Server.listen (net.js:1474:7)
    at Context.<anonymous> (debug-hanging-mocha.js:9:12)


    Type: TCPWRAP
    Error
    at AsyncHook.init (async-dump.js:12:62)
    at TCP.emitInitNative (async_hooks.js:458:43)
    at Socket.connect (net.js:997:40)
    at Object.connect (net.js:104:35)
    at Context.<anonymous> (debug-hanging-mocha.js:17:22)


    Type: DNSCHANNEL
    Error
    at AsyncHook.init (async-dump.js:12:62)
    at ChannelWrap.emitInitNative (async_hooks.js:458:43)
    at new Resolver (dns.js:249:20)
    at dns.js:380:25
    at NativeModule.compile (bootstrap_node.js:614:7)
    at NativeModule.require (bootstrap_node.js:559:18)
    at lazyDns (net.js:1376:11)
    at lookupAndConnect (net.js:1026:15)
    at Socket.connect (net.js:1019:5)
    at Object.connect (net.js:104:35)
    at Context.<anonymous> (debug-hanging-mocha.js:17:22)


    Type: TCPWRAP
    Error
    at AsyncHook.init (async-dump.js:12:62)
    at TCP.emitInitNative (async_hooks.js:458:43)


    Type: Immediate
    Error
    at AsyncHook.init (async-dump.js:12:62)
    at emitInitNative (async_hooks.js:458:43)
    at emitInitScript (async_hooks.js:361:3)


    1 passing (25ms)
    ```

    These are not *Errors*; everything that's printed above are async tasks that have been "started" but not "finished".

    From the above, you will notice that the `TCPWRAP` resource(s) are interesting. Within the stack trace, you can see where exactly these happen in your code:

    ```
    Type: TCPWRAP
    Error
    at AsyncHook.init (async-dump.js:12:62)
    at TCP.emitInitNative (async_hooks.js:458:43)
    at createServerHandle (net.js:1267:14)
    at Server.setupListenHandle [as _listen2] (net.js:1310:14)
    at listenInCluster (net.js:1391:12)
    at Server.listen (net.js:1474:7)
    at Context.<anonymous> (debug-hanging-mocha.js:9:12)
    ```

    On line 9, we're listening on a port, but we never shut down the server.

    `async-dump.js` could certainly be improved upon, but this is a rough guide.

    The Node.js inspector (use `--inspect-brk`, etc.) will also give you similar information, but it's not so easily filtered.

    [why-is-node-running](https://npm.im/why-is-node-running) can also be used with some fiddling (do `node --expose-internals /path/to/_mocha test.js` then `require('why-is-node-running')()` at the top of `test.js`), but YMMV.
    35 changes: 35 additions & 0 deletions async-dump.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    'use strict';

    const {createHook} = require('async_hooks');
    const {stackTraceFilter} = require('mocha/lib/utils');
    const allResources = new Map();
    const resourceActivity = new Set();

    // this will pull Mocha internals out of the stacks
    const filterStack = stackTraceFilter();

    const hook = createHook({
    init(asyncId, type, triggerAsyncId) {
    allResources.set(asyncId, {type, triggerAsyncId, stack: (new Error()).stack});
    },
    before(asyncId) {
    resourceActivity.add(asyncId);
    },
    after(asyncId) {
    resourceActivity.delete(asyncId);
    },
    destroy(asyncId) {
    allResources.delete(asyncId);
    }
    }).enable();

    global.asyncDump = module.exports = () => {
    hook.disable();
    console.error(`
    STUFF STILL IN THE EVENT LOOP:`)
    allResources.forEach(value=> {
    console.error(`Type: ${value.type}`);
    console.error(filterStack(value.stack));
    console.error('\n');
    });
    };
    18 changes: 18 additions & 0 deletions mytest.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    'use strict';

    const net = require('net');
    const assert = require('assert');

    describe('how to debug Mocha when it hangs', function () {
    before(function (done) {
    const server = net.createServer();
    server.listen(10101, done);
    });

    it('should complete, but Mocha should not exit', function(done) {
    const sock = net.createConnection(10101, () => {
    assert.deepEqual(sock.address().family, 'IPv4');
    done();
    });
    });
    });