API to get stack frame info from generator

Mark S. Miller erights at google.com
Tue Jun 18 09:57:31 PDT 2013

On Mon, Jun 17, 2013 at 11:33 AM, Chad Austin <chad at imvu.com> wrote:

> Hi Mark,
> I'm the author of IMVU's task system, which is very similar to what Bruno
> describes except with Python Futures and Python generators.  Python
> provides stack traces as a linked list of activation records from the
> thrower to the catcher.  On the other hand, Error().stack in most
> JavaScript implementations returns a complete stack trace all the way up to
> the main event loop.  Python's linked list stack traces allows stitching
> them together so that if async generator A yields to async generator B, and
> B throws, both A and B can be made to appear in the stack together.
>  Finally, and this is key, Python attaches stack traces to the act of
> _throwing_ and not to the exception object itself.

I hope we can stitch together stack traces in JS as well. This is
desperately needed to make asynchronous and distributed code practical,
more on a par with local synchronous code. Please follow the Causeway links
in the email you're responding to.

The Causeway work was done in the context of event-loops and promises, but
not generators. We should explore how to extend its stack stitching
approach to include generators.

> I'm not familiar with the security or encapsulation issues with
> Error().stack (is it that bad if Chrome and Firefox appear to support the
> .stack property?)

Yes. Fortunately on Chrome we can effectively remove it <

> , but I would like to express my support for being able to stitch together
> stack traces on the open web so that if something goes wrong developers
> have a shot at diagnosing what happened.  Native application developers
> have dozens of fantastic options for phone-home crash reporting, but on the
> web the best you can hope for is that your customer's browser supports
> Error#stack and window.onerror provides the thrown exception.

The built-in window.onerror can have/use the rights amplification powers of
the getStack or @stacktrace in my previous message. This would not violate
any security constraint, since untrusted scripts within a frame are denied
direct access to their true window and global scope anyway.

> Thanks,
> Chad
> p.s. E was hugely influential in my thinking about concurrency and led me
> to build the task system at IMVU in the first place, so thanks!

You are very welcome! Always nice to hear.

