• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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';
21import {LONG, NUM} from '../../trace_processor/query_result';
22
23export class CounterSelectionAggregator implements AreaSelectionAggregator {
24  readonly id = 'counter_aggregation';
25
26  // This just describes which counters we match, we don't actually use the
27  // resulting datasets, but it's a useful too to show what we actually match.
28  readonly trackKind = COUNTER_TRACK_KIND;
29  readonly schema = {
30    id: NUM,
31    ts: LONG,
32    value: NUM,
33  };
34
35  async createAggregateView(engine: Engine, area: AreaSelection) {
36    const trackIds: (string | number)[] = [];
37    for (const trackInfo of area.tracks) {
38      if (trackInfo?.tags?.kind === COUNTER_TRACK_KIND) {
39        trackInfo.tags?.trackIds && trackIds.push(...trackInfo.tags.trackIds);
40      }
41    }
42    if (trackIds.length === 0) return false;
43    const duration = area.end - area.start;
44    const durationSec = Duration.toSeconds(duration);
45
46    await engine.query(`include perfetto module counters.intervals`);
47
48    // TODO(lalitm): Rewrite this query in a way that is both simpler and faster
49    let query;
50    if (trackIds.length === 1) {
51      // Optimized query for the special case where there is only 1 track id.
52      query = `CREATE OR REPLACE PERFETTO TABLE ${this.id} AS
53      WITH
54        res AS (
55          select c.*
56          from counter_leading_intervals!((
57            SELECT counter.*
58            FROM counter
59            WHERE counter.track_id = ${trackIds[0]}
60              AND counter.ts <= ${area.end}
61          )) c
62          WHERE c.ts + c.dur >= ${area.start}
63        ),
64        aggregated AS (
65          SELECT
66            COUNT(1) AS count,
67            ROUND(SUM(
68              (MIN(ts + dur, ${area.end}) - MAX(ts,${area.start}))*value)/${duration},
69              2
70            ) AS avg_value,
71            value_at_max_ts(ts, value) AS last_value,
72            value_at_max_ts(-ts, value) AS first_value,
73            MIN(value) AS min_value,
74            MAX(value) AS max_value
75          FROM res
76        )
77      SELECT
78        (SELECT name FROM counter_track WHERE id = ${trackIds[0]}) AS name,
79        *,
80        MAX(last_value) - MIN(first_value) AS delta_value,
81        ROUND((MAX(last_value) - MIN(first_value))/${durationSec}, 2) AS rate
82      FROM aggregated`;
83    } else {
84      // Slower, but general purspose query that can aggregate multiple tracks
85      query = `CREATE OR REPLACE PERFETTO TABLE ${this.id} AS
86      WITH
87        res AS (
88          select c.*
89          from counter_leading_intervals!((
90            SELECT counter.*
91            FROM counter
92            WHERE counter.track_id in (${trackIds})
93              AND counter.ts <= ${area.end}
94          )) c
95          where c.ts + c.dur >= ${area.start}
96        ),
97        aggregated AS (
98          SELECT track_id,
99            COUNT(1) AS count,
100            ROUND(SUM(
101              (MIN(ts + dur, ${area.end}) - MAX(ts,${area.start}))*value)/${duration},
102              2
103            ) AS avg_value,
104            value_at_max_ts(-ts, value) AS first,
105            value_at_max_ts(ts, value) AS last,
106            MIN(value) AS min_value,
107            MAX(value) AS max_value
108          FROM res
109          GROUP BY track_id
110        )
111      SELECT
112        name,
113        count,
114        avg_value,
115        last AS last_value,
116        first AS first_value,
117        last - first AS delta_value,
118        ROUND((last - first)/${durationSec}, 2) AS rate,
119        min_value,
120        max_value
121      FROM aggregated JOIN counter_track ON
122        track_id = counter_track.id
123      GROUP BY track_id`;
124    }
125    await engine.query(query);
126    return true;
127  }
128
129  getColumnDefinitions(): ColumnDef[] {
130    return [
131      {
132        title: 'Name',
133        kind: 'STRING',
134        columnConstructor: Uint16Array,
135        columnId: 'name',
136      },
137      {
138        title: 'Delta value',
139        kind: 'NUMBER',
140        columnConstructor: Float64Array,
141        columnId: 'delta_value',
142      },
143      {
144        title: 'Rate /s',
145        kind: 'Number',
146        columnConstructor: Float64Array,
147        columnId: 'rate',
148      },
149      {
150        title: 'Weighted avg value',
151        kind: 'Number',
152        columnConstructor: Float64Array,
153        columnId: 'avg_value',
154      },
155      {
156        title: 'Count',
157        kind: 'Number',
158        columnConstructor: Float64Array,
159        columnId: 'count',
160        sum: true,
161      },
162      {
163        title: 'First value',
164        kind: 'NUMBER',
165        columnConstructor: Float64Array,
166        columnId: 'first_value',
167      },
168      {
169        title: 'Last value',
170        kind: 'NUMBER',
171        columnConstructor: Float64Array,
172        columnId: 'last_value',
173      },
174      {
175        title: 'Min value',
176        kind: 'NUMBER',
177        columnConstructor: Float64Array,
178        columnId: 'min_value',
179      },
180      {
181        title: 'Max value',
182        kind: 'NUMBER',
183        columnConstructor: Float64Array,
184        columnId: 'max_value',
185      },
186    ];
187  }
188
189  async getExtra() {}
190
191  getTabName() {
192    return 'Counters';
193  }
194
195  getDefaultSorting(): Sorting {
196    return {column: 'name', direction: 'DESC'};
197  }
198}
199