summaryrefslogtreecommitdiffstats
path: root/tools/profiler/tests/browser/browser_test_profile_capture_by_pid.js
blob: 0d206a7148d33ad16d5de70487660ce5e860b79c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/* 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_HTTPS + "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:<content>'.");
    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(
    () => ["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(() => [
    "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"
  );
});