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 { ColorUtils } from '../../component/trace/base/ColorUtils'; 17import { TraceRow } from '../../component/trace/base/TraceRow'; 18import { 19 isFrameContainPoint, 20 ns2x, 21 Render, 22 RequestMessage, 23 drawFunString, 24 drawLoadingFrame, 25 Rect, 26} from './ProcedureWorkerCommon'; 27import { FuncStruct as BaseFuncStruct } from '../../bean/FuncStruct'; 28import { FlagsConfig } from '../../component/SpFlags'; 29import { TabPaneTaskFrames } from '../../component/trace/sheet/task/TabPaneTaskFrames'; 30import { SpSystemTrace } from '../../component/SpSystemTrace'; 31export class FuncRender { 32 renderMainThread( 33 req: { 34 useCache: boolean; 35 context: CanvasRenderingContext2D; 36 type: string; 37 }, 38 row: TraceRow<FuncStruct> 39 ): void { 40 let funcList = row.dataList; 41 let funcFilter = row.dataListCache; 42 func( 43 funcList, 44 funcFilter, 45 TraceRow.range!.startNS, 46 TraceRow.range!.endNS, 47 TraceRow.range!.totalNS, 48 row.frame, 49 req.useCache || !TraceRow.range!.refresh, 50 row.funcExpand 51 ); 52 drawLoadingFrame(req.context, funcFilter, row, true); 53 req.context.beginPath(); 54 let funcFind = false; 55 for (let re of funcFilter) { 56 FuncStruct.draw(req.context, re); 57 if (row.isHover) { 58 if (re.dur === 0 || re.dur === null || re.dur === undefined) { 59 if ( 60 re.frame && 61 re.itid && 62 row.hoverX >= re.frame.x - 5 && 63 row.hoverX <= re.frame.x + 5 && 64 row.hoverY >= re.frame.y && 65 row.hoverY <= re.frame.y + re.frame.height 66 ) { 67 FuncStruct.hoverFuncStruct = re; 68 funcFind = true; 69 } 70 } else { 71 if (re.frame && re.itid && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { 72 FuncStruct.hoverFuncStruct = re; 73 funcFind = true; 74 } 75 } 76 } 77 } 78 if (!funcFind && row.isHover) { 79 FuncStruct.hoverFuncStruct = undefined; 80 } 81 req.context.closePath(); 82 } 83 84 render(req: RequestMessage, list: Array<FuncStruct>, filter: Array<FuncStruct>): void {} 85} 86 87export function func( 88 funcList: Array<FuncStruct>, 89 funcFilter: Array<FuncStruct>, 90 startNS: number, 91 endNS: number, 92 totalNS: number, 93 frame: Rect, 94 use: boolean, 95 expand: boolean 96): void { 97 if (use && funcFilter.length > 0) { 98 for (let i = 0, len = funcFilter.length; i < len; i++) { 99 if ((funcFilter[i].startTs || 0) + (funcFilter[i].dur || 0) >= startNS && (funcFilter[i].startTs || 0) <= endNS) { 100 FuncStruct.setFuncFrame(funcFilter[i], 0, startNS, endNS, totalNS, frame); 101 } else { 102 funcFilter[i].frame = undefined; 103 } 104 } 105 return; 106 } 107 funcFilter.length = 0; 108 if (funcList) { 109 let groups = funcList 110 .filter( 111 (it) => 112 (it.startTs ?? 0) + (it.dur ?? 0) >= startNS && 113 (it.startTs ?? 0) <= endNS && 114 ((!expand && it.depth === 0) || expand) 115 ) 116 .map((it) => { 117 FuncStruct.setFuncFrame(it, 0, startNS, endNS, totalNS, frame); 118 return it; 119 }) 120 .reduce((pre, current, index, arr) => { 121 //@ts-ignore 122 (pre[`${current.frame.x}-${current.depth}`] = pre[`${current.frame.x}-${current.depth}`] || []).push(current); 123 return pre; 124 }, {}); 125 Reflect.ownKeys(groups).map((kv) => { 126 //@ts-ignore 127 let arr = groups[kv].sort((a: FuncStruct, b: FuncStruct) => b.dur - a.dur); 128 funcFilter.push(arr[0]); 129 }); 130 } 131} 132export function funcStructOnClick( 133 clickRowType: string, 134 sp: SpSystemTrace, 135 row: TraceRow<FuncStruct> | undefined, 136 scrollToFuncHandler: Function, 137 entry?: FuncStruct 138): Promise<unknown> { 139 return new Promise((resolve, reject) => { 140 if (clickRowType === TraceRow.ROW_TYPE_FUNC && (FuncStruct.hoverFuncStruct || entry)) { 141 if (FuncStruct.funcSelect) { 142 TabPaneTaskFrames.TaskArray = []; 143 sp.removeLinkLinesByBusinessType('task'); 144 let hoverFuncStruct = entry || FuncStruct.hoverFuncStruct; 145 FuncStruct.selectFuncStruct = hoverFuncStruct; 146 sp.timerShaftEL?.drawTriangle(FuncStruct.selectFuncStruct!.startTs || 0, 'inverted'); 147 let flagConfig = FlagsConfig.getFlagsConfig('TaskPool'); 148 let showTabArray: Array<string> = ['current-selection']; 149 if (flagConfig!.TaskPool === 'Enabled') { 150 if (FuncStruct.selectFuncStruct?.funName) { 151 if (FuncStruct.selectFuncStruct.funName.indexOf('H:Task ') >= 0) { 152 showTabArray.push('box-task-frames'); 153 sp.drawTaskPollLine(row); 154 } 155 } 156 } 157 sp.traceSheetEL?.displayFuncData(showTabArray, FuncStruct.selectFuncStruct!, scrollToFuncHandler); 158 sp.timerShaftEL?.modifyFlagList(undefined); 159 } 160 reject(new Error()); 161 } else { 162 resolve(null); 163 } 164 }); 165} 166export class FuncStruct extends BaseFuncStruct { 167 static hoverFuncStruct: FuncStruct | undefined; 168 static selectFuncStruct: FuncStruct | undefined; 169 flag: string | undefined; // 570000 170 textMetricsWidth: number | undefined; 171 static funcSelect: boolean = true; 172 static setFuncFrame( 173 funcNode: FuncStruct, 174 padding: number, 175 startNS: number, 176 endNS: number, 177 totalNS: number, 178 frame: Rect 179 ): void { 180 let x1: number; 181 let x2: number; 182 if ((funcNode.startTs || 0) > startNS && (funcNode.startTs || 0) <= endNS) { 183 x1 = ns2x(funcNode.startTs || 0, startNS, endNS, totalNS, frame); 184 } else { 185 x1 = 0; 186 } 187 if ( 188 (funcNode.startTs || 0) + (funcNode.dur || 0) > startNS && 189 (funcNode.startTs || 0) + (funcNode.dur || 0) <= endNS 190 ) { 191 x2 = ns2x((funcNode.startTs || 0) + (funcNode.dur || 0), startNS, endNS, totalNS, frame); 192 } else { 193 x2 = frame.width; 194 } 195 if (!funcNode.frame) { 196 funcNode.frame = new Rect(0, 0, 0, 0); 197 } 198 let getV: number = x2 - x1 < 1 ? 1 : x2 - x1; 199 funcNode.frame.x = Math.floor(x1); 200 funcNode.frame.y = funcNode.depth! * 18 + 3; 201 funcNode.frame.width = Math.ceil(getV); 202 funcNode.frame.height = 18; 203 } 204 205 static draw(ctx: CanvasRenderingContext2D, data: FuncStruct): void { 206 if (data.frame) { 207 let isBinder = FuncStruct.isBinder(data); 208 if (data.dur === undefined || data.dur === null) { 209 } else { 210 ctx.globalAlpha = 1; 211 ctx.fillStyle = ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.funName || '', 0, ColorUtils.FUNC_COLOR.length)]; 212 let textColor = ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.funName || '', 0, ColorUtils.FUNC_COLOR.length)]; 213 if (FuncStruct.hoverFuncStruct && data.funName === FuncStruct.hoverFuncStruct.funName) { 214 ctx.globalAlpha = 0.7; 215 } 216 ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, data.frame.height); 217 if (data.frame.width > 10) { 218 ctx.fillStyle = ColorUtils.funcTextColor(textColor); 219 ctx.textBaseline = 'middle'; 220 drawFunString(ctx, `${data.funName || ''}`, 5, data.frame, data); 221 } 222 if ( 223 data.callid === FuncStruct.selectFuncStruct?.callid && 224 data.startTs === FuncStruct.selectFuncStruct?.startTs && 225 data.depth === FuncStruct.selectFuncStruct?.depth 226 ) { 227 ctx.strokeStyle = '#000'; 228 ctx.lineWidth = 2; 229 ctx.strokeRect(data.frame.x, data.frame.y + 1, data.frame.width, data.frame.height - 2); 230 } 231 let flagConfig = FlagsConfig.getFlagsConfig('TaskPool'); 232 if ( 233 flagConfig!.TaskPool === 'Enabled' && 234 data.funName!.indexOf('H:Task PerformTask End:') >= 0 && 235 data.funName!.indexOf('Successful') < 0 236 ) { 237 if (data.frame!.width < 10) { 238 FuncStruct.drawTaskPoolUnSuccessFlag(ctx, data.frame!.x, (data.depth! + 0.5) * 18, 3, data!); 239 } else { 240 FuncStruct.drawTaskPoolUnSuccessFlag(ctx, data.frame!.x, (data.depth! + 0.5) * 18, 6, data!); 241 } 242 } 243 if (flagConfig!.TaskPool === 'Enabled' && data.funName!.indexOf('H:Thread Timeout Exit') >= 0) { 244 FuncStruct.drawTaskPoolTimeOutFlag(ctx, data.frame!.x, (data.depth! + 0.5) * 18, 10, data!); 245 } 246 // 如果该函数没有结束时间,则绘制锯齿。 247 if (data.nofinish && data.frame!.width > 4) { 248 FuncStruct.drawRupture(ctx, data.frame.x, data.frame.y, data.frame.width, data.frame.height); 249 } 250 } 251 } 252 } 253 254 /** 255 * 绘制锯齿 256 * @param ctx 绘图上下文环境 257 * @param x 水平坐标 258 * @param y 垂直坐标 259 * @param width 函数矩形框的宽度 260 * @param height 函数矩形框的高度 261 */ 262 static drawRupture(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number): void { 263 ctx.fillStyle = '#fff'; // 白色: '#fff' , 红色: '#FF0000'; 264 let ruptureWidth = 5; 265 let ruptureNode = height / ruptureWidth; 266 let len = height / ruptureNode; 267 ctx.moveTo(x + width - 1, y); 268 for (let i = 1; i <= ruptureNode; i++) { 269 ctx.lineTo(x + width - 1 - (i % 2 === 0 ? 0 : ruptureWidth), y + len * i - 2); 270 } 271 ctx.closePath(); 272 ctx.fill(); 273 } 274 275 static drawTaskPoolUnSuccessFlag( 276 ctx: CanvasRenderingContext2D, 277 x: number, 278 y: number, 279 radius: number, 280 data: FuncStruct 281 ): void { 282 ctx.strokeStyle = '#FFC880'; 283 ctx.lineWidth = 1; 284 ctx.beginPath(); 285 ctx.arc(x + data.frame!.width, y, radius, 0, Math.PI * 2); 286 ctx.closePath(); 287 ctx.fillStyle = '#E64566'; 288 ctx.fill(); 289 ctx.stroke(); 290 } 291 292 static drawTaskPoolTimeOutFlag( 293 canvas: CanvasRenderingContext2D, 294 x: number, 295 y: number, 296 radius: number, 297 data: FuncStruct 298 ): void { 299 canvas.strokeStyle = '#FFC880'; 300 canvas.lineWidth = 1; 301 canvas.beginPath(); 302 canvas.arc(x + data.frame!.width + 20, y, radius, 0, Math.PI * 2); 303 canvas.closePath(); 304 canvas.fillStyle = '#FFC880'; 305 canvas.fill(); 306 canvas.stroke(); 307 canvas.font = '18px Arial'; 308 canvas.fillStyle = ColorUtils.GREY_COLOR; 309 canvas.textAlign = 'center'; 310 canvas.fillText('¡', x + data.frame!.width + 20, y); 311 } 312 313 static isSelected(data: FuncStruct): boolean { 314 return ( 315 FuncStruct.selectFuncStruct !== undefined && 316 FuncStruct.selectFuncStruct.startTs === data.startTs && 317 FuncStruct.selectFuncStruct.depth === data.depth 318 ); 319 } 320} 321