Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

add_task(async function test_psi_annotation_in_crash_report() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump(
"INFO | test_crash_psi_annotation.js | Can't test crashreporter in a non-libxul build.\n"
);
return;
}
// Create a mock PSI file with known values for testing
const mockPSIContent =
"some avg10=10.00 avg60=8.00 avg300=6.00 total=1000\n" +
"full avg10=2.00 avg60=1.50 avg300=1.00 total=500\n";
// Create mock PSI file as a temporary file
const psiFile = do_get_tempdir();
psiFile.append("test_psi_memory");
psiFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
// Write mock PSI data
await IOUtils.writeUTF8(psiFile.path, mockPSIContent);
// Register cleanup to remove the test file
registerCleanupFunction(async function () {
if (psiFile.exists()) {
try {
psiFile.remove(false);
} catch (e) {
// Ignore cleanup errors
}
}
});
// Set the PSI path for testing through environment variable
// The callback runs in a separate process, so we need to pass the path via env
Services.env.set("MOZ_TEST_PSI_PATH", psiFile.path);
// Test that PSI annotation is recorded in crash report
await do_crash(
async function () {
shouldWaitSetup = true;
shouldDelay = true;
// Read the PSI path from environment variable in the separate process
const psiPath = Services.env.get("MOZ_TEST_PSI_PATH");
const watcher = Cc["@mozilla.org/xpcom/memory-watcher;1"].getService(
Ci.nsIAvailableMemoryWatcherBase
);
if (psiPath) {
const testingWatcher = watcher.QueryInterface(
Ci.nsIAvailableMemoryWatcherTestingLinux
);
testingWatcher.setPSIPathForTesting(psiPath);
}
// Set up tab unloader and wait for it to be called
let tabUnloaderCalled = false;
const tabUnloaderPromise = new Promise(resolve => {
const mockTabUnloader = {
queryInterface: ChromeUtils.generateQI(["nsITabUnloader"]),
unloadTabAsync() {
tabUnloaderCalled = true;
resolve();
},
};
// Register our mock tab unloader through the service
watcher.registerTabUnloader(mockTabUnloader);
});
// Set memory threshold to 100% to ensure memory pressure is detected
Services.prefs.setIntPref(
"browser.low_commit_space_threshold_percent",
100
);
// Start user interaction to begin polling
Services.obs.notifyObservers(null, "user-interaction-active");
// Wait for the tab unloader to be called
await tabUnloaderPromise;
// Verify that the tab unloader was actually called
if (!tabUnloaderCalled) {
throw new Error("Tab unloader was not called");
}
// Trigger the crash now that PSI data has been processed
crashType = CrashTestUtils.CRASH_PURE_VIRTUAL_CALL;
shouldWaitSetup = false;
},
function (mdump, extra) {
Assert.ok(
"LinuxMemoryPSI" in extra,
"LinuxMemoryPSI annotation should be present"
);
// Verify the format is correct (comma-separated values)
const psiValues = extra.LinuxMemoryPSI;
Assert.strictEqual(
typeof psiValues,
"string",
"PSI values should be a string"
);
// Parse the comma-separated values
const values = psiValues.split(",");
Assert.equal(
values.length,
8,
"PSI annotation should have 8 comma-separated values"
);
// Verify the expected values from our mock PSI file
// Format: some_avg10,some_avg60,some_avg300,some_total,full_avg10,full_avg60,full_avg300,full_total
Assert.equal(values[0], "10", "some_avg10 should be 10");
Assert.equal(values[1], "8", "some_avg60 should be 8");
Assert.equal(values[2], "6", "some_avg300 should be 6");
Assert.equal(values[3], "1000", "some_total should be 1000");
Assert.equal(values[4], "2", "full_avg10 should be 2");
Assert.equal(values[5], "1", "full_avg60 should be 1");
Assert.equal(values[6], "1", "full_avg300 should be 1");
Assert.equal(values[7], "500", "full_total should be 500");
dump("INFO | PSI annotation test passed: " + psiValues + "\n");
},
// process will exit with a zero exit status
true
);
});
// Test PSI annotation when PSI file is not available
add_task(async function test_psi_annotation_no_psi_file() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump(
"INFO | test_crash_psi_annotation.js | Can't test crashreporter in a non-libxul build.\n"
);
return;
}
await do_crash(
async function () {
shouldWaitSetup = true;
shouldDelay = true;
// Set the PSI path for testing (point to a unique non-existent file)
const watcher = Cc["@mozilla.org/xpcom/memory-watcher;1"].getService(
Ci.nsIAvailableMemoryWatcherBase
);
const testingWatcher = watcher.QueryInterface(
Ci.nsIAvailableMemoryWatcherTestingLinux
);
// Build a unique path by appending PID and timestamp to the base name.
// If a file happens to exist at that path, append a sequence number.
const basePath =
Services.env.get("XPCSHELL_TEST_TEMP_DIR") + "/non_existent_psi_file";
const pid = Services.appinfo.processID;
const timestamp = Date.now();
let sequence = 0;
let psiPath = `${basePath}_${pid}_${timestamp}`;
while (await IOUtils.exists(psiPath)) {
sequence++;
psiPath = `${basePath}_${pid}_${timestamp}_${sequence}`;
}
testingWatcher.setPSIPathForTesting(psiPath);
// Set memory threshold to 100% to ensure memory pressure is detected
Services.prefs.setIntPref(
"browser.low_commit_space_threshold_percent",
100
);
// Wait for PSI data to be processed
await new Promise(resolve => {
Services.obs.addObserver(function observer(_subject, _topic) {
Services.obs.removeObserver(observer, "memory-poller-sync");
resolve();
}, "memory-poller-sync");
Services.obs.notifyObservers(null, "user-interaction-active");
});
// Trigger the crash now that PSI data has been processed
crashType = CrashTestUtils.CRASH_PURE_VIRTUAL_CALL;
shouldWaitSetup = false;
},
function (mdump, extra) {
Assert.ok(
"LinuxMemoryPSI" in extra,
"LinuxMemoryPSI annotation should be present even when PSI file is unavailable"
);
const psiValues = extra.LinuxMemoryPSI;
const values = psiValues.split(",");
Assert.equal(
values.length,
8,
"PSI annotation should have 8 values even when PSI file is unavailable"
);
// All values should be zero when PSI file is not available
for (let i = 0; i < 8; i++) {
Assert.equal(
values[i],
"0",
`PSI value ${i} should be 0 when PSI file is unavailable`
);
}
dump("INFO | PSI annotation test (no file) passed: " + psiValues + "\n");
},
true
);
});