• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2019 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 m from 'mithril';
16
17import {Actions} from '../common/actions';
18import {
19  AggregateData,
20  Column,
21  ThreadStateExtra,
22} from '../common/aggregation_data';
23import {colorForState, textColorForState} from '../common/colorizer';
24import {translateState} from '../common/thread_state';
25import {tpTimeToMillis} from '../common/time';
26
27import {globals} from './globals';
28import {Panel} from './panel';
29
30export interface AggregationPanelAttrs {
31  data: AggregateData;
32  kind: string;
33}
34
35export class AggregationPanel extends Panel<AggregationPanelAttrs> {
36  view({attrs}: m.CVnode<AggregationPanelAttrs>) {
37    return m(
38        '.details-panel',
39        m('.details-panel-heading.aggregation',
40          attrs.data.extra !== undefined &&
41                  attrs.data.extra.kind === 'THREAD_STATE' ?
42              this.showStateSummary(attrs.data.extra) :
43              null,
44          this.showTimeRange(),
45          m('table',
46            m('tr',
47              attrs.data.columns.map(
48                  (col) => this.formatColumnHeading(col, attrs.kind))),
49            m('tr.sum', attrs.data.columnSums.map((sum) => {
50              const sumClass = sum === '' ? 'td' : 'td.sum-data';
51              return m(sumClass, sum);
52            })))),
53        m(
54            '.details-table.aggregation',
55            m('table', this.getRows(attrs.data)),
56            ));
57  }
58
59  formatColumnHeading(col: Column, id: string) {
60    const pref = globals.state.aggregatePreferences[id];
61    let sortIcon = '';
62    if (pref && pref.sorting && pref.sorting.column === col.columnId) {
63      sortIcon = pref.sorting.direction === 'DESC' ? 'arrow_drop_down' :
64                                                     'arrow_drop_up';
65    }
66    return m(
67        'th',
68        {
69          onclick: () => {
70            globals.dispatch(
71                Actions.updateAggregateSorting({id, column: col.columnId}));
72          },
73        },
74        col.title,
75        m('i.material-icons', sortIcon));
76  }
77
78  getRows(data: AggregateData) {
79    if (data.columns.length === 0) return;
80    const rows = [];
81    for (let i = 0; i < data.columns[0].data.length; i++) {
82      const row = [];
83      for (let j = 0; j < data.columns.length; j++) {
84        row.push(m('td', this.getFormattedData(data, i, j)));
85      }
86      rows.push(m('tr', row));
87    }
88    return rows;
89  }
90
91  getFormattedData(data: AggregateData, rowIndex: number, columnIndex: number) {
92    switch (data.columns[columnIndex].kind) {
93      case 'STRING':
94        return data.strings[data.columns[columnIndex].data[rowIndex]];
95      case 'TIMESTAMP_NS':
96        return `${data.columns[columnIndex].data[rowIndex] / 1000000}`;
97      case 'STATE': {
98        const concatState =
99            data.strings[data.columns[columnIndex].data[rowIndex]];
100        const split = concatState.split(',');
101        const ioWait =
102            split[1] === 'NULL' ? undefined : !!Number.parseInt(split[1], 10);
103        return translateState(split[0], ioWait);
104      }
105      case 'NUMBER':
106      default:
107        return data.columns[columnIndex].data[rowIndex];
108    }
109  }
110
111  showTimeRange() {
112    const selection = globals.state.currentSelection;
113    if (selection === null || selection.kind !== 'AREA') return undefined;
114    const selectedArea = globals.state.areas[selection.areaId];
115    const rangeDurationMs =
116        tpTimeToMillis(selectedArea.end - selectedArea.start);
117    return m('.time-range', `Selected range: ${rangeDurationMs.toFixed(6)} ms`);
118  }
119
120  // Thread state aggregation panel only
121  showStateSummary(data: ThreadStateExtra) {
122    if (data === undefined) return undefined;
123    const states = [];
124    for (let i = 0; i < data.states.length; i++) {
125      const color = colorForState(data.states[i]);
126      const textColor = textColorForState(data.states[i]);
127      const width = data.values[i] / data.totalMs * 100;
128      states.push(
129          m('.state',
130            {
131              style: {
132                background: `hsl(${color.h},${color.s}%,${color.l}%)`,
133                color: `${textColor}`,
134                width: `${width}%`,
135              },
136            },
137            `${data.states[i]}: ${data.values[i]} ms`));
138    }
139    return m('.states', states);
140  }
141
142  renderCanvas() {}
143}
144