1// Copyright (C) 2025 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 m from 'mithril'; 16import {assertExists} from '../../base/logging'; 17import {Icons} from '../../base/semantic_icons'; 18import {PivotTable} from '../../components/widgets/sql/pivot_table/pivot_table'; 19import {PivotTableState} from '../../components/widgets/sql/pivot_table/pivot_table_state'; 20import {getSqlTableDescription} from '../../components/widgets/sql/table/sql_table_registry'; 21import { 22 AreaSelection, 23 areaSelectionsEqual, 24 AreaSelectionTab, 25} from '../../public/selection'; 26import {SLICE_TRACK_KIND} from '../../public/track_kinds'; 27import {Button} from '../../widgets/button'; 28import {Trace} from '../../public/trace'; 29import {extensions} from '../../components/extensions'; 30 31export class PivotTableTab implements AreaSelectionTab { 32 readonly id = 'pivot_table'; 33 readonly name = 'Pivot Table'; 34 35 private state?: PivotTableState; 36 private previousSelection?: AreaSelection; 37 private trackIds: number[] = []; 38 39 constructor(private readonly trace: Trace) {} 40 41 render(selection: AreaSelection) { 42 if ( 43 this.previousSelection === undefined || 44 !areaSelectionsEqual(this.previousSelection, selection) 45 ) { 46 this.previousSelection = selection; 47 this.trackIds = selection.tracks 48 .filter((track) => track.tags?.kind == SLICE_TRACK_KIND) 49 .flatMap((track) => track.tags?.trackIds ?? []); 50 this.getOrCreateState().filters.setFilters([ 51 { 52 op: (cols) => `${cols[0]} + ${cols[1]} > ${selection.start}`, 53 columns: ['ts', 'dur'], 54 }, 55 {op: (cols) => `${cols[0]} < ${selection.end}`, columns: ['ts']}, 56 { 57 op: (cols) => `${cols[0]} in (${this.trackIds.join(', ')})`, 58 columns: ['track_id'], 59 }, 60 ]); 61 } 62 if (this.trackIds.length === 0) return undefined; 63 const state = this.getOrCreateState(); 64 65 return { 66 isLoading: state?.getData() === undefined, 67 content: m(PivotTable, { 68 state, 69 extraRowButton: (node) => 70 m(Button, { 71 icon: Icons.GoTo, 72 onclick: () => { 73 extensions.addLegacySqlTableTab(this.trace, { 74 table: assertExists(getSqlTableDescription('slice')), 75 filters: [ 76 ...(state?.filters.get() ?? []), 77 ...node.getFilters(), 78 ], 79 }); 80 }, 81 }), 82 }), 83 }; 84 } 85 86 private getOrCreateState(): PivotTableState { 87 if (this.state !== undefined) return this.state; 88 const sliceTable = assertExists(getSqlTableDescription('slice')); 89 const name = assertExists( 90 sliceTable.columns.find((c) => c.column === 'name'), 91 ); 92 const dur = assertExists( 93 sliceTable.columns.find((c) => c.column === 'dur'), 94 ); 95 this.state = new PivotTableState({ 96 trace: this.trace, 97 table: sliceTable, 98 pivots: [name], 99 aggregations: [ 100 { 101 column: dur, 102 op: 'sum', 103 }, 104 ], 105 }); 106 return this.state; 107 } 108} 109