• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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