Bug 1516578 Part 8 - ReplayDebugger changes for working with control code, r=lsmyth.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 27 Dec 2018 13:39:15 -1000
changeset 510807 f7361bad84986de51a8f3fbe1954961f50d7b51e
parent 510806 477c15c5f25a3333d9cc0b9cf91e0466b0564235
child 510808 25b80743d6504a24c2e9a981e38cd7ed0fd3b7d5
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsmyth
bugs1516578
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1516578 Part 8 - ReplayDebugger changes for working with control code, r=lsmyth.
devtools/server/actors/replay/debugger.js
--- a/devtools/server/actors/replay/debugger.js
+++ b/devtools/server/actors/replay/debugger.js
@@ -8,18 +8,18 @@
 // When recording/replaying an execution with Web Replay, Devtools server code
 // runs in the middleman process instead of the recording/replaying process the
 // code is interested in.
 //
 // This file defines replay objects analogous to those constructed by the
 // C++ Debugger (Debugger, Debugger.Object, etc.), which implement similar
 // methods and properties to those C++ objects. These replay objects are
 // created in the middleman process, and describe things that exist in the
-// recording/replaying process, inspecting them via the RecordReplayControl
-// interface.
+// recording/replaying process, inspecting them via the interface provided by
+// control.js.
 
 "use strict";
 
 const RecordReplayControl = !isWorker && require("RecordReplayControl");
 const Services = require("Services");
 
 ///////////////////////////////////////////////////////////////////////////////
 // ReplayDebugger
