debugging interfaces

Jordan Osete jor at joozt.net
Thu Aug 13 04:51:27 PDT 2009


Christian Plesner Hansen a écrit :
> Having recently implemented stack traces in v8 I can provide some
> input on this from an implementation perspective.
>   
Thank you for your feedback.
> We have pretty strict performance requirements so I did a lot of
> experimentation to see how much information could be collected without
> affecting performance noticeably.  The result was that it was feasible
> to collect the value of this, the function being called, and the
> current position within the function.  Collecting arguments would have
> been too expensive.  Collecting variables would have been much too
> expensive.
>   
Still nice to know that the most important information (script 
identification and in-script localization), can be collected without too 
much effort.

Unfortunately the current function cannot be converted to JSON yet (as 
it is a function), so I don't know if it can be used. Maybe just add a 
functionName property to the stack frame, it would still be useful for 
named functions. But the state of the this object is definitely useful.
It's a shame that we cannot get information about the variables and 
arguments objects, though. Couldn't it be possible to work around that ?
> Processing the stack trace, say converting objects to JSON, isn't a
> big problem from a performance perspective as long as the processing
> doesn't have to happen when the stack trace is captured.  In v8 we
> capture a "bare metal" trace when an error is created and use an
> accessor to get the .stack property which formats the stack trace the
> first time it is read.
>   
Now, to me the main issue with postponing the information collection 
seems to be the fact that the objects could be altered between the time 
the stack trace is created, and the time the accessor is first read. 
However, if it is the only way to achieve acceptable performance, then 
it is still better than having no information at all.
It should be clearly stated in the API description, so that developers 
know its limitations.

Then, if we accept the idea of keeping only a reference to the objects 
until information about them is explicitly requested, couldn't we do the 
same things for the variables and arguments objects ?
I don't know the internals of V8, but when the engine enters a function, 
does it have some kind of reference to an object holding the accessible 
variables (in case a closure is created inside the given function) ? It 
is not important whether it is just the variables created in the 
function, or all of the variables created in "parent" functions. 
Actually I think it is even better if we can have references to the 
variables created in parents functions as well:

function A(){
    var a = 32;
    function B( c ){
       var b = 48;
       ...
    };
    ...
    B( 64 );
};

Now if we get a stack trace from inside the call to function B( 64 ), 
and if we could have some variables object, we could just store it, and 
the accessor would upon reading return something like { a: 32, b: 48, c: 
64 }. The user of the stack trace can sort it out by himself.
(In that case, the arguments object would be an array with length=1, [ 
64 ] )

Still, since V8 makes a bunch of optimizations (and other modern JS 
engines as well), I guess it may not keep references to all accessible 
variables at all times, if it knows there won't be a closure and it 
won't be needed...

Then, even if that is the case, there are times when performance may not 
matter that much, like in the development phase of an application. Would 
it be possible to have some kind of debugAtAnyCost mode to allow "slower 
but more informative" stack traces ?
> Stack traces should not be limited to built-in errors, they should be
> available and efficient for user-defined errors as well.  In v8 we
>   
Indeed.
> have an Error.captureStackTrace(error, cutoff_opt) function that
> allows you to attach a stack trace to any object, say:
>
> function MyError(message) {
>   this.message = message;
>   Error.captureStackTrace(this, MyError);
> }
>
> If you pass a function as the cutoff argument all stack frames above
> and include the topmost call to that function will not be included in
> the stack frame.  This was intended to solve the getStackTrace problem
> you describe in the proposal but it turns out that it can also be
> useful to hide more of the stack trace, to avoid cluttering it with
> the internal mechanics of library code.
>   
If I understand it well, Error.captureStackTrace only works with an 
object that has been used with throw ?
It is OK with me.

However, now that I think about it, if we want to add the "functionName" 
to the stack frame, it would allow users to implement almost the same 
functionality with a loop. This may be slower than your approach 
however, so we can probably have both.
By the way, could Error.captureStackTrace accept more than one cutoff 
optional argument ? This way, the user can for example remove any of the 
functions it may be called from.
> Having a getStackTrace function might be useful but is problematic
> because, as I read it, it both collects and formats the stack trace,
> which is expensive.  For performance reasons we want to postpone
> formatting using an accessor.  That's the reason for the otherwise odd
> api of passing the error object into captureStackTrace and having it
> set up the appropriate accessor.
>   
It seems OK to me as well, but if someone really needs a stack trace 
from somewhere, she can just throw an exception, catch it, and use 
captureStackTrace on it, so it won't really prevent code from getting a 
stack trace anywhere it wants. So the spec should really warn the users 
about possible performance degradation when using this feature.

Regards,

Jordan OSETE
 
