1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// 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 HeapSnapshotFileUtils = require("devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
const { getLabelAndShallowSize } = require("devtools/shared/heapsnapshot/DominatorTreeNode");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_empty.html";
function* getWindowsInSnapshot(front) {
dumpn("Taking snapshot.");
const path = yield 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, function* ({ tab, panel }) {
const heapWorker = panel.panelWin.gHeapAnalysesClient;
const front = panel.panelWin.gFront;
const store = panel.panelWin.gStore;
const { getState, dispatch } = store;
const doc = panel.panelWin.document;
const startWindows = yield getWindowsInSnapshot(front);
dumpn("Initial windows found = " + startWindows.map(w => "0x" + w.toString(16)).join(", "));
is(startWindows.length, 1);
yield refreshTab(tab);
const endWindows = yield 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 = yield 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 (let 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 (let 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)");
}
}
});
|