xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.AsyncSlices/async_slice_track.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2023 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 {BigintMath as BIMath} from '../../base/bigint_math';
16import {clamp} from '../../base/math_utils';
17import {
18  NAMED_ROW,
19  NamedSliceTrack,
20} from '../../components/tracks/named_slice_track';
21import {SLICE_LAYOUT_FIT_CONTENT_DEFAULTS} from '../../components/tracks/slice_layout';
22import {TrackEventDetails} from '../../public/selection';
23import {Trace} from '../../public/trace';
24import {Slice} from '../../public/track';
25import {SourceDataset, Dataset} from '../../trace_processor/dataset';
26import {
27  LONG,
28  LONG_NULL,
29  NUM,
30  NUM_NULL,
31  STR,
32} from '../../trace_processor/query_result';
33
34export const THREAD_SLICE_ROW = {
35  // Base columns (tsq, ts, dur, id, depth).
36  ...NAMED_ROW,
37
38  // Thread-specific columns.
39  threadDur: LONG_NULL,
40};
41export type ThreadSliceRow = typeof THREAD_SLICE_ROW;
42
43export class AsyncSliceTrack extends NamedSliceTrack<Slice, ThreadSliceRow> {
44  constructor(
45    trace: Trace,
46    uri: string,
47    maxDepth: number,
48    private readonly trackIds: number[],
49  ) {
50    super(trace, uri);
51    this.sliceLayout = {
52      ...SLICE_LAYOUT_FIT_CONTENT_DEFAULTS,
53      depthGuess: maxDepth,
54    };
55  }
56
57  getRowSpec(): ThreadSliceRow {
58    return THREAD_SLICE_ROW;
59  }
60
61  rowToSlice(row: ThreadSliceRow): Slice {
62    const namedSlice = this.rowToSliceBase(row);
63
64    if (row.dur > 0n && row.threadDur !== null) {
65      const fillRatio = clamp(BIMath.ratio(row.threadDur, row.dur), 0, 1);
66      return {...namedSlice, fillRatio};
67    } else {
68      return namedSlice;
69    }
70  }
71
72  getSqlSource(): string {
73    // If we only have one track ID we can avoid the overhead of
74    // experimental_slice_layout, and just go straight to the slice table.
75    if (this.trackIds.length === 1) {
76      return `
77        select
78          ts,
79          dur,
80          id,
81          depth,
82          ifnull(name, '[null]') as name,
83          thread_dur as threadDur
84        from slice
85        where track_id = ${this.trackIds[0]}
86      `;
87    } else {
88      return `
89        select
90          id,
91          ts,
92          dur,
93          layout_depth as depth,
94          ifnull(name, '[null]') as name,
95          thread_dur as threadDur
96        from experimental_slice_layout
97        where filter_track_ids = '${this.trackIds.join(',')}'
98      `;
99    }
100  }
101
102  onUpdatedSlices(slices: Slice[]) {
103    for (const slice of slices) {
104      slice.isHighlighted = slice === this.hoveredSlice;
105    }
106  }
107
108  async getSelectionDetails(
109    id: number,
110  ): Promise<TrackEventDetails | undefined> {
111    const baseDetails = await super.getSelectionDetails(id);
112    if (!baseDetails) return undefined;
113    return {
114      ...baseDetails,
115      tableName: 'slice',
116    };
117  }
118
119  override getDataset(): Dataset {
120    return new SourceDataset({
121      src: `slice`,
122      filter: {
123        col: 'track_id',
124        in: this.trackIds,
125      },
126      schema: {
127        id: NUM,
128        name: STR,
129        ts: LONG,
130        dur: LONG,
131        parent_id: NUM_NULL,
132      },
133    });
134  }
135}
136