• 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.js';
17import {
18  queryDmaSampsData,
19  queryGpuMemoryData,
20  querySmapsData,
21  querySmapsExits,
22  queryVmTrackerShmData,
23  queryPurgeableProcessData,
24  queryGpuGLData,
25  queryGpuTotalData,
26  queryGpuTotalType,
27  queryGpuWindowData,
28  queryGpuWindowType,
29} from '../../database/SqlLite.js';
30import { TraceRow } from '../trace/base/TraceRow.js';
31import { BaseStruct } from '../../bean/BaseStruct.js';
32import { renders } from '../../database/ui-worker/ProcedureWorker.js';
33import { Utils } from '../trace/base/Utils.js';
34import { EmptyRender } from '../../database/ui-worker/ProcedureWorkerCPU.js';
35import { info } from '../../../log/Log.js';
36import { SnapshotRender, SnapshotStruct } from '../../database/ui-worker/ProcedureWorkerSnapshot.js';
37import { TreeItemData } from '../../../base-ui/tree/LitTree.js';
38import { MemoryConfig } from '../../bean/MemoryConfig.js';
39
40export class VmTrackerChart {
41  private trace: SpSystemTrace;
42  private rowFolder!: TraceRow<BaseStruct>;
43  private sMapsFolder!: TraceRow<BaseStruct>;
44  private gpuFolder!: TraceRow<BaseStruct>;
45  private memoryConfig: MemoryConfig = MemoryConfig.getInstance();
46  static gpuTotalModule: number | null = null; //ns
47  static gpuWindow: number | null = null; //ns
48  static gpuWindowModule: number | null = null; //ns
49  constructor(trace: SpSystemTrace) {
50    this.trace = trace;
51  }
52
53  async init(): Promise<void> {
54    const result = await querySmapsExits();
55    if (result.length <= 0) {
56      return;
57    }
58    await this.initVmTrackerFolder();
59    await this.initSMapsFolder();
60    const rowNameList: Array<string> = ['Dirty', 'Swapped', 'RSS', 'PSS', 'USS'];
61    for (const rowName of rowNameList) {
62      await this.initSmapsRows(rowName);
63    }
64    await this.initShmRows();
65    await this.initPurgeableVM();
66    await this.initDmaRow();
67    const gpuMemoryData = await queryGpuMemoryData(this.memoryConfig.iPid);
68    const glArr = await queryGpuGLData(MemoryConfig.getInstance().iPid).then((res) => {
69      res.forEach((gl, index) => {
70        (gl as any).name = `SnapShot ${index}`;
71      });
72      return res as SnapshotStruct[];
73    });
74    if (gpuMemoryData.length > 0 || glArr.length > 0) {
75      await this.initGpuFolder();
76      if (gpuMemoryData.length > 0) {
77        await this.initGpuMemoryRow(gpuMemoryData);
78      }
79      await this.addGpuGLRow(glArr);
80      if (glArr.length > 0) {
81        await this.addGpuTotalRow();
82        await this.addGpuWindowRow();
83      }
84    }
85  }
86
87  private initVmTrackerFolder = async (): Promise<void> => {
88    let VmTrackerRow = TraceRow.skeleton();
89    VmTrackerRow.rowId = 'VmTrackerRow';
90    VmTrackerRow.rowType = TraceRow.ROW_TYPE_VM_TRACKER;
91    VmTrackerRow.rowParentId = '';
92    VmTrackerRow.style.height = '40px';
93    VmTrackerRow.index = 0;
94    VmTrackerRow.folder = true;
95    VmTrackerRow.name = `VM Tracker (${this.memoryConfig.processName} ${this.memoryConfig.pid})`;
96    VmTrackerRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
97    VmTrackerRow.selectChangeHandler = this.trace.selectChangeHandler;
98    VmTrackerRow.supplier = (): Promise<Array<SnapshotStruct>> =>
99      new Promise<Array<SnapshotStruct>>((resolve) => resolve([]));
100    VmTrackerRow.onThreadHandler = (useCache): void => {
101      VmTrackerRow.canvasSave(this.trace.canvasPanelCtx!);
102      if (VmTrackerRow.expansion) {
103        this.trace.canvasPanelCtx?.clearRect(0, 0, VmTrackerRow.frame.width, VmTrackerRow.frame.height);
104      } else {
105        (renders.empty as EmptyRender).renderMainThread(
106          {
107            context: this.trace.canvasPanelCtx,
108            useCache: useCache,
109            type: '',
110          },
111          VmTrackerRow
112        );
113      }
114      VmTrackerRow.canvasRestore(this.trace.canvasPanelCtx!);
115    };
116    this.rowFolder = VmTrackerRow;
117    this.trace.rowsEL?.appendChild(VmTrackerRow);
118  };
119
120  private initSMapsFolder = async (): Promise<void> => {
121    let sMapsRow = TraceRow.skeleton<SnapshotStruct>();
122    sMapsRow.rowId = 'smapsRow';
123    sMapsRow.rowParentId = 'VmTrackerRow';
124    sMapsRow.rowHidden = !this.rowFolder.expansion;
125    sMapsRow.rowType = TraceRow.ROW_TYPE_VM_TRACKER_SMAPS;
126    sMapsRow.folder = true;
127    sMapsRow.name = 'Smaps';
128    sMapsRow.folderPaddingLeft = 20;
129    sMapsRow.style.height = '40px';
130    sMapsRow.style.width = '100%';
131    sMapsRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
132    sMapsRow.selectChangeHandler = this.trace.selectChangeHandler;
133    sMapsRow.supplier = (): Promise<Array<SnapshotStruct>> =>
134      new Promise<Array<SnapshotStruct>>((resolve) => resolve([]));
135    sMapsRow.onThreadHandler = (useCache): void => {
136      sMapsRow.canvasSave(this.trace.canvasPanelCtx!);
137      if (sMapsRow.expansion) {
138        this.trace.canvasPanelCtx?.clearRect(0, 0, sMapsRow.frame.width, sMapsRow.frame.height);
139      } else {
140        (renders.empty as EmptyRender).renderMainThread(
141          {
142            context: this.trace.canvasPanelCtx,
143            useCache: useCache,
144            type: '',
145          },
146          sMapsRow
147        );
148      }
149      sMapsRow.canvasRestore(this.trace.canvasPanelCtx!);
150    };
151    this.sMapsFolder = sMapsRow;
152    this.rowFolder?.addChildTraceRow(sMapsRow);
153  };
154
155  private initGpuFolder = async (): Promise<TraceRow<SnapshotStruct>> => {
156    let gpuTraceRow = TraceRow.skeleton<SnapshotStruct>();
157    gpuTraceRow.rowId = 'skiaGpuTraceRow';
158    gpuTraceRow.rowType = TraceRow.ROW_TYPE_SYS_MEMORY_GPU;
159    gpuTraceRow.rowParentId = 'VmTrackerRow';
160    gpuTraceRow.style.height = '40px';
161    gpuTraceRow.folder = true;
162    gpuTraceRow.folderPaddingLeft = 20;
163    gpuTraceRow.rowHidden = !this.rowFolder.expansion;
164    gpuTraceRow.name = 'GPU';
165    gpuTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
166    gpuTraceRow.selectChangeHandler = this.trace.selectChangeHandler;
167    gpuTraceRow.supplier = (): Promise<Array<SnapshotStruct>> =>
168      new Promise<Array<SnapshotStruct>>((resolve) => resolve([]));
169    gpuTraceRow.onThreadHandler = (useCache): void => {
170      gpuTraceRow.canvasSave(this.trace.canvasPanelCtx!);
171      if (gpuTraceRow.expansion) {
172        this.trace.canvasPanelCtx?.clearRect(0, 0, gpuTraceRow.frame.width, gpuTraceRow.frame.height);
173      } else {
174        (renders.empty as EmptyRender).renderMainThread(
175          {
176            context: this.trace.canvasPanelCtx,
177            useCache: useCache,
178            type: '',
179          },
180          gpuTraceRow
181        );
182      }
183      gpuTraceRow.canvasRestore(this.trace.canvasPanelCtx!);
184    };
185    this.gpuFolder = gpuTraceRow;
186    this.rowFolder.addChildTraceRow(gpuTraceRow);
187    return gpuTraceRow;
188  };
189
190  private getSmapsKeyName(rowName: string): string {
191    let columnName = rowName.toLowerCase();
192    let keyName = '';
193    switch (rowName) {
194      case 'USS':
195        keyName = 'private_clean + private_dirty';
196        break;
197      case 'RSS':
198        keyName = 'resident_size';
199        break;
200      default:
201        keyName = columnName;
202    }
203    return keyName;
204  }
205
206  private initSmapsRows = async (rowName: string): Promise<void> => {
207    let sMapsTraceRow = this.initTraceRow(rowName, TraceRow.ROW_TYPE_VM_TRACKER_SMAPS, 'smapsRow');
208    sMapsTraceRow.rowHidden = !this.sMapsFolder.expansion;
209    sMapsTraceRow.folderTextLeft = 40;
210    let sMapsData = await querySmapsData(this.getSmapsKeyName(rowName));
211    for (let i = 0; i < sMapsData.length; i++) {
212      sMapsData[i].name = `Snapshot${i}`;
213    }
214    sMapsTraceRow.supplier = (): Promise<Array<SnapshotStruct>> =>
215      new Promise<Array<SnapshotStruct>>((resolve) => resolve(sMapsData));
216    this.sMapsFolder.addChildTraceRow(sMapsTraceRow);
217  };
218
219  private initShmRows = async (): Promise<void> => {
220    let shmTraceRow = this.initTraceRow('SHM', TraceRow.ROW_TYPE_VMTRACKER_SHM, 'VmTrackerRow');
221    let shmData = await queryVmTrackerShmData(this.memoryConfig.iPid);
222    for (let i = 0; i < shmData.length; i++) {
223      shmData[i].name = `Snapshot${i}`;
224    }
225    shmTraceRow.supplier = (): Promise<Array<SnapshotStruct>> =>
226      new Promise<Array<SnapshotStruct>>((resolve) => resolve(shmData));
227    if (shmData.length > 0) {
228      this.rowFolder.addChildTraceRow(shmTraceRow);
229    }
230  };
231
232  private async initPurgeableTotal(): Promise<void> {
233    let purgeableTotalData = await queryPurgeableProcessData(this.memoryConfig.iPid);
234    if (purgeableTotalData.length > 0) {
235      for (let i = 0; i < purgeableTotalData.length; i++) {
236        purgeableTotalData[i].name = `Snapshot${i}`;
237      }
238      let totalTraceRow = this.initTraceRow('Purgeable Total', TraceRow.ROW_TYPE_PURGEABLE_TOTAL_VM, 'VmTrackerRow');
239      totalTraceRow.supplier = (): Promise<Array<SnapshotStruct>> =>
240        new Promise<Array<SnapshotStruct>>((resolve) => resolve(purgeableTotalData));
241      this.rowFolder.addChildTraceRow(totalTraceRow);
242    }
243  }
244
245  private async initPurgeablePin(): Promise<void> {
246    let purgeablePinData = await queryPurgeableProcessData(this.memoryConfig.iPid, true);
247    if (purgeablePinData.length > 0) {
248      for (let i = 0; i < purgeablePinData.length; i++) {
249        purgeablePinData[i].name = `Snapshot${i}`;
250      }
251      let pinTraceRow = this.initTraceRow('Purgeable Pin', TraceRow.ROW_TYPE_PURGEABLE_PIN_VM, 'VmTrackerRow');
252      pinTraceRow.supplier = (): Promise<Array<SnapshotStruct>> =>
253        new Promise<Array<SnapshotStruct>>((resolve) => resolve(purgeablePinData));
254      this.rowFolder.addChildTraceRow(pinTraceRow);
255    }
256  }
257
258  private initPurgeableVM = async (): Promise<void> => {
259    let time = new Date().getTime();
260    await this.initPurgeableTotal();
261    await this.initPurgeablePin();
262    let durTime = new Date().getTime() - time;
263    info('The time to load the VM Purgeable is: ', durTime);
264  };
265
266  private initDmaRow = async (): Promise<void> => {
267    let dmaData = await queryDmaSampsData(this.memoryConfig.iPid);
268    if (dmaData.length > 0) {
269      for (let i = 0; i < dmaData.length; i++) {
270        dmaData[i].name = `Snapshot${i}`;
271      }
272      let dmaTraceRow = this.initTraceRow('DMA', TraceRow.ROW_TYPE_DMA_VMTRACKER, 'VmTrackerRow');
273      dmaTraceRow.supplier = (): Promise<Array<SnapshotStruct>> =>
274        new Promise<Array<SnapshotStruct>>((resolve) => resolve(dmaData));
275      this.rowFolder.addChildTraceRow(dmaTraceRow);
276    }
277  };
278
279  private initGpuMemoryRow = async (gpuMemoryData: Array<SnapshotStruct>): Promise<void> => {
280    for (let i = 0; i < gpuMemoryData.length; i++) {
281      gpuMemoryData[i].name = `Snapshot${i}`;
282    }
283    let gpuMemoryTraceRow = this.initTraceRow(
284      'Skia Gpu Memory',
285      TraceRow.ROW_TYPE_GPU_MEMORY_VMTRACKER,
286      'skiaGpuTraceRow'
287    );
288    gpuMemoryTraceRow.rowHidden = !this.gpuFolder.expansion;
289    gpuMemoryTraceRow.folderTextLeft = 40;
290    gpuMemoryTraceRow.supplier = (): Promise<Array<SnapshotStruct>> =>
291      new Promise<Array<SnapshotStruct>>((resolve) => resolve(gpuMemoryData));
292    this.gpuFolder.addChildTraceRow(gpuMemoryTraceRow);
293  };
294
295  private async addGpuGLRow(glArr: Array<SnapshotStruct>): Promise<void> {
296    if (glArr.length > 0) {
297      let glRow = this.initTraceRow('GL', TraceRow.ROW_TYPE_SYS_MEMORY_GPU_GL, this.gpuFolder.rowId!);
298      glRow.addTemplateTypes('sys-memory');
299      glRow.folderTextLeft = 40;
300      glRow.supplier = () => new Promise((resolve) => resolve(glArr));
301      this.gpuFolder.addChildTraceRow(glRow);
302    }
303  }
304
305  private async addGpuTotalRow(): Promise<void> {
306    let types = await queryGpuTotalType();
307    let gpuTotalRow = this.initTraceRow(
308      'Skia Gpu Dump Total',
309      TraceRow.ROW_TYPE_SYS_MEMORY_GPU_TOTAL,
310      this.gpuFolder.rowId!
311    );
312    gpuTotalRow.folderTextLeft = 40;
313    gpuTotalRow.addTemplateTypes('sys-memory');
314    gpuTotalRow.rowSetting = 'enable';
315    gpuTotalRow.rowSettingList = [
316      {
317        key: 'total',
318        title: 'Total',
319        checked: true,
320      },
321      ...types.map((it) => {
322        return {
323          key: `${it.id}`,
324          title: it.data,
325        };
326      }),
327    ];
328    gpuTotalRow.onRowSettingChangeHandler = (setting) => {
329      if (setting && setting.length > 0) {
330        gpuTotalRow.dataListCache = [];
331        gpuTotalRow.dataList = [];
332        gpuTotalRow.isComplete = false;
333        VmTrackerChart.gpuTotalModule = setting[0] === 'total' ? null : parseInt(setting[0]);
334        this.trace.refreshCanvas(false);
335      }
336    };
337    gpuTotalRow.supplier = (): Promise<Array<SnapshotStruct>> => {
338      return queryGpuTotalData(VmTrackerChart.gpuTotalModule).then((res) => {
339        res.forEach((it, index) => {
340          (it as any).name = `SnapShot ${index}`;
341        });
342        return res as SnapshotStruct[];
343      });
344    };
345    this.gpuFolder.addChildTraceRow(gpuTotalRow);
346  }
347
348  private async addGpuWindowRow(): Promise<void> {
349    let types = await queryGpuWindowType();
350    let settings: TreeItemData[] = types
351      .filter((it) => it.pid === null)
352      .map((it) => {
353        return {
354          key: `${it.id}`,
355          title: it.data,
356          children: [],
357        };
358      });
359    settings.forEach((it) => {
360      it.children = types
361        .filter((child) => `${child.pid}` === it.key)
362        .map((item) => {
363          return {
364            key: `${it.key}-${item.id}`,
365            title: item.data,
366          };
367        });
368    });
369    settings[0].checked = true;
370    VmTrackerChart.gpuWindow = parseInt(settings[0].key);
371    VmTrackerChart.gpuWindowModule = null;
372    let gpuWindowRow = this.initTraceRow(
373      'Skia Gpu Dump Window',
374      TraceRow.ROW_TYPE_SYS_MEMORY_GPU_WINDOW,
375      this.gpuFolder.rowId!
376    );
377    gpuWindowRow.folderTextLeft = 40;
378    gpuWindowRow.rowSetting = 'enable';
379    gpuWindowRow.rowSettingList = settings;
380    gpuWindowRow.addTemplateTypes('sys-memory');
381    gpuWindowRow.onRowSettingChangeHandler = (setting) => {
382      if (setting && setting.length > 0) {
383        let split = setting[0].split('-');
384        VmTrackerChart.gpuWindow = parseInt(split[0]);
385        VmTrackerChart.gpuWindowModule = split.length > 1 ? parseInt(split[1]) : null;
386        gpuWindowRow.dataListCache = [];
387        gpuWindowRow.dataList = [];
388        gpuWindowRow.isComplete = false;
389        this.trace.refreshCanvas(false);
390      }
391    };
392    gpuWindowRow.supplier = () => {
393      return queryGpuWindowData(VmTrackerChart.gpuWindow!, VmTrackerChart.gpuWindowModule).then((res) => {
394        res.forEach((window, index) => {
395          (window as any).name = `SnapShot ${index}`;
396        });
397        return res as SnapshotStruct[];
398      });
399    };
400    this.gpuFolder.addChildTraceRow(gpuWindowRow);
401  }
402
403  private initTraceRow(rowName: string, type: string, rowParentId: string): TraceRow<SnapshotStruct> {
404    let vmTrackerTraceRow = TraceRow.skeleton<SnapshotStruct>();
405    vmTrackerTraceRow.rowParentId = rowParentId;
406    vmTrackerTraceRow.rowId = rowName;
407    vmTrackerTraceRow.rowType = type;
408    vmTrackerTraceRow.folderTextLeft = 20;
409    vmTrackerTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
410    vmTrackerTraceRow.selectChangeHandler = this.trace.selectChangeHandler;
411    vmTrackerTraceRow.style.height = '40px';
412    vmTrackerTraceRow.style.width = '100%';
413    vmTrackerTraceRow.setAttribute('children', '');
414    vmTrackerTraceRow.name = rowName;
415    vmTrackerTraceRow.focusHandler = (): void => {
416      this.showTip(vmTrackerTraceRow);
417    };
418    vmTrackerTraceRow.onThreadHandler = (useCache): void => {
419      let context = vmTrackerTraceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
420      vmTrackerTraceRow.canvasSave(context);
421      (renders.snapshot as SnapshotRender).renderMainThread(
422        {
423          context: context,
424          useCache: useCache,
425          type: 'snapshot',
426        },
427        vmTrackerTraceRow
428      );
429      vmTrackerTraceRow.canvasRestore(context);
430    };
431    return vmTrackerTraceRow;
432  }
433
434  private showTip(traceRow: TraceRow<SnapshotStruct>): void {
435    this.trace?.displayTip(
436      traceRow,
437      SnapshotStruct.hoverSnapshotStruct,
438      `<span>Name: ${SnapshotStruct.hoverSnapshotStruct?.name || ''}</span>
439      <span>Size: ${Utils.getBinaryByteWithUnit(SnapshotStruct.hoverSnapshotStruct?.value || 0)}</span>`
440    );
441  }
442}
443