V8 Expose Scope Object in stack traces

35 views
Skip to first unread message

Tim Caswell

unread,
Oct 10, 2010, 5:23:19 PM10/10/10
to nod...@googlegroups.com
While thinking about the error handling problem with node servers (uncaught exceptions not having a reference to the client) I came up with three possible solutions.

first is to put a global lock around the request handler so that only one request goes through the program logic at a time. Then you can just have a reference to the current request in some global that the uncaughtException handler can see. The problem is this has terrible performance impact and reduces concurrency to 1.

second is to wait till Ryan adds a patch to make stack traces long. Then you can create a named function at the top of the stack (right after accepting the request from the browser). The name of the function will have embedded in it the key to a global array of handlers to the clients. When an exception happens, you can do string manipulation on the long stack trace and get the id to the proper object in the global hash. This enables concurrency, but requires a lot of extra objects laying around for both the long stack traces and the global hash storing handles to the clients. Also it's somewhat fragile since it requires string manipulation on the stack trace.

third is to patch v8 to expose the magical [scope] property in the error object that get's sent to uncaught exception. This will contain the chain of variable objects and give access to all the variables that were available to the code when the exception happened. This has very little overhead since that object already exists and is required by the spec. The downside here is it exposes some interesting security implications. Given node is used more for building robust network servers and less for running untrusted code (in the main context at least), then I think that's a suitable tradeoff.

-Tim Caswell

Tim Caswell

unread,
Oct 10, 2010, 5:52:30 PM10/10/10
to nodejs
Actually, the [scope] thing won't help as shown in this example:

function handle(res) {
// From here I want to somehow catch the exception thrown in add's
setTimeout callback
// and I need access to the current `res` variable so I can output
the error
add(1, 4, function (result) {
res.output(result);
});
}

function add(a, b, callback) {
setTimeout(function () {
throw new Error("Ouch")
// Notice that `res` isn't available at this point, so access to
the closure variables doesn't help.
callback(a + b);
});
}


setTimeout(function () {
handle({output:console.log});
});

It seems we need some sort of linking stacks at the level of node's
event system.

r...@tinyclouds.org

unread,
Oct 10, 2010, 5:58:38 PM10/10/10
to nod...@googlegroups.com
On Sun, Oct 10, 2010 at 2:23 PM, Tim Caswell <t...@creationix.com> wrote:
> While thinking about the error handling problem with node servers (uncaught exceptions not having a reference to the client)  I came up with three possible solutions.
>
> first is to put a global lock around the request handler so that only one request goes through the program logic at a time.  Then you can just have a reference to the current request in some global that the uncaughtException handler can see.  The problem is this has terrible performance impact and reduces concurrency to 1.

A non-option because of the performance penalty.

> second is to wait till Ryan adds a patch to make stack traces long.  Then you can create a named function at the top of the stack (right after accepting the request from the browser).  The name of the function will have embedded in it the key to a global array of handlers to the clients.  When an exception happens, you can do string manipulation on the long stack trace and get the id to the proper object in the global hash.  This enables concurrency, but requires a lot of extra objects laying around for both the long stack traces and the global hash storing handles to the clients.  Also it's somewhat fragile since it requires string manipulation on the stack trace.

The patch will not enable the long stacks all the time - they're going
to have to be either turned on during a debug mode or enabled
dynamically.

> third is to patch v8 to expose the magical [scope] property in the error object that get's sent to uncaught exception. This will contain the chain of variable objects and give access to all the variables that were available to the code when the exception happened.  This has very little overhead since that object already exists and is required by the spec.  The downside here is it exposes some interesting security implications.  Given node is used more for building robust network servers and less for running untrusted code (in the main context at least), then I think that's a suitable tradeoff.

I don't know how difficult that is. Do you? It sounds like it might be
insurmountable.

Jorge

unread,
Oct 11, 2010, 9:21:55 AM10/11/10
to nod...@googlegroups.com
On 10/10/2010, at 23:23, Tim Caswell wrote:

> While thinking about the error handling problem with node servers (uncaught exceptions not having a reference to the client) I came up with three possible solutions.

As there's no way to go back in time to the point where the callback function was setup as a callback, istm, all that one could do is to save the stack trace at the point of setup so that it can be recovered later if needed (if it ever throws).

This can be accomplished in this way ( `talk is cheap, show me the code` ) : http://github.com/xk/nodeSnippets/blob/master/nodeErrorsHowTo.js

so, for example, given this code:

function i () {
throw Error('This is the error that we want to track');
}

(function d () {
(function e () {
(function f () {
(function g () {
setTimeout(wrap(i), 10);
})();
})();
})();
})();

When/if i() throws, you'd get this stackTrace:

Caught exception:
[ 'Error: This is the error that we want to track'
, ' at EventEmitter.emit (events:41:20)'
, ' at node.js:599:9'
]
[ 'Error: previousTrace'
, ' at g (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:7:20)'
, ' at f (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:8:8)'
, ' at e (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:9:6)'
, ' at d (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:10:4)'
, ' at Object.<anonymous> (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:11:2)'
, ' at Module._compile (node.js:315:23)'
, ' at Object..js (node.js:323:12)'
, ' at Module.load (node.js:250:25)'
, ' at Object.runMain (node.js:337:24)'
, ' at Array.0 (node.js:587:12)'
]

Is that what you wanted ?
--
Jorge.

Jorge Chamorro

unread,
Oct 11, 2010, 9:29:13 AM10/11/10
to nod...@googlegroups.com
On 11/10/2010, at 15:21, Jorge wrote:
> When/if i() throws, you'd get this stackTrace:
>
> Caught exception:
> [ 'Error: This is the error that we want to track'
> , ' at EventEmitter.emit (events:41:20)'
> , ' at node.js:599:9'
> ]
> [ 'Error: previousTrace'
> , ' at g (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:7:20)'
> , ' at f (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:8:8)'
> , ' at e (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:9:6)'
> , ' at d (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:10:4)'
> , ' at Object.<anonymous> (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:11:2)'
> , ' at Module._compile (node.js:315:23)'
> , ' at Object..js (node.js:323:12)'
> , ' at Module.load (node.js:250:25)'
> , ' at Object.runMain (node.js:337:24)'
> , ' at Array.0 (node.js:587:12)'
> ]

Oops, wrong copy-pasta :

Caught exception:
[ 'Error: This is the error that we want to track'

, ' at Error (unknown source)'
, ' at i (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:30:9)'
, ' at Timer.callback (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:20:16)'


, ' at node.js:599:9'
]
[ 'Error: previousTrace'
, ' at g (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:7:20)'
, ' at f (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:8:8)'
, ' at e (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:9:6)'
, ' at d (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:10:4)'
, ' at Object.<anonymous> (/Users/jorge/JAVASCRIPT/nodeSnippets/nodeErrorsHowTo.js:11:2)'
, ' at Module._compile (node.js:315:23)'
, ' at Object..js (node.js:323:12)'
, ' at Module.load (node.js:250:25)'
, ' at Object.runMain (node.js:337:24)'
, ' at Array.0 (node.js:587:12)'
]

--
Jorge.

Reply all
Reply to author
Forward
0 new messages