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