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