summaryrefslogtreecommitdiffstats
path: root/devtools/shared/heapsnapshot/tests/xpcshell/test_HeapAnalyses_takeCensus_04.js
blob: 26f78ad4f7fc2b0db32f55e769c1e72ba5e6de38 (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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";

// Test that the HeapAnalyses{Client,Worker} can send SavedFrame stacks from
// by-allocation-stack reports from the worker.

add_task(async function test() {
  const client = new HeapAnalysesClient();

  // Track some allocation stacks.

  const g = newGlobal();
  const dbg = new Debugger(g);
  g.eval(`                                                   // 1
         this.log = [];                                      // 2
         function f() { this.log.push(allocationMarker()); } // 3
         function g() { this.log.push(allocationMarker()); } // 4
         function h() { this.log.push(allocationMarker()); } // 5
         `);

  // Create one allocationMarker with tracking turned off,
  // so it will have no associated stack.
  g.f();

  dbg.memory.allocationSamplingProbability = 1;

  for (const [func, n] of [
    [g.f, 20],
    [g.g, 10],
    [g.h, 5],
  ]) {
    for (let i = 0; i < n; i++) {
      dbg.memory.trackingAllocationSites = true;
      // All allocations of allocationMarker occur with this line as the oldest
      // stack frame.
      func();
      dbg.memory.trackingAllocationSites = false;
    }
  }

  // Take a heap snapshot.

  const snapshotFilePath = saveNewHeapSnapshot({ debugger: dbg });
  await client.readHeapSnapshot(snapshotFilePath);
  ok(true, "Should have read the heap snapshot");

  // Run a census broken down by class name -> allocation stack so we can grab
  // only the AllocationMarker objects we have complete control over.

  const { report } = await client.takeCensus(snapshotFilePath, {
    breakdown: {
      by: "objectClass",
      then: {
        by: "allocationStack",
        then: {
          by: "count",
          bytes: true,
          count: true,
        },
        noStack: {
          by: "count",
          bytes: true,
          count: true,
        },
      },
    },
  });

  // Test the generated report.

  ok(report, "Should get a report");

  const map = report.AllocationMarker;
  ok(map, "Should get AllocationMarkers in the report.");
  // From a module with a different global, and therefore a different Map
  // constructor, so we can't use instanceof.
  equal(Object.getPrototypeOf(map).constructor.name, "Map");

  equal(
    map.size,
    4,
    "Should have 4 allocation stacks (including the lack of a stack)"
  );

  // Gather the stacks we are expecting to appear as keys, and
  // check that there are no unexpected keys.
  const stacks = {};

  map.forEach((v, k) => {
    if (k === "noStack") {
      // No need to save this key.
    } else if (
      k.functionDisplayName === "f" &&
      k.parent.functionDisplayName === "test"
    ) {
      stacks.f = k;
    } else if (
      k.functionDisplayName === "g" &&
      k.parent.functionDisplayName === "test"
    ) {
      stacks.g = k;
    } else if (
      k.functionDisplayName === "h" &&
      k.parent.functionDisplayName === "test"
    ) {
      stacks.h = k;
    } else {
      dumpn("Unexpected allocation stack:");
      k.toString()
        .split(/\n/g)
        .forEach(s => dumpn(s));
      ok(false);
    }
  });

  ok(map.get("noStack"));
  equal(map.get("noStack").count, 1);

  ok(stacks.f);
  ok(map.get(stacks.f));
  equal(map.get(stacks.f).count, 20);

  ok(stacks.g);
  ok(map.get(stacks.g));
  equal(map.get(stacks.g).count, 10);

  ok(stacks.h);
  ok(map.get(stacks.h));
  equal(map.get(stacks.h).count, 5);

  client.destroy();
});