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 { 18 BaseStruct, 19 dataFilterHandler, 20 drawFlagLine, 21 drawLines, 22 drawLoadingFrame, 23 drawSelection, 24 drawWakeUp, 25 drawWakeUpList, 26 Render, 27 RequestMessage, 28} from '../ProcedureWorkerCommon'; 29import { TraceRow } from '../../../component/trace/base/TraceRow'; 30import { SpSystemTrace } from '../../../component/SpSystemTrace'; 31 32export class EmptyRender extends Render { 33 renderMainThread(req: any, row: TraceRow<any>): void { 34 drawLoadingFrame(req.context, [], row); 35 } 36 render(cpuReqMessage: RequestMessage, list: Array<any>, filter: Array<any>): void { 37 if (cpuReqMessage.canvas) { 38 cpuReqMessage.context.clearRect(0, 0, cpuReqMessage.frame.width, cpuReqMessage.frame.height); 39 cpuReqMessage.context.beginPath(); 40 drawLines(cpuReqMessage.context, cpuReqMessage.xs, cpuReqMessage.frame.height, cpuReqMessage.lineColor); 41 drawSelection(cpuReqMessage.context, cpuReqMessage.params); 42 cpuReqMessage.context.closePath(); 43 drawFlagLine( 44 cpuReqMessage.context, 45 cpuReqMessage.flagMoveInfo, 46 cpuReqMessage.flagSelectedInfo, 47 cpuReqMessage.startNS, 48 cpuReqMessage.endNS, 49 cpuReqMessage.totalNS, 50 cpuReqMessage.frame, 51 cpuReqMessage.slicesTime 52 ); 53 } 54 // @ts-ignore 55 self.postMessage({ 56 id: cpuReqMessage.id, 57 type: cpuReqMessage.type, 58 results: cpuReqMessage.canvas ? undefined : filter, 59 hover: null, 60 }); 61 } 62} 63 64export class CpuRender { 65 renderMainThread( 66 req: { 67 ctx: CanvasRenderingContext2D; 68 useCache: boolean; 69 type: string; 70 translateY: number; 71 }, 72 row: TraceRow<CpuStruct> 73 ) { 74 let cpuList = row.dataList; 75 let cpuFilter = row.dataListCache; 76 let startNS = TraceRow.range!.startNS ?? 0; 77 let endNS = TraceRow.range!.endNS ?? 0; 78 let totalNS = TraceRow.range!.totalNS ?? 0; 79 dataFilterHandler(cpuList, cpuFilter, { 80 startKey: 'startTime', 81 durKey: 'dur', 82 startNS: startNS, 83 endNS: endNS, 84 totalNS: totalNS, 85 frame: row.frame, 86 paddingTop: 5, 87 useCache: req.useCache || !(TraceRow.range?.refresh ?? false), 88 }); 89 drawLoadingFrame(req.ctx, cpuFilter, row); 90 req.ctx.beginPath(); 91 req.ctx.font = '11px sans-serif'; 92 cpuFilter.forEach((re) => { 93 re.translateY = req.translateY; 94 CpuStruct.draw(req.ctx, re, req.translateY); 95 }); 96 req.ctx.closePath(); 97 let currentCpu = parseInt(req.type!.replace('cpu-data-', '')); 98 let wakeup = req.type == `cpu-data-${CpuStruct.selectCpuStruct?.cpu || 0}` ? CpuStruct.selectCpuStruct : undefined; 99 drawWakeUp(req.ctx, CpuStruct.wakeupBean, startNS, endNS, totalNS, row.frame, wakeup, currentCpu, true); 100 for (let i = 0; i < SpSystemTrace.wakeupList.length; i++) { 101 if (i + 1 == SpSystemTrace.wakeupList.length) { 102 return; 103 } 104 let wake = SpSystemTrace.wakeupList[i + 1]; 105 let wakeupListItem = 106 req.type == `cpu-data-${SpSystemTrace.wakeupList[i]?.cpu || 0}` ? SpSystemTrace.wakeupList[i] : undefined; 107 drawWakeUpList(req.ctx, wake, startNS, endNS, totalNS, row.frame, wakeupListItem, currentCpu, true); 108 } 109 } 110 111 cpu( 112 cpuList: Array<any>, 113 cpuRes: Array<any>, 114 startNS: number, 115 endNS: number, 116 totalNS: number, 117 frame: any, 118 use: boolean 119 ): void { 120 if (use && cpuRes.length > 0) { 121 this.setFrameCpuByRes(cpuRes, startNS, endNS, frame); 122 return; 123 } 124 if (cpuList) { 125 this.setFrameCpuByList(cpuRes, startNS, endNS, frame, cpuList); 126 } 127 } 128 129 setFrameCpuByRes(cpuRes: Array<any>, startNS: number, endNS: number, frame: any) { 130 let pns = (endNS - startNS) / frame.width; 131 let y = frame.y + 5; 132 let height = frame.height - 10; 133 for (let i = 0, len = cpuRes.length; i < len; i++) { 134 let it = cpuRes[i]; 135 if ((it.startTime || 0) + (it.dur || 0) > startNS && (it.startTime || 0) < endNS) { 136 if (!cpuRes[i].frame) { 137 cpuRes[i].frame = {}; 138 cpuRes[i].frame.y = y; 139 cpuRes[i].frame.height = height; 140 } 141 CpuStruct.setCpuFrame(cpuRes[i], pns, startNS, endNS, frame); 142 } else { 143 cpuRes[i].frame = null; 144 } 145 } 146 } 147 148 setFrameCpuByList(cpuRes: Array<any>, startNS: number, endNS: number, frame: any, cpuList: Array<any>): void { 149 cpuRes.length = 0; 150 let pns = (endNS - startNS) / frame.width; //每个像素多少ns 151 let y = frame.y + 5; 152 let height = frame.height - 10; 153 let left = 0, 154 right = 0; 155 for (let i = 0, j = cpuList.length - 1, ib = true, jb = true; i < cpuList.length, j >= 0; i++, j--) { 156 if (cpuList[j].startTime <= endNS && jb) { 157 right = j; 158 jb = false; 159 } 160 if (cpuList[i].startTime + cpuList[i].dur >= startNS && ib) { 161 left = i; 162 ib = false; 163 } 164 if (!ib && !jb) { 165 break; 166 } 167 } 168 let slice = cpuList.slice(left, right + 1); 169 let sum = 0; 170 for (let i = 0; i < slice.length; i++) { 171 if (!slice[i].frame) { 172 slice[i].frame = {}; 173 slice[i].frame.y = y; 174 slice[i].frame.height = height; 175 } 176 if (slice[i].dur >= pns) { 177 slice[i].v = true; 178 CpuStruct.setCpuFrame(slice[i], pns, startNS, endNS, frame); 179 } else { 180 if (i > 0) { 181 let c = slice[i].startTime - slice[i - 1].startTime - slice[i - 1].dur; 182 if (c < pns && sum < pns) { 183 sum += c + slice[i - 1].dur; 184 slice[i].v = false; 185 } else { 186 slice[i].v = true; 187 CpuStruct.setCpuFrame(slice[i], pns, startNS, endNS, frame); 188 sum = 0; 189 } 190 } 191 } 192 } 193 cpuRes.push(...slice.filter((it) => it.v)); 194 } 195} 196export function CpuStructOnClick(rowType: string, sp: SpSystemTrace, cpuClickHandler: any) { 197 return new Promise((resolve,reject) => { 198 if (rowType === TraceRow.ROW_TYPE_CPU && CpuStruct.hoverCpuStruct) { 199 CpuStruct.selectCpuStruct = CpuStruct.hoverCpuStruct; 200 sp.timerShaftEL?.drawTriangle(CpuStruct.selectCpuStruct!.startTime || 0, 'inverted'); 201 sp.traceSheetEL?.displayCpuData( 202 CpuStruct.selectCpuStruct, 203 (wakeUpBean) => { 204 CpuStruct.wakeupBean = wakeUpBean; 205 sp.refreshCanvas(false); 206 }, 207 cpuClickHandler 208 ); 209 sp.timerShaftEL?.modifyFlagList(undefined); 210 reject(); 211 }else{ 212 resolve(null); 213 } 214 }); 215 216} 217export class CpuStruct extends BaseStruct { 218 static cpuCount: number = 1; //最大cpu数量 219 static hoverCpuStruct: CpuStruct | undefined; 220 static selectCpuStruct: CpuStruct | undefined; 221 static wakeupBean: WakeupBean | null | undefined = null; 222 cpu: number | undefined; 223 dur: number | undefined; 224 end_state: string | undefined; 225 state: string | undefined; 226 id: number | undefined; 227 tid: number | undefined; 228 name: string | undefined; 229 priority: number | undefined; 230 processCmdLine: string | undefined; 231 processId: number | undefined; 232 processName: string | undefined; 233 displayProcess: string | undefined; 234 displayThread: string | undefined; 235 measurePWidth: number = 0; 236 measureTWidth: number = 0; 237 startTime: number | undefined; 238 argSetID: number | undefined; 239 type: string | undefined; 240 v: boolean = false; 241 nofinish: boolean = false; 242 ts: number | undefined; 243 itid: number | undefined; 244 process: string | undefined; 245 pid: number | undefined; 246 thread: string | undefined; 247 isKeyPath?: number; 248 249 static draw(ctx: CanvasRenderingContext2D, data: CpuStruct, translateY: number): void { 250 if (data.frame) { 251 let pid = data.processId || 0; 252 let tid = data.tid || 0; 253 let width = data.frame.width || 0; 254 if (data.tid === CpuStruct.hoverCpuStruct?.tid || !CpuStruct.hoverCpuStruct) { 255 ctx.globalAlpha = 1; 256 ctx.fillStyle = ColorUtils.colorForTid(pid > 0 ? pid : tid); 257 } else if (data.processId === CpuStruct.hoverCpuStruct?.processId) { 258 ctx.globalAlpha = 0.6; 259 ctx.fillStyle = ColorUtils.colorForTid(pid > 0 ? pid : tid); 260 } else { 261 ctx.globalAlpha = 1; 262 ctx.fillStyle = '#e0e0e0'; 263 } 264 ctx.fillRect(data.frame.x, data.frame.y, width, data.frame.height); 265 ctx.globalAlpha = 1; 266 CpuStruct.drawText(ctx, data, width, pid, tid); 267 CpuStruct.drawRim(ctx, data, width); 268 } 269 } 270 static drawText(ctx: CanvasRenderingContext2D, data: CpuStruct, width: number, pid: number, tid: number) { 271 let textFillWidth = width - textPadding * 2; 272 if (data.frame && textFillWidth > 3) { 273 if (data.displayProcess === undefined) { 274 data.displayProcess = `${data.processName || 'Process'} [${data.processId}]`; 275 data.measurePWidth = ctx.measureText(data.displayProcess).width; 276 } 277 if (data.displayThread === undefined) { 278 data.displayThread = `${data.name || 'Thread'} [${data.tid}] [Prio:${data.priority || 0}]`; 279 data.measureTWidth = ctx.measureText(data.displayThread).width; 280 } 281 let processCharWidth = Math.round(data.measurePWidth / data.displayProcess.length); 282 let threadCharWidth = Math.round(data.measureTWidth / data.displayThread.length); 283 ctx.fillStyle = ColorUtils.funcTextColor(ColorUtils.colorForTid(pid > 0 ? pid : tid)); 284 ctx.font = '9px sans-serif'; 285 ctx.textBaseline = 'bottom'; 286 let y = data.frame.height / 2 + data.frame.y; 287 if (data.measurePWidth < textFillWidth) { 288 let x1 = Math.floor(width / 2 - data.measurePWidth / 2 + data.frame.x + textPadding); 289 ctx.fillText(data.displayProcess, x1, y, textFillWidth); 290 } else { 291 if (textFillWidth >= processCharWidth) { 292 let chatNum = textFillWidth / processCharWidth; 293 let x1 = data.frame.x + textPadding; 294 if (chatNum < 2) { 295 ctx.fillText(data.displayProcess.substring(0, 1), x1, y, textFillWidth); 296 } else { 297 ctx.fillText(data.displayProcess.substring(0, chatNum - 1) + '...', x1, y, textFillWidth); 298 } 299 } 300 } 301 ctx.textBaseline = 'top'; 302 if (data.measureTWidth < textFillWidth) { 303 let x2 = Math.floor(width / 2 - data.measureTWidth / 2 + data.frame.x + textPadding); 304 ctx.fillText(data.displayThread, x2, y + 2, textFillWidth); 305 } else { 306 if (textFillWidth >= threadCharWidth) { 307 let chatNum = textFillWidth / threadCharWidth; 308 let x1 = data.frame.x + textPadding; 309 if (chatNum < 2) { 310 ctx.fillText(data.displayThread.substring(0, 1), x1, y + 2, textFillWidth); 311 } else { 312 ctx.fillText(data.displayThread.substring(0, chatNum - 1) + '...', x1, y + 2, textFillWidth); 313 } 314 } 315 } 316 } 317 } 318 static drawRim(ctx: CanvasRenderingContext2D, data: CpuStruct, width: number) { 319 if (data.frame) { 320 if (data.nofinish && width > 4) { 321 ctx.fillStyle = '#fff'; 322 let ruptureWidth = 4; 323 let ruptureNode = 8; 324 ctx.moveTo(data.frame.x + data.frame.width - 1, data.frame.y); 325 for (let i = 1; i <= ruptureNode; i++) { 326 ctx.lineTo( 327 data.frame.x + data.frame.width - 1 - (i % 2 == 0 ? 0 : ruptureWidth), 328 data.frame.y + (data.frame.height / ruptureNode) * i 329 ); 330 } 331 ctx.closePath(); 332 ctx.fill(); 333 } 334 if (data.isKeyPath) { 335 const gradient = ctx.createLinearGradient(0, 0, 0, 40); 336 gradient.addColorStop(0, '#000000'); 337 gradient.addColorStop(0.5, '#0000FF'); 338 gradient.addColorStop(1, '#FF0000'); 339 ctx.strokeStyle = gradient; 340 ctx.lineWidth = 4; 341 ctx.strokeRect(data.frame.x, data.frame.y - 2, width - 2, data.frame.height + 2); 342 } 343 if (CpuStruct.selectCpuStruct && CpuStruct.equals(CpuStruct.selectCpuStruct, data)) { 344 ctx.strokeStyle = '#232c5d'; 345 ctx.lineWidth = 2; 346 ctx.strokeRect(data.frame.x, data.frame.y, width - 2, data.frame.height); 347 } 348 } 349 } 350 351 static setCpuFrame(cpuNode: any, pns: number, startNS: number, endNS: number, frame: any): void { 352 if ((cpuNode.startTime || 0) < startNS) { 353 cpuNode.frame.x = 0; 354 } else { 355 cpuNode.frame.x = Math.floor(((cpuNode.startTime || 0) - startNS) / pns); 356 } 357 if ((cpuNode.startTime || 0) + (cpuNode.dur || 0) > endNS) { 358 cpuNode.frame.width = frame.width - cpuNode.frame.x; 359 } else { 360 cpuNode.frame.width = Math.ceil( 361 ((cpuNode.startTime || 0) + (cpuNode.dur || 0) - startNS) / pns - cpuNode.frame.x 362 ); 363 } 364 if (cpuNode.frame.width < 1) { 365 cpuNode.frame.width = 1; 366 } 367 } 368 369 static equals(d1: CpuStruct, d2: CpuStruct): boolean { 370 return ( 371 d1 && 372 d2 && 373 d1.cpu === d2.cpu && 374 d1.tid === d2.tid && 375 d1.processId === d2.processId && 376 d1.startTime === d2.startTime && 377 d1.dur === d2.dur 378 ); 379 } 380} 381 382export class WakeupBean { 383 wakeupTime: number | undefined; 384 cpu: number | undefined; 385 process: string | undefined; 386 pid: number | undefined; 387 thread: string | undefined; 388 dur: number | null | undefined; 389 tid: number | undefined; 390 schedulingLatency: number | undefined; 391 ts: number | undefined; 392 schedulingDesc: string | undefined; 393 itid: number | undefined; 394 state: string | undefined; 395 argSetID: number | undefined; 396} 397 398const textPadding = 2; 399