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