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