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