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 { LitTable } from '../../../../../base-ui/table/lit-table'; 18import { SelectionParam } from '../../../../bean/BoxSelection'; 19import { LitChartPie } from '../../../../../base-ui/chart/pie/LitChartPie'; 20import '../../../../../base-ui/chart/pie/LitChartPie'; 21import { LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar'; 22import { procedurePool } from '../../../../database/Procedure'; 23import { TabPaneFilter } from '../TabPaneFilter'; 24import { LitCheckBox } from '../../../../../base-ui/checkbox/LitCheckBox'; 25import { initSort } from '../SheetUtils'; 26import { TabpanePerfProfile } from './TabPerfProfile'; 27import { TabPanePerfAnalysisHtml } from './TabPanePerfAnalysis.html'; 28import { WebSocketManager } from '../../../../../webSocket/WebSocketManager'; 29import { Constants, TypeConstants } from '../../../../../webSocket/Constants'; 30 31@element('tabpane-perf-analysis') 32export class TabPanePerfAnalysis extends BaseElement { 33 private currentSelection: SelectionParam | unknown; 34 private perfAnalysisPie: LitChartPie | null | undefined; 35 private processData!: Array<unknown>; 36 private pidData!: unknown[]; 37 private threadData!: unknown[]; 38 private soData!: unknown[]; 39 private functionData!: unknown[]; 40 private perfTableThread: LitTable | null | undefined; 41 private perfTableProcess: LitTable | null | undefined; 42 private perfTableSo: LitTable | null | undefined; 43 private tableFunction: LitTable | null | undefined; 44 private sumCount: number | undefined | null; 45 private sumEventCount: number | undefined | null; 46 private perfAnalysisRange: HTMLLabelElement | null | undefined; 47 private back: HTMLDivElement | null | undefined; 48 private tabName: HTMLDivElement | null | undefined; 49 private progressEL: LitProgressBar | null | undefined; 50 private processName: string = ''; 51 private threadName: string = ''; 52 private callChainMap!: Map<number, unknown>; 53 private sortColumn: string = ''; 54 private sortType: number = 0; 55 private allProcessCount!: {}; 56 private allThreadCount!: {}; 57 private allLibCount!: {}; 58 private allSymbolCount!: {}; 59 private currentLevel = -1; 60 private currentLevelData!: Array<unknown>; 61 private titleEl: HTMLDivElement | undefined | null; 62 private filterEl: TabPaneFilter | undefined | null; 63 private hideProcessCheckBox: LitCheckBox | undefined | null; 64 private hideThreadCheckBox: LitCheckBox | undefined | null; 65 private checkBoxs: NodeListOf<LitCheckBox> | undefined | null; 66 private tableArray: NodeListOf<LitTable> | undefined | null; 67 private isComplete: boolean = true; 68 private currentSelectionParam: SelectionParam | undefined | null; 69 static tabLoadingList: Array<string> = []; 70 private vaddrList: Array<unknown> = []; 71 private selectedTabfileName: string = ''; 72 private clickFuncVaddrList: Array<unknown> = []; 73 private functionListener!: Function | undefined | null; 74 private currentSoName: string = ''; 75 76 set data(val: SelectionParam) { 77 if (val === this.currentSelection) { 78 this.pidData.unshift(this.allProcessCount); 79 this.perfTableProcess!.recycleDataSource = this.pidData; 80 // @ts-ignore 81 this.pidData.shift(this.allProcessCount); 82 return; 83 } 84 if (this.tableArray) { 85 for (let table of this.tableArray) { 86 initSort(table!, this.sortColumn, this.sortType); 87 } 88 } 89 TabPanePerfAnalysis.tabLoadingList = []; 90 this.currentSelection = val; 91 this.tabName!.textContent = ''; 92 this.hideProcessCheckBox!.checked = false; 93 this.hideThreadCheckBox!.checked = false; 94 this.reset(this.perfTableProcess!, false); 95 this.titleEl!.textContent = ''; 96 this.perfAnalysisRange!.textContent = `Selected range: ${parseFloat( 97 ((val.rightNs - val.leftNs) / 1000000.0).toFixed(5) 98 )} ms`; 99 this.currentSelectionParam = val; 100 if (!this.callChainMap && this.isComplete) { 101 this.isComplete = false; 102 TabPanePerfAnalysis.tabLoadingList.push('analysis'); 103 this.getCallChainDataFromWorker(val); 104 } 105 } 106 107 private initPerfTableListener(): void { 108 for (let perfTable of this.tableArray!) { 109 let querySelector = perfTable.shadowRoot?.querySelector<HTMLDivElement>('.table'); 110 if (querySelector) { 111 querySelector.style.height = 'calc(100% - 31px)'; 112 } 113 perfTable!.addEventListener('column-click', (evt) => { 114 // @ts-ignore 115 this.sortColumn = evt.detail.key; 116 // @ts-ignore 117 this.sortType = evt.detail.sort; 118 this.sortByColumn(); 119 }); 120 perfTable!.addEventListener('contextmenu', function (event) { 121 event.preventDefault(); // 阻止默认的上下文菜单弹框 122 }); 123 perfTable!.addEventListener('row-hover', (evt) => { 124 this.perfTableRowHover(evt); 125 }); 126 perfTable!.addEventListener('row-click', (evt) => { 127 // @ts-ignore 128 let detail = evt.detail; 129 let perfProfileTab = this.parentElement?.parentElement?.querySelector<TabpanePerfProfile>( 130 '#box-perf-profile > tabpane-perf-profile' 131 ); 132 if (detail.button === 2 && detail.tableName && detail.tableName !== '') { 133 perfProfileTab!.cWidth = this.clientWidth; 134 perfProfileTab!.currentLevel = this.currentLevel; 135 if (this.hideProcessCheckBox?.checked && this.hideThreadCheckBox?.checked) { 136 detail.data.pid = undefined; 137 detail.data.tid = undefined; 138 } 139 perfProfileTab!.rowClickData = detail.data; 140 let title; 141 if (this.titleEl?.textContent === '') { 142 title = detail.data.tableName; 143 } else { 144 title = `${this.titleEl?.textContent} / ${detail.data.tableName}`; 145 } 146 perfProfileTab!.pieTitle = title; 147 // 是否是在表格上右键点击跳转到火焰图的 148 // @ts-ignore 149 this.currentSelection.isRowClick = true; 150 perfProfileTab!.data = this.currentSelection; 151 } 152 }); 153 } 154 } 155 156 private perfTableRowHover(evt: Event): void { 157 // @ts-ignore 158 let detail = evt.detail; 159 if (detail.data) { 160 let data = detail.data; 161 data.isHover = true; 162 if (detail.callBack) { 163 detail.callBack(true); 164 } 165 } 166 this.perfAnalysisPie?.showHover(); 167 this.perfAnalysisPie?.hideTip(); 168 } 169 170 initElements(): void { 171 this.perfAnalysisPie = this.shadowRoot!.querySelector<LitChartPie>('#perf-chart-pie'); 172 this.perfAnalysisRange = this.shadowRoot?.querySelector('#time-range'); 173 this.perfTableProcess = this.shadowRoot!.querySelector<LitTable>('#tb-process-usage'); 174 this.perfTableSo = this.shadowRoot!.querySelector<LitTable>('#tb-so-usage'); 175 this.tableFunction = this.shadowRoot!.querySelector<LitTable>('#tb-function-usage'); 176 this.perfTableThread = this.shadowRoot!.querySelector<LitTable>('#tb-thread-usage'); 177 this.back = this.shadowRoot!.querySelector<HTMLDivElement>('.perf-go-back'); 178 this.tabName = this.shadowRoot!.querySelector<HTMLDivElement>('.perf-subheading'); 179 this.progressEL = this.shadowRoot?.querySelector('.perf-progress') as LitProgressBar; 180 this.titleEl = this.shadowRoot!.querySelector<HTMLDivElement>('.title'); 181 this.filterEl = this.shadowRoot?.querySelector('#filter'); 182 this.filterEl!.setOptionsList(['Hide Process', 'Hide Thread']); 183 let popover = this.filterEl!.shadowRoot!.querySelector('#check-popover'); 184 this.hideProcessCheckBox = popover!!.querySelector<LitCheckBox>('div > #hideProcess'); 185 this.hideThreadCheckBox = popover!!.querySelector<LitCheckBox>('div > #hideThread'); 186 this.checkBoxs = popover!.querySelectorAll<LitCheckBox>('.check-wrap > lit-check-box'); 187 this.tableArray = this.shadowRoot!.querySelectorAll('lit-table') as NodeListOf<LitTable>; 188 this.initPerfTableListener(); 189 for (let box of this.checkBoxs) { 190 box!.addEventListener('change', () => { 191 if ( 192 (this.hideProcessCheckBox!.checked && this.hideThreadCheckBox!.checked) || 193 (this.hideThreadCheckBox!.checked && 194 // @ts-ignore 195 this.currentSelection.perfThread.length > 0 && 196 // @ts-ignore 197 this.currentSelection.perfProcess.length === 0) 198 ) { 199 this.processName = ''; 200 this.hideThread(); 201 this.back!.style.visibility = 'hidden'; 202 } else if (this.hideProcessCheckBox!.checked && !this.hideThreadCheckBox!.checked) { 203 this.hideProcess(); 204 } else { 205 // @ts-ignore 206 this.getHiperfProcess(this.currentSelection); 207 } 208 }); 209 } 210 this.getBack(); 211 this.addRowClickEventListener(this.perfTableProcess!, this.perfProcessLevelClickEvent.bind(this)); 212 this.addRowClickEventListener(this.perfTableThread!, this.perfThreadLevelClickEvent.bind(this)); 213 this.addRowClickEventListener(this.perfTableSo!, this.perfSoLevelClickEvent.bind(this)); 214 this.addRowClickEventListener(this.tableFunction!, this.functionClickEvent.bind(this)); 215 } 216 217 private addRowClickEventListener(table: LitTable, clickEvent: Function): void { 218 table.addEventListener('row-click', (evt) => { 219 // @ts-ignore 220 const detail = evt.detail; 221 // @ts-ignore 222 const data = detail.data; 223 if (detail.button === 0 && data.tableName && data.count !== 0) { 224 clickEvent(data, this.currentSelection); 225 } 226 }); 227 } 228 229 private reset(showTable: LitTable, isShowBack: boolean): void { 230 this.clearData(); 231 if (isShowBack) { 232 this.back!.style.visibility = 'visible'; 233 } else { 234 this.back!.style.visibility = 'hidden'; 235 this.titleEl!.textContent = ''; 236 } 237 if (this.tableArray) { 238 for (let table of this.tableArray) { 239 if (table === showTable) { 240 initSort(table!, this.sortColumn, this.sortType); 241 table.style.display = 'grid'; 242 table!.removeAttribute('hideDownload'); 243 } else { 244 table!.style.display = 'none'; 245 table.setAttribute('hideDownload', ''); 246 } 247 } 248 } 249 } 250 251 private clearData(): void { 252 this.perfAnalysisPie!.dataSource = []; 253 this.perfTableProcess!.recycleDataSource = []; 254 this.perfTableThread!.recycleDataSource = []; 255 this.perfTableSo!.recycleDataSource = []; 256 this.tableFunction!.recycleDataSource = []; 257 } 258 259 private showAssignLevel( 260 showTable: LitTable, 261 hideTable: LitTable, 262 currentLevel: number, 263 currentLevelData: Array<unknown> 264 ): void { 265 showTable!.style.display = 'grid'; 266 hideTable!.style.display = 'none'; 267 hideTable.setAttribute('hideDownload', ''); 268 showTable?.removeAttribute('hideDownload'); 269 this.currentLevel = currentLevel; 270 this.currentLevelData = currentLevelData; 271 } 272 273 private getBack(): void { 274 this.back!.addEventListener('click', () => { 275 if (this.tabName!.textContent === 'Statistic By Thread Count') { 276 this.showAssignLevel(this.perfTableProcess!, this.perfTableThread!, 0, this.pidData); 277 this.back!.style.visibility = 'hidden'; 278 // @ts-ignore 279 this.processPieChart(this.currentSelection); 280 } else if (this.tabName!.textContent === 'Statistic By Library Count') { 281 if (this.hideThreadCheckBox?.checked) { 282 this.showAssignLevel(this.perfTableProcess!, this.perfTableSo!, 0, this.pidData); 283 this.back!.style.visibility = 'hidden'; 284 // @ts-ignore 285 this.processPieChart(this.currentSelection); 286 } else { 287 this.showAssignLevel(this.perfTableThread!, this.perfTableSo!, 1, this.threadData); 288 // @ts-ignore 289 this.threadPieChart(this.currentSelection); 290 } 291 } else if (this.tabName!.textContent === 'Statistic By Function Count') { 292 this.showAssignLevel(this.perfTableSo!, this.tableFunction!, 2, this.soData); 293 this.libraryPieChart(); 294 } 295 if ( 296 (this.hideProcessCheckBox?.checked && this.hideThreadCheckBox?.checked) || 297 (this.hideProcessCheckBox?.checked && this.tabName!.textContent === 'Statistic By Thread Count') || 298 (this.hideThreadCheckBox?.checked && 299 this.tabName!.textContent === 'Statistic By Library Count' && 300 // @ts-ignore 301 this.currentSelection.perfThread.length > 0 && 302 // @ts-ignore 303 this.currentSelection.perfProcess.length === 0) 304 ) { 305 this.back!.style.visibility = 'hidden'; 306 this.titleEl!.textContent = ''; 307 } 308 }); 309 } 310 311 private hideProcess(): void { 312 this.reset(this.perfTableThread!, false); 313 this.showAssignLevel(this.perfTableThread!, this.perfTableProcess!, 1, this.perfTableThread!.recycleDataSource); 314 this.processName = ''; 315 this.titleEl!.textContent = ''; 316 // @ts-ignore 317 this.getHiperfThread(null, this.currentSelection); 318 } 319 320 private hideThread(it?: unknown): void { 321 this.reset(this.perfTableSo!, true); 322 this.showAssignLevel(this.perfTableSo!, this.perfTableProcess!, 1, this.soData); 323 this.threadName = ''; 324 if (it) { 325 // @ts-ignore 326 this.getHiperfSo(it, this.currentSelection); 327 } else { 328 // @ts-ignore 329 this.getHiperfSo(null, this.currentSelection); 330 } 331 } 332 333 private processPieChart(val: SelectionParam): void { 334 // @ts-ignore 335 this.sumCount = this.allProcessCount.allCount; 336 // @ts-ignore 337 this.sumEventCount = this.allProcessCount.allEventCount; 338 this.perfAnalysisPie!.config = { 339 appendPadding: 0, 340 data: this.getPerfPieChartData(this.pidData), 341 angleField: 'count', 342 colorField: 'tableName', 343 radius: 1, 344 label: { 345 type: 'outer', 346 }, 347 tip: (perfObj): string => { 348 return `<div> 349 <div>Process:${ 350 // @ts-ignore 351 perfObj.obj.tableName 352 }</div> 353 <div>Sample Count:${ 354 // @ts-ignore 355 perfObj.obj.count 356 }</div> 357 <div>Percent:${ 358 // @ts-ignore 359 perfObj.obj.percent 360 }%</div> 361 <div>Event Count:${ 362 // @ts-ignore 363 perfObj.obj.eventCount 364 }</div> 365 <div>Percent:${ 366 // @ts-ignore 367 perfObj.obj.eventPercent 368 }%</div> 369 </div> 370 `; 371 }, 372 angleClick: (it): void => { 373 // @ts-ignore 374 if (it.tableName !== 'other') { 375 this.perfProcessLevelClickEvent(it, val); 376 } 377 }, 378 hoverHandler: (perfAnalyData): void => { 379 if (perfAnalyData) { 380 this.perfTableProcess!.setCurrentHover(perfAnalyData); 381 } else { 382 this.perfTableProcess!.mouseOut(); 383 } 384 }, 385 interactions: [ 386 { 387 type: 'element-active', 388 }, 389 ], 390 }; 391 this.titleEl!.textContent = ''; 392 this.tabName!.textContent = 'Statistic By Process Count'; 393 if (this.pidData.length > 0) { 394 this.pidData.unshift(this.allProcessCount); 395 } 396 this.perfTableProcess!.recycleDataSource = this.pidData; 397 this.perfTableProcess?.reMeauseHeight(); 398 // @ts-ignore 399 this.pidData.shift(this.allProcessCount); 400 this.currentLevelData = this.pidData; 401 } 402 403 private perfProcessLevelClickEvent(it: unknown, val: SelectionParam): void { 404 if (this.hideThreadCheckBox!.checked) { 405 this.hideThread(it); 406 this.showAssignLevel(this.perfTableSo!, this.perfTableProcess!, 1, this.soData); 407 } else { 408 this.reset(this.perfTableThread!, true); 409 this.showAssignLevel(this.perfTableThread!, this.perfTableProcess!, 1, this.threadData); 410 this.getHiperfThread(it, val); 411 } 412 // @ts-ignore 413 this.titleEl!.textContent = it.tableName; 414 // @ts-ignore 415 this.processName = it.tableName; 416 this.perfAnalysisPie?.hideTip(); 417 } 418 419 private threadPieChart(val: SelectionParam): void { 420 if (val.perfThread.length > 0 && val.perfProcess.length === 0) { 421 this.back!.style.visibility = 'hidden'; 422 this.titleEl!.textContent = ''; 423 this.perfTableThread!.style.display = 'grid'; 424 } else { 425 // @ts-ignore 426 this.titleEl!.textContent = this.processName; 427 } 428 // @ts-ignore 429 this.sumCount = this.allThreadCount.allCount; 430 // @ts-ignore 431 this.sumEventCount = this.allThreadCount.allEventCount; 432 this.perfAnalysisPie!.config = { 433 appendPadding: 0, 434 data: this.getPerfPieChartData(this.threadData), 435 angleField: 'count', 436 colorField: 'tableName', 437 radius: 1, 438 label: { 439 type: 'outer', 440 }, 441 tip: (threadObj): string => { 442 // @ts-ignore 443 const obj = threadObj.obj as AnalysisObj; 444 return `<div><div>Thread:${obj.tableName}</div> 445 <div>Sample Count:${obj.count}</div> 446 <div>Percent:${obj.percent}%</div> 447 <div>Event Count:${obj.eventCount}</div> 448 <div>Percent:${obj.eventPercent}%</div> </div>`; 449 }, 450 angleClick: (it): void => { 451 // @ts-ignore 452 if (it.tableName !== 'other') { 453 this.perfThreadLevelClickEvent(it, val); 454 } 455 }, 456 hoverHandler: (data): void => { 457 if (data) { 458 this.perfTableThread!.setCurrentHover(data); 459 } else { 460 this.perfTableThread!.mouseOut(); 461 } 462 }, 463 interactions: [{ type: 'element-active' }], 464 }; 465 this.tabName!.textContent = 'Statistic By Thread Count'; 466 this.threadData.unshift(this.allThreadCount); 467 this.perfTableThread!.recycleDataSource = this.threadData; 468 this.perfTableThread?.reMeauseHeight(); 469 // @ts-ignore 470 this.threadData.shift(this.allThreadCount); 471 this.currentLevelData = this.threadData; 472 } 473 474 private perfThreadLevelClickEvent(it: unknown, val: SelectionParam): void { 475 this.reset(this.perfTableSo!, true); 476 this.showAssignLevel(this.perfTableSo!, this.perfTableThread!, 2, this.soData); 477 this.getHiperfSo(it, val); 478 let pName = this.processName; 479 // @ts-ignore 480 if (this.processName.length > 0 && it.tableName.length > 0) { 481 pName = `${this.processName} / `; 482 } 483 // @ts-ignore 484 this.titleEl!.textContent = pName + it.tableName; 485 // @ts-ignore 486 this.threadName = it.tableName; 487 this.perfAnalysisPie?.hideTip(); 488 } 489 490 private initPerfAnalysisPieConfig(): void { 491 this.perfAnalysisPie!.config = { 492 appendPadding: 0, 493 data: this.getPerfPieChartData(this.soData), 494 angleField: 'count', 495 colorField: 'tableName', 496 radius: 1, 497 label: { 498 type: 'outer', 499 }, 500 tip: (processObj): string => { 501 // @ts-ignore 502 const obj = processObj.obj as AnalysisObj; 503 return `<div> 504 <div>Library:${obj.tableName}</div> 505 <div>Sample Count:${obj.count}</div> 506 <div>Percent:${obj.percent}%</div> 507 <div>Event Count:${obj.eventCount}</div> 508 <div>Percent:${obj.eventPercent}%</div> 509 </div>`; 510 }, 511 angleClick: (it): void => { 512 // @ts-ignore 513 if (it.tableName !== 'other') { 514 this.perfSoLevelClickEvent(it); 515 } 516 }, 517 hoverHandler: (data): void => { 518 if (data) { 519 this.perfTableSo!.setCurrentHover(data); 520 } else { 521 this.perfTableSo!.mouseOut(); 522 } 523 }, 524 interactions: [ 525 { 526 type: 'element-active', 527 }, 528 ], 529 }; 530 } 531 532 private libraryPieChart(): void { 533 // @ts-ignore 534 this.sumCount = this.allLibCount.allCount; 535 // @ts-ignore 536 this.sumEventCount = this.allLibCount.allEventCount; 537 this.initPerfAnalysisPieConfig(); 538 let pName = this.processName; 539 if (this.processName.length > 0 && this.threadName.length > 0) { 540 pName = `${this.processName} / `; 541 } 542 this.titleEl!.textContent = pName + this.threadName; 543 this.tabName!.textContent = 'Statistic By Library Count'; 544 this.soData.unshift(this.allLibCount); 545 this.perfTableSo!.recycleDataSource = this.soData; 546 this.perfTableSo?.reMeauseHeight(); 547 // @ts-ignore 548 this.soData.shift(this.allLibCount); 549 this.currentLevelData = this.soData; 550 } 551 552 private perfSoLevelClickEvent(it: unknown): void { 553 this.reset(this.tableFunction!, true); 554 this.showAssignLevel(this.tableFunction!, this.perfTableSo!, 3, this.functionData); 555 // @ts-ignore 556 this.currentSoName = it.tableName; 557 this.getHiperfFunction(it); 558 let title = ''; 559 if (this.processName.length > 0) { 560 title += `${this.processName} / `; 561 } 562 if (this.threadName.length > 0) { 563 title += `${this.threadName} / `; 564 } 565 // @ts-ignore 566 if (it.tableName.length > 0) { 567 // @ts-ignore 568 title += it.tableName; 569 } 570 this.titleEl!.textContent = title; 571 this.perfAnalysisPie?.hideTip(); 572 // @ts-ignore 573 this.selectedTabfileName = it.tableName; 574 } 575 576 private functionClickEvent(it: unknown): void { 577 this.clickFuncVaddrList = this.vaddrList.filter((item: unknown) => { 578 // @ts-ignore 579 return item.process_id === it.pid && 580 // @ts-ignore 581 item.thread_id === it.tid && 582 // @ts-ignore 583 item.libName === this.selectedTabfileName && 584 // @ts-ignore 585 item.symbolName === it.tableName; 586 }); 587 if (this.clickFuncVaddrList.length > 0) { 588 const textEncoder = new TextEncoder(); 589 const queryData = { 590 elf_name: this.currentSoName, 591 //@ts-ignore 592 vaddr: this.clickFuncVaddrList[0].vaddrInFile, 593 //@ts-ignore 594 func: it.tableName 595 }; 596 const dataString = JSON.stringify(queryData); 597 const encodedData = textEncoder.encode(dataString); 598 WebSocketManager.getInstance()?.sendMessage(TypeConstants.DISASSEMBLY_TYPE, Constants.DISASSEMBLY_QUERY_CMD, encodedData); 599 } 600 this.functionListener!(it, this.clickFuncVaddrList); 601 } 602 603 private sortByColumn(): void { 604 let currentTable: LitTable | null | undefined; 605 switch (this.currentLevel) { 606 case 0: 607 currentTable = this.perfTableProcess; 608 break; 609 case 1: 610 currentTable = this.perfTableThread; 611 break; 612 case 2: 613 currentTable = this.perfTableSo; 614 break; 615 case 3: 616 currentTable = this.tableFunction; 617 break; 618 } 619 if (!currentTable) { 620 return; 621 } 622 if (this.sortType === 0) { 623 let arr = [...this.currentLevelData]; 624 switch (this.currentLevel) { 625 case 0: 626 arr.unshift(this.allProcessCount); 627 break; 628 case 1: 629 arr.unshift(this.allThreadCount); 630 break; 631 case 2: 632 arr.unshift(this.allLibCount); 633 break; 634 case 3: 635 arr.unshift(this.allSymbolCount); 636 break; 637 } 638 currentTable!.recycleDataSource = arr; 639 } else { 640 this.sortTypeNoZero(currentTable); 641 } 642 } 643 644 private sortTypeNoZero(currentTable: LitTable): void { 645 let array = [...this.currentLevelData]; 646 if (this.sortColumn === 'tableName') { 647 currentTable!.recycleDataSource = array.sort((leftA, rightB) => { 648 if (this.sortType === 1) { 649 // @ts-ignore 650 if (leftA.tableName > rightB.tableName) { 651 return 1; 652 // @ts-ignore 653 } else if (leftA.tableName === rightB.tableName) { 654 return 0; 655 } else { 656 return -1; 657 } 658 } else { 659 // @ts-ignore 660 if (rightB.tableName > leftA.tableName) { 661 return 1; 662 // @ts-ignore 663 } else if (leftA.tableName === rightB.tableName) { 664 return 0; 665 } else { 666 return -1; 667 } 668 } 669 }); 670 } else if (this.sortColumn === 'count' || this.sortColumn === 'percent') { 671 currentTable!.recycleDataSource = array.sort((a, b) => { 672 // @ts-ignore 673 return this.sortType === 1 ? a.count - b.count : b.count - a.count; 674 }); 675 } else if (this.sortColumn === 'eventCount' || this.sortColumn === 'eventPercent') { 676 currentTable!.recycleDataSource = array.sort((a, b) => { 677 // @ts-ignore 678 return this.sortType === 1 ? a.eventCount - b.eventCount : b.eventCount - a.eventCount; 679 }); 680 } 681 switch (this.currentLevel) { 682 case 0: 683 array.unshift(this.allProcessCount); 684 break; 685 case 1: 686 array.unshift(this.allThreadCount); 687 break; 688 case 2: 689 array.unshift(this.allLibCount); 690 break; 691 case 3: 692 array.unshift(this.allSymbolCount); 693 break; 694 } 695 currentTable!.recycleDataSource = array; 696 } 697 698 private initHiPerfProcessSelect(val: SelectionParam): void { 699 this.reset(this.perfTableProcess!, false); 700 this.progressEL!.loading = true; 701 if (!this.processData || this.processData.length === 0) { 702 this.progressEL!.loading = false; 703 if (val.perfThread.length > 0 && val.perfProcess.length === 0) { 704 this.threadData = []; 705 this.allThreadCount = []; 706 this.perfTableProcess!.style.display = 'none'; 707 this.threadPieChart(val); 708 } else { 709 this.pidData = []; 710 this.allProcessCount = []; 711 this.processPieChart(val); 712 } 713 return; 714 } 715 } 716 717 async getHiperfProcess(val: SelectionParam): Promise<void> { 718 this.initHiPerfProcessSelect(val); 719 let allCount = 0; 720 let allEventCount = 0; 721 let pidMap = new Map<number, Array<number | string>>(); 722 if (val.perfThread.length > 0 && val.perfProcess.length === 0) { 723 this.perfTableProcess!.style.display = 'none'; 724 this.getHiperfThread(null, val); 725 } else { 726 for (let itemData of this.processData) { 727 // @ts-ignore 728 allCount += itemData.count; 729 // @ts-ignore 730 allEventCount += itemData.eventCount; 731 // @ts-ignore 732 if (pidMap.has(itemData.pid)) { 733 // @ts-ignore 734 pidMap.get(itemData.pid)?.push(itemData); 735 } else { 736 let itemArray: Array<number | string> = []; 737 // @ts-ignore 738 itemArray.push(itemData); 739 // @ts-ignore 740 pidMap.set(itemData.pid, itemArray); 741 } 742 } 743 this.pidData = []; 744 pidMap.forEach((arr: Array<unknown>, pid: number) => { 745 let count = 0; 746 let eventCount = 0; 747 for (let item of arr) { 748 // @ts-ignore 749 count += item.count; 750 // @ts-ignore 751 eventCount += item.eventCount; 752 } 753 // @ts-ignore 754 const pName = `${arr[0].processName}(${pid})`; 755 const pidData = { 756 tableName: pName, 757 pid: pid, 758 percent: ((count / allCount) * 100).toFixed(2), 759 count: count, 760 eventCount: eventCount, 761 eventPercent: ((eventCount / allEventCount) * 100).toFixed(2), 762 }; 763 this.pidData.push(pidData); 764 }); 765 // @ts-ignore 766 this.pidData.sort((a, b) => b.count - a.count); 767 this.allProcessCount = this.totalCountData(allCount, allEventCount); 768 this.currentLevel = 0; 769 this.progressEL!.loading = false; 770 this.processPieChart(val); 771 } 772 } 773 774 private getHiperfThread(item: unknown, val: SelectionParam): void { 775 this.progressEL!.loading = true; 776 let threadMap = new Map<number, Array<number | string>>(); 777 let allCount = 0; 778 let allEventCount = 0; 779 if (!this.processData || this.processData.length === 0) { 780 return; 781 } 782 for (let itemData of this.processData) { 783 // @ts-ignore 784 if (item && itemData.pid !== item.pid && !this.hideProcessCheckBox?.checked) { 785 continue; 786 } 787 // @ts-ignore 788 allCount += itemData.count; 789 // @ts-ignore 790 allEventCount += itemData.eventCount; 791 // @ts-ignore 792 if (threadMap.has(itemData.tid)) { 793 // @ts-ignore 794 threadMap.get(itemData.tid)?.push(itemData); 795 } else { 796 let itemArray: Array<number | string> = []; 797 // @ts-ignore 798 itemArray.push(itemData); 799 // @ts-ignore 800 threadMap.set(itemData.tid, itemArray); 801 } 802 } 803 this.threadData = []; 804 threadMap.forEach((arr: Array<unknown>, tid: number) => { 805 let threadCount = 0; 806 let threadEventCount = 0; 807 // @ts-ignore 808 let tName = `${arr[0].threadName}(${tid})`; 809 for (let item of arr) { 810 // @ts-ignore 811 threadCount += item.count; 812 // @ts-ignore 813 threadEventCount += item.eventCount; 814 } 815 const threadData = { 816 // @ts-ignore 817 pid: item === null ? arr[0].pid : item.pid, 818 tid: tid, 819 tableName: tName, 820 count: threadCount, 821 percent: ((threadCount / allCount) * 100).toFixed(2), 822 eventCount: threadEventCount, 823 eventPercent: ((threadEventCount / allEventCount) * 100).toFixed(2), 824 }; 825 this.threadData.push(threadData); 826 }); 827 this.allThreadCount = this.totalCountData(allCount, allEventCount); 828 this.currentLevel = 1; 829 // @ts-ignore 830 this.threadData.sort((a, b) => b.count - a.count); 831 this.progressEL!.loading = false; 832 this.threadPieChart(val); 833 } 834 835 private getHiPerfSoIdByProcessData(item: unknown, itemData: unknown): boolean { 836 if (!this.hideProcessCheckBox?.checked && !this.hideThreadCheckBox?.checked) { 837 // @ts-ignore 838 if (item && (itemData.pid !== item.pid || itemData.tid !== item.tid)) { 839 return true; 840 } 841 } else if (!this.hideProcessCheckBox?.checked && this.hideThreadCheckBox?.checked) { 842 // @ts-ignore 843 if (item && itemData.pid !== item.pid) { 844 return true; 845 } 846 } else if (this.hideProcessCheckBox?.checked && !this.hideThreadCheckBox?.checked) { 847 // @ts-ignore 848 if (item && itemData.tid !== item.tid) { 849 return true; 850 } 851 } 852 return false; 853 } 854 855 private getHiperfSo(item: unknown, val: SelectionParam): void { 856 this.progressEL!.loading = true; 857 let parentEventCount = 0; 858 let allCount = 0; 859 let allEventCount = 0; 860 let libMap = new Map<string, Array<number | string>>(); 861 if (!this.processData || this.processData.length === 0) { 862 return; 863 } 864 for (let itemData of this.processData) { 865 if (this.getHiPerfSoIdByProcessData(item, itemData)) { 866 continue; 867 } 868 // @ts-ignore 869 allCount += itemData.count; 870 // @ts-ignore 871 allEventCount += itemData.eventCount; 872 // @ts-ignore 873 if (libMap.has(`${itemData.libId}-${itemData.libName}`)) { 874 // @ts-ignore 875 libMap.get(`${itemData.libId}-${itemData.libName}`)?.push(itemData); 876 } else { 877 let dataArray: Array<number | string> = []; 878 // @ts-ignore 879 dataArray.push(itemData); 880 // @ts-ignore 881 libMap.set(`${itemData.libId}-${itemData.libName}`, dataArray); 882 } 883 } 884 // @ts-ignore 885 item ? (parentEventCount = item.eventCount) : (parentEventCount = allEventCount); 886 this.soData = []; 887 libMap.forEach((arr: Array<unknown>) => { 888 let libCount = 0; 889 let libEventCount = 0; 890 // @ts-ignore 891 let libName = arr[0].libName; 892 // @ts-ignore 893 let libId = arr[0].libId; 894 for (let item of arr) { 895 // @ts-ignore 896 libCount += item.count; 897 // @ts-ignore 898 libEventCount += item.eventCount; 899 } 900 const libData = { 901 // @ts-ignore 902 pid: item === null ? arr[0].pid : item.pid, 903 // @ts-ignore 904 tid: item === null ? arr[0].tid : item.tid, 905 percent: ((libCount / allCount) * 100).toFixed(2), 906 count: libCount, 907 eventPercent: ((libEventCount / parentEventCount) * 100).toFixed(2), 908 eventCount: libEventCount, 909 tableName: libName, 910 libId: libId, 911 }; 912 this.soData.push(libData); 913 }); 914 this.initPerfSoData(allCount, allEventCount); 915 } 916 917 private initPerfSoData(allCount: number, allEventCount: number): void { 918 this.allLibCount = this.totalCountData(allCount, allEventCount); 919 // @ts-ignore 920 this.soData.sort((a, b) => b.count - a.count); 921 this.currentLevel = 2; 922 this.progressEL!.loading = false; 923 this.libraryPieChart(); 924 } 925 926 private getHiperfFunction(item: unknown): void { 927 this.progressEL!.loading = true; 928 this.shadowRoot!.querySelector<HTMLDivElement>('.perf-subheading')!.textContent = 'Statistic By Function Count'; 929 // @ts-ignore 930 let parentCount = item.count; 931 // @ts-ignore 932 let parentEventCount = item.eventCount; 933 let allCount = 0; 934 let allEventCount = 0; 935 let symbolMap = new Map<string, Array<unknown>>(); 936 if (!this.processData || this.processData.length === 0) { 937 return; 938 } 939 for (let itemData of this.processData) { 940 if (this.getIdByProcessData(itemData, item)) { 941 continue; 942 } 943 // @ts-ignore 944 allCount += itemData.count; 945 // @ts-ignore 946 allEventCount += itemData.eventCount; 947 // @ts-ignore 948 if (symbolMap.has(`${itemData.symbolId}-${itemData.symbolName}`)) { 949 // @ts-ignore 950 symbolMap.get(`${itemData.symbolId}-${itemData.symbolName}`)?.push(itemData); 951 } else { 952 let dataArray: Array<number | string> = []; 953 // @ts-ignore 954 dataArray.push(itemData); 955 // @ts-ignore 956 symbolMap.set(`${itemData.symbolId}-${itemData.symbolName}`, dataArray); 957 } 958 } 959 this.functionData = []; 960 symbolMap.forEach((arr) => { 961 let symbolCount = 0; 962 let symbolEventCount = 0; 963 for (let item of arr) { 964 // @ts-ignore 965 symbolCount += item.count; 966 // @ts-ignore 967 symbolEventCount += item.eventCount; 968 } 969 // @ts-ignore 970 let symbolName = arr[0].symbolName; 971 // @ts-ignore 972 let symbolId = arr[0].symbolId; 973 const symbolData = { 974 // @ts-ignore 975 pid: item.pid, 976 // @ts-ignore 977 tid: item.tid, 978 // @ts-ignore 979 libId: item.libId, 980 percent: ((symbolCount / parentCount) * 100).toFixed(2), 981 count: symbolCount, 982 symbolId: symbolId, 983 eventPercent: ((symbolEventCount / parentEventCount) * 100).toFixed(2), 984 eventCount: symbolEventCount, 985 tableName: symbolName, 986 }; 987 this.functionData.push(symbolData); 988 }); 989 this.initPerfFunData(allCount, allEventCount); 990 } 991 992 private getIdByProcessData(itemData: unknown, item: unknown): boolean { 993 // @ts-ignore 994 let tid = item.tid; 995 // @ts-ignore 996 let pid = item.pid; 997 // @ts-ignore 998 let libId = item.libId; 999 let isContinue = false; 1000 if (!this.hideProcessCheckBox?.checked && !this.hideThreadCheckBox?.checked) { 1001 // @ts-ignore 1002 if (itemData.pid !== pid || itemData.tid !== tid || itemData.libId !== libId) { 1003 isContinue = true; 1004 } 1005 } else if (!this.hideProcessCheckBox?.checked && this.hideThreadCheckBox?.checked) { 1006 // @ts-ignore 1007 if (itemData.pid !== pid || itemData.libId !== libId) { 1008 isContinue = true; 1009 } 1010 } else if (this.hideProcessCheckBox?.checked && !this.hideThreadCheckBox?.checked) { 1011 // @ts-ignore 1012 if (itemData.tid !== tid || itemData.libId !== libId) { 1013 isContinue = true; 1014 } 1015 } else if (this.hideProcessCheckBox?.checked && this.hideThreadCheckBox?.checked) { 1016 // @ts-ignore 1017 if (itemData.libId !== libId) { 1018 isContinue = true; 1019 } 1020 } 1021 return isContinue; 1022 } 1023 1024 private initPerfFunData(allCount: number, allEventCount: number): void { 1025 // @ts-ignore 1026 this.functionData.sort((a, b) => b.count - a.count); 1027 this.allSymbolCount = this.totalCountData(allCount, allEventCount); 1028 this.currentLevel = 3; 1029 this.progressEL!.loading = false; 1030 // @ts-ignore 1031 this.sumCount = this.allSymbolCount.allCount; 1032 // @ts-ignore 1033 this.sumEventCount = this.allSymbolCount.allEventCount; 1034 this.perfAnalysisPie!.config = { 1035 appendPadding: 0, 1036 data: this.getPerfPieChartData(this.functionData), 1037 angleField: 'count', 1038 colorField: 'tableName', 1039 radius: 1, 1040 label: { 1041 type: 'outer', 1042 }, 1043 // @ts-ignore 1044 tip: this.getTip(), 1045 hoverHandler: (data): void => { 1046 if (data) { 1047 this.tableFunction!.setCurrentHover(data); 1048 } else { 1049 this.tableFunction!.mouseOut(); 1050 } 1051 }, 1052 interactions: [ 1053 { 1054 type: 'element-active', 1055 }, 1056 ], 1057 }; 1058 this.functionData.unshift(this.allSymbolCount); 1059 this.tableFunction!.recycleDataSource = this.functionData; 1060 this.tableFunction?.reMeauseHeight(); 1061 // @ts-ignore 1062 this.functionData.shift(this.allSymbolCount); 1063 this.currentLevelData = this.functionData; 1064 } 1065 1066 private getTip() { 1067 return (obj: { 1068 obj: { tableName: unknown; count: unknown; percent: unknown; eventCount: unknown; eventPercent: unknown }; 1069 }): string => { 1070 return `<div> 1071 <div>Function:${obj.obj.tableName}</div> 1072 <div>Sample Count:${obj.obj.count}</div> 1073 <div>Percent:${obj.obj.percent}%</div> 1074 <div>Event Count:${obj.obj.eventCount}</div> 1075 <div>Percent:${obj.obj.eventPercent}%</div> 1076 </div>`; 1077 }; 1078 } 1079 1080 totalCountData( 1081 count: number, 1082 eventCount: number 1083 ): { 1084 percent: string; 1085 count: number; 1086 allCount: number; 1087 eventCount: number; 1088 eventPercent: string; 1089 allEventCount: number; 1090 pid: string; 1091 } { 1092 let allCount; 1093 allCount = { 1094 percent: ((count / count) * 100).toFixed(2), 1095 count: count, 1096 allCount: count, 1097 eventCount: eventCount, 1098 allEventCount: eventCount, 1099 eventPercent: '100.00', 1100 pid: '', 1101 }; 1102 return allCount; 1103 } 1104 1105 private getPerfPieChartData(res: unknown[]): unknown[] { 1106 if (res.length > 20) { 1107 let pieChartArr: string[] = []; 1108 let other: unknown = { 1109 tableName: 'other', 1110 count: 0, 1111 percent: 0, 1112 eventCount: 0, 1113 eventPercent: 0, 1114 }; 1115 for (let i = 0; i < res.length; i++) { 1116 if (i < 19) { 1117 // @ts-ignore 1118 pieChartArr.push(res[i]); 1119 } else { 1120 // @ts-ignore 1121 other.count += res[i].count; 1122 // @ts-ignore 1123 other.percent = ((other.count / this.sumCount!) * 100).toFixed(2); 1124 // @ts-ignore 1125 other.eventCount += res[i].eventCount; 1126 // @ts-ignore 1127 other.eventPercent = ((other.eventCount / this.sumEventCount!) * 100).toFixed(2); 1128 } 1129 } 1130 // @ts-ignore 1131 pieChartArr.push(other); 1132 return pieChartArr; 1133 } 1134 return res; 1135 } 1136 1137 private getCallChainDataFromWorker(val: SelectionParam): void { 1138 this.getDataByWorker(val, (results: unknown) => { 1139 this.isComplete = true; 1140 if (this.currentSelectionParam !== val) { 1141 this.getCallChainDataFromWorker(this.currentSelectionParam!); 1142 return; 1143 } 1144 // @ts-ignore 1145 this.processData = results; 1146 if (this.processData.length === 0) { 1147 this.hideProcessCheckBox?.setAttribute('disabled', 'disabled'); 1148 this.hideThreadCheckBox?.setAttribute('disabled', 'disabled'); 1149 } else { 1150 this.hideProcessCheckBox?.removeAttribute('disabled'); 1151 this.hideThreadCheckBox?.removeAttribute('disabled'); 1152 } 1153 this.progressEL!.loading = false; 1154 this.getHiperfProcess(val); 1155 if (TabPanePerfAnalysis.tabLoadingList[0] === 'analysis') { 1156 TabPanePerfAnalysis.tabLoadingList.shift(); 1157 } 1158 }); 1159 const args = [ 1160 { 1161 funcName: 'getVaddrToFile', 1162 funcArgs: [val], 1163 }, 1164 ]; 1165 procedurePool.submitWithName('logic0', 'perf-vaddr', args, undefined, (results: Array<unknown>) => { 1166 this.vaddrList = results; 1167 }); 1168 } 1169 1170 private getDataByWorker(val: SelectionParam, handler: Function): void { 1171 this.progressEL!.loading = true; 1172 const args = [ 1173 { 1174 funcName: 'setSearchValue', 1175 funcArgs: [''], 1176 }, 1177 { 1178 funcName: 'getCurrentDataFromDbAnalysis', 1179 funcArgs: [val], 1180 }, 1181 ]; 1182 procedurePool.submitWithName('logic0', 'perf-action', args, undefined, (results: unknown) => { 1183 handler(results); 1184 }); 1185 } 1186 1187 public connectedCallback(): void { 1188 new ResizeObserver(() => { 1189 this.perfTableProcess!.style.height = `${this.parentElement!.clientHeight - 50}px`; 1190 this.perfTableProcess?.reMeauseHeight(); 1191 this.perfTableThread!.style.height = `${this.parentElement!.clientHeight - 50}px`; 1192 this.perfTableThread?.reMeauseHeight(); 1193 this.tableFunction!.style.height = `${this.parentElement!.clientHeight - 50}px`; 1194 this.tableFunction?.reMeauseHeight(); 1195 this.perfTableSo!.style.height = `${this.parentElement!.clientHeight - 50}px`; 1196 this.perfTableSo?.reMeauseHeight(); 1197 if (this.parentElement!.clientHeight >= 0 && this.parentElement!.clientHeight <= 31) { 1198 this.filterEl!.style.display = 'none'; 1199 } else { 1200 this.filterEl!.style.display = 'flex'; 1201 } 1202 }).observe(this.parentElement!); 1203 } 1204 1205 public addFunctionRowClickEventListener(clickEvent: Function): void { 1206 this.functionListener = clickEvent; 1207 } 1208 1209 initHtml(): string { 1210 return TabPanePerfAnalysisHtml; 1211 } 1212} 1213