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 { SpSystemTrace } from '../SpSystemTrace'; 17import { TraceRow } from '../trace/base/TraceRow'; 18import { type BaseStruct } from '../../bean/BaseStruct'; 19import { renders } from '../../database/ui-worker/ProcedureWorker'; 20import { Utils } from '../trace/base/Utils'; 21import { type EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; 22import { info } from '../../../log/Log'; 23import { type SnapshotRender, SnapshotStruct } from '../../database/ui-worker/ProcedureWorkerSnapshot'; 24import { type TreeItemData } from '../../../base-ui/tree/LitTree'; 25import { MemoryConfig } from '../../bean/MemoryConfig'; 26import { TabPaneSmapsRecord } from '../trace/sheet/smaps/TabPaneSmapsRecord'; 27import { 28 dmaDataSender, 29 gpuGpuDataSender, 30 gpuMemoryDataSender, 31 gpuResourceDataSender, 32 gpuTotalDataSender, 33 gpuWindowDataSender, 34 purgeableDataSender, 35 sMapsDataSender, 36 shmDataSender, 37} from '../../database/data-trafic/VmTrackerDataSender'; 38import { resetVmTracker } from '../../database/data-trafic/VmTrackerDataReceiver'; 39import {querySmapsExits} from "../../database/sql/Smaps.sql"; 40import { 41 queryisExistsGpuMemoryData, 42 queryisExistsPurgeableData, 43 queryisExistsShmData 44} from "../../database/sql/Memory.sql"; 45import {queryisExistsDmaData} from "../../database/sql/Dma.sql"; 46import { 47 queryGpuTotalType, 48 queryGpuWindowType, 49 queryisExistsGpuData, 50 queryisExistsGpuResourceData 51} from "../../database/sql/Gpu.sql"; 52 53export class VmTrackerChart { 54 private trace: SpSystemTrace; 55 private rowFolder!: TraceRow<BaseStruct>; 56 private sMapsFolder!: TraceRow<BaseStruct>; 57 private gpuFolder!: TraceRow<BaseStruct>; 58 private memoryConfig: MemoryConfig = MemoryConfig.getInstance(); 59 static gpuTotalModule: number | null = null; //ns 60 static gpuWindow: number | null = null; //ns 61 static gpuWindowModule: number | null = null; //ns 62 private smapsRecordTab: TabPaneSmapsRecord | undefined | null; 63 private scratchId = -1; 64 constructor(trace: SpSystemTrace) { 65 this.trace = trace; 66 } 67 68 async init(): Promise<void> { 69 this.smapsRecordTab = this.trace 70 .shadowRoot!.querySelector('div > trace-sheet')! 71 .shadowRoot!.querySelector<TabPaneSmapsRecord>('#box-smaps-record > tabpane-smaps-record'); 72 if (this.scratchId == -1) { 73 for (let [key, value] of SpSystemTrace.DATA_DICT) { 74 if (value === 'Scratch') { 75 this.scratchId = key; 76 break; 77 } 78 } 79 } 80 const result = await querySmapsExits(); 81 if (result.length <= 0) { 82 return; 83 } 84 await this.initVmTrackerFolder(); 85 await this.initSMapsFolder(); 86 const rowNameList: Array<string> = ['Dirty', 'Swapped', 'RSS', 'PSS', 'USS']; 87 for (const rowName of rowNameList) { 88 await this.initSmapsRows(rowName); 89 } 90 const isExistsShm = await queryisExistsShmData(this.memoryConfig.iPid); 91 const isExistsDma = await queryisExistsDmaData(this.memoryConfig.iPid); 92 if (isExistsShm[0].data_exists) { 93 await this.initShmRows(); 94 } 95 await this.initPurgeableVM(); 96 // @ts-ignore 97 if (isExistsDma[0].data_exists) { 98 await this.initDmaRow(); 99 } 100 const isExistsGpuMemory = await queryisExistsGpuMemoryData(this.memoryConfig.iPid); 101 const isExistsGpuResource = await queryisExistsGpuResourceData(this.scratchId); 102 const isExistsGraph = await queryisExistsGpuData(MemoryConfig.getInstance().iPid, "'mem.graph_pss'"); 103 const isExistsGl = await queryisExistsGpuData(MemoryConfig.getInstance().iPid, "'mem.gl_pss'"); 104 105 if ( 106 // @ts-ignore 107 isExistsGpuMemory[0].data_exists || 108 // @ts-ignore 109 isExistsGpuResource[0].data_exists || 110 // @ts-ignore 111 isExistsGraph[0].data_exists || 112 // @ts-ignore 113 isExistsGl[0].data_exists 114 ) { 115 await this.initGpuFolder(); 116 // @ts-ignore 117 if (isExistsGpuMemory[0].data_exists) { 118 await this.initGpuMemoryRow(); 119 } 120 // @ts-ignore 121 if (isExistsGpuResource[0].data_exists) { 122 await this.initGpuResourceRow(this.scratchId); 123 } else { 124 this.smapsRecordTab!.GLESHostCache = []; 125 } 126 // @ts-ignore 127 if (isExistsGraph[0].data_exists) { 128 await this.addGpuGraphRow(); 129 } 130 // @ts-ignore 131 if (isExistsGl[0].data_exists) { 132 await this.addGpuGLRow(); 133 await this.addGpuTotalRow(); 134 await this.addGpuWindowRow(); 135 } 136 } 137 } 138 139 private initVmTrackerFolder = async (): Promise<void> => { 140 let VmTrackerRow = TraceRow.skeleton(); 141 VmTrackerRow.rowId = 'VmTrackerRow'; 142 VmTrackerRow.rowType = TraceRow.ROW_TYPE_VM_TRACKER; 143 VmTrackerRow.addTemplateTypes('ProcessMemory'); 144 VmTrackerRow.addTemplateTypes('Memory'); 145 VmTrackerRow.rowParentId = ''; 146 VmTrackerRow.style.height = '40px'; 147 VmTrackerRow.index = 0; 148 VmTrackerRow.folder = true; 149 VmTrackerRow.name = `VM Tracker (${this.memoryConfig.processName} ${this.memoryConfig.pid})`; 150 VmTrackerRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 151 VmTrackerRow.selectChangeHandler = this.trace.selectChangeHandler; 152 VmTrackerRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => 153 new Promise<Array<SnapshotStruct>>((resolve) => resolve([])); 154 VmTrackerRow.onThreadHandler = (useCache): void => { 155 let context: CanvasRenderingContext2D; 156 if (VmTrackerRow.currentContext) { 157 context = VmTrackerRow.currentContext; 158 } else { 159 context = VmTrackerRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 160 } 161 VmTrackerRow.canvasSave(context); 162 if (VmTrackerRow.expansion) { 163 context?.clearRect(0, 0, VmTrackerRow.frame.width, VmTrackerRow.frame.height); 164 } else { 165 (renders.empty as EmptyRender).renderMainThread( 166 { 167 context: context, 168 useCache: useCache, 169 type: '', 170 }, 171 VmTrackerRow 172 ); 173 } 174 VmTrackerRow.canvasRestore(context, this.trace); 175 }; 176 this.rowFolder = VmTrackerRow; 177 this.trace.rowsEL?.appendChild(VmTrackerRow); 178 }; 179 180 private initSMapsFolder = async (): Promise<void> => { 181 let sMapsRow = TraceRow.skeleton<SnapshotStruct>(); 182 sMapsRow.rowId = 'smapsRow'; 183 sMapsRow.rowParentId = 'VmTrackerRow'; 184 sMapsRow.rowHidden = !this.rowFolder.expansion; 185 sMapsRow.rowType = TraceRow.ROW_TYPE_VM_TRACKER_SMAPS; 186 sMapsRow.folder = true; 187 sMapsRow.name = 'Smaps'; 188 sMapsRow.folderPaddingLeft = 20; 189 sMapsRow.style.height = '40px'; 190 sMapsRow.style.width = '100%'; 191 sMapsRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 192 sMapsRow.selectChangeHandler = this.trace.selectChangeHandler; 193 sMapsRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => 194 new Promise<Array<SnapshotStruct>>((resolve) => resolve([])); 195 sMapsRow.onThreadHandler = (useCache): void => { 196 let context: CanvasRenderingContext2D; 197 if (sMapsRow.currentContext) { 198 context = sMapsRow.currentContext; 199 } else { 200 context = sMapsRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 201 } 202 sMapsRow.canvasSave(context); 203 if (sMapsRow.expansion) { 204 context?.clearRect(0, 0, sMapsRow.frame.width, sMapsRow.frame.height); 205 } else { 206 (renders.empty as EmptyRender).renderMainThread( 207 { 208 context: context, 209 useCache: useCache, 210 type: '', 211 }, 212 sMapsRow 213 ); 214 } 215 sMapsRow.canvasRestore(context, this.trace); 216 }; 217 this.sMapsFolder = sMapsRow; 218 this.rowFolder?.addChildTraceRow(sMapsRow); 219 }; 220 221 private initGpuFolder = async (): Promise<TraceRow<SnapshotStruct>> => { 222 let gpuTraceRow = TraceRow.skeleton<SnapshotStruct>(); 223 gpuTraceRow.rowId = 'skiaGpuTraceRow'; 224 gpuTraceRow.rowType = TraceRow.ROW_TYPE_SYS_MEMORY_GPU; 225 gpuTraceRow.rowParentId = 'VmTrackerRow'; 226 gpuTraceRow.style.height = '40px'; 227 gpuTraceRow.folder = true; 228 gpuTraceRow.folderPaddingLeft = 20; 229 gpuTraceRow.rowHidden = !this.rowFolder.expansion; 230 gpuTraceRow.name = 'GPU'; 231 gpuTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 232 gpuTraceRow.selectChangeHandler = this.trace.selectChangeHandler; 233 gpuTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => 234 new Promise<Array<SnapshotStruct>>((resolve) => resolve([])); 235 gpuTraceRow.onThreadHandler = (useCache): void => { 236 let context: CanvasRenderingContext2D; 237 if (gpuTraceRow.currentContext) { 238 context = gpuTraceRow.currentContext; 239 } else { 240 context = gpuTraceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 241 } 242 gpuTraceRow.canvasSave(context); 243 if (gpuTraceRow.expansion) { 244 context?.clearRect(0, 0, gpuTraceRow.frame.width, gpuTraceRow.frame.height); 245 } else { 246 (renders.empty as EmptyRender).renderMainThread( 247 { 248 context: context, 249 useCache: useCache, 250 type: '', 251 }, 252 gpuTraceRow 253 ); 254 } 255 gpuTraceRow.canvasRestore(context, this.trace); 256 }; 257 this.gpuFolder = gpuTraceRow; 258 this.rowFolder.addChildTraceRow(gpuTraceRow); 259 return gpuTraceRow; 260 }; 261 262 private getSmapsKeyName(rowName: string): string { 263 let columnName = rowName.toLowerCase(); 264 let keyName = ''; 265 switch (rowName) { 266 case 'USS': 267 keyName = 'private_clean + private_dirty'; 268 break; 269 case 'RSS': 270 keyName = 'resident_size'; 271 break; 272 case 'Swapped': 273 keyName = 'swap + swap_pss'; 274 break; 275 default: 276 keyName = columnName; 277 } 278 return keyName; 279 } 280 281 private initSmapsRows = async (rowName: string): Promise<void> => { 282 let sMapsTraceRow = this.initTraceRow(rowName, TraceRow.ROW_TYPE_VM_TRACKER_SMAPS, 'smapsRow'); 283 sMapsTraceRow.rowHidden = !this.sMapsFolder.expansion; 284 sMapsTraceRow.folderTextLeft = 40; 285 sMapsTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => { 286 return sMapsDataSender(this.getSmapsKeyName(rowName), sMapsTraceRow).then((sMaps: any[]) => { 287 this.setName(sMaps); 288 return sMaps; 289 }); 290 }; 291 this.sMapsFolder.addChildTraceRow(sMapsTraceRow); 292 }; 293 294 private initShmRows = async (): Promise<void> => { 295 let shmTraceRow = this.initTraceRow('SHM', TraceRow.ROW_TYPE_VMTRACKER_SHM, 'VmTrackerRow'); 296 shmTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => { 297 return shmDataSender(this.memoryConfig.iPid, shmTraceRow).then((shmData: any[]) => { 298 this.setName(shmData); 299 return shmData; 300 }); 301 }; 302 this.rowFolder.addChildTraceRow(shmTraceRow); 303 }; 304 305 private async initPurgeableTotal(): Promise<void> { 306 let totalTraceRow = this.initTraceRow('Purgeable Total', TraceRow.ROW_TYPE_PURGEABLE_TOTAL_VM, 'VmTrackerRow'); 307 totalTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => { 308 return purgeableDataSender(this.memoryConfig.iPid, totalTraceRow, false).then((purgeableTotalData: any[]) => { 309 this.setName(purgeableTotalData); 310 return purgeableTotalData; 311 }); 312 }; 313 this.rowFolder.addChildTraceRow(totalTraceRow); 314 } 315 316 private async initPurgeablePin(): Promise<void> { 317 let pinTraceRow = this.initTraceRow('Purgeable Pin', TraceRow.ROW_TYPE_PURGEABLE_PIN_VM, 'VmTrackerRow'); 318 pinTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => { 319 return purgeableDataSender(this.memoryConfig.iPid, pinTraceRow, true).then((purgeablePinData: any[]) => { 320 this.setName(purgeablePinData); 321 return purgeablePinData; 322 }); 323 }; 324 this.rowFolder.addChildTraceRow(pinTraceRow); 325 } 326 327 private initPurgeableVM = async (): Promise<void> => { 328 let time = new Date().getTime(); 329 const isExistsPurgeableTotal = await queryisExistsPurgeableData(this.memoryConfig.iPid, false); 330 const isExistsPurgeablePin = await queryisExistsPurgeableData(this.memoryConfig.iPid, true); 331 if (isExistsPurgeableTotal[0].data_exists) { 332 await this.initPurgeableTotal(); 333 } 334 if (isExistsPurgeablePin[0].data_exists) { 335 await this.initPurgeablePin(); 336 } 337 let durTime = new Date().getTime() - time; 338 info('The time to load the VM Purgeable is: ', durTime); 339 }; 340 341 private initDmaRow = async (): Promise<void> => { 342 let dmaTraceRow = this.initTraceRow('DMA', TraceRow.ROW_TYPE_DMA_VMTRACKER, 'VmTrackerRow'); 343 dmaTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => { 344 return dmaDataSender(this.memoryConfig.iPid, dmaTraceRow).then((dmaData: any[]) => { 345 this.setName(dmaData); 346 return dmaData; 347 }); 348 }; 349 this.rowFolder.addChildTraceRow(dmaTraceRow); 350 }; 351 352 private initGpuMemoryRow = async (): Promise<void> => { 353 let gpuMemoryTraceRow = this.initTraceRow( 354 'Skia Gpu Memory', 355 TraceRow.ROW_TYPE_GPU_MEMORY_VMTRACKER, 356 'skiaGpuTraceRow' 357 ); 358 gpuMemoryTraceRow.rowHidden = !this.gpuFolder.expansion; 359 gpuMemoryTraceRow.folderTextLeft = 40; 360 gpuMemoryTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => { 361 return gpuMemoryDataSender(this.memoryConfig.iPid, gpuMemoryTraceRow).then((gpuMemoryData: any[]) => { 362 this.setName(gpuMemoryData); 363 return gpuMemoryData; 364 }); 365 }; 366 this.gpuFolder.addChildTraceRow(gpuMemoryTraceRow); 367 }; 368 369 private initGpuResourceRow = async (scratchId: number): Promise<void> => { 370 let gpuMemoryTraceRow = this.initTraceRow( 371 'Gpu Resource', 372 TraceRow.ROW_TYPE_GPU_RESOURCE_VMTRACKER, 373 this.gpuFolder.rowId! 374 ); 375 gpuMemoryTraceRow.rowHidden = !this.gpuFolder.expansion; 376 gpuMemoryTraceRow.folderTextLeft = 40; 377 gpuMemoryTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => { 378 return gpuResourceDataSender(scratchId, gpuMemoryTraceRow).then((gpuResourceData: any[]) => { 379 this.setName(gpuResourceData); 380 // 将泳道图数据传递给Native Heap Tab页 381 this.smapsRecordTab!.GLESHostCache = gpuResourceData; 382 return gpuResourceData; 383 }); 384 }; 385 this.gpuFolder.addChildTraceRow(gpuMemoryTraceRow); 386 }; 387 388 private async addGpuGraphRow(): Promise<void> { 389 let graphRow = this.initTraceRow('Graph', TraceRow.ROW_TYPE_SYS_MEMORY_GPU_GRAPH, this.gpuFolder.rowId!); 390 graphRow.addTemplateTypes('sys-memory'); 391 graphRow.folderTextLeft = 40; 392 graphRow.supplierFrame = (): Promise<SnapshotStruct[]> => { 393 return gpuGpuDataSender(this.memoryConfig.iPid, "'mem.graph_pss'", graphRow).then((graphData: any[]) => { 394 this.setName(graphData); 395 return graphData; 396 }); 397 }; 398 this.gpuFolder.addChildTraceRow(graphRow); 399 } 400 401 private async addGpuGLRow(): Promise<void> { 402 let glRow = this.initTraceRow('GL', TraceRow.ROW_TYPE_SYS_MEMORY_GPU_GL, this.gpuFolder.rowId!); 403 glRow.addTemplateTypes('sys-memory'); 404 glRow.folderTextLeft = 40; 405 glRow.supplierFrame = (): Promise<SnapshotStruct[]> => { 406 return gpuGpuDataSender(this.memoryConfig.iPid, "'mem.gl_pss'", glRow).then((glData: any[]) => { 407 this.setName(glData); 408 return glData; 409 }); 410 }; 411 this.gpuFolder.addChildTraceRow(glRow); 412 } 413 414 private async addGpuTotalRow(): Promise<void> { 415 let types = await queryGpuTotalType(); 416 if (!types || types.length === 0) { 417 return; 418 } 419 let gpuTotalRow = this.initTraceRow( 420 'Skia Gpu Dump Total', 421 TraceRow.ROW_TYPE_SYS_MEMORY_GPU_TOTAL, 422 this.gpuFolder.rowId! 423 ); 424 gpuTotalRow.folderTextLeft = 40; 425 gpuTotalRow.addTemplateTypes('sys-memory'); 426 gpuTotalRow.addRowSettingPop(); 427 gpuTotalRow.rowSetting = 'enable'; 428 gpuTotalRow.rowSettingList = [ 429 { 430 key: 'total', 431 title: 'Total', 432 checked: true, 433 }, 434 ...types.map( 435 ( 436 it 437 ): { 438 key: string; 439 title: string; 440 } => { 441 return { 442 key: `${it.id}`, 443 title: it.data, 444 }; 445 } 446 ), 447 ]; 448 gpuTotalRow.onRowSettingChangeHandler = (setting): void => { 449 if (setting && setting.length > 0) { 450 gpuTotalRow.dataListCache = []; 451 gpuTotalRow.dataList = []; 452 gpuTotalRow.isComplete = false; 453 VmTrackerChart.gpuTotalModule = setting[0] === 'total' ? null : parseInt(setting[0]); 454 gpuTotalRow.needRefresh = true; 455 gpuTotalRow.drawFrame(); 456 } 457 }; 458 gpuTotalRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => { 459 return gpuTotalDataSender(VmTrackerChart.gpuTotalModule, gpuTotalRow).then((gpuTotalData: any[]) => { 460 this.setName(gpuTotalData); 461 return gpuTotalData; 462 }); 463 }; 464 this.gpuFolder.addChildTraceRow(gpuTotalRow); 465 } 466 467 private async addGpuWindowRow(): Promise<void> { 468 let types = await queryGpuWindowType(); 469 if (!types || types.length === 0) { 470 return; 471 } 472 let settings: TreeItemData[] = types 473 .filter((it) => it.pid === null) 474 .map((it) => { 475 return { 476 key: `${it.id}`, 477 title: it.data, 478 children: [], 479 }; 480 }); 481 settings.forEach((it) => { 482 it.children = types 483 .filter((child) => `${child.pid}` === it.key) 484 .map((item) => { 485 return { 486 key: `${it.key}-${item.id}`, 487 title: item.data, 488 }; 489 }); 490 }); 491 settings[0].checked = true; 492 VmTrackerChart.gpuWindow = parseInt(settings[0].key); 493 VmTrackerChart.gpuWindowModule = null; 494 let gpuWindowRow = this.initTraceRow( 495 'Skia Gpu Dump Window', 496 TraceRow.ROW_TYPE_SYS_MEMORY_GPU_WINDOW, 497 this.gpuFolder.rowId! 498 ); 499 gpuWindowRow.folderTextLeft = 40; 500 gpuWindowRow.addRowSettingPop(); 501 gpuWindowRow.rowSetting = 'enable'; 502 gpuWindowRow.rowSettingList = settings; 503 gpuWindowRow.addTemplateTypes('sys-memory'); 504 gpuWindowRow.onRowSettingChangeHandler = (setting) => { 505 if (setting && setting.length > 0) { 506 let split = setting[0].split('-'); 507 VmTrackerChart.gpuWindow = parseInt(split[0]); 508 VmTrackerChart.gpuWindowModule = split.length > 1 ? parseInt(split[1]) : null; 509 gpuWindowRow.dataListCache = []; 510 gpuWindowRow.dataList = []; 511 gpuWindowRow.isComplete = false; 512 gpuWindowRow.needRefresh = true; 513 gpuWindowRow.drawFrame(); 514 } 515 }; 516 gpuWindowRow.supplierFrame = () => { 517 return gpuWindowDataSender(VmTrackerChart.gpuWindow!, VmTrackerChart.gpuWindowModule, gpuWindowRow).then( 518 (gpuWindowData: any[]) => { 519 this.setName(gpuWindowData); 520 return gpuWindowData; 521 } 522 ); 523 }; 524 this.gpuFolder.addChildTraceRow(gpuWindowRow); 525 } 526 527 private initTraceRow(rowName: string, type: string, rowParentId: string): TraceRow<SnapshotStruct> { 528 let vmTrackerTraceRow = TraceRow.skeleton<SnapshotStruct>(); 529 vmTrackerTraceRow.rowParentId = rowParentId; 530 vmTrackerTraceRow.rowId = rowName; 531 vmTrackerTraceRow.rowType = type; 532 vmTrackerTraceRow.folderTextLeft = 20; 533 vmTrackerTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 534 vmTrackerTraceRow.selectChangeHandler = this.trace.selectChangeHandler; 535 vmTrackerTraceRow.style.height = '40px'; 536 vmTrackerTraceRow.style.width = '100%'; 537 vmTrackerTraceRow.setAttribute('children', ''); 538 vmTrackerTraceRow.name = rowName; 539 vmTrackerTraceRow.focusHandler = (): void => { 540 this.showTip(vmTrackerTraceRow); 541 }; 542 vmTrackerTraceRow.findHoverStruct = () => { 543 SnapshotStruct.hoverSnapshotStruct = vmTrackerTraceRow.getHoverStruct(); 544 }; 545 vmTrackerTraceRow.onThreadHandler = (useCache): void => { 546 let context: CanvasRenderingContext2D; 547 if (vmTrackerTraceRow.currentContext) { 548 context = vmTrackerTraceRow.currentContext; 549 } else { 550 context = vmTrackerTraceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 551 } 552 vmTrackerTraceRow.canvasSave(context); 553 (renders.snapshot as SnapshotRender).renderMainThread( 554 { 555 context: context, 556 useCache: useCache, 557 type: 'snapshot', 558 }, 559 vmTrackerTraceRow 560 ); 561 vmTrackerTraceRow.canvasRestore(context, this.trace); 562 }; 563 return vmTrackerTraceRow; 564 } 565 566 private showTip(traceRow: TraceRow<SnapshotStruct>): void { 567 this.trace?.displayTip( 568 traceRow, 569 SnapshotStruct.hoverSnapshotStruct, 570 `<span>Name: ${SnapshotStruct.hoverSnapshotStruct?.name || ''}</span> 571 <span>Size: ${Utils.getBinaryByteWithUnit(SnapshotStruct.hoverSnapshotStruct?.value || 0)}</span>` 572 ); 573 } 574 575 private setName(data: Array<any>): void { 576 if (data.length > 0) { 577 data.forEach((item, index) => { 578 item.name = `SnapShot ${index}`; 579 }); 580 } 581 } 582} 583