Closed Bug 1483319 Opened 6 years ago Closed 6 years ago

Console messages are not shown for todomvc

Categories

(Core Graveyard :: Web Replay, defect)

defect
Not set
normal

Tracking

(firefox63 fixed)

RESOLVED FIXED
mozilla63
Tracking Status
firefox63 --- fixed

People

(Reporter: jlast, Assigned: bhackett1024)

Details

Attachments

(6 files)

STR:
1. open devtools console
2. go to https://firefox-dev.tools/debugger-examples/examples/todomvc/

ER: see a couple "todo created" messages
AR: see an error in the browser console "hud" or "jsterm" is undefined
Attached patch patchSplinter Review
The basic problem here is that the console message support in web replay doesn't handle objects that are passed to console.log() calls.  We throw an exception in the web console's server code and then things break.  The attached patch fixes this, and the console messages and associated object previews show up in the web console window.  I'll break it up for review in a minute, as some internal things had to change in order to support this.

There's one aspect of the web console UI that still doesn't work right.  There is a drop down menu on logged objects that allows users to explore their properties, prototype chains, and so forth.  When we are paused at the point where we received a console message, this menu can be accessed, but if we have rewound or run forwards then the drop down menu doesn't work (well, usually doesn't; there is some client side caching of the results going on that sometimes masks this).  This is because the server is using the same Debugger.Object that it got when adding the message, and when replaying Debugger.Objects become unusable after unpausing.  I didn't fix this because I'm not sure what to do about it; the best thing might be to remove the drop down menu when replaying, as users can just warp to the point where the message was logged and inspect the object's original state (and all other state) at that point.
Assignee: nobody → bhackett1024
Currently, we convert object arguments to console messages into JSON and pass them up to the middleman process.  The devtools server code running there can't create ReplayDebuggerObjects for these objects, as it can only do so for objects that exist in the replaying process.  This patch changes how console messages are transmitted to the middleman process so that values representing replaying process values are used, which the ReplayDebugger in the server can convert to ReplayDebuggerObjects.  The web console server code can then use these as is when handing grips for them up to the client for processing.
Attachment #9001816 - Flags: review?(jimb)
This isn't really related to the other parts of this bug, but I ran into it while testing.  replayForcedPause pauses need to be treated by the debugger client as unsolicited, or else it might interpret them as responses to messages it has sent and get terminally confused.
Attachment #9001817 - Flags: review?(jimb)
Currently, we have different phases in the navigation code for being paused at a checkpoint or paused at an intra-checkpoint execution point.  These are similar in a lot of ways but the latter allows the debugger a lot more latitude: it can perform queries on the process that might trigger an unhandled recording divergence, which are necessary in order to inspect the contents of objects that are logged by the console.  If the replaying process pauses at a checkpoint and the server code tries to look at the arguments to console messages that have been logged so far, then that server code will not be able to inspect the contents of objects that have been logged.

The best solution here is I think to merge these two phases into a single Paused phase.  This allows debugger queries to be performed anywhere the process might be paused.  This patch makes this change, and also reworks how temporary checkpoints are taken when paused so that they are only saved when dealing with the first request that might trigger an unhandled recording divergence.  This avoids saving (and, later on, restoring) temporary checkpoints whenever a replaying process reaches a checkpoint.
Attachment #9001819 - Flags: review?(continuation)
Currently, when pausing at a breakpoint (other than NewScript, via the junky IsUserBreakpoint heuristic method) the middleman process ensures that the debugger is interacting with a replaying process that can perform operations that might trigger unhandled recording divergences.  This strategy doesn't work when such queries might happen at checkpoints, though --- if we did this eagerly we would have to switch to a replaying child whenever the recording child reaches a checkpoint, which would be pretty slow.

The attached patch removes this heuristic from the middleman's code and encodes it as a RecordReplayControl method, so that the ReplayDebugger can ask the middleman process to make sure it is communicating with a replaying process.
Attachment #9001820 - Flags: review?(continuation)
This patch uses the API added in part 4 to ensure that the debugger is interacting with a replaying process when necessary: a query is being sent to the child process which might trigger a recording divergence or other side effect.
Attachment #9001821 - Flags: review?(jimb)
Attachment #9001821 - Attachment is patch: true
Attachment #9001819 - Attachment description: Combine BreakpointPaused and CheckpointPaused phases. → Part 3 - Combine BreakpointPaused and CheckpointPaused phases.
Comment on attachment 9001819 [details] [diff] [review]
Part 3 - Combine BreakpointPaused and CheckpointPaused phases.

Review of attachment 9001819 [details] [diff] [review]:
-----------------------------------------------------------------

