• 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 { ColorUtils } from '../../../component/trace/base/ColorUtils';
17import {
18  BaseStruct,
19  Rect,
20  ns2x,
21  drawString,
22  Render,
23  isFrameContainPoint,
24  drawLoadingFrame,
25} from '../ProcedureWorkerCommon';
26import { TraceRow } from '../../../component/trace/base/TraceRow';
27import { HiPerfChartFrame } from '../../../bean/PerfStruct';
28
29export class HiPerfCallChartRender extends Render {
30  renderMainThread(req: unknown, row: TraceRow<HiPerfCallChartStruct>): void {
31    //@ts-ignore
32    const ctx = req.context as CanvasRenderingContext2D;
33    let list = row.dataList;
34    let filter = row.dataListCache;
35    hiperf(
36      list,
37      filter,
38      TraceRow.range!.startNS,
39      TraceRow.range!.endNS,
40      TraceRow.range!.totalNS,
41      //@ts-ignore
42      row.frame,
43      //@ts-ignore
44      req.useCache || !TraceRow.range!.refresh,
45      row.funcExpand
46    );
47    drawLoadingFrame(ctx, filter, row);
48    ctx.beginPath();
49    let find = false;
50    let offset = 5;
51    for (let re of filter) {
52      HiPerfCallChartStruct.draw(ctx, re);
53      if (row.isHover) {
54        if (
55          re.endTime - re.startTime === 0 ||
56          re.endTime - re.startTime == null ||
57          re.endTime - re.startTime === undefined
58        ) {
59          if (
60            re.frame &&
61            row.hoverX >= re.frame.x - offset &&
62            row.hoverX <= re.frame.x + re.frame.width + offset &&
63            row.hoverY >= re.frame.y &&
64            row.hoverY <= re.frame.y + re.frame.height
65          ) {
66            HiPerfCallChartStruct.hoverPerfCallCutStruct = re;
67            find = true;
68          }
69        } else {
70          if (re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) {
71            HiPerfCallChartStruct.hoverPerfCallCutStruct = re;
72            find = true;
73          }
74        }
75      }
76      if (!find && row.isHover) {
77        HiPerfCallChartStruct.hoverPerfCallCutStruct = undefined;
78      }
79    }
80    ctx.closePath();
81  }
82}
83
84// 火焰图数据模板
85let padding = 1;
86export class HiPerfCallChartStruct extends BaseStruct {
87  static selectStruct: HiPerfCallChartStruct | undefined;
88  static hoverPerfCallCutStruct: HiPerfCallChartStruct | undefined;
89  id: number = 0;
90  name: string = '';
91  startTime: number = 0;
92  endTime: number = 0;
93  eventCount: number = 0;
94  depth: number = 0;
95  fileId: number = 0;
96  symbolId: number = 0;
97  children!: Array<HiPerfChartFrame>;
98  isSelect: boolean = false;
99  totalTime: number = 0;
100  callchain_id: number = 0;
101  selfDur: number = 0;
102
103  static setPerfFrame(
104    hiPerfNode: HiPerfCallChartStruct,
105    startNS: number,
106    endNS: number,
107    totalNS: number,
108    frame: Rect
109  ): void {
110    let x1: number, x2: number;
111    if ((hiPerfNode.startTime || 0) > startNS && (hiPerfNode.startTime || 0) < endNS) {
112      x1 = ns2x(hiPerfNode.startTime || 0, startNS, endNS, totalNS, frame);
113    } else {
114      x1 = 0;
115    }
116    if (
117      (hiPerfNode.startTime || 0) + (hiPerfNode.totalTime || 0) > startNS &&
118      (hiPerfNode.startTime || 0) + (hiPerfNode.totalTime || 0) < endNS
119    ) {
120      x2 = ns2x((hiPerfNode.startTime || 0) + (hiPerfNode.totalTime || 0), startNS, endNS, totalNS, frame);
121    } else {
122      x2 = frame.width;
123    }
124    if (!hiPerfNode.frame) {
125      hiPerfNode.frame = new Rect(0, 0, 0, 0);
126    }
127    let getV: number = x2 - x1 < 1 ? 1 : x2 - x1;
128    hiPerfNode.frame.x = Math.floor(x1);
129    hiPerfNode.frame.y = hiPerfNode.depth * 20;
130    hiPerfNode.frame.width = Math.ceil(getV);
131    hiPerfNode.frame.height = 20;
132  }
133
134  static draw(ctx: CanvasRenderingContext2D, data: HiPerfCallChartStruct): void {
135    if (data.frame) {
136      if (data.endTime - data.startTime === undefined || data.endTime - data.startTime === null) {
137      } else {
138        ctx.globalAlpha = 1;
139        if (data.name === '(program)') {
140          ctx.fillStyle = '#ccc';
141        } else if (data.name === '(idle)') {
142          ctx.fillStyle = '#f0f0f0';
143        } else {
144          ctx.fillStyle = ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.name || '', 0, ColorUtils.FUNC_COLOR.length)];
145        }
146        let miniHeight = 20;
147        if (HiPerfCallChartStruct.hoverPerfCallCutStruct && data === HiPerfCallChartStruct.hoverPerfCallCutStruct) {
148          ctx.globalAlpha = 0.7;
149        }
150        ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2);
151        if (data.frame.width > 8) {
152          ctx.lineWidth = 1;
153          ctx.fillStyle = '#fff';
154          ctx.textBaseline = 'middle';
155          drawString(ctx, `${data.name || ''}`, 4, data.frame, data);
156        }
157        if (data === HiPerfCallChartStruct.selectStruct) {
158          ctx.strokeStyle = '#000';
159          ctx.lineWidth = 2;
160          ctx.strokeRect(data.frame.x + 1, data.frame.y + 1, data.frame.width - 2, miniHeight - padding * 2 - 2);
161        }
162      }
163    }
164  }
165}
166
167export function hiperf(
168  list: Array<HiPerfCallChartStruct>,
169  filter: Array<HiPerfCallChartStruct>,
170  startNS: number,
171  endNS: number,
172  totalNS: number,
173  frame: Rect,
174  use: boolean,
175  expand: boolean
176): void {
177  if (use && filter.length > 0) {
178    for (let i = 0, len = filter.length; i < len; i++) {
179      if (
180        filter[i].totalTime > 0 &&
181        (filter[i].startTime || 0) + (filter[i].totalTime || 0) >= startNS &&
182        (filter[i].startTime || 0) <= endNS
183      ) {
184        HiPerfCallChartStruct.setPerfFrame(filter[i], startNS, endNS, totalNS, frame);
185      } else {
186        filter[i].frame = undefined;
187      }
188    }
189    return;
190  }
191  filter.length = 0;
192  if (list) {
193    let groups = list
194      .filter(
195        (it) =>
196          it.totalTime > 0 &&
197          (it.startTime ?? 0) + (it.totalTime ?? 0) >= startNS &&
198          (it.startTime ?? 0) <= endNS &&
199          ((!expand && it.depth === 0) || expand)
200      )
201      .map((it) => {
202        HiPerfCallChartStruct.setPerfFrame(it, startNS, endNS, totalNS, frame);
203        return it;
204      })
205      .reduce((pre, current) => {
206        //@ts-ignore
207        (pre[`${current.frame.x}-${current.depth}`] = pre[`${current.frame.x}-${current.depth}`] || []).push(current);
208        return pre;
209      }, {});
210    Reflect.ownKeys(groups).map((kv) => {
211      // 从小到大排序
212      //@ts-ignore
213      let arr = groups[kv].sort((a: HiPerfChartFrame, b: HiPerfChartFrame) => b.totalTime - a.totalTime);
214      filter.push(arr[0]);
215    });
216  }
217}
218