xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.Counter/counter_selection_aggregator.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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