::: toolkit/recordreplay/ipc/ChildNavigation.cpp
@@ +633,3 @@
>  
> +  if (!mResumeForward && !mRecoveringFromDivergence) {
> +    // We processed this request normally, remember the response and send it to

nit: I think this should be "normally. Remember".

@@ +699,5 @@
>    }
> +
> +  if (!EnsureTemporaryCheckpoint()) {
> +    // One of the premature exit cases was hit in EnsureTemporaryCheckpoint,
> +    // don't allow any operations that can diverge from the recording.

Nit: split this into two sentences or use a colon instead of the comma.
Attachment #9001819 - Flags: review?(continuation) → review+
Comment on attachment 9001820 [details] [diff] [review]
Part 4 - Give ReplayDebugger control over when the active child should be replaying.

Review of attachment 9001820 [details] [diff] [review]:
-----------------------------------------------------------------

::: toolkit/recordreplay/ipc/ParentIPC.cpp
@@ +821,5 @@
>  
> +void
> +EnsureActiveChildIsReplaying()
> +{
> +  if (gActiveChild->IsRecording() && CanRewind()) {

Why does this just do nothing if we're recording but we can't rewind? I would have expected an assert or a JS exception or something.
Attachment #9001820 - Flags: review?(continuation) → review+
(In reply to Andrew McCreight [:mccr8] from comment #8)
> Comment on attachment 9001820 [details] [diff] [review]
> Part 4 - Give ReplayDebugger control over when the active child should be
> replaying.
> 
> Review of attachment 9001820 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: toolkit/recordreplay/ipc/ParentIPC.cpp
> @@ +821,5 @@
> >  
> > +void
> > +EnsureActiveChildIsReplaying()
> > +{
> > +  if (gActiveChild->IsRecording() && CanRewind()) {
> 
> Why does this just do nothing if we're recording but we can't rewind? I
> would have expected an assert or a JS exception or something.

This situation is possible if the devtools.recordreplay.enableRewinding pref is unset.  In that case, when the ReplayDebugger sends a request to the child process that might trigger a recording divergence, the recording process return a response that indicates the request could not be processed --- the same response that is sent when an unhandled recording divergence actually occurred (e.g. trying to eval 'window.alert("foo")').  When the ReplayDebugger receives this response it could throw if it wanted to, but doing that will usually break the debugger.  Leaving things as a no-op here gives other code more latitude in how to deal with this situation, and the methods for doing so are the same as for the more common case of a request that triggered an unhandled recording divergence.

This method is pretty badly named, though.  I'll change it to MaybeSwitchToReplayingChild.
That makes sense. Yeah, renaming the function is a good idea.
Attachment #9001817 - Flags: review?(jimb) → review+
Comment on attachment 9001816 [details] [diff] [review]
Part 1 - Transmit console message arguments as debuggee values.

Review of attachment 9001816 [details] [diff] [review]:
-----------------------------------------------------------------

::: devtools/server/actors/replay/replay.js
@@ +674,5 @@
>      return gPopFrameResult ? convertCompletionValue(gPopFrameResult) : {};
>    },
>  
>    findConsoleMessages(request) {
> +    return gConsoleMessages.map(convertConsoleMessage);

Would it make sense to convert the messages once when they are placed in gConsoleMessages, rather than converting them each time they're extracted?
Attachment #9001816 - Flags: review?(jimb) → review+
Attachment #9001821 - Flags: review?(jimb) → review+
(In reply to Jim Blandy :jimb from comment #11)
> Comment on attachment 9001816 [details] [diff] [review]
> Part 1 - Transmit console message arguments as debuggee values.
> 
> Review of attachment 9001816 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: devtools/server/actors/replay/replay.js
> @@ +674,5 @@
> >      return gPopFrameResult ? convertCompletionValue(gPopFrameResult) : {};
> >    },
> >  
> >    findConsoleMessages(request) {
> > +    return gConsoleMessages.map(convertConsoleMessage);
> 
> Would it make sense to convert the messages once when they are placed in
> gConsoleMessages, rather than converting them each time they're extracted?

Unfortunately, because the converted messages can contain references to debugger objects, which are invalidated when unpausing, we have to do the conversion at same point in execution where the debugger request occurs.
Okay, that makes sense.
Pushed by bhackett@mozilla.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/fd6f1b574cbb
Part 1 - Transmit console message arguments as debuggee values, r=jimb.
https://hg.mozilla.org/integration/mozilla-inbound/rev/ed84aae6166c
Part 2 - Treat replayForcedPause as unsolicited, r=jimb.
https://hg.mozilla.org/integration/mozilla-inbound/rev/2ddf1cb297c5
Part 3 - Combine BreakpointPaused and CheckpointPaused phases, r=mccr8.
https://hg.mozilla.org/integration/mozilla-inbound/rev/4eb666e572f9
Part 4 - Give ReplayDebugger control over when the active child should be replaying, r=mccr8.
https://hg.mozilla.org/integration/mozilla-inbound/rev/8b832de5ad1d
Part 5 - Ensure that the child process is replaying when sending requests that might trigger recording divergence, r=jimb.
QA Whiteboard: [good first verify]
Product: Core → Core Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: