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