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