> -- Christian
>
> On Thu, Aug 13, 2009 at 12:33 AM, Jordan Osete<jor at joozt.net> wrote:
>   
>> (Sorry for the late answer. Other stuff got this thread out of my mind.)
>>
>> I understand that specifying a complete stratified API is necessary, however
>> as it is still far from being ready yet, would it still be possible to
>> implement something close to my first proposal (minus the offending parts) ?
>> As it stays quite simplistic (and only requires defining a single function),
>> it could be ready much earlier, and would be a convenient replacement for
>> the time being, until a complete stratified API is ready.
>>
>> Well, just in case, here is what I would change about it today:
>>
>> ----------------------------
>>
>> Keep some kind of global getStackTrace() function to get an array of stack
>> frames. Each stack frame object has a number of properties to provide
>> information about the given stack frame. I had divided this proposal in
>> sections:
>>
>> To identify what script we are in ("Script identification" section):
>> - I would not remove anything from this section.
>> - Maybe add an evalSourceURL property to identify the source file name for
>> "named evals" (with "hacks" like this one, if something like that is ever
>> standardized:
>> http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/).
>>
>> To identify where in the script we are ("In-script localization"):
>> - I would keep this section as it is.
>>
>> And about the environment:
>> - This is the offending part that needs rewriting, as it is the one to
>> propose providing direct references to objects and functions in the running
>> environment (thus violating the JS-closures encapsuiation mechanism).
>> - To avoid the direct references, while still providing most of the
>> available information, those objects could be converted to a different
>> representation of them, a representation which would not expose everything
>> we want to keep safe.
>> For example, the implementation could transform the object to JSON and
>> provide the JSON'ed representation (it would have to watch for circular
>> references though). Or create something like a "structured clone"
>> (http://www.whatwg.org/specs/web-apps/current-work/multipage/infrastructure.html#structured-clone),
>> or anything of the sort.
>> - It would still not be perfect, as it would provide no information for
>> things like circular references, nor functions, or native objects (both JSON
>> and structured-cloning algorythms would have to ignore any of these). It
>> would also not allow testing for strict equality between objects, etc.
>> However, it would still provide a bunch of useful information, hopefully
>> enough for most use cases, while still keeping it simple.
>> Actually, I really like the idea of using JSON. It is something that already
>> exists, is well known, well understood, human readable, and widely
>> supported. As it is a string, it can also easily be sent to a server or
>> stored somewhere for later inspection.
>> - So for example, if we use JSON representation, 3 properties would remain
>> for this section: thisObj (the JSON-Representation of the this object in the
>> given stack frame), arguments (JSON-R of the arguments object), and
>> variables (key-value  pairs containing information about every variable
>> accessible in the stack frame). The function object currently executed can
>> not be represented.
>>
>> I hope this makes the proposal more acceptable. It can of course still be
>> improved or extended.
>>
>> Regards,
>>
>> Jordan OSETE
>>
>>
>> Mark S. Miller a écrit :
>>
>> On Tue, Jun 16, 2009 at 7:18 AM, Jordan Osete <jor at joozt.net> wrote:
>>     
>>> After a quick look into this PDF (very instructive, thanks), I think I
>>> understand the advantages of having a mirror API that is clearly distinct
>>> from the rest of the ES API.
>>> However, ES already has a number of reflection features built in, that are
>>> clearly not stratified. From the prototype and constructor property,
>>> for...in statements that allow to list properties, typeof, instanceof,
>>> including new ones like the functions to __define / __lookup a Getter__ /
>>> Setter__,
>>>       
>> Yes, JavaScript is already pervasively reflective in a non-stratified way.
>> However, none of these violate the encapsulation of the one and only
>> encapsulation mechanism present in EcmaScript -- functions evaluating to
>> lexical closure that capture the variables in their scope. All the new
>> reflective operators introduced by ES5 were careful to respect this boundary
>> as well. De-facto JavaScript does have further reflective operators that do
>> violate encapsulation -- <function-instance>.caller,
>> <function-instance>.arguments, arguments.caller, and arguments.callee. ES5
>> specifies that these be disabled for strict functions so that the
>> encapsulation of strict functions remains defensible.
>>
>>     
>>> including the actual stack traces API (like the Mozilla Stack object
>>> https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error/stack).
>>>       
>> This does violate information encapsulation and so does threaten
>> confidentiality. However, it provides no access and so does not threaten
>> integrity.
>>
>>     
>>> Also, there are a variety of environments a JS "thread" can run in today.
>>> For browsers, it may run in an HTML page, or in a Web worker, but it may
>>> also run in a command-line interpreter, and even as a server-side language.
>>> Will all those need different ways to get JS threads for examination ?
>>>
>>> As for the browser, for example (sorry, it is by far the environment I am
>>> most familiar with), I think it would be nice to still allow a page to
>>> access this mirror API to "explore" another page, without needing explicit
>>> user consent under certain conditions (like a same-origin policy for the
>>> inspector / inspected page, for example). Test suites come to mind.
>>> Would this kind of things be acceptable, from a security point of view ?
>>>       
>> Not by itself, no.
>>
>>     
>>> It *seems* Opera Dragonfly is using some kind of "Scope" thing as its
>>> mirror / proxy API
>>> (http://dev.opera.com/articles/view/opera-dragonfly-architecture/), and then
>>> the rest is pure web (Opera developers' insight would be welcome on that
>>> matter).
>>> However (still about the browser), even the proxy part can be moved to
>>> pure web technologies (like XHR, through some server), in a debugging lib.
>>> That would leave only the mirror API to define in the standard.
>>>       
>> Will take a look. Thanks for the pointer.
>>
>> --
>>    Cheers,
>>    --MarkM
>>
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>>
>>     
>
>   

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


More information about the es-discuss mailing list