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