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