• 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 '../base-ui/menu/LitMainMenu';
18import '../base-ui/icon/LitIcon';
19import { SpMetrics } from './component/SpMetrics';
20import { SpHelp } from './component/SpHelp';
21import './component/SpHelp';
22import { SpQuerySQL } from './component/SpQuerySQL';
23import './component/SpQuerySQL';
24import { SpSystemTrace } from './component/SpSystemTrace';
25import { LitMainMenu, MenuGroup, MenuItem } from '../base-ui/menu/LitMainMenu';
26import { SpInfoAndStats } from './component/SpInfoAndStas';
27import '../base-ui/progress-bar/LitProgressBar';
28import { LitProgressBar } from '../base-ui/progress-bar/LitProgressBar';
29import { SpRecordTrace } from './component/SpRecordTrace';
30import { SpWelcomePage } from './component/SpWelcomePage';
31import { LitSearch } from './component/trace/search/Search';
32import {
33  getThreadPoolTraceBuffer,
34  getThreadPoolTraceBufferCacheKey,
35  setThreadPoolTraceBuffer,
36  threadPool,
37  threadPool2,
38} from './database/SqlLite';
39import './component/trace/search/Search';
40import './component/SpWelcomePage';
41import './component/SpSystemTrace';
42import './component/SpRecordTrace';
43import './component/SpMetrics';
44import './component/SpInfoAndStas';
45import './component/trace/base/TraceRow';
46import './component/schedulingAnalysis/SpSchedulingAnalysis';
47import { error, info, log } from '../log/Log';
48import { LitMainMenuGroup } from '../base-ui/menu/LitMainMenuGroup';
49import { LitMainMenuItem } from '../base-ui/menu/LitMainMenuItem';
50import { LitIcon } from '../base-ui/icon/LitIcon';
51import { TraceRow } from './component/trace/base/TraceRow';
52import { SpSchedulingAnalysis } from './component/schedulingAnalysis/SpSchedulingAnalysis';
53import './component/trace/base/TraceRowConfig';
54import { TraceRowConfig } from './component/trace/base/TraceRowConfig';
55import { ColorUtils } from './component/trace/base/ColorUtils';
56import { SpStatisticsHttpUtil } from '../statistics/util/SpStatisticsHttpUtil';
57import { FlagsConfig, SpFlags } from './component/SpFlags';
58import './component/SpFlags';
59import './component/trace/base/CustomThemeColor';
60import { CustomThemeColor, Theme } from './component/trace/base/CustomThemeColor';
61import { convertPool } from './database/Convert';
62import { LongTraceDBUtils } from './database/LongTraceDBUtils';
63import { type SpKeyboard } from './component/SpKeyboard';
64import './component/SpKeyboard';
65import { parseKeyPathJson } from './component/Utils';
66import { Utils } from './component/trace/base/Utils';
67import {
68  applicationHtml,
69  clearTraceFileCache,
70  findFreeSizeAlgorithm,
71  getCurrentDataTime,
72  indexedDataToBufferData,
73  postLog,
74  readTraceFileBuffer,
75  TraceMode,
76} from './SpApplicationPublicFunc';
77import { queryExistFtrace } from './database/sql/SqlLite.sql';
78import '../base-ui/chart/scatter/LitChartScatter';
79import { SpThirdParty } from './component/SpThirdParty';
80import './component/SpThirdParty';
81import { cancelCurrentTraceRowHighlight } from './component/SpSystemTrace.init';
82
83@element('sp-application')
84export class SpApplication extends BaseElement {
85  private static loadingProgress: number = 0;
86  private static progressStep: number = 2;
87  longTraceHeadMessageList: Array<{
88    pageNum: number;
89    data: ArrayBuffer;
90  }> = [];
91
92  longTraceDataList: Array<{
93    fileType: string;
94    index: number;
95    pageNum: number;
96    startOffsetSize: number;
97    endOffsetSize: number;
98  }> = [];
99
100  longTraceTypeMessageMap:
101    | Map<
102      number,
103      Array<{
104        fileType: string;
105        startIndex: number;
106        endIndex: number;
107        size: number;
108      }>
109    >
110    | undefined
111    | null;
112  static skinChange: Function | null | undefined = null;
113  static skinChange2: Function | null | undefined = null;
114  skinChangeArray: Array<Function> = [];
115  private rootEL: HTMLDivElement | undefined | null;
116  private headerDiv: HTMLDivElement | undefined | null;
117  private spWelcomePage: SpWelcomePage | undefined | null;
118  private spMetrics: SpMetrics | undefined | null;
119  private spQuerySQL: SpQuerySQL | undefined | null;
120  private spInfoAndStats: SpInfoAndStats | undefined | null;
121  private spSystemTrace: SpSystemTrace | undefined | null;
122  private spHelp: SpHelp | undefined | null;
123  private spKeyboard: SpKeyboard | undefined | null;
124  private spFlags: SpFlags | undefined | null;
125  private spRecordTrace: SpRecordTrace | undefined | null;
126  private spRecordTemplate: SpRecordTrace | undefined | null;
127  private spSchedulingAnalysis: SpSchedulingAnalysis | undefined | null;
128  private mainMenu: LitMainMenu | undefined | null;
129  private menu: HTMLDivElement | undefined | null;
130  private progressEL: LitProgressBar | undefined | null;
131  private litSearch: LitSearch | undefined | null;
132  private litRecordSearch: LitSearch | undefined | null;
133  private sidebarButton: HTMLDivElement | undefined | null;
134  private chartFilter: TraceRowConfig | undefined | null;
135  private cutTraceFile: HTMLImageElement | undefined | null;
136  private exportRecord: LitIcon | undefined | null;
137  private longTracePage: HTMLDivElement | undefined | null;
138  private customColor: CustomThemeColor | undefined | null;
139  private filterConfig: LitIcon | undefined | null;
140  private configClose: LitIcon | undefined | null;
141  private spThirdParty: SpThirdParty | undefined | null;
142  // 关键路径标识
143  private importConfigDiv: HTMLInputElement | undefined | null;
144  private closeKeyPath: HTMLDivElement | undefined | null;
145  private importFileBt: HTMLInputElement | undefined | null;
146  private contentLeftOption: HTMLDivElement | undefined | null;
147  private contentCenterOption: HTMLDivElement | undefined | null;
148  private contentRightOption: HTMLDivElement | undefined | null;
149  private childComponent: Array<unknown> | undefined | null;
150  private keyCodeMap = {
151    61: true,
152    107: true,
153    109: true,
154    173: true,
155    187: true,
156    189: true,
157  };
158  private traceFileName: string | undefined;
159  private markJson: string | undefined;
160  colorTransiton?: NodeJS.Timeout;
161  static isLongTrace: boolean = false;
162  fileTypeList: string[] = ['ebpf', 'arkts', 'hiperf'];
163  private pageTimStamp: number = 0;
164  private currentPageNum: number = 1;
165  private currentDataTime: string[] = [];
166  static traceType: String = '';
167
168  static get observedAttributes(): Array<string> {
169    return ['server', 'sqlite', 'wasm', 'dark', 'vs', 'query-sql', 'subsection'];
170  }
171
172  get dark(): boolean {
173    return this.hasAttribute('dark');
174  }
175
176  set dark(value) {
177    if (value) {
178      this.rootEL!.classList.add('dark');
179      this.setAttribute('dark', '');
180    } else {
181      this.rootEL!.classList.remove('dark');
182      this.removeAttribute('dark');
183    }
184    if (this.skinChangeArray.length > 0) {
185      this.skinChangeArray.forEach((item) => item(value));
186    }
187    if (SpApplication.skinChange) {
188      SpApplication.skinChange(value);
189    }
190    if (SpApplication.skinChange2) {
191      SpApplication.skinChange2(value);
192    }
193
194    if (this.spHelp) {
195      this.spHelp.dark = value;
196    }
197  }
198
199  get sqlite(): boolean {
200    return this.hasAttribute('sqlite');
201  }
202
203  get wasm(): boolean {
204    return this.hasAttribute('wasm');
205  }
206
207  set wasm(isWasm: boolean) {
208    if (isWasm) {
209      this.setAttribute('wasm', '');
210    } else {
211      this.hasAttribute('wasm') && this.removeAttribute('wasm');
212    }
213  }
214
215  get server(): boolean {
216    return this.hasAttribute('server');
217  }
218
219  set server(s: boolean) {
220    if (s) {
221      this.setAttribute('server', '');
222    } else {
223      this.removeAttribute('server');
224    }
225  }
226
227  get querySql(): boolean {
228    return this.hasAttribute('query-sql');
229  }
230
231  set querySql(isShowMetric) {
232    if (isShowMetric) {
233      this.setAttribute('query-sql', '');
234    } else {
235      this.removeAttribute('query-sql');
236    }
237  }
238
239  set search(search: boolean) {
240    if (search) {
241      this.setAttribute('search', '');
242    } else {
243      this.removeAttribute('search');
244    }
245  }
246
247  get search(): boolean {
248    return this.hasAttribute('search');
249  }
250
251  addSkinListener(handler: Function): void {
252    this.skinChangeArray.push(handler);
253  }
254
255  removeSkinListener(handler: Function): void {
256    this.skinChangeArray.splice(this.skinChangeArray.indexOf(handler), 1);
257  }
258
259  initHtml(): string {
260    return applicationHtml;
261  }
262
263  initPlugin(): void {
264    SpStatisticsHttpUtil.initStatisticsServerConfig();
265    SpStatisticsHttpUtil.addUserVisitAction('visit');
266    LongTraceDBUtils.getInstance().createDBAndTable().then();
267  }
268
269  initElements(): void {
270    this.wasm = true;
271    this.initPlugin();
272    this.querySql = true;
273    this.rootEL = this.shadowRoot!.querySelector<HTMLDivElement>('.root');
274    this.headerDiv = this.shadowRoot!.querySelector<HTMLDivElement>('.search-vessel');
275    this.spWelcomePage = this.shadowRoot!.querySelector('#sp-welcome') as SpWelcomePage;
276    this.spMetrics = this.shadowRoot!.querySelector<SpMetrics>('#sp-metrics') as SpMetrics; // new SpMetrics();
277    this.spQuerySQL = this.shadowRoot!.querySelector<SpQuerySQL>('#sp-query-sql') as SpQuerySQL; // new SpQuerySQL();
278    this.spInfoAndStats = this.shadowRoot!.querySelector<SpInfoAndStats>('#sp-info-and-stats'); // new SpInfoAndStats();
279    this.spSystemTrace = this.shadowRoot!.querySelector<SpSystemTrace>('#sp-system-trace');
280    this.spHelp = this.shadowRoot!.querySelector<SpHelp>('#sp-help');
281    this.spKeyboard = this.shadowRoot!.querySelector<SpKeyboard>('#sp-keyboard') as SpKeyboard;
282    this.spFlags = this.shadowRoot!.querySelector<SpFlags>('#sp-flags') as SpFlags;
283    this.spRecordTrace = this.shadowRoot!.querySelector<SpRecordTrace>('#sp-record-trace');
284    this.spRecordTemplate = this.shadowRoot!.querySelector<SpRecordTrace>('#sp-record-template');
285    this.spSchedulingAnalysis = this.shadowRoot!.querySelector<SpSchedulingAnalysis>('#sp-scheduling-analysis');
286    this.mainMenu = this.shadowRoot?.querySelector('#main-menu') as LitMainMenu;
287    this.menu = this.mainMenu.shadowRoot?.querySelector('.menu-button') as HTMLDivElement;
288    this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar;
289    this.litSearch = this.shadowRoot?.querySelector('#lit-search') as LitSearch;
290    this.litRecordSearch = this.shadowRoot?.querySelector('#lit-record-search') as LitSearch;
291    this.sidebarButton = this.shadowRoot?.querySelector('.sidebar-button');
292    this.chartFilter = this.shadowRoot?.querySelector('.chart-filter') as TraceRowConfig;
293    this.cutTraceFile = this.shadowRoot?.querySelector('.cut-trace-file') as HTMLImageElement;
294    this.exportRecord = this.shadowRoot?.querySelector('.export-record') as LitIcon;
295    this.longTracePage = this.shadowRoot!.querySelector('.long_trace_page') as HTMLDivElement;
296    this.customColor = this.shadowRoot?.querySelector('.custom-color') as CustomThemeColor;
297    this.filterConfig = this.shadowRoot?.querySelector('.filter-config') as LitIcon;
298    this.spThirdParty = this.shadowRoot!.querySelector('#sp-third-party') as SpThirdParty;
299    this.configClose = this.shadowRoot
300      ?.querySelector<HTMLElement>('.chart-filter')!
301      .shadowRoot?.querySelector<LitIcon>('.config-close');
302    this.importConfigDiv = this.shadowRoot?.querySelector<HTMLInputElement>('#import-key-path');
303    this.closeKeyPath = this.shadowRoot?.querySelector<HTMLDivElement>('#close-key-path');
304    this.importFileBt = this.shadowRoot?.querySelector<HTMLInputElement>('#import-config');
305    this.contentRightOption = this.shadowRoot?.querySelector<HTMLDivElement>('.content-right-option');
306    this.contentLeftOption = this.shadowRoot?.querySelector<HTMLDivElement>('.content-left-option');
307    this.contentCenterOption = this.shadowRoot?.querySelector<HTMLDivElement>('.content-center-option');
308    this.initElementsAttr();
309    this.initEvents();
310    this.initRecordEvents();
311    this.initCustomEvents();
312    this.initCustomColorHandler();
313    this.initImportConfigEvent();
314    this.initSlideMenuEvents();
315    this.resetMenus();
316    this.initGlobalEvents();
317    this.initDocumentListener();
318    this.initElementsEnd();
319  }
320
321  private initElementsEnd(): void {
322    let urlParams = new URL(window.location.href).searchParams;
323    if (urlParams && urlParams.get('trace') && urlParams.get('link')) {
324      this.openLineFileHandler(urlParams);
325    } else if (urlParams && urlParams.get('action')) {
326      this.helpClick(urlParams!);
327    } else {
328      this.openMenu(true);
329    }
330  }
331
332  private helpClick(urlParams: URLSearchParams): void {
333    if (urlParams.get('action') === 'help') {
334      SpStatisticsHttpUtil.addOrdinaryVisitAction({
335        event: 'help_page',
336        action: 'help_doc',
337      });
338      this.spHelp!.dark = this.dark;
339      this.showContent(this.spHelp!);
340    } else if (urlParams.get('action')!.length > 4) {
341      this.showContent(this.spHelp!);
342    }
343  }
344
345  private initElementsAttr(): void {
346    this.mainMenu!.setAttribute('main_menu', '1');
347    this.chartFilter!.setAttribute('mode', '');
348    this.chartFilter!.setAttribute('hidden', '');
349    this.customColor!.setAttribute('mode', '');
350    this.customColor!.setAttribute('hidden', '');
351    this.childComponent = [
352      this.spSystemTrace,
353      this.spRecordTrace,
354      this.spWelcomePage,
355      this.spMetrics,
356      this.spQuerySQL,
357      this.spSchedulingAnalysis,
358      this.spInfoAndStats,
359      this.spHelp,
360      this.spRecordTemplate,
361      this.spFlags,
362      this.spKeyboard,
363      this.spThirdParty,
364    ];
365  }
366
367  private openLongTraceFile(ev: unknown, isRecordTrace: boolean = false): void {
368    this.returnOriginalUrl();
369    this.wasm = true;
370    this.openFileInit(true);
371    // @ts-ignore
372    let detail = ev.detail;
373    let initRes = this.longTraceFileInit(isRecordTrace, detail);
374    if (!isRecordTrace && initRes) {
375      let readSize = 0;
376      let timStamp = new Date().getTime();
377      const { traceTypePage, allFileSize, normalTraceNames, specialTraceNames } = initRes;
378      if (normalTraceNames.length <= 0) {
379        return;
380      }
381      const readFiles = async (
382        files: FileList,
383        traceTypePage: Array<number>,
384        normalNames: Array<string>,
385        specialNames: Array<string>
386      ): Promise<unknown> => {
387        const promises = Array.from(files).map((file) => {
388          if (normalNames.indexOf(file.name.toLowerCase()) >= 0) {
389            return this.longTraceFileRead(file, true, traceTypePage, readSize, timStamp, allFileSize);
390          } else if (specialNames.indexOf(file.name.toLowerCase()) >= 0) {
391            return this.longTraceFileRead(file, false, traceTypePage, readSize, timStamp, allFileSize);
392          } else {
393            return;
394          }
395        });
396        return Promise.all(promises);
397      };
398      this.litSearch!.setPercent('Read in file: ', 1);
399      readFiles(detail, traceTypePage, normalTraceNames, specialTraceNames).then(() => {
400        this.litSearch!.setPercent('Cut in file: ', 1);
401        this.sendCutFileMessage(timStamp);
402      });
403    }
404  }
405
406  private longTraceFileRead = async (
407    file: File,
408    isNormalType: boolean,
409    traceTypePage: Array<number>,
410    readSize: number,
411    timStamp: number,
412    allFileSize: number
413  ): Promise<boolean> => {
414    info('reading long trace file ', file.name);
415    return new Promise((resolve, reject) => {
416      let fr = new FileReader();
417      let message = { fileType: '', startIndex: 0, endIndex: 0, size: 0 };
418      info('Parse long trace using wasm mode ');
419      const { fileType, pageNumber } = this.getFileTypeAndPages(file.name, isNormalType, traceTypePage);
420      let chunk = 48 * 1024 * 1024;
421      let offset = 0;
422      let sliceLen = 0;
423      let index = 1;
424      fr.onload = (): void => {
425        let data = fr.result as ArrayBuffer;
426        LongTraceDBUtils.getInstance()
427          .addLongTableData(data, fileType, timStamp, pageNumber, index, offset, sliceLen)
428          .then(() => {
429            this.longTraceFileReadMessagePush(index, isNormalType, pageNumber, offset, sliceLen, fileType, data);
430            offset += sliceLen;
431            if (offset < file.size) {
432              index++;
433            }
434            continueReading();
435          });
436      };
437      const continueReading = (): void => {
438        if (offset >= file.size) {
439          message.endIndex = index;
440          message.size = file.size;
441          this.longTraceFileReadMessageHandler(pageNumber, message);
442          resolve(true);
443          return;
444        }
445        if (index === 1) {
446          message.fileType = fileType;
447          message.startIndex = index;
448        }
449        sliceLen = Math.min(file.size - offset, chunk);
450        let slice = file.slice(offset, offset + sliceLen);
451        readSize += slice.size;
452        let percentValue = ((readSize * 100) / allFileSize).toFixed(2);
453        this.litSearch!.setPercent('Read in file: ', Number(percentValue));
454        fr.readAsArrayBuffer(slice);
455      };
456      continueReading();
457      fr.onerror = (): void => reject(false);
458      info('read over long trace file ', file.name);
459    });
460  };
461
462  getFileTypeAndPages(
463    fileName: string,
464    isNormalType: boolean,
465    traceTypePage: Array<number>
466  ): {
467    fileType: string;
468    pageNumber: number;
469  } {
470    let fileType = 'trace';
471    let pageNumber = 0;
472    let firstLastIndexOf = fileName.lastIndexOf('.');
473    let firstText = fileName.slice(0, firstLastIndexOf);
474    let resultLastIndexOf = firstText.lastIndexOf('_');
475    let searchResult = firstText.slice(resultLastIndexOf + 1, firstText.length);
476    if (isNormalType) {
477      pageNumber = traceTypePage.lastIndexOf(Number(searchResult));
478    } else {
479      fileType = searchResult;
480    }
481    return { fileType, pageNumber };
482  }
483
484  private longTraceFileInit(
485    isRecordTrace: boolean,
486    detail: unknown
487  ):
488    | {
489      traceTypePage: number[];
490      allFileSize: number;
491      normalTraceNames: string[];
492      specialTraceNames: string[];
493    }
494    | undefined {
495    if (!this.wasm) {
496      this.progressEL!.loading = false;
497      return;
498    }
499    if (this.longTracePage) {
500      this.longTracePage.style.display = 'none';
501      this.litSearch!.style.marginLeft = '0px';
502      this.shadowRoot!.querySelector('.page-number-list')!.innerHTML = '';
503    }
504    this.currentPageNum = 1;
505    if (isRecordTrace) {
506      //@ts-ignore
507      this.sendCutFileMessage(detail.timeStamp);
508      return undefined;
509    } else {
510      this.longTraceHeadMessageList = [];
511      this.longTraceTypeMessageMap = undefined;
512      this.longTraceDataList = [];
513      let traceTypePage: Array<number> = [];
514      let allFileSize = 0;
515      let normalTraceNames: Array<string> = [];
516      let specialTraceNames: Array<string> = []; //@ts-ignore
517      for (let index = 0; index < detail.length; index++) {
518        //@ts-ignore
519        let file = detail[index];
520        let fileName = file.name as string;
521        allFileSize += file.size;
522        let specialMatch = fileName.match(/_(arkts|ebpf|hiperf)\.htrace$/);
523        let normalMatch = fileName.match(/_\d{8}_\d{6}_\d+\.htrace$/);
524        if (normalMatch) {
525          normalTraceNames.push(fileName);
526          let fileNameStr = fileName.split('.')[0];
527          let pageMatch = fileNameStr.match(/\d+$/);
528          if (pageMatch) {
529            traceTypePage.push(Number(pageMatch[0]));
530          }
531        } else if (specialMatch) {
532          specialTraceNames.push(fileName);
533        }
534      }
535      if (normalTraceNames.length <= 0) {
536        this.traceFileLoadFailedHandler('No large trace files exists in the folder!');
537      }
538      traceTypePage.sort((leftNum: number, rightNum: number) => leftNum - rightNum);
539      return { traceTypePage, allFileSize, normalTraceNames, specialTraceNames };
540    }
541  }
542
543  longTraceFileReadMessagePush(
544    index: number,
545    isNormalType: boolean,
546    pageNumber: number,
547    offset: number,
548    sliceLen: number,
549    fileType: string,
550    data: ArrayBuffer
551  ): void {
552    if (index === 1 && isNormalType) {
553      this.longTraceHeadMessageList.push({
554        pageNum: pageNumber,
555        data: data.slice(offset, 1024),
556      });
557    }
558    this.longTraceDataList.push({
559      index: index,
560      fileType: fileType,
561      pageNum: pageNumber,
562      startOffsetSize: offset,
563      endOffsetSize: offset + sliceLen,
564    });
565  }
566
567  longTraceFileReadMessageHandler(pageNumber: number, message: unknown): void {
568    if (this.longTraceTypeMessageMap) {
569      if (this.longTraceTypeMessageMap?.has(pageNumber)) {
570        let oldTypeList = this.longTraceTypeMessageMap?.get(pageNumber);
571        //@ts-ignore
572        oldTypeList?.push(message);
573        this.longTraceTypeMessageMap?.set(pageNumber, oldTypeList!);
574      } else {
575        //@ts-ignore
576        this.longTraceTypeMessageMap?.set(pageNumber, [message]);
577      }
578    } else {
579      this.longTraceTypeMessageMap = new Map();
580      //@ts-ignore
581      this.longTraceTypeMessageMap.set(pageNumber, [message]);
582    }
583  }
584
585  private openTraceFile(ev: unknown, isClickHandle?: boolean): void {
586    this.returnOriginalUrl();
587    this.removeAttribute('custom-color');
588    this.chartFilter!.setAttribute('hidden', '');
589    this.customColor!.setAttribute('hidden', '');
590    this.longTracePage!.style.display = 'none';
591    this.litSearch!.style.marginLeft = '0px';
592    let pageListDiv = this.shadowRoot?.querySelector('.page-number-list') as HTMLDivElement;
593    pageListDiv.innerHTML = '';
594    this.openFileInit();
595    if (this.importConfigDiv && this.closeKeyPath) {
596      this.importConfigDiv.style.display = 'none';
597      this.closeKeyPath.style.display = 'none';
598    }
599    //@ts-ignore
600    let fileName = ev.name;
601    this.traceFileName = fileName;
602    let showFileName = fileName.lastIndexOf('.') === -1 ? fileName : fileName.substring(0, fileName.lastIndexOf('.'));
603    TraceRow.rangeSelectObject = undefined;
604    //@ts-ignore
605    let typeHeader = ev.slice(0, 6);
606    let reader: FileReader | null = new FileReader();
607    reader.readAsText(typeHeader);
608    reader.onloadend = (event): void => {
609      let headerStr: string = `${reader?.result}`;
610      SpApplication.traceType = headerStr;
611      if (headerStr.indexOf('SQLite') === 0) {
612        info('Parse trace headerStr sql mode');
613        this.wasm = false;
614        //@ts-ignore
615        this.handleSqliteMode(ev, showFileName, ev.size, fileName);
616      } else {
617        info('Parse trace using wasm mode ');
618        this.wasm = true;
619        //@ts-ignore
620        this.handleWasmMode(ev, showFileName, ev.size, fileName);
621      }
622    };
623  }
624
625  private openDistributedTraceFile(ev: InputEvent): void {
626    this.openFileInit(true);
627    this.importConfigDiv!.style.display = 'none';
628    this.closeKeyPath!.style.display = 'none';
629    // @ts-ignore
630    let fileList = ev.detail as FileList;
631    if (fileList.length === 2) {
632      this.handleDistributedWasmMode(fileList.item(0)!, fileList.item(1)!);
633    } else {
634      this.progressEL!.loading = false;
635      this.litSearch!.setPercent('load distributed trace error', -1);
636      this.resetMenus();
637      this.freshMenuDisable(false);
638      this.headerDiv!.style.pointerEvents = 'auto';
639    }
640  }
641
642  private openLineFileHandler(urlParams: URLSearchParams): void {
643    Utils.currentTraceMode = TraceMode.NORMAL;
644    this.openFileInit();
645    this.openMenu(false);
646    let downloadLineFile = urlParams.get('local') ? false : true;
647    this.setProgress(downloadLineFile ? 'download trace file' : 'open trace file');
648    this.downloadOnLineFile(
649      urlParams.get('trace') as string,
650      downloadLineFile,
651      (arrayBuf, fileName, showFileName, fileSize) => {
652        this.handleWasmMode(new File([arrayBuf], fileName), showFileName, fileSize, fileName);
653      },
654      (localPath) => {
655        let path = urlParams.get('trace') as string;
656        let fileName: string = '';
657        let showFileName: string = '';
658        if (urlParams.get('local')) {
659          this.openMenu(true);
660          fileName = urlParams.get('traceName') as string;
661        } else {
662          fileName = path.split('/').reverse()[0];
663        }
664        this.traceFileName = fileName;
665        showFileName = fileName.lastIndexOf('.') === -1 ? fileName : fileName.substring(0, fileName.lastIndexOf('.'));
666        TraceRow.rangeSelectObject = undefined;
667        let localUrl = downloadLineFile ? `${window.location.origin}${localPath}` : urlParams.get('trace')!;
668        fetch(localUrl)
669          .then((res) => {
670            res.arrayBuffer().then((arrayBuf) => {
671              if (urlParams.get('local')) {
672                URL.revokeObjectURL(localUrl);
673              }
674              this.handleWasmMode(new File([arrayBuf], fileName), showFileName, arrayBuf.byteLength, fileName);
675            });
676          })
677          .catch((e) => {
678            if (!downloadLineFile) {
679              const firstQuestionMarkIndex = window.location.href.indexOf('?');
680              location.replace(window.location.href.substring(0, firstQuestionMarkIndex));
681            }
682          });
683      }
684    );
685  }
686
687  private openMenu(open: boolean): void {
688    if (this.mainMenu) {
689      this.mainMenu.style.width = open ? '248px' : '0px';
690      this.mainMenu.style.zIndex = open ? '2000' : '0';
691    }
692    if (this.sidebarButton) {
693      this.sidebarButton.style.width = open ? '0px' : '48px';
694      this.importConfigDiv!.style.left = open ? '5px' : '45px';
695      this.closeKeyPath!.style.left = open ? '25px' : '65px';
696    }
697  }
698
699  private initGlobalDropEvents(): void {
700    let body = document.querySelector('body');
701    body!.addEventListener(
702      'drop',
703      (e) => {
704        e.preventDefault();
705        e.stopPropagation();
706        if (this.rootEL!.classList.contains('filedrag')) {
707          this.rootEL!.classList.remove('filedrag');
708        }
709        //@ts-ignore
710        if (e.dataTransfer.items !== undefined && e.dataTransfer.items.length > 0) {
711          //@ts-ignore
712          let item = e.dataTransfer.items[0];
713          if (item.webkitGetAsEntry()?.isFile) {
714            this.openTraceFile(item.getAsFile());
715          } else if (item.webkitGetAsEntry()?.isDirectory) {
716            this.litSearch!.setPercent('This File is not supported!', -1);
717            this.progressEL!.loading = false;
718            this.freshMenuDisable(false);
719            this.mainMenu!.menus!.splice(2, 1);
720            this.mainMenu!.menus = this.mainMenu!.menus!;
721            this.spSystemTrace!.reset(null);
722          }
723        }
724      },
725      false
726    );
727  }
728  private initGlobalEvents(): void {
729    let body = document.querySelector('body');
730    body!.addEventListener(
731      'dragover',
732      (e) => {
733        e.preventDefault();
734        e.stopPropagation();
735        //@ts-ignore
736        if (e.dataTransfer.items.length > 0 && e.dataTransfer.items[0].kind === 'file') {
737          //@ts-ignore
738          e.dataTransfer.dropEffect = 'copy';
739          if (!this.rootEL!.classList.contains('filedrag')) {
740            this.rootEL!.classList.add('filedrag');
741          }
742        }
743      },
744      false
745    );
746    body!.addEventListener(
747      'dragleave',
748      (e) => {
749        e.stopPropagation();
750        e.preventDefault();
751        if (this.rootEL!.classList.contains('filedrag')) {
752          this.rootEL!.classList.remove('filedrag');
753        }
754      },
755      false
756    );
757    this.initGlobalDropEvents();
758  }
759
760  private initDocumentListener(): void {
761    document.addEventListener('file-error', () => {
762      this.litSearch!.setPercent('This File is Error!', -1);
763    });
764    document.addEventListener('file-correct', () => {
765      this.litSearch!.setPercent('', 101);
766    });
767    document.addEventListener('visibilitychange', () => {
768      if (document.visibilityState === 'visible') {
769        this.validateFileCacheLost();
770        if (window.localStorage.getItem('Theme') === 'dark') {
771          this.changeTheme(Theme.DARK);
772        } else {
773          this.changeTheme(Theme.LIGHT);
774        }
775      }
776    });
777    document.addEventListener('keydown', (event) => {
778      const e = event || window.event;
779      const ctrlKey = e.ctrlKey || e.metaKey;
780      //@ts-ignore
781      if (ctrlKey && this.keyCodeMap[e.keyCode]) {
782        e.preventDefault();
783      } else if (e.detail) {
784        // Firefox
785        event.returnValue = false;
786      }
787    });
788    document.body.addEventListener(
789      'wheel',
790      (e) => {
791        if (e.ctrlKey) {
792          if (e.deltaY < 0) {
793            e.preventDefault();
794            return false;
795          }
796          if (e.deltaY > 0) {
797            e.preventDefault();
798            return false;
799          }
800        }
801      },
802      { passive: false }
803    );
804  }
805
806  private initNavigationMenu(multiTrace: boolean): MenuGroup[] {
807    return [
808      {
809        collapsed: false,
810        title: 'Navigation',
811        second: false,
812        icon: 'caret-down',
813        describe: 'Open or record a new trace',
814        children: [
815          {
816            title: 'Open trace file',
817            icon: 'folder',
818            fileChoose: true,
819            fileHandler: (ev: InputEvent): void => {
820              Utils.currentTraceMode = TraceMode.NORMAL;
821              this.openTraceFile(ev.detail);
822            },
823          },
824          {
825            title: 'Record new trace',
826            icon: 'copyhovered',
827            clickHandler: (item: MenuItem): void => this.clickHandleByRecordNewTrace(),
828          },
829          {
830            title: 'Record template',
831            icon: 'copyhovered',
832            clickHandler: (item: MenuItem): void => this.clickHandleByRecordTemplate(),
833          },
834        ],
835      },
836      {
837        collapsed: !multiTrace,
838        title: 'Open multiple trace',
839        second: false,
840        icon: 'caret-down',
841        describe: 'long trace or distributed trace',
842        children: [
843          {
844            title: 'Open long trace',
845            icon: 'folder',
846            fileChoose: true,
847            clickHandler: (ev: InputEvent): void => {
848              Utils.currentTraceMode = TraceMode.LONG_TRACE;
849              this.openLongTraceFile(ev, true);
850            },
851            fileHandler: (ev: InputEvent): void => {
852              Utils.currentTraceMode = TraceMode.LONG_TRACE;
853              this.openLongTraceFile(ev);
854            },
855          },
856          {
857            title: 'Open distributed trace',
858            icon: 'folder',
859            multi: true,
860            fileChoose: true,
861            fileHandler: (ev: InputEvent): void => {
862              Utils.currentTraceMode = TraceMode.DISTRIBUTED;
863              this.openDistributedTraceFile(ev);
864            },
865          },
866        ],
867      },
868    ];
869  }
870
871  private initSupportMenus(): MenuGroup {
872    return {
873      collapsed: false,
874      title: 'Support',
875      second: false,
876      icon: 'caret-down',
877      describe: 'Support',
878      children: [
879        {
880          title: 'Help Documents',
881          icon: 'smart-help',
882          clickHandler: (item: MenuItem): void => this.clickHandleByHelpDocuments(),
883        },
884        {
885          title: 'Flags',
886          icon: 'menu',
887          clickHandler: (item: MenuItem): void => this.clickHandleByFlags(),
888        },
889        {
890          title: 'Keyboard Shortcuts',
891          icon: 'smart-help',
892          clickHandler: (item: MenuItem): void => this.clickHandleByKeyboardShortcuts(),
893        }
894      ],
895    };
896  }
897
898  private clickHandleByHelpDocuments(): void {
899    this.spHelp!.dark = this.dark;
900    this.search = false;
901    this.showContent(this.spHelp!);
902    SpStatisticsHttpUtil.addOrdinaryVisitAction({
903      event: 'help_page',
904      action: 'help_doc',
905    });
906    this.changeUrl();
907  }
908
909  private clickHandleByFlags(): void {
910    this.returnOriginalUrl();
911    this.search = false;
912    this.showContent(this.spFlags!);
913    SpStatisticsHttpUtil.addOrdinaryVisitAction({
914      event: 'flags',
915      action: 'flags',
916    });
917  }
918
919  private clickHandleByKeyboardShortcuts(): void {
920    this.returnOriginalUrl();
921    document
922      .querySelector('body > sp-application')!
923      .shadowRoot!.querySelector<HTMLDivElement>('#sp-keyboard')!.style.visibility = 'visible';
924    SpSystemTrace.keyboardFlar = false;
925    SpStatisticsHttpUtil.addOrdinaryVisitAction({
926      event: 'Keyboard Shortcuts',
927      action: 'Keyboard Shortcuts',
928    });
929  }
930
931  private clickHandleByRecordNewTrace(): void {
932    this.returnOriginalUrl();
933    this.spRecordTrace!.synchronizeDeviceList();
934    this.spRecordTemplate!.record_template = false;
935    this.spRecordTrace!.refreshConfig(true);
936    this.showContent(this.spRecordTrace!);
937  }
938
939  private clickHandleByRecordTemplate(): void {
940    this.returnOriginalUrl();
941    this.spRecordTemplate!.refreshHint();
942    this.spRecordTemplate!.record_template = true;
943    this.spRecordTemplate!.refreshConfig(false);
944    this.spRecordTemplate!.synchronizeDeviceList();
945    this.showContent(this.spRecordTemplate!);
946  }
947
948  private changeUrl(): void {
949    let url = new URL(window.location.href);
950    let actionParam = url.searchParams.get('action');
951    let newActionValue = 'help';
952    if (actionParam) {
953      url.searchParams.set('action', newActionValue);
954    } else {
955      url.searchParams.append('action', newActionValue);
956    }
957    let newURL = url.href;
958    history.pushState({}, '', newURL);
959  }
960
961  private returnOriginalUrl(): void {
962    history.pushState({}, '', window.location.origin + window.location.pathname);
963  }
964
965  private handleSqliteMode(ev: unknown, showFileName: string, fileSize: number, fileName: string): void {
966    let fileSizeStr = (fileSize / 1048576).toFixed(1);
967    postLog(fileName, fileSizeStr);
968    document.title = `${showFileName} (${fileSizeStr}M)`;
969    this.litSearch!.setPercent('', 0);
970    threadPool.init('sqlite').then((res) => {
971      let reader = new FileReader();
972      reader.readAsArrayBuffer(ev as Blob);
973      reader.onloadend = (ev): void => {
974        SpApplication.loadingProgress = 0;
975        SpApplication.progressStep = 3;
976        this.spSystemTrace!.loadDatabaseArrayBuffer(
977          reader.result as ArrayBuffer,
978          '',
979          (command: string, _: number) => {
980            this.setProgress(command);
981          },
982          false,
983          () => {
984            this.mainMenu!.menus!.splice(2, this.mainMenu!.menus!.length > 2 ? 1 : 0, {
985              collapsed: false,
986              title: 'Current Trace',
987              second: false,
988              icon: 'caret-down',
989              describe: 'Actions on the current trace',
990              children: this.getTraceOptionMenus(showFileName, fileSizeStr, fileName, true, false),
991            });
992            this.mainMenu!.menus!.splice(3, 1, {
993              collapsed: false,
994              title: 'Support',
995              second: false,
996              icon: 'caret-down',
997              describe: 'Support',
998              children: this.getTraceSupportMenus(),
999            });
1000            this.litSearch!.setPercent('', 101);
1001            this.chartFilter!.setAttribute('mode', '');
1002            this.progressEL!.loading = false;
1003            this.freshMenuDisable(false);
1004            this.spInfoAndStats!.initInfoAndStatsData();
1005            this.cutTraceFile!.style.display = 'none';
1006            this.headerDiv!.style.pointerEvents = 'auto';
1007          }
1008        );
1009      };
1010    });
1011  }
1012
1013  private handleDistributedWasmMode(file1: File, file2: File): void {
1014    this.litSearch!.setPercent('', 1);
1015    document.title = 'Distributed Trace';
1016    let completeHandler = async (res: unknown): Promise<void> => {
1017      await this.traceLoadCompleteHandler(res, '', '', file1.name, true, file2.name);
1018    };
1019    let typeHeader = file1.slice(0, 6);
1020    let reader: FileReader | null = new FileReader();
1021    reader.readAsText(typeHeader);
1022    reader.onloadend = (event): void => {
1023      let headerStr: string = `${reader?.result}`;
1024      let traceType = 'wasm';
1025      if (headerStr.indexOf('SQLite') === 0) {
1026        traceType = 'sqlite';
1027      }
1028      Promise.all([threadPool.init(traceType), threadPool2.init(traceType)]).then(() => {
1029        let wasmUrl = `https://${window.location.host.split(':')[0]}:${window.location.port}/application/wasm.json`;
1030        Promise.all([file1.arrayBuffer(), file2.arrayBuffer()]).then((bufArr) => {
1031          this.litSearch!.setPercent('ArrayBuffer loaded  ', 2);
1032          SpApplication.loadingProgress = 0;
1033          SpApplication.progressStep = 2;
1034          let buf1 = this.markPositionHandler(bufArr[0]);
1035          let buf2 = this.markPositionHandler(bufArr[1]);
1036          info('initData start Parse Data');
1037          this.spSystemTrace!.loadDatabaseArrayBuffer(
1038            buf1,
1039            wasmUrl,
1040            (command: string, _: number) => this.setProgress(command),
1041            true,
1042            completeHandler,
1043            buf2,
1044            file1.name,
1045            file2.name
1046          );
1047        });
1048      });
1049    };
1050  }
1051
1052  private handleWasmMode(ev: unknown, showFileName: string, fileSize: number, fileName: string): void {
1053    this.litSearch!.setPercent('', 1);
1054    if (fileName.endsWith('.json')) {
1055      this.progressEL!.loading = true;
1056      //@ts-ignore
1057      self.spSystemTrace!.loadSample(ev).then(() => {
1058        this.showContent(this.spSystemTrace!);
1059        this.litSearch!.setPercent('', 101);
1060        this.freshMenuDisable(false);
1061        this.chartFilter!.setAttribute('mode', '');
1062        this.progressEL!.loading = false;
1063      });
1064    } else if (fileName.endsWith('.csv')) {
1065      this.progressEL!.loading = true;
1066      this.spSystemTrace!.loadGpuCounter(ev as File).then(() => {
1067        this.showContent(this.spSystemTrace!);
1068        this.litSearch!.setPercent('', 101);
1069        this.freshMenuDisable(false);
1070        this.chartFilter!.setAttribute('mode', '');
1071        this.progressEL!.loading = false;
1072      });
1073    } else {
1074      let fileSizeStr = (fileSize / 1048576).toFixed(1);
1075      postLog(fileName, fileSizeStr);
1076      document.title = `${showFileName} (${fileSizeStr}M)`;
1077      info('Parse trace using wasm mode ');
1078      let completeHandler = async (res: unknown): Promise<void> => {
1079        await this.traceLoadCompleteHandler(res, fileSizeStr, showFileName, fileName, false);
1080        if (this.markJson) {
1081          window.publish(window.SmartEvent.UI.ImportRecord, this.markJson);
1082        }
1083      };
1084      threadPool.init('wasm').then((res) => {
1085        let reader: FileReader = new FileReader();
1086        //@ts-ignore
1087        reader.readAsArrayBuffer(ev);
1088        reader.onloadend = (ev): void => {
1089          info('read file onloadend');
1090          this.litSearch!.setPercent('ArrayBuffer loaded  ', 2);
1091          let wasmUrl = `https://${window.location.host.split(':')[0]}:${window.location.port}/application/wasm.json`;
1092          SpApplication.loadingProgress = 0;
1093          SpApplication.progressStep = 3;
1094          let data = this.markPositionHandler(reader.result as ArrayBuffer);
1095          info('initData start Parse Data');
1096          this.spSystemTrace!.loadDatabaseArrayBuffer(
1097            data,
1098            wasmUrl,
1099            (command: string, _: number) => this.setProgress(command),
1100            false,
1101            completeHandler
1102          );
1103        };
1104      });
1105    }
1106  }
1107
1108  private markPositionHandler(buf: ArrayBuffer): ArrayBuffer {
1109    const decoder = new TextDecoder('utf-8');
1110    const headText = decoder.decode(buf.slice(0, 100));
1111    let hasMark = headText.includes('MarkPositionJSON');
1112    if (hasMark) {
1113      let markLength = headText.split('->')[0].replace('MarkPositionJSON', '');
1114      let mark = decoder.decode(buf.slice(0, markLength.length + parseInt(markLength)));
1115      if (mark.includes('->')) {
1116        this.markJson = mark.split('->')[1];
1117      }
1118      return buf.slice(markLength.length + parseInt(markLength));
1119    } else {
1120      return buf;
1121    }
1122  }
1123
1124  private async traceLoadCompleteHandler(
1125    res: unknown,
1126    fileSize: string,
1127    showFileName: string,
1128    fileName: string,
1129    isDistributed: boolean,
1130    fileName2?: string
1131  ): Promise<void> {
1132    let index = 3;
1133    if (isDistributed) {
1134      //分布式,隐藏 裁剪 trace 和 导出带记录的收藏trace 功能
1135      this.cutTraceFile!.style.display = 'none';
1136      this.exportRecord!.style.display = 'none';
1137      this.litSearch?.setAttribute('distributed', '');
1138      setThreadPoolTraceBuffer('1', null);
1139      setThreadPoolTraceBuffer('2', null);
1140    } else {
1141      let existFtrace = await queryExistFtrace();
1142      let isAllowTrace = true;
1143      let sharedBuffer = getThreadPoolTraceBuffer('1');
1144      if (sharedBuffer) {
1145        let traceHeadData = new Uint8Array(sharedBuffer!.slice(0, 10));
1146        let enc = new TextDecoder();
1147        let headerStr = enc.decode(traceHeadData);
1148        let rowTraceStr = Array.from(new Uint8Array(sharedBuffer!.slice(0, 2)))
1149          .map((byte) => byte.toString(16).padStart(2, '0'))
1150          .join('');
1151        if (headerStr.indexOf('OHOSPROF') !== 0 && rowTraceStr.indexOf('49df') !== 0) {
1152          isAllowTrace = false;
1153        }
1154        this.cutTraceFile!.style.display = 'block';
1155        this.exportRecord!.style.display = 'block';
1156        setThreadPoolTraceBuffer('1', null);
1157      }
1158      if (existFtrace.length > 0 && isAllowTrace) {
1159        this.showConvertTraceMenu(fileName);
1160        index = 4;
1161      }
1162    }
1163
1164    this.loadTraceCompleteMenuHandler(index);
1165    //@ts-ignore
1166    if (res.status) {
1167      info('loadDatabaseArrayBuffer success');
1168      if (isDistributed) {
1169        Utils.distributedTrace.push(fileName || 'trace1');
1170        Utils.distributedTrace.push(fileName2 || 'trace2');
1171        this.litSearch!.setTraceSelectOptions();
1172      } else {
1173        //@ts-ignore
1174        (window as unknown).traceFileName = fileName;
1175      }
1176      this.showCurrentTraceMenu(fileSize, showFileName, fileName, isDistributed);
1177      if (!isDistributed) {
1178        this.importConfigDiv!.style.display = Utils.getInstance().getSchedSliceMap().size > 0 ? 'block' : 'none';
1179      }
1180      this.showContent(this.spSystemTrace!);
1181      this.litSearch!.setPercent('', 101);
1182      this.chartFilter!.setAttribute('mode', '');
1183      this.freshMenuDisable(false);
1184    } else {
1185      info('loadDatabaseArrayBuffer failed');
1186      //@ts-ignore
1187      this.litSearch!.setPercent(res.msg || 'This File is not supported!', -1);
1188      this.resetMenus();
1189      this.freshMenuDisable(false);
1190    }
1191    this.progressEL!.loading = false;
1192    this.headerDiv!.style.pointerEvents = 'auto';
1193    this.spInfoAndStats!.initInfoAndStatsData();
1194  }
1195
1196  private resetMenus(multiTrace: boolean = false): void {
1197    this.mainMenu!.menus = [...this.initNavigationMenu(multiTrace), this.initSupportMenus()];
1198  }
1199
1200  private showConvertTraceMenu(fileName: string): void {
1201    this.mainMenu!.menus!.splice(3, 1, {
1202      collapsed: false,
1203      title: 'Convert trace',
1204      second: false,
1205      icon: 'caret-down',
1206      describe: 'Convert to other formats',
1207      children: this.pushConvertTrace(fileName),
1208    });
1209  }
1210
1211  private showCurrentTraceMenu(fileSize: string, showFileName: string, fileName: string, isDistributed: boolean): void {
1212    this.mainMenu!.menus!.splice(2, this.mainMenu!.menus!.length > 2 ? 1 : 0, {
1213      collapsed: false,
1214      title: 'Current Trace',
1215      second: false,
1216      icon: 'caret-down',
1217      describe: 'Actions on the current trace',
1218      children: this.getTraceOptionMenus(showFileName, fileSize, fileName, false, isDistributed),
1219    });
1220  }
1221
1222  private loadTraceCompleteMenuHandler(index: number): void {
1223    this.mainMenu!.menus!.splice(index, 1, {
1224      collapsed: false,
1225      title: 'Support',
1226      second: false,
1227      icon: 'caret-down',
1228      describe: 'Support',
1229      children: [
1230        {
1231          title: 'Help Documents',
1232          icon: 'smart-help',
1233          clickHandler: (item: MenuItem): void => this.clickHandleByHelpDocuments(),
1234        },
1235        {
1236          title: 'Flags',
1237          icon: 'menu',
1238          clickHandler: (item: MenuItem): void => this.clickHandleByFlags(),
1239        },
1240        {
1241          title: 'Keyboard Shortcuts',
1242          icon: 'smart-help',
1243          clickHandler: (item: MenuItem): void => this.clickHandleByKeyboardShortcuts(),
1244        },
1245      ],
1246    });
1247  }
1248
1249  private validateGetTraceFileByPage(): boolean {
1250    if (!this.wasm) {
1251      this.progressEL!.loading = false;
1252      return false;
1253    }
1254    return this.pageTimStamp !== 0;
1255  }
1256
1257  private queryFileByPage(
1258    instance: LongTraceDBUtils,
1259    indexedDbPageNum: number,
1260    maxTraceFileLength: number,
1261    traceRange: IDBKeyRange
1262  ): void {
1263    instance.indexedDBHelp.get(instance.tableName, traceRange, 'QueryFileByPage').then((result) => {
1264      let traceData = indexedDataToBufferData(result);
1265      let ebpfRange = this.getIDBKeyRange(indexedDbPageNum, 'ebpf_new');
1266      let arkTsRange = this.getIDBKeyRange(indexedDbPageNum, 'arkts_new');
1267      let hiperfRange = this.getIDBKeyRange(indexedDbPageNum, 'hiperf_new');
1268      Promise.all([
1269        instance.getByRange(ebpfRange),
1270        instance.getByRange(arkTsRange),
1271        instance.getByRange(hiperfRange),
1272      ]).then((otherResult) => {
1273        let ebpfData = indexedDataToBufferData(otherResult[0]);
1274        let arkTsData = indexedDataToBufferData(otherResult[1]);
1275        let hiperfData = indexedDataToBufferData(otherResult[2]);
1276        let traceArray = new Uint8Array(traceData);
1277        let ebpfArray = new Uint8Array(ebpfData);
1278        let arkTsArray = new Uint8Array(arkTsData);
1279        let hiPerfArray = new Uint8Array(hiperfData);
1280        let allOtherData = [ebpfData, arkTsData, hiperfData];
1281        let otherDataLength = traceData.byteLength + ebpfData.byteLength + arkTsData.byteLength + hiperfData.byteLength;
1282        let timeStamp =
1283          this.currentDataTime[0] +
1284          this.currentDataTime[1] +
1285          this.currentDataTime[2] +
1286          '_' +
1287          this.currentDataTime[3] +
1288          this.currentDataTime[4] +
1289          this.currentDataTime[5];
1290        this.traceFileName = `hiprofiler_long_${timeStamp}_${indexedDbPageNum}.htrace`;
1291        if (otherDataLength > maxTraceFileLength) {
1292          if (traceData.byteLength > maxTraceFileLength) {
1293            this.traceFileLoadFailedHandler('hitrace file too big!');
1294          } else {
1295            let freeDataLength = maxTraceFileLength - traceData.byteLength;
1296            let freeDataIndex = findFreeSizeAlgorithm(
1297              [ebpfData.byteLength, arkTsData.byteLength, hiperfData.byteLength],
1298              freeDataLength
1299            );
1300            let finalData = [traceData];
1301            freeDataIndex.forEach((dataIndex) => {
1302              finalData.push(allOtherData[dataIndex]);
1303            });
1304            const file = new File([new Blob(finalData)], this.traceFileName);
1305            this.handleWasmMode(file, file.name, file.size, this.traceFileName);
1306          }
1307        } else {
1308          let fileBlob = new Blob([traceArray, ebpfArray, arkTsArray, hiPerfArray]);
1309          const file = new File([fileBlob], this.traceFileName);
1310          this.handleWasmMode(file, file.name, file.size, file.name);
1311        }
1312        this.refreshLongTraceButtonStyle();
1313        this.longTracePage!.style.display = 'flex';
1314      });
1315    });
1316  }
1317
1318  private refreshLongTraceButtonStyle(): void {
1319    let pageListDiv = this.shadowRoot?.querySelector('.page-number-list') as HTMLDivElement;
1320    let pageNodeList = pageListDiv.querySelectorAll<HTMLDivElement>('div');
1321    let pageInput = this.shadowRoot?.querySelector<HTMLInputElement>('.page-input');
1322    let previewButton: HTMLDivElement | null | undefined =
1323      this.shadowRoot?.querySelector<HTMLDivElement>('#preview-button');
1324    let nextButton: HTMLDivElement | null | undefined = this.shadowRoot?.querySelector<HTMLDivElement>('#next-button');
1325    let pageConfirmEl = this.shadowRoot?.querySelector<HTMLDivElement>('.confirm-button');
1326    pageInput!.style.pointerEvents = 'auto';
1327    pageNodeList.forEach((pageItem) => {
1328      pageItem.style.pointerEvents = 'auto';
1329    });
1330    nextButton!.style.pointerEvents = 'auto';
1331    nextButton!.style.opacity = '1';
1332    previewButton!.style.pointerEvents = 'auto';
1333    previewButton!.style.opacity = '1';
1334    if (this.currentPageNum === 1) {
1335      previewButton!.style.pointerEvents = 'none';
1336      previewButton!.style.opacity = '0.7';
1337    } else if (this.currentPageNum === this.longTraceHeadMessageList.length) {
1338      nextButton!.style.pointerEvents = 'none';
1339      nextButton!.style.opacity = '0.7';
1340    }
1341    //
1342    pageConfirmEl!.style.pointerEvents = 'auto';
1343  }
1344
1345  private getTraceFileByPage(pageNumber: number): void {
1346    this.openFileInit(true);
1347    if (this.validateGetTraceFileByPage()) {
1348      let indexedDbPageNum = pageNumber - 1;
1349      let maxTraceFileLength = 400 * 1024 * 1024;
1350      let traceRange = this.getIDBKeyRange(indexedDbPageNum, 'trace');
1351      let instance = LongTraceDBUtils.getInstance();
1352      this.queryFileByPage(instance, indexedDbPageNum, maxTraceFileLength, traceRange);
1353    }
1354  }
1355
1356  private traceFileLoadFailedHandler(reason: string): void {
1357    this.litSearch!.isLoading = false;
1358    this.litSearch!.setPercent(reason, -1);
1359    this.progressEL!.loading = false;
1360    this.freshMenuDisable(false);
1361  }
1362
1363  private getIDBKeyRange(indexedDbPageNum: number, key: string): IDBKeyRange {
1364    return IDBKeyRange.bound(
1365      [this.pageTimStamp, key, indexedDbPageNum],
1366      [this.pageTimStamp, key, indexedDbPageNum],
1367      false,
1368      false
1369    );
1370  }
1371
1372  private refreshPageListHandler(
1373    pageListDiv: HTMLDivElement,
1374    previewButton: HTMLDivElement,
1375    nextButton: HTMLDivElement,
1376    pageInput: HTMLInputElement
1377  ): void {
1378    this.progressEL!.loading = true;
1379    this.refreshPageList(
1380      pageListDiv,
1381      previewButton!,
1382      nextButton!,
1383      pageInput!,
1384      this.currentPageNum,
1385      this.longTraceHeadMessageList.length
1386    );
1387    this.getTraceFileByPage(this.currentPageNum);
1388  }
1389
1390  private sendCutFileMessage(timStamp: number): void {
1391    this.pageTimStamp = timStamp;
1392    threadPool.init('wasm').then(() => {
1393      let headUintArray = new Uint8Array(this.longTraceHeadMessageList.length * 1024);
1394      let headOffset = 0;
1395      this.longTraceHeadMessageList = this.longTraceHeadMessageList.sort(
1396        (leftMessage, rightMessage) => leftMessage.pageNum - rightMessage.pageNum
1397      );
1398      for (let index = 0; index < this.longTraceHeadMessageList.length; index++) {
1399        let currentUintArray = new Uint8Array(this.longTraceHeadMessageList[index].data);
1400        headUintArray.set(currentUintArray, headOffset);
1401        headOffset += currentUintArray.length;
1402      }
1403      threadPool.submit(
1404        'ts-cut-file',
1405        '',
1406        {
1407          headArray: headUintArray,
1408          timeStamp: timStamp,
1409          splitFileInfo: this.longTraceTypeMessageMap?.get(0),
1410          splitDataList: this.longTraceDataList,
1411        },
1412        (res: Array<unknown>) => {
1413          this.litSearch!.setPercent('Cut in file ', 100);
1414          this.currentDataTime = getCurrentDataTime();
1415          if (this.longTraceHeadMessageList.length > 0) {
1416            this.getTraceFileByPage(this.currentPageNum);
1417            this.litSearch!.style.marginLeft = '80px';
1418            this.longTracePage!.style.display = 'flex';
1419            this.initCutFileEvent();
1420          } else {
1421            this.progressEL!.loading = false;
1422            this.litSearch!.setPercent('Missing basic trace in the large-file scenario!', -1);
1423            this.freshMenuDisable(false);
1424            return;
1425          }
1426        },
1427        'long_trace'
1428      );
1429    });
1430  }
1431
1432  private initCutFileEvent(): void {
1433    let pageListDiv = this.shadowRoot?.querySelector('.page-number-list') as HTMLDivElement;
1434    let previewButton: HTMLDivElement | null | undefined =
1435      this.shadowRoot?.querySelector<HTMLDivElement>('#preview-button');
1436    let nextButton: HTMLDivElement | null | undefined = this.shadowRoot?.querySelector<HTMLDivElement>('#next-button');
1437    let pageInput = this.shadowRoot?.querySelector<HTMLInputElement>('.page-input');
1438    pageListDiv.innerHTML = '';
1439    this.refreshPageList(
1440      pageListDiv,
1441      previewButton!,
1442      nextButton!,
1443      pageInput!,
1444      this.currentPageNum,
1445      this.longTraceHeadMessageList.length
1446    );
1447    this.initCutFileNextOrPreEvents(previewButton!, nextButton!, pageListDiv, pageInput!);
1448    let nodeListOf = pageListDiv.querySelectorAll<HTMLDivElement>('div');
1449    nodeListOf.forEach((divEL, index) => {
1450      divEL.addEventListener('click', () => {
1451        if (this.progressEL!.loading) {
1452          return;
1453        }
1454        if (divEL.textContent === '...') {
1455          let freeSize = Number(nodeListOf[index + 1].textContent) - Number(nodeListOf[index - 1].textContent);
1456          this.currentPageNum = Math.floor(freeSize / 2 + Number(nodeListOf[index - 1].textContent));
1457        } else {
1458          this.currentPageNum = Number(divEL.textContent);
1459        }
1460        this.refreshPageListHandler(pageListDiv, previewButton!, nextButton!, pageInput!);
1461      });
1462    });
1463    pageInput!.addEventListener('input', () => {
1464      let value = pageInput!.value;
1465      value = value.replace(/\D/g, '');
1466      if (value) {
1467        value = Math.min(this.longTraceHeadMessageList.length, parseInt(value)).toString();
1468      }
1469      pageInput!.value = value;
1470    });
1471    let pageConfirmEl = this.shadowRoot?.querySelector<HTMLDivElement>('.confirm-button');
1472    pageConfirmEl!.addEventListener('click', () => {
1473      if (this.progressEL!.loading) {
1474        return;
1475      }
1476      this.currentPageNum = Number(pageInput!.value);
1477      this.refreshPageListHandler(pageListDiv, previewButton!, nextButton!, pageInput!);
1478    });
1479  }
1480
1481  private initCutFileNextOrPreEvents(
1482    previewButton: HTMLDivElement,
1483    nextButton: HTMLDivElement,
1484    pageListDiv: HTMLDivElement,
1485    pageInput: HTMLInputElement
1486  ): void {
1487    if (previewButton) {
1488      previewButton.addEventListener('click', () => {
1489        if (this.progressEL!.loading || this.currentPageNum === 1) {
1490          return;
1491        }
1492        if (this.currentPageNum > 1) {
1493          this.currentPageNum--;
1494          this.refreshPageListHandler(pageListDiv, previewButton!, nextButton!, pageInput!);
1495        }
1496      });
1497    }
1498    nextButton!.addEventListener('click', () => {
1499      if (this.progressEL!.loading || this.currentPageNum === this.longTraceHeadMessageList.length) {
1500        return;
1501      }
1502      if (this.currentPageNum < this.longTraceHeadMessageList.length) {
1503        this.currentPageNum++;
1504        this.refreshPageListHandler(pageListDiv, previewButton!, nextButton!, pageInput!);
1505      }
1506    });
1507  }
1508
1509  private initCustomColorHandler(): void {
1510    let customColorShow = this.shadowRoot
1511      ?.querySelector('lit-main-menu')!
1512      .shadowRoot!.querySelector('.customColor') as HTMLDivElement;
1513    customColorShow.addEventListener('click', (ev) => {
1514      if (this!.hasAttribute('custom-color')) {
1515        this!.removeAttribute('custom-color');
1516        this.customColor!.setAttribute('hidden', '');
1517        this.customColor!.cancelOperate();
1518      } else {
1519        this!.removeAttribute('chart_filter');
1520        this.chartFilter!.setAttribute('hidden', '');
1521        this!.setAttribute('custom-color', '');
1522        this.customColor!.removeAttribute('hidden');
1523      }
1524    });
1525  }
1526
1527  private openFileInit(multiTrace: boolean = false): void {
1528    clearTraceFileCache();
1529    this.litSearch!.clear();
1530    Utils.currentSelectTrace = undefined;
1531    this.markJson = undefined;
1532    if (!multiTrace) {
1533      this.longTracePage!.style.display = 'none';
1534    }
1535    this.litSearch?.removeAttribute('distributed');
1536    SpStatisticsHttpUtil.addOrdinaryVisitAction({
1537      event: 'open_trace',
1538      action: 'open_trace',
1539    });
1540    info('openTraceFile');
1541    this.headerDiv!.style.pointerEvents = 'none';
1542    this.spSystemTrace!.clearPointPair();
1543    this.spSystemTrace!.reset((command: string, percent: number): void => {
1544      this.setProgress(command);
1545    });
1546    threadPool2.reset().then();
1547    window.publish(window.SmartEvent.UI.MouseEventEnable, {
1548      mouseEnable: false,
1549    });
1550    window.clearTraceRowComplete();
1551    SpSchedulingAnalysis.resetCpu();
1552    this.resetMenus(multiTrace);
1553    this.freshMenuDisable(true);
1554    this.showContent(this.spSystemTrace!);
1555    this.progressEL!.loading = true;
1556  }
1557
1558  private restoreDownLoadIcons(): void {
1559    let querySelectorAll = this.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuGroup>('lit-main-menu-group');
1560    querySelectorAll!.forEach((menuGroup) => {
1561      let attribute = menuGroup.getAttribute('title');
1562      if (attribute === 'Convert trace') {
1563        let querySelectors = menuGroup.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
1564        querySelectors.forEach((item) => {
1565          if (item.getAttribute('title') === 'Convert to .systrace') {
1566            item!.setAttribute('icon', 'download');
1567            let querySelector = item!.shadowRoot?.querySelector('.icon') as LitIcon;
1568            querySelector.removeAttribute('spin');
1569          }
1570        });
1571      }
1572    });
1573  }
1574
1575  private postConvert(fileName: string): void {
1576    let newFileName = fileName.substring(0, fileName.lastIndexOf('.')) + '.systrace';
1577    let aElement = document.createElement('a');
1578    convertPool.submitWithName('getConvertData', (status: boolean, msg: string, results: Blob) => {
1579      aElement.href = URL.createObjectURL(results);
1580      aElement.download = newFileName;
1581      let timeoutId = 0;
1582      aElement.addEventListener('click', (ev) => {
1583        clearTimeout(timeoutId);
1584        timeoutId = window.setTimeout(() => {
1585          this.restoreDownLoadIcons();
1586        }, 2000);
1587      });
1588      aElement.click();
1589      window.URL.revokeObjectURL(aElement.href);
1590    });
1591  }
1592
1593  private pushConvertTrace(fileName: string): Array<MenuItem> {
1594    let instance = this;
1595    let menus = [];
1596    menus.push({
1597      title: 'Convert to .systrace',
1598      icon: 'download',
1599      clickHandler: function () {
1600        convertPool.init('convert').then((item) => {
1601          let querySelectorAll =
1602            instance.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuGroup>('lit-main-menu-group');
1603          querySelectorAll!.forEach((menuGroup) => {
1604            let attribute = menuGroup.getAttribute('title');
1605            if (attribute === 'Convert trace') {
1606              let querySelectors = menuGroup.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
1607              querySelectors.forEach((item) => {
1608                if (item.getAttribute('title') === 'Convert to .systrace') {
1609                  item!.setAttribute('icon', 'convert-loading');
1610                  item!.classList.add('pending');
1611                  item!.style.fontKerning = '';
1612                  let querySelector = item!.shadowRoot?.querySelector('.icon') as LitIcon;
1613                  querySelector.setAttribute('spin', '');
1614                }
1615              });
1616            }
1617          });
1618          instance.postConvert(fileName);
1619        });
1620      },
1621    });
1622    return menus;
1623  }
1624
1625  private setProgress(command: string): void {
1626    if (command === 'database ready' && SpApplication.loadingProgress < 50) {
1627      SpApplication.progressStep = 6;
1628    }
1629    if (command === 'process' && SpApplication.loadingProgress < 92) {
1630      SpApplication.loadingProgress = 92 + Math.round(Math.random() * SpApplication.progressStep);
1631    } else {
1632      SpApplication.loadingProgress += Math.round(Math.random() * SpApplication.progressStep + Math.random());
1633    }
1634    if (SpApplication.loadingProgress > 99) {
1635      SpApplication.loadingProgress = 99;
1636    }
1637    info('setPercent :' + command + 'percent :' + SpApplication.loadingProgress);
1638    this.litSearch!.setPercent(command + '  ', SpApplication.loadingProgress);
1639  }
1640
1641  private getTraceOptionMenus(
1642    showFileName: string,
1643    fileSize: string,
1644    fileName: string,
1645    isServer: boolean,
1646    isDistributed: boolean,
1647    dbName?: string
1648  ): Array<MenuItem> {
1649    let menus = [
1650      {
1651        title: isDistributed ? 'Distributed Trace' : `${showFileName} (${fileSize}M)`,
1652        icon: 'file-fill',
1653        clickHandler: (): void => {
1654          this.search = true;
1655          this.showContent(this.spSystemTrace!);
1656        },
1657      },
1658    ];
1659    if (
1660      !isDistributed &&
1661      Utils.getInstance().getWinCpuCount() > 0 &&
1662      FlagsConfig.getFlagsConfigEnableStatus('SchedulingAnalysis')
1663    ) {
1664      menus.push({
1665        title: 'Scheduling Analysis',
1666        icon: 'piechart-circle-fil',
1667        clickHandler: (): void => {
1668          SpStatisticsHttpUtil.addOrdinaryVisitAction({
1669            event: 'Scheduling Analysis',
1670            action: 'scheduling_analysis',
1671          });
1672          this.showContent(this.spSchedulingAnalysis!);
1673          this.spSchedulingAnalysis!.init();
1674        },
1675      });
1676    }
1677    if (!isDistributed) {
1678      menus.push({
1679        title: 'Download File',
1680        icon: 'download', // @ts-ignore
1681        fileModel: this.wasm ? 'wasm' : 'db',
1682        clickHandler: (): void => {
1683          this.download(this.mainMenu!, fileName, isServer, dbName);
1684          SpStatisticsHttpUtil.addOrdinaryVisitAction({
1685            event: 'download',
1686            action: 'download',
1687          });
1688        },
1689      });
1690      menus.push({
1691        title: 'Download Database',
1692        icon: 'download', // @ts-ignore
1693        fileModel: this.wasm ? 'wasm' : 'db',
1694        clickHandler: (): void => {
1695          this.downloadDB(this.mainMenu!, fileName);
1696          SpStatisticsHttpUtil.addOrdinaryVisitAction({
1697            event: 'download_db',
1698            action: 'download',
1699          });
1700        },
1701      });
1702      this.getTraceQuerySqlMenus(menus);
1703    }
1704    return menus;
1705  }
1706
1707  private getTraceSupportMenus(): Array<MenuItem> {
1708    return [
1709      {
1710        title: 'Help Documents',
1711        icon: 'smart-help',
1712        clickHandler: (item: MenuItem): void => {
1713          this.spHelp!.dark = this.dark;
1714          this.search = false;
1715          this.showContent(this.spHelp!);
1716          SpStatisticsHttpUtil.addOrdinaryVisitAction({
1717            event: 'help_page',
1718            action: 'help_doc',
1719          });
1720        },
1721      },
1722      {
1723        title: 'Flags',
1724        icon: 'menu',
1725        fileModel: this.wasm ? 'wasm' : 'db',
1726        clickHandler: (item: MenuItem): void => {
1727          this.search = false;
1728          this.showContent(this.spFlags!);
1729          SpStatisticsHttpUtil.addOrdinaryVisitAction({
1730            event: 'flags',
1731            action: 'flags',
1732          });
1733        },
1734      },
1735      {
1736        title: 'Keyboard Shortcuts',
1737        icon: 'smart-help',
1738        clickHandler: (item: MenuItem): void => {
1739          document
1740            .querySelector('body > sp-application')!
1741            .shadowRoot!.querySelector<HTMLDivElement>('#sp-keyboard')!.style.visibility = 'visible';
1742          SpStatisticsHttpUtil.addOrdinaryVisitAction({
1743            event: 'Keyboard Shortcuts',
1744            action: 'Keyboard Shortcuts',
1745          });
1746        },
1747      },
1748      {
1749        title: '第三方文件',
1750        icon: 'file-fill',
1751        fileModel: this.wasm ? 'wasm' : 'db',
1752        clickHandler: (item: MenuItem): void => {
1753          this.search = false;
1754          this.showContent(this.spThirdParty!);
1755        },
1756      },
1757    ];
1758  }
1759
1760  private getTraceQuerySqlMenus(menus: Array<unknown>): void {
1761    if (this.querySql) {
1762      if (this.spQuerySQL) {
1763        this.spQuerySQL!.reset();
1764        menus.push({
1765          title: 'Query (SQL)',
1766          icon: 'filesearch',
1767          clickHandler: () => {
1768            this.showContent(this.spQuerySQL!);
1769          },
1770        });
1771      }
1772      if (this.spMetrics) {
1773        this.spMetrics!.reset();
1774        menus.push({
1775          title: 'Metrics',
1776          icon: 'metric',
1777          fileModel: this.wasm ? 'wasm' : 'db',
1778          clickHandler: () => {
1779            this.showContent(this.spMetrics!);
1780          },
1781        });
1782      }
1783      if (this.spInfoAndStats) {
1784        menus.push({
1785          title: 'Info and stats',
1786          icon: 'info',
1787          clickHandler: () => {
1788            SpStatisticsHttpUtil.addOrdinaryVisitAction({
1789              event: 'info',
1790              action: 'info_stats',
1791            });
1792            this.showContent(this.spInfoAndStats!);
1793          },
1794        });
1795      }
1796    }
1797  }
1798
1799  private initSlideMenuEvents(): void {
1800    //打开侧边栏
1801    this.sidebarButton!.onclick = (e): void => {
1802      if (this.sidebarButton) {
1803        this.sidebarButton.style.width = '0px';
1804        this.importConfigDiv!.style.left = '5px';
1805        this.closeKeyPath!.style.left = '25px';
1806      }
1807      if (this.mainMenu) {
1808        this.mainMenu.style.width = '248px';
1809        this.mainMenu.style.zIndex = '2000';
1810        this.mainMenu.style.display = 'flex';
1811      }
1812    };
1813    let icon: HTMLDivElement | undefined | null = this.mainMenu?.shadowRoot?.querySelector('div.header > div');
1814    icon!.style.pointerEvents = 'none';
1815    icon!.onclick = (e): void => {
1816      if (this.mainMenu) {
1817        this.mainMenu.style.width = '0px';
1818        this.mainMenu.style.display = 'flex';
1819        this.mainMenu.style.zIndex = '0';
1820      }
1821      if (this.sidebarButton) {
1822        this.sidebarButton.style.width = '48px';
1823        this.importConfigDiv!.style.left = '45px';
1824        this.closeKeyPath!.style.left = '65px';
1825      }
1826    };
1827  }
1828
1829  private initImportConfigEvent(): void {
1830    this.importFileBt?.addEventListener('change', (): void => {
1831      let files = this.importFileBt!.files;
1832      if (files && files.length === 1) {
1833        const reader = new FileReader();
1834        reader.readAsText(files[0], 'UTF-8');
1835        reader.onload = (e): void => {
1836          if (e.target?.result) {
1837            try {
1838              const result = parseKeyPathJson(e.target.result as string);
1839              window.publish(window.SmartEvent.UI.KeyPath, result);
1840              this.closeKeyPath!.style.display = 'block';
1841            } catch {
1842              error('json Parse Failed');
1843              this.litSearch!.setPercent('Json Parse Failed!', -1);
1844              window.setTimeout(() => {
1845                this.litSearch!.setPercent('Json Parse Failed!', 101);
1846              }, 1000);
1847            }
1848          } else {
1849            window.publish(window.SmartEvent.UI.KeyPath, []);
1850            this.closeKeyPath!.style.display = 'none';
1851          }
1852        };
1853      }
1854      this.importFileBt!.files = null;
1855      this.importFileBt!.value = '';
1856    });
1857    if (this.closeKeyPath) {
1858      this.closeKeyPath.addEventListener('click', (): void => {
1859        window.publish(window.SmartEvent.UI.KeyPath, []);
1860        this.closeKeyPath!.style.display = 'none';
1861      });
1862    }
1863  }
1864
1865  private initCustomEvents(): void {
1866    window.subscribe(window.SmartEvent.UI.MenuTrace, () => this.showContent(this.spSystemTrace!));
1867    window.subscribe(window.SmartEvent.UI.Error, (err) => {
1868      //@ts-ignore
1869      this.litSearch!.setPercent(err, -1);
1870      this.progressEL!.loading = false;
1871      this.freshMenuDisable(false);
1872    }); //@ts-ignore
1873    window.subscribe(window.SmartEvent.UI.Loading, (arg: { loading: boolean; text?: string }) => {
1874      if (arg.text) {
1875        this.litSearch!.setPercent(arg.text || '', arg.loading ? -1 : 101);
1876      }
1877      if (this.headerDiv) {
1878        this.headerDiv.style.pointerEvents = arg.loading ? 'none' : 'auto';
1879      }
1880      window.publish(window.SmartEvent.UI.MouseEventEnable, {
1881        mouseEnable: !arg.loading,
1882      });
1883      this.progressEL!.loading = arg.loading;
1884    });
1885  }
1886
1887  private initEvents(): void {
1888    this.addEventListener('copy', function (event) {
1889      SpSystemTrace.isMouseLeftDown = false;
1890      let clipdata = event.clipboardData;
1891      let value = clipdata!.getData('text/plain');
1892      let searchValue = value.toString().trim();
1893      clipdata!.setData('text/plain', searchValue);
1894    });
1895    this.initSearchEvents();
1896    this.initSystemTraceEvents();
1897    this.filterConfig!.addEventListener('click', (ev) => {
1898      SpSystemTrace.isMouseLeftDown = false;
1899      this.filterRowConfigClickHandle();
1900    });
1901    this.configClose!.addEventListener('click', (ev) => {
1902      this.filterRowConfigClickHandle();
1903    });
1904    this.cutTraceFile!.addEventListener('click', (ev) => {
1905      this.croppingFile(this.progressEL!, this.litSearch!);
1906    });
1907  }
1908
1909  private filterRowConfigClickHandle(): void {
1910    if (this!.hasAttribute('chart_filter')) {
1911      this!.removeAttribute('chart_filter');
1912      this.chartFilter!.setAttribute('hidden', '');
1913    } else {
1914      this!.removeAttribute('custom-color');
1915      this.customColor!.setAttribute('hidden', '');
1916      this.customColor!.cancelOperate();
1917      this!.setAttribute('chart_filter', '');
1918      this.chartFilter!.removeAttribute('hidden');
1919    }
1920  }
1921
1922  private initRecordEvents(): void {
1923    this.exportRecord?.addEventListener('click', () => {
1924      this.headerDiv!.style.pointerEvents = 'none';
1925      window.publish(window.SmartEvent.UI.Loading, { loading: true, text: 'Downloading trace file with mark' });
1926      window.publish(window.SmartEvent.UI.ExportRecord, { bt: this.exportRecord });
1927    });
1928  }
1929
1930  private initSearchChangeEvents(): void {
1931    let timer: NodeJS.Timeout;
1932    this.litSearch!.valueChangeHandler = (value: string): void => {
1933      Utils.currentSelectTrace = this.litSearch?.getSearchTraceId();
1934      this.litSearch!.currenSearchValue = value;
1935      if (value.length > 0) {
1936        this.progressEL!.loading = true;
1937      } else {
1938        this.progressEL!.loading = false;
1939      }
1940      if (this.litSearch!.index > 0) {
1941        let currentEntry = this.litSearch!.list[this.litSearch!.index];
1942        cancelCurrentTraceRowHighlight(this.spSystemTrace!, currentEntry);
1943      }
1944      this.litSearch!.list = [];
1945      if (timer) {
1946        clearTimeout(timer);
1947      }
1948      timer = setTimeout(() => {
1949        this.litSearch!.isClearValue = false;
1950        if (value.length > 0) {
1951          let list = [];
1952          this.spSystemTrace!.searchCPU(value).then((cpus) => {
1953            list = cpus;
1954            let asyncFuncArr = this.spSystemTrace!.seachAsyncFunc(value);
1955            this.spSystemTrace!.searchFunction(list, asyncFuncArr, value).then((mixedResults) => {
1956              if (this.litSearch!.searchValue !== '') {
1957                if (!Utils.isDistributedMode()) {
1958                  this.litSearch!.list = this.spSystemTrace!.searchSdk(mixedResults, value);
1959                } else {
1960                  this.litSearch!.list = mixedResults;
1961                }
1962                this.litSearch!.index = this.spSystemTrace!.showStruct(false, -1, this.litSearch!.list);
1963              }
1964              this.progressEL!.loading = false;
1965            });
1966          });
1967        } else {
1968          let indexEL = this.litSearch!.shadowRoot!.querySelector<HTMLSpanElement>('#index');
1969          indexEL!.textContent = '0';
1970          this.litSearch!.list = [];
1971          this.spSystemTrace?.visibleRows.forEach((it) => {
1972            it.highlight = false;
1973            it.draw();
1974          });
1975          this.spSystemTrace?.timerShaftEL?.removeTriangle('inverted');
1976        }
1977      }, 1000);
1978    };
1979  }
1980  private initSearchEvents(): void {
1981    this.litSearch!.addEventListener('previous-data', (ev) => {
1982      if (this.progressEL!.loading) {
1983        return;
1984      }
1985      this.litSearch!.index = this.spSystemTrace!.showStruct(true, this.litSearch!.index, this.litSearch!.list);
1986      this.litSearch!.blur();
1987    });
1988    this.litSearch!.addEventListener('next-data', (ev) => {
1989      if (this.progressEL!.loading) {
1990        return;
1991      }
1992      this.litSearch!.index = this.spSystemTrace!.showStruct(false, this.litSearch!.index, this.litSearch!.list);
1993      this.litSearch!.blur();
1994    });
1995    // 翻页事件
1996    this.litSearch!.addEventListener('retarget-data', (ev) => {
1997      if (this.progressEL!.loading) {
1998        return;
1999      }
2000      this.litSearch!.index = this.spSystemTrace!.showStruct(
2001        false,
2002        //@ts-ignore
2003        ev.detail.value,
2004        this.litSearch!.list,
2005        //@ts-ignore
2006        ev.detail.value
2007      );
2008      this.litSearch!.blur();
2009    });
2010    this.litSearch!.addEventListener('trace-change', (e): void => {
2011      this.spSystemTrace?.clickEmptyArea();
2012      this.spSystemTrace!.searchTargetTraceHandler();
2013    });
2014    this.initSearchChangeEvents();
2015  }
2016
2017  private initSystemTraceEvents(): void {
2018    this.spSystemTrace?.addEventListener('trace-previous-data', (ev) => {
2019      if (this.progressEL!.loading) {
2020        return;
2021      }
2022      this.litSearch!.index = this.spSystemTrace!.showStruct(true, this.litSearch!.index, this.litSearch!.list);
2023    });
2024    this.spSystemTrace?.addEventListener('trace-next-data', (ev) => {
2025      if (this.progressEL!.loading) {
2026        return;
2027      }
2028      this.litSearch!.index = this.spSystemTrace!.showStruct(false, this.litSearch!.index, this.litSearch!.list);
2029    });
2030  }
2031
2032  private showContent(showNode: HTMLElement): void {
2033    if (showNode === this.spSystemTrace) {
2034      this.menu!.style.pointerEvents = 'auto';
2035      this.sidebarButton!.style.pointerEvents = 'auto';
2036      this.search = true;
2037      this.litRecordSearch!.style.display = 'none';
2038      this.litSearch!.style.display = 'block';
2039      this.contentRightOption!.style.display = 'flex';
2040      this.contentLeftOption!.style.display = 'flex';
2041      this.contentCenterOption!.style.display = 'flex';
2042      window.publish(window.SmartEvent.UI.KeyboardEnable, {
2043        enable: true,
2044      });
2045      this.filterConfig!.style.visibility = 'visible';
2046    } else {
2047      this.removeAttribute('custom-color');
2048      this.customColor!.setAttribute('hidden', '');
2049      this.customColor!.cancelOperate();
2050      this.menu!.style.pointerEvents = 'none';
2051      this.sidebarButton!.style.pointerEvents = 'none';
2052      this.search = this.litSearch!.isLoading;
2053      this.contentRightOption!.style.display = 'none';
2054      this.contentLeftOption!.style.display = 'none';
2055      this.contentCenterOption!.style.display = 'none';
2056      if (!this.search) {
2057        this.litSearch!.style.display = 'none';
2058        this.litRecordSearch!.style.display = 'block';
2059      }
2060      window.publish(window.SmartEvent.UI.KeyboardEnable, {
2061        enable: false,
2062      });
2063      this.filterConfig!.style.visibility = 'hidden';
2064      this.removeAttribute('chart_filter');
2065      this.chartFilter!.setAttribute('hidden', '');
2066    }
2067    this.childComponent!.forEach((node) => {
2068      if (this.hasAttribute('chart_filter')) {
2069        this.removeAttribute('chart_filter');
2070      }
2071      if (this.hasAttribute('custom-color')) {
2072        this.removeAttribute('custom-color');
2073        this.customColor!.setAttribute('hidden', '');
2074        this.customColor!.cancelOperate();
2075      }
2076      if (node === showNode) {
2077        showNode.style.visibility = 'visible';
2078      } else {
2079        (node! as HTMLElement).style.visibility = 'hidden';
2080      }
2081    });
2082  }
2083
2084  private validateFileCacheLost(): void {
2085    caches.has(getThreadPoolTraceBufferCacheKey('1')).then((exist): void => {
2086      if (!exist) {
2087        this.mainMenu!.menus?.forEach((mg) => {
2088          mg.children.forEach((mi: MenuItem) => {
2089            if (mi.title === 'Download File') {
2090              mi.disabled = true;
2091            }
2092          });
2093        });
2094        this.cutTraceFile!.style.display = 'none';
2095        this.mainMenu!.menus = this.mainMenu!.menus;
2096      }
2097    });
2098  }
2099
2100  private refreshPageList(
2101    pageListDiv: HTMLDivElement,
2102    previewButton: HTMLDivElement,
2103    nextButton: HTMLDivElement,
2104    pageInput: HTMLInputElement,
2105    currentPageNum: number,
2106    maxPageNumber: number
2107  ): void {
2108    if (pageInput) {
2109      pageInput.textContent = currentPageNum.toString();
2110      pageInput.value = currentPageNum.toString();
2111      pageInput.style.pointerEvents = 'none';
2112    }
2113    let pageText: string[] = [];
2114    if (maxPageNumber > 7) {
2115      pageText = this.largePageHandler(currentPageNum, maxPageNumber, previewButton, nextButton);
2116    } else {
2117      pageText = [];
2118      for (let index = 0; index < maxPageNumber; index++) {
2119        pageText.push((index + 1).toString());
2120      }
2121    }
2122    this.pageNodeHandler(pageListDiv, pageText, currentPageNum);
2123  }
2124
2125  private pageNodeHandler(pageListDiv: HTMLDivElement, pageText: Array<string>, currentPageNum: number): void {
2126    let pageNodeList = pageListDiv.querySelectorAll<HTMLDivElement>('div');
2127    if (pageNodeList.length > 0) {
2128      pageNodeList.forEach((page, index) => {
2129        page.textContent = pageText[index].toString();
2130        page.title = pageText[index];
2131        if (currentPageNum.toString() === pageText[index]) {
2132          page.setAttribute('selected', '');
2133        } else {
2134          if (page.hasAttribute('selected')) {
2135            page.removeAttribute('selected');
2136          }
2137        }
2138      });
2139    } else {
2140      pageListDiv.innerHTML = '';
2141      pageText.forEach((page) => {
2142        let element = document.createElement('div');
2143        element.className = 'page-number pagination';
2144        element.textContent = page.toString();
2145        element.title = page.toString();
2146        if (currentPageNum.toString() === page.toString()) {
2147          element.setAttribute('selected', '');
2148        }
2149        pageListDiv.appendChild(element);
2150      });
2151    }
2152  }
2153
2154  private largePageHandler(
2155    currentPageNum: number,
2156    maxPageNumber: number,
2157    previewButton: HTMLDivElement,
2158    nextButton: HTMLDivElement
2159  ): Array<string> {
2160    switch (currentPageNum) {
2161      case 1:
2162      case 2:
2163      case 3:
2164      case 4:
2165      case 5:
2166        return ['1', '2', '3', '4', '5', '...', maxPageNumber.toString()];
2167      case maxPageNumber:
2168      case maxPageNumber - 1:
2169      case maxPageNumber - 2:
2170      case maxPageNumber - 3:
2171      case maxPageNumber - 4:
2172        return [
2173          '1',
2174          '...',
2175          (maxPageNumber - 4).toString(),
2176          (maxPageNumber - 3).toString(),
2177          (maxPageNumber - 2).toString(),
2178          (maxPageNumber - 1).toString(),
2179          maxPageNumber.toString(),
2180        ];
2181      default:
2182        nextButton.style.pointerEvents = 'auto';
2183        previewButton!.style.pointerEvents = 'auto';
2184        nextButton.style.opacity = '1';
2185        previewButton!.style.opacity = '1';
2186        return [
2187          '1',
2188          '...',
2189          (currentPageNum - 1).toString(),
2190          currentPageNum.toString(),
2191          (currentPageNum + 1).toString(),
2192          '...',
2193          maxPageNumber.toString(),
2194        ];
2195    }
2196  }
2197
2198  /**
2199   * 修改颜色或者主题,重新绘制侧边栏和泳道图
2200   * @param theme 当前主题(深色和浅色)
2201   * @param colorsArray 预览的情况下传入
2202   */
2203  changeTheme(theme: Theme, colorsArray?: Array<string>): void {
2204    if (!colorsArray) {
2205      this.customColor!.setRadioChecked(theme);
2206    }
2207    if (theme === Theme.DARK) {
2208      this.changeDarkTheme(colorsArray);
2209    } else {
2210      this.changeLightTheme(colorsArray);
2211    }
2212    this.spSystemTrace!.timerShaftEL!.rangeRuler!.draw();
2213    if (this.colorTransiton) {
2214      clearTimeout(this.colorTransiton);
2215    }
2216    this.colorTransiton = setTimeout(() => (this.mainMenu!.style.transition = '0s'), 1000);
2217  }
2218
2219  private changeDarkTheme(colorsArray?: Array<string>): void {
2220    let menuGroup = this.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuGroup>('lit-main-menu-group');
2221    let menuItem = this.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
2222    this.mainMenu!.style.backgroundColor = '#262f3c';
2223    this.mainMenu!.style.transition = '1s';
2224    menuGroup!.forEach((item) => {
2225      let groupName = item!.shadowRoot!.querySelector('.group-name') as LitMainMenuGroup;
2226      let groupDescribe = item!.shadowRoot!.querySelector('.group-describe') as LitMainMenuGroup;
2227      groupName.style.color = 'white';
2228      groupDescribe.style.color = 'white';
2229    });
2230    menuItem!.forEach((item) => {
2231      item.style.color = 'white';
2232    });
2233    if (
2234      !colorsArray &&
2235      window.localStorage.getItem('DarkThemeColors') &&
2236      ColorUtils.FUNC_COLOR_B !== JSON.parse(window.localStorage.getItem('DarkThemeColors')!)
2237    ) {
2238      ColorUtils.MD_PALETTE = JSON.parse(window.localStorage.getItem('DarkThemeColors')!);
2239      ColorUtils.FUNC_COLOR = JSON.parse(window.localStorage.getItem('DarkThemeColors')!);
2240    } else if (colorsArray) {
2241      ColorUtils.MD_PALETTE = colorsArray;
2242      ColorUtils.FUNC_COLOR = colorsArray;
2243    } else {
2244      ColorUtils.MD_PALETTE = ColorUtils.FUNC_COLOR_B;
2245      ColorUtils.FUNC_COLOR = ColorUtils.FUNC_COLOR_B;
2246    }
2247  }
2248
2249  private changeLightTheme(colorsArray?: Array<string>): void {
2250    let menuGroup = this.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuGroup>('lit-main-menu-group');
2251    let menuItem = this.mainMenu!.shadowRoot?.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
2252    this.mainMenu!.style.backgroundColor = 'white';
2253    this.mainMenu!.style.transition = '1s';
2254    menuGroup!.forEach((item) => {
2255      let groupName = item!.shadowRoot!.querySelector('.group-name') as LitMainMenuGroup;
2256      let groupDescribe = item!.shadowRoot!.querySelector('.group-describe') as LitMainMenuGroup;
2257      groupName.style.color = 'black';
2258      groupDescribe.style.color = '#92959b';
2259    });
2260    menuItem!.forEach((item) => {
2261      item.style.color = 'black';
2262    });
2263    if (
2264      !colorsArray &&
2265      window.localStorage.getItem('LightThemeColors') &&
2266      ColorUtils.FUNC_COLOR_A !== JSON.parse(window.localStorage.getItem('LightThemeColors')!)
2267    ) {
2268      ColorUtils.MD_PALETTE = JSON.parse(window.localStorage.getItem('LightThemeColors')!);
2269      ColorUtils.FUNC_COLOR = JSON.parse(window.localStorage.getItem('LightThemeColors')!);
2270    } else if (colorsArray) {
2271      ColorUtils.MD_PALETTE = colorsArray;
2272      ColorUtils.FUNC_COLOR = colorsArray;
2273    } else {
2274      ColorUtils.MD_PALETTE = ColorUtils.FUNC_COLOR_A;
2275      ColorUtils.FUNC_COLOR = ColorUtils.FUNC_COLOR_A;
2276    }
2277  }
2278
2279  private downloadOnLineFile(
2280    url: string,
2281    download: boolean,
2282    openUrl: (buffer: ArrayBuffer, fileName: string, showFileName: string, fileSize: number) => void,
2283    openFileHandler: (path: string) => void
2284  ): void {
2285    if (download) {
2286      fetch(url)
2287        .then((res) => {
2288          res.arrayBuffer().then((arrayBuf) => {
2289            let fileName = url.split('/').reverse()[0];
2290            this.traceFileName = fileName;
2291            let showFileName =
2292              fileName.lastIndexOf('.') === -1 ? fileName : fileName.substring(0, fileName.lastIndexOf('.'));
2293            openUrl(arrayBuf, fileName, showFileName, arrayBuf.byteLength);
2294          });
2295        })
2296        .catch((e) => {
2297          let api = `${window.location.origin}/application/download-file`;
2298          fetch(api, {
2299            method: 'POST',
2300            headers: {
2301              'Content-Type': 'application/x-www-form-urlencoded',
2302            },
2303            body: new URLSearchParams({
2304              url: url,
2305            }),
2306          })
2307            .then((response) => response.json())
2308            .then((res) => {
2309              if (res.code === 0 && res.success) {
2310                let resultUrl = res.data.url;
2311                if (resultUrl) {
2312                  openFileHandler(resultUrl.toString().replace(/\\/g, '/'));
2313                }
2314              }
2315            });
2316        });
2317    } else {
2318      openFileHandler(url);
2319    }
2320  }
2321
2322  private croppingFile(progressEL: LitProgressBar, litSearch: LitSearch): void {
2323    let cutLeftNs = TraceRow.rangeSelectObject?.startNS || 0;
2324    let cutRightNs = TraceRow.rangeSelectObject?.endNS || 0;
2325    if (cutRightNs === cutLeftNs) {
2326      return;
2327    }
2328    let recordStartNS = window.recordStartNS;
2329    let cutLeftTs = recordStartNS + cutLeftNs;
2330    let cutRightTs = recordStartNS + cutRightNs;
2331    let minCutDur = 1_000_000;
2332    if (cutRightTs - cutLeftTs < minCutDur) {
2333      let unitTs = (cutRightTs - cutLeftTs) / 2;
2334      let midTs = cutLeftTs + unitTs;
2335      cutLeftTs = midTs - minCutDur / 2;
2336      cutRightTs = midTs + minCutDur / 2;
2337    }
2338    progressEL.loading = true;
2339    threadPool.cutFile(cutLeftTs, cutRightTs, (status: boolean, msg: string, cutBuffer?: ArrayBuffer) => {
2340      progressEL.loading = false;
2341      if (status) {
2342        FlagsConfig.updateFlagsConfig('FfrtConvert', 'Disabled');
2343        let traceFileName = this.traceFileName as string;
2344        let cutIndex = traceFileName.indexOf('_cut_');
2345        let fileType = traceFileName.substring(traceFileName.lastIndexOf('.'));
2346        let traceName = document.title.replace(/\s*\([^)]*\)/g, '').trim();
2347        if (cutIndex !== -1) {
2348          traceName = traceName.substring(0, cutIndex);
2349        }
2350        if (cutBuffer !== undefined && cutBuffer.byteLength <= 12) {
2351          this.litSearch!.setPercent('The cut is empty data. Select a time range for valid data!', -1);
2352          this.progressEL!.loading = false;
2353          this.freshMenuDisable(false);
2354          return;
2355        }
2356        let blobUrl = URL.createObjectURL(new Blob([cutBuffer!]));
2357        window.open(
2358          `index.html?link=true&local=true&traceName=${encodeURIComponent(
2359            traceName
2360          )}_cut_${cutLeftTs}${fileType}&trace=${encodeURIComponent(blobUrl)}`
2361        );
2362      } else {
2363        litSearch.setPercent(msg, -1);
2364        window.setTimeout(() => {
2365          litSearch.setPercent(msg, 101);
2366        }, 1000);
2367      }
2368    });
2369  }
2370
2371  private downloadDB(mainMenu: LitMainMenu, fileDbName: string): void {
2372    let fileName = `${fileDbName?.substring(0, fileDbName?.lastIndexOf('.'))}.db`;
2373    threadPool.submit(
2374      'download-db',
2375      '',
2376      {},
2377      (reqBufferDB: ArrayBuffer) => {
2378        let a = document.createElement('a');
2379        a.href = URL.createObjectURL(new Blob([reqBufferDB]));
2380        a.download = fileName;
2381        a.click();
2382        this.itemIconLoading(mainMenu, 'Current Trace', 'Download Database', true);
2383        let timer = setInterval(() => {
2384          this.itemIconLoading(mainMenu, 'Current Trace', 'Download Database', false);
2385          clearInterval(timer);
2386        }, 4000);
2387      },
2388      'download-db'
2389    );
2390  }
2391
2392  private async download(mainMenu: LitMainMenu, fileName: string, isServer: boolean, dbName?: string): Promise<void> {
2393    let a = document.createElement('a');
2394    if (isServer) {
2395      if (dbName !== '') {
2396        let file = dbName?.substring(0, dbName?.lastIndexOf('.')) + fileName.substring(fileName.lastIndexOf('.'));
2397        a.href = `https://${window.location.host.split(':')[0]}:${window.location.port}${file}`;
2398      } else {
2399        return;
2400      }
2401    } else {
2402      let buffer = await readTraceFileBuffer();
2403      if (buffer) {
2404        a.href = URL.createObjectURL(new Blob([buffer]));
2405      }
2406    }
2407    a.download = fileName;
2408    a.click();
2409    window.URL.revokeObjectURL(a.href);
2410    this.itemIconLoading(mainMenu, 'Current Trace', 'Download File', true);
2411    let timer = setInterval(() => {
2412      this.itemIconLoading(mainMenu, 'Current Trace', 'Download File', false);
2413      clearInterval(timer);
2414    }, 4000);
2415  }
2416
2417  private itemIconLoading(mainMenu: LitMainMenu, groupName: string, itemName: string, start: boolean): void {
2418    let currentTraceGroup = mainMenu.shadowRoot?.querySelector<LitMainMenuGroup>(
2419      `lit-main-menu-group[title='${groupName}']`
2420    );
2421    let downloadItem = currentTraceGroup!.querySelector<LitMainMenuItem>(`lit-main-menu-item[title='${itemName}']`);
2422    let downloadIcon = downloadItem!.shadowRoot?.querySelector('.icon') as LitIcon;
2423    if (start) {
2424      downloadItem!.setAttribute('icon', 'convert-loading');
2425      downloadIcon.setAttribute('spin', '');
2426    } else {
2427      downloadItem!.setAttribute('icon', 'download');
2428      downloadIcon.removeAttribute('spin');
2429    }
2430  }
2431
2432  freshMenuDisable(disable: boolean): void {
2433    // @ts-ignore
2434    this.mainMenu!.menus[0].children[0].disabled = disable;
2435    // @ts-ignore
2436    this.mainMenu!.menus[0].children[1].disabled = disable;
2437    if (this.mainMenu!.menus!.length > 2) {
2438      // @ts-ignore
2439      this.mainMenu!.menus[1].children.map((it) => (it.disabled = disable));
2440    }
2441    this.mainMenu!.menus = this.mainMenu!.menus;
2442    this.filterConfig!.style.visibility = disable ? 'hidden' : 'visible';
2443  }
2444}
2445