Bug 1505895 - Add handler for changes to a recording/replaying child's state, r=lsmyth.

--HG--
extra : rebase_source : 5cf996fd6d7dfef4a9fc85d697ad35819809ad96
This commit is contained in:
Brian Hackett 2018-11-08 10:06:20 -10:00
parent 2ec0586f2c
commit 4be9981b8d
4 changed files with 47 additions and 10 deletions

View File

@ -76,6 +76,8 @@ ReplayDebugger.prototype = {
return this._sendRequest({ type: "recordingEndpoint" }); return this._sendRequest({ type: "recordingEndpoint" });
}, },
replayIsRecording: RecordReplayControl.childIsRecording,
addDebuggee() {}, addDebuggee() {},
removeAllDebuggees() {}, removeAllDebuggees() {},
@ -384,6 +386,14 @@ ReplayDebugger.prototype = {
handler.call(this, this.getNewestFrame()); }); handler.call(this, this.getNewestFrame()); });
}, },
get replayingOnPositionChange() {
return this._breakpointKindGetter("PositionChange");
},
set replayingOnPositionChange(handler) {
this._breakpointKindSetter("PositionChange", handler,
() => { handler.call(this); });
},
getNewConsoleMessage() { getNewConsoleMessage() {
const message = this._sendRequest({ type: "getNewConsoleMessage" }); const message = this._sendRequest({ type: "getNewConsoleMessage" });
return this._convertConsoleMessage(message); return this._convertConsoleMessage(message);

View File

@ -432,6 +432,14 @@ Middleman_HadRepaintFailure(JSContext* aCx, unsigned aArgc, Value* aVp)
return true; return true;
} }
static bool
Middleman_ChildIsRecording(JSContext* aCx, unsigned aArgc, Value* aVp)
{
CallArgs args = CallArgsFromVp(aArgc, aVp);
args.rval().setBoolean(parent::ActiveChildIsRecording());
return true;
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Devtools Sandbox // Devtools Sandbox
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -931,6 +939,7 @@ static const JSFunctionSpec gMiddlemanMethods[] = {
JS_FN("maybeSwitchToReplayingChild", Middleman_MaybeSwitchToReplayingChild, 0, 0), JS_FN("maybeSwitchToReplayingChild", Middleman_MaybeSwitchToReplayingChild, 0, 0),
JS_FN("hadRepaint", Middleman_HadRepaint, 2, 0), JS_FN("hadRepaint", Middleman_HadRepaint, 2, 0),
JS_FN("hadRepaintFailure", Middleman_HadRepaintFailure, 0, 0), JS_FN("hadRepaintFailure", Middleman_HadRepaintFailure, 0, 0),
JS_FN("childIsRecording", Middleman_ChildIsRecording, 0, 0),
JS_FS_END JS_FS_END
}; };

View File

@ -66,7 +66,11 @@ struct BreakpointPosition
// Break when the debugger should pause even if no breakpoint has been // Break when the debugger should pause even if no breakpoint has been
// set: the beginning or end of the replay has been reached, or a time // set: the beginning or end of the replay has been reached, or a time
// warp has reached its destination. // warp has reached its destination.
ForcedPause ForcedPause,
// Break when the child process reaches a checkpoint or we switch between
// recording and replaying child processes.
PositionChange
)); ));
Kind mKind; Kind mKind;
@ -120,6 +124,7 @@ struct BreakpointPosition
case ConsoleMessage: return "ConsoleMessage"; case ConsoleMessage: return "ConsoleMessage";
case WarpTarget: return "WarpTarget"; case WarpTarget: return "WarpTarget";
case ForcedPause: return "ForcedPause"; case ForcedPause: return "ForcedPause";
case PositionChange: return "PositionChange";
} }
MOZ_CRASH("Bad BreakpointPosition kind"); MOZ_CRASH("Bad BreakpointPosition kind");
} }

View File

