document jsdIDebuggerService.pauseDepth changes by the platform

RESOLVED WONTFIX

Status

()

Core
JavaScript Engine
RESOLVED WONTFIX
8 years ago
7 years ago

People

(Reporter: John J. Barton, Unassigned)

Tracking

Trunk
Points:
---

Firefox Tracking Flags

(Not tracked)

Details

(Reporter)

Description

8 years ago
The current API documentation gives the strong impression that jsd.pauseDepth is controlled by jsd.pause() and jsd.unpause(). No documentation on jsd that I have read in my 2 years on Firebug gave me any idea that the platform may also change this value. See:
http://mxr.mozilla.org/mozilla/source/js/jsd/idl/jsdIDebuggerService.idl#246
 
Boris told me and I confirmed experimentally that the platform changes the pauseDepth. It seems to add 2 for a call to enterNestedEventLoop().
(Reporter)

Updated

8 years ago
Blocks: 449452
"The platform" just calls pause() and and unpause(), so your impression is correct.  It's just your assumption that "you" are the only caller of pause() and unpause() that's incorrect.

So I'm not quite sure what there is to document...  You just shouldn't be assuming that you're the only caller of particular XPCOM API methods.  I don't see why such an assumption would ever be reasonable...
In all honesty, I have no idea why the pauseDepth API exists at all.  It's not particularly useful; in particular I can't think of any way someone could use it at all without making exactly the assumption that they're the only debugger service consumer and the only caller of any debugger service methods...
(Reporter)

Comment 3

8 years ago
Which ever assumption I made that is incorrect, I made based on the documentation (or lack of).
I really don't see how the documentation can keep you from making this assumption.  Should every single method on every single interface be documented as "just because you call this method doesn't mean that no one else does"?

Seriously, I'm happy to improve the documentation; I just don't see how.  Any suggestions?
(Reporter)

Comment 5

8 years ago
I suggest that the documentation for enterNestedEventLoop: 
http://mxr.mozilla.org/mozilla/source/js/jsd/idl/jsdIDebuggerService.idl#375
change from:
  Push a new network queue, and enter a new UI event loop.
to
  Push a new network queue, enter a new UI event loop, and call pause() to disable hooks.

and 
http://mxr.mozilla.org/mozilla/source/js/jsd/idl/jsdIDebuggerService.idl#384

   Exit the current nested event loop after the current iteration completes,
   and pop the network event queue.
to
   Exit the current nested event loop after the current iteration completes,
   pop the network event queue, and call unpause() to match the calls in
   enterNestedEventLoop().

and for pause()
http://mxr.mozilla.org/mozilla/source/js/jsd/idl/jsdIDebuggerService.idl#253

  Temporarily disable the debugger.  Hooks will not be called while the
  debugger is paused.  Multiple calls to pause will increase the "pause
  depth", and equal number of unPause calles must be made to resume
  normal debugging.
to
  Temporarily disable the debugger.  Hooks will not be called while the
  debugger is paused.  Multiple calls to pause will increase the "pause
  depth", and equal number of unPause calls must be made to resume
  normal debugging. enterNestedEventLoop() increases the pause depth; 
  exitNestedEventLoop() decreases it.
> Push a new network queue, enter a new UI event loop, and call pause() to
> disable hooks.

That's not what happens, though.  EnterNestedEventLoop doesn't pause anything by itself; it only pauses around the call to the callback.  Is that what you're observing?  The debugger pauses itself before calls into ANY debugger callback, and unpauses after.  It sort of has to, if you think about it: you don't want it debugging the callback code, after all.
(Reporter)

Comment 7

8 years ago
Ok now we know even more, that's great. 

pause()
http://mxr.mozilla.org/mozilla/source/js/jsd/idl/jsdIDebuggerService.idl#253
from:
  Temporarily disable the debugger.  Hooks will not be called while the
  debugger is paused.  Multiple calls to pause will increase the "pause
  depth", and equal number of unPause calles must be made to resume
  normal debugging.
to
  Temporarily disable the debugger.  Hooks will not be called while the
  debugger is paused.  Multiple calls to pause will increase the "pause
  depth", and equal number of unPause calls must be made to resume
  normal debugging. The debugger service itself calls pause() before 
  calling any hook or other callback and calls unpause() upon return.
Comment 7 sounds fine to me.  Do you want to just write up a patch as long as you're here?  Or want me to do it?
(Reporter)

