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