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 { Rect, Render, isFrameContainPoint, ns2x, drawLoadingFrame } from './ProcedureWorkerCommon';
17import { TraceRow } from '../../component/trace/base/TraceRow';
18import { HeapStruct as BaseHeapStruct } from '../../bean/HeapStruct';
19import { SpSystemTrace } from '../../component/SpSystemTrace';
20export class NativeMemoryRender {
21  renderMainThread(req: HeapStruct, row: TraceRow<HeapStruct>): void {}
22}
23export class HeapRender {
24  renderMainThread(
25    req: {
26      context: CanvasRenderingContext2D;
27      useCache: boolean;
28      type: string;
29    },
30    row: TraceRow<HeapStruct>
31  ): void {
32    let heapList = row.dataList;
33    let heapFilter = row.dataListCache;
34    heap(
35      heapList,
36      heapFilter,
37      TraceRow.range?.startNS ?? 0,
38      TraceRow.range?.endNS ?? 0,
39      TraceRow.range?.totalNS ?? 0,
40      row.frame,
41      req.useCache || (TraceRow.range?.refresh ?? false)
42    );
43    drawLoadingFrame(req.context, heapFilter, row);
44    setRenderHeapFrame(heapFilter, row);
45    drawHeap(req, heapFilter, row);
46  }
47}
48
49function setRenderHeapFrame(heapFilter: HeapStruct[], row: TraceRow<HeapStruct>): void {
50  // 多条数据,最后一条数据在结束点也需要绘制
51  if (heapFilter.length >= 2 && heapFilter[heapFilter.length - 1].dur === 0) {
52    if (heapFilter[heapFilter.length - 2].frame && heapFilter[heapFilter.length - 1].frame) {
53      heapFilter[heapFilter.length - 2].frame!.width = heapFilter[heapFilter.length - 2].frame!.width - 1;
54      heapFilter[heapFilter.length - 1].frame!.width = 1;
55      heapFilter[heapFilter.length - 1].frame!.x -= 1;
56    }
57  }
58  // 只有一条数据并且数据在结束点
59  // @ts-ignore
60  if (heapFilter.length === 1 && row.frame.width === heapFilter[0].frame?.x) {
61    heapFilter[0].frame!.x -= 1;
62  }
63}
64
65function drawHeap(
66  req: {
67    context: CanvasRenderingContext2D;
68    useCache: boolean;
69    type: string;
70  },
71  heapFilter: HeapStruct[],
72  row: TraceRow<HeapStruct>
73): void {
74  req.context.beginPath();
75  let find = false;
76  for (let re of heapFilter) {
77    if (row.isHover && re.frame && !find && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) {
78      HeapStruct.hoverHeapStruct = re;
79      find = true;
80    }
81  }
82  for (let re of heapFilter) {
83    HeapStruct.drawHeap(req.context, re, row.drawType);
84  }
85  if (!find && row.isHover) {
86    HeapStruct.hoverHeapStruct = undefined;
87  }
88  req.context.closePath();
89}
90
91export function heap(
92  heapList: Array<HeapStruct>,
93  res: Array<HeapStruct>,
94  startNS: number,
95  endNS: number,
96  totalNS: number,
97  frame: Rect,
98  use: boolean
99): void {
100  if (use && res.length > 0) {
101    setHeapFrameIfUse(res, startNS, endNS, totalNS, frame);
102    return;
103  }
104  res.length = 0;
105  for (let i = 0, len = heapList.length; i < len; i++) {
106    let it = heapList[i];
107    if ((it.startTime || 0) + (it.dur || 0) > startNS && (it.startTime || 0) <= endNS) {
108      HeapStruct.setFrame(it, 5, startNS, endNS, totalNS, frame);
109      if (i > 0) {
110        let last = heapList[i - 1];
111        if (last.frame?.x !== it.frame!.x || last.frame.width !== it.frame!.width) {
112          res.push(it);
113        }
114      } else {
115        res.push(it);
116      }
117    }
118  }
119}
120
121function setHeapFrameIfUse(res: Array<HeapStruct>, startNS: number, endNS: number, totalNS: number, frame: Rect): void {
122  for (let i = 0; i < res.length; i++) {
123    let it = res[i];
124    if ((it.startTime || 0) + (it.dur || 0) > startNS && (it.startTime || 0) <= endNS) {
125      HeapStruct.setFrame(res[i], 5, startNS, endNS, totalNS, frame);
126    } else {
127      res[i].frame = undefined;
128    }
129  }
130}
131
132export function HeapStructOnClick(
133  clickRowType: string,
134  sp: SpSystemTrace,
135  row: undefined | TraceRow<HeapStruct>,
136  entry?: HeapStruct,
137): Promise<unknown> {
138  return new Promise((resolve, reject) => {
139    if (
140      clickRowType === TraceRow.ROW_TYPE_HEAP &&
141      row &&
142      row.getAttribute('heap-type') === 'native_hook_statistic' &&
143      (HeapStruct.hoverHeapStruct || entry)
144    ) {
145      HeapStruct.selectHeapStruct = entry || HeapStruct.hoverHeapStruct;
146      const key = row.rowParentId!.split(' ');
147      let ipid = 1;
148      if (key.length > 0) {
149        ipid = Number(key[key.length - 1]);
150      }
151      sp.traceSheetEL?.displayNativeHookData(HeapStruct.selectHeapStruct!, row.rowId!, ipid);
152      sp.timerShaftEL?.modifyFlagList(undefined);
153      reject(new Error());
154    } else {
155      resolve(null);
156    }
157  });
158}
159export class HeapStruct extends BaseHeapStruct {
160  static hoverHeapStruct: HeapStruct | undefined;
161  static selectHeapStruct: HeapStruct | undefined;
162  maxDensity: number = 0;
163  minDensity: number = 0;
164
165  static setFrame(
166    node: HeapStruct,
167    padding: number,
168    startNS: number,
169    endNS: number,
170    totalNS: number,
171    frame: Rect
172  ): void {
173    let x1: number;
174    let x2: number;
175    if ((node.startTime || 0) < startNS) {
176      x1 = 0;
177    } else {
178      x1 = ns2x(node.startTime || 0, startNS, endNS, totalNS, frame);
179    }
180    if ((node.startTime || 0) + (node.dur || 0) > endNS) {
181      x2 = frame.width;
182    } else {
183      x2 = ns2x(
184        // @ts-ignore
185        node.startTime + node.dur,
186        startNS,
187        endNS,
188        totalNS,
189        frame
190      );
191    }
192    let getV: number = x2 - x1 <= 1 ? 1 : x2 - x1;
193    let rectangle: Rect = new Rect(
194      Math.floor(x1),
195      Math.ceil(frame.y + padding),
196      Math.ceil(getV),
197      Math.floor(frame.height - padding * 2)
198    );
199    node.frame = rectangle;
200  }
201
202  static drawHeap(heapContext: CanvasRenderingContext2D, data: HeapStruct, drawType: number): void {
203    if (data.frame) {
204      let width = data.frame.width || 0;
205      heapContext.fillStyle = '#2db3aa';
206      heapContext.strokeStyle = '#2db3aa';
207      let drawHeight: number = 0;
208      if (drawType === 0) {
209        if (data.minHeapSize < 0) {
210          drawHeight = Math.ceil(
211            (((data.heapsize || 0) - data.minHeapSize) * (data.frame.height || 0)) /
212              (data.maxHeapSize - data.minHeapSize)
213          );
214        } else {
215          drawHeight = Math.ceil(((data.heapsize || 0) * (data.frame.height || 0)) / data.maxHeapSize);
216        }
217      } else {
218        if (data.minDensity < 0) {
219          drawHeight = Math.ceil(
220            (((data.density || 0) - data.minDensity) * (data.frame.height || 0)) / (data.maxDensity - data.minDensity)
221          );
222        } else {
223          drawHeight = Math.ceil(((data.density || 0) * (data.frame.height || 0)) / data.maxDensity);
224        }
225      }
226      if (data === HeapStruct.hoverHeapStruct || data === HeapStruct.selectHeapStruct) {
227        heapContext.lineWidth = 1;
228        heapContext.globalAlpha = 0.6;
229        heapContext.fillRect(data.frame.x, data.frame.y + data.frame.height - drawHeight, width, drawHeight);
230        heapContext.beginPath();
231        heapContext.arc(data.frame.x, data.frame.y + data.frame.height - drawHeight, 3, 0, 2 * Math.PI, true);
232        heapContext.fill();
233        heapContext.globalAlpha = 1.0;
234        heapContext.stroke();
235        heapContext.beginPath();
236        heapContext.moveTo(data.frame.x + 3, data.frame.y + data.frame.height - drawHeight);
237        heapContext.lineWidth = 3;
238        heapContext.lineTo(data.frame.x + width, data.frame.y + data.frame.height - drawHeight);
239        heapContext.stroke();
240      } else {
241        heapContext.globalAlpha = 0.6;
242        heapContext.lineWidth = 1;
243        heapContext.fillRect(data.frame.x, data.frame.y + data.frame.height - drawHeight, width, drawHeight);
244      }
245    }
246    heapContext.globalAlpha = 1.0;
247    heapContext.lineWidth = 1;
248  }
249}
250