Bug 1582557 - Fix some stepping issues, r=jlast.

Differential Revision: https://phabricator.services.mozilla.com/D46518

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Brian Hackett 2019-09-19 21:18:57 +00:00
parent 49b0f644a2
commit 33f0851783
13 changed files with 104 additions and 34 deletions

View file

@ -90,6 +90,7 @@ devtools/client/debugger/bin/
devtools/client/debugger/packages/**/fixtures/
devtools/client/debugger/node_modules
devtools/client/debugger/out
devtools/client/webreplay/mochitest/examples/
# Ignore devtools debugger files which aren't intended for linting, and also
# aren't included in any .eslintignore or .prettierignore file.

View file

@ -28,6 +28,7 @@ support-files =
[browser_dbg_rr_stepping-02.js]
[browser_dbg_rr_stepping-03.js]
[browser_dbg_rr_stepping-04.js]
[browser_dbg_rr_stepping-05.js]
[browser_dbg_rr_replay-01.js]
[browser_dbg_rr_replay-02.js]
[browser_dbg_rr_replay-03.js]

View file

@ -6,8 +6,6 @@
// Test event logpoints when replaying.
add_task(async function() {
await pushPref("devtools.debugger.features.log-event-breakpoints", true);
const dbg = await attachRecordingDebugger("doc_events.html", {
waitForRecording: true,
});

View file

@ -0,0 +1,31 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test stepping in pretty-printed code.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_minified.html", {
waitForRecording: true,
});
await selectSource(dbg, "minified.js");
await prettyPrint(dbg);
await dbg.actions.addEventListenerBreakpoints(["event.mouse.click"]);
await dbg.actions.toggleEventLogging();
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
await warpToMessage(hud, dbg, "click", 12);
await stepInToLine(dbg, 2);
await stepOutToLine(dbg, 12);
await stepInToLine(dbg, 9);
await stepOutToLine(dbg, 13);
await stepInToLine(dbg, 5);
await stepOutToLine(dbg, 14);
await shutdownDebugger(dbg);
});

View file

@ -0,0 +1,13 @@
<div id="divvy">Hello World!</div>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="minified.js"></script>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
window.setTimeout(() => {
synthesizeMouseAtCenter(divvy, {});
window.setTimeout(recordingFinished);
});
</script>

View file

@ -0,0 +1 @@
const s={getWindow:()=>window};function f(){this.getElementById("divvy").innerHTML="Done!"}const nf=f.bind(document);function DOMEvent(n,e){console.log("DOMEvent",e)}function h(n){n=new DOMEvent(n,s.getWindow()),!1===nf.call(s,n)}document.getElementById("divvy").addEventListener("click",h);

View file

