1// Copyright (C) 2023 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 {TrackEventDetailsPanel} from '../../public/details_panel'; 17import {ColumnType} from '../../trace_processor/query_result'; 18import {sqlValueToReadableString} from '../../trace_processor/sql_utils'; 19import {DetailsShell} from '../../widgets/details_shell'; 20import {GridLayout} from '../../widgets/grid_layout'; 21import {Section} from '../../widgets/section'; 22import {SqlRef} from '../../widgets/sql_ref'; 23import {dictToTree, Tree, TreeNode} from '../../widgets/tree'; 24import {Trace} from '../../public/trace'; 25 26export interface ColumnConfig { 27 readonly displayName?: string; 28} 29 30export type Columns = { 31 readonly [columnName: string]: ColumnConfig; 32}; 33 34// A details tab, which fetches slice-like object from a given SQL table by id 35// and renders it according to the provided config, specifying which columns 36// need to be rendered and how. 37export class GenericSliceDetailsTab implements TrackEventDetailsPanel { 38 private data?: {[key: string]: ColumnType}; 39 40 constructor( 41 private readonly trace: Trace, 42 private readonly sqlTableName: string, 43 private readonly id: number, 44 private readonly title: string, 45 private readonly columns?: Columns, 46 ) {} 47 48 async load() { 49 const result = await this.trace.engine.query( 50 `select * from ${this.sqlTableName} where id = ${this.id}`, 51 ); 52 53 this.data = result.firstRow({}); 54 } 55 56 render() { 57 if (!this.data) { 58 return m('h2', 'Loading'); 59 } 60 61 const args: {[key: string]: m.Child} = {}; 62 if (this.columns !== undefined) { 63 for (const key of Object.keys(this.columns)) { 64 let argKey = key; 65 if (this.columns[key].displayName !== undefined) { 66 argKey = this.columns[key].displayName!; 67 } 68 args[argKey] = sqlValueToReadableString(this.data[key]); 69 } 70 } else { 71 for (const key of Object.keys(this.data)) { 72 args[key] = sqlValueToReadableString(this.data[key]); 73 } 74 } 75 76 const details = dictToTree(args); 77 78 return m( 79 DetailsShell, 80 { 81 title: this.title, 82 }, 83 m( 84 GridLayout, 85 m(Section, {title: 'Details'}, m(Tree, details)), 86 m( 87 Section, 88 {title: 'Metadata'}, 89 m(Tree, [ 90 m(TreeNode, { 91 left: 'SQL ID', 92 right: m(SqlRef, { 93 table: this.sqlTableName, 94 id: this.id, 95 }), 96 }), 97 ]), 98 ), 99 ), 100 ); 101 } 102} 103