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