Bug 1479058 Part 2 - Add RunToPoint message and handling in child, r=mccr8.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 02 Aug 2018 23:27:39 +0000
changeset 430180 d0166e64481a1c6f6e935c842c914f30209b3522
parent 430179 bbc1cd3eb21a63f3751d4714b67b0d56f2aef575
child 430181 2f09369fc42f4107b3346ad5ef1c86b4f9ebf931
push id34387
push usernbeleuzu@mozilla.com
push dateSun, 05 Aug 2018 09:45:59 +0000
treeherdermozilla-central@b3e7de2df93d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1479058
milestone63.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 1479058 Part 2 - Add RunToPoint message and handling in child, r=mccr8.
toolkit/recordreplay/ipc/Channel.h
toolkit/recordreplay/ipc/ChildIPC.cpp
toolkit/recordreplay/ipc/ChildInternal.h
toolkit/recordreplay/ipc/ChildNavigation.cpp
--- a/toolkit/recordreplay/ipc/Channel.h
+++ b/toolkit/recordreplay/ipc/Channel.h
@@ -74,16 +74,20 @@ namespace recordreplay {
   /* Unpause the child and play execution either to the next point when a */ \
   /* breakpoint is hit, or to the next checkpoint. Resumption may be either */ \
   /* forward or backward. */                                   \
   _Macro(Resume)                                               \
                                                                \
   /* Rewind to a particular saved checkpoint in the past. */   \
   _Macro(RestoreCheckpoint)                                    \
                                                                \
+  /* Run forward to a particular execution point between the current checkpoint */ \
+  /* and the next one. */                                      \
+  _Macro(RunToPoint)                                           \
+                                                               \
   /* Notify the child whether it is the active child and should send paint and similar */ \
   /* messages to the middleman. */                             \
   _Macro(SetIsActive)                                          \
                                                                \
   /* Set whether to perform intentional crashes, for testing. */ \
   _Macro(SetAllowIntentionalCrashes)                           \
                                                                \
   /* Set whether to save a particular checkpoint. */           \
@@ -278,16 +282,27 @@ struct RestoreCheckpointMessage : public
   size_t mCheckpoint;
 
   explicit RestoreCheckpointMessage(size_t aCheckpoint)
     : Message(MessageType::RestoreCheckpoint, sizeof(*this))
     , mCheckpoint(aCheckpoint)
   {}
 };
 
+struct RunToPointMessage : public Message
+{
+  // The target execution point.
+  js::ExecutionPoint mTarget;
+
+  explicit RunToPointMessage(const js::ExecutionPoint& aTarget)
+    : Message(MessageType::RunToPoint, sizeof(*this))
+    , mTarget(aTarget)
+  {}
+};
+
 struct SetIsActiveMessage : public Message
 {
   // Whether this is the active child process (see ParentIPC.cpp).
   bool mActive;
 
   explicit SetIsActiveMessage(bool aActive)
     : Message(MessageType::SetIsActive, sizeof(*this))
     , mActive(aActive)
--- a/toolkit/recordreplay/ipc/ChildIPC.cpp
+++ b/toolkit/recordreplay/ipc/ChildIPC.cpp
@@ -131,16 +131,23 @@ ChannelMessageHandler(Message* aMsg)
   }
   case MessageType::RestoreCheckpoint: {
     const RestoreCheckpointMessage& nmsg = (const RestoreCheckpointMessage&) *aMsg;
     PauseMainThreadAndInvokeCallback([=]() {
         navigation::RestoreCheckpoint(nmsg.mCheckpoint);
       });
     break;
   }
+  case MessageType::RunToPoint: {
+    const RunToPointMessage& nmsg = (const RunToPointMessage&) *aMsg;
+    PauseMainThreadAndInvokeCallback([=]() {
+        navigation::RunToPoint(nmsg.mTarget);
+      });
+    break;
+  }
   default:
     MOZ_CRASH();
   }
 
   free(aMsg);
 }
 
 // Main routine for a thread whose sole purpose is to listen to requests from