@ -14,6 +14,7 @@
#include "js/Proxy.h" #include "js/Proxy.h"
#include "mozilla/dom/ContentProcessMessageManager.h" #include "mozilla/dom/ContentProcessMessageManager.h"
#include "InfallibleVector.h" #include "InfallibleVector.h"
#include "JSControl.h"
#include "Monitor.h" #include "Monitor.h"
#include "ProcessRecordReplay.h" #include "ProcessRecordReplay.h"
#include "ProcessRedirect.h" #include "ProcessRedirect.h"
@ -583,6 +584,10 @@ SpawnReplayingChildren()
AssignMajorCheckpoint(gSecondReplayingChild, CheckpointId::First); AssignMajorCheckpoint(gSecondReplayingChild, CheckpointId::First);
} }
// Hit any installed breakpoints with the specified kind.
static void HitBreakpointsWithKind(js::BreakpointPosition::Kind aKind,
bool aRecordingBoundary = false);
// Change the current active child, and select a new role for the old one. // Change the current active child, and select a new role for the old one.
static void static void
SwitchActiveChild(ChildProcessInfo* aChild, bool aRecoverPosition = true) SwitchActiveChild(ChildProcessInfo* aChild, bool aRecoverPosition = true)
@ -608,6 +613,12 @@ SwitchActiveChild(ChildProcessInfo* aChild, bool aRecoverPosition = true)
oldActiveChild->RecoverToCheckpoint(oldActiveChild->MostRecentSavedCheckpoint()); oldActiveChild->RecoverToCheckpoint(oldActiveChild->MostRecentSavedCheckpoint());
oldActiveChild->SetRole(MakeUnique<ChildRoleStandby>()); oldActiveChild->SetRole(MakeUnique<ChildRoleStandby>());
} }
// Position state is affected when we switch between recording and
// replaying children.
if (aChild->IsRecording() != oldActiveChild->IsRecording()) {
HitBreakpointsWithKind(js::BreakpointPosition::Kind::PositionChange);
}
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -995,9 +1006,6 @@ static bool gChildExecuteBackward = false;
// main thread. This will continue execution in the preferred direction. // main thread. This will continue execution in the preferred direction.
static bool gResumeForwardOrBackward = false; static bool gResumeForwardOrBackward = false;
// Hit any breakpoints installed for forced pauses.
static void HitForcedPauseBreakpoints(bool aRecordingBoundary);
static void static void
MaybeSendRepaintMessage() MaybeSendRepaintMessage()
{ {
@ -1050,7 +1058,8 @@ Resume(bool aForward)
// Don't rewind if we are at the beginning of the recording. // Don't rewind if we are at the beginning of the recording.
if (targetCheckpoint == CheckpointId::Invalid) { if (targetCheckpoint == CheckpointId::Invalid) {
SendMessageToUIProcess("HitRecordingBeginning"); SendMessageToUIProcess("HitRecordingBeginning");
HitForcedPauseBreakpoints(true); HitBreakpointsWithKind(js::BreakpointPosition::Kind::ForcedPause,
/* aRecordingBoundary = */ true);
return; return;
} }
@ -1077,7 +1086,8 @@ Resume(bool aForward)
MOZ_RELEASE_ASSERT(!gActiveChild->IsRecording()); MOZ_RELEASE_ASSERT(!gActiveChild->IsRecording());
if (!gRecordingChild) { if (!gRecordingChild) {
SendMessageToUIProcess("HitRecordingEndpoint"); SendMessageToUIProcess("HitRecordingEndpoint");
HitForcedPauseBreakpoints(true); HitBreakpointsWithKind(js::BreakpointPosition::Kind::ForcedPause,
/* aRecordingBoundary = */ true);
return; return;
} }
@ -1142,7 +1152,7 @@ TimeWarp(const js::ExecutionPoint& aTarget)
gActiveChild->WaitUntilPaused(); gActiveChild->WaitUntilPaused();
SendMessageToUIProcess("TimeWarpFinished"); SendMessageToUIProcess("TimeWarpFinished");
HitForcedPauseBreakpoints(false); HitBreakpointsWithKind(js::BreakpointPosition::Kind::ForcedPause);
} }
void void
@ -1189,6 +1199,9 @@ RecvHitCheckpoint(const HitCheckpointMessage& aMsg)
UpdateCheckpointTimes(aMsg); UpdateCheckpointTimes(aMsg);
MaybeUpdateGraphicsAtCheckpoint(aMsg.mCheckpointId); MaybeUpdateGraphicsAtCheckpoint(aMsg.mCheckpointId);
// Position state is affected when new checkpoints are reached.
HitBreakpointsWithKind(js::BreakpointPosition::Kind::PositionChange);
// Resume either forwards or backwards. Break the resume off into a separate // Resume either forwards or backwards. Break the resume off into a separate
// runnable, to avoid starving any code already on the stack and waiting for // runnable, to avoid starving any code already on the stack and waiting for
// the process to pause. Immediately resume if the main thread is blocked. // the process to pause. Immediately resume if the main thread is blocked.
@ -1247,11 +1260,11 @@ RecvHitBreakpoint(const HitBreakpointMessage& aMsg)
} }
static void static void
HitForcedPauseBreakpoints(bool aRecordingBoundary) HitBreakpointsWithKind(js::BreakpointPosition::Kind aKind, bool aRecordingBoundary)
{ {
Vector<uint32_t> breakpoints; Vector<uint32_t> breakpoints;
gActiveChild->GetMatchingInstalledBreakpoints([=](js::BreakpointPosition::Kind aKind) { gActiveChild->GetMatchingInstalledBreakpoints([=](js::BreakpointPosition::Kind aInstalled) {
return aKind == js::BreakpointPosition::ForcedPause; return aInstalled == aKind;
}, breakpoints); }, breakpoints);
if (!breakpoints.empty()) { if (!breakpoints.empty()) {
uint32_t* newBreakpoints = new uint32_t[breakpoints.length()]; uint32_t* newBreakpoints = new uint32_t[breakpoints.length()];