/* 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/. */ function ProcessHasSamplerThread(process) { return process.threads.some(t => t.name == "SamplerThread"); } async function GetPidsWithSamplerThread() { let parentProc = await ChromeUtils.requestProcInfo(); let pids = parentProc.children .filter(ProcessHasSamplerThread) .map(proc => proc.pid); if (ProcessHasSamplerThread(parentProc)) { pids.unshift(parentProc.pid); } return pids; } // fnFilterWithContentId: Called with content child pid, returns filters to use. // E.g.: 123 => ["GeckoMain", "pid:123"], or 123 => ["pid:456"]. async function test_with_filter(fnFilterWithContentId) { Assert.ok(!Services.profiler.IsActive()); info("Clear the previous pages just in case we still some open tabs."); await Services.profiler.ClearAllPages(); info("Open a tab with single_frame.html in it."); const url = BASE_URL + "single_frame.html"; return BrowserTestUtils.withNewTab(url, async function (contentBrowser) { const contentPid = await SpecialPowers.spawn(contentBrowser, [], () => { return Services.appinfo.processID; }); Assert.deepEqual( await GetPidsWithSamplerThread(), [], "There should be no SamplerThreads before starting the profiler" ); info("Start the profiler to test filters including 'pid:'."); await startProfiler({ threads: fnFilterWithContentId(contentPid) }); let pidsWithSamplerThread = null; await TestUtils.waitForCondition( async function () { let pidsStringBefore = JSON.stringify(pidsWithSamplerThread); pidsWithSamplerThread = await GetPidsWithSamplerThread(); return JSON.stringify(pidsWithSamplerThread) == pidsStringBefore; }, "Wait for sampler threads to stabilize after profiler start", /* interval (ms) */ 250, /* maxTries */ 10 ); info("Capture the profile data."); const profile = await waitSamplingAndStopAndGetProfile(); await TestUtils.waitForCondition(async function () { return !(await GetPidsWithSamplerThread()).length; }, "Wait for all sampler threads to stop after profiler stop"); return { contentPid, pidsWithSamplerThread, profile }; }); } add_task(async function browser_test_profile_capture_along_with_content_pid() { const { contentPid, pidsWithSamplerThread, profile } = await test_with_filter( contentPid => ["GeckoMain", "pid:" + contentPid] ); Assert.greater( pidsWithSamplerThread.length, 2, "There should be lots of SamplerThreads after starting the profiler" ); let contentProcessIndex = profile.processes.findIndex( p => p.threads[0].pid == contentPid ); Assert.notEqual( contentProcessIndex, -1, "The content process should be present" ); // Note: Some threads may not be registered, so we can't expect that many. But // 10 is much more than the default 4. Assert.greater( profile.processes[contentProcessIndex].threads.length, 10, "The content process should have many threads" ); Assert.equal( profile.threads.length, 1, "The parent process should have only one thread" ); Assert.equal( profile.threads[0].name, "GeckoMain", "The parent process should have the main thread" ); }); add_task(async function browser_test_profile_capture_along_with_other_pid() { const parentPid = Services.appinfo.processID; const { contentPid, pidsWithSamplerThread, profile } = await test_with_filter( contentPid => ["GeckoMain", "pid:" + parentPid] ); Assert.greater( pidsWithSamplerThread.length, 2, "There should be lots of SamplerThreads after starting the profiler" ); let contentProcessIndex = profile.processes.findIndex( p => p.threads[0].pid == contentPid ); Assert.notEqual( contentProcessIndex, -1, "The content process should be present" ); Assert.equal( profile.processes[contentProcessIndex].threads.length, 1, "The content process should have only one thread" ); // Note: Some threads may not be registered, so we can't expect that many. But // 10 is much more than the default 4. Assert.greater( profile.threads.length, 10, "The parent process should have many threads" ); }); add_task(async function browser_test_profile_capture_by_only_content_pid() { const parentPid = Services.appinfo.processID; const { contentPid, pidsWithSamplerThread, profile } = await test_with_filter( contentPid => ["pid:" + contentPid] ); // The sampler thread always runs in the parent process, see bug 1754100. Assert.deepEqual( pidsWithSamplerThread, [parentPid, contentPid], "There should only be SamplerThreads in the parent and the target child" ); Assert.equal( profile.processes.length, 1, "There should only be one child process" ); // Note: Some threads may not be registered, so we can't expect that many. But // 10 is much more than the default 4. Assert.greater( profile.processes[0].threads.length, 10, "The child process should have many threads" ); Assert.equal( profile.processes[0].threads[0].pid, contentPid, "The only child process should be our content" ); }); add_task(async function browser_test_profile_capture_by_only_parent_pid() { const parentPid = Services.appinfo.processID; const { pidsWithSamplerThread, profile } = await test_with_filter( contentPid => ["pid:" + parentPid] ); Assert.deepEqual( pidsWithSamplerThread, [parentPid], "There should only be a SamplerThread in the parent" ); // Note: Some threads may not be registered, so we can't expect that many. But // 10 is much more than the default 4. Assert.greater( profile.threads.length, 10, "The parent process should have many threads" ); Assert.equal( profile.processes.length, 0, "There should be no child processes" ); });