@ -148,7 +148,7 @@ async function waitForMessageCount(hud, text, length, selector = ".message") {
return messages;
}
async function warpToMessage(hud, dbg, text) {
async function warpToMessage(hud, dbg, text, maybeLine) {
let messages = await waitForMessages(hud, text);
ok(messages.length == 1, "Found one message");
const message = messages.pop();
@ -168,6 +168,11 @@ async function warpToMessage(hud, dbg, text) {
messages = findMessages(hud, "", ".paused");
ok(messages.length == 1, "Found one paused message");
if (maybeLine) {
const pauseLine = getVisibleSelectedFrameLine(dbg);
ok(pauseLine == maybeLine, `Paused at line ${maybeLine} after warp`);
}
return message;
async function openConsoleContextMenu(element) {

View file

@ -219,6 +219,9 @@ ChildProcess.prototype = {
if (response.memoryUsage) {
this.lastMemoryUsage = response.memoryUsage;
}
if (response.exception) {
ThrowError(response.exception);
}
}
this.paused = true;
this.manifest.onFinished(response);
@ -1546,7 +1549,16 @@ async function findEventFrameEntry(checkpoint, progress) {
scanCheckpoint: savedCheckpoint,
});
return gEventFrameEntryPoints.get(progress);
const enterFramePoint = gEventFrameEntryPoints.get(progress);
if (!enterFramePoint) {
return null;
}
// We want to stop at the first step in the frame, not at the EnterFrame.
const frameSteps = await findFrameSteps(enterFramePoint);
assert(pointEquals(frameSteps[0], enterFramePoint));
return frameSteps[1];
}
async function findEventLogpointHits(checkpoint, event, callback) {
@ -1687,12 +1699,11 @@ let gLastFlushTime = Date.now();
// If necessary, synchronously flush the recording to disk.
function ensureFlushed() {
assert(gActiveChild == gMainChild);
gMainChild.waitUntilPaused(true);
gLastFlushTime = Date.now();
if (gLastFlushCheckpoint == gActiveChild.pauseCheckpoint()) {
if (gLastFlushCheckpoint == gMainChild.pauseCheckpoint()) {
return;
}
@ -1884,6 +1895,7 @@ const gControl = {
// Add a breakpoint where the active child should pause while resuming.
addBreakpoint(position) {
dumpv(`AddBreakpoint ${JSON.stringify(position)}`);
gBreakpoints.push(position);
// Start searching for breakpoint hits in the recording immediately.
@ -1904,7 +1916,9 @@ const gControl = {
// Clear all installed breakpoints.
clearBreakpoints() {
dumpv(`ClearBreakpoints\n`);
gBreakpoints.length = 0;
if (gActiveChild == gMainChild) {
// As for addBreakpoint(), update the active breakpoints in the recording
// child immediately.

View file

@ -268,9 +268,6 @@ ReplayDebugger.prototype = {
_processResponse(request, response, divergeResponse) {
dumpv(`SendRequest: ${stringify(request)} -> ${stringify(response)}`);
if (response.exception) {
ThrowError(response.exception);
}
if (response.unhandledDivergence) {
if (divergeResponse) {
return divergeResponse;

View file

@ -1054,7 +1054,10 @@ function ManifestStart(manifest) {
dump(`Unknown manifest: ${JSON.stringify(manifest)}\n`);
}
} catch (e) {
printError("ManifestStart", e);
const msg = printError("ManifestStart", e);
RecordReplayControl.manifestFinished({
exception: `ManifestStart failed: ${msg}`,
});
}
}
@ -1184,7 +1187,10 @@ function HitCheckpoint(id) {
try {
processManifestAfterCheckpoint(point);
} catch (e) {
printError("AfterCheckpoint", e);
const msg = printError("AfterCheckpoint", e);
RecordReplayControl.manifestFinished({
exception: `AfterCheckpoint failed: ${msg}`,
});
}
}
@ -1356,6 +1362,7 @@ function getObjectData(id) {
rv.proxyTarget = convertValue(object.proxyTarget);
rv.proxyHandler = convertValue(object.proxyHandler);
}
try {
if (object.errorMessageName) {
rv.errorMessageName = object.errorMessageName;
}
@ -1368,6 +1375,9 @@ function getObjectData(id) {
if (object.errorColumnNumber) {
rv.errorColumnNumber = object.errorColumnNumber;
}
} catch (e) {
// Error getters can throw access denied errors.
}
if (CSSRule.isInstance(object.unsafeDereference())) {
rv.isInstance = "CSSRule";
} else if (Event.isInstance(object.unsafeDereference())) {
@ -2048,15 +2058,10 @@ const gRequestHandlers = {
};
function processRequest(request) {
try {
if (gRequestHandlers[request.type]) {
return gRequestHandlers[request.type](request);
}
return { exception: "No handler for " + request.type };
} catch (e) {
printError("processRequest", e);
return { exception: `Request failed: ${request.type}` };
}
throwError(`"No handler for ${request.type}`);
}
function printError(why, e) {
@ -2067,6 +2072,7 @@ function printError(why, e) {
msg = "Unknown";
}
dump(`Record/Replay Error: ${why}: ${msg}\n`);
return msg;
}
// eslint-disable-next-line no-unused-vars

View file

@ -16,7 +16,7 @@ function getNeighbors(frame, offset, rewinding) {
* return an array of all the step targets
* that could be reached next from startLocation.
*/
function findStepOffsets(frame, rewinding) {
function findStepOffsets(frame, rewinding, requireStepStart = true) {
const seen = [];
const result = [];
let worklist = getNeighbors(frame, frame.offset, rewinding);
@ -28,7 +28,7 @@ function findStepOffsets(frame, rewinding) {
}
seen.push(offset);
const meta = frame.script.getOffsetMetadata(offset);
if (meta.isStepStart) {
if (requireStepStart ? meta.isStepStart : meta.isBreakpoint) {
if (!result.includes(offset)) {
result.push(offset);
}

View file

@ -223,7 +223,6 @@ const SourceActor = ActorClassWithSpec(sourceSpec, {
// original recording. If we try to fetch it now it may have changed or
// may no longer exist.
if (this.dbg.replaying) {
assert(!this._contentType);
return this.dbg.replayingContent(this.url);
}

View file

@ -897,7 +897,11 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
});
if (thread.dbg.replaying) {
const offsets = findStepOffsets(parentFrame, rewinding);
const offsets = findStepOffsets(
parentFrame,
rewinding,
/* requireStepStart */ false
);
parentFrame.setReplayingOnStep(onStep, offsets);
} else {
parentFrame.onStep = onStep;