• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
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 */
15
16import { BaseElement, element } from '../../../../../base-ui/BaseElement';
17import { SelectionParam } from '../../../../bean/BoxSelection';
18import { TraceRow } from '../../base/TraceRow';
19import { TraceSheet } from '../../base/TraceSheet';
20import { Flag } from '../../timer-shaft/Flag';
21import { SpSystemTrace } from '../../../SpSystemTrace';
22import { ns2Timestamp, ns2x, Rect } from '../../../../database/ui-worker/ProcedureWorkerCommon';
23import { ColorUtils } from '../../base/ColorUtils';
24import { LitPageTable } from '../../../../../base-ui/table/LitPageTable';
25import { LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar';
26import { TabPaneHangHtml } from './TabPaneHang.html';
27import { HangStruct } from '../../../../database/ui-worker/ProcedureWorkerHang';
28import { queryAllHangs } from '../../../../database/sql/Hang.sql';
29import { HangType, SpHangChart } from '../../../chart/SpHangChart';
30import { getTimeString } from '../TabPaneCurrentSelection';
31
32/// Hangs 框选Tab页1
33@element('tab-hang')
34export class TabPaneHang extends BaseElement {
35  // Elements
36  private spSystemTrace: SpSystemTrace | undefined | null;
37  private traceSheetEl: TraceSheet | undefined | null;
38  private levelFilterInput: HTMLSelectElement | undefined | null;
39  private searchFilterInput: HTMLInputElement | undefined | null;
40  private processFilter: HTMLInputElement | undefined | null;
41  private hangTableTitle: HTMLDivElement | undefined | null;
42  private hangTbl: LitPageTable | undefined | null;
43
44  private tableTimeHandle: (() => void) | undefined;
45  private tableTitleTimeHandle: (() => void) | undefined;
46  private systemHangSource: HangStructInPane[] = [];
47  private filterData: HangStructInPane[] = [];
48
49  private optionLevel: string[] = ['Instant', 'Circumstantial', 'Micro', 'Severe'];
50  private allowTag: Set<string> = new Set();
51  private progressEL: LitProgressBar | null | undefined;
52  private timeOutId: number | undefined;
53
54  /// 框选时段范围时触发
55  set data(selectionParam: SelectionParam) {
56    if (this.hangTbl) {
57      this.hangTbl.recycleDataSource = [];
58      this.filterData = [];
59    }
60    window.clearTimeout(this.timeOutId);
61    queryAllHangs().then((ret) => {
62      const filter = new Set([...selectionParam.hangMapData.keys()].map(key => key.split(' ').at(-1)));
63      ret = ret.filter(struct => (
64        filter.has(`${struct.pid ?? 0}`) &&
65        ((struct.startNS ?? 0) <= selectionParam.rightNs) &&
66        (selectionParam.leftNs <= ((struct.startNS ?? 0) + (struct.dur ?? 0)))
67      ));
68
69      if (ret.length === 0) {
70        this.progressEL!.loading = false;
71      }
72      this.systemHangSource = ret.map(HangStructInPane.new);
73      this.refreshTable();
74    });
75  }
76
77  init(): void {
78    this.levelFilterInput = this.shadowRoot?.querySelector<HTMLSelectElement>('#level-filter');
79    this.hangTableTitle = this.shadowRoot?.querySelector<HTMLDivElement>('#hang-title');
80    this.searchFilterInput = this.shadowRoot?.querySelector<HTMLInputElement>('#search-filter');
81    this.processFilter = this.shadowRoot?.querySelector<HTMLInputElement>('#process-filter');
82    this.spSystemTrace = document.querySelector('body > sp-application')?.shadowRoot?.querySelector<SpSystemTrace>('#sp-system-trace');
83    this.tableTimeHandle = this.delayedRefresh(this.refreshTable);
84    this.tableTitleTimeHandle = this.delayedRefresh(this.refreshHangsTitle);
85    this.hangTbl = this.shadowRoot?.querySelector<LitPageTable>('#tb-hang');
86    this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar;
87    this.hangTbl!.getItemTextColor = (data): string => {
88      const hangData = data as HangStructInPane;
89      return ColorUtils.getHangColor(hangData.type as HangType);
90    };
91    this.hangTbl!.itemTextHandleMap.set('startNS', (startTs) => {
92      // @ts-ignore
93      return ns2Timestamp(startTs);
94    });
95    this.hangTbl!.addEventListener('row-hover', (e): void => {
96      // @ts-ignore
97      let data = e.detail.data as HangStructInPane;
98      if (data) {
99        let pointX: number = ns2x(
100          data.startNS || 0,
101          TraceRow.range!.startNS,
102          TraceRow.range!.endNS,
103          TraceRow.range!.totalNS,
104          new Rect(0, 0, TraceRow.FRAME_WIDTH, 0),
105        );
106        this.traceSheetEl!.systemLogFlag = new Flag(
107          Math.floor(pointX), 0, 0, 0, data.startNS, '#999999', '', true, '',
108        );
109        this.spSystemTrace?.refreshCanvas(false);
110      }
111    });
112    let tbl = this.hangTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table');
113    tbl!.addEventListener('scroll', () => {
114      this.tableTitleTimeHandle?.();
115    });
116  }
117
118  initElements(): void {
119    this.init();
120    this.searchFilterInput!.oninput = (): void => {
121      this.tableTimeHandle?.();
122    };
123    this.processFilter!.oninput = (): void => {
124      this.tableTimeHandle?.();
125    };
126    this.levelFilterInput!.onchange = (): void => {
127      this.tableTimeHandle?.();
128    };
129  }
130
131  connectedCallback(): void {
132    super.connectedCallback();
133    new ResizeObserver((): void => {
134      this.parentElement!.style.overflow = 'hidden';
135      if (this.hangTbl) {
136        // @ts-ignore
137        this.hangTbl.shadowRoot.querySelector('.table').style.height =
138          this.parentElement!.clientHeight - 20 - 45 + 'px';
139      }
140      if (this.filterData.length > 0) {
141        this.refreshTable();
142        this.tableTitleTimeHandle?.();
143      }
144    }).observe(this.parentElement!);
145  }
146
147  disconnectedCallback(): void {
148    super.disconnectedCallback();
149  }
150
151  initHtml(): string {
152    return TabPaneHangHtml;
153  }
154
155  refreshHangTab(): void {
156    let tbl = this.hangTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table');
157    let height = 0;
158    if (tbl) {
159      const trs = tbl.querySelectorAll<HTMLElement>('.tr');
160      trs.forEach((trEl: HTMLElement, index: number): void => {
161        if (index === 0) {
162          let frontTotalRowSize = Math.round((tbl!.scrollTop / trEl.clientHeight) * 100) / 100;
163          if (frontTotalRowSize.toString().indexOf('.') >= 0) {
164            let rowCount = frontTotalRowSize.toString().split('.');
165            height += trEl.clientHeight - (Number(rowCount[1]) / 100) * trEl.clientHeight;
166          }
167        }
168        let allTdEl = trEl.querySelectorAll<HTMLElement>('.td');
169        allTdEl[0].style.color = '#3D88C7';
170        allTdEl[0].style.textDecoration = 'underline';
171        allTdEl[0].style.textDecorationColor = '#3D88C7';
172      });
173    }
174  }
175
176  refreshHangsTitle(): void {
177    let tbl = this.hangTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table');
178    let height = 0;
179    let firstRowHeight = 27;
180    let tableHeadHeight = 26;
181    this.refreshHangTab();
182    if (this.hangTbl && this.hangTbl.currentRecycleList.length > 0) {
183      let startDataIndex = this.hangTbl.startSkip + 1;
184      let endDataIndex = startDataIndex;
185      let crossTopHeight = tbl!.scrollTop % firstRowHeight;
186      let topShowHeight = crossTopHeight === 0 ? 0 : firstRowHeight - crossTopHeight;
187      if (topShowHeight < firstRowHeight * 0.3) {
188        startDataIndex++;
189      }
190      let tableHeight = Number(tbl!.style.height.replace('px', '')) - tableHeadHeight;
191      while (height < tableHeight) {
192        if (firstRowHeight <= 0 || height + firstRowHeight > tableHeight) {
193          break;
194        }
195        height += firstRowHeight;
196        endDataIndex++;
197      }
198      if (tableHeight - height - topShowHeight > firstRowHeight * 0.3) {
199        endDataIndex++;
200      }
201      if (endDataIndex >= this.filterData.length) {
202        endDataIndex = this.filterData.length;
203      } else {
204        endDataIndex = this.hangTbl.startSkip === 0 ? endDataIndex - 1 : endDataIndex;
205      }
206      this.hangTableTitle!.textContent = `Hangs [${this.hangTbl.startSkip === 0 ? 1 : startDataIndex},
207        ${endDataIndex}] / ${this.filterData.length || 0}`;
208    } else {
209      this.hangTableTitle!.textContent = 'Hangs [0, 0] / 0';
210    }
211    if (this.hangTbl!.recycleDataSource.length > 0) {
212      this.progressEL!.loading = false;
213    }
214  }
215
216  initTabSheetEl(traceSheet: TraceSheet): void {
217    this.traceSheetEl = traceSheet;
218    this.levelFilterInput!.selectedIndex = 0;
219    this.allowTag.clear();
220    this.processFilter!.value = '';
221    this.searchFilterInput!.value = '';
222  }
223
224  private updateFilterData(): void {
225    if (this.systemHangSource?.length > 0) {
226      this.filterData = this.systemHangSource.filter((data) => this.isFilterHang(data));
227    }
228    if (this.hangTbl) {
229      // @ts-ignore
230      this.hangTbl.shadowRoot.querySelector('.table').style.height = this.parentElement.clientHeight - 20 - 45 + 'px';
231    }
232    if (this.filterData.length > 0) {
233      this.hangTbl!.recycleDataSource = this.filterData;
234    } else {
235      this.hangTbl!.recycleDataSource = [];
236    }
237    this.refreshHangsTitle();
238  }
239
240  private isFilterHang(data: HangStructInPane): boolean {
241    let type = this.levelFilterInput?.selectedIndex ?? 0;
242    let search = this.searchFilterInput?.value.toLocaleLowerCase() ?? '';
243    let process = this.processFilter?.value.toLocaleLowerCase() ?? '';
244    return (
245      (type === 0 || this.optionLevel.indexOf(data.type) >= type) &&
246      (search === '' || data.caller.toLocaleLowerCase().indexOf(search) >= 0) &&
247      (process === '' || data.pname.toLocaleLowerCase().indexOf(process) >= 0)
248    );
249  }
250
251  private refreshTable(): void {
252    if (this.traceSheetEl) {
253      this.traceSheetEl.systemLogFlag = undefined;
254      this.spSystemTrace?.refreshCanvas(false);
255      this.updateFilterData();
256    }
257  }
258
259  private delayedRefresh(optionFn: Function, dur: number = tableTimeOut): () => void {
260    return (...args: []): void => {
261      window.clearTimeout(this.timeOutId);
262      this.timeOutId = window.setTimeout((): void => {
263        optionFn.apply(this, ...args);
264      }, dur);
265    };
266  }
267}
268
269let defaultIndex: number = 1;
270let tableTimeOut: number = 50;
271
272export class HangStructInPane {
273  startNS: number = 0;
274  dur: string = '0';
275  pname: string = 'Process';
276  type: string;
277
278  sendEventTid: string;
279  sendTime: string;
280  expectHandleTime: string;
281  taskNameId: string;
282  caller: string;
283
284  constructor(parent: HangStruct) {
285    this.startNS = parent.startNS ?? this.startNS;
286    this.dur = getTimeString(parent.dur ?? 0);
287    this.pname = `${parent.pname ?? this.pname} ${parent.pid ?? ''}`.trim();
288    this.type = SpHangChart.calculateHangType(parent.dur ?? 0);
289    [this.sendEventTid, this.sendTime, this.expectHandleTime, this.taskNameId, this.caller] = (parent.content ?? ',0,0,,').split(',').map(i => i.trim());
290    this.sendEventTid = this.sendEventTid.split(':').at(-1)!;
291  }
292
293  static new(parent: HangStruct): HangStructInPane {
294    return new HangStructInPane(parent);
295  }
296}