Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
/* global ChromeUtils */
// Test that refreshing the page with devtools open does not leak the old
// windows from previous navigations.
//
// IF THIS TEST STARTS FAILING, YOU ARE LEAKING EVERY WINDOW EVER NAVIGATED TO
// WHILE DEVTOOLS ARE OPEN! THIS IS NOT SPECIFIC TO THE MEMORY TOOL ONLY!
"use strict";
const {
getLabelAndShallowSize,
const TEST_URL =
async function getWindowsInSnapshot(front) {
dumpn("Taking snapshot.");
const path = await front.saveHeapSnapshot();
dumpn("Took snapshot with path = " + path);
const snapshot = ChromeUtils.readHeapSnapshot(path);
dumpn("Read snapshot into memory, taking census.");
const report = snapshot.takeCensus({
breakdown: {
by: "objectClass",
then: { by: "bucket" },
other: { by: "count", count: true, bytes: false },
},
});
dumpn("Took census, window count = " + report.Window.count);
return report.Window;
}
const DESCRIPTION = {
by: "coarseType",
objects: {
by: "objectClass",
then: { by: "count", count: true, bytes: false },
other: { by: "count", count: true, bytes: false },
},
strings: { by: "count", count: true, bytes: false },
scripts: {
by: "internalType",
then: { by: "count", count: true, bytes: false },
},
other: {
by: "internalType",
then: { by: "count", count: true, bytes: false },
},
};
this.test = makeMemoryTest(TEST_URL, async function ({ panel }) {
let front = panel.panelWin.gStore.getState().front;
const startWindows = await getWindowsInSnapshot(front);
dumpn(
"Initial windows found = " +
startWindows.map(w => "0x" + w.toString(16)).join(", ")
);
is(startWindows.length, 1);
await reloadBrowser();
// Update the front as we may have switched to a new target and a new memory front
front = panel.panelWin.gStore.getState().front;
const endWindows = await getWindowsInSnapshot(front);
is(endWindows.length, 1);
if (endWindows.length === 1) {
return;
}
dumpn("Test failed, diagnosing leaking windows.");
dumpn(
"(This may fail if a moving GC has relocated the initial Window objects.)"
);
dumpn("Taking full runtime snapshot.");
const path = await front.saveHeapSnapshot({ boundaries: { runtime: true } });
dumpn("Full runtime's snapshot path = " + path);
dumpn("Reading full runtime heap snapshot.");
const snapshot = ChromeUtils.readHeapSnapshot(path);
dumpn("Done reading full runtime heap snapshot.");
const dominatorTree = snapshot.computeDominatorTree();
const paths = snapshot.computeShortestPaths(
dominatorTree.root,
startWindows,
50
);
for (let i = 0; i < startWindows.length; i++) {
dumpn(
"Shortest retaining paths for leaking Window 0x" +
startWindows[i].toString(16) +
" ========================="
);
let j = 0;
for (const retainingPath of paths.get(startWindows[i])) {
if (retainingPath.find(part => part.predecessor === startWindows[i])) {
// Skip paths that loop out from the target window and back to it again.
continue;
}
dumpn(
" Path #" +
++j +
": --------------------------------------------------------------------"
);
for (const part of retainingPath) {
const { label } = getLabelAndShallowSize(
part.predecessor,
snapshot,
DESCRIPTION
);
dumpn(
" 0x" +
part.predecessor.toString(16) +
" (" +
label.join(" > ") +
")"
);
dumpn(" |");
dumpn(" " + part.edge);
dumpn(" |");
dumpn(" V");
}
dumpn(
" 0x" + startWindows[i].toString(16) + " (objects > Window)"
);
}
}
});