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