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