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