• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2020 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use size 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 * as m from 'mithril';
16
17import {Actions} from '../common/actions';
18
19import {Flow, globals} from './globals';
20import {BLANK_CHECKBOX, CHECKBOX} from './icons';
21import {Panel, PanelSize} from './panel';
22import {findUiTrackId} from './scroll_helper';
23
24export const ALL_CATEGORIES = '_all_';
25
26export function getFlowCategories(flow: Flow): string[] {
27  const categories: string[] = [];
28  // v1 flows have their own categories
29  if (flow.category) {
30    categories.push(...flow.category.split(','));
31    return categories;
32  }
33  const beginCats = flow.begin.sliceCategory.split(',');
34  const endCats = flow.end.sliceCategory.split(',');
35  categories.push(...new Set([...beginCats, ...endCats]));
36  return categories;
37}
38
39export class FlowEventsPanel extends Panel {
40  view() {
41    const selection = globals.state.currentSelection;
42    if (!selection || selection.kind !== 'CHROME_SLICE') {
43      return;
44    }
45
46    const flowClickHandler = (sliceId: number, trackId: number) => {
47      const uiTrackId = findUiTrackId(trackId);
48      if (uiTrackId) {
49        globals.makeSelection(
50            Actions.selectChromeSlice(
51                {id: sliceId, trackId: uiTrackId, table: 'slice'}),
52            'bound_flows');
53      }
54    };
55
56    // Can happen only for flow events version 1
57    const haveCategories =
58        globals.connectedFlows.filter(flow => flow.category).length > 0;
59
60    const columns = [
61      m('th', 'Direction'),
62      m('th', 'Connected Slice ID'),
63      m('th', 'Connected Slice Name')
64    ];
65
66    if (haveCategories) {
67      columns.push(m('th', 'Flow Category'));
68      columns.push(m('th', 'Flow Name'));
69    }
70
71    const rows = [m('tr', columns)];
72
73    // Fill the table with all the directly connected flow events
74    globals.connectedFlows.forEach(flow => {
75      if (selection.id !== flow.begin.sliceId &&
76          selection.id !== flow.end.sliceId) {
77        return;
78      }
79
80      const outgoing = selection.id === flow.begin.sliceId;
81      const otherEnd = (outgoing ? flow.end : flow.begin);
82
83      const args = {
84        onclick: () => flowClickHandler(otherEnd.sliceId, otherEnd.trackId),
85        onmousemove: () =>
86            globals.frontendLocalState.setHighlightedSliceId(otherEnd.sliceId),
87        onmouseleave: () => globals.frontendLocalState.setHighlightedSliceId(-1)
88      };
89
90      const data = [
91        m('td.flow-link', args, outgoing ? 'Outgoing' : 'Incoming'),
92        m('td.flow-link', args, otherEnd.sliceId.toString()),
93        m('td.flow-link', args, otherEnd.sliceName)
94      ];
95
96      if (haveCategories) {
97        data.push(m('td.flow-info', flow.category || '-'));
98        data.push(m('td.flow-info', flow.name || '-'));
99      }
100
101      rows.push(m('tr', data));
102    });
103
104    return m('.details-panel', [
105      m('.details-panel-heading', m('h2', `Flow events`)),
106      m('.flow-events-table', m('table.half-width', rows))
107    ]);
108  }
109
110  renderCanvas(_ctx: CanvasRenderingContext2D, _size: PanelSize) {}
111}
112
113export class FlowEventsAreaSelectedPanel extends Panel {
114  view() {
115    const selection = globals.state.currentSelection;
116    if (!selection || selection.kind !== 'AREA') {
117      return;
118    }
119
120    const columns = [
121      m('th', 'Flow Category'),
122      m('th', 'Number of flows'),
123      m('th',
124        'Show',
125        m('a.warning',
126          m('i.material-icons', 'warning'),
127          m('.tooltip',
128            'Showing a large number of flows may impact performance.')))
129    ];
130
131    const rows = [m('tr', columns)];
132
133    const categoryToFlowsNum = new Map<string, number>();
134
135    globals.selectedFlows.forEach(flow => {
136      const categories = getFlowCategories(flow);
137      categories.forEach(cat => {
138        if (!categoryToFlowsNum.has(cat)) {
139          categoryToFlowsNum.set(cat, 0);
140        }
141        categoryToFlowsNum.set(cat, categoryToFlowsNum.get(cat)! + 1);
142      });
143    });
144
145    const allWasChecked = globals.visibleFlowCategories.get(ALL_CATEGORIES);
146    rows.push(m('tr.sum', [
147      m('td.sum-data', 'All'),
148      m('td.sum-data', globals.selectedFlows.length),
149      m('td.sum-data',
150        m('i.material-icons',
151          {
152            onclick: () => {
153              if (allWasChecked) {
154                globals.visibleFlowCategories.clear();
155              } else {
156                categoryToFlowsNum.forEach((_, cat) => {
157                  globals.visibleFlowCategories.set(cat, true);
158                });
159              }
160              globals.visibleFlowCategories.set(ALL_CATEGORIES, !allWasChecked);
161              globals.rafScheduler.scheduleFullRedraw();
162            },
163          },
164          allWasChecked ? CHECKBOX : BLANK_CHECKBOX))
165    ]));
166
167    categoryToFlowsNum.forEach((num, cat) => {
168      const wasChecked = globals.visibleFlowCategories.get(cat) ||
169          globals.visibleFlowCategories.get(ALL_CATEGORIES);
170      const data = [
171        m('td.flow-info', cat),
172        m('td.flow-info', num),
173        m('td.flow-info',
174          m('i.material-icons',
175            {
176              onclick: () => {
177                if (wasChecked) {
178                  globals.visibleFlowCategories.set(ALL_CATEGORIES, false);
179                }
180                globals.visibleFlowCategories.set(cat, !wasChecked);
181                globals.rafScheduler.scheduleFullRedraw();
182              },
183            },
184            wasChecked ? CHECKBOX : BLANK_CHECKBOX))
185      ];
186      rows.push(m('tr', data));
187    });
188
189    return m('.details-panel', [
190      m('.details-panel-heading', m('h2', `Selected flow events`)),
191      m('.flow-events-table', m('table.half-width', rows)),
192    ]);
193  }
194
195  renderCanvas(_ctx: CanvasRenderingContext2D, _size: PanelSize) {}
196}
197