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 BaseStruct, 20 isFrameContainPoint, 21 ns2x, 22 Rect, 23 Render, 24 RequestMessage, 25 drawString, 26 drawLoadingFrame, 27} from './ProcedureWorkerCommon'; 28import { FuncStruct as BaseFuncStruct } from '../../bean/FuncStruct'; 29import { FlagsConfig } from '../../component/SpFlags'; 30import {TabPaneTaskFrames} from "../../component/trace/sheet/task/TabPaneTaskFrames"; 31export class FuncRender extends Render { 32 renderMainThread( 33 req: { 34 useCache: boolean; 35 context: CanvasRenderingContext2D; 36 type: string; 37 }, 38 row: TraceRow<FuncStruct> 39 ) { 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 row.hoverX >= re.frame.x - 5 && 62 row.hoverX <= re.frame.x + 5 && 63 row.hoverY >= re.frame.y && 64 row.hoverY <= re.frame.y + re.frame.height 65 ) { 66 FuncStruct.hoverFuncStruct = re; 67 funcFind = true; 68 } 69 } else { 70 if (re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { 71 FuncStruct.hoverFuncStruct = re; 72 funcFind = true; 73 } 74 } 75 } 76 } 77 if (!funcFind && row.isHover) FuncStruct.hoverFuncStruct = undefined; 78 req.context.closePath(); 79 } 80 81 render(req: RequestMessage, list: Array<any>, filter: Array<any>) {} 82} 83 84export function func( 85 funcList: Array<any>, 86 funcFilter: Array<any>, 87 startNS: number, 88 endNS: number, 89 totalNS: number, 90 frame: any, 91 use: boolean, 92 expand: boolean 93) { 94 if (use && funcFilter.length > 0) { 95 for (let i = 0, len = funcFilter.length; i < len; i++) { 96 if ((funcFilter[i].startTs || 0) + (funcFilter[i].dur || 0) >= startNS && (funcFilter[i].startTs || 0) <= endNS) { 97 FuncStruct.setFuncFrame(funcFilter[i], 0, startNS, endNS, totalNS, frame); 98 } else { 99 funcFilter[i].frame = null; 100 } 101 } 102 return; 103 } 104 funcFilter.length = 0; 105 if (funcList) { 106 let groups = funcList 107 .filter( 108 (it) => 109 (it.startTs ?? 0) + (it.dur ?? 0) >= startNS && 110 (it.startTs ?? 0) <= endNS && 111 ((!expand && it.depth === 0) || expand) 112 ) 113 .map((it) => { 114 FuncStruct.setFuncFrame(it, 0, startNS, endNS, totalNS, frame); 115 return it; 116 }) 117 .reduce((pre, current, index, arr) => { 118 (pre[`${current.frame.x}-${current.depth}`] = pre[`${current.frame.x}-${current.depth}`] || []).push(current); 119 return pre; 120 }, {}); 121 Reflect.ownKeys(groups).map((kv) => { 122 let arr = groups[kv].sort((a: any, b: any) => b.dur - a.dur); 123 funcFilter.push(arr[0]); 124 }); 125 } 126} 127export function FuncStructOnClick(clickRowType: string, sp:any,row:TraceRow<any>|undefined, scrollToFuncHandler: any) { 128 return new Promise((resolve, reject) => { 129 if (clickRowType === TraceRow.ROW_TYPE_FUNC && FuncStruct.hoverFuncStruct) { 130 TabPaneTaskFrames.TaskArray = []; 131 sp.removeLinkLinesByBusinessType('task'); 132 FuncStruct.selectFuncStruct = FuncStruct.hoverFuncStruct; 133 let hoverFuncStruct = FuncStruct.hoverFuncStruct; 134 sp.timerShaftEL?.drawTriangle(FuncStruct.selectFuncStruct!.startTs || 0, 'inverted'); 135 FuncStruct.selectFuncStruct = hoverFuncStruct; 136 let flagConfig = FlagsConfig.getFlagsConfig('TaskPool'); 137 let showTabArray: Array<string> = ['current-selection']; 138 if (flagConfig!.TaskPool === 'Enabled') { 139 if (FuncStruct.selectFuncStruct?.funName) { 140 if (FuncStruct.selectFuncStruct.funName.indexOf('H:Task ') >= 0) { 141 showTabArray.push('box-task-frames'); 142 sp.drawTaskPollLine(row); 143 } 144 } 145 } 146 sp.traceSheetEL?.displayFuncData(showTabArray, FuncStruct.selectFuncStruct, scrollToFuncHandler); 147 sp.timerShaftEL?.modifyFlagList(undefined); 148 reject(); 149 } else { 150 resolve(null); 151 } 152 }); 153} 154export class FuncStruct extends BaseFuncStruct { 155 static hoverFuncStruct: FuncStruct | undefined; 156 static selectFuncStruct: FuncStruct | undefined; 157 flag: string | undefined; // 570000 158 textMetricsWidth: number | undefined; 159 static setFuncFrame(funcNode: any, padding: number, startNS: number, endNS: number, totalNS: number, frame: any) { 160 let x1: number, x2: number; 161 if ((funcNode.startTs || 0) > startNS && (funcNode.startTs || 0) <= endNS) { 162 x1 = ns2x(funcNode.startTs || 0, startNS, endNS, totalNS, frame); 163 } else { 164 x1 = 0; 165 } 166 if ( 167 (funcNode.startTs || 0) + (funcNode.dur || 0) > startNS && 168 (funcNode.startTs || 0) + (funcNode.dur || 0) <= endNS 169 ) { 170 x2 = ns2x((funcNode.startTs || 0) + (funcNode.dur || 0), startNS, endNS, totalNS, frame); 171 } else { 172 x2 = frame.width; 173 } 174 if (!funcNode.frame) { 175 funcNode.frame = {}; 176 } 177 let getV: number = x2 - x1 < 1 ? 1 : x2 - x1; 178 funcNode.frame.x = Math.floor(x1); 179 funcNode.frame.y = funcNode.depth * 20; 180 funcNode.frame.width = Math.ceil(getV); 181 funcNode.frame.height = 20; 182 } 183 184 static draw(ctx: CanvasRenderingContext2D, data: FuncStruct) { 185 if (data.frame) { 186 let isBinder = FuncStruct.isBinder(data); 187 if (data.dur == undefined || data.dur == null) { 188 } else { 189 ctx.globalAlpha = 1; 190 ctx.fillStyle = ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.funName || '', 0, ColorUtils.FUNC_COLOR.length)]; 191 let textColor = ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.funName || '', 0, ColorUtils.FUNC_COLOR.length)]; 192 let miniHeight = 20; 193 if (FuncStruct.hoverFuncStruct && data.funName == FuncStruct.hoverFuncStruct.funName) { 194 ctx.globalAlpha = 0.7; 195 } 196 ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2); 197 if (data.frame.width > 10) { 198 ctx.strokeStyle = '#fff'; 199 ctx.lineWidth = 1; 200 ctx.strokeRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2); 201 ctx.fillStyle = ColorUtils.funcTextColor(textColor); 202 drawString(ctx, `${data.funName || ''}`, 5, data.frame, data); 203 } 204 if (data.callid == FuncStruct.selectFuncStruct?.callid&& 205 data.startTs == FuncStruct.selectFuncStruct?.startTs&& 206 data.depth == FuncStruct.selectFuncStruct?.depth) { 207 ctx.strokeStyle = '#000'; 208 ctx.lineWidth = 2; 209 ctx.strokeRect(data.frame.x, data.frame.y + 1, data.frame.width, miniHeight - padding * 2 - 2); 210 } 211 let flagConfig = FlagsConfig.getFlagsConfig('TaskPool'); 212 if ( 213 flagConfig!.TaskPool === 'Enabled' && 214 data.funName!.indexOf('H:Task PerformTask End:') >= 0 && 215 data.funName!.indexOf('Successful') < 0 216 ) { 217 if (data.frame!.width < 10) { 218 FuncStruct.drawTaskPoolUnSuccessFlag(ctx, data.frame!.x, (data.depth! + 0.5) * 20, 3, data!); 219 } else { 220 FuncStruct.drawTaskPoolUnSuccessFlag(ctx, data.frame!.x, (data.depth! + 0.5) * 20, 6, data!); 221 } 222 } 223 if (flagConfig!.TaskPool === 'Enabled' && data.funName!.indexOf('H:Thread Timeout Exit') >= 0) { 224 FuncStruct.drawTaskPoolTimeOutFlag(ctx, data.frame!.x, (data.depth! + 0.5) * 20, 10, data!); 225 } 226 } 227 } 228 } 229 230 static drawTaskPoolUnSuccessFlag( 231 ctx: CanvasRenderingContext2D, 232 x: number, 233 y: number, 234 radius: number, 235 data: FuncStruct 236 ): void { 237 ctx.strokeStyle = '#FFC880'; 238 ctx.lineWidth = 1; 239 ctx.beginPath(); 240 ctx.arc(x + data.frame!.width, y, radius, 0, Math.PI * 2); 241 ctx.closePath(); 242 ctx.fillStyle = '#E64566'; 243 ctx.fill(); 244 ctx.stroke(); 245 } 246 247 static drawTaskPoolTimeOutFlag( 248 canvas: CanvasRenderingContext2D, 249 x: number, 250 y: number, 251 radius: number, 252 data: FuncStruct 253 ): void { 254 canvas.strokeStyle = '#FFC880'; 255 canvas.lineWidth = 1; 256 canvas.beginPath(); 257 canvas.arc(x + data.frame!.width + 20, y, radius, 0, Math.PI * 2); 258 canvas.closePath(); 259 canvas.fillStyle = '#FFC880'; 260 canvas.fill(); 261 canvas.stroke(); 262 canvas.font = '18px Arial'; 263 canvas.fillStyle = ColorUtils.GREY_COLOR; 264 canvas.textAlign = 'center'; 265 canvas.fillText('¡', x + data.frame!.width + 20, y); 266 } 267 268 static isSelected(data: FuncStruct): boolean { 269 return ( 270 FuncStruct.selectFuncStruct != undefined && 271 FuncStruct.selectFuncStruct.startTs == data.startTs && 272 FuncStruct.selectFuncStruct.depth == data.depth 273 ); 274 } 275} 276 277const padding = 1; 278