xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.Frames/index.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2021 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {
16  ACTUAL_FRAMES_SLICE_TRACK_KIND,
17  EXPECTED_FRAMES_SLICE_TRACK_KIND,
18} from '../../public/track_kinds';
19import {Trace} from '../../public/trace';
20import {PerfettoPlugin} from '../../public/plugin';
21import {getTrackName} from '../../public/utils';
22import {TrackNode} from '../../public/workspace';
23import {NUM, NUM_NULL, STR, STR_NULL} from '../../trace_processor/query_result';
24import {ActualFramesTrack} from './actual_frames_track';
25import {ExpectedFramesTrack} from './expected_frames_track';
26import {FrameSelectionAggregator} from './frame_selection_aggregator';
27import ProcessThreadGroupsPlugin from '../dev.perfetto.ProcessThreadGroups';
28
29export default class implements PerfettoPlugin {
30  static readonly id = 'dev.perfetto.Frames';
31  static readonly dependencies = [ProcessThreadGroupsPlugin];
32
33  async onTraceLoad(ctx: Trace): Promise<void> {
34    this.addExpectedFrames(ctx);
35    this.addActualFrames(ctx);
36    ctx.selection.registerAreaSelectionAggregator(
37      new FrameSelectionAggregator(),
38    );
39  }
40
41  async addExpectedFrames(ctx: Trace): Promise<void> {
42    const {engine} = ctx;
43    const result = await engine.query(`
44      select
45        upid,
46        t.name as trackName,
47        t.track_ids as trackIds,
48        process.name as processName,
49        process.pid as pid,
50        __max_layout_depth(t.track_count, t.track_ids) as maxDepth
51      from _process_track_summary_by_upid_and_parent_id_and_name t
52      join process using(upid)
53      where t.name = "Expected Timeline"
54    `);
55
56    const it = result.iter({
57      upid: NUM,
58      trackName: STR_NULL,
59      trackIds: STR,
60      processName: STR_NULL,
61      pid: NUM_NULL,
62      maxDepth: NUM,
63    });
64
65    for (; it.valid(); it.next()) {
66      const upid = it.upid;
67      const trackName = it.trackName;
68      const rawTrackIds = it.trackIds;
69      const trackIds = rawTrackIds.split(',').map((v) => Number(v));
70      const processName = it.processName;
71      const pid = it.pid;
72      const maxDepth = it.maxDepth;
73
74      const title = getTrackName({
75        name: trackName,
76        upid,
77        pid,
78        processName,
79        kind: 'ExpectedFrames',
80      });
81
82      const uri = `/process_${upid}/expected_frames`;
83      ctx.tracks.registerTrack({
84        uri,
85        title,
86        track: new ExpectedFramesTrack(ctx, maxDepth, uri, trackIds),
87        tags: {
88          trackIds,
89          upid,
90          kind: EXPECTED_FRAMES_SLICE_TRACK_KIND,
91        },
92      });
93      const group = ctx.plugins
94        .getPlugin(ProcessThreadGroupsPlugin)
95        .getGroupForProcess(upid);
96      const track = new TrackNode({uri, title, sortOrder: -50});
97      group?.addChildInOrder(track);
98    }
99  }
100
101  async addActualFrames(ctx: Trace): Promise<void> {
102    const {engine} = ctx;
103    const result = await engine.query(`
104      select
105        upid,
106        t.name as trackName,
107        t.track_ids as trackIds,
108        process.name as processName,
109        process.pid as pid,
110        __max_layout_depth(t.track_count, t.track_ids) as maxDepth
111      from _process_track_summary_by_upid_and_parent_id_and_name t
112      join process using(upid)
113      where t.name = "Actual Timeline"
114    `);
115
116    const it = result.iter({
117      upid: NUM,
118      trackName: STR_NULL,
119      trackIds: STR,
120      processName: STR_NULL,
121      pid: NUM_NULL,
122      maxDepth: NUM_NULL,
123    });
124    for (; it.valid(); it.next()) {
125      const upid = it.upid;
126      const trackName = it.trackName;
127      const rawTrackIds = it.trackIds;
128      const trackIds = rawTrackIds.split(',').map((v) => Number(v));
129      const processName = it.processName;
130      const pid = it.pid;
131      const maxDepth = it.maxDepth;
132
133      if (maxDepth === null) {
134        // If there are no slices in this track, skip it.
135        continue;
136      }
137
138      const kind = 'ActualFrames';
139      const title = getTrackName({
140        name: trackName,
141        upid,
142        pid,
143        processName,
144        kind,
145      });
146
147      const uri = `/process_${upid}/actual_frames`;
148      ctx.tracks.registerTrack({
149        uri,
150        title,
151        track: new ActualFramesTrack(ctx, maxDepth, uri, trackIds),
152        tags: {
153          upid,
154          trackIds,
155          kind: ACTUAL_FRAMES_SLICE_TRACK_KIND,
156        },
157      });
158      const group = ctx.plugins
159        .getPlugin(ProcessThreadGroupsPlugin)
160        .getGroupForProcess(upid);
161      const track = new TrackNode({uri, title, sortOrder: -50});
162      group?.addChildInOrder(track);
163    }
164  }
165}
166