Comment 9

8 years ago
I can do it next week when I am on a machine with m-c source loaded.

Comment 10

8 years ago
I'm not quite sure I like this comment change. (Actually, I'm fairly certain I dislike it).

A more accurate set of statements follow:
 "You are required to call unpause() once for each call to pause() you make." 
 "This method like all other public methods might be called by others, as such you should not assume that merely because pauseLevel was 1 and you called unpause that the pauseLevel will reach 0 and that the debugger will thus be unpaused."

 As a comment to pauseLevel():
 The pauseLevel() during a callback will be one greater than the natural pauseLevel because the debugger is paused while it makes calls to callbacks. You can *not* assert that it will in fact be one, it might be some other number.

Comment 11

8 years ago
Perhaps a more useful tidbit:

It is *NOT* the job of an IDL file to document how an IMPLEMENTATION should call one of its own public functions to effect the result of another function.

It is *ALMOST* ok for an IDL file to document the state of a given function during another call. But only if we actually believe that it will be true for ALL implementations of an interface.

Please do remember that IDL files define interfaces, they are not equivalent to contracts against a specific implementation.

While it is often the case that there is only one implementation of a given interface, that doesn't mean it is always true or that the implementor wishes to be bound by things not currently written into the interface.
(Reporter)

Comment 12

8 years ago
While in general I agree with your concerns, the practical facts here are different.  There is only one implementation of this interface. The only "others" here is the implementation itself. (Efforts to use this interface in two extensions fail). The goal here is just a bit more documentation on a puzzling and arcane system, not a contract.

Comment 13

8 years ago
Many clients of the jsdIDebuggerService do want/need exclusive access to it.

For instance, the Page Speed Activity profiler expects to get OnCall() callbacks for each function entry/exit.

When multiple clients of jsdIDebuggerService are active at the same time, this expectation may no longer hold. Another client might pause() the jsdIDebuggerService causing callbacks to stop getting called. Likewise another client might add unexpected jsdIFilters.

Because there's no easy way to communicate between jsdIDebuggerService clients, when Page Speed Activity starts up we unwind the pause depth to zero and remove all filters. I'd prefer to not do this but I don't have a choice. Page Speed Activity needs to be the exclusive controller of the jsdIDebuggerService so we just reset the jsdIDebuggerService state during set up, and try to restore that state on tear-down. This is of course very fragile but it's the best we can do.

It would be nice if jsdIDebuggerService exposed an API to acquire the jsdIDebuggerService. This acquisition could be informational only to help prevent multiple clients from stepping on each others' toes. Here's how I see it working:

var jsd = ...;  // get a handle to the jsd
var myName = 'Page Speed Activity';
var ownerName = jsd.tryAcquire(myName);
if (ownerName != myName) {
  alert('Client ' + ownerName + ' is currently using the JSD. Please disable that client in order to continue.');
  return;
}
try {
... we hold the "lock" ...
} finally {
  // we've released the "lock"
  jsd.release(myName);
}

tryAcquire() should also reset the jsdIDebuggerService state (clear all filters, reset pause depth, etc). release() could also reset the state.

None of the existing jsdIDebuggerService methods would assert that the caller is the one holding the lock. It's just the expectation of jsdIDebuggerService clients that you only muck with the jsdIDebuggerService state if you are the one holding the lock. It's up to the clients to be well behaved about this. Clients that were written before the new tryAcquire/release API was added would continue to work, they just wouldn't be well behaved since they wouldn't use this API.

If a client fails to call release(), this could be potentially problematic (jsdIDebuggerService is held forever), but that'd be a bug in the jsdIDebuggerService client which would need to be fixed.

John, would this kind of API be useful to you in Firebug?
Given that nowadays the function hook perturbs the performance of the JS engine to the point where the results have little bearing on real performance, it might be more worthwhile to figure out how the new profiling APIs being worked on (bug 507012) should interact with the use cases you have than trying to polish the already-broken jsd behavior.
(Reporter)

Comment 15

8 years ago
Bryan, as a practical matter we will have to deal with this ourselves.
Status: NEW → RESOLVED
Last Resolved: 8 years ago
Resolution: --- → WONTFIX
(Assignee)

Updated

7 years ago
Component: JavaScript Debugging/Profiling APIs → JavaScript Engine
Product: Core → Core
You need to log in before you can comment on or make changes to this bug.