xref: /aosp_15_r20/external/perfetto/ui/src/components/tracks/query_counter_track.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2024 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 {createPerfettoTable} from '../../trace_processor/sql_utils';
16import {Trace} from '../../public/trace';
17import {sqlNameSafe} from '../../base/string_utils';
18import {BaseCounterTrack, CounterOptions} from './base_counter_track';
19import {Engine} from '../../trace_processor/engine';
20
21export interface QueryCounterTrackArgs {
22  // The trace object used to run queries.
23  readonly trace: Trace;
24
25  // A unique, reproducible ID for this track.
26  readonly uri: string;
27
28  // The query and optional column remapping.
29  readonly data: SqlDataSource;
30
31  // Optional: Which columns should be used for ts, and value. If omitted,
32  // the defaults 'ts', and 'value' will be used.
33  readonly columns?: Partial<CounterColumnMapping>;
34
35  // Optional: Display options for the counter track.
36  readonly options?: Partial<CounterOptions>;
37}
38
39export interface SqlDataSource {
40  // SQL source selecting the necessary data.
41  readonly sqlSource: string;
42
43  // Optional: Rename columns from the query result.
44  // If omitted, original column names from the query are used instead.
45  // The caller is responsible for ensuring that the number of items in this
46  // list matches the number of columns returned by sqlSource.
47  readonly columns?: string[];
48}
49
50export interface CounterColumnMapping {
51  readonly ts: string;
52  readonly value: string;
53}
54
55/**
56 * Creates a counter track based on a query.
57 *
58 * The query must provide the following columns:
59 * - ts: INTEGER - The timestamp of each sample.
60 * - value: REAL | INTEGER - The value of each sample.
61 *
62 * The column names don't have to be 'ts' and 'value', and can be remapped if
63 * convenient using the config.columns parameter.
64 */
65export async function createQueryCounterTrack(args: QueryCounterTrackArgs) {
66  const tableName = `__query_counter_track_${sqlNameSafe(args.uri)}`;
67  await createPerfettoTableForTrack(
68    args.trace.engine,
69    tableName,
70    args.data,
71    args.columns,
72  );
73  return new SqlTableCounterTrack(
74    args.trace,
75    args.uri,
76    tableName,
77    args.options,
78  );
79}
80
81async function createPerfettoTableForTrack(
82  engine: Engine,
83  tableName: string,
84  data: SqlDataSource,
85  columnMapping: Partial<CounterColumnMapping> = {},
86) {
87  const {ts = 'ts', value = 'value'} = columnMapping;
88  const query = `
89    with data as (
90      ${data.sqlSource}
91    )
92    select
93      ${ts} as ts,
94      ${value} as value
95    from data
96    order by ts
97  `;
98
99  return await createPerfettoTable(engine, tableName, query);
100}
101
102class SqlTableCounterTrack extends BaseCounterTrack {
103  constructor(
104    trace: Trace,
105    uri: string,
106    private readonly sqlTableName: string,
107    options?: Partial<CounterOptions>,
108  ) {
109    super(trace, uri, options);
110  }
111
112  getSqlSource(): string {
113    return `select * from ${this.sqlTableName}`;
114  }
115}
116