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 m from 'mithril'; 16import {PageWithTraceAttrs} from '../../public/page'; 17import {SqlTableState as SqlTableViewState} from '../../components/widgets/sql/table/state'; 18import {SqlTable as SqlTableView} from '../../components/widgets/sql/table/table'; 19import {exists} from '../../base/utils'; 20import {Menu, MenuItem, MenuItemAttrs} from '../../widgets/menu'; 21import {Button} from '../../widgets/button'; 22import {Icons} from '../../base/semantic_icons'; 23import {DetailsShell} from '../../widgets/details_shell'; 24import { 25 Chart, 26 ChartOption, 27 createChartConfigFromSqlTableState, 28 renderChartComponent, 29} from '../../components/widgets/charts/chart'; 30import {AddChartMenuItem} from '../../components/widgets/charts/add_chart_menu'; 31import { 32 CollapsiblePanel, 33 CollapsiblePanelVisibility, 34} from '../../components/widgets/collapsible_panel'; 35import {Trace} from '../../public/trace'; 36import SqlModulesPlugin from '../dev.perfetto.SqlModules'; 37 38export interface ExploreTableState { 39 sqlTableViewState?: SqlTableViewState; 40 selectedTableName?: string; 41} 42 43interface ExplorePageAttrs extends PageWithTraceAttrs { 44 readonly state: ExploreTableState; 45 readonly charts: Chart[]; 46} 47 48export class ExplorePage implements m.ClassComponent<ExplorePageAttrs> { 49 private visibility = CollapsiblePanelVisibility.VISIBLE; 50 51 // Show menu with standard library tables 52 private renderSelectableTablesMenuItems( 53 trace: Trace, 54 state: ExploreTableState, 55 ): m.Vnode<MenuItemAttrs, unknown>[] { 56 const sqlModules = trace.plugins 57 .getPlugin(SqlModulesPlugin) 58 .getSqlModules(); 59 return sqlModules.listTables().map((tableName) => { 60 const sqlTable = sqlModules 61 .getModuleForTable(tableName) 62 ?.getTable(tableName); 63 const sqlTableViewDescription = sqlModules 64 .getModuleForTable(tableName) 65 ?.getSqlTableDescription(tableName); 66 67 return m(MenuItem, { 68 label: tableName, 69 onclick: () => { 70 if ( 71 (state.selectedTableName && 72 tableName === state.selectedTableName) || 73 sqlTable === undefined || 74 sqlTableViewDescription === undefined 75 ) { 76 return; 77 } 78 79 state.selectedTableName = sqlTable.name; 80 state.sqlTableViewState = new SqlTableViewState( 81 trace, 82 { 83 name: tableName, 84 columns: sqlTable.getTableColumns(), 85 }, 86 {imports: sqlTableViewDescription.imports}, 87 ); 88 }, 89 }); 90 }); 91 } 92 93 private renderSqlTable(state: ExploreTableState, charts: Chart[]) { 94 const sqlTableViewState = state.sqlTableViewState; 95 96 if (sqlTableViewState === undefined) return; 97 98 const range = sqlTableViewState.getDisplayedRange(); 99 const rowCount = sqlTableViewState.getTotalRowCount(); 100 101 const navigation = [ 102 exists(range) && 103 exists(rowCount) && 104 `Showing rows ${range.from}-${range.to} of ${rowCount}`, 105 m(Button, { 106 icon: Icons.GoBack, 107 disabled: !sqlTableViewState.canGoBack(), 108 onclick: () => sqlTableViewState!.goBack(), 109 }), 110 m(Button, { 111 icon: Icons.GoForward, 112 disabled: !sqlTableViewState.canGoForward(), 113 onclick: () => sqlTableViewState!.goForward(), 114 }), 115 ]; 116 117 return m( 118 DetailsShell, 119 { 120 title: 'Explore Table', 121 buttons: navigation, 122 fillParent: false, 123 }, 124 m(SqlTableView, { 125 state: sqlTableViewState, 126 addColumnMenuItems: (column, columnAlias) => 127 m(AddChartMenuItem, { 128 chartConfig: createChartConfigFromSqlTableState( 129 column, 130 columnAlias, 131 sqlTableViewState, 132 ), 133 chartOptions: [ChartOption.HISTOGRAM], 134 addChart: (chart) => charts.push(chart), 135 }), 136 }), 137 ); 138 } 139 140 view({attrs}: m.CVnode<ExplorePageAttrs>) { 141 const {trace, state, charts} = attrs; 142 143 return m( 144 '.page.explore-page', 145 m( 146 '.chart-container', 147 m(Menu, this.renderSelectableTablesMenuItems(trace, state)), 148 ), 149 m( 150 '.chart-container', 151 charts.map((chart) => renderChartComponent(chart)), 152 ), 153 state.selectedTableName && 154 m(CollapsiblePanel, { 155 visibility: this.visibility, 156 setVisibility: (visibility) => { 157 this.visibility = visibility; 158 }, 159 tabs: [this.renderSqlTable(state, charts)], 160 }), 161 ); 162 } 163} 164