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} 81 82export enum ChartMode { 83 Byte, // Native Memory 84 Count, // Perf 85 Duration, // eBpf 86 EventCount, //cycles 87} 88 89export function setFuncFrame(node: ChartStruct, canvasFrame: Rect, total: number, mode: ChartMode): void { 90 if (!node.frame) { 91 node.frame = new Rect(0, 0, 0, 0); 92 } 93 // filter depth is 0 94 if (node.parent) { 95 let idx = node.parent.children.indexOf(node); 96 if (idx === 0) { 97 node.frame!.x = node.parent.frame!.x; 98 } else { 99 // set x by left frame. left frame is parent.children[idx - 1] 100 node.frame.x = node.parent.children[idx - 1].frame!.x + node.parent.children[idx - 1].frame!.width; 101 } 102 if (node.parent?.isChartSelect && !node.isChartSelect) { 103 node.frame!.width = 0; 104 } else { 105 switch (mode) { 106 case ChartMode.Byte: 107 node.frame!.width = Math.floor(((node.drawSize || node.size) / total) * canvasFrame.width); 108 break; 109 case ChartMode.Count: 110 node.frame!.width = Math.floor(((node.drawCount || node.count) / total) * canvasFrame.width); 111 break; 112 case ChartMode.Duration: 113 node.frame!.width = Math.floor(((node.drawDur || node.dur) / total) * canvasFrame.width); 114 break; 115 case ChartMode.EventCount: 116 node.frame!.width = Math.floor(((node.drawEventCount || node.eventCount) / total) * canvasFrame.width); 117 break; 118 default: 119 warn('not match ChartMode'); 120 } 121 } 122 123 node.frame!.y = node.parent.frame!.y + rectHeight; 124 node.frame!.height = rectHeight; 125 } 126} 127 128/** 129 * draw rect 130 * @param canvasCtx CanvasRenderingContext2D 131 * @param node rect which is need draw 132 * @param percent function size or count / total size or count 133 */ 134export function draw(canvasCtx: CanvasRenderingContext2D, node: ChartStruct): void { 135 let spApplication = <SpApplication>document.getElementsByTagName('sp-application')[0]; 136 if (!node.frame) { 137 return; 138 } 139 //主体 140 const drawHeight = rectHeight - padding * 2; //绘制方块上下留一个像素 141 if (node.depth === 0 || (node.isChartSelectParent && node !== ChartStruct.selectFuncStruct)) { 142 canvasCtx.fillStyle = `rgba(${lightBlue.g}, ${lightBlue.g}, ${lightBlue.g}, ${lightBlue.a})`; 143 } else { 144 if (node.isSearch) { 145 canvasCtx.fillStyle = `rgba(${lightBlue.r}, ${lightBlue.g}, ${lightBlue.b}, ${lightBlue.a})`; 146 } else { 147 if (node.isJsStack) { 148 canvasCtx.fillStyle = `rgba(${lightGreen.r}, ${lightGreen.g}, ${lightGreen.b}, ${lightGreen.a})`; 149 } else { 150 canvasCtx.fillStyle = getHeatColor(node.percent); 151 } 152 } 153 } 154 canvasCtx.fillRect(node.frame.x, node.frame.y, node.frame.width, drawHeight); 155 //边框 156 canvasCtx.lineWidth = 0.4; 157 if (isHover(node)) { 158 if (spApplication.dark) { 159 canvasCtx.strokeStyle = '#fff'; 160 } else { 161 canvasCtx.strokeStyle = '#000'; 162 } 163 } else { 164 if (spApplication.dark) { 165 canvasCtx.strokeStyle = '#000'; 166 } else { 167 canvasCtx.strokeStyle = '#fff'; 168 } 169 } 170 canvasCtx.strokeRect(node.frame.x, node.frame.y, node.frame.width - canvasCtx.lineWidth, drawHeight); 171 //文字 172 if (node.frame.width > 10) { 173 if (node.percent > 0.6 || node.isSearch) { 174 canvasCtx.fillStyle = '#fff'; 175 } else { 176 canvasCtx.fillStyle = '#000'; 177 } 178 drawString(canvasCtx, splitSymbol(node), 5, node.frame, node); 179 } 180 node.isDraw = true; 181} 182 183function splitSymbol(node: ChartStruct): string { 184 if (node.depth === 0 || node.isProcess || node.isThread) { 185 return node.symbol; 186 } 187 return node.symbol.split(' (')[0]; 188} 189 190/** 191 * 火焰图颜色计算,根据每个node占总大小的百分比调整 192 * @param widthPercentage 百分比 193 * @returns rbg 194 */ 195function getHeatColor(widthPercentage: number): string { 196 return `rgba( 197 ${Math.floor(245 + 10 * (1 - widthPercentage))}, 198 ${Math.floor(110 + 105 * (1 - widthPercentage))}, 199 ${100}, 200 0.9)`; 201} 202 203function isHover(data: ChartStruct): boolean { 204 return ChartStruct.hoverFuncStruct === data; 205} 206