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