xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.AsyncSlices/index.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2021 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 {removeFalsyValues} from '../../base/array_utils';
16*6dbdd20aSAndroid Build Coastguard Workerimport {TrackNode} from '../../public/workspace';
17*6dbdd20aSAndroid Build Coastguard Workerimport {SLICE_TRACK_KIND} from '../../public/track_kinds';
18*6dbdd20aSAndroid Build Coastguard Workerimport {Trace} from '../../public/trace';
19*6dbdd20aSAndroid Build Coastguard Workerimport {PerfettoPlugin} from '../../public/plugin';
20*6dbdd20aSAndroid Build Coastguard Workerimport {getThreadUriPrefix, getTrackName} from '../../public/utils';
21*6dbdd20aSAndroid Build Coastguard Workerimport {NUM, NUM_NULL, STR, STR_NULL} from '../../trace_processor/query_result';
22*6dbdd20aSAndroid Build Coastguard Workerimport {AsyncSliceTrack} from './async_slice_track';
23*6dbdd20aSAndroid Build Coastguard Workerimport {exists} from '../../base/utils';
24*6dbdd20aSAndroid Build Coastguard Workerimport {assertExists, assertTrue} from '../../base/logging';
25*6dbdd20aSAndroid Build Coastguard Workerimport {SliceSelectionAggregator} from './slice_selection_aggregator';
26*6dbdd20aSAndroid Build Coastguard Workerimport ProcessThreadGroupsPlugin from '../dev.perfetto.ProcessThreadGroups';
27*6dbdd20aSAndroid Build Coastguard Worker
28*6dbdd20aSAndroid Build Coastguard Workerexport default class implements PerfettoPlugin {
29*6dbdd20aSAndroid Build Coastguard Worker  static readonly id = 'dev.perfetto.AsyncSlices';
30*6dbdd20aSAndroid Build Coastguard Worker  static readonly dependencies = [ProcessThreadGroupsPlugin];
31*6dbdd20aSAndroid Build Coastguard Worker
32*6dbdd20aSAndroid Build Coastguard Worker  async onTraceLoad(ctx: Trace): Promise<void> {
33*6dbdd20aSAndroid Build Coastguard Worker    const trackIdsToUris = new Map<number, string>();
34*6dbdd20aSAndroid Build Coastguard Worker
35*6dbdd20aSAndroid Build Coastguard Worker    await this.addGlobalAsyncTracks(ctx, trackIdsToUris);
36*6dbdd20aSAndroid Build Coastguard Worker    await this.addProcessAsyncSliceTracks(ctx, trackIdsToUris);
37*6dbdd20aSAndroid Build Coastguard Worker    await this.addThreadAsyncSliceTracks(ctx, trackIdsToUris);
38*6dbdd20aSAndroid Build Coastguard Worker
39*6dbdd20aSAndroid Build Coastguard Worker    ctx.selection.registerSqlSelectionResolver({
40*6dbdd20aSAndroid Build Coastguard Worker      sqlTableName: 'slice',
41*6dbdd20aSAndroid Build Coastguard Worker      callback: async (id: number) => {
42*6dbdd20aSAndroid Build Coastguard Worker        // Locate the track for a given id in the slice table
43*6dbdd20aSAndroid Build Coastguard Worker        const result = await ctx.engine.query(`
44*6dbdd20aSAndroid Build Coastguard Worker          select
45*6dbdd20aSAndroid Build Coastguard Worker            track_id as trackId
46*6dbdd20aSAndroid Build Coastguard Worker          from
47*6dbdd20aSAndroid Build Coastguard Worker            slice
48*6dbdd20aSAndroid Build Coastguard Worker          where slice.id = ${id}
49*6dbdd20aSAndroid Build Coastguard Worker        `);
50*6dbdd20aSAndroid Build Coastguard Worker
51*6dbdd20aSAndroid Build Coastguard Worker        if (result.numRows() === 0) {
52*6dbdd20aSAndroid Build Coastguard Worker          return undefined;
53*6dbdd20aSAndroid Build Coastguard Worker        }
54*6dbdd20aSAndroid Build Coastguard Worker
55*6dbdd20aSAndroid Build Coastguard Worker        const {trackId} = result.firstRow({
56*6dbdd20aSAndroid Build Coastguard Worker          trackId: NUM,
57*6dbdd20aSAndroid Build Coastguard Worker        });
58*6dbdd20aSAndroid Build Coastguard Worker
59*6dbdd20aSAndroid Build Coastguard Worker        const trackUri = trackIdsToUris.get(trackId);
60*6dbdd20aSAndroid Build Coastguard Worker        if (!trackUri) {
61*6dbdd20aSAndroid Build Coastguard Worker          return undefined;
62*6dbdd20aSAndroid Build Coastguard Worker        }
63*6dbdd20aSAndroid Build Coastguard Worker
64*6dbdd20aSAndroid Build Coastguard Worker        return {
65*6dbdd20aSAndroid Build Coastguard Worker          trackUri,
66*6dbdd20aSAndroid Build Coastguard Worker          eventId: id,
67*6dbdd20aSAndroid Build Coastguard Worker        };
68*6dbdd20aSAndroid Build Coastguard Worker      },
69*6dbdd20aSAndroid Build Coastguard Worker    });
70*6dbdd20aSAndroid Build Coastguard Worker
71*6dbdd20aSAndroid Build Coastguard Worker    ctx.selection.registerAreaSelectionAggregator(
72*6dbdd20aSAndroid Build Coastguard Worker      new SliceSelectionAggregator(),
73*6dbdd20aSAndroid Build Coastguard Worker    );
74*6dbdd20aSAndroid Build Coastguard Worker  }
75*6dbdd20aSAndroid Build Coastguard Worker
76*6dbdd20aSAndroid Build Coastguard Worker  async addGlobalAsyncTracks(
77*6dbdd20aSAndroid Build Coastguard Worker    ctx: Trace,
78*6dbdd20aSAndroid Build Coastguard Worker    trackIdsToUris: Map<number, string>,
79*6dbdd20aSAndroid Build Coastguard Worker  ): Promise<void> {
80*6dbdd20aSAndroid Build Coastguard Worker    const {engine} = ctx;
81*6dbdd20aSAndroid Build Coastguard Worker    // TODO(stevegolton): The track exclusion logic is currently a hack. This will be replaced
82*6dbdd20aSAndroid Build Coastguard Worker    // by a mechanism for more specific plugins to override tracks from more generic plugins.
83*6dbdd20aSAndroid Build Coastguard Worker    const suspendResumeLatencyTrackName = 'Suspend/Resume Latency';
84*6dbdd20aSAndroid Build Coastguard Worker    const rawGlobalAsyncTracks = await engine.query(`
85*6dbdd20aSAndroid Build Coastguard Worker      include perfetto module graphs.search;
86*6dbdd20aSAndroid Build Coastguard Worker      include perfetto module viz.summary.tracks;
87*6dbdd20aSAndroid Build Coastguard Worker
88*6dbdd20aSAndroid Build Coastguard Worker      with global_tracks_grouped as (
89*6dbdd20aSAndroid Build Coastguard Worker        select
90*6dbdd20aSAndroid Build Coastguard Worker          t.parent_id,
91*6dbdd20aSAndroid Build Coastguard Worker          t.name,
92*6dbdd20aSAndroid Build Coastguard Worker          group_concat(t.id) as trackIds,
93*6dbdd20aSAndroid Build Coastguard Worker          count() as trackCount,
94*6dbdd20aSAndroid Build Coastguard Worker          ifnull(min(a.order_id), 0) as order_id
95*6dbdd20aSAndroid Build Coastguard Worker        from track t
96*6dbdd20aSAndroid Build Coastguard Worker        join _slice_track_summary using (id)
97*6dbdd20aSAndroid Build Coastguard Worker        left join _track_event_tracks_ordered a USING (id)
98*6dbdd20aSAndroid Build Coastguard Worker        where
99*6dbdd20aSAndroid Build Coastguard Worker          t.type in ('__intrinsic_track', 'gpu_track', '__intrinsic_cpu_track')
100*6dbdd20aSAndroid Build Coastguard Worker          and (name != '${suspendResumeLatencyTrackName}' or name is null)
101*6dbdd20aSAndroid Build Coastguard Worker          and classification not in (
102*6dbdd20aSAndroid Build Coastguard Worker            'linux_rpm',
103*6dbdd20aSAndroid Build Coastguard Worker            'linux_device_frequency',
104*6dbdd20aSAndroid Build Coastguard Worker            'irq_counter',
105*6dbdd20aSAndroid Build Coastguard Worker            'softirq_counter',
106*6dbdd20aSAndroid Build Coastguard Worker            'android_energy_estimation_breakdown',
107*6dbdd20aSAndroid Build Coastguard Worker            'android_energy_estimation_breakdown_per_uid'
108*6dbdd20aSAndroid Build Coastguard Worker          )
109*6dbdd20aSAndroid Build Coastguard Worker        group by parent_id, name
110*6dbdd20aSAndroid Build Coastguard Worker        order by parent_id, order_id
111*6dbdd20aSAndroid Build Coastguard Worker      ),
112*6dbdd20aSAndroid Build Coastguard Worker      intermediate_groups as (
113*6dbdd20aSAndroid Build Coastguard Worker        select
114*6dbdd20aSAndroid Build Coastguard Worker          t.name,
115*6dbdd20aSAndroid Build Coastguard Worker          t.id,
116*6dbdd20aSAndroid Build Coastguard Worker          t.parent_id,
117*6dbdd20aSAndroid Build Coastguard Worker          ifnull(a.order_id, 0) as order_id
118*6dbdd20aSAndroid Build Coastguard Worker        from graph_reachable_dfs!(
119*6dbdd20aSAndroid Build Coastguard Worker          (
120*6dbdd20aSAndroid Build Coastguard Worker            select id as source_node_id, parent_id as dest_node_id
121*6dbdd20aSAndroid Build Coastguard Worker            from track
122*6dbdd20aSAndroid Build Coastguard Worker            where parent_id is not null
123*6dbdd20aSAndroid Build Coastguard Worker          ),
124*6dbdd20aSAndroid Build Coastguard Worker          (
125*6dbdd20aSAndroid Build Coastguard Worker            select distinct parent_id as node_id
126*6dbdd20aSAndroid Build Coastguard Worker            from global_tracks_grouped
127*6dbdd20aSAndroid Build Coastguard Worker            where parent_id is not null
128*6dbdd20aSAndroid Build Coastguard Worker          )
129*6dbdd20aSAndroid Build Coastguard Worker        ) g
130*6dbdd20aSAndroid Build Coastguard Worker        join track t on g.node_id = t.id
131*6dbdd20aSAndroid Build Coastguard Worker        left join _track_event_tracks_ordered a USING (id)
132*6dbdd20aSAndroid Build Coastguard Worker      )
133*6dbdd20aSAndroid Build Coastguard Worker      select
134*6dbdd20aSAndroid Build Coastguard Worker        t.name as name,
135*6dbdd20aSAndroid Build Coastguard Worker        t.parent_id as parentId,
136*6dbdd20aSAndroid Build Coastguard Worker        t.trackIds as trackIds,
137*6dbdd20aSAndroid Build Coastguard Worker        t.order_id as orderId,
138*6dbdd20aSAndroid Build Coastguard Worker        __max_layout_depth(t.trackCount, t.trackIds) as maxDepth
139*6dbdd20aSAndroid Build Coastguard Worker      from global_tracks_grouped t
140*6dbdd20aSAndroid Build Coastguard Worker      union all
141*6dbdd20aSAndroid Build Coastguard Worker      select
142*6dbdd20aSAndroid Build Coastguard Worker        t.name as name,
143*6dbdd20aSAndroid Build Coastguard Worker        t.parent_id as parentId,
144*6dbdd20aSAndroid Build Coastguard Worker        cast_string!(t.id) as trackIds,
145*6dbdd20aSAndroid Build Coastguard Worker        t.order_id as orderId,
146*6dbdd20aSAndroid Build Coastguard Worker        NULL as maxDepth
147*6dbdd20aSAndroid Build Coastguard Worker      from intermediate_groups t
148*6dbdd20aSAndroid Build Coastguard Worker      left join _slice_track_summary s using (id)
149*6dbdd20aSAndroid Build Coastguard Worker      where s.id is null
150*6dbdd20aSAndroid Build Coastguard Worker      order by parentId, orderId
151*6dbdd20aSAndroid Build Coastguard Worker    `);
152*6dbdd20aSAndroid Build Coastguard Worker    const it = rawGlobalAsyncTracks.iter({
153*6dbdd20aSAndroid Build Coastguard Worker      name: STR_NULL,
154*6dbdd20aSAndroid Build Coastguard Worker      parentId: NUM_NULL,
155*6dbdd20aSAndroid Build Coastguard Worker      trackIds: STR,
156*6dbdd20aSAndroid Build Coastguard Worker      orderId: NUM,
157*6dbdd20aSAndroid Build Coastguard Worker      maxDepth: NUM_NULL,
158*6dbdd20aSAndroid Build Coastguard Worker    });
159*6dbdd20aSAndroid Build Coastguard Worker
160*6dbdd20aSAndroid Build Coastguard Worker    // Create a map of track nodes by id
161*6dbdd20aSAndroid Build Coastguard Worker    const trackMap = new Map<
162*6dbdd20aSAndroid Build Coastguard Worker      number,
163*6dbdd20aSAndroid Build Coastguard Worker      {parentId: number | null; trackNode: TrackNode}
164*6dbdd20aSAndroid Build Coastguard Worker    >();
165*6dbdd20aSAndroid Build Coastguard Worker
166*6dbdd20aSAndroid Build Coastguard Worker    for (; it.valid(); it.next()) {
167*6dbdd20aSAndroid Build Coastguard Worker      const rawName = it.name === null ? undefined : it.name;
168*6dbdd20aSAndroid Build Coastguard Worker      const title = getTrackName({
169*6dbdd20aSAndroid Build Coastguard Worker        name: rawName,
170*6dbdd20aSAndroid Build Coastguard Worker        kind: SLICE_TRACK_KIND,
171*6dbdd20aSAndroid Build Coastguard Worker      });
172*6dbdd20aSAndroid Build Coastguard Worker      const rawTrackIds = it.trackIds;
173*6dbdd20aSAndroid Build Coastguard Worker      const trackIds = rawTrackIds.split(',').map((v) => Number(v));
174*6dbdd20aSAndroid Build Coastguard Worker      const maxDepth = it.maxDepth;
175*6dbdd20aSAndroid Build Coastguard Worker
176*6dbdd20aSAndroid Build Coastguard Worker      if (maxDepth === null) {
177*6dbdd20aSAndroid Build Coastguard Worker        assertTrue(trackIds.length == 1);
178*6dbdd20aSAndroid Build Coastguard Worker        const trackNode = new TrackNode({title, sortOrder: -25});
179*6dbdd20aSAndroid Build Coastguard Worker        trackMap.set(trackIds[0], {parentId: it.parentId, trackNode});
180*6dbdd20aSAndroid Build Coastguard Worker      } else {
181*6dbdd20aSAndroid Build Coastguard Worker        const uri = `/async_slices_${rawName}_${it.parentId}`;
182*6dbdd20aSAndroid Build Coastguard Worker        ctx.tracks.registerTrack({
183*6dbdd20aSAndroid Build Coastguard Worker          uri,
184*6dbdd20aSAndroid Build Coastguard Worker          title,
185*6dbdd20aSAndroid Build Coastguard Worker          tags: {
186*6dbdd20aSAndroid Build Coastguard Worker            trackIds,
187*6dbdd20aSAndroid Build Coastguard Worker            kind: SLICE_TRACK_KIND,
188*6dbdd20aSAndroid Build Coastguard Worker            scope: 'global',
189*6dbdd20aSAndroid Build Coastguard Worker          },
190*6dbdd20aSAndroid Build Coastguard Worker          track: new AsyncSliceTrack(ctx, uri, maxDepth, trackIds),
191*6dbdd20aSAndroid Build Coastguard Worker        });
192*6dbdd20aSAndroid Build Coastguard Worker        const trackNode = new TrackNode({
193*6dbdd20aSAndroid Build Coastguard Worker          uri,
194*6dbdd20aSAndroid Build Coastguard Worker          title,
195*6dbdd20aSAndroid Build Coastguard Worker          sortOrder: it.orderId,
196*6dbdd20aSAndroid Build Coastguard Worker        });
197*6dbdd20aSAndroid Build Coastguard Worker        trackIds.forEach((id) => {
198*6dbdd20aSAndroid Build Coastguard Worker          trackMap.set(id, {parentId: it.parentId, trackNode});
199*6dbdd20aSAndroid Build Coastguard Worker          trackIdsToUris.set(id, uri);
200*6dbdd20aSAndroid Build Coastguard Worker        });
201*6dbdd20aSAndroid Build Coastguard Worker      }
202*6dbdd20aSAndroid Build Coastguard Worker    }
203*6dbdd20aSAndroid Build Coastguard Worker
204*6dbdd20aSAndroid Build Coastguard Worker    // Attach track nodes to parents / or the workspace if they have no parent
205*6dbdd20aSAndroid Build Coastguard Worker    trackMap.forEach(({parentId, trackNode}) => {
206*6dbdd20aSAndroid Build Coastguard Worker      if (exists(parentId)) {
207*6dbdd20aSAndroid Build Coastguard Worker        const parent = assertExists(trackMap.get(parentId));
208*6dbdd20aSAndroid Build Coastguard Worker        parent.trackNode.addChildInOrder(trackNode);
209*6dbdd20aSAndroid Build Coastguard Worker      } else {
210*6dbdd20aSAndroid Build Coastguard Worker        ctx.workspace.addChildInOrder(trackNode);
211*6dbdd20aSAndroid Build Coastguard Worker      }
212*6dbdd20aSAndroid Build Coastguard Worker    });
213*6dbdd20aSAndroid Build Coastguard Worker  }
214*6dbdd20aSAndroid Build Coastguard Worker
215*6dbdd20aSAndroid Build Coastguard Worker  async addProcessAsyncSliceTracks(
216*6dbdd20aSAndroid Build Coastguard Worker    ctx: Trace,
217*6dbdd20aSAndroid Build Coastguard Worker    trackIdsToUris: Map<number, string>,
218*6dbdd20aSAndroid Build Coastguard Worker  ): Promise<void> {
219*6dbdd20aSAndroid Build Coastguard Worker    const result = await ctx.engine.query(`
220*6dbdd20aSAndroid Build Coastguard Worker      select
221*6dbdd20aSAndroid Build Coastguard Worker        upid,
222*6dbdd20aSAndroid Build Coastguard Worker        t.name as trackName,
223*6dbdd20aSAndroid Build Coastguard Worker        t.track_ids as trackIds,
224*6dbdd20aSAndroid Build Coastguard Worker        process.name as processName,
225*6dbdd20aSAndroid Build Coastguard Worker        process.pid as pid,
226*6dbdd20aSAndroid Build Coastguard Worker        t.parent_id as parentId,
227*6dbdd20aSAndroid Build Coastguard Worker        __max_layout_depth(t.track_count, t.track_ids) as maxDepth
228*6dbdd20aSAndroid Build Coastguard Worker      from _process_track_summary_by_upid_and_parent_id_and_name t
229*6dbdd20aSAndroid Build Coastguard Worker      join process using (upid)
230*6dbdd20aSAndroid Build Coastguard Worker      where t.name is null or t.name not glob "* Timeline"
231*6dbdd20aSAndroid Build Coastguard Worker    `);
232*6dbdd20aSAndroid Build Coastguard Worker
233*6dbdd20aSAndroid Build Coastguard Worker    const it = result.iter({
234*6dbdd20aSAndroid Build Coastguard Worker      upid: NUM,
235*6dbdd20aSAndroid Build Coastguard Worker      parentId: NUM_NULL,
236*6dbdd20aSAndroid Build Coastguard Worker      trackName: STR_NULL,
237*6dbdd20aSAndroid Build Coastguard Worker      trackIds: STR,
238*6dbdd20aSAndroid Build Coastguard Worker      processName: STR_NULL,
239*6dbdd20aSAndroid Build Coastguard Worker      pid: NUM_NULL,
240*6dbdd20aSAndroid Build Coastguard Worker      maxDepth: NUM,
241*6dbdd20aSAndroid Build Coastguard Worker    });
242*6dbdd20aSAndroid Build Coastguard Worker
243*6dbdd20aSAndroid Build Coastguard Worker    const trackMap = new Map<
244*6dbdd20aSAndroid Build Coastguard Worker      number,
245*6dbdd20aSAndroid Build Coastguard Worker      {parentId: number | null; upid: number; trackNode: TrackNode}
246*6dbdd20aSAndroid Build Coastguard Worker    >();
247*6dbdd20aSAndroid Build Coastguard Worker
248*6dbdd20aSAndroid Build Coastguard Worker    for (; it.valid(); it.next()) {
249*6dbdd20aSAndroid Build Coastguard Worker      const upid = it.upid;
250*6dbdd20aSAndroid Build Coastguard Worker      const trackName = it.trackName;
251*6dbdd20aSAndroid Build Coastguard Worker      const rawTrackIds = it.trackIds;
252*6dbdd20aSAndroid Build Coastguard Worker      const trackIds = rawTrackIds.split(',').map((v) => Number(v));
253*6dbdd20aSAndroid Build Coastguard Worker      const processName = it.processName;
254*6dbdd20aSAndroid Build Coastguard Worker      const pid = it.pid;
255*6dbdd20aSAndroid Build Coastguard Worker      const maxDepth = it.maxDepth;
256*6dbdd20aSAndroid Build Coastguard Worker
257*6dbdd20aSAndroid Build Coastguard Worker      const kind = SLICE_TRACK_KIND;
258*6dbdd20aSAndroid Build Coastguard Worker      const title = getTrackName({
259*6dbdd20aSAndroid Build Coastguard Worker        name: trackName,
260*6dbdd20aSAndroid Build Coastguard Worker        upid,
261*6dbdd20aSAndroid Build Coastguard Worker        pid,
262*6dbdd20aSAndroid Build Coastguard Worker        processName,
263*6dbdd20aSAndroid Build Coastguard Worker        kind,
264*6dbdd20aSAndroid Build Coastguard Worker      });
265*6dbdd20aSAndroid Build Coastguard Worker
266*6dbdd20aSAndroid Build Coastguard Worker      const uri = `/process_${upid}/async_slices_${rawTrackIds}`;
267*6dbdd20aSAndroid Build Coastguard Worker      ctx.tracks.registerTrack({
268*6dbdd20aSAndroid Build Coastguard Worker        uri,
269*6dbdd20aSAndroid Build Coastguard Worker        title,
270*6dbdd20aSAndroid Build Coastguard Worker        tags: {
271*6dbdd20aSAndroid Build Coastguard Worker          trackIds,
272*6dbdd20aSAndroid Build Coastguard Worker          kind: SLICE_TRACK_KIND,
273*6dbdd20aSAndroid Build Coastguard Worker          scope: 'process',
274*6dbdd20aSAndroid Build Coastguard Worker          upid,
275*6dbdd20aSAndroid Build Coastguard Worker        },
276*6dbdd20aSAndroid Build Coastguard Worker        track: new AsyncSliceTrack(ctx, uri, maxDepth, trackIds),
277*6dbdd20aSAndroid Build Coastguard Worker      });
278*6dbdd20aSAndroid Build Coastguard Worker      const track = new TrackNode({uri, title, sortOrder: 30});
279*6dbdd20aSAndroid Build Coastguard Worker      trackIds.forEach((id) => {
280*6dbdd20aSAndroid Build Coastguard Worker        trackMap.set(id, {trackNode: track, parentId: it.parentId, upid});
281*6dbdd20aSAndroid Build Coastguard Worker        trackIdsToUris.set(id, uri);
282*6dbdd20aSAndroid Build Coastguard Worker      });
283*6dbdd20aSAndroid Build Coastguard Worker    }
284*6dbdd20aSAndroid Build Coastguard Worker
285*6dbdd20aSAndroid Build Coastguard Worker    // Attach track nodes to parents / or the workspace if they have no parent
286*6dbdd20aSAndroid Build Coastguard Worker    trackMap.forEach((t) => {
287*6dbdd20aSAndroid Build Coastguard Worker      const parent = exists(t.parentId) && trackMap.get(t.parentId);
288*6dbdd20aSAndroid Build Coastguard Worker      if (parent !== false && parent !== undefined) {
289*6dbdd20aSAndroid Build Coastguard Worker        parent.trackNode.addChildInOrder(t.trackNode);
290*6dbdd20aSAndroid Build Coastguard Worker      } else {
291*6dbdd20aSAndroid Build Coastguard Worker        const processGroup = ctx.plugins
292*6dbdd20aSAndroid Build Coastguard Worker          .getPlugin(ProcessThreadGroupsPlugin)
293*6dbdd20aSAndroid Build Coastguard Worker          .getGroupForProcess(t.upid);
294*6dbdd20aSAndroid Build Coastguard Worker        processGroup?.addChildInOrder(t.trackNode);
295*6dbdd20aSAndroid Build Coastguard Worker      }
296*6dbdd20aSAndroid Build Coastguard Worker    });
297*6dbdd20aSAndroid Build Coastguard Worker  }
298*6dbdd20aSAndroid Build Coastguard Worker
299*6dbdd20aSAndroid Build Coastguard Worker  async addThreadAsyncSliceTracks(
300*6dbdd20aSAndroid Build Coastguard Worker    ctx: Trace,
301*6dbdd20aSAndroid Build Coastguard Worker    trackIdsToUris: Map<number, string>,
302*6dbdd20aSAndroid Build Coastguard Worker  ): Promise<void> {
303*6dbdd20aSAndroid Build Coastguard Worker    const result = await ctx.engine.query(`
304*6dbdd20aSAndroid Build Coastguard Worker      include perfetto module viz.summary.slices;
305*6dbdd20aSAndroid Build Coastguard Worker      include perfetto module viz.summary.threads;
306*6dbdd20aSAndroid Build Coastguard Worker      include perfetto module viz.threads;
307*6dbdd20aSAndroid Build Coastguard Worker
308*6dbdd20aSAndroid Build Coastguard Worker      select
309*6dbdd20aSAndroid Build Coastguard Worker        t.utid,
310*6dbdd20aSAndroid Build Coastguard Worker        t.parent_id as parentId,
311*6dbdd20aSAndroid Build Coastguard Worker        thread.upid,
312*6dbdd20aSAndroid Build Coastguard Worker        t.name as trackName,
313*6dbdd20aSAndroid Build Coastguard Worker        thread.name as threadName,
314*6dbdd20aSAndroid Build Coastguard Worker        thread.tid as tid,
315*6dbdd20aSAndroid Build Coastguard Worker        t.track_ids as trackIds,
316*6dbdd20aSAndroid Build Coastguard Worker        __max_layout_depth(t.track_count, t.track_ids) as maxDepth,
317*6dbdd20aSAndroid Build Coastguard Worker        k.is_main_thread as isMainThread,
318*6dbdd20aSAndroid Build Coastguard Worker        k.is_kernel_thread AS isKernelThread
319*6dbdd20aSAndroid Build Coastguard Worker      from _thread_track_summary_by_utid_and_name t
320*6dbdd20aSAndroid Build Coastguard Worker      join _threads_with_kernel_flag k using(utid)
321*6dbdd20aSAndroid Build Coastguard Worker      join thread using (utid)
322*6dbdd20aSAndroid Build Coastguard Worker    `);
323*6dbdd20aSAndroid Build Coastguard Worker
324*6dbdd20aSAndroid Build Coastguard Worker    const it = result.iter({
325*6dbdd20aSAndroid Build Coastguard Worker      utid: NUM,
326*6dbdd20aSAndroid Build Coastguard Worker      parentId: NUM_NULL,
327*6dbdd20aSAndroid Build Coastguard Worker      upid: NUM_NULL,
328*6dbdd20aSAndroid Build Coastguard Worker      trackName: STR_NULL,
329*6dbdd20aSAndroid Build Coastguard Worker      trackIds: STR,
330*6dbdd20aSAndroid Build Coastguard Worker      maxDepth: NUM,
331*6dbdd20aSAndroid Build Coastguard Worker      isMainThread: NUM_NULL,
332*6dbdd20aSAndroid Build Coastguard Worker      isKernelThread: NUM,
333*6dbdd20aSAndroid Build Coastguard Worker      threadName: STR_NULL,
334*6dbdd20aSAndroid Build Coastguard Worker      tid: NUM_NULL,
335*6dbdd20aSAndroid Build Coastguard Worker    });
336*6dbdd20aSAndroid Build Coastguard Worker
337*6dbdd20aSAndroid Build Coastguard Worker    const trackMap = new Map<
338*6dbdd20aSAndroid Build Coastguard Worker      number,
339*6dbdd20aSAndroid Build Coastguard Worker      {parentId: number | null; utid: number; trackNode: TrackNode}
340*6dbdd20aSAndroid Build Coastguard Worker    >();
341*6dbdd20aSAndroid Build Coastguard Worker
342*6dbdd20aSAndroid Build Coastguard Worker    for (; it.valid(); it.next()) {
343*6dbdd20aSAndroid Build Coastguard Worker      const {
344*6dbdd20aSAndroid Build Coastguard Worker        utid,
345*6dbdd20aSAndroid Build Coastguard Worker        parentId,
346*6dbdd20aSAndroid Build Coastguard Worker        upid,
347*6dbdd20aSAndroid Build Coastguard Worker        trackName,
348*6dbdd20aSAndroid Build Coastguard Worker        isMainThread,
349*6dbdd20aSAndroid Build Coastguard Worker        isKernelThread,
350*6dbdd20aSAndroid Build Coastguard Worker        maxDepth,
351*6dbdd20aSAndroid Build Coastguard Worker        threadName,
352*6dbdd20aSAndroid Build Coastguard Worker        tid,
353*6dbdd20aSAndroid Build Coastguard Worker      } = it;
354*6dbdd20aSAndroid Build Coastguard Worker      const rawTrackIds = it.trackIds;
355*6dbdd20aSAndroid Build Coastguard Worker      const trackIds = rawTrackIds.split(',').map((v) => Number(v));
356*6dbdd20aSAndroid Build Coastguard Worker      const title = getTrackName({
357*6dbdd20aSAndroid Build Coastguard Worker        name: trackName,
358*6dbdd20aSAndroid Build Coastguard Worker        utid,
359*6dbdd20aSAndroid Build Coastguard Worker        tid,
360*6dbdd20aSAndroid Build Coastguard Worker        threadName,
361*6dbdd20aSAndroid Build Coastguard Worker        kind: 'Slices',
362*6dbdd20aSAndroid Build Coastguard Worker      });
363*6dbdd20aSAndroid Build Coastguard Worker
364*6dbdd20aSAndroid Build Coastguard Worker      const uri = `/${getThreadUriPrefix(upid, utid)}_slice_${rawTrackIds}`;
365*6dbdd20aSAndroid Build Coastguard Worker      ctx.tracks.registerTrack({
366*6dbdd20aSAndroid Build Coastguard Worker        uri,
367*6dbdd20aSAndroid Build Coastguard Worker        title,
368*6dbdd20aSAndroid Build Coastguard Worker        tags: {
369*6dbdd20aSAndroid Build Coastguard Worker          trackIds,
370*6dbdd20aSAndroid Build Coastguard Worker          kind: SLICE_TRACK_KIND,
371*6dbdd20aSAndroid Build Coastguard Worker          scope: 'thread',
372*6dbdd20aSAndroid Build Coastguard Worker          utid,
373*6dbdd20aSAndroid Build Coastguard Worker          upid: upid ?? undefined,
374*6dbdd20aSAndroid Build Coastguard Worker          ...(isKernelThread === 1 && {kernelThread: true}),
375*6dbdd20aSAndroid Build Coastguard Worker        },
376*6dbdd20aSAndroid Build Coastguard Worker        chips: removeFalsyValues([
377*6dbdd20aSAndroid Build Coastguard Worker          isKernelThread === 0 && isMainThread === 1 && 'main thread',
378*6dbdd20aSAndroid Build Coastguard Worker        ]),
379*6dbdd20aSAndroid Build Coastguard Worker        track: new AsyncSliceTrack(ctx, uri, maxDepth, trackIds),
380*6dbdd20aSAndroid Build Coastguard Worker      });
381*6dbdd20aSAndroid Build Coastguard Worker      const track = new TrackNode({uri, title, sortOrder: 20});
382*6dbdd20aSAndroid Build Coastguard Worker      trackIds.forEach((id) => {
383*6dbdd20aSAndroid Build Coastguard Worker        trackMap.set(id, {trackNode: track, parentId, utid});
384*6dbdd20aSAndroid Build Coastguard Worker        trackIdsToUris.set(id, uri);
385*6dbdd20aSAndroid Build Coastguard Worker      });
386*6dbdd20aSAndroid Build Coastguard Worker    }
387*6dbdd20aSAndroid Build Coastguard Worker
388*6dbdd20aSAndroid Build Coastguard Worker    // Attach track nodes to parents / or the workspace if they have no parent
389*6dbdd20aSAndroid Build Coastguard Worker    trackMap.forEach((t) => {
390*6dbdd20aSAndroid Build Coastguard Worker      const parent = exists(t.parentId) && trackMap.get(t.parentId);
391*6dbdd20aSAndroid Build Coastguard Worker      if (parent !== false && parent !== undefined) {
392*6dbdd20aSAndroid Build Coastguard Worker        parent.trackNode.addChildInOrder(t.trackNode);
393*6dbdd20aSAndroid Build Coastguard Worker      } else {
394*6dbdd20aSAndroid Build Coastguard Worker        const group = ctx.plugins
395*6dbdd20aSAndroid Build Coastguard Worker          .getPlugin(ProcessThreadGroupsPlugin)
396*6dbdd20aSAndroid Build Coastguard Worker          .getGroupForThread(t.utid);
397*6dbdd20aSAndroid Build Coastguard Worker        group?.addChildInOrder(t.trackNode);
398*6dbdd20aSAndroid Build Coastguard Worker      }
399*6dbdd20aSAndroid Build Coastguard Worker    });
400*6dbdd20aSAndroid Build Coastguard Worker  }
401*6dbdd20aSAndroid Build Coastguard Worker}
402