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 { BaseStruct, Rect, Render, drawLoadingFrame, isFrameContainPoint } from './ProcedureWorkerCommon';
17import { TraceRow } from '../../component/trace/base/TraceRow';
18import { Utils } from '../../component/trace/base/Utils';
19import { MemoryConfig } from '../../bean/MemoryConfig';
20import { SpSystemTrace } from '../../component/SpSystemTrace';
21
22export class SnapshotRender extends Render {
23  renderMainThread(
24    req: {
25      context: CanvasRenderingContext2D;
26      useCache: boolean;
27      type: string;
28    },
29    row: TraceRow<SnapshotStruct>
30  ): void {
31    let filter = row.dataListCache;
32    let maxValue = 0;
33    for (let item of filter) {
34      maxValue = Math.max(maxValue, item.value || 0);
35    }
36    snapshot(
37      filter,
38      maxValue,
39      TraceRow.range?.startNS ?? 0,
40      (TraceRow.range?.endNS ?? 0) - (TraceRow.range?.startNS! ?? 0), // @ts-ignore
41      row.frame
42    );
43    drawLoadingFrame(req.context, row.dataListCache, row);
44    req.context!.beginPath();
45    let find = false;
46    for (let re of filter) {
47      SnapshotStruct.draw(req.context, re);
48      if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) {
49        SnapshotStruct.hoverSnapshotStruct = re;
50        find = true;
51      }
52    }
53    if (!find && row.isHover) {
54      SnapshotStruct.hoverSnapshotStruct = undefined;
55    }
56    req.context!.closePath();
57  }
58}
59export function snapshot(
60  filter: Array<SnapshotStruct>,
61  maxValue: number,
62  startNs: number,
63  totalNs: number,
64  frame: Rect
65): void {
66  for (let file of filter) {
67    SnapshotStruct.setFrame(file, maxValue, startNs || 0, totalNs || 0, frame);
68  }
69}
70const padding = 2;
71
72const snapshotTypeHandlerMap = new Map<string, (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown) => void>(
73  [
74    [
75      TraceRow.ROW_TYPE_SYS_MEMORY_GPU_TOTAL,
76
77      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void =>
78        // @ts-ignore
79        displayGpuDumpTotalSheet(sp, row, reject),
80    ],
81    [
82      TraceRow.ROW_TYPE_SYS_MEMORY_GPU_WINDOW,
83      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void =>
84        // @ts-ignore
85        displayGpuDumpWindowSheet(sp, row, reject),
86    ],
87    [
88      TraceRow.ROW_TYPE_VM_TRACKER_SMAPS,
89      // @ts-ignore
90      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void => displaySmapsSheet(sp, row, reject),
91    ],
92    [
93      TraceRow.ROW_TYPE_VMTRACKER_SHM,
94      // @ts-ignore
95      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void => displayShmSheet(sp, row, reject),
96    ],
97    [
98      TraceRow.ROW_TYPE_PURGEABLE_TOTAL_ABILITY,
99      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void =>
100        // @ts-ignore
101        displayTotalAbilitySheet(sp, row, reject),
102    ],
103    [
104      TraceRow.ROW_TYPE_PURGEABLE_PIN_ABILITY,
105      // @ts-ignore
106      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void => displayPinAbilitySheet(sp, row, reject),
107    ],
108    [
109      TraceRow.ROW_TYPE_PURGEABLE_TOTAL_VM,
110      // @ts-ignore
111      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void => displayTotalVMSheet(sp, row, reject),
112    ],
113    [
114      TraceRow.ROW_TYPE_PURGEABLE_PIN_VM,
115      // @ts-ignore
116      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void => displayPinVMSheet(sp, row, reject),
117    ],
118    [
119      TraceRow.ROW_TYPE_DMA_ABILITY,
120      // @ts-ignore
121      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void => displayDmaAbilitySheet(sp, row, reject),
122    ],
123    [
124      TraceRow.ROW_TYPE_DMA_VMTRACKER,
125      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void =>
126        // @ts-ignore
127        displayDmaVmTrackerSheet(sp, row, reject),
128    ],
129    [
130      TraceRow.ROW_TYPE_GPU_MEMORY_ABILITY,
131      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void =>
132        // @ts-ignore
133        displayGpuMemoryAbilitySheet(sp, row, reject),
134    ],
135    [
136      TraceRow.ROW_TYPE_GPU_MEMORY_VMTRACKER,
137      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void =>
138        // @ts-ignore
139        displayGpuMemoryVmTrackerSheet(sp, row, reject),
140    ],
141    [
142      TraceRow.ROW_TYPE_GPU_RESOURCE_VMTRACKER,
143      (sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: unknown): void => displayGpuResourceSheet(sp),
144    ],
145  ]
146);
147
148export function SnapshotStructOnClick(
149  clickRowType: string,
150  sp: SpSystemTrace,
151  row: TraceRow<SnapshotStruct>,
152  entry?: SnapshotStruct,
153): Promise<unknown> {
154  return new Promise((resolve, reject) => {
155    if (snapshotTypeHandlerMap.has(clickRowType)) {
156      SnapshotStruct.selectSnapshotStruct =
157        SnapshotStruct.hoverSnapshotStruct || (row.getHoverStruct() as SnapshotStruct);
158      snapshotTypeHandlerMap.get(clickRowType)?.(sp, row, reject);
159      reject(new Error());
160    } else {
161      resolve(null);
162    }
163  });
164}
165
166function displayGpuDumpTotalSheet(
167  sp: SpSystemTrace,
168  row: TraceRow<BaseStruct>,
169  reject: (reason?: unknown) => void
170): void {
171  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
172  // @ts-ignore
173  sp.traceSheetEL?.displayGpuSelectedData('total', SnapshotStruct.selectSnapshotStruct!.startNs, row!.dataListCache);
174  sp.timerShaftEL?.modifyFlagList(undefined);
175  reject();
176}
177
178function displayGpuDumpWindowSheet(
179  sp: SpSystemTrace,
180  row: TraceRow<BaseStruct>,
181  reject: (reason?: unknown) => void
182): void {
183  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
184  // @ts-ignore
185  sp.traceSheetEL?.displayGpuSelectedData('window', SnapshotStruct.selectSnapshotStruct!.startNs, row!.dataListCache);
186  sp.timerShaftEL?.modifyFlagList(undefined);
187  reject();
188}
189
190function displaySmapsSheet(sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: (reason?: unknown) => void): void {
191  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
192  // @ts-ignore
193  sp.traceSheetEL?.displaySmapsData(SnapshotStruct.selectSnapshotStruct!, row!.dataListCache);
194  reject();
195}
196
197function displayShmSheet(sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: (reason?: unknown) => void): void {
198  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
199  // @ts-ignore
200  sp.traceSheetEL?.displayShmData(SnapshotStruct.selectSnapshotStruct!, row!.dataListCache);
201  reject();
202}
203
204function displayTotalAbilitySheet(
205  sp: SpSystemTrace,
206  row: TraceRow<BaseStruct>,
207  reject: (reason?: unknown) => void
208): void {
209  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
210  // @ts-ignore
211  sp.traceSheetEL?.displayPurgTotalAbilityData(SnapshotStruct.hoverSnapshotStruct!, row!.dataListCache);
212  reject();
213}
214
215function displayPinAbilitySheet(
216  sp: SpSystemTrace,
217  row: TraceRow<BaseStruct>,
218  reject: (reason?: unknown) => void
219): void {
220  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
221  // @ts-ignore
222  sp.traceSheetEL?.displayPurgPinAbilityData(SnapshotStruct.hoverSnapshotStruct!, row!.dataListCache);
223  reject();
224}
225
226function displayTotalVMSheet(sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: (reason?: unknown) => void): void {
227  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
228  // @ts-ignore
229  sp.traceSheetEL?.displayPurgTotalVMData(SnapshotStruct.hoverSnapshotStruct!, row!.dataListCache);
230  reject();
231}
232
233function displayPinVMSheet(sp: SpSystemTrace, row: TraceRow<BaseStruct>, reject: (reason?: unknown) => void): void {
234  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
235  // @ts-ignore
236  sp.traceSheetEL?.displayPurgPinVMData(SnapshotStruct.hoverSnapshotStruct!, row!.dataListCache);
237  reject();
238}
239
240function displayDmaAbilitySheet(
241  sp: SpSystemTrace,
242  row: TraceRow<BaseStruct>,
243  reject: (reason?: unknown) => void
244): void {
245  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
246  // @ts-ignore
247  sp.traceSheetEL?.displayDmaAbility(SnapshotStruct.selectSnapshotStruct!.startNs, row!.dataListCache);
248  reject();
249}
250
251function displayDmaVmTrackerSheet(
252  sp: SpSystemTrace,
253  row: TraceRow<BaseStruct>,
254  reject: (reason?: unknown) => void
255): void {
256  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
257  // @ts-ignore
258  sp.traceSheetEL?.displayDmaVmTracker(SnapshotStruct.selectSnapshotStruct!.startNs, row!.dataListCache);
259  reject();
260}
261
262function displayGpuMemoryAbilitySheet(
263  sp: SpSystemTrace,
264  row: TraceRow<BaseStruct>,
265  reject: (reason?: unknown) => void
266): void {
267  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
268  // @ts-ignore
269  sp.traceSheetEL?.displayGpuMemoryAbility(SnapshotStruct.selectSnapshotStruct!.startNs, row!.dataListCache);
270  reject();
271}
272
273function displayGpuMemoryVmTrackerSheet(
274  sp: SpSystemTrace,
275  row: TraceRow<BaseStruct>,
276  reject: (reason?: unknown) => void
277): void {
278  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
279  // @ts-ignore
280  sp.traceSheetEL?.displayGpuMemoryVmTracker(SnapshotStruct.selectSnapshotStruct!.startNs, row!.dataListCache);
281  reject();
282}
283
284function displayGpuResourceSheet(sp: SpSystemTrace): void {
285  SnapshotStruct.selectSnapshotStruct = SnapshotStruct.hoverSnapshotStruct;
286  sp.traceSheetEL?.displayGpuResourceVmTracker(SnapshotStruct.selectSnapshotStruct!.startNs);
287}
288
289export class SnapshotStruct extends BaseStruct {
290  startNs: number = 0;
291  endNs: number = 0;
292  dur: number = 0;
293  name: string = '';
294  aSize: number = 0;
295  categoryNameId: number = 0;
296  textWidth: number = 0;
297  value: number = 0;
298  type: string = '';
299  static hoverSnapshotStruct: SnapshotStruct | undefined;
300  static selectSnapshotStruct: SnapshotStruct | undefined;
301  static setFrame(node: SnapshotStruct, maxValue: number, startNs: number, totalNs: number, frame: Rect): void {
302    node.frame = undefined;
303    frame.height = 40 - padding * 2;
304    // sample_interval单位是ms,startNs和endNs单位是纳秒,每一次采样的时间按采样间隔的五分之一算
305    node.dur = MemoryConfig.getInstance().snapshotDur;
306    node.endNs = node.startNs + node.dur;
307    if ((node.startNs - startNs || node.startNs - startNs === 0) && node.endNs - node.startNs) {
308      let rectangle: Rect = new Rect(
309        Math.floor(((node.startNs - startNs) / totalNs) * frame.width),
310        Math.floor(((maxValue - node.value) / maxValue) * frame.height),
311        Math.ceil(((node.endNs - node.startNs) / totalNs) * frame.width),
312        Math.ceil((node.value / maxValue) * frame.height)
313      );
314      node.frame = rectangle;
315    }
316    if (node.value === 0) {
317      let rectangle: Rect = new Rect(
318        Math.floor(((node.startNs - startNs) / totalNs) * frame.width),
319        30,
320        Math.ceil((node.dur / totalNs) * frame.width),
321        1
322      );
323      node.frame = rectangle;
324    }
325  }
326  static draw(ctx: CanvasRenderingContext2D, data: SnapshotStruct): void {
327    if (data.frame) {
328      ctx.fillStyle = 'rgb(86,192,197)';
329      ctx!.fillRect(data.frame!.x, data.frame!.y + padding, data.frame!.width, data.frame!.height);
330      if (data.frame!.width > 7) {
331        ctx.globalAlpha = 1.0;
332        ctx.lineWidth = 1;
333        ctx.fillStyle = '#fff';
334        ctx.textBaseline = 'middle';
335        if (data.frame!.height > 10 && data.frame!.height < 25) {
336          SnapshotStruct.drawString(ctx, data.name || '', 4, data.frame!, data, 4);
337        } else if (data.frame!.height >= 25) {
338          SnapshotStruct.drawString(ctx, data.name || '', 4, data.frame!, data, 4);
339          SnapshotStruct.drawString(ctx, Utils.getBinaryByteWithUnit(data.value || 0), 11, data.frame!, data, 2);
340        }
341      }
342      if (SnapshotStruct.selectSnapshotStruct && SnapshotStruct.equals(SnapshotStruct.selectSnapshotStruct, data)) {
343        ctx.strokeStyle = '#232c5d';
344        ctx.lineWidth = 2;
345        ctx.strokeRect(data.frame!.x, data.frame!.y + padding, data.frame!.width - 2, data.frame!.height);
346      }
347    }
348  }
349  /**
350   *
351   * @param ctx current context
352   * @param str text
353   * @param textPadding padding
354   * @param frame rectangle
355   * @param data PurgeableStruct
356   * @param location the position of the string, the bigger the numerical value, the higher the position on the canvas
357   */
358  static drawString(
359    ctx: CanvasRenderingContext2D,
360    str: string,
361    textPadding: number,
362    frame: Rect,
363    data: SnapshotStruct,
364    location: number
365  ): void {
366    if (data.textWidth === undefined) {
367      data.textWidth = ctx.measureText(str).width;
368    }
369    let textWidth = Math.round(data.textWidth / str.length);
370    let fillTextWidth = frame.width - textPadding * 2;
371    if (data.textWidth < fillTextWidth) {
372      let x = Math.floor(frame.width / 2 - data.textWidth / 2 + frame.x + textPadding);
373      ctx.fillText(str, x, Math.floor(frame.y + frame.height / location + textPadding), fillTextWidth);
374    } else {
375      if (fillTextWidth >= textWidth) {
376        let characterNum = fillTextWidth / textWidth;
377        let x = frame.x + textPadding;
378        if (characterNum < 2) {
379          ctx.fillText(
380            str.substring(0, 1),
381            x,
382            Math.floor(frame.y + frame.height / location + textPadding),
383            fillTextWidth
384          );
385        } else {
386          ctx.fillText(
387            `${str.substring(0, characterNum - 1)}...`,
388            x,
389            Math.floor(frame.y + frame.height / location + textPadding),
390            fillTextWidth
391          );
392        }
393      }
394    }
395  }
396  static equals(baseSnapshot: SnapshotStruct, targetSnapshot: SnapshotStruct): boolean {
397    return baseSnapshot === targetSnapshot;
398  }
399}
400