1// Copyright (C) 2020 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 {Duration} from '../../base/time'; 16import {ColumnDef, Sorting} from '../../public/aggregation'; 17import {AreaSelection} from '../../public/selection'; 18import {COUNTER_TRACK_KIND} from '../../public/track_kinds'; 19import {Engine} from '../../trace_processor/engine'; 20import {AreaSelectionAggregator} from '../../public/selection'; 21 22export class CounterSelectionAggregator implements AreaSelectionAggregator { 23 readonly id = 'counter_aggregation'; 24 25 async createAggregateView(engine: Engine, area: AreaSelection) { 26 const trackIds: (string | number)[] = []; 27 for (const trackInfo of area.tracks) { 28 if (trackInfo?.tags?.kind === COUNTER_TRACK_KIND) { 29 trackInfo.tags?.trackIds && trackIds.push(...trackInfo.tags.trackIds); 30 } 31 } 32 if (trackIds.length === 0) return false; 33 const duration = area.end - area.start; 34 const durationSec = Duration.toSeconds(duration); 35 36 // TODO(lalitm): Rewrite this query in a way that is both simpler and faster 37 let query; 38 if (trackIds.length === 1) { 39 // Optimized query for the special case where there is only 1 track id. 40 query = `CREATE OR REPLACE PERFETTO TABLE ${this.id} AS 41 WITH aggregated AS ( 42 SELECT 43 COUNT(1) AS count, 44 ROUND(SUM( 45 (MIN(ts + dur, ${area.end}) - MAX(ts,${area.start}))*value)/${duration}, 46 2 47 ) AS avg_value, 48 (SELECT value FROM experimental_counter_dur WHERE track_id = ${trackIds[0]} 49 AND ts + dur >= ${area.start} 50 AND ts <= ${area.end} ORDER BY ts DESC LIMIT 1) 51 AS last_value, 52 (SELECT value FROM experimental_counter_dur WHERE track_id = ${trackIds[0]} 53 AND ts + dur >= ${area.start} 54 AND ts <= ${area.end} ORDER BY ts ASC LIMIT 1) 55 AS first_value, 56 MIN(value) AS min_value, 57 MAX(value) AS max_value 58 FROM experimental_counter_dur 59 WHERE track_id = ${trackIds[0]} 60 AND ts + dur >= ${area.start} 61 AND ts <= ${area.end}) 62 SELECT 63 (SELECT name FROM counter_track WHERE id = ${trackIds[0]}) AS name, 64 *, 65 MAX(last_value) - MIN(first_value) AS delta_value, 66 ROUND((MAX(last_value) - MIN(first_value))/${durationSec}, 2) AS rate 67 FROM aggregated`; 68 } else { 69 // Slower, but general purspose query that can aggregate multiple tracks 70 query = `CREATE OR REPLACE PERFETTO TABLE ${this.id} AS 71 WITH aggregated AS ( 72 SELECT track_id, 73 COUNT(1) AS count, 74 ROUND(SUM( 75 (MIN(ts + dur, ${area.end}) - MAX(ts,${area.start}))*value)/${duration}, 76 2 77 ) AS avg_value, 78 value_at_max_ts(-ts, value) AS first, 79 value_at_max_ts(ts, value) AS last, 80 MIN(value) AS min_value, 81 MAX(value) AS max_value 82 FROM experimental_counter_dur 83 WHERE track_id IN (${trackIds}) 84 AND ts + dur >= ${area.start} AND 85 ts <= ${area.end} 86 GROUP BY track_id 87 ) 88 SELECT 89 name, 90 count, 91 avg_value, 92 last AS last_value, 93 first AS first_value, 94 last - first AS delta_value, 95 ROUND((last - first)/${durationSec}, 2) AS rate, 96 min_value, 97 max_value 98 FROM aggregated JOIN counter_track ON 99 track_id = counter_track.id 100 GROUP BY track_id`; 101 } 102 await engine.query(query); 103 return true; 104 } 105 106 getColumnDefinitions(): ColumnDef[] { 107 return [ 108 { 109 title: 'Name', 110 kind: 'STRING', 111 columnConstructor: Uint16Array, 112 columnId: 'name', 113 }, 114 { 115 title: 'Delta value', 116 kind: 'NUMBER', 117 columnConstructor: Float64Array, 118 columnId: 'delta_value', 119 }, 120 { 121 title: 'Rate /s', 122 kind: 'Number', 123 columnConstructor: Float64Array, 124 columnId: 'rate', 125 }, 126 { 127 title: 'Weighted avg value', 128 kind: 'Number', 129 columnConstructor: Float64Array, 130 columnId: 'avg_value', 131 }, 132 { 133 title: 'Count', 134 kind: 'Number', 135 columnConstructor: Float64Array, 136 columnId: 'count', 137 sum: true, 138 }, 139 { 140 title: 'First value', 141 kind: 'NUMBER', 142 columnConstructor: Float64Array, 143 columnId: 'first_value', 144 }, 145 { 146 title: 'Last value', 147 kind: 'NUMBER', 148 columnConstructor: Float64Array, 149 columnId: 'last_value', 150 }, 151 { 152 title: 'Min value', 153 kind: 'NUMBER', 154 columnConstructor: Float64Array, 155 columnId: 'min_value', 156 }, 157 { 158 title: 'Max value', 159 kind: 'NUMBER', 160 columnConstructor: Float64Array, 161 columnId: 'max_value', 162 }, 163 ]; 164 } 165 166 async getExtra() {} 167 168 getTabName() { 169 return 'Counters'; 170 } 171 172 getDefaultSorting(): Sorting { 173 return {column: 'name', direction: 'DESC'}; 174 } 175} 176