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