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