Bug 1483319 Part 3 - Combine BreakpointPaused and CheckpointPaused phases, r=mccr8.

--HG--
extra : rebase_source : 0a78564d14a50fd609ef463b68d4536c61bc1e88
This commit is contained in:
Brian Hackett 2018-08-18 05:39:48 -10:00
Родитель bc4ee25f64
Коммит ff088ffadd
1 изменённых файлов: 223 добавлений и 228 удалений

Просмотреть файл

@ -44,8 +44,7 @@ ExecutionPointToString(const ExecutionPoint& aPoint, nsAutoCString& aStr)
// At all times, the navigation state will be in exactly one of the following // At all times, the navigation state will be in exactly one of the following
// phases: // phases:
// //
// - BreakpointPaused: The process is paused somewhere between two checkpoints. // - Paused: The process is paused somewhere.
// - CheckpointPaused: The process is paused at a normal checkpoint.
// - Forward: The process is running forward and scanning for breakpoint hits. // - Forward: The process is running forward and scanning for breakpoint hits.
// - ReachBreakpoint: The process is running forward from a checkpoint to a // - ReachBreakpoint: The process is running forward from a checkpoint to a
// particular execution point before the next checkpoint. // particular execution point before the next checkpoint.
@ -149,57 +148,40 @@ typedef InfallibleVector<RequestInfo, 4, UntrackedAllocPolicy> UntrackedRequestV
typedef InfallibleVector<uint32_t> BreakpointVector; typedef InfallibleVector<uint32_t> BreakpointVector;
// Phase when the replaying process is paused at a breakpoint. // Phase when the replaying process is paused.
class BreakpointPausedPhase final : public NavigationPhase class PausedPhase final : public NavigationPhase
{ {
// Where the pause is at. // Location of the pause.
ExecutionPoint mPoint; ExecutionPoint mPoint;
// Whether we are paused at the end of the recording.
bool mRecordingEndpoint;
// All debugger requests we have seen while paused here. // All debugger requests we have seen while paused here.
UntrackedRequestVector mRequests; UntrackedRequestVector mRequests;
// Whether we had to restore a checkpoint to deal with an unhandled
// recording divergence, and haven't finished rehandling old requests.
bool mRecoveringFromDivergence;
// Index of the request currently being processed. Normally this is the // Index of the request currently being processed. Normally this is the
// last entry in |mRequests|, though may be earlier if we are recovering // last entry in |mRequests|, though may be earlier if we are recovering
// from an unhandled divergence. // from an unhandled divergence.
size_t mRequestIndex; size_t mRequestIndex;
// Whether we have saved a temporary checkpoint.
bool mSavedTemporaryCheckpoint;
// Whether we had to restore a checkpoint to deal with an unhandled
// recording divergence, and haven't finished rehandling old requests.
bool mRecoveringFromDivergence;
// Set when we were told to resume forward and need to clean up our state. // Set when we were told to resume forward and need to clean up our state.
bool mResumeForward; bool mResumeForward;
public: public:
void Enter(const ExecutionPoint& aPoint, bool aRecordingEndpoint, void Enter(const ExecutionPoint& aPoint,
const BreakpointVector& aBreakpoints); const BreakpointVector& aBreakpoints = BreakpointVector(),
bool aRewind = false, bool aRecordingEndpoint = false);
void ToString(nsAutoCString& aStr) override { void ToString(nsAutoCString& aStr) override {
aStr.AppendPrintf("BreakpointPaused RecoveringFromDivergence %d", mRecoveringFromDivergence); aStr.AppendPrintf("Paused RecoveringFromDivergence %d", mRecoveringFromDivergence);
}
void AfterCheckpoint(const CheckpointId& aCheckpoint) override;
void PositionHit(const ExecutionPoint& aPoint) override;
void Resume(bool aForward) override;
void RestoreCheckpoint(size_t aCheckpoint) override;
void HandleDebuggerRequest(js::CharBuffer* aRequestBuffer) override;
bool MaybeDivergeFromRecording() override;
ExecutionPoint GetRecordingEndpoint() override;
void RespondAfterRecoveringFromDivergence();
};
// 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 aRecordingEndpoint);
void ToString(nsAutoCString& aStr) override {
aStr.AppendPrintf("CheckpointPaused");
} }
void AfterCheckpoint(const CheckpointId& aCheckpoint) override; void AfterCheckpoint(const CheckpointId& aCheckpoint) override;
@ -208,7 +190,10 @@ public:
void RestoreCheckpoint(size_t aCheckpoint) override; void RestoreCheckpoint(size_t aCheckpoint) override;
void RunToPoint(const ExecutionPoint& aTarget) override; void RunToPoint(const ExecutionPoint& aTarget) override;
void HandleDebuggerRequest(js::CharBuffer* aRequestBuffer) override; void HandleDebuggerRequest(js::CharBuffer* aRequestBuffer) override;
bool MaybeDivergeFromRecording() override;
ExecutionPoint GetRecordingEndpoint() override; ExecutionPoint GetRecordingEndpoint() override;
bool EnsureTemporaryCheckpoint();
}; };
// Phase when execution is proceeding forwards in search of breakpoint hits. // Phase when execution is proceeding forwards in search of breakpoint hits.
@ -363,8 +348,7 @@ public:
} }
} }
BreakpointPausedPhase mBreakpointPausedPhase; PausedPhase mPausedPhase;
CheckpointPausedPhase mCheckpointPausedPhase;
ForwardPhase mForwardPhase; ForwardPhase mForwardPhase;
ReachBreakpointPhase mReachBreakpointPhase; ReachBreakpointPhase mReachBreakpointPhase;
FindLastHitPhase mFindLastHitPhase; FindLastHitPhase mFindLastHitPhase;
@ -407,8 +391,8 @@ public:
MOZ_RELEASE_ASSERT(IsRecording() || MOZ_RELEASE_ASSERT(IsRecording() ||
aCheckpoint.mNormal <= mRecordingEndpoint.mCheckpoint); aCheckpoint.mNormal <= mRecordingEndpoint.mCheckpoint);
if (aCheckpoint.mNormal == mRecordingEndpoint.mCheckpoint) { if (aCheckpoint.mNormal == mRecordingEndpoint.mCheckpoint &&
MOZ_RELEASE_ASSERT(mRecordingEndpoint.HasPosition()); mRecordingEndpoint.HasPosition()) {
js::EnsurePositionHandler(mRecordingEndpoint.mPosition); js::EnsurePositionHandler(mRecordingEndpoint.mPosition);
} }
} }
@ -481,13 +465,6 @@ public:
MOZ_RELEASE_ASSERT(!mTemporaryCheckpoints.empty()); MOZ_RELEASE_ASSERT(!mTemporaryCheckpoints.empty());
return mTemporaryCheckpoints.back(); return mTemporaryCheckpoints.back();
} }
CheckpointId LastTemporaryCheckpointId() {
MOZ_RELEASE_ASSERT(!mTemporaryCheckpoints.empty());
size_t normal = mTemporaryCheckpoints.back().mCheckpoint;
size_t temporary = mTemporaryCheckpoints.length();
return CheckpointId(normal, temporary);
}
}; };
static NavigationState* gNavigation; static NavigationState* gNavigation;
@ -505,7 +482,7 @@ GetAllBreakpointHits(const ExecutionPoint& aPoint, BreakpointVector& aHitBreakpo
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// BreakpointPaused Phase // Paused Phase
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
static bool static bool
@ -515,81 +492,70 @@ ThisProcessCanRewind()
} }
void void
BreakpointPausedPhase::Enter(const ExecutionPoint& aPoint, bool aRecordingEndpoint, PausedPhase::Enter(const ExecutionPoint& aPoint, const BreakpointVector& aBreakpoints,
const BreakpointVector& aBreakpoints) bool aRewind, bool aRecordingEndpoint)
{ {
MOZ_RELEASE_ASSERT(aPoint.HasPosition());
mPoint = aPoint; mPoint = aPoint;
mRecordingEndpoint = aRecordingEndpoint;
mRequests.clear(); mRequests.clear();
mRecoveringFromDivergence = false;
mRequestIndex = 0; mRequestIndex = 0;
mSavedTemporaryCheckpoint = false;
mRecoveringFromDivergence = false;
mResumeForward = false; mResumeForward = false;
gNavigation->SetPhase(this); gNavigation->SetPhase(this);
if (ThisProcessCanRewind()) { // Breakpoints will never be hit if we are at a checkpoint.
// Immediately save a temporary checkpoint and update the point to be MOZ_RELEASE_ASSERT(aPoint.HasPosition() || aBreakpoints.empty());
// in relation to this checkpoint. If we rewind due to a recording
// divergence we will end up here. if (aRewind) {
if (!gNavigation->SaveTemporaryCheckpoint(aPoint)) { MOZ_RELEASE_ASSERT(!aPoint.HasPosition());
// We just restored the checkpoint, and could be in any phase, RestoreCheckpointAndResume(CheckpointId(aPoint.mCheckpoint));
// including this one. Unreachable();
if (gNavigation->mPhase == this) {
MOZ_RELEASE_ASSERT(!mRecoveringFromDivergence);
// If we are transitioning to the forward phase, avoid hitting
// breakpoints at this point.
if (mResumeForward) {
gNavigation->mForwardPhase.Enter(aPoint);
return;
}
// Otherwise we restored after hitting an unhandled recording
// divergence.
mRecoveringFromDivergence = true;
PauseMainThreadAndInvokeCallback([=]() {
RespondAfterRecoveringFromDivergence();
});
Unreachable();
}
gNavigation->PositionHit(aPoint);
return;
}
} }
child::HitBreakpoint(aRecordingEndpoint, aBreakpoints.begin(), aBreakpoints.length()); if (aPoint.HasPosition()) {
child::HitBreakpoint(aRecordingEndpoint, aBreakpoints.begin(), aBreakpoints.length());
// When rewinding is allowed we will rewind before resuming to erase side effects. } else {
MOZ_RELEASE_ASSERT(!ThisProcessCanRewind()); child::HitCheckpoint(aPoint.mCheckpoint, aRecordingEndpoint);
}
} }
void void
BreakpointPausedPhase::AfterCheckpoint(const CheckpointId& aCheckpoint) PausedPhase::AfterCheckpoint(const CheckpointId& aCheckpoint)
{ {
// We just saved or restored the temporary checkpoint before reaching the MOZ_RELEASE_ASSERT(!mRecoveringFromDivergence);
// breakpoint. if (!aCheckpoint.mTemporary) {
MOZ_RELEASE_ASSERT(ThisProcessCanRewind()); // We just rewound here, and are now where we should pause.
MOZ_RELEASE_ASSERT(aCheckpoint == gNavigation->LastTemporaryCheckpointId()); MOZ_RELEASE_ASSERT(mPoint == ExecutionPoint(aCheckpoint.mNormal));
child::HitCheckpoint(mPoint.mCheckpoint, mRecordingEndpoint);
} else {
// We just saved or restored the temporary checkpoint taken while
// processing debugger requests here.
MOZ_RELEASE_ASSERT(ThisProcessCanRewind());
MOZ_RELEASE_ASSERT(mSavedTemporaryCheckpoint);
}
} }
void void
BreakpointPausedPhase::PositionHit(const ExecutionPoint& aPoint) PausedPhase::PositionHit(const ExecutionPoint& aPoint)
{ {
// Ignore positions hit while paused (we're probably doing an eval). // Ignore positions hit while paused (we're probably doing an eval).
} }
void void
BreakpointPausedPhase::Resume(bool aForward) PausedPhase::Resume(bool aForward)
{ {
MOZ_RELEASE_ASSERT(!mRecoveringFromDivergence); MOZ_RELEASE_ASSERT(!mRecoveringFromDivergence);
MOZ_RELEASE_ASSERT(!mResumeForward);
if (aForward) { if (aForward) {
// If we are paused at a breakpoint and can rewind, we may have // If we have saved any temporary checkpoint, we performed an operation
// diverged from the recording. We have to clear any unwanted changes // that may have side effects. Clear these unwanted changes by restoring
// induced by evals and so forth by restoring the temporary checkpoint // the temporary checkpoint we saved earlier.
// we saved before pausing here. if (mSavedTemporaryCheckpoint) {
if (ThisProcessCanRewind()) {
mResumeForward = true; mResumeForward = true;
RestoreCheckpointAndResume(gNavigation->LastTemporaryCheckpointId()); RestoreCheckpointAndResume(gNavigation->LastCheckpoint());
Unreachable(); Unreachable();
} }
@ -601,57 +567,106 @@ BreakpointPausedPhase::Resume(bool aForward)
} }
// Search backwards in the execution space. // Search backwards in the execution space.
CheckpointId start = gNavigation->LastTemporaryCheckpointId(); if (mPoint.HasPosition()) {
start.mTemporary--; CheckpointId start = gNavigation->LastCheckpoint();
gNavigation->mFindLastHitPhase.Enter(start, Some(mPoint));
// Skip over any temporary checkpoint we saved.
if (mSavedTemporaryCheckpoint) {
MOZ_RELEASE_ASSERT(start.mTemporary);
start.mTemporary--;
}
gNavigation->mFindLastHitPhase.Enter(start, Some(mPoint));
} else {
// We can't rewind past the beginning of the replay.
MOZ_RELEASE_ASSERT(mPoint.mCheckpoint != CheckpointId::First);
CheckpointId start(mPoint.mCheckpoint - 1);
gNavigation->mFindLastHitPhase.Enter(start, Nothing());
}
Unreachable(); Unreachable();
} }
void void
BreakpointPausedPhase::RestoreCheckpoint(size_t aCheckpoint) PausedPhase::RestoreCheckpoint(size_t aCheckpoint)
{ {
gNavigation->mCheckpointPausedPhase.Enter(aCheckpoint, /* aRewind = */ true, ExecutionPoint target(aCheckpoint);
/* aRecordingEndpoint = */ false); bool rewind = target != mPoint;
Enter(target, BreakpointVector(), rewind, /* aRecordingEndpoint = */ false);
} }
void void
BreakpointPausedPhase::HandleDebuggerRequest(js::CharBuffer* aRequestBuffer) PausedPhase::RunToPoint(const ExecutionPoint& aTarget)
{
// This may only be used when we are paused at a normal checkpoint.
MOZ_RELEASE_ASSERT(!mPoint.HasPosition());
size_t checkpoint = mPoint.mCheckpoint;
MOZ_RELEASE_ASSERT(aTarget.mCheckpoint == checkpoint);
ResumeExecution();
gNavigation->mReachBreakpointPhase.Enter(CheckpointId(checkpoint), /* aRewind = */ false,
aTarget, /* aTemporaryCheckpoint = */ Nothing());
}
void
PausedPhase::HandleDebuggerRequest(js::CharBuffer* aRequestBuffer)
{ {
MOZ_RELEASE_ASSERT(!mRecoveringFromDivergence); MOZ_RELEASE_ASSERT(!mRecoveringFromDivergence);
MOZ_RELEASE_ASSERT(!mResumeForward);
mRequests.emplaceBack(); mRequests.emplaceBack();
RequestInfo& info = mRequests.back(); size_t index = mRequests.length() - 1;
mRequestIndex = mRequests.length() - 1; mRequests[index].mRequestBuffer.append(aRequestBuffer->begin(), aRequestBuffer->length());
info.mRequestBuffer.append(aRequestBuffer->begin(), aRequestBuffer->length()); mRequestIndex = index;
js::CharBuffer responseBuffer; js::CharBuffer responseBuffer;
js::ProcessRequest(aRequestBuffer->begin(), aRequestBuffer->length(), &responseBuffer); js::ProcessRequest(aRequestBuffer->begin(), aRequestBuffer->length(), &responseBuffer);
delete aRequestBuffer; delete aRequestBuffer;
info.mResponseBuffer.append(responseBuffer.begin(), responseBuffer.length()); if (gNavigation->mPhase != this) {
child::RespondToRequest(responseBuffer); // We saved a temporary checkpoint by calling MaybeDivergeFromRecording
} // within ProcessRequest, then restored it while scanning backwards.
ResumeExecution();
return;
}
void if (!mResumeForward && !mRecoveringFromDivergence) {
BreakpointPausedPhase::RespondAfterRecoveringFromDivergence() // We processed this request normally. Remember the response and send it to
{ // the middleman process.
MOZ_RELEASE_ASSERT(mRecoveringFromDivergence); MOZ_RELEASE_ASSERT(index == mRequestIndex);
MOZ_RELEASE_ASSERT(mRequests.length()); mRequests[index].mResponseBuffer.append(responseBuffer.begin(), responseBuffer.length());
child::RespondToRequest(responseBuffer);
return;
}
// Remember that the last request has triggered an unhandled divergence. if (mResumeForward) {
// We rewound to erase side effects from the temporary checkpoint we saved
// under ProcessRequest. Just start running forward.
MOZ_RELEASE_ASSERT(!mRecoveringFromDivergence);
gNavigation->mForwardPhase.Enter(mPoint);
return;
}
// We rewound after having an unhandled recording divergence while processing
// mRequests[index] or some later request. We need to redo all requests up to
// the last request we received.
// Remember that the last request triggered an unhandled divergence.
MOZ_RELEASE_ASSERT(!mRequests.back().mUnhandledDivergence); MOZ_RELEASE_ASSERT(!mRequests.back().mUnhandledDivergence);
mRequests.back().mUnhandledDivergence = true; mRequests.back().mUnhandledDivergence = true;
// Redo all existing requests. for (size_t i = index; i < mRequests.length(); i++) {
for (size_t i = 0; i < mRequests.length(); i++) {
RequestInfo& info = mRequests[i]; RequestInfo& info = mRequests[i];
mRequestIndex = i; mRequestIndex = i;
js::CharBuffer responseBuffer; if (i == index) {
js::ProcessRequest(info.mRequestBuffer.begin(), info.mRequestBuffer.length(), // We just performed this request, and responseBuffer has the right contents.
&responseBuffer); } else {
responseBuffer.clear();
js::ProcessRequest(info.mRequestBuffer.begin(), info.mRequestBuffer.length(),
&responseBuffer);
}
if (i < mRequests.length() - 1) { if (i < mRequests.length() - 1) {
// This is an old request, and we don't need to send another // This is an old request, and we don't need to send another
@ -673,7 +688,7 @@ BreakpointPausedPhase::RespondAfterRecoveringFromDivergence()
} }
bool bool
BreakpointPausedPhase::MaybeDivergeFromRecording() PausedPhase::MaybeDivergeFromRecording()
{ {
if (!ThisProcessCanRewind()) { if (!ThisProcessCanRewind()) {
// Recording divergence is not supported if we can't rewind. We can't // Recording divergence is not supported if we can't rewind. We can't
@ -682,103 +697,81 @@ BreakpointPausedPhase::MaybeDivergeFromRecording()
// will not be reflected in the recording. // will not be reflected in the recording.
return false; return false;
} }
if (mRequests[mRequestIndex].mUnhandledDivergence) {
if (!EnsureTemporaryCheckpoint()) {
// One of the premature exit cases was hit in EnsureTemporaryCheckpoint.
// Don't allow any operations that can diverge from the recording.
return false; return false;
} }
if (mRequests[mRequestIndex].mUnhandledDivergence) {
// We tried to process this request before and had an unhandled divergence.
// Disallow the request handler from doing anything that might diverge from
// the recording.
return false;
}
DivergeFromRecording(); DivergeFromRecording();
return true; return true;
} }
bool
PausedPhase::EnsureTemporaryCheckpoint()
{
if (mSavedTemporaryCheckpoint) {
return true;
}
// We need to save a temporary checkpoint that we can restore if we hit
// a recording divergence.
mSavedTemporaryCheckpoint = true;
size_t index = mRequestIndex;
if (gNavigation->SaveTemporaryCheckpoint(mPoint)) {
// We just saved the temporary checkpoint.
return true;
}
// We just rewound here.
if (gNavigation->mPhase != this) {
// We are no longer paused at this point. We should be searching
// backwards in the region after this temporary checkpoint was taken.
// Return false to ensure we don't perform any side effects before
// resuming forward.
return false;
}
// We are still paused at this point. Either we had an unhandled
// recording divergence, or we intentionally rewound to erase side
// effects that occurred while paused here.
MOZ_RELEASE_ASSERT(!mRecoveringFromDivergence);
if (mResumeForward) {
// We can't diverge from the recording before resuming forward execution.
return false;
}
mRecoveringFromDivergence = true;
if (index == mRequestIndex) {
// We had an unhandled divergence for the same request where we
// created the temporary checkpoint. mUnhandledDivergence hasn't been
// set yet, but return now to avoid triggering the same divergence
// and rewinding again.
return false;
}
// Allow the caller to check mUnhandledDivergence.
return true;
}
ExecutionPoint ExecutionPoint
BreakpointPausedPhase::GetRecordingEndpoint() PausedPhase::GetRecordingEndpoint()
{ {
MOZ_RELEASE_ASSERT(IsRecording()); MOZ_RELEASE_ASSERT(IsRecording());
return mPoint; return mPoint;
} }
///////////////////////////////////////////////////////////////////////////////
// CheckpointPausedPhase
///////////////////////////////////////////////////////////////////////////////
void
CheckpointPausedPhase::Enter(size_t aCheckpoint, bool aRewind, bool aAtRecordingEndpoint)
{
mCheckpoint = aCheckpoint;
mAtRecordingEndpoint = aAtRecordingEndpoint;
gNavigation->SetPhase(this);
if (aRewind) {
RestoreCheckpointAndResume(CheckpointId(mCheckpoint));
Unreachable();
}
AfterCheckpoint(CheckpointId(mCheckpoint));
}
void
CheckpointPausedPhase::AfterCheckpoint(const CheckpointId& aCheckpoint)
{
MOZ_RELEASE_ASSERT(aCheckpoint == CheckpointId(mCheckpoint));
child::HitCheckpoint(mCheckpoint, mAtRecordingEndpoint);
}
void
CheckpointPausedPhase::PositionHit(const ExecutionPoint& aPoint)
{
// Ignore positions hit while paused (we're probably doing an eval).
}
void
CheckpointPausedPhase::Resume(bool aForward)
{
// We can't rewind past the beginning of the replay.
MOZ_RELEASE_ASSERT(aForward || mCheckpoint != CheckpointId::First);
if (aForward) {
// Run forward from the current execution point.
js::ClearPausedState();
ExecutionPoint search(mCheckpoint);
gNavigation->mForwardPhase.Enter(search);
} else {
CheckpointId start(mCheckpoint - 1);
gNavigation->mFindLastHitPhase.Enter(start, Nothing());
Unreachable();
}
}
void
CheckpointPausedPhase::RestoreCheckpoint(size_t aCheckpoint)
{
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);
delete aRequestBuffer;
child::RespondToRequest(responseBuffer);
}
ExecutionPoint
CheckpointPausedPhase::GetRecordingEndpoint()
{
return ExecutionPoint(mCheckpoint);
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// ForwardPhase // ForwardPhase
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -805,8 +798,7 @@ ForwardPhase::AfterCheckpoint(const CheckpointId& aCheckpoint)
{ {
MOZ_RELEASE_ASSERT(!aCheckpoint.mTemporary && MOZ_RELEASE_ASSERT(!aCheckpoint.mTemporary &&
aCheckpoint.mNormal == mPoint.mCheckpoint + 1); aCheckpoint.mNormal == mPoint.mCheckpoint + 1);
gNavigation->mCheckpointPausedPhase.Enter(aCheckpoint.mNormal, /* aRewind = */ false, gNavigation->mPausedPhase.Enter(ExecutionPoint(aCheckpoint.mNormal));
/* aRecordingEndpoint = */ false);
} }
void void
@ -816,22 +808,21 @@ ForwardPhase::PositionHit(const ExecutionPoint& aPoint)
GetAllBreakpointHits(aPoint, hitBreakpoints); GetAllBreakpointHits(aPoint, hitBreakpoints);
if (!hitBreakpoints.empty()) { if (!hitBreakpoints.empty()) {
gNavigation->mBreakpointPausedPhase.Enter(aPoint, /* aRecordingEndpoint = */ false, gNavigation->mPausedPhase.Enter(aPoint, hitBreakpoints);
hitBreakpoints);
} }
} }
void void
ForwardPhase::HitRecordingEndpoint(const ExecutionPoint& aPoint) ForwardPhase::HitRecordingEndpoint(const ExecutionPoint& aPoint)
{ {
if (aPoint.HasPosition()) { nsAutoCString str;
BreakpointVector emptyBreakpoints; ExecutionPointToString(aPoint, str);
gNavigation->mBreakpointPausedPhase.Enter(aPoint, /* aRecordingEndpoint = */ true,
emptyBreakpoints); // Use an empty vector even if there are breakpoints here. If we started
} else { // running forward from aPoint and immediately hit the recording endpoint,
gNavigation->mCheckpointPausedPhase.Enter(aPoint.mCheckpoint, /* aRewind = */ false, // we don't want to hit the breakpoints again.
/* aRecordingEndpoint = */ true); gNavigation->mPausedPhase.Enter(aPoint, BreakpointVector(),
} /* aRewind = */ false, /* aRecordingEndpoint = */ true);
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -914,9 +905,7 @@ ReachBreakpointPhase::PositionHit(const ExecutionPoint& aPoint)
if (mPoint == aPoint) { if (mPoint == aPoint) {
BreakpointVector hitBreakpoints; BreakpointVector hitBreakpoints;
GetAllBreakpointHits(aPoint, hitBreakpoints); GetAllBreakpointHits(aPoint, hitBreakpoints);
gNavigation->mPausedPhase.Enter(aPoint, hitBreakpoints);
gNavigation->mBreakpointPausedPhase.Enter(aPoint, /* aRecordingEndpoint = */ false,
hitBreakpoints);
} }
} }
@ -1040,14 +1029,20 @@ FindLastHitPhase::OnRegionEnd()
CheckpointId start = mStart; CheckpointId start = mStart;
start.mTemporary--; start.mTemporary--;
ExecutionPoint end = gNavigation->LastTemporaryCheckpointLocation(); ExecutionPoint end = gNavigation->LastTemporaryCheckpointLocation();
gNavigation->mFindLastHitPhase.Enter(start, Some(end)); if (end.HasPosition()) {
Unreachable(); gNavigation->mFindLastHitPhase.Enter(start, Some(end));
} else { Unreachable();
// Rewind to the last normal checkpoint and pause. } else {
gNavigation->mCheckpointPausedPhase.Enter(mStart.mNormal, /* aRewind = */ true, // The last temporary checkpoint may be at the same execution point as
/* aRecordingEndpoint = */ false); // the last normal checkpoint, if it was created while handling
Unreachable(); // debugger requests there.
}
} }
// Rewind to the last normal checkpoint and pause.
gNavigation->mPausedPhase.Enter(ExecutionPoint(mStart.mNormal), BreakpointVector(),
/* aRewind = */ true);
Unreachable();
} }
// When running backwards, we don't want to place temporary checkpoints at // When running backwards, we don't want to place temporary checkpoints at