• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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';
16
17import {GenericSliceDetailsTabConfig} from '../core/generic_slice_details_types';
18import {raf} from '../core/raf_scheduler';
19import {ColumnType} from '../trace_processor/query_result';
20import {sqlValueToReadableString} from '../trace_processor/sql_utils';
21import {DetailsShell} from '../widgets/details_shell';
22import {GridLayout} from '../widgets/grid_layout';
23import {Section} from '../widgets/section';
24import {SqlRef} from '../widgets/sql_ref';
25import {dictToTree, Tree, TreeNode} from '../widgets/tree';
26
27import {BottomTab, NewBottomTabArgs} from './bottom_tab';
28
29export {
30  ColumnConfig,
31  Columns,
32  GenericSliceDetailsTabConfig,
33  GenericSliceDetailsTabConfigBase,
34} from '../core/generic_slice_details_types';
35
36// A details tab, which fetches slice-like object from a given SQL table by id
37// and renders it according to the provided config, specifying which columns
38// need to be rendered and how.
39export class GenericSliceDetailsTab extends BottomTab<GenericSliceDetailsTabConfig> {
40  static readonly kind = 'dev.perfetto.GenericSliceDetailsTab';
41
42  data: {[key: string]: ColumnType} | undefined;
43
44  static create(
45    args: NewBottomTabArgs<GenericSliceDetailsTabConfig>,
46  ): GenericSliceDetailsTab {
47    return new GenericSliceDetailsTab(args);
48  }
49
50  constructor(args: NewBottomTabArgs<GenericSliceDetailsTabConfig>) {
51    super(args);
52
53    this.engine
54      .query(
55        `select * from ${this.config.sqlTableName} where id = ${this.config.id}`,
56      )
57      .then((queryResult) => {
58        this.data = queryResult.firstRow({});
59        raf.scheduleFullRedraw();
60      });
61  }
62
63  viewTab() {
64    if (this.data === undefined) {
65      return m('h2', 'Loading');
66    }
67
68    const args: {[key: string]: m.Child} = {};
69    if (this.config.columns !== undefined) {
70      for (const key of Object.keys(this.config.columns)) {
71        let argKey = key;
72        if (this.config.columns[key].displayName !== undefined) {
73          argKey = this.config.columns[key].displayName!;
74        }
75        args[argKey] = sqlValueToReadableString(this.data[key]);
76      }
77    } else {
78      for (const key of Object.keys(this.data)) {
79        args[key] = sqlValueToReadableString(this.data[key]);
80      }
81    }
82
83    const details = dictToTree(args);
84
85    return m(
86      DetailsShell,
87      {
88        title: this.config.title,
89      },
90      m(
91        GridLayout,
92        m(Section, {title: 'Details'}, m(Tree, details)),
93        m(
94          Section,
95          {title: 'Metadata'},
96          m(Tree, [
97            m(TreeNode, {
98              left: 'SQL ID',
99              right: m(SqlRef, {
100                table: this.config.sqlTableName,
101                id: this.config.id,
102              }),
103            }),
104          ]),
105        ),
106      ),
107    );
108  }
109
110  getTitle(): string {
111    return this.config.title;
112  }
113
114  isLoading() {
115    return this.data === undefined;
116  }
117}
118