test_HeapAnalyses_takeCensus_04.js (3530B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 "use strict"; 4 5 // Test that the HeapAnalyses{Client,Worker} can send SavedFrame stacks from 6 // by-allocation-stack reports from the worker. 7 8 add_task(async function test() { 9 const client = new HeapAnalysesClient(); 10 11 // Track some allocation stacks. 12 13 const g = newGlobal(); 14 const dbg = new Debugger(g); 15 g.eval(` // 1 16 this.log = []; // 2 17 function f() { this.log.push(allocationMarker()); } // 3 18 function g() { this.log.push(allocationMarker()); } // 4 19 function h() { this.log.push(allocationMarker()); } // 5 20 `); 21 22 // Create one allocationMarker with tracking turned off, 23 // so it will have no associated stack. 24 g.f(); 25 26 dbg.memory.allocationSamplingProbability = 1; 27 28 for (const [func, n] of [ 29 [g.f, 20], 30 [g.g, 10], 31 [g.h, 5], 32 ]) { 33 for (let i = 0; i < n; i++) { 34 dbg.memory.trackingAllocationSites = true; 35 // All allocations of allocationMarker occur with this line as the oldest 36 // stack frame. 37 func(); 38 dbg.memory.trackingAllocationSites = false; 39 } 40 } 41 42 // Take a heap snapshot. 43 44 const snapshotFilePath = saveNewHeapSnapshot({ debugger: dbg }); 45 await client.readHeapSnapshot(snapshotFilePath); 46 ok(true, "Should have read the heap snapshot"); 47 48 // Run a census broken down by class name -> allocation stack so we can grab 49 // only the AllocationMarker objects we have complete control over. 50 51 const { report } = await client.takeCensus(snapshotFilePath, { 52 breakdown: { 53 by: "objectClass", 54 then: { 55 by: "allocationStack", 56 then: { 57 by: "count", 58 bytes: true, 59 count: true, 60 }, 61 noStack: { 62 by: "count", 63 bytes: true, 64 count: true, 65 }, 66 }, 67 }, 68 }); 69 70 // Test the generated report. 71 72 ok(report, "Should get a report"); 73 74 const map = report.AllocationMarker; 75 ok(map, "Should get AllocationMarkers in the report."); 76 // From a module with a different global, and therefore a different Map 77 // constructor, so we can't use instanceof. 78 equal(Object.getPrototypeOf(map).constructor.name, "Map"); 79 80 equal( 81 map.size, 82 4, 83 "Should have 4 allocation stacks (including the lack of a stack)" 84 ); 85 86 // Gather the stacks we are expecting to appear as keys, and 87 // check that there are no unexpected keys. 88 const stacks = {}; 89 90 map.forEach((v, k) => { 91 if (k === "noStack") { 92 // No need to save this key. 93 } else if ( 94 k.functionDisplayName === "f" && 95 k.parent.functionDisplayName === "test" 96 ) { 97 stacks.f = k; 98 } else if ( 99 k.functionDisplayName === "g" && 100 k.parent.functionDisplayName === "test" 101 ) { 102 stacks.g = k; 103 } else if ( 104 k.functionDisplayName === "h" && 105 k.parent.functionDisplayName === "test" 106 ) { 107 stacks.h = k; 108 } else { 109 dumpn("Unexpected allocation stack:"); 110 k.toString() 111 .split(/\n/g) 112 .forEach(s => dumpn(s)); 113 ok(false); 114 } 115 }); 116 117 ok(map.get("noStack")); 118 equal(map.get("noStack").count, 1); 119 120 ok(stacks.f); 121 ok(map.get(stacks.f)); 122 equal(map.get(stacks.f).count, 20); 123 124 ok(stacks.g); 125 ok(map.get(stacks.g)); 126 equal(map.get(stacks.g).count, 10); 127 128 ok(stacks.h); 129 ok(map.get(stacks.h)); 130 equal(map.get(stacks.h).count, 5); 131 132 client.destroy(); 133 });