• 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 { HiSysEventStruct } from '../../../../database/ui-worker/ProcedureWorkerHiSysEvent';
19import { ns2x, Rect } from '../../../../database/ui-worker/ProcedureWorkerCommon';
20import { LitPageTable } from '../../../../../base-ui/table/LitPageTable';
21import { LitTable } from '../../../../../base-ui/table/lit-table';
22import { LitSlicerTrack } from '../../../../../base-ui/slicer/lit-slicer';
23import { TraceRow } from '../../base/TraceRow';
24import { Flag } from '../../timer-shaft/Flag';
25import { TraceSheet } from '../../base/TraceSheet';
26import { SpSystemTrace } from '../../../SpSystemTrace';
27import { ColorUtils } from '../../base/ColorUtils';
28import {queryHiSysEventTabData} from "../../../../database/sql/Perf.sql";
29import {queryRealTime} from "../../../../database/sql/Clock.sql";
30import { TabPaneHiSysEventsHtml } from './TabPaneHisysEvents.html';
31
32@element('tab-hisysevents')
33export class TabPaneHisysEvents extends BaseElement {
34  private hisysEventSource: Array<HiSysEventStruct> = [];
35  private filterDataList: HiSysEventStruct[] = [];
36  private hiSysEventTable: LitPageTable | undefined | null;
37  private currentSelection: SelectionParam | undefined;
38  private domainFilterInput: HTMLInputElement | undefined | null;
39  private eventNameFilterInput: HTMLInputElement | undefined | null;
40  private levelFilter: HTMLSelectElement | undefined | null;
41  private contentFilterInput: HTMLInputElement | undefined | null;
42  private domainTag: Set<string> = new Set();
43  private domainTagDiv: HTMLDivElement | undefined | null;
44  private eventNameTag: Set<string> = new Set();
45  private eventNameTagDiv: HTMLDivElement | undefined | null;
46  private traceSheetEl: TraceSheet | undefined | null;
47  private spSystemTrace: SpSystemTrace | undefined | null;
48  private detailsTbl: LitTable | null | undefined;
49  private boxDetails: HTMLDivElement | null | undefined;
50  private slicerTrack: LitSlicerTrack | null | undefined;
51  private tableElement: HTMLDivElement | undefined | null;
52  private detailbox: HTMLDivElement | null | undefined;
53  private changeInput: HTMLInputElement | null | undefined;
54  private eventTableTitle: HTMLLabelElement | undefined | null;
55  tableTitleTimeHandle: (() => void) | undefined;
56  private currentDetailList: Array<{ key: string; value: string }> = [];
57  private realTime: number = -1;
58  private baseTime: string = '';
59
60  set data(systemEventParam: SelectionParam) {
61    if (systemEventParam === this.currentSelection) {
62      return;
63    }
64    if (this.hiSysEventTable) {
65      this.hiSysEventTable.recycleDataSource = [];
66      this.filterDataList = [];
67    }
68    if (this.detailsTbl) {
69      this.detailsTbl!.recycleDataSource = [];
70    }
71    this.initTabSheetEl();
72    queryRealTime().then((result) => {
73      if (result && result.length > 0) {
74        this.realTime = Math.floor(result[0].ts / millisecond);
75      }
76      queryHiSysEventTabData(systemEventParam.leftNs, systemEventParam.rightNs).then((res) => {
77        this.currentSelection = systemEventParam;
78        systemEventParam.sysAllEventsData = res;
79        this.hiSysEventTable!.recycleDataSource = res;
80        this.hisysEventSource = res;
81        this.updateData();
82      });
83    });
84  }
85
86  queryElements(): void {
87    this.boxDetails = this.shadowRoot?.querySelector<HTMLDivElement>('.box-details');
88    this.hiSysEventTable = this.shadowRoot?.querySelector<LitPageTable>('#tb-hisysevent');
89    this.hiSysEventTable!.getItemTextColor = (data) => {
90      return ColorUtils.getHisysEventColor(data.level);
91    };
92    this.domainTagDiv = this.shadowRoot?.querySelector<HTMLDivElement>('#domainTagFilter');
93    this.eventNameTagDiv = this.shadowRoot?.querySelector<HTMLDivElement>('#eventNameTagFilter');
94    this.domainFilterInput = this.shadowRoot?.querySelector<HTMLInputElement>('#domain-filter');
95    this.eventNameFilterInput = this.shadowRoot?.querySelector<HTMLInputElement>('#event-name-filter');
96    this.levelFilter = this.shadowRoot?.querySelector<HTMLSelectElement>('#level-filter');
97    this.spSystemTrace = document.querySelector('body > sp-application')
98      ?.shadowRoot?.querySelector<SpSystemTrace>('#sp-system-trace');
99    this.traceSheetEl = this.spSystemTrace?.shadowRoot?.querySelector('.trace-sheet');
100    this.contentFilterInput = this.shadowRoot?.querySelector<HTMLInputElement>('#contents-filter');
101    this.changeInput = this.shadowRoot?.querySelector<HTMLInputElement>('#contents-change');
102    this.detailsTbl = this.shadowRoot?.querySelector<LitTable>('#tb-hisysevent-data');
103    this.slicerTrack = this.shadowRoot?.querySelector<LitSlicerTrack>('lit-slicer-track');
104    this.detailbox = this.shadowRoot?.querySelector<HTMLDivElement>('.detail-content');
105    this.tableElement = this.hiSysEventTable?.shadowRoot?.querySelector('.table') as HTMLDivElement;
106    this.eventTableTitle = this.shadowRoot?.querySelector<HTMLLabelElement>('#event-title');
107    this.tableTitleTimeHandle = this.delayedRefresh(this.refreshEventsTitle);
108    this.initHiSysEventListener();
109  }
110
111  private initHiSysEventListener(): void {
112    this.hiSysEventTable!.addEventListener('row-click', (event) => {
113      this.changeInput!.value = '';
114      // @ts-ignore
115      const data = event.detail.data;
116      this.convertData(data);
117      this.hiSysEventTable?.clearAllSelection();
118      data.isSelected = true;
119      this.hiSysEventTable?.setCurrentSelection(data);
120      this.updateDetail(this.baseTime);
121    });
122    this.boxDetails!.addEventListener('click', (ev) => {
123      if (ev.target !== this.hiSysEventTable) {
124        this.hiSysEventTable?.clearAllSelection();
125        this.detailsTbl!.dataSource = [];
126        this.boxDetails!.style.width = '100%';
127        this.detailbox!.style.display = 'none';
128        this.slicerTrack!.style.visibility = 'hidden';
129        this.detailsTbl!.style.paddingLeft = '0px';
130      }
131    });
132    this.hiSysEventTable!.addEventListener('row-hover', (e) => {
133      // @ts-ignore
134      let data = e.detail.data;
135      if (data) {
136        this.drawFlag(data.startTs, '#999999');
137      }
138    });
139    this.hiSysEventTable!.addEventListener('column-click', (evt) => {
140      // @ts-ignore
141      this.sortByColumn(evt.detail);
142    });
143    this.tableElement?.addEventListener('mouseout', () => {
144      this.traceSheetEl!.systemLogFlag = undefined;
145      this.spSystemTrace?.refreshCanvas(false);
146    });
147  }
148
149  initElements(): void {
150    this.queryElements();
151    this.detailsTbl!.addEventListener('row-hover', (e) => {
152      // @ts-ignore
153      let data = e.detail.data;
154      if (data && data.key && this.realTime >= 0) {
155        if (data.key.endsWith('_TIME') || data.key.endsWith('_LATENCY')) {
156          this.drawFlag(data.value, '#999999');
157          return;
158        }
159      }
160      this.traceSheetEl!.systemLogFlag = undefined;
161      this.spSystemTrace?.refreshCanvas(false);
162    });
163  }
164
165  private delayedRefresh(optionFn: Function, dur: number = 50): () => void {
166    let timeOutId: number;
167    return (...args: []): void => {
168      window.clearTimeout(timeOutId);
169      timeOutId = window.setTimeout((): void => {
170        optionFn.apply(this, ...args);
171      }, dur);
172    };
173  }
174
175  private refreshEventsTitle() {
176    let tbl = this.hiSysEventTable?.shadowRoot?.querySelector<HTMLDivElement>('.table');
177    let height = 0;
178    let firstRowHeight = 27;
179    let tableHeadHeight = 26;
180    if (this.hiSysEventTable && this.hiSysEventTable.currentRecycleList.length > 0) {
181      let startDataIndex = this.hiSysEventTable.startSkip + 1;
182      let endDataIndex = startDataIndex;
183      if (height < firstRowHeight * 0.3) {
184        startDataIndex++;
185      }
186      let tableHeight = Number(tbl!.style.height.replace('px', '')) - tableHeadHeight;
187      while (height < tableHeight) {
188        if (firstRowHeight <= 0 || height + firstRowHeight > tableHeight) {
189          break;
190        }
191        height += firstRowHeight;
192        endDataIndex++;
193      }
194      if (tableHeight - height > firstRowHeight * 0.3) {
195        endDataIndex++;
196      }
197      if (endDataIndex >= this.filterDataList.length) {
198        endDataIndex = this.filterDataList.length;
199      } else {
200        endDataIndex = this.hiSysEventTable.startSkip === 0 ? endDataIndex - 1 : endDataIndex;
201      }
202      this.eventTableTitle!.textContent = `HisysEvents [${this.hiSysEventTable.startSkip === 0 ? 1 : startDataIndex},
203        ${endDataIndex}] / ${this.filterDataList.length || 0}`;
204    } else {
205      this.eventTableTitle!.textContent = 'HisysEvents [0, 0] / 0';
206    }
207  }
208
209  initTabSheetEl(): void {
210    this.levelFilter!.selectedIndex = 0;
211    this.domainFilterInput!.value = '';
212    this.domainTagDiv!.innerHTML = '';
213    this.domainTag.clear();
214    this.eventNameFilterInput!.value = '';
215    this.eventNameTagDiv!.innerHTML = '';
216    this.eventNameTag.clear();
217    this.contentFilterInput!.value = '';
218    this.boxDetails!.style.width = '100%';
219    this.detailbox!.style.display = 'none';
220    this.slicerTrack!.style.visibility = 'hidden';
221    this.detailsTbl!.style.paddingLeft = '0px';
222  }
223
224  initHtml(): string {
225    return TabPaneHiSysEventsHtml;
226  }
227
228  connectedCallback(): void {
229    this.domainFilterInput?.addEventListener('keyup', this.domainKeyEvent);
230    this.eventNameFilterInput?.addEventListener('keyup', this.eventNameKeyEvent);
231    this.contentFilterInput?.addEventListener('input', this.filterInputEvent);
232    this.levelFilter?.addEventListener('change', this.filterInputEvent);
233    this.domainTagDiv?.addEventListener('click', this.domainDivClickEvent);
234    this.eventNameTagDiv?.addEventListener('click', this.eventNameDivClickEvent);
235    this.changeInput?.addEventListener('input', this.changeInputEvent);
236    new ResizeObserver(() => {
237      this.tableElement!.style.height = `${this.parentElement!.clientHeight - 20 - 35}px`;
238      this.hiSysEventTable?.reMeauseHeight();
239      this.detailsTbl!.style.height = `${this.parentElement!.clientHeight - 30}px`;
240      this.parentElement!.style.overflow = 'hidden';
241      this.detailsTbl?.reMeauseHeight();
242      this.updateData();
243      this.tableTitleTimeHandle?.();
244    }).observe(this.parentElement!);
245    let tbl = this.hiSysEventTable?.shadowRoot?.querySelector<HTMLDivElement>('.table');
246    tbl!.addEventListener('scroll', () => {
247      this.tableTitleTimeHandle?.();
248    });
249  }
250
251  disconnectedCallback(): void {
252    super.disconnectedCallback();
253    this.domainFilterInput?.removeEventListener('keyup', this.domainKeyEvent);
254    this.eventNameFilterInput?.removeEventListener('keyup', this.eventNameKeyEvent);
255    this.contentFilterInput?.removeEventListener('input', this.filterInputEvent);
256    this.levelFilter?.addEventListener('change', this.filterInputEvent);
257    this.changeInput?.addEventListener('input', this.changeInputEvent);
258    this.domainTagDiv?.removeEventListener('click', this.domainDivClickEvent);
259    this.eventNameTagDiv?.removeEventListener('click', this.eventNameDivClickEvent);
260  }
261
262  filterInputEvent = (): void => {
263    this.updateData();
264  };
265
266  domainDivClickEvent = (ev: Event): void => {
267    // @ts-ignore
268    let parentNode = ev.target.parentNode;
269    if (parentNode && this.domainTagDiv!.contains(parentNode)) {
270      this.domainTagDiv!.removeChild(parentNode);
271      this.domainTag['delete'](parentNode.textContent.trim().toLowerCase());
272    }
273    this.updateData();
274  };
275
276  eventNameDivClickEvent = (ev: Event): void => {
277    // @ts-ignore
278    let parentNode = ev.target.parentNode;
279    if (parentNode && this.eventNameTagDiv!.contains(parentNode)) {
280      this.eventNameTagDiv!.removeChild(parentNode);
281      this.eventNameTag['delete'](parentNode.textContent.trim().toLowerCase());
282    }
283    this.updateData();
284  };
285
286  domainKeyEvent = (e: KeyboardEvent): void => {
287    let domainValue = this.domainFilterInput!.value.trim();
288    if (e.key === 'Enter') {
289      if (domainValue !== '' && !this.domainTag.has(domainValue.toLowerCase()) && this.domainTag.size < 10) {
290        let tagElement = this.buildTag(domainValue);
291        this.domainTag.add(domainValue.toLowerCase());
292        this.domainTagDiv!.append(tagElement);
293        this.domainFilterInput!.value = '';
294      }
295    } else if (e.key === 'Backspace') {
296      let index = this.domainTagDiv!.childNodes.length - 1;
297      if (index >= 0 && domainValue === '') {
298        let childNode = this.domainTagDiv!.childNodes[index];
299        this.domainTagDiv!.removeChild(childNode);
300        this.domainTag['delete'](childNode.textContent!.trim().toLowerCase());
301      }
302    }
303    this.updateData();
304  };
305
306  private buildTag(domainValue: string): HTMLDivElement {
307    let tagElement = document.createElement('div');
308    tagElement.className = 'tagElement';
309    tagElement.id = domainValue;
310    let tag = document.createElement('div');
311    tag.className = 'tag';
312    tag.innerHTML = domainValue;
313    let closeButton = document.createElement('lit-icon');
314    closeButton.setAttribute('name', 'close-light');
315    closeButton.style.color = '#FFFFFF';
316    tagElement.append(tag);
317    tagElement.append(closeButton);
318    return tagElement;
319  }
320
321  eventNameKeyEvent = (e: KeyboardEvent): void => {
322    let eventNameValue = this.eventNameFilterInput!.value.trim();
323    if (e.key === 'Enter') {
324      if (eventNameValue !== '' && !this.eventNameTag.has(eventNameValue.toLowerCase()) && this.eventNameTag.size < 10) {
325        let tagElement = this.buildTag(eventNameValue);
326        this.eventNameTag!.add(eventNameValue.toLowerCase());
327        this.eventNameTagDiv!.append(tagElement);
328        this.eventNameFilterInput!.value = '';
329      }
330    } else if (e.key === 'Backspace') {
331      let index = this.eventNameTagDiv!.childNodes.length - 1;
332      if (index >= 0 && eventNameValue === '') {
333        let childNode = this.eventNameTagDiv!.childNodes[index];
334        this.eventNameTagDiv!.removeChild(childNode);
335        this.eventNameTag['delete'](childNode.textContent!.trim().toLowerCase());
336      }
337    }
338    this.updateData();
339  };
340
341  updateData(): void {
342    if (this.hisysEventSource.length > 0) {
343      this.filterDataList = this.hisysEventSource.filter((data) => this.filterData(data));
344    }
345    if (this.filterDataList.length > 0) {
346      this.hiSysEventTable!.recycleDataSource = this.filterDataList;
347    } else {
348      this.hiSysEventTable!.recycleDataSource = [];
349    }
350    this.refreshEventsTitle();
351  }
352
353  filterData(data: HiSysEventStruct): boolean {
354    let level = this.levelFilter?.value;
355    let contentsValue = this.contentFilterInput?.value.toLowerCase() || '';
356    contentsValue = contentsValue.replace(/\s/g, '');
357    return (
358      (level === 'ALL' || data.level! === level) &&
359      (this.domainTag.size === 0 || this.domainTag.has(data.domain!.toLowerCase())) &&
360      (this.eventNameTag.size === 0 || this.eventNameTag.has(data.eventName!.toLowerCase())) &&
361      (contentsValue === '' || data.contents!.toLowerCase().replace(/\s/g, '').indexOf(contentsValue) >= 0)
362    );
363  }
364
365  changeInputEvent = (): void => {
366    const changeValue = this.changeInput!.value;
367    const currentValue = changeValue;
368    if (!/^[0-9]*$/.test(changeValue) || isNaN(Number(currentValue))) {
369      this.changeInput!.value = '';
370      this.updateDetail(this.baseTime);
371    } else {
372      this.updateDetail(this.changeInput!.value);
373    }
374  };
375
376  updateDetail(baseTime: string): void {
377    const latencySuffix = '_LATENCY';
378    let detailList: Array<{ key: string; value: string }> = [];
379    this.currentDetailList.forEach((item) => {
380      const latencyValue = item.key && item.key.endsWith(latencySuffix) ? item.value + Number(baseTime) : item.value;
381      detailList.push({
382        key: item.key,
383        value: latencyValue,
384      });
385    });
386    this.detailsTbl!.recycleDataSource = detailList;
387  }
388
389  convertData = (data: HiSysEventStruct): void => {
390    this.baseTime = '';
391    this.currentDetailList = [
392      {
393        key: 'key',
394        value: 'value',
395      },
396    ];
397    const content = JSON.parse(data.contents ?? '{}');
398    if (content && typeof content === 'object') {
399      let isFirstTime = true;
400      let keyList = Object.keys(content);
401      keyList.forEach((key) => {
402        const value: string = content[key];
403        let contentValue = value;
404        if (key.endsWith('_TIME')) {
405          if (!isNaN(Number(value))) {
406            contentValue = ((Number(value) - this.realTime) * millisecond).toString();
407            if (this.realTime < 0) {
408              contentValue = value;
409            }
410            if (isFirstTime) {
411              this.baseTime = contentValue;
412              isFirstTime = false;
413            }
414          }
415          if (key === 'INPUT_TIME') {
416            this.baseTime = contentValue;
417            isFirstTime = false;
418          }
419        }
420        this.currentDetailList.push({
421          key: key,
422          value: contentValue,
423        });
424      });
425    }
426    this.changeInput!.value = `${this.baseTime}`;
427    this.detailsTbl!.recycleDataSource = this.currentDetailList;
428    this.slicerTrack!.style.visibility = 'visible';
429    this.detailsTbl!.style.paddingLeft = '20px';
430    this.boxDetails!.style.width = '65%';
431    this.detailbox!.style.display = 'block';
432  };
433
434  sortByColumn(framesDetail: { sort: number; key: string }): void {
435    let compare = function (property: string, sort: number, type: string) {
436      return function (eventLeftData: HiSysEventStruct, eventRightData: HiSysEventStruct): number {
437        let firstSortNumber: number = -1;
438        let SecondSortNumber: number = 1;
439        let thirdSortNumber: number = 2;
440        // @ts-ignore
441        let rightEventData = eventRightData[property];
442        // @ts-ignore
443        let leftEventData = eventLeftData[property];
444        if (type === 'number') {
445          return sort === thirdSortNumber
446            ? parseFloat(rightEventData) - parseFloat(leftEventData)
447            : parseFloat(leftEventData) - parseFloat(rightEventData);
448        } else {
449          if (rightEventData > leftEventData) {
450            return sort === thirdSortNumber ? SecondSortNumber : firstSortNumber;
451          } else {
452            if (rightEventData === leftEventData) {
453              return 0;
454            } else {
455              return sort === thirdSortNumber ? firstSortNumber : SecondSortNumber;
456            }
457          }
458        }
459      };
460    };
461    this.hisysEventSource.sort(compare(framesDetail.key, framesDetail.sort, 'number'));
462    this.hiSysEventTable!.recycleDataSource = this.hisysEventSource;
463  }
464
465  drawFlag(value: number, color: string): void {
466    let pointX: number = ns2x(
467      value || 0,
468      TraceRow.range!.startNS,
469      TraceRow.range!.endNS,
470      TraceRow.range!.totalNS,
471      new Rect(0, 0, TraceRow.FRAME_WIDTH, 0)
472    );
473    this.traceSheetEl!.systemLogFlag = new Flag(Math.floor(pointX), 0, 0, 0, value!, color, '', true, '');
474    this.spSystemTrace?.refreshCanvas(false);
475  }
476}
477
478const millisecond = 1000_000;
479