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