@@ -35,18 +35,18 @@ const Direction = {
 function ReplayDebugger() {
   const existing = RecordReplayControl.registerReplayDebugger(this);
   if (existing) {
     // There is already a ReplayDebugger in existence, use that. There can only
     // be one ReplayDebugger in the process.
     return existing;
   }
 
-  // Whether the process is currently paused.
-  this._paused = false;
+  // We should have been connected to control.js by the call above.
+  assert(this._control);
 
   // Preferred direction of travel when not explicitly resumed.
   this._direction = Direction.NONE;
 
   // All breakpoint positions and handlers installed by this debugger.
   this._breakpoints = [];
 
   // All ReplayDebuggerFramees that have been created while paused at the
@@ -92,53 +92,54 @@ ReplayDebugger.prototype = {
   // General methods
   /////////////////////////////////////////////////////////
 
   replaying: true,
 
   canRewind: RecordReplayControl.canRewind,
 
   replayCurrentExecutionPoint() {
-    return this._sendRequest({ type: "currentExecutionPoint" });
+    assert(this._paused);
+    return this._control.pausePoint();
   },
 
   replayRecordingEndpoint() {
     return this._sendRequest({ type: "recordingEndpoint" });
   },
 
-  replayIsRecording: RecordReplayControl.childIsRecording,
+  replayIsRecording() {
+    return this._control.childIsRecording();
+  },
 
   addDebuggee() {},
   removeAllDebuggees() {},
 
   replayingContent(url) {
     this._ensurePaused();
     return this._sendRequest({ type: "getContent", url });
   },
 
   // Send a request object to the child process, and synchronously wait for it
   // to respond.
   _sendRequest(request) {
-    assert(this._paused);
-    const data = RecordReplayControl.sendRequest(request);
+    const data = this._control.sendRequest(request);
     dumpv("SendRequest: " +
           JSON.stringify(request) + " -> " + JSON.stringify(data));
     if (data.exception) {
       ThrowError(data.exception);
     }
     return data;
   },
 
   // Send a request that requires the child process to perform actions that
   // diverge from the recording. In such cases we want to be interacting with a
   // replaying process (if there is one), as recording child processes won't
   // provide useful responses to such requests.
   _sendRequestAllowDiverge(request) {
-    assert(this._paused);
-    RecordReplayControl.maybeSwitchToReplayingChild();
+    this._control.maybeSwitchToReplayingChild();
     return this._sendRequest(request);
   },
 
   // Update graphics according to the current state of the child process. This
   // should be done anytime we pause and allow the user to interact with the
   // debugger.
   _repaint() {
     const rv = this._sendRequestAllowDiverge({ type: "repaint" });
@@ -168,41 +169,43 @@ ReplayDebugger.prototype = {
   //   loop is running that is *not* associated with the thread actor's nested
   //   pauses. As long as the thread actor has pushed a pause, the child will
   //   remain paused.
   //
   // - After the child resumes, installed breakpoint handlers will only execute
   //   when an event loop is running (which, because of the above point, cannot
   //   be associated with a thread actor's nested pause).
 
+  get _paused() {
+    return !!this._control.pausePoint();
+  },
+
   replayResumeBackward() { this._resume(/* forward = */ false); },
   replayResumeForward() { this._resume(/* forward = */ true); },
 
   _resume(forward) {
     this._ensurePaused();
     this._setResume(() => {
-      this._paused = false;
       this._direction = forward ? Direction.FORWARD : Direction.BACKWARD;
       dumpv("Resuming " + this._direction);
-      RecordReplayControl.resume(forward);
+      this._control.resume(forward);
       if (this._paused) {
         // If we resume and immediately pause, we are at an endpoint of the
         // recording. Force the thread to pause.
         this.replayingOnForcedPause(this.getNewestFrame());
       }
     });
   },
 
   replayTimeWarp(target) {
     this._ensurePaused();
     this._setResume(() => {
-      this._paused = false;
       this._direction = Direction.NONE;
       dumpv("Warping " + JSON.stringify(target));
-      RecordReplayControl.timeWarp(target);
+      this._control.timeWarp(target);
 
       // timeWarp() doesn't return until the child has reached the target of
       // the warp, after which we force the thread to pause.
       assert(this._paused);
       this.replayingOnForcedPause(this.getNewestFrame());
     });
   },
 
@@ -210,27 +213,25 @@ ReplayDebugger.prototype = {
     this._ensurePaused();
 
     // Cancel any pending resume.
     this._resumeCallback = null;
   },
 
   _ensurePaused() {
     if (!this._paused) {
-      RecordReplayControl.waitUntilPaused();
+      this._control.waitUntilPaused();
       assert(this._paused);
     }
   },
 
   // This hook is called whenever the child has paused, which can happen
-  // within a RecordReplayControl method (resume, timeWarp, waitUntilPaused) or
-  // or be delivered via the event loop.
+  // within a control method (resume, timeWarp, waitUntilPaused) or be
+  // delivered via the event loop.
   _onPause() {
-    this._paused = true;
-
     // The position change handler is always called on pause notifications.
     if (this.replayingOnPositionChange) {
       this.replayingOnPositionChange();
     }
 
     // Call _performPause() soon via the event loop to check for breakpoint
     // handlers at this point.
     this._cancelPerformPause = false;
@@ -243,17 +244,17 @@ ReplayDebugger.prototype = {
     // have already told the child to resume, don't call handlers.
     if (!this._paused || this._cancelPerformPause || this._resumeCallback) {
       return;
     }
 
     const point = this.replayCurrentExecutionPoint();
     dumpv("PerformPause " + JSON.stringify(point));
 
-    if (point.position.kind == "Invalid") {
+    if (!point.position) {
       // We paused at a checkpoint, and there are no handlers to call.
     } else {
       // Call any handlers for this point, unless one resumes execution.
       for (const { handler, position } of this._breakpoints) {
         if (RecordReplayControl.positionSubsumes(position, point.position)) {
           handler();
           assert(!this._threadPauseCount);
           if (this._resumeCallback) {
@@ -275,32 +276,28 @@ ReplayDebugger.prototype = {
     }
   },
 
   // This hook is called whenever we switch between recording and replaying
   // child processes.
   _onSwitchChild() {
     // The position change handler listens to changes to the current child.
     if (this.replayingOnPositionChange) {
-      // Children are paused whenever we switch between them.
-      const paused = this._paused;
-      this._paused = true;
       this.replayingOnPositionChange();
-      this._paused = paused;
     }
   },
 
   replayPushThreadPause() {
     // The thread has paused so that the user can interact with it. The child
     // will stay paused until this thread-wide pause has been popped.
     assert(this._paused);
     assert(!this._resumeCallback);
     if (++this._threadPauseCount == 1) {
       // Save checkpoints near the current position in case the user rewinds.
-      RecordReplayControl.markExplicitPause();
+      this._control.markExplicitPause();
 
       // There is no preferred direction of travel after an explicit pause.
       this._direction = Direction.NONE;
 
       // Update graphics according to the current state of the child.
       this._repaint();
 
       // If breakpoint handlers for the pause haven't been called yet, don't
@@ -353,29 +350,29 @@ ReplayDebugger.prototype = {
 
   /////////////////////////////////////////////////////////
   // Breakpoint management
   /////////////////////////////////////////////////////////
 
   _setBreakpoint(handler, position, data) {
     this._ensurePaused();
     dumpv("AddBreakpoint " + JSON.stringify(position));
-    RecordReplayControl.addBreakpoint(position);
+    this._control.addBreakpoint(position);
     this._breakpoints.push({handler, position, data});
   },
 
   _clearMatchingBreakpoints(callback) {
     this._ensurePaused();
     const newBreakpoints = this._breakpoints.filter(bp => !callback(bp));
     if (newBreakpoints.length != this._breakpoints.length) {
       dumpv("ClearBreakpoints");
-      RecordReplayControl.clearBreakpoints();
+      this._control.clearBreakpoints();
       for (const { position } of newBreakpoints) {
         dumpv("AddBreakpoint " + JSON.stringify(position));
-        RecordReplayControl.addBreakpoint(position);
+        this._control.addBreakpoint(position);
       }
     }
     this._breakpoints = newBreakpoints;
   },
 
   _searchBreakpoints(callback) {
     for (const breakpoint of this._breakpoints) {
       const v = callback(breakpoint);