diff --git a/devtools/client/debugger/src/actions/pause/paused.js b/devtools/client/debugger/src/actions/pause/paused.js index 3e16cb9cae6b8..81157d68304db 100644 --- a/devtools/client/debugger/src/actions/pause/paused.js +++ b/devtools/client/debugger/src/actions/pause/paused.js @@ -38,6 +38,10 @@ export function paused(pauseInfo: Pause) { const cx = getThreadContext(getState()); assert(cx.thread == thread, "Thread mismatch"); + if (frame) { + await dispatch(selectLocation(cx, frame.location)); + } + await dispatch(fetchFrames(cx)); const hiddenBreakpoint = getHiddenBreakpoint(getState()); diff --git a/devtools/server/actors/thread.js b/devtools/server/actors/thread.js index 58c5db78c1fbd..9d4029db0daac 100644 --- a/devtools/server/actors/thread.js +++ b/devtools/server/actors/thread.js @@ -11,6 +11,7 @@ const { ActorPool } = require("devtools/server/actors/common"); const { createValueGrip } = require("devtools/server/actors/object/utils"); const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); +const ChromeUtils = require("ChromeUtils"); const { assert, dumpn, reportException } = DevToolsUtils; const { threadSpec } = require("devtools/shared/specs/thread"); const { @@ -1100,6 +1101,10 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { rewinding, }); + if (isReplaying) { + await this.youngestFrame.ensureMinimalOlderFrame(); + } + // Make sure there is still a frame on the stack if we are to continue // stepping. const stepFrame = this._getNextStepFrame(this.youngestFrame, rewinding); @@ -1129,11 +1134,13 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { // Fall through. case "finish": if (rewinding) { + await stepFrame.ensureMinimalOlderFrame(); let olderFrame = stepFrame.older; while ( olderFrame && (!olderFrame.script || this.sources.isFrameBlackBoxed(olderFrame)) ) { + await olderFrame.ensureMinimalOlderFrame(); olderFrame = olderFrame.older; } if (olderFrame) { @@ -1229,6 +1236,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { this.doResume({ resumeLimit, rewind }); return {}; } catch (error) { + ChromeUtils.recordReplayLog(`ThreadActor.onResume Error: ${error}`); return error instanceof Error ? { error: "unknownError", diff --git a/toolkit/recordreplay/ipc/JSControl.cpp b/toolkit/recordreplay/ipc/JSControl.cpp index 7deb33bf9e813..29ad8bb038467 100644 --- a/toolkit/recordreplay/ipc/JSControl.cpp +++ b/toolkit/recordreplay/ipc/JSControl.cpp @@ -1232,6 +1232,7 @@ enum ChangeFrameKind { ChangeFrameEnter, ChangeFrameExit, ChangeFrameResume, + ChangeFrameCall, NumChangeFrameKinds }; @@ -1281,12 +1282,16 @@ struct ScriptHitInfo { struct AnyScriptHit { uint32_t mScript; - uint32_t mFrameIndex; - ProgressCounter mProgress; + uint32_t mOffset; + uint32_t mFrameIndex : 16; + ProgressCounter mProgress : 48; + + AnyScriptHit() {} - AnyScriptHit(uint32_t aScript, uint32_t aFrameIndex, + AnyScriptHit(uint32_t aScript, uint32_t aOffset, uint32_t aFrameIndex, ProgressCounter aProgress) - : mScript(aScript), mFrameIndex(aFrameIndex), mProgress(aProgress) { + : mScript(aScript), mOffset(aOffset), mFrameIndex(aFrameIndex), + mProgress(aProgress) { static_assert(sizeof(AnyScriptHit) == 16); } }; @@ -1339,7 +1344,7 @@ struct ScriptHitInfo { for (auto& vector : mChangeFrames) { MOZ_RELEASE_ASSERT(vector.empty()); size_t numChangeFrames = aStream.ReadScalar32(); - vector.appendN(AnyScriptHit(0, 0, 0), numChangeFrames); + vector.appendN(AnyScriptHit(), numChangeFrames); aStream.ReadBytes(vector.begin(), vector.length() * sizeof(AnyScriptHit)); } @@ -1352,6 +1357,10 @@ struct ScriptHitInfo { InfallibleVector mInfo; + // When scanning the recording, this has the last breakpoint hit on a script + // at each frame depth. + InfallibleVector mLastHits; + CheckpointInfo* GetInfo(uint32_t aCheckpoint, bool aIncorporateData = true) { if (aIncorporateData) { MaybeIncorporateScanData(); @@ -1387,13 +1396,28 @@ struct ScriptHitInfo { ScriptHitVector* hits = p->value(); hits->append(ScriptHit(aFrameIndex, aProgress)); + + while (aFrameIndex >= mLastHits.length()) { + mLastHits.emplaceBack(); + } + AnyScriptHit& lastHit = mLastHits[aFrameIndex]; + lastHit.mScript = aScript; + lastHit.mOffset = aOffset; + lastHit.mFrameIndex = aFrameIndex; + lastHit.mProgress = aProgress; + } + + const AnyScriptHit& LastHit(uint32_t aFrameIndex) { + MOZ_RELEASE_ASSERT(aFrameIndex < mLastHits.length()); + return mLastHits[aFrameIndex]; } void AddChangeFrame(uint32_t aCheckpoint, uint32_t aWhich, uint32_t aScript, - uint32_t aFrameIndex, ProgressCounter aProgress) { + uint32_t aOffset, uint32_t aFrameIndex, + ProgressCounter aProgress) { CheckpointInfo* info = GetInfo(aCheckpoint); MOZ_RELEASE_ASSERT(aWhich < NumChangeFrameKinds); - info->mChangeFrames[aWhich].emplaceBack(aScript, aFrameIndex, aProgress); + info->mChangeFrames[aWhich].emplaceBack(aScript, aOffset, aFrameIndex, aProgress); } AnyScriptHitVector* FindChangeFrames(uint32_t aCheckpoint, uint32_t aWhich) { @@ -1566,7 +1590,16 @@ static bool RecordReplay_OnChangeFrame(JSContext* aCx, unsigned aArgc, } uint32_t frameIndex = gFrameDepth - 1; - gScriptHits->AddChangeFrame(GetLastCheckpoint(), Kind, script, frameIndex, + + if (Kind == ChangeFrameEnter && frameIndex) { + // Find the last breakpoint hit in the calling frame. + const ScriptHitInfo::AnyScriptHit& lastHit = gScriptHits->LastHit(frameIndex - 1); + gScriptHits->AddChangeFrame(GetLastCheckpoint(), ChangeFrameCall, + lastHit.mScript, lastHit.mOffset, + lastHit.mFrameIndex, lastHit.mProgress); + } + + gScriptHits->AddChangeFrame(GetLastCheckpoint(), Kind, script, 0, frameIndex, gProgressCounter); if (Kind == ChangeFrameExit) { @@ -1784,6 +1817,8 @@ static bool RecordReplay_FindChangeFrames(JSContext* aCx, unsigned aArgc, JSPROP_ENUMERATE) || !JS_DefineProperty(aCx, hitObject, "frameIndex", hit.mFrameIndex, JSPROP_ENUMERATE) || + !JS_DefineProperty(aCx, hitObject, "offset", hit.mOffset, + JSPROP_ENUMERATE) || !values.append(ObjectValue(*hitObject))) { return false; }