| // Copyright (C) 2024 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use size file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| import m from 'mithril'; |
| import {Disposable, Trash} from '../base/disposable'; |
| import {AggregationPanel} from './aggregation_panel'; |
| import {globals} from './globals'; |
| import {Area, AreaSelection} from '../common/state'; |
| import {Anchor} from '../widgets/anchor'; |
| import {Actions} from '../common/actions'; |
| import {isEmptyData} from '../common/aggregation_data'; |
| import {DetailsShell} from '../widgets/details_shell'; |
| import {Section} from '../widgets/section'; |
| import {GridLayout} from '../widgets/grid_layout'; |
| import {Icons} from '../base/semantic_icons'; |
| import {Tree, TreeNode} from '../widgets/tree'; |
| import {Timestamp} from './widgets/timestamp'; |
| |
| interface AreaDetailsPanelAttrs { |
| selection: AreaSelection; |
| } |
| |
| class AreaDetailsPanel implements m.ClassComponent<AreaDetailsPanelAttrs> { |
| view(vnode: m.Vnode<AreaDetailsPanelAttrs>): m.Children { |
| const { |
| selection, |
| } = vnode.attrs; |
| |
| const areaId = selection.areaId; |
| const area = globals.state.areas[areaId]; |
| |
| return m(DetailsShell, |
| { |
| title: 'Area Selection', |
| }, |
| m(GridLayout, |
| this.renderDetailsSection(area), |
| this.renderLinksSection(), |
| ), |
| ); |
| } |
| |
| private renderDetailsSection(area: Area) { |
| return m(Section, |
| { |
| title: 'Details', |
| }, |
| m(Tree, |
| m(TreeNode, {left: 'Start', right: m(Timestamp, {ts: area.start})}), |
| m(TreeNode, {left: 'End', right: m(Timestamp, {ts: area.end})}), |
| m(TreeNode, {left: 'Track Count', right: area.tracks.length}), |
| ), |
| ); |
| } |
| |
| private renderLinksSection() { |
| const aggTabLinks: m.Children = []; |
| globals.aggregateDataStore.forEach((value, type) => { |
| if (!isEmptyData(value)) { |
| const anchor = m(Anchor, |
| { |
| icon: Icons.ChangeTab, |
| onclick: () => { |
| globals.dispatch(Actions.showTab({uri: uriForAggType(type)})); |
| }, |
| }, |
| value.tabName, |
| ); |
| const node = m(TreeNode, {left: anchor}); |
| aggTabLinks.push(node); |
| } |
| }); |
| if (aggTabLinks.length === 0) return undefined; |
| return m(Section, |
| { |
| title: 'Relevant Aggregations', |
| }, |
| m(Tree, aggTabLinks), |
| ); |
| } |
| } |
| |
| function uriForAggType(type: string): string { |
| return `aggregationTab#${type}`; |
| } |
| |
| export class AggregationsTabs implements Disposable { |
| private tabs = [ |
| { |
| type: 'cpu_aggregation', |
| title: 'CPU by thread', |
| }, |
| { |
| type: 'thread_state_aggregation', |
| title: 'Thread States', |
| }, |
| { |
| type: 'cpu_by_process_aggregation', |
| title: 'CPU by process', |
| }, |
| { |
| type: 'slice_aggregation', |
| title: 'Slices', |
| }, |
| { |
| type: 'counter_aggregation', |
| title: 'Counters', |
| }, |
| { |
| type: 'frame_aggregation', |
| title: 'Frames', |
| }, |
| ]; |
| |
| private trash = new Trash(); |
| |
| constructor() { |
| for (const {type, title} of this.tabs) { |
| const unregister = globals.tabManager.registerTab({ |
| uri: uriForAggType(type), |
| isEphemeral: false, |
| content: { |
| getTitle: () => `Aggregation: ${title}`, |
| render: () => { |
| const data = globals.aggregateDataStore.get(type); |
| return m(AggregationPanel, {kind: type, data}); |
| }, |
| }, |
| }); |
| this.trash.add(unregister); |
| } |
| |
| const unregister = globals.tabManager.registerDetailsPanel({ |
| render(selection) { |
| if (selection.kind === 'AREA') { |
| return m(AreaDetailsPanel, {selection}); |
| } else { |
| return undefined; |
| } |
| }, |
| }); |
| |
| this.trash.add(unregister); |
| } |
| |
| dispose(): void { |
| this.trash.dispose(); |
| } |
| } |