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 '../../../../../base-ui/table/lit-table'; 19import { type ConstructorItem, FileInfo } from '../../../../../js-heap/model/UiStruct'; 20import { HeapDataInterface } from '../../../../../js-heap/HeapDataInterface'; 21import '../../../../../base-ui/table/lit-table-column'; 22import { TabPaneJsMemoryFilter } from '../TabPaneJsMemoryFilter'; 23import '../TabPaneJsMemoryFilter'; 24import { LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar'; 25import '../../../../../base-ui/progress-bar/LitProgressBar'; 26import '../../../../../base-ui/slicer/lit-slicer'; 27import { HeapSnapshotStruct } from '../../../../database/ui-worker/ProcedureWorkerHeapSnapshot'; 28import { HeapTraceFunctionInfo } from '../../../../../js-heap/model/DatabaseStruct'; 29import { TabPaneSummaryHtml } from './TabPaneSummary.html'; 30 31@element('tabpane-summary') 32export class TabPaneSummary extends BaseElement { 33 private tblSummary: LitTable | null | undefined; 34 private tbs: LitTable | null | undefined; 35 private stackTable: LitTable | null | undefined; 36 private summary: Array<ConstructorItem> = []; 37 private retainsData: Array<ConstructorItem> = []; 38 private stackData: Array<HeapTraceFunctionInfo> = []; 39 private stackText: HTMLElement | undefined; 40 private fileSize: number = 0; 41 private tabFilter: TabPaneJsMemoryFilter | undefined | null; 42 private progressEL: LitProgressBar | null | undefined; 43 private summaryFilter: Array<ConstructorItem> = []; 44 private summaryData: Array<ConstructorItem> = []; 45 private search: HTMLInputElement | null | undefined; 46 private tbsTable: HTMLDivElement | null | undefined; 47 private tblTable: HTMLDivElement | null | undefined; 48 private rightTheadTable: HTMLDivElement | null | undefined; 49 private leftTheadTable: HTMLDivElement | null | undefined; 50 private leftArray: ConstructorItem[] = []; 51 private rightArray: ConstructorItem[] = []; 52 private stack: HTMLLIElement | null | undefined; 53 private retainers: HTMLLIElement | null | undefined; 54 private file: FileInfo | undefined | null; 55 private leftTable: HTMLDivElement | null | undefined; 56 57 initElements(): void { 58 this.tblSummary = this.shadowRoot?.querySelector<LitTable>('#summary_left'); 59 this.tbs = this.shadowRoot?.querySelector<LitTable>('#summary_right'); 60 this.stackTable = this.shadowRoot?.querySelector<LitTable>('#stackTable'); 61 this.stackText = this.shadowRoot?.querySelector('.stackText') as HTMLElement; 62 this.tabFilter = this.shadowRoot?.querySelector('#filter') as TabPaneJsMemoryFilter; 63 this.progressEL = this.shadowRoot?.querySelector('.summary_progress') as LitProgressBar; 64 this.search = this.tabFilter?.shadowRoot?.querySelector('#js-memory-filter-input') as HTMLInputElement; 65 this.stack = this.shadowRoot?.querySelector('#stack') as HTMLLIElement; 66 this.retainers = this.shadowRoot?.querySelector('#retainers') as HTMLLIElement; 67 this.tblTable = this.tblSummary!.shadowRoot?.querySelector('.table') as HTMLDivElement; 68 this.rightTheadTable = this.tbs!.shadowRoot?.querySelector('.thead') as HTMLDivElement; 69 this.leftTheadTable = this.tblSummary!.shadowRoot?.querySelector('.thead') as HTMLDivElement; 70 this.tbsTable = this.tbs!.shadowRoot?.querySelector('.table') as HTMLDivElement; 71 this.leftTable = this.shadowRoot?.querySelector('#summary_left_table') as HTMLDivElement; 72 this.classFilter(); 73 } 74 75 setSnapshotData( 76 data: HeapSnapshotStruct, 77 dataListCache: Array<HeapSnapshotStruct>, 78 scrollCallback: ((d: HeapSnapshotStruct, ds: Array<HeapSnapshotStruct>) => void) | undefined 79 ): void { 80 if (scrollCallback) { 81 scrollCallback(data, dataListCache); 82 } 83 this.summary = []; 84 this.initSummaryData(data); 85 } 86 87 initSummaryData(file: FileInfo | HeapSnapshotStruct, minNodeId?: number, maxNodeId?: number): void { 88 this.file = file as FileInfo; 89 this.clear(); 90 this.summary = []; 91 this.progressEL!.loading = true; 92 this.summary = HeapDataInterface.getInstance().getClassesListForSummary(file.id, minNodeId, maxNodeId); 93 this.fileSize = file.size; 94 this.summary.forEach((summaryEl: unknown) => { 95 // @ts-ignore 96 if (summaryEl.childCount > 1) { 97 // @ts-ignore 98 let count = `${summaryEl.nodeName} ×${summaryEl.childCount}`; // @ts-ignore 99 summaryEl.objectName = count; // @ts-ignore 100 summaryEl.count = ` ×${summaryEl.childCount}`; // @ts-ignore 101 } else { 102 // @ts-ignore 103 summaryEl.objectName = summaryEl.nodeName; // @ts-ignore 104 } // @ts-ignore 105 let shallow = `${Math.round((summaryEl.shallowSize / file.size) * 100)}%`; // @ts-ignore 106 let retained = `${Math.round((summaryEl.retainedSize / file.size) * 100)}%`; // @ts-ignore 107 summaryEl.shallowPercent = shallow; // @ts-ignore 108 summaryEl.retainedPercent = retained; // @ts-ignore 109 if (summaryEl.distance >= 100000000 || summaryEl.distance === -5) { 110 // @ts-ignore 111 summaryEl.distance = '-'; 112 } 113 }); 114 if (this.summary.length > 0) { 115 this.summaryData = this.summary; 116 this.tblSummary!.snapshotDataSource = this.summary; 117 this.progressEL!.loading = false; 118 } else { 119 this.tblSummary!.snapshotDataSource = []; 120 this.progressEL!.loading = false; 121 } 122 new ResizeObserver(() => { 123 if (this.parentElement?.clientHeight !== 0) { 124 this.tblSummary!.style.height = '100%'; 125 this.tblSummary!.reMeauseHeight(); 126 } 127 }).observe(this.parentElement!); 128 if (this.file!.name.includes('Timeline')) { 129 this.retainers!.classList.add('active'); 130 this.stack!.style.display = 'flex'; 131 this.retainers!.style.pointerEvents = 'auto'; 132 } else { 133 this.stack!.style.display = 'none'; 134 this.retainers!.classList.remove('active'); 135 this.retainers!.style.pointerEvents = 'none'; 136 } 137 this.clickToggleTable(); 138 } 139 140 private retainsTableByDistance(currentLeftItem: ConstructorItem, sort: number): void { 141 const getList = function (list: Array<ConstructorItem>): void { 142 list.sort((leftA, rightB) => { 143 return sort === 1 ? leftA.distance - rightB.distance : rightB.distance - leftA.distance; 144 }); 145 list.forEach(function (row) { 146 if (row.children.length > 0) { 147 getList(row.children); 148 } 149 }); 150 }; 151 getList(currentLeftItem.children); 152 } 153 154 private retainsTableByShallowSize(currentLeftItem: ConstructorItem, sort: number): void { 155 const getList = function (list: Array<ConstructorItem>): void { 156 list.sort((leftA, rightB) => { 157 return sort === 1 ? leftA.shallowSize - rightB.shallowSize : rightB.shallowSize - leftA.shallowSize; 158 }); 159 list.forEach(function (row) { 160 if (row.children.length > 0) { 161 getList(row.children); 162 } 163 }); 164 }; 165 getList(currentLeftItem.children); 166 } 167 168 private retainsTableByRetainedSize(currentLeftItem: ConstructorItem, sort: number): void { 169 const getList = function (list: Array<ConstructorItem>): void { 170 list.sort((leftA, rightB) => { 171 return sort === 1 ? leftA.retainedSize - rightB.retainedSize : rightB.retainedSize - leftA.retainedSize; 172 }); 173 list.forEach(function (row) { 174 if (row.children.length > 0) { 175 getList(row.children); 176 } 177 }); 178 }; 179 getList(currentLeftItem.children); 180 } 181 182 private retainsTableByObjectName(currentLeftItem: ConstructorItem, sort: number): void { 183 const getList = function (list: Array<ConstructorItem>): void { 184 list.sort((leftA, rightB) => { 185 return sort === 1 186 ? `${leftA.objectName}`.localeCompare(`${rightB.objectName}`) 187 : `${rightB.objectName}`.localeCompare(`${leftA.objectName}`); 188 }); 189 list.forEach(function (row) { 190 if (row.children.length > 0) { 191 getList(row.children); 192 } 193 }); 194 }; 195 getList(currentLeftItem.children); 196 } 197 198 sortByLeftTable(column: string, sort: number): void { 199 switch (sort) { 200 case 0: 201 if (this.search!.value === '') { 202 this.tblSummary!.snapshotDataSource = this.summary; 203 } else { 204 this.tblSummary!.snapshotDataSource = this.summaryFilter; 205 } 206 break; 207 default: 208 if (this.search!.value === '') { 209 this.leftArray = [...this.summary]; 210 } else { 211 this.leftArray = [...this.summaryFilter]; 212 } 213 switch (column) { 214 case 'distance': 215 this.sortLeftByDistanceColum(sort); 216 break; 217 case 'shallowSize': 218 this.sortLeftByShallowSizeColum(sort); 219 break; 220 case 'retainedSize': 221 this.sortLeftByRetainedSizeColum(sort); 222 break; 223 case 'objectName': 224 this.sortLeftByObjectNameColum(sort); 225 break; 226 } 227 break; 228 } 229 } 230 private sortLeftByObjectNameColum(sort: number): void { 231 this.tblSummary!.snapshotDataSource = this.leftArray.sort((leftData, rightData) => { 232 return sort === 1 233 ? `${leftData.objectName}`.localeCompare(`${rightData.objectName}`) 234 : `${rightData.objectName}`.localeCompare(`${leftData.objectName}`); 235 }); 236 this.leftArray.forEach((currentLeftItem) => { 237 this.retainsTableByObjectName(currentLeftItem, sort); 238 }); 239 this.tblSummary!.snapshotDataSource = this.leftArray; 240 } 241 242 private sortLeftByRetainedSizeColum(sort: number): void { 243 this.tblSummary!.snapshotDataSource = this.leftArray.sort((leftData, rightData) => { 244 return sort === 1 245 ? leftData.retainedSize - rightData.retainedSize 246 : rightData.retainedSize - leftData.retainedSize; 247 }); 248 this.leftArray.forEach((currentLeftItem) => { 249 this.retainsTableByRetainedSize(currentLeftItem, sort); 250 }); 251 this.tblSummary!.snapshotDataSource = this.leftArray; 252 } 253 254 private sortLeftByShallowSizeColum(sort: number): void { 255 this.tblSummary!.snapshotDataSource = this.leftArray.sort((leftData, rightData) => { 256 return sort === 1 ? leftData.shallowSize - rightData.shallowSize : rightData.shallowSize - leftData.shallowSize; 257 }); 258 this.leftArray.forEach((currentLeftItem) => { 259 this.retainsTableByShallowSize(currentLeftItem, sort); 260 }); 261 this.tblSummary!.snapshotDataSource = this.leftArray; 262 } 263 264 private sortLeftByDistanceColum(sort: number): void { 265 this.tblSummary!.snapshotDataSource = this.leftArray.sort((leftData, rightData) => { 266 return sort === 1 ? leftData.distance - rightData.distance : rightData.distance - leftData.distance; 267 }); 268 this.leftArray.forEach((currentLeftItem) => { 269 this.retainsTableByDistance(currentLeftItem, sort); 270 }); 271 this.tblSummary!.snapshotDataSource = this.leftArray; 272 } 273 274 sortByRightTable(column: string, sort: number): void { 275 switch (sort) { 276 case 0: 277 this.tbs!.snapshotDataSource = this.retainsData; 278 break; 279 default: 280 this.rightArray = [...this.retainsData]; 281 switch (column) { 282 case 'distance': 283 this.tbs!.snapshotDataSource = this.rightArray.sort((a, b) => { 284 return sort === 1 ? a.distance - b.distance : b.distance - a.distance; 285 }); 286 this.rightArray.forEach((list) => { 287 this.retainsTableByDistance(list, sort); 288 }); 289 this.tbs!.snapshotDataSource = this.rightArray; 290 break; 291 case 'shallowSize': 292 this.tbs!.snapshotDataSource = this.rightArray.sort((a, b) => { 293 return sort === 1 ? a.shallowSize - b.shallowSize : b.shallowSize - a.shallowSize; 294 }); 295 this.rightArray.forEach((list) => { 296 this.retainsTableByShallowSize(list, sort); 297 }); 298 this.tbs!.snapshotDataSource = this.rightArray; 299 break; 300 case 'retainedSize': 301 this.tbs!.snapshotDataSource = this.rightArray.sort((a, b) => { 302 return sort === 1 ? a.retainedSize - b.retainedSize : b.retainedSize - a.retainedSize; 303 }); 304 this.rightArray.forEach((list) => { 305 this.retainsTableByRetainedSize(list, sort); 306 }); 307 this.tbs!.snapshotDataSource = this.rightArray; 308 break; 309 case 'objectName': 310 this.tbs!.snapshotDataSource = this.rightArray.sort((a, b) => { 311 return sort === 1 312 ? `${a.objectName}`.localeCompare(`${b.objectName}`) 313 : `${b.objectName}`.localeCompare(`${a.objectName}`); 314 }); 315 this.rightArray.forEach((list) => { 316 this.retainsTableByObjectName(list, sort); 317 }); 318 this.tbs!.snapshotDataSource = this.rightArray; 319 break; 320 } 321 break; 322 } 323 } 324 325 clickToggleTable(): void { 326 let lis = this.shadowRoot?.querySelectorAll<HTMLElement>('li')!; 327 for (const item of lis) { 328 item.onclick = (): void => { 329 item.className = ''; 330 switch (item.textContent) { 331 case 'Retainers': 332 this.stackTable!.style.display = 'none'; 333 this.stackText!.style.display = 'none'; 334 this.tbs!.style.display = 'flex'; 335 this.tbs!.snapshotDataSource = this.retainsData; 336 break; 337 case 'Allocation stack': 338 if (this.stackData.length > 0) { 339 this.stackText!.style.display = 'none'; 340 this.stackTable!.style.display = 'flex'; 341 this.stackTable!.recycleDataSource = this.stackData; 342 } else { 343 this.stackText!.style.display = 'flex'; 344 if (this.retainsData === undefined || this.retainsData.length === 0) { 345 this.stackText!.textContent = ''; 346 } else { 347 this.stackText!.textContent = 348 'Stack was not recorded for this object because it had been allocated before ' + 349 'this profile recording started.'; 350 } 351 } 352 this.tbs!.style.display = 'none'; 353 break; 354 } 355 // @ts-ignore 356 this.className = 'active'; 357 }; 358 } 359 } 360 361 classFilter(): void { 362 this.search!.addEventListener('keyup', () => { 363 this.summaryFilter = []; 364 this.summaryData.forEach((a) => { 365 if (a.objectName.toLowerCase().includes(this.search!.value.toLowerCase())) { 366 this.summaryFilter.push(a); 367 } else { 368 } 369 }); 370 this.tblSummary!.snapshotDataSource = this.summaryFilter; 371 let summaryTable = this.tblSummary!.shadowRoot?.querySelector('.table') as HTMLDivElement; 372 summaryTable.scrollTop = 0; 373 this.tblSummary!.reMeauseHeight(); 374 }); 375 } 376 377 clear(): void { 378 this.tbs!.snapshotDataSource = []; 379 this.stackTable!.recycleDataSource = []; 380 this.retainsData = []; 381 this.stackText!.textContent = ''; 382 this.search!.value = ''; 383 this.stack!.classList.remove('active'); 384 this.tblTable!.scrollTop = 0; 385 this.stackTable!.style.display = 'none'; 386 this.stackText!.style.display = 'none'; 387 this.tbs!.style.display = 'flex'; 388 this.rightTheadTable!.removeAttribute('sort'); 389 this.leftTheadTable!.removeAttribute('sort'); 390 } 391 392 connectedCallback(): void { 393 super.connectedCallback(); 394 let filterHeight = 0; 395 let parentWidth = `${this.parentElement!.clientWidth}px`; 396 let system = document 397 .querySelector('body > sp-application') 398 ?.shadowRoot?.querySelector('#app-content > sp-system-trace'); 399 new ResizeObserver(() => { 400 let summaryPaneFilter = this.shadowRoot!.querySelector('#filter') as HTMLElement; 401 if (summaryPaneFilter.clientHeight > 0) { 402 filterHeight = summaryPaneFilter.clientHeight; 403 } 404 if (this.parentElement!.clientHeight > filterHeight) { 405 summaryPaneFilter.style.display = 'flex'; 406 } else { 407 summaryPaneFilter.style.display = 'none'; 408 } 409 parentWidth = `${this.parentElement!.clientWidth}px`; 410 this.tbs!.style.height = 'calc(100% - 30px)'; 411 this.tbsTable!.style.width = `calc(${parentWidth} - ${this.leftTable!.style.width} - 5px)`; 412 this.tbs!.reMeauseHeight(); 413 this.tblSummary!.reMeauseHeight(); 414 }).observe(this.parentElement!); 415 new ResizeObserver(() => { 416 this.parentElement!.style.width = `${system!.clientWidth}px`; 417 this.style.width = `${system!.clientWidth}px`; 418 }).observe(system!); 419 new ResizeObserver(() => { 420 this.tbsTable!.style.width = `calc(${parentWidth} - ${this.leftTable!.style.width} - 5px)`; 421 }).observe(this.leftTable!); 422 423 this.tblSummary!.addEventListener('row-click', this.tblSummaryRowClick); 424 this.tbs!.addEventListener('row-click', this.tbsRowClick); 425 this.tblSummary!.addEventListener('icon-click', this.tblSummaryIconClick); 426 this.tbs!.addEventListener('icon-click', this.tbsIconClick); 427 this.tblSummary!.addEventListener('column-click', this.tblSummaryColumnClick); 428 this.tbs!.addEventListener('column-click', this.tblColumnClick); 429 } 430 431 disconnectedCallback(): void { 432 super.disconnectedCallback(); 433 this.tblSummary!.removeEventListener('row-click', this.tblSummaryRowClick); 434 this.tbs!.removeEventListener('row-click', this.tbsRowClick); 435 this.tblSummary!.removeEventListener('icon-click', this.tblSummaryIconClick); 436 this.tbs!.removeEventListener('icon-click', this.tbsIconClick); 437 this.tblSummary!.removeEventListener('column-click', this.tblSummaryColumnClick); 438 this.tbs!.removeEventListener('column-click', this.tblColumnClick); 439 } 440 441 tblColumnClick = (evt: Event): void => { 442 // @ts-ignore 443 this.sortByRightTable(evt.detail.key, evt.detail.sort); 444 this.tbs!.reMeauseHeight(); 445 }; 446 447 tblSummaryColumnClick = (evt: Event): void => { 448 // @ts-ignore 449 this.sortByLeftTable(evt.detail.key, evt.detail.sort); 450 this.tblSummary!.reMeauseHeight(); 451 }; 452 453 tbsIconClick = (evt: Event): void => { 454 // @ts-ignore 455 let data = evt.detail.data; 456 if (data.status) { 457 data.getChildren(); 458 let i = 0; 459 let retainsTable = (): void => { 460 const getList = (list: Array<ConstructorItem>): void => { 461 list.forEach((currentRow: ConstructorItem) => { 462 let shallow = `${Math.round((currentRow.shallowSize / this.fileSize) * 100)}%`; 463 let retained = `${Math.round((currentRow.retainedSize / this.fileSize) * 100)}%`; 464 currentRow.shallowPercent = shallow; 465 currentRow.retainedPercent = retained; 466 let nodeId = `${currentRow.nodeName} @${currentRow.id}`; 467 currentRow.objectName = `${currentRow.edgeName}\xa0` + 'in' + `\xa0${nodeId}`; 468 if (currentRow.distance >= 100000000 || currentRow.distance === -5) { 469 // @ts-ignore 470 currentRow.distance = '-'; 471 } 472 i++; 473 // @ts-ignore 474 if (i < evt.detail.data.distance - 1 && list[0].distance !== '-') { 475 list[0].getChildren(); 476 list[0].expanded = false; 477 if (currentRow.hasNext) { 478 getList(currentRow.children); 479 } 480 } else { 481 return; 482 } 483 }); 484 }; 485 getList(data.children); 486 }; 487 retainsTable(); 488 } else { 489 data.status = true; 490 } 491 if (this.rightTheadTable!.hasAttribute('sort')) { 492 this.tbs!.snapshotDataSource = this.rightArray; 493 } else { 494 this.tbs!.snapshotDataSource = this.retainsData; 495 } 496 new ResizeObserver(() => { 497 if (this.parentElement?.clientHeight !== 0) { 498 this.tbs!.style.height = 'calc(100% - 30px)'; 499 this.tbs!.reMeauseHeight(); 500 } 501 }).observe(this.parentElement!); 502 }; 503 504 tblSummaryIconClick = (evt: Event): void => { 505 // @ts-ignore 506 let data = evt.detail.data; 507 if (data.status) { 508 data.getChildren(); 509 if (data.children.length > 0) { 510 data.children.sort(function (a: ConstructorItem, b: ConstructorItem) { 511 return b.retainedSize - a.retainedSize; 512 }); 513 data.children.forEach((summaryDataEl: any) => { 514 let shallow = `${Math.round((summaryDataEl.shallowSize / this.fileSize) * 100)}%`; 515 let retained = `${Math.round((summaryDataEl.retainedSize / this.fileSize) * 100)}%`; 516 summaryDataEl.shallowPercent = shallow; 517 summaryDataEl.retainedPercent = retained; 518 if (summaryDataEl.distance >= 100000000 || summaryDataEl.distance === -5) { 519 summaryDataEl.distance = '-'; 520 } 521 let nodeId = `${summaryDataEl.nodeName} @${summaryDataEl.id}`; 522 summaryDataEl.nodeId = ` @${summaryDataEl.id}`; 523 if (data.isString()) { 524 summaryDataEl.objectName = `"${summaryDataEl.nodeName}"` + ` @${summaryDataEl.id}`; 525 } else { 526 summaryDataEl.objectName = nodeId; 527 } 528 if (summaryDataEl.edgeName !== '') { 529 summaryDataEl.objectName = `${summaryDataEl.edgeName}\xa0` + '::' + `\xa0${nodeId}`; 530 } 531 }); 532 } else { 533 this.tblSummary!.snapshotDataSource = []; 534 } 535 } else { 536 data.status = true; 537 } 538 this.tblSummaryIconClickExtend(); 539 }; 540 541 tblSummaryIconClickExtend(): void { 542 if (this.search!.value !== '') { 543 if (this.leftTheadTable!.hasAttribute('sort')) { 544 this.tblSummary!.snapshotDataSource = this.leftArray; 545 } else { 546 this.tblSummary!.snapshotDataSource = this.summaryFilter; 547 } 548 } else { 549 if (this.leftTheadTable!.hasAttribute('sort')) { 550 this.tblSummary!.snapshotDataSource = this.leftArray; 551 } else { 552 this.tblSummary!.snapshotDataSource = this.summary; 553 } 554 } 555 new ResizeObserver(() => { 556 if (this.parentElement?.clientHeight !== 0) { 557 this.tblSummary!.style.height = '100%'; 558 this.tblSummary!.reMeauseHeight(); 559 } 560 }).observe(this.parentElement!); 561 } 562 563 tbsRowClick = (rowEvent: Event): void => { 564 // @ts-ignore 565 let data = rowEvent.detail.data as ConstructorItem; 566 (data as any).isSelected = true; 567 // @ts-ignore 568 if ((rowEvent.detail as any).callBack) { 569 // @ts-ignore 570 (rowEvent.detail as any).callBack(true); 571 } 572 }; 573 574 tblSummaryRowClick = (evt: Event): void => { 575 this.rightTheadTable!.removeAttribute('sort'); 576 this.tbsTable!.scrollTop = 0; 577 //@ts-ignore 578 let data = evt.detail.data as ConstructorItem; 579 (data as any).isSelected = true; 580 this.initRetainsData(data); 581 if (this.retainsData.length > 0) { 582 if (this.retainsData[0].distance > 1) { 583 this.retainsData[0].getChildren(); 584 this.retainsData[0].expanded = false; 585 } 586 let i = 0; 587 let retainsTable = (): void => { 588 const getList = (list: Array<ConstructorItem>): void => { 589 list.forEach((summaryRow: ConstructorItem) => { 590 let retainsShallow = `${Math.round((summaryRow.shallowSize / this.fileSize) * 100)}%`; 591 let retained = `${Math.round((summaryRow.retainedSize / this.fileSize) * 100)}%`; 592 summaryRow.shallowPercent = retainsShallow; 593 summaryRow.retainedPercent = retained; 594 let nodeId = `${summaryRow.nodeName} @${summaryRow.id}`; 595 summaryRow.objectName = `${summaryRow.edgeName}\xa0` + 'in' + `\xa0${nodeId}`; 596 if (summaryRow.distance >= 100000000 || summaryRow.distance === -5) { 597 //@ts-ignore 598 summaryRow.distance = '-'; 599 } 600 i++; 601 //@ts-ignore 602 if (i < this.retainsData[0].distance - 1 && list[0].distance !== '-') { 603 list[0].getChildren(); 604 list[0].expanded = false; 605 if (summaryRow.hasNext) { 606 getList(summaryRow.children); 607 } 608 } else { 609 return; 610 } 611 }); 612 }; 613 getList(this.retainsData[0].children); 614 }; 615 retainsTable(); 616 this.tbs!.snapshotDataSource = this.retainsData; 617 } else { 618 this.tbs!.snapshotDataSource = []; 619 } 620 //@ts-ignore 621 this.tblSummaryRowClickExtend(evt.detail); 622 }; 623 624 private initRetainsData(data: ConstructorItem): void { 625 this.retainsData = []; 626 this.retainsData = HeapDataInterface.getInstance().getRetains(data); 627 this.retainsData.forEach((element) => { 628 let shallow = `${Math.round((element.shallowSize / this.fileSize) * 100)}%`; 629 let retained = `${Math.round((element.retainedSize / this.fileSize) * 100)}%`; 630 element.shallowPercent = shallow; 631 element.retainedPercent = retained; 632 if (element.distance >= 100000000 || element.distance === -5) { 633 //@ts-ignore 634 element.distance = '-'; 635 } 636 let nodeId = `${element.nodeName} @${element.id}`; 637 element.objectName = `${element.edgeName}\xa0` + 'in' + `\xa0${nodeId}`; 638 }); 639 } 640 641 private tblSummaryRowClickExtend(detail: any): void { 642 if (this.file!.name.includes('Timeline')) { 643 this.stackData = HeapDataInterface.getInstance().getAllocationStackData(detail.data); 644 if (this.stackData.length > 0) { 645 this.stackTable!.recycleDataSource = this.stackData; 646 this.stackText!.textContent = ''; 647 this.stackText!.style.display = 'none'; 648 if (this.stack!.className === 'active') { 649 this.stackTable!.style.display = 'grid'; 650 this.tbs!.style.display = 'none'; 651 } 652 } else { 653 this.stackText!.style.display = 'flex'; 654 this.stackTable!.recycleDataSource = []; 655 this.stackTable!.style.display = 'none'; 656 if (this.retainers!.className === 'active') { 657 this.stackText!.style.display = 'none'; 658 } 659 if (this.retainsData === undefined || this.retainsData.length === 0) { 660 this.stackText!.textContent = ''; 661 } else { 662 this.stackText!.textContent = 663 'Stack was not recorded for this object because it had been allocated before ' + 664 'this profile recording started.'; 665 } 666 } 667 } 668 new ResizeObserver(() => { 669 this.tbs!.style.height = 'calc(100% - 30px)'; 670 this.tbs!.reMeauseHeight(); 671 this.stackTable!.style.height = 'calc(100% - 30px)'; 672 this.stackTable!.reMeauseHeight(); 673 }).observe(this.parentElement!); 674 if (detail.callBack) { 675 detail.callBack(true); 676 } 677 } 678 679 initHtml(): string { 680 return TabPaneSummaryHtml; 681 } 682} 683