> On Sat, Jun 15, 2013 at 6:56 PM, Mark S. Miller <erights at google.com>wrote:
>> On Sat, Jun 15, 2013 at 10:17 AM, David Bruant <bruant.d at gmail.com>wrote:
>>> Le 15/06/2013 11:18, Bruno Jouhier a écrit :
>>>  A generator object represents a computation which has been suspended.
>>>> We have an API to resume this computation (next/throw). What's missing is
>>>> an API to get information about this suspended computation (which function,
>>>> where in the source).
>>> As an aside, note that we already have this sort of problem today with
>>> event loop turns.
>>>     function schedule(){
>>>         if(Math.random() < 0.5)
>>>             throw new Error('whatever');
>>>         else
>>>             setTimeout(schedule, Math.random()*100);
>>>     }
>>>     setTimeout(schedule, (Math.random()*100) |0)
>>>     setTimeout(schedule, (Math.random()*100) |0)
>>> There is no way to know how many scheduling happened before the first
>>> thrown error, nor whether the error comes from the first or second
>>> scheduling originally. And that's a dummy 5-line example. It gets worse
>>> when you have a sequence of different events (or promise resolutions)
>>> called at different times, adding other listeners, etc.
>>> Q solves that with long stack traces [1] (only for promises I believe).
>>> It might be worth looking into it.
>>> Back to your problem, I worry that this kind of information (a call to
>>> get the stack trace of where the generator last yielding with line number)
>>> may partially break encapsulation which wouldn't be good for security.
>>> I'm thinking of something like:
>>>     (exports => {
>>>         var someCrucialInfo = // boolean
>>>         exports.bla = function*(){
>>>             if(someCrucialInfo){
>>>                 doX();
>>>                 yield 27;
>>>             }
>>>             else{
>>>                 doY();
>>>                 yield 27;
>>>             }
>>>         }
>>>     })(this);
>>>     var v = this.bla();
>>>     var l = getLastBlaYieldLine(bla);
>>>     // given l, the encapsulated value of someCrucialInfo can be inferred
>>> cc'ing MarkM to get his eyes on it as I don't feel qualified to assess
>>> the gravity.
>> Thanks for calling my attention to this thread. Generators and multiple
>> turns aside (see below), we've been over the security issue before but a
>> recap is useful. The stack trace information cannot be publicly accessible
>> from the Error object because it violates encapsulation. (Historical note:
>> When E entered its first security review, it had this vulnerability. Thanks
>> to David Wagner for catching it.) Instead, some sort of rights
>> amplification is required. One possible interface is a privileged function,
>> like the getStack function at <
>> https://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/debug.js#221>,
>> where
>> getStack(err) returns the stacktrace. If you don't have the getStack
>> function, you can't get the stacktrace. getStack is implemented using a
>> WeakMap associating Errors with stacktraces.
>> Another approach, if <
>> http://wiki.ecmascript.org/doku.php?id=strawman:relationships> are
>> adopted in ES7, is to have a relationship associating Error objects with
>> stacktraces. The full read/write relationship would be internal to the
>> implementation, but a relationship representing a readonly facet of that
>> relationship, say @stacktrace, could be made available in the same
>> privileged manner as getStack above. Then
>> err at stacktrace would return the same stacktrace that getStack(err) would
>> return above.
>> If relationships don't happen in ES7 (it's too late for ES6) and private
>> symbols do, then @stacktrace could be a private symbol.
>> Multiple turn distributed debugging is indeed the next frontier, and one
>> that will become ever more urgent as promises continue to catch on. I
>> participated in a cool project, Causeway, that explored some of this
>> territory well
>> http://www.hpl.hp.com/techreports/2009/HPL-2009-78.html
>> https://code.google.com/p/causeway/
>> Although the project has since been abandoned, its trace log format is a
>> good place to start, in order to be able to support something like this in
>> the future:
>> http://wiki.erights.org/wiki/Causeway_Platform_Developer
>> http://wiki.erights.org/wiki/Causeway_Platform_Developer:_Ajax
>> http://wiki.erights.org/wiki/Causeway_Platform_Developer:_Promises
>> https://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/debug.js
>> also provides some support for the Causeway log format. See getCWStack.
>> See also
>> http://crpit.com/confpapers/CRPITV135Murray.pdf and
>> http://infoscience.epfl.ch/record/181543/files/EPFL_TH5533.pdf
>> for other interesting approaches to extended debugging
>> As for generators specifically, it seems to me that whatever rights
>> amplification operation one applies to an Error object to get a stack
>> trace, one should also be able to apply to a generator instance to get a
>> 1-level stacktrace showing where it is currently suspended.
>>> oh... and we have the source code of functions by default with
>>> Function#toString.
>>> This sort of inference can happen in regular error stack traces too, but
>>> requires for a function to throw which happens only if it's supposed to,
>>> not at the caller's will.
>>> Though I realize now that anyone holding a reference to a generator can
>>> force an error being thrown and, if uncaught, it generates a stack trace
>>> leaking the line number even without the API you're asking for (but that
>>> would work only once)
>>>  I see this as being similar to asking for a portable "stack" property
>>>> in Error objects. I don't know if it is actually mandated by ES6 but it
>>>> looks like all major JS engines support it now.
>>> I believe the state of TC39 on Error#stack can be found at
>>> http://wiki.ecmascript.org/**doku.php?id=strawman:error_**stack<http://wiki.ecmascript.org/doku.php?id=strawman:error_stack>
>>> David
>>> [1] https://github.com/kriskowal/**q#long-stack-traces<https://github.com/kriskowal/q#long-stack-traces>
>> --
>>     Cheers,
>>     --MarkM
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
> --
> Chad Austin
> Technical Director, IMVU
> http://www.imvu.com/members/Chad/

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130618/4669a08c/attachment-0001.html>

More information about the es-discuss mailing list