--- a/toolkit/recordreplay/ipc/ChildInternal.h
+++ b/toolkit/recordreplay/ipc/ChildInternal.h
@@ -37,16 +37,17 @@ void SetRecordingEndpoint(size_t aIndex,
 // Save temporary checkpoints at all opportunities during navigation.
 void AlwaysSaveTemporaryCheckpoints();
 
 // Process incoming requests from the middleman.
 void DebuggerRequest(js::CharBuffer* aBuffer);
 void SetBreakpoint(size_t aId, const js::BreakpointPosition& aPosition);
 void Resume(bool aForward);
 void RestoreCheckpoint(size_t aId);
+void RunToPoint(const js::ExecutionPoint& aPoint);
 
 // Attempt to diverge from the recording so that new recorded events cause
 // the process to rewind. Returns false if the divergence failed: either we
 // can't rewind, or already diverged here and then had an unhandled divergence.
 bool MaybeDivergeFromRecording();
 
 // Notify navigation that a position was hit.
 void PositionHit(const js::BreakpointPosition& aPosition);
--- a/toolkit/recordreplay/ipc/ChildNavigation.cpp
+++ b/toolkit/recordreplay/ipc/ChildNavigation.cpp
@@ -94,16 +94,21 @@ public:
     Unsupported("Resume");
   }
 
   // Called after the middleman tells us to rewind to a specific checkpoint.
   virtual void RestoreCheckpoint(size_t aCheckpoint) {
     Unsupported("RestoreCheckpoint");
   }
 
