Maximally minimal stack trace standardization

Filip Pizlo fpizlo at apple.com
Mon Sep 29 13:54:50 PDT 2014



> On Sep 29, 2014, at 12:19 PM, Steve Fink <sphink at gmail.com> wrote:
> 
>> On 09/29/2014 09:14 AM, Sam Tobin-Hochstadt wrote:
>>> On Mon, Sep 29, 2014 at 10:55 AM, John Lenz <concavelenz at gmail.com> wrote:
>>> I really have no idea what the behavior should be in the faces of optimized
>>> tail calls (which is must broader than simply self recursive methods that
>>> can be rewritten as a loop).   I've seen various suggestions (a capped call
>>> history) but I'm curious how efficient functional languages deal with this.
>> Different functional languages do a variety of things here:
>> 
>> - simply show the current stack, without the functions that made tail
>> calls (this is probably the most common)
>> - have a bounded buffer for stack traces
>> - implement tail calls via a trampoline; this has the side-effect that
>> the stack has "recent" tail calls in it already
>> 
>> I'm sure there are other choices here that people have made.
> 
> "Stack traces" are really an overload of (at least?) 3 different concepts:
> 
> 1. A record of how execution reached the current state. What debuggers
> want, mostly.
> 2. The continuation from this point on - what function will be returned
> to when the current function returns normally, recursively up the call
> chain.
> 3. A description of the actual state of the stack.
> 
> In all of these, the semantics of the youngest frame are different from
> all other frames in the stack trace.
> 
> For #2, thrown exceptions make the implied continuation ordering a lie,
> or at least a little more nuanced. You sort of want to see what frames
> will catch exceptions. (But that's not a trivial determination if you
> have some "native" frames mixed in there, with arbitrary logic for
> determining whether to catch or propagate an exception. Even JS frames
> may re-throw.)
> 
> Inlined functions may cause gaps in #1 and #2, unless the implementation
> takes pains to fill them in with dummy frames (in which case it's not
> really #3 anymore.)

AFAICT, production compilers already take pains to ensure that they leave behind sufficient meta-data for the runtime to fill in the missing stack frames whenever inlining has happened. This is certainly true in JSC. Crucially, the infrastructure to do this is also needed for other random stuff and it imposes zero overhead. 

So let's not compare this to inlining. 

-Filip

> 
> Unless the implementation plays games, tail calls can make #1 lie as
> well. You really called f(), but it doesn't appear because its frame was
> used for executing g() before pushing the remaining frames on your
> stack. Tail calls don't really muck with #2 afaict.
> 
> All three meanings are legitimate things to want, and all of them
> require some implementation effort. Even #3 is tricky with a JIT
> involved. And I'm not even considering floating generator frames, which
> may not fit into a linear structure at all. Or when users want "long
> stacks" for callbacks, where the stack in effect when a callback was set
> is relevant.
> 
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss


More information about the es-discuss mailing list