• 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 { SpApplication } from '../SpApplication';
17import { Rect } from '../component/trace/timer-shaft/Rect';
18import { warn } from '../../log/Log';
19import { BaseStruct, drawString } from '../database/ui-worker/ProcedureWorkerCommon';
20
21const padding: number = 1;
22const rectHeight = 20;
23const lightBlue = {
24  r: 82,
25  g: 145,
26  b: 255,
27  a: 0.9,
28};
29
30export class ChartStruct extends BaseStruct {
31  static hoverFuncStruct: ChartStruct | undefined;
32  static selectFuncStruct: ChartStruct | undefined;
33  static lastSelectFuncStruct: ChartStruct | undefined;
34  isDraw = false; // 是否绘制,太小的不绘制
35  depth: number = 0;
36  symbol: string = '';
37  lib: string = '';
38
39  size: number = 0; // 实际size
40  count: number = 0; // 实际count
41  eventCount: number = 0;
42  dur: number = 0; // 实际dur
43  //搜索后会根据搜索匹配的函数的值赋值给parent
44  searchSize: number = 0; //
45  searchCount: number = 0;
46  searchDur: number = 0;
47  searchEventCount: number = 0;
48  //点击绘制的size在搜索的基础上,赋值给parent
49  drawSize: number = 0;
50  drawCount: number = 0;
51  drawDur: number = 0;
52  drawEventCount: number = 0;
53
54  parent: ChartStruct | undefined;
55  children: Array<ChartStruct> = [];
56  percent: number = 0; // 0 - 1 该node所占整体的百分比
57  addr: string = '';
58  isSearch: boolean = false;
59  isChartSelect: boolean = false; // 是否为点选的调用链
60  isChartSelectParent: boolean = false; // 用来显示灰色
61  tsArray: Array<number> = [];
62  countArray: Array<number> = [];
63  durArray: Array<number> = [];
64  isThread: boolean = false;
65  isProcess: boolean = false;
66}
67
68export enum ChartMode {
69  Byte, // Native Memory
70  Count, // Perf
71  Duration, // eBpf
72  EventCount, //cycles
73}
74
75export function setFuncFrame(node: ChartStruct, canvasFrame: Rect, total: number, mode: ChartMode): void {
76  if (!node.frame) {
77    node.frame = new Rect(0, 0, 0, 0);
78  }
79  // filter depth is 0
80  if (node.parent) {
81    let idx = node.parent.children.indexOf(node);
82    if (idx === 0) {
83      node.frame!.x = node.parent.frame!.x;
84    } else {
85      // set x by left frame. left frame is parent.children[idx - 1]
86      node.frame.x = node.parent.children[idx - 1].frame!.x + node.parent.children[idx - 1].frame!.width;
87    }
88    if (node.parent?.isChartSelect && !node.isChartSelect) {
89      node.frame!.width = 0;
90    } else {
91      switch (mode) {
92        case ChartMode.Byte:
93          node.frame!.width = Math.floor(((node.drawSize || node.size) / total) * canvasFrame.width);
94          break;
95        case ChartMode.Count:
96          node.frame!.width = Math.floor(((node.drawCount || node.count) / total) * canvasFrame.width);
97          break;
98        case ChartMode.Duration:
99          node.frame!.width = Math.floor(((node.drawDur || node.dur) / total) * canvasFrame.width);
100          break;
101        case ChartMode.EventCount:
102          node.frame!.width = Math.floor(((node.drawEventCount || node.eventCount) / total) * canvasFrame.width);
103          break;
104        default:
105          warn('not match ChartMode');
106      }
107    }
108
109    node.frame!.y = node.parent.frame!.y + rectHeight;
110    node.frame!.height = rectHeight;
111  }
112}
113
114/**
115 * draw rect
116 * @param canvasCtx CanvasRenderingContext2D
117 * @param node rect which is need draw
118 * @param percent function size or count / total size or count
119 */
120export function draw(canvasCtx: CanvasRenderingContext2D, node: ChartStruct): void {
121  let spApplication = <SpApplication>document.getElementsByTagName('sp-application')[0];
122  if (!node.frame) {
123    return;
124  }
125  //主体
126  const drawHeight = rectHeight - padding * 2; //绘制方块上下留一个像素
127  if (node.depth === 0 || (node.isChartSelectParent && node !== ChartStruct.selectFuncStruct)) {
128    canvasCtx.fillStyle = `rgba(${lightBlue.g}, ${lightBlue.g}, ${lightBlue.g}, ${lightBlue.a})`;
129  } else {
130    if (node.isSearch) {
131      canvasCtx.fillStyle = `rgba(${lightBlue.r}, ${lightBlue.g}, ${lightBlue.b}, ${lightBlue.a})`;
132    } else {
133      canvasCtx.fillStyle = getHeatColor(node.percent);
134    }
135  }
136  canvasCtx.fillRect(node.frame.x, node.frame.y, node.frame.width, drawHeight);
137  //边框
138  canvasCtx.lineWidth = 0.4;
139  if (isHover(node)) {
140    if (spApplication.dark) {
141      canvasCtx.strokeStyle = '#fff';
142    } else {
143      canvasCtx.strokeStyle = '#000';
144    }
145  } else {
146    if (spApplication.dark) {
147      canvasCtx.strokeStyle = '#000';
148    } else {
149      canvasCtx.strokeStyle = '#fff';
150    }
151  }
152  canvasCtx.strokeRect(node.frame.x, node.frame.y, node.frame.width, drawHeight);
153  //文字
154  if (node.frame.width > 10) {
155    if (node.percent > 0.6 || node.isSearch) {
156      canvasCtx.fillStyle = '#fff';
157    } else {
158      canvasCtx.fillStyle = '#000';
159    }
160    drawString(canvasCtx, node.symbol || '', 5, node.frame, node);
161  }
162  node.isDraw = true;
163}
164
165/**
166 * 火焰图颜色计算,根据每个node占总大小的百分比调整
167 * @param widthPercentage 百分比
168 * @returns rbg
169 */
170function getHeatColor(widthPercentage: number): string {
171  return `rgba(
172    ${Math.floor(245 + 10 * (1 - widthPercentage))},
173    ${Math.floor(110 + 105 * (1 - widthPercentage))},
174    ${100},
175    0.9)`;
176}
177
178function isHover(data: ChartStruct): boolean {
179  return ChartStruct.hoverFuncStruct === data;
180}
181