Bug 1516578 Part 8 - ReplayDebugger changes for working with control code, r=lsmyth.
☠☠ backed out by 9e3564442734 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 27 Dec 2018 13:39:15 -1000
changeset 513644 ab90c34ff486cc6b637b234b7d801987b8364090
parent 513643 6689773a4e1caafd89480b2c6182c5a41fc081ce
child 513645 ec9a7aa1898ccb5b0e227847ba9c4434236524a9
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [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);