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