158 lines
4.5 KiB
JavaScript
158 lines
4.5 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
add_task(async () => {
|
|
if (!Services.profiler.GetFeatures().includes("nativeallocations")) {
|
|
Assert.ok(
|
|
true,
|
|
"Native allocations are not supported by this build, " +
|
|
"skip run the rest of the test."
|
|
);
|
|
return;
|
|
}
|
|
|
|
Assert.ok(
|
|
!Services.profiler.IsActive(),
|
|
"The profiler is not currently active"
|
|
);
|
|
|
|
info(
|
|
"Test that the profiler can install memory hooks and collect native allocation " +
|
|
"information in the marker payloads."
|
|
);
|
|
{
|
|
info("Start the profiler.");
|
|
await ProfilerTestUtils.startProfiler({
|
|
// Only instrument the main thread.
|
|
threads: ["GeckoMain"],
|
|
features: ["js", "nativeallocations"],
|
|
});
|
|
|
|
info(
|
|
"Do some JS work for a little bit. This will increase the amount of allocations " +
|
|
"that take place."
|
|
);
|
|
doWork();
|
|
|
|
info("Get the profile data and analyze it.");
|
|
const profile = await ProfilerTestUtils.waitSamplingAndStopAndGetProfile();
|
|
|
|
const {
|
|
allocationPayloads,
|
|
unmatchedAllocations,
|
|
logAllocationsAndDeallocations,
|
|
} = getAllocationInformation(profile);
|
|
|
|
Assert.greater(
|
|
allocationPayloads.length,
|
|
0,
|
|
"Native allocation payloads were recorded for the parent process' main thread when " +
|
|
"the Native Allocation feature was turned on."
|
|
);
|
|
|
|
if (unmatchedAllocations.length !== 0) {
|
|
info(
|
|
"There were unmatched allocations. Log all of the allocations and " +
|
|
"deallocations in order to aid debugging."
|
|
);
|
|
logAllocationsAndDeallocations();
|
|
ok(
|
|
false,
|
|
"Found a deallocation that did not have a matching allocation site. " +
|
|
"This could happen if balanced allocations is broken, or if the the " +
|
|
"buffer size of this test was too small, and some markers ended up " +
|
|
"rolling off."
|
|
);
|
|
}
|
|
|
|
ok(true, "All deallocation sites had matching allocations.");
|
|
}
|
|
|
|
info("Restart the profiler, to ensure that we get no more allocations.");
|
|
{
|
|
await ProfilerTestUtils.startProfiler({ features: ["js"] });
|
|
info("Do some work again.");
|
|
doWork();
|
|
info("Wait for the periodic sampling.");
|
|
const profile = await ProfilerTestUtils.waitSamplingAndStopAndGetProfile();
|
|
const allocationPayloads = ProfilerTestUtils.getPayloadsOfType(
|
|
profile.threads[0],
|
|
"Native allocation"
|
|
);
|
|
|
|
Assert.equal(
|
|
allocationPayloads.length,
|
|
0,
|
|
"No native allocations were collected when the feature was disabled."
|
|
);
|
|
}
|
|
});
|
|
|
|
function doWork() {
|
|
this.n = 0;
|
|
for (let i = 0; i < 1e5; i++) {
|
|
this.n += Math.random();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract the allocation payloads, and find the unmatched allocations.
|
|
*/
|
|
function getAllocationInformation(profile) {
|
|
// Get all of the allocation payloads.
|
|
const allocationPayloads = ProfilerTestUtils.getPayloadsOfType(
|
|
profile.threads[0],
|
|
"Native allocation"
|
|
);
|
|
|
|
// Decide what is an allocation and deallocation.
|
|
const allocations = allocationPayloads.filter(
|
|
payload => ensureIsNumber(payload.size) >= 0
|
|
);
|
|
const deallocations = allocationPayloads.filter(
|
|
payload => ensureIsNumber(payload.size) < 0
|
|
);
|
|
|
|
// Now determine the unmatched allocations by building a set
|
|
const allocationSites = new Set(
|
|
allocations.map(({ memoryAddress }) => memoryAddress)
|
|
);
|
|
|
|
const unmatchedAllocations = deallocations.filter(
|
|
({ memoryAddress }) => !allocationSites.has(memoryAddress)
|
|
);
|
|
|
|
// Provide a helper to log out the allocations and deallocations on failure.
|
|
function logAllocationsAndDeallocations() {
|
|
for (const { memoryAddress } of allocations) {
|
|
console.log("Allocations", formatHex(memoryAddress));
|
|
allocationSites.add(memoryAddress);
|
|
}
|
|
|
|
for (const { memoryAddress } of deallocations) {
|
|
console.log("Deallocations", formatHex(memoryAddress));
|
|
}
|
|
|
|
for (const { memoryAddress } of unmatchedAllocations) {
|
|
console.log("Deallocation with no allocation", formatHex(memoryAddress));
|
|
}
|
|
}
|
|
|
|
return {
|
|
allocationPayloads,
|
|
unmatchedAllocations,
|
|
logAllocationsAndDeallocations,
|
|
};
|
|
}
|
|
|
|
function ensureIsNumber(value) {
|
|
if (typeof value !== "number") {
|
|
throw new Error(`Expected a number: ${value}`);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function formatHex(number) {
|
|
return `0x${number.toString(16)}`;
|
|
}
|