• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {exists} from '../../base/utils';
16import {ColumnDef, Sorting} from '../../public/aggregation';
17import {AreaSelection} from '../../public/selection';
18import {Engine} from '../../trace_processor/engine';
19import {CPU_SLICE_TRACK_KIND} from '../../public/track_kinds';
20import {AreaSelectionAggregator} from '../../public/selection';
21
22export class WattsonThreadSelectionAggregator
23  implements AreaSelectionAggregator
24{
25  readonly id = 'wattson_plugin_thread_aggregation';
26
27  async createAggregateView(engine: Engine, area: AreaSelection) {
28    await engine.query(`drop view if exists ${this.id};`);
29
30    const selectedCpus: number[] = [];
31    for (const trackInfo of area.tracks) {
32      if (trackInfo?.tags?.kind === CPU_SLICE_TRACK_KIND) {
33        exists(trackInfo.tags.cpu) && selectedCpus.push(trackInfo.tags.cpu);
34      }
35    }
36    if (selectedCpus.length === 0) return false;
37
38    const duration = area.end - area.start;
39    const cpusCsv = `(` + selectedCpus.join() + `)`;
40    engine.query(`
41      INCLUDE PERFETTO MODULE viz.summary.threads_w_processes;
42      INCLUDE PERFETTO MODULE wattson.curves.idle_attribution;
43      INCLUDE PERFETTO MODULE wattson.curves.estimates;
44
45      CREATE OR REPLACE PERFETTO TABLE wattson_plugin_ui_selection_window AS
46      SELECT
47        ${area.start} as ts,
48        ${duration} as dur;
49
50      -- Processes filtered by CPU within the UI defined time window
51      DROP TABLE IF EXISTS wattson_plugin_windowed_summary;
52      CREATE VIRTUAL TABLE wattson_plugin_windowed_summary
53      USING SPAN_JOIN(
54        wattson_plugin_ui_selection_window,
55        _sched_w_thread_process_package_summary
56      );
57
58      -- Only get idle attribution in user defined window and filter by selected
59      -- CPUs and GROUP BY thread
60      CREATE OR REPLACE PERFETTO TABLE wattson_plugin_per_thread_idle_cost AS
61      SELECT
62        SUM(idle_cost_mws) as idle_cost_mws,
63        utid
64      FROM _filter_idle_attribution(${area.start}, ${duration})
65      WHERE cpu in ${cpusCsv}
66      GROUP BY utid
67      ;
68    `);
69    this.runEstimateThreadsQuery(engine, selectedCpus, duration);
70
71    return true;
72  }
73
74  // This function returns a query that gets the average and estimate from
75  // Wattson for the selection in the UI window based on thread. The grouping by
76  // thread needs to 'remove' 2 dimensions; the threads need to be grouped over
77  // time and the threads need to be grouped over CPUs.
78  // 1. Window and associate thread with proper Wattson estimate slice
79  // 2. Group all threads over time on a per CPU basis
80  // 3. Group all threads over all CPUs
81  runEstimateThreadsQuery(
82    engine: Engine,
83    selectedCpu: number[],
84    duration: bigint,
85  ) {
86    // Estimate and total per UTID per CPU
87    selectedCpu.forEach((cpu) => {
88      engine.query(`
89        -- Packages filtered by CPU
90        CREATE OR REPLACE PERFETTO VIEW
91        wattson_plugin_windowed_summary_per_cpu${cpu} AS
92        SELECT *
93        FROM wattson_plugin_windowed_summary WHERE cpu = ${cpu};
94
95        -- CPU specific track with slices for curves
96        CREATE OR REPLACE PERFETTO VIEW wattson_plugin_per_cpu${cpu}_curve AS
97        SELECT ts, dur, cpu${cpu}_curve
98        FROM _system_state_curves;
99
100        -- Filter out track when threads are available
101        DROP TABLE IF EXISTS wattson_plugin_windowed_thread_curve${cpu};
102        CREATE VIRTUAL TABLE wattson_plugin_windowed_thread_curve${cpu}
103        USING SPAN_JOIN(
104          wattson_plugin_per_cpu${cpu}_curve,
105          wattson_plugin_windowed_summary_per_cpu${cpu}
106        );
107
108        -- Total estimate per UTID per CPU
109        CREATE OR REPLACE PERFETTO VIEW wattson_plugin_total_per_cpu${cpu} AS
110        SELECT
111          SUM(cpu${cpu}_curve * dur) as total_pws,
112          SUM(dur) as dur,
113          tid,
114          pid,
115          uid,
116          utid,
117          upid,
118          thread_name,
119          process_name,
120          package_name
121        FROM wattson_plugin_windowed_thread_curve${cpu}
122        GROUP BY utid;
123      `);
124    });
125
126    // Estimate and total per UTID, removing CPU dimension
127    let query = `
128      CREATE OR REPLACE PERFETTO TABLE wattson_plugin_unioned_per_cpu_total AS
129    `;
130    selectedCpu.forEach((cpu, i) => {
131      query += i != 0 ? `UNION ALL\n` : ``;
132      query += `SELECT * from wattson_plugin_total_per_cpu${cpu}\n`;
133    });
134    query += `
135      ;
136
137      -- Grouped again by UTID, but this time to make it CPU agnostic
138      CREATE PERFETTO VIEW ${this.id} AS
139      WITH base AS (
140        SELECT
141          ROUND(SUM(total_pws) / ${duration}, 3) as active_mw,
142          ROUND(SUM(total_pws) / 1000000000, 3) as active_mws,
143          ROUND(COALESCE(idle_cost_mws, 0), 3) as idle_cost_mws,
144          ROUND(
145            COALESCE(idle_cost_mws, 0) + SUM(total_pws) / 1000000000,
146            3
147          ) as total_mws,
148          thread_name,
149          utid,
150          tid,
151          pid
152        FROM wattson_plugin_unioned_per_cpu_total
153        LEFT JOIN wattson_plugin_per_thread_idle_cost USING (utid)
154        GROUP BY utid
155      ),
156      secondary AS (
157        SELECT utid,
158          ROUND(100 * (total_mws) / (SUM(total_mws) OVER()), 3)
159            AS percent_of_total_energy
160        FROM base
161        GROUP BY utid
162      )
163      select *
164        from base INNER JOIN secondary
165        USING (utid);
166    `;
167
168    engine.query(query);
169
170    return;
171  }
172
173  getColumnDefinitions(): ColumnDef[] {
174    return [
175      {
176        title: 'Thread Name',
177        kind: 'STRING',
178        columnConstructor: Uint16Array,
179        columnId: 'thread_name',
180      },
181      {
182        title: 'TID',
183        kind: 'NUMBER',
184        columnConstructor: Uint16Array,
185        columnId: 'tid',
186      },
187      {
188        title: 'PID',
189        kind: 'NUMBER',
190        columnConstructor: Uint16Array,
191        columnId: 'pid',
192      },
193      {
194        title: 'Active power (estimated mW)',
195        kind: 'NUMBER',
196        columnConstructor: Float64Array,
197        columnId: 'active_mw',
198        sum: true,
199      },
200      {
201        title: 'Active energy (estimated mWs)',
202        kind: 'NUMBER',
203        columnConstructor: Float64Array,
204        columnId: 'active_mws',
205        sum: true,
206      },
207      {
208        title: 'Idle transitions overhead (estimated mWs)',
209        kind: 'NUMBER',
210        columnConstructor: Float64Array,
211        columnId: 'idle_cost_mws',
212        sum: false,
213      },
214      {
215        title: 'Total energy (estimated mWs)',
216        kind: 'NUMBER',
217        columnConstructor: Float64Array,
218        columnId: 'total_mws',
219        sum: true,
220      },
221      {
222        title: '% of total energy',
223        kind: 'PERCENT',
224        columnConstructor: Float64Array,
225        columnId: 'percent_of_total_energy',
226        sum: false,
227      },
228    ];
229  }
230
231  async getExtra() {}
232
233  getTabName() {
234    return 'Wattson by thread';
235  }
236
237  getDefaultSorting(): Sorting {
238    return {column: 'active_mws', direction: 'DESC'};
239  }
240}
241