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