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';
19
20import { SpSystemTrace } from '../../component/SpSystemTrace';
21export class HeapSnapshotRender extends Render {
22  renderMainThread(
23    req: {
24      context: CanvasRenderingContext2D;
25      useCache: boolean;
26      type: string;
27    },
28    row: TraceRow<HeapSnapshotStruct>
29  ): void {
30    let filter = row.dataListCache;
31    HeapSnapshot(
32      filter,
33      TraceRow.range?.startNS ?? 0,
34      TraceRow.range?.endNS ?? 0,
35      (TraceRow.range?.endNS ?? 0) - (TraceRow.range?.startNS! ?? 0), // @ts-ignore
36      row.frame
37    );
38    drawLoadingFrame(req.context, filter, row);
39    req.context!.beginPath();
40    for (let re of filter) {
41      HeapSnapshotStruct.draw(req.context, re);
42    }
43    for (let re of filter) {
44      if (re.frame && !isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) {
45        HeapSnapshotStruct.hoverSnapshotStruct = undefined;
46      }
47      if (re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) {
48        HeapSnapshotStruct.hoverSnapshotStruct = re;
49        break;
50      }
51    }
52    req.context!.closePath();
53  }
54}
55export function HeapSnapshot(
56  list: Array<HeapSnapshotStruct>,
57  startNS: number,
58  endNS: number,
59  totalNS: number,
60  frame: Rect
61): void {
62  for (let file of list) {
63    HeapSnapshotStruct.setFrame(file, startNS || 0, endNS || 0, totalNS || 0, frame);
64  }
65}
66const padding = 3;
67export function HeapSnapshotStructOnClick(
68  clickRowType: string,
69  sp: SpSystemTrace,
70  row: TraceRow<HeapSnapshotStruct>,
71  snapshotClickHandler: unknown,
72  entry?: HeapSnapshotStruct
73): Promise<unknown> {
74  return new Promise((resolve, reject) => {
75    if (clickRowType === TraceRow.ROW_TYPE_HEAP_SNAPSHOT) {
76      if (row.findHoverStruct) {
77        row.findHoverStruct();
78      } else {
79        HeapSnapshotStruct.hoverSnapshotStruct = HeapSnapshotStruct.hoverSnapshotStruct || row.getHoverStruct();
80      }
81      if (HeapSnapshotStruct.hoverSnapshotStruct || entry) {
82        HeapSnapshotStruct.selectSnapshotStruct = entry || HeapSnapshotStruct.hoverSnapshotStruct;
83        sp.traceSheetEL?.displaySnapshotData(
84          HeapSnapshotStruct.selectSnapshotStruct!,
85          row!.dataListCache,
86          //@ts-ignore
87          snapshotClickHandler
88        );
89      }
90      reject(new Error());
91    } else {
92      resolve(null);
93    }
94  });
95}
96export class HeapSnapshotStruct extends BaseStruct {
97  startTs: number = 0;
98  endTs: number = 0;
99  id: number = 0;
100  pid: number = 0;
101  name: string | undefined;
102  textWidth: number | undefined;
103  size: number = 0;
104  static hoverSnapshotStruct: HeapSnapshotStruct | undefined;
105  static selectSnapshotStruct: HeapSnapshotStruct | undefined;
106
107  static setFrame(node: HeapSnapshotStruct, startNS: number, endNS: number, totalNS: number, frame: Rect): void {
108    node.frame = undefined;
109    if ((node.startTs - startNS || node.startTs - startNS === 0) && node.endTs - node.startTs) {
110      let rectangle: Rect = new Rect(
111        Math.floor(((node.startTs - startNS) / totalNS) * frame.width),
112        0,
113        Math.ceil(((node.endTs - node.startTs) / totalNS) * frame.width),
114        40
115      );
116      node.frame = rectangle;
117    }
118  }
119
120  static draw(ctx: CanvasRenderingContext2D, data: HeapSnapshotStruct): void {
121    if (data.frame) {
122      ctx.fillStyle = 'rgb(86,192,197)';
123      ctx!.fillRect(data.frame!.x, data.frame!.y + padding, data.frame!.width, data.frame!.height - padding * 2);
124      if (data.frame!.width > 7) {
125        ctx.globalAlpha = 1.0;
126        ctx.lineWidth = 1;
127        ctx.fillStyle = '#fff';
128        ctx.textBaseline = 'middle';
129        ctx.font = '12px sans-serif';
130        HeapSnapshotStruct.drawString(ctx, data.name || '', 3, data.frame!, data, 4);
131        HeapSnapshotStruct.drawString(ctx, Utils.getBinaryByteWithUnit(data.size) || '', 9, data.frame!, data, 2);
132      }
133      if (
134        HeapSnapshotStruct.selectSnapshotStruct &&
135        HeapSnapshotStruct.equals(HeapSnapshotStruct.selectSnapshotStruct, data)
136      ) {
137        ctx.strokeStyle = '#232c5d';
138        ctx.lineWidth = 2;
139        ctx.strokeRect(data.frame!.x, data.frame!.y + padding, data.frame!.width - 2, data.frame!.height - padding * 2);
140      }
141    }
142  }
143
144  /**
145   *
146   * @param ctx current context
147   * @param str text
148   * @param textPadding padding
149   * @param frame rectangle
150   * @param data HeapSnapshotStruct
151   * @param location the position of the string, the bigger the numerical value, the higher the position on the canvas
152   */
153  static drawString(
154    ctx: CanvasRenderingContext2D,
155    str: string,
156    textPadding: number,
157    frame: Rect,
158    HeapSnapshotdata: HeapSnapshotStruct,
159    location: number
160  ): void {
161    if (HeapSnapshotdata.textWidth === undefined) {
162      HeapSnapshotdata.textWidth = ctx.measureText(str).width;
163    }
164    let textWidth = Math.round(HeapSnapshotdata.textWidth / str.length);
165    let maxTextWidth = frame.width - textPadding * 2;
166    if (HeapSnapshotdata.textWidth < maxTextWidth) {
167      let x = Math.floor(frame.width / 2 - HeapSnapshotdata.textWidth / 2 + frame.x + textPadding);
168      ctx.fillText(str, x, Math.floor(frame.y + frame.height / location + textPadding), maxTextWidth);
169    } else {
170      if (maxTextWidth >= textWidth) {
171        let characterNum = maxTextWidth / textWidth;
172        let x = frame.x + textPadding;
173        if (characterNum < 2) {
174          ctx.fillText(
175            str.substring(0, 1),
176            x,
177            Math.floor(frame.y + frame.height / location + textPadding),
178            maxTextWidth
179          );
180        } else {
181          ctx.fillText(
182            str.substring(0, characterNum - 1) + '...',
183            x,
184            Math.floor(frame.y + frame.height / location + textPadding),
185            maxTextWidth
186          );
187        }
188      }
189    }
190  }
191
192  static equals(d1: HeapSnapshotStruct, d2: HeapSnapshotStruct): boolean {
193    return (
194      d1 &&
195      d2 &&
196      d1.name === d2.name &&
197      d1.id === d2.id &&
198      d1.pid === d2.pid &&
199      d1.startTs === d2.startTs &&
200      d1.endTs === d2.endTs
201    );
202  }
203}
204