/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // Test that the HeapAnalyses{Client,Worker} can send SavedFrame stacks from // by-allocation-stack reports from the worker. function run_test() { run_next_test(); } add_task(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 `); // 6 // Create one allocationMarker with tracking turned off, // so it will have no associated stack. g.f(); dbg.memory.allocationSamplingProbability = 1; for (let [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 }); yield 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 } = yield 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(map.__proto__.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. let 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(); });