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