Bug 1124754: Use approximate object counts when testing takeCensus, to avoid being flummoxed by SpiderMonkey implementation details. r=shu

--HG--
extra : rebase_source : 21d82c9b552c2e2f72e35e3c74f660c7a99341a6
This commit is contained in:
Jim Blandy 2015-02-11 15:15:12 -08:00
parent 670653d955
commit fb57bc6ba1
2 changed files with 60 additions and 63 deletions

View File

@ -106,12 +106,12 @@ const Census = {};
};
}
function missingProp() {
throw "Census mismatch: subject lacks property present in basis";
function missingProp(prop) {
throw "Census mismatch: subject lacks property present in basis: " + uneval(prop);
}
function extraProp() {
throw "Census mismatch: subject has property not present in basis";
function extraProp(prop) {
throw "Census mismatch: subject has property not present in basis: " + uneval(prop);
}
// Return a walker that checks that the subject census has counts all equal to
@ -130,4 +130,22 @@ const Census = {};
extra: () => Census.walkAnything
});
// Return a walker that checks that the subject census has at most as many
// items of each category as |basis|.
Census.assertAllNotMoreThan = makeBasisChecker({
compare: (subject, basis) => assertEq(subject <= basis, true),
missing: missingProp,
extra: () => Census.walkAnything
});
// Return a walker that checks that the subject census has within |fudge|
// items of each category of the count in |basis|.
Census.assertAllWithin = function (fudge, basis) {
return makeBasisChecker({
compare: (subject, basis) => assertEq(Math.abs(subject - basis) <= fudge, true),
missing: missingProp,
extra: () => Census.walkAnything
})(basis);
}
})();

View File

@ -1,70 +1,49 @@
// Debugger.Memory.prototype.takeCensus behaves plausibly as we allocate and drop objects.
// Debugger.Memory.prototype.takeCensus behaves plausibly as we allocate objects.
// Exact object counts vary in ways we can't predict. For example,
// BaselineScripts can hold onto "template objects", which exist only to hold
// the shape and type for newly created objects. When BaselineScripts are
// discarded, these template objects go with them.
//
// So instead of expecting precise counts, we expect counts that are at least as
// many as we would expect given the object graph we've built.
load(libdir + 'census.js');
// A Debugger with no debuggees had better not find anything.
var dbg = new Debugger;
var census0 = dbg.memory.takeCensus();
Census.walkCensus(census0, "census0", Census.assertAllZeros);
var g1 = newGlobal();
g1.eval('var a = [];');
g1.eval('function add(f) { a.push({}); a.push(f ? (() => undefined) : null); }');
g1.eval('function remove() { a.pop(); a.pop(); }');
g1.add();
g1.remove();
// Adding a global shouldn't cause any counts to *decrease*.
dbg.addDebuggee(g1);
var census1 = dbg.memory.takeCensus();
Census.walkCensus(census1, "census1", Census.assertAllNotLessThan(census0));
function pointCheck(label, lhs, rhs, objComp, funComp) {
print(label);
try {
assertEq(objComp(lhs.objects.Object.count, rhs.objects.Object.count), true);
assertEq(funComp(lhs.objects.Function.count, rhs.objects.Function.count), true);
} catch (ex) {
print("pointCheck failed: " + ex);
print("lhs: " + JSON.stringify(lhs, undefined, 2));
print("rhs: " + JSON.stringify(rhs, undefined, 2));
// Do it again, and put the result where Mozilla's continuous integration
// code will find it.
var upload_dir = os.getenv("MOZ_UPLOAD_DIR") || ".";
redirect(upload_dir + "/Memory-takeCensus-02.txt");
print("pointCheck failed: " + ex);
print("lhs: " + JSON.stringify(lhs, undefined, 2));
print("rhs: " + JSON.stringify(rhs, undefined, 2));
throw ex;
}
function newGlobalWithDefs() {
var g = newGlobal();
g.eval(`
function times(n, fn) {
var a=[];
for (var i = 0; i<n; i++)
a.push(fn());
return a;
}`);
return g;
}
function eq(lhs, rhs) { return lhs === rhs; }
function lt(lhs, rhs) { return lhs < rhs; }
function gt(lhs, rhs) { return lhs > rhs; }
// Allocate a large number of various types of objects, and check that census
// finds them.
var g = newGlobalWithDefs();
dbg.addDebuggee(g);
// As we increase the number of reachable objects, the census should
// reflect that.
g1.add(false);
var census2 = dbg.memory.takeCensus();
pointCheck("census2", census2, census1, gt, eq);
g.eval('var objs = times(100, () => ({}));');
g.eval('var rxs = times(200, () => /foo/);');
g.eval('var ars = times(400, () => []);');
g.eval('var fns = times(800, () => () => {});');
g1.add(true);
var census3 = dbg.memory.takeCensus();
pointCheck("census3", census3, census2, gt, gt);
g1.add(false);
var census4 = dbg.memory.takeCensus();
pointCheck("census4", census4, census3, gt, eq);
// As we decrease the number of reachable objects, the census counts should go
// down. Note that since the census does its own reachability analysis, we don't
// need to GC here to see the counts drop.
g1.remove();
var census5 = dbg.memory.takeCensus();
pointCheck("census5", census5, census4, lt, eq);
g1.remove();
var census6 = dbg.memory.takeCensus();
pointCheck("census6", census6, census5, lt, lt);
var census1 = dbg.memory.takeCensus();
Census.walkCensus(census1, "census1",
Census.assertAllNotLessThan(
{ 'objects':
{ 'Object': { count: 100 },
'RegExp': { count: 200 },
'Array': { count: 400 },
'Function': { count: 800 }
}
}));