summaryrefslogtreecommitdiffstats
path: root/devtools/client/memory/test/browser/browser_memory_refresh_does_not_leak.js
blob: e0ee1eee413b5c44f46239b4c31076fd63b0c3f9 (plain)
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

/* 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,
} = require("resource://devtools/shared/heapsnapshot/DominatorTreeNode.js");

const TEST_URL =
  "http://example.com/browser/devtools/client/memory/test/browser/doc_empty.html";

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 ({ tab, 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)"
      );
    }
  }
});