xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.ProcessThreadGroups/index.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2024 The Android Open Source Project
2*6dbdd20aSAndroid Build Coastguard Worker//
3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*6dbdd20aSAndroid Build Coastguard Worker//
7*6dbdd20aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
8*6dbdd20aSAndroid Build Coastguard Worker//
9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License.
14*6dbdd20aSAndroid Build Coastguard Worker
15*6dbdd20aSAndroid Build Coastguard Workerimport {Trace} from '../../public/trace';
16*6dbdd20aSAndroid Build Coastguard Workerimport {PerfettoPlugin} from '../../public/plugin';
17*6dbdd20aSAndroid Build Coastguard Workerimport {TrackNode} from '../../public/workspace';
18*6dbdd20aSAndroid Build Coastguard Workerimport {NUM, STR, STR_NULL} from '../../trace_processor/query_result';
19*6dbdd20aSAndroid Build Coastguard Worker
20*6dbdd20aSAndroid Build Coastguard Workerfunction stripPathFromExecutable(path: string) {
21*6dbdd20aSAndroid Build Coastguard Worker  if (path[0] === '/') {
22*6dbdd20aSAndroid Build Coastguard Worker    return path.split('/').slice(-1)[0];
23*6dbdd20aSAndroid Build Coastguard Worker  } else {
24*6dbdd20aSAndroid Build Coastguard Worker    return path;
25*6dbdd20aSAndroid Build Coastguard Worker  }
26*6dbdd20aSAndroid Build Coastguard Worker}
27*6dbdd20aSAndroid Build Coastguard Worker
28*6dbdd20aSAndroid Build Coastguard Workerfunction getThreadDisplayName(threadName: string | undefined, tid: number) {
29*6dbdd20aSAndroid Build Coastguard Worker  if (threadName) {
30*6dbdd20aSAndroid Build Coastguard Worker    return `${stripPathFromExecutable(threadName)} ${tid}`;
31*6dbdd20aSAndroid Build Coastguard Worker  } else {
32*6dbdd20aSAndroid Build Coastguard Worker    return `Thread ${tid}`;
33*6dbdd20aSAndroid Build Coastguard Worker  }
34*6dbdd20aSAndroid Build Coastguard Worker}
35*6dbdd20aSAndroid Build Coastguard Worker
36*6dbdd20aSAndroid Build Coastguard Worker// This plugin is responsible for organizing all process and thread groups
37*6dbdd20aSAndroid Build Coastguard Worker// including the kernel groups, sorting, and adding summary tracks.
38*6dbdd20aSAndroid Build Coastguard Workerexport default class implements PerfettoPlugin {
39*6dbdd20aSAndroid Build Coastguard Worker  static readonly id = 'dev.perfetto.ProcessThreadGroups';
40*6dbdd20aSAndroid Build Coastguard Worker
41*6dbdd20aSAndroid Build Coastguard Worker  private readonly processGroups = new Map<number, TrackNode>();
42*6dbdd20aSAndroid Build Coastguard Worker  private readonly threadGroups = new Map<number, TrackNode>();
43*6dbdd20aSAndroid Build Coastguard Worker
44*6dbdd20aSAndroid Build Coastguard Worker  constructor(private readonly ctx: Trace) {}
45*6dbdd20aSAndroid Build Coastguard Worker
46*6dbdd20aSAndroid Build Coastguard Worker  getGroupForProcess(upid: number): TrackNode | undefined {
47*6dbdd20aSAndroid Build Coastguard Worker    return this.processGroups.get(upid);
48*6dbdd20aSAndroid Build Coastguard Worker  }
49*6dbdd20aSAndroid Build Coastguard Worker
50*6dbdd20aSAndroid Build Coastguard Worker  getGroupForThread(utid: number): TrackNode | undefined {
51*6dbdd20aSAndroid Build Coastguard Worker    return this.threadGroups.get(utid);
52*6dbdd20aSAndroid Build Coastguard Worker  }
53*6dbdd20aSAndroid Build Coastguard Worker
54*6dbdd20aSAndroid Build Coastguard Worker  async onTraceLoad(ctx: Trace): Promise<void> {
55*6dbdd20aSAndroid Build Coastguard Worker    // Pre-group all kernel "threads" (actually processes) if this is a linux
56*6dbdd20aSAndroid Build Coastguard Worker    // system trace. Below, addProcessTrackGroups will skip them due to an
57*6dbdd20aSAndroid Build Coastguard Worker    // existing group uuid, and addThreadStateTracks will fill in the
58*6dbdd20aSAndroid Build Coastguard Worker    // per-thread tracks. Quirk: since all threads will appear to be
59*6dbdd20aSAndroid Build Coastguard Worker    // TrackKindPriority.MAIN_THREAD, any process-level tracks will end up
60*6dbdd20aSAndroid Build Coastguard Worker    // pushed to the bottom of the group in the UI.
61*6dbdd20aSAndroid Build Coastguard Worker    await this.addKernelThreadGrouping();
62*6dbdd20aSAndroid Build Coastguard Worker
63*6dbdd20aSAndroid Build Coastguard Worker    // Create the per-process track groups. Note that this won't necessarily
64*6dbdd20aSAndroid Build Coastguard Worker    // create a track per process. If a process has been completely idle and has
65*6dbdd20aSAndroid Build Coastguard Worker    // no sched events, no track group will be emitted.
66*6dbdd20aSAndroid Build Coastguard Worker    // Will populate this.addTrackGroupActions
67*6dbdd20aSAndroid Build Coastguard Worker    await this.addProcessGroups();
68*6dbdd20aSAndroid Build Coastguard Worker    await this.addThreadGroups();
69*6dbdd20aSAndroid Build Coastguard Worker
70*6dbdd20aSAndroid Build Coastguard Worker    ctx.onTraceReady.addListener(() => {
71*6dbdd20aSAndroid Build Coastguard Worker      // If, by the time the trace has finished loading, some of the process or
72*6dbdd20aSAndroid Build Coastguard Worker      // thread group tracks nodes have no children, just remove them.
73*6dbdd20aSAndroid Build Coastguard Worker      const removeIfEmpty = (g: TrackNode) => {
74*6dbdd20aSAndroid Build Coastguard Worker        if (!g.hasChildren) {
75*6dbdd20aSAndroid Build Coastguard Worker          g.remove();
76*6dbdd20aSAndroid Build Coastguard Worker        }
77*6dbdd20aSAndroid Build Coastguard Worker      };
78*6dbdd20aSAndroid Build Coastguard Worker      this.processGroups.forEach(removeIfEmpty);
79*6dbdd20aSAndroid Build Coastguard Worker      this.threadGroups.forEach(removeIfEmpty);
80*6dbdd20aSAndroid Build Coastguard Worker    });
81*6dbdd20aSAndroid Build Coastguard Worker  }
82*6dbdd20aSAndroid Build Coastguard Worker
83*6dbdd20aSAndroid Build Coastguard Worker  private async addKernelThreadGrouping(): Promise<void> {
84*6dbdd20aSAndroid Build Coastguard Worker    // Identify kernel threads if this is a linux system trace, and sufficient
85*6dbdd20aSAndroid Build Coastguard Worker    // process information is available. Kernel threads are identified by being
86*6dbdd20aSAndroid Build Coastguard Worker    // children of kthreadd (always pid 2).
87*6dbdd20aSAndroid Build Coastguard Worker    // The query will return the kthreadd process row first, which must exist
88*6dbdd20aSAndroid Build Coastguard Worker    // for any other kthreads to be returned by the query.
89*6dbdd20aSAndroid Build Coastguard Worker    // TODO(rsavitski): figure out how to handle the idle process (swapper),
90*6dbdd20aSAndroid Build Coastguard Worker    // which has pid 0 but appears as a distinct process (with its own comm) on
91*6dbdd20aSAndroid Build Coastguard Worker    // each cpu. It'd make sense to exclude its thread state track, but still
92*6dbdd20aSAndroid Build Coastguard Worker    // put process-scoped tracks in this group.
93*6dbdd20aSAndroid Build Coastguard Worker    const result = await this.ctx.engine.query(`
94*6dbdd20aSAndroid Build Coastguard Worker      select
95*6dbdd20aSAndroid Build Coastguard Worker        t.utid, p.upid, (case p.pid when 2 then 1 else 0 end) isKthreadd
96*6dbdd20aSAndroid Build Coastguard Worker      from
97*6dbdd20aSAndroid Build Coastguard Worker        thread t
98*6dbdd20aSAndroid Build Coastguard Worker        join process p using (upid)
99*6dbdd20aSAndroid Build Coastguard Worker        left join process parent on (p.parent_upid = parent.upid)
100*6dbdd20aSAndroid Build Coastguard Worker        join
101*6dbdd20aSAndroid Build Coastguard Worker          (select true from metadata m
102*6dbdd20aSAndroid Build Coastguard Worker             where (m.name = 'system_name' and m.str_value = 'Linux')
103*6dbdd20aSAndroid Build Coastguard Worker           union
104*6dbdd20aSAndroid Build Coastguard Worker           select 1 from (select true from sched limit 1))
105*6dbdd20aSAndroid Build Coastguard Worker      where
106*6dbdd20aSAndroid Build Coastguard Worker        p.pid = 2 or parent.pid = 2
107*6dbdd20aSAndroid Build Coastguard Worker      order by isKthreadd desc
108*6dbdd20aSAndroid Build Coastguard Worker    `);
109*6dbdd20aSAndroid Build Coastguard Worker
110*6dbdd20aSAndroid Build Coastguard Worker    const it = result.iter({
111*6dbdd20aSAndroid Build Coastguard Worker      utid: NUM,
112*6dbdd20aSAndroid Build Coastguard Worker      upid: NUM,
113*6dbdd20aSAndroid Build Coastguard Worker    });
114*6dbdd20aSAndroid Build Coastguard Worker
115*6dbdd20aSAndroid Build Coastguard Worker    // Not applying kernel thread grouping.
116*6dbdd20aSAndroid Build Coastguard Worker    if (!it.valid()) {
117*6dbdd20aSAndroid Build Coastguard Worker      return;
118*6dbdd20aSAndroid Build Coastguard Worker    }
119*6dbdd20aSAndroid Build Coastguard Worker
120*6dbdd20aSAndroid Build Coastguard Worker    // Create the track group. Use kthreadd's PROCESS_SUMMARY_TRACK for the
121*6dbdd20aSAndroid Build Coastguard Worker    // main track. It doesn't summarise the kernel threads within the group,
122*6dbdd20aSAndroid Build Coastguard Worker    // but creating a dedicated track type is out of scope at the time of
123*6dbdd20aSAndroid Build Coastguard Worker    // writing.
124*6dbdd20aSAndroid Build Coastguard Worker    const kernelThreadsGroup = new TrackNode({
125*6dbdd20aSAndroid Build Coastguard Worker      title: 'Kernel threads',
126*6dbdd20aSAndroid Build Coastguard Worker      uri: '/kernel',
127*6dbdd20aSAndroid Build Coastguard Worker      sortOrder: 50,
128*6dbdd20aSAndroid Build Coastguard Worker      isSummary: true,
129*6dbdd20aSAndroid Build Coastguard Worker    });
130*6dbdd20aSAndroid Build Coastguard Worker    this.ctx.workspace.addChildInOrder(kernelThreadsGroup);
131*6dbdd20aSAndroid Build Coastguard Worker
132*6dbdd20aSAndroid Build Coastguard Worker    // Set the group for all kernel threads (including kthreadd itself).
133*6dbdd20aSAndroid Build Coastguard Worker    for (; it.valid(); it.next()) {
134*6dbdd20aSAndroid Build Coastguard Worker      const {utid} = it;
135*6dbdd20aSAndroid Build Coastguard Worker
136*6dbdd20aSAndroid Build Coastguard Worker      const threadGroup = new TrackNode({
137*6dbdd20aSAndroid Build Coastguard Worker        uri: `thread${utid}`,
138*6dbdd20aSAndroid Build Coastguard Worker        title: `Thread ${utid}`,
139*6dbdd20aSAndroid Build Coastguard Worker        isSummary: true,
140*6dbdd20aSAndroid Build Coastguard Worker        headless: true,
141*6dbdd20aSAndroid Build Coastguard Worker      });
142*6dbdd20aSAndroid Build Coastguard Worker      kernelThreadsGroup.addChildInOrder(threadGroup);
143*6dbdd20aSAndroid Build Coastguard Worker      this.threadGroups.set(utid, threadGroup);
144*6dbdd20aSAndroid Build Coastguard Worker    }
145*6dbdd20aSAndroid Build Coastguard Worker  }
146*6dbdd20aSAndroid Build Coastguard Worker
147*6dbdd20aSAndroid Build Coastguard Worker  // Adds top level groups for processes and thread that don't belong to a
148*6dbdd20aSAndroid Build Coastguard Worker  // process.
149*6dbdd20aSAndroid Build Coastguard Worker  private async addProcessGroups(): Promise<void> {
150*6dbdd20aSAndroid Build Coastguard Worker    const result = await this.ctx.engine.query(`
151*6dbdd20aSAndroid Build Coastguard Worker      with processGroups as (
152*6dbdd20aSAndroid Build Coastguard Worker        select
153*6dbdd20aSAndroid Build Coastguard Worker          upid,
154*6dbdd20aSAndroid Build Coastguard Worker          process.pid as pid,
155*6dbdd20aSAndroid Build Coastguard Worker          process.name as processName,
156*6dbdd20aSAndroid Build Coastguard Worker          sum_running_dur as sumRunningDur,
157*6dbdd20aSAndroid Build Coastguard Worker          thread_slice_count + process_slice_count as sliceCount,
158*6dbdd20aSAndroid Build Coastguard Worker          perf_sample_count as perfSampleCount,
159*6dbdd20aSAndroid Build Coastguard Worker          allocation_count as heapProfileAllocationCount,
160*6dbdd20aSAndroid Build Coastguard Worker          graph_object_count as heapGraphObjectCount,
161*6dbdd20aSAndroid Build Coastguard Worker          (
162*6dbdd20aSAndroid Build Coastguard Worker            select group_concat(string_value)
163*6dbdd20aSAndroid Build Coastguard Worker            from args
164*6dbdd20aSAndroid Build Coastguard Worker            where
165*6dbdd20aSAndroid Build Coastguard Worker              process.arg_set_id is not null and
166*6dbdd20aSAndroid Build Coastguard Worker              arg_set_id = process.arg_set_id and
167*6dbdd20aSAndroid Build Coastguard Worker              flat_key = 'chrome.process_label'
168*6dbdd20aSAndroid Build Coastguard Worker          ) chromeProcessLabels,
169*6dbdd20aSAndroid Build Coastguard Worker          case process.name
170*6dbdd20aSAndroid Build Coastguard Worker            when 'Browser' then 3
171*6dbdd20aSAndroid Build Coastguard Worker            when 'Gpu' then 2
172*6dbdd20aSAndroid Build Coastguard Worker            when 'Renderer' then 1
173*6dbdd20aSAndroid Build Coastguard Worker            else 0
174*6dbdd20aSAndroid Build Coastguard Worker          end as chromeProcessRank
175*6dbdd20aSAndroid Build Coastguard Worker        from _process_available_info_summary
176*6dbdd20aSAndroid Build Coastguard Worker        join process using(upid)
177*6dbdd20aSAndroid Build Coastguard Worker      ),
178*6dbdd20aSAndroid Build Coastguard Worker      threadGroups as (
179*6dbdd20aSAndroid Build Coastguard Worker        select
180*6dbdd20aSAndroid Build Coastguard Worker          utid,
181*6dbdd20aSAndroid Build Coastguard Worker          tid,
182*6dbdd20aSAndroid Build Coastguard Worker          thread.name as threadName,
183*6dbdd20aSAndroid Build Coastguard Worker          sum_running_dur as sumRunningDur,
184*6dbdd20aSAndroid Build Coastguard Worker          slice_count as sliceCount,
185*6dbdd20aSAndroid Build Coastguard Worker          perf_sample_count as perfSampleCount
186*6dbdd20aSAndroid Build Coastguard Worker        from _thread_available_info_summary
187*6dbdd20aSAndroid Build Coastguard Worker        join thread using (utid)
188*6dbdd20aSAndroid Build Coastguard Worker        where upid is null
189*6dbdd20aSAndroid Build Coastguard Worker      )
190*6dbdd20aSAndroid Build Coastguard Worker      select *
191*6dbdd20aSAndroid Build Coastguard Worker      from (
192*6dbdd20aSAndroid Build Coastguard Worker        select
193*6dbdd20aSAndroid Build Coastguard Worker          'process' as kind,
194*6dbdd20aSAndroid Build Coastguard Worker          upid as uid,
195*6dbdd20aSAndroid Build Coastguard Worker          pid as id,
196*6dbdd20aSAndroid Build Coastguard Worker          processName as name
197*6dbdd20aSAndroid Build Coastguard Worker        from processGroups
198*6dbdd20aSAndroid Build Coastguard Worker        order by
199*6dbdd20aSAndroid Build Coastguard Worker          chromeProcessRank desc,
200*6dbdd20aSAndroid Build Coastguard Worker          heapProfileAllocationCount desc,
201*6dbdd20aSAndroid Build Coastguard Worker          heapGraphObjectCount desc,
202*6dbdd20aSAndroid Build Coastguard Worker          perfSampleCount desc,
203*6dbdd20aSAndroid Build Coastguard Worker          sumRunningDur desc,
204*6dbdd20aSAndroid Build Coastguard Worker          sliceCount desc,
205*6dbdd20aSAndroid Build Coastguard Worker          processName asc,
206*6dbdd20aSAndroid Build Coastguard Worker          upid asc
207*6dbdd20aSAndroid Build Coastguard Worker      )
208*6dbdd20aSAndroid Build Coastguard Worker      union all
209*6dbdd20aSAndroid Build Coastguard Worker      select *
210*6dbdd20aSAndroid Build Coastguard Worker      from (
211*6dbdd20aSAndroid Build Coastguard Worker        select
212*6dbdd20aSAndroid Build Coastguard Worker          'thread' as kind,
213*6dbdd20aSAndroid Build Coastguard Worker          utid as uid,
214*6dbdd20aSAndroid Build Coastguard Worker          tid as id,
215*6dbdd20aSAndroid Build Coastguard Worker          threadName as name
216*6dbdd20aSAndroid Build Coastguard Worker        from threadGroups
217*6dbdd20aSAndroid Build Coastguard Worker        order by
218*6dbdd20aSAndroid Build Coastguard Worker          perfSampleCount desc,
219*6dbdd20aSAndroid Build Coastguard Worker          sumRunningDur desc,
220*6dbdd20aSAndroid Build Coastguard Worker          sliceCount desc,
221*6dbdd20aSAndroid Build Coastguard Worker          threadName asc,
222*6dbdd20aSAndroid Build Coastguard Worker          utid asc
223*6dbdd20aSAndroid Build Coastguard Worker      )
224*6dbdd20aSAndroid Build Coastguard Worker  `);
225*6dbdd20aSAndroid Build Coastguard Worker
226*6dbdd20aSAndroid Build Coastguard Worker    const it = result.iter({
227*6dbdd20aSAndroid Build Coastguard Worker      kind: STR,
228*6dbdd20aSAndroid Build Coastguard Worker      uid: NUM,
229*6dbdd20aSAndroid Build Coastguard Worker      id: NUM,
230*6dbdd20aSAndroid Build Coastguard Worker      name: STR_NULL,
231*6dbdd20aSAndroid Build Coastguard Worker    });
232*6dbdd20aSAndroid Build Coastguard Worker    for (; it.valid(); it.next()) {
233*6dbdd20aSAndroid Build Coastguard Worker      const {kind, uid, id, name} = it;
234*6dbdd20aSAndroid Build Coastguard Worker
235*6dbdd20aSAndroid Build Coastguard Worker      if (kind === 'process') {
236*6dbdd20aSAndroid Build Coastguard Worker        // Ignore kernel process groups
237*6dbdd20aSAndroid Build Coastguard Worker        if (this.processGroups.has(uid)) {
238*6dbdd20aSAndroid Build Coastguard Worker          continue;
239*6dbdd20aSAndroid Build Coastguard Worker        }
240*6dbdd20aSAndroid Build Coastguard Worker
241*6dbdd20aSAndroid Build Coastguard Worker        function getProcessDisplayName(
242*6dbdd20aSAndroid Build Coastguard Worker          processName: string | undefined,
243*6dbdd20aSAndroid Build Coastguard Worker          pid: number,
244*6dbdd20aSAndroid Build Coastguard Worker        ) {
245*6dbdd20aSAndroid Build Coastguard Worker          if (processName) {
246*6dbdd20aSAndroid Build Coastguard Worker            return `${stripPathFromExecutable(processName)} ${pid}`;
247*6dbdd20aSAndroid Build Coastguard Worker          } else {
248*6dbdd20aSAndroid Build Coastguard Worker            return `Process ${pid}`;
249*6dbdd20aSAndroid Build Coastguard Worker          }
250*6dbdd20aSAndroid Build Coastguard Worker        }
251*6dbdd20aSAndroid Build Coastguard Worker
252*6dbdd20aSAndroid Build Coastguard Worker        const displayName = getProcessDisplayName(name ?? undefined, id);
253*6dbdd20aSAndroid Build Coastguard Worker        const group = new TrackNode({
254*6dbdd20aSAndroid Build Coastguard Worker          uri: `/process_${uid}`,
255*6dbdd20aSAndroid Build Coastguard Worker          title: displayName,
256*6dbdd20aSAndroid Build Coastguard Worker          isSummary: true,
257*6dbdd20aSAndroid Build Coastguard Worker          sortOrder: 50,
258*6dbdd20aSAndroid Build Coastguard Worker        });
259*6dbdd20aSAndroid Build Coastguard Worker
260*6dbdd20aSAndroid Build Coastguard Worker        // Re-insert the child node to sort it
261*6dbdd20aSAndroid Build Coastguard Worker        this.ctx.workspace.addChildInOrder(group);
262*6dbdd20aSAndroid Build Coastguard Worker        this.processGroups.set(uid, group);
263*6dbdd20aSAndroid Build Coastguard Worker      } else {
264*6dbdd20aSAndroid Build Coastguard Worker        // Ignore kernel process groups
265*6dbdd20aSAndroid Build Coastguard Worker        if (this.threadGroups.has(uid)) {
266*6dbdd20aSAndroid Build Coastguard Worker          continue;
267*6dbdd20aSAndroid Build Coastguard Worker        }
268*6dbdd20aSAndroid Build Coastguard Worker
269*6dbdd20aSAndroid Build Coastguard Worker        const displayName = getThreadDisplayName(name ?? undefined, id);
270*6dbdd20aSAndroid Build Coastguard Worker        const group = new TrackNode({
271*6dbdd20aSAndroid Build Coastguard Worker          uri: `/thread_${uid}`,
272*6dbdd20aSAndroid Build Coastguard Worker          title: displayName,
273*6dbdd20aSAndroid Build Coastguard Worker          isSummary: true,
274*6dbdd20aSAndroid Build Coastguard Worker          sortOrder: 50,
275*6dbdd20aSAndroid Build Coastguard Worker        });
276*6dbdd20aSAndroid Build Coastguard Worker
277*6dbdd20aSAndroid Build Coastguard Worker        // Re-insert the child node to sort it
278*6dbdd20aSAndroid Build Coastguard Worker        this.ctx.workspace.addChildInOrder(group);
279*6dbdd20aSAndroid Build Coastguard Worker        this.threadGroups.set(uid, group);
280*6dbdd20aSAndroid Build Coastguard Worker      }
281*6dbdd20aSAndroid Build Coastguard Worker    }
282*6dbdd20aSAndroid Build Coastguard Worker  }
283*6dbdd20aSAndroid Build Coastguard Worker
284*6dbdd20aSAndroid Build Coastguard Worker  // Create all the nested & headless thread groups that live inside existing
285*6dbdd20aSAndroid Build Coastguard Worker  // process groups.
286*6dbdd20aSAndroid Build Coastguard Worker  private async addThreadGroups(): Promise<void> {
287*6dbdd20aSAndroid Build Coastguard Worker    const result = await this.ctx.engine.query(`
288*6dbdd20aSAndroid Build Coastguard Worker      with threadGroups as (
289*6dbdd20aSAndroid Build Coastguard Worker        select
290*6dbdd20aSAndroid Build Coastguard Worker          utid,
291*6dbdd20aSAndroid Build Coastguard Worker          upid,
292*6dbdd20aSAndroid Build Coastguard Worker          tid,
293*6dbdd20aSAndroid Build Coastguard Worker          thread.name as threadName,
294*6dbdd20aSAndroid Build Coastguard Worker          CASE
295*6dbdd20aSAndroid Build Coastguard Worker            WHEN thread.is_main_thread = 1 THEN 10
296*6dbdd20aSAndroid Build Coastguard Worker            WHEN thread.name = 'CrBrowserMain' THEN 10
297*6dbdd20aSAndroid Build Coastguard Worker            WHEN thread.name = 'CrRendererMain' THEN 10
298*6dbdd20aSAndroid Build Coastguard Worker            WHEN thread.name = 'CrGpuMain' THEN 10
299*6dbdd20aSAndroid Build Coastguard Worker            WHEN thread.name glob '*RenderThread*' THEN 9
300*6dbdd20aSAndroid Build Coastguard Worker            WHEN thread.name glob '*GPU completion*' THEN 8
301*6dbdd20aSAndroid Build Coastguard Worker            WHEN thread.name = 'Chrome_ChildIOThread' THEN 7
302*6dbdd20aSAndroid Build Coastguard Worker            WHEN thread.name = 'Chrome_IOThread' THEN 7
303*6dbdd20aSAndroid Build Coastguard Worker            WHEN thread.name = 'Compositor' THEN 6
304*6dbdd20aSAndroid Build Coastguard Worker            WHEN thread.name = 'VizCompositorThread' THEN 6
305*6dbdd20aSAndroid Build Coastguard Worker            ELSE 5
306*6dbdd20aSAndroid Build Coastguard Worker          END as priority
307*6dbdd20aSAndroid Build Coastguard Worker        from _thread_available_info_summary
308*6dbdd20aSAndroid Build Coastguard Worker        join thread using (utid)
309*6dbdd20aSAndroid Build Coastguard Worker        where upid is not null
310*6dbdd20aSAndroid Build Coastguard Worker      )
311*6dbdd20aSAndroid Build Coastguard Worker      select *
312*6dbdd20aSAndroid Build Coastguard Worker      from (
313*6dbdd20aSAndroid Build Coastguard Worker        select
314*6dbdd20aSAndroid Build Coastguard Worker          utid,
315*6dbdd20aSAndroid Build Coastguard Worker          upid,
316*6dbdd20aSAndroid Build Coastguard Worker          tid,
317*6dbdd20aSAndroid Build Coastguard Worker          threadName
318*6dbdd20aSAndroid Build Coastguard Worker        from threadGroups
319*6dbdd20aSAndroid Build Coastguard Worker        order by
320*6dbdd20aSAndroid Build Coastguard Worker          priority desc,
321*6dbdd20aSAndroid Build Coastguard Worker          tid asc
322*6dbdd20aSAndroid Build Coastguard Worker      )
323*6dbdd20aSAndroid Build Coastguard Worker  `);
324*6dbdd20aSAndroid Build Coastguard Worker
325*6dbdd20aSAndroid Build Coastguard Worker    const it = result.iter({
326*6dbdd20aSAndroid Build Coastguard Worker      utid: NUM,
327*6dbdd20aSAndroid Build Coastguard Worker      tid: NUM,
328*6dbdd20aSAndroid Build Coastguard Worker      upid: NUM,
329*6dbdd20aSAndroid Build Coastguard Worker      threadName: STR_NULL,
330*6dbdd20aSAndroid Build Coastguard Worker    });
331*6dbdd20aSAndroid Build Coastguard Worker    for (; it.valid(); it.next()) {
332*6dbdd20aSAndroid Build Coastguard Worker      const {utid, tid, upid, threadName} = it;
333*6dbdd20aSAndroid Build Coastguard Worker
334*6dbdd20aSAndroid Build Coastguard Worker      // Ignore kernel thread groups
335*6dbdd20aSAndroid Build Coastguard Worker      if (this.threadGroups.has(utid)) {
336*6dbdd20aSAndroid Build Coastguard Worker        continue;
337*6dbdd20aSAndroid Build Coastguard Worker      }
338*6dbdd20aSAndroid Build Coastguard Worker
339*6dbdd20aSAndroid Build Coastguard Worker      const group = new TrackNode({
340*6dbdd20aSAndroid Build Coastguard Worker        uri: `/thread_${utid}`,
341*6dbdd20aSAndroid Build Coastguard Worker        title: getThreadDisplayName(threadName ?? undefined, tid),
342*6dbdd20aSAndroid Build Coastguard Worker        isSummary: true,
343*6dbdd20aSAndroid Build Coastguard Worker        headless: true,
344*6dbdd20aSAndroid Build Coastguard Worker      });
345*6dbdd20aSAndroid Build Coastguard Worker      this.threadGroups.set(utid, group);
346*6dbdd20aSAndroid Build Coastguard Worker      this.processGroups.get(upid)?.addChildInOrder(group);
347*6dbdd20aSAndroid Build Coastguard Worker    }
348*6dbdd20aSAndroid Build Coastguard Worker  }
349*6dbdd20aSAndroid Build Coastguard Worker}
350