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