+  // Called after the middleman tells us to run forward to a specific point.
+  virtual void RunToPoint(const ExecutionPoint& aTarget) {
+    Unsupported("RunToPoint");
+  }
+
   // Process an incoming debugger request from the middleman.
   virtual void HandleDebuggerRequest(js::CharBuffer* aRequestBuffer) {
     Unsupported("HandleDebuggerRequest");
   }
 
   // Called when a debugger request wants to try an operation that may
   // trigger an unhandled divergence from the recording.
   virtual bool MaybeDivergeFromRecording() {
@@ -161,17 +166,18 @@ class BreakpointPausedPhase final : publ
   // last entry in |mRequests|, though may be earlier if we are recovering
   // from an unhandled divergence.
   size_t mRequestIndex;
 
   // Set when we were told to resume forward and need to clean up our state.
   bool mResumeForward;
 
 public:
-  void Enter(const ExecutionPoint& aPoint, const BreakpointVector& aBreakpoints);
+  void Enter(const ExecutionPoint& aPoint, bool aRecordingEndpoint,
+             const BreakpointVector& aBreakpoints);
 
   void ToString(nsAutoCString& aStr) override {
     aStr.AppendPrintf("BreakpointPaused RecoveringFromDivergence %d", mRecoveringFromDivergence);
   }
 
   void AfterCheckpoint(const CheckpointId& aCheckpoint) override;
   void PositionHit(const ExecutionPoint& aPoint) override;
   void Resume(bool aForward) override;
@@ -185,26 +191,27 @@ public:
 
 // Phase when the replaying process is paused at a normal checkpoint.
 class CheckpointPausedPhase final : public NavigationPhase
 {
   size_t mCheckpoint;
   bool mAtRecordingEndpoint;
 
 public:
-  void Enter(size_t aCheckpoint, bool aRewind, bool aAtRecordingEndpoint);
+  void Enter(size_t aCheckpoint, bool aRewind, bool aRecordingEndpoint);
 
   void ToString(nsAutoCString& aStr) override {
     aStr.AppendPrintf("CheckpointPaused");
   }
 
   void AfterCheckpoint(const CheckpointId& aCheckpoint) override;
   void PositionHit(const ExecutionPoint& aPoint) override;
   void Resume(bool aForward) override;
   void RestoreCheckpoint(size_t aCheckpoint) override;
+  void RunToPoint(const ExecutionPoint& aTarget) override;
   void HandleDebuggerRequest(js::CharBuffer* aRequestBuffer) override;
   ExecutionPoint GetRecordingEndpoint() override;
 };
 
 // Phase when execution is proceeding forwards in search of breakpoint hits.
 class ForwardPhase final : public NavigationPhase
 {
   // Some execution point in the recent past. There are no checkpoints or
@@ -240,18 +247,18 @@ private:
   // Whether we have saved a temporary checkpoint at the specified point.
   bool mSavedTemporaryCheckpoint;
 
   // The time at which we started running forward from the initial
   // checkpoint, in microseconds.
   double mStartTime;
 
 public:
-  // Note: this always rewinds.
   void Enter(const CheckpointId& aStart,
+             bool aRewind,
              const ExecutionPoint& aPoint,
              const Maybe<ExecutionPoint>& aTemporaryCheckpoint);
 
   void ToString(nsAutoCString& aStr) override {
     aStr.AppendPrintf("ReachBreakpoint: ");
     ExecutionPointToString(mPoint, aStr);
     if (mTemporaryCheckpoint.isSome()) {
       aStr.AppendPrintf(" TemporaryCheckpoint: ");
@@ -414,16 +421,20 @@ public:
   void Resume(bool aForward) {
     mPhase->Resume(aForward);
   }
 
   void RestoreCheckpoint(size_t aCheckpoint) {
     mPhase->RestoreCheckpoint(aCheckpoint);
   }
 
+  void RunToPoint(const ExecutionPoint& aTarget) {
+    mPhase->RunToPoint(aTarget);
+  }
+
   void HandleDebuggerRequest(js::CharBuffer* aRequestBuffer) {
     mPhase->HandleDebuggerRequest(aRequestBuffer);
   }
 
   bool MaybeDivergeFromRecording() {
     return mPhase->MaybeDivergeFromRecording();
   }
 
@@ -499,17 +510,18 @@ GetAllBreakpointHits(const ExecutionPoin
 
 static bool
 ThisProcessCanRewind()
 {
   return HasSavedCheckpoint();
 }
 
 void
-BreakpointPausedPhase::Enter(const ExecutionPoint& aPoint, const BreakpointVector& aBreakpoints)
+BreakpointPausedPhase::Enter(const ExecutionPoint& aPoint, bool aRecordingEndpoint,
+                             const BreakpointVector& aBreakpoints)
 {
   MOZ_RELEASE_ASSERT(aPoint.HasPosition());
 
   mPoint = aPoint;
   mRequests.clear();
   mRecoveringFromDivergence = false;
   mRequestIndex = 0;
   mResumeForward = false;
@@ -539,18 +551,17 @@ BreakpointPausedPhase::Enter(const Execu
           });
         Unreachable();
       }
       gNavigation->PositionHit(aPoint);
       return;
     }
   }
 
-  bool endpoint = aBreakpoints.empty();
-  child::HitBreakpoint(endpoint, aBreakpoints.begin(), aBreakpoints.length());
+  child::HitBreakpoint(aRecordingEndpoint, aBreakpoints.begin(), aBreakpoints.length());
 
   // When rewinding is allowed we will rewind before resuming to erase side effects.
   MOZ_RELEASE_ASSERT(!ThisProcessCanRewind());
 }
 
 void
 BreakpointPausedPhase::AfterCheckpoint(const CheckpointId& aCheckpoint)
 {
@@ -595,17 +606,17 @@ BreakpointPausedPhase::Resume(bool aForw
   gNavigation->mFindLastHitPhase.Enter(start, Some(mPoint));
   Unreachable();
 }
 
 void
 BreakpointPausedPhase::RestoreCheckpoint(size_t aCheckpoint)
 {
   gNavigation->mCheckpointPausedPhase.Enter(aCheckpoint, /* aRewind = */ true,
-                                            /* aAtRecordingEndpoint = */ false);
+                                            /* aRecordingEndpoint = */ false);
 }
 
 void
 BreakpointPausedPhase::HandleDebuggerRequest(js::CharBuffer* aRequestBuffer)
 {
   MOZ_RELEASE_ASSERT(!mRecoveringFromDivergence);
 
   mRequests.emplaceBack();
@@ -734,17 +745,26 @@ CheckpointPausedPhase::Resume(bool aForw
     gNavigation->mFindLastHitPhase.Enter(start, Nothing());
     Unreachable();
   }
 }
 
 void
 CheckpointPausedPhase::RestoreCheckpoint(size_t aCheckpoint)
 {
-  Enter(aCheckpoint, /* aRewind = */ true, /* aAtRecordingEndpoint = */ false);
+  Enter(aCheckpoint, aCheckpoint != mCheckpoint, /* aRecordingEndpoint = */ false);
+}
+
+void
+CheckpointPausedPhase::RunToPoint(const ExecutionPoint& aTarget)
+{
+  MOZ_RELEASE_ASSERT(aTarget.mCheckpoint == mCheckpoint);
+  ResumeExecution();
+  gNavigation->mReachBreakpointPhase.Enter(CheckpointId(mCheckpoint), /* aRewind = */ false,
+                                           aTarget, /* aTemporaryCheckpoint = */ Nothing());
 }
 
 void
 CheckpointPausedPhase::HandleDebuggerRequest(js::CharBuffer* aRequestBuffer)
 {
   js::CharBuffer responseBuffer;
   js::ProcessRequest(aRequestBuffer->begin(), aRequestBuffer->length(), &responseBuffer);
 
@@ -781,64 +801,71 @@ ForwardPhase::Enter(const ExecutionPoint
 }
 
 void
 ForwardPhase::AfterCheckpoint(const CheckpointId& aCheckpoint)
 {
   MOZ_RELEASE_ASSERT(!aCheckpoint.mTemporary &&
                      aCheckpoint.mNormal == mPoint.mCheckpoint + 1);
   gNavigation->mCheckpointPausedPhase.Enter(aCheckpoint.mNormal, /* aRewind = */ false,
-                                            /* aAtRecordingEndpoint = */ false);
+                                            /* aRecordingEndpoint = */ false);
 }
 
 void
 ForwardPhase::PositionHit(const ExecutionPoint& aPoint)
 {
   BreakpointVector hitBreakpoints;
   GetAllBreakpointHits(aPoint, hitBreakpoints);
 
   if (!hitBreakpoints.empty()) {
-    gNavigation->mBreakpointPausedPhase.Enter(aPoint, hitBreakpoints);
+    gNavigation->mBreakpointPausedPhase.Enter(aPoint, /* aRecordingEndpoint = */ false,
+                                              hitBreakpoints);
   }
 }
 
 void
 ForwardPhase::HitRecordingEndpoint(const ExecutionPoint& aPoint)
 {
   if (aPoint.HasPosition()) {
     BreakpointVector emptyBreakpoints;
-    gNavigation->mBreakpointPausedPhase.Enter(aPoint, emptyBreakpoints);
+    gNavigation->mBreakpointPausedPhase.Enter(aPoint, /* aRecordingEndpoint = */ true,
+                                              emptyBreakpoints);
   } else {
     gNavigation->mCheckpointPausedPhase.Enter(aPoint.mCheckpoint, /* aRewind = */ false,
-                                              /* aAtRecordingEndpoint = */ true);
+                                              /* aRecordingEndpoint = */ true);
   }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // ReachBreakpointPhase
 ///////////////////////////////////////////////////////////////////////////////
 
 void
 ReachBreakpointPhase::Enter(const CheckpointId& aStart,
+                            bool aRewind,
                             const ExecutionPoint& aPoint,
                             const Maybe<ExecutionPoint>& aTemporaryCheckpoint)
 {
   MOZ_RELEASE_ASSERT(aPoint.HasPosition());
   MOZ_RELEASE_ASSERT(aTemporaryCheckpoint.isNothing() ||
                      (aTemporaryCheckpoint.ref().HasPosition() &&
                       aTemporaryCheckpoint.ref() != aPoint));
   mStart = aStart;
   mPoint = aPoint;
   mTemporaryCheckpoint = aTemporaryCheckpoint;
   mSavedTemporaryCheckpoint = false;
 
   gNavigation->SetPhase(this);
 
-  RestoreCheckpointAndResume(aStart);
-  Unreachable();
+  if (aRewind) {
+    RestoreCheckpointAndResume(aStart);
+    Unreachable();
+  } else {
+    AfterCheckpoint(aStart);
+  }
 }
 
 void
 ReachBreakpointPhase::AfterCheckpoint(const CheckpointId& aCheckpoint)
 {
   if (aCheckpoint == mStart && mTemporaryCheckpoint.isSome()) {
     js::EnsurePositionHandler(mTemporaryCheckpoint.ref().mPosition);
 
@@ -882,19 +909,19 @@ ReachBreakpointPhase::PositionHit(const 
         return;
       }
     }
   }
 
   if (mPoint == aPoint) {
     BreakpointVector hitBreakpoints;
     GetAllBreakpointHits(aPoint, hitBreakpoints);
-    MOZ_RELEASE_ASSERT(!hitBreakpoints.empty());
 
-    gNavigation->mBreakpointPausedPhase.Enter(aPoint, hitBreakpoints);
+    gNavigation->mBreakpointPausedPhase.Enter(aPoint, /* aRecordingEndpoint = */ false,
+                                              hitBreakpoints);
   }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // FindLastHitPhase
 ///////////////////////////////////////////////////////////////////////////////
 
 void
@@ -1013,17 +1040,17 @@ FindLastHitPhase::OnRegionEnd()
       CheckpointId start = mStart;
       start.mTemporary--;
       ExecutionPoint end = gNavigation->LastTemporaryCheckpointLocation();
       gNavigation->mFindLastHitPhase.Enter(start, Some(end));
       Unreachable();
     } else {
       // Rewind to the last normal checkpoint and pause.
       gNavigation->mCheckpointPausedPhase.Enter(mStart.mNormal, /* aRewind = */ true,
-                                                /* aAtRecordingEndpoint = */ false);
+                                                /* aRecordingEndpoint = */ false);
       Unreachable();
     }
   }
 
   // When running backwards, we don't want to place temporary checkpoints at
   // the breakpoint where we are going to stop at. If the user continues
   // rewinding then we will just have to discard the checkpoint and waste the
   // work we did in saving it.
@@ -1032,25 +1059,27 @@ FindLastHitPhase::OnRegionEnd()
   // breakpoint's script was entered. This optimizes for the case of stepping
   // around within a frame.
   Maybe<BreakpointPosition> baseEntry = GetEntryPosition(lastBreakpoint.ref().mPosition);
   if (baseEntry.isSome()) {
     const TrackedPosition& tracked = FindTrackedPosition(baseEntry.ref());
     if (tracked.mLastHit.HasPosition() &&
         tracked.mLastHitCount < lastBreakpoint.ref().mLastHitCount)
     {
-      gNavigation->mReachBreakpointPhase.Enter(mStart, lastBreakpoint.ref().mLastHit,
+      gNavigation->mReachBreakpointPhase.Enter(mStart, /* aRewind = */ true,
+                                               lastBreakpoint.ref().mLastHit,
                                                Some(tracked.mLastHit));
       Unreachable();
     }
   }
 
   // There was no suitable place for a temporary checkpoint, so rewind to the
   // last checkpoint and play forward to the last breakpoint hit we found.
-  gNavigation->mReachBreakpointPhase.Enter(mStart, lastBreakpoint.ref().mLastHit, Nothing());
+  gNavigation->mReachBreakpointPhase.Enter(mStart, /* aRewind = */ true,
+                                           lastBreakpoint.ref().mLastHit, Nothing());
   Unreachable();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Hooks
 ///////////////////////////////////////////////////////////////////////////////
 
 bool
@@ -1110,16 +1139,22 @@ Resume(bool aForward)
 }
 
 void
 RestoreCheckpoint(size_t aId)
 {
   gNavigation->RestoreCheckpoint(aId);
 }
 
+void
+RunToPoint(const ExecutionPoint& aTarget)
+{
+  gNavigation->RunToPoint(aTarget);
+}
+
 ExecutionPoint
 GetRecordingEndpoint()
 {
   MOZ_RELEASE_ASSERT(IsRecording());
   return gNavigation->GetRecordingEndpoint();
 }
 
 void