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 drawFlagLine, 20 drawLines, 21 drawLoading, 22 drawSelection, drawWakeUp, Render, 23 RequestMessage 24} from "./ProcedureWorkerCommon.js"; 25 26export class EmptyRender extends Render{ 27 render(req: RequestMessage, list: Array<any>, filter: Array<any>){ 28 if (req.canvas) { 29 req.context.clearRect(0, 0, req.frame.width, req.frame.height); 30 req.context.beginPath(); 31 drawLines(req.context, req.xs, req.frame.height, req.lineColor) 32 drawSelection(req.context, req.params); 33 req.context.closePath(); 34 drawFlagLine(req.context, req.flagMoveInfo, req.flagSelectedInfo, req.startNS, req.endNS, req.totalNS, req.frame, req.slicesTime); 35 } 36 // @ts-ignore 37 self.postMessage({ 38 id: req.id, 39 type: req.type, 40 results: req.canvas ? undefined : filter, 41 hover: null 42 }); 43 } 44} 45 46export class CpuRender { 47 render(req:RequestMessage,list:Array<any>,filter:Array<any>){ 48 if (req.lazyRefresh) { 49 this.cpu(list, filter, req.startNS, req.endNS, req.totalNS, req.frame, req.useCache || !req.range.refresh); 50 } else { 51 if (!req.useCache) { 52 this.cpu(list, filter, req.startNS, req.endNS, req.totalNS, req.frame, false); 53 } 54 } 55 if (req.canvas) { 56 req.context.clearRect(0, 0, req.frame.width, req.frame.height); 57 let arr = filter; 58 if (arr.length > 0 && !req.range.refresh && !req.useCache && req.lazyRefresh) { 59 drawLoading(req.context, req.startNS, req.endNS, req.totalNS, req.frame, arr[0].startTime, arr[arr.length - 1].startTime + arr[arr.length - 1].dur) 60 } 61 req.context.beginPath(); 62 drawLines(req.context, req.xs, req.frame.height, req.lineColor); 63 CpuStruct.hoverCpuStruct = undefined; 64 if (req.isHover) { 65 for (let re of filter) { 66 if (re.frame && req.hoverX >= re.frame.x && req.hoverX <= re.frame.x + re.frame.width && req.hoverY >= re.frame.y && req.hoverY <= re.frame.y + re.frame.height) { 67 CpuStruct.hoverCpuStruct = re; 68 break; 69 } 70 } 71 } else { 72 CpuStruct.hoverCpuStruct = req.params.hoverCpuStruct; 73 } 74 CpuStruct.selectCpuStruct = req.params.selectCpuStruct; 75 req.context.font = "11px sans-serif"; 76 for (let re of filter) { 77 CpuStruct.draw(req.context, re); 78 } 79 drawSelection(req.context, req.params); 80 req.context.closePath(); 81 drawFlagLine(req.context, req.flagMoveInfo, req.flagSelectedInfo, req.startNS, req.endNS, req.totalNS, req.frame, req.slicesTime); 82 let currentCpu = parseInt(req.type!.replace("cpu-data-", "")); 83 drawWakeUp(req.context, req.wakeupBean, req.startNS, req.endNS, req.totalNS, req.frame, req.type == `cpu-data-${CpuStruct.selectCpuStruct?.cpu || 0}` ? CpuStruct.selectCpuStruct : undefined, currentCpu); 84 } 85 // @ts-ignore 86 self.postMessage({ 87 id: req.id, 88 type: req.type, 89 results: req.canvas ? undefined : filter, 90 hover: CpuStruct.hoverCpuStruct 91 }); 92 } 93 94 cpu(list: Array<any>, res: Array<any>, startNS: number, endNS: number, totalNS: number, frame: any,use:boolean) { 95 if(use && res.length > 0){ 96 let pns = (endNS - startNS) / frame.width; 97 let y = frame.y + 5; 98 let height = frame.height - 10; 99 for (let i = 0, len = res.length; i < len; i++) { 100 let it = res[i]; 101 if ((it.startTime || 0) + (it.dur || 0) > startNS && (it.startTime || 0) < endNS) { 102 if (!res[i].frame) { 103 res[i].frame = {}; 104 res[i].frame.y = y; 105 res[i].frame.height = height; 106 } 107 CpuStruct.setCpuFrame(res[i], pns, startNS, endNS, frame) 108 }else{ 109 res[i].frame = null; 110 } 111 } 112 return; 113 } 114 res.length = 0; 115 if (list) { 116 let pns = (endNS - startNS) / frame.width; 117 let y = frame.y + 5; 118 let height = frame.height - 10; 119 for (let i = 0, len = list.length; i < len; i++) { 120 let it = list[i]; 121 if ((it.startTime || 0) + (it.dur || 0) > startNS && (it.startTime || 0) < endNS) { 122 if (!it.frame) { 123 it.frame = {}; 124 it.frame.y = y; 125 it.frame.height = height; 126 } 127 CpuStruct.setCpuFrame(it, pns, startNS, endNS, frame) 128 if (i > 0 && ((list[i - 1].frame?.x || 0) == (it.frame?.x || 0) && ((list[i - 1].frame?.width || 0) == (it.frame?.width || 0)))) { 129 130 } else { 131 res.push(it) 132 } 133 }else{ 134 it.frame = null; 135 } 136 } 137 } 138 } 139} 140 141 142let dec = new TextDecoder(); 143function rtCpu(buf: ArrayBuffer | null | undefined, res: Array<any>, startNS: number, endNS: number, totalNS: number, frame: any) { 144 if (buf) { 145 res.length=0; 146 let pns = (endNS - startNS) / frame.width; 147 let y = frame.y + 5; 148 let height = frame.height - 10; 149 let str = dec.decode(buf); 150 str = str.substring(str.indexOf("\n") + 1); 151 let parse = JSON.parse(str); 152 let columns = parse.columns; 153 let values = parse.values; 154 for (let i = 0; i < values.length; i++) { 155 let obj: any = {} 156 for (let j = 0; j < columns.length; j++) { 157 obj[columns[j]] = values[i][j] 158 } 159 obj.frame = { 160 // x: obj.x1, 161 y: frame.y + 5, 162 // width: obj.x2 - obj.x1 > 0 ? obj.x2 - obj.x1 : 1, 163 height: frame.height - 10, 164 } 165 CpuStruct.setCpuFrame(obj, pns, startNS, endNS, frame) 166 res.push(obj); 167 } 168 }else{ 169 let pns = (endNS - startNS) / frame.width; 170 res.forEach(it=>{ 171 CpuStruct.setCpuFrame(it, pns, startNS, endNS, frame) 172 }) 173 } 174} 175 176export class CpuStruct extends BaseStruct { 177 static cpuCount: number = 1 //最大cpu数量 178 static hoverCpuStruct: CpuStruct | undefined; 179 static selectCpuStruct: CpuStruct | undefined; 180 cpu: number | undefined 181 dur: number | undefined 182 end_state: string | undefined 183 id: number | undefined 184 name: string | undefined 185 priority: number | undefined 186 processCmdLine: string | undefined 187 processId: number | undefined 188 processName: string | undefined 189 schedId: number | undefined 190 startTime: number | undefined 191 tid: number | undefined 192 type: string | undefined 193 194 static draw(ctx: CanvasRenderingContext2D, data: CpuStruct) { 195 if (data.frame) { 196 let width = data.frame.width || 0; 197 if (data.tid === CpuStruct.hoverCpuStruct?.tid || !CpuStruct.hoverCpuStruct) { 198 ctx.globalAlpha = 1 199 ctx.fillStyle = ColorUtils.colorForTid((data.processId || 0) > 0 ? (data.processId || 0) : (data.tid || 0)) 200 } else if(data.processId === CpuStruct.hoverCpuStruct?.processId ){ 201 ctx.globalAlpha = 0.6 202 ctx.fillStyle = ColorUtils.colorForTid((data.processId || 0) > 0 ? (data.processId || 0) : (data.tid || 0)) 203 } else { 204 ctx.globalAlpha = 1 205 ctx.fillStyle = "#e0e0e0" 206 } 207 ctx.fillRect(data.frame.x, data.frame.y, width, data.frame.height) 208 ctx.globalAlpha = 1 209 if (width > textPadding * 2) { 210 let process = `${(data.processName || "Process")} [${data.processId}]` 211 let thread = `${data.name || "Thread"} [${data.tid}] [Prio:${data.priority||0}]` 212 let processMeasure = ctx.measureText(process); 213 let threadMeasure = ctx.measureText(thread); 214 let processCharWidth = Math.round(processMeasure.width / process.length) 215 let threadCharWidth = Math.round(threadMeasure.width / thread.length) 216 ctx.fillStyle = "#ffffff" 217 let y = data.frame.height / 2 + data.frame.y; 218 if (processMeasure.width < width - textPadding * 2) { 219 let x1 = Math.floor(width / 2 - processMeasure.width / 2 + data.frame.x + textPadding) 220 ctx.textBaseline = "bottom"; 221 ctx.fillText(process, x1, y, width - textPadding * 2) 222 } else if (width - textPadding * 2 > processCharWidth * 4) { 223 let chatNum = (width - textPadding * 2) / processCharWidth; 224 let x1 = data.frame.x + textPadding 225 ctx.textBaseline = "bottom"; 226 ctx.fillText(process.substring(0, chatNum - 4) + '...', x1, y, width - textPadding * 2) 227 } 228 ctx.fillStyle = "#ffffff" 229 ctx.font = "9px sans-serif"; 230 if (threadMeasure.width < width - textPadding * 2) { 231 ctx.textBaseline = "top"; 232 let x2 = Math.floor(width / 2 - threadMeasure.width / 2 + data.frame.x + textPadding) 233 ctx.fillText(thread, x2, y + 2, width - textPadding * 2) 234 } else if (width - textPadding * 2 > threadCharWidth * 4) { 235 let chatNum = (width - textPadding * 2) / threadCharWidth; 236 let x1 = data.frame.x + textPadding 237 ctx.textBaseline = "top"; 238 ctx.fillText(thread.substring(0, chatNum - 4) + '...', x1, y + 2, width - textPadding * 2) 239 } 240 } 241 if (CpuStruct.selectCpuStruct && CpuStruct.equals(CpuStruct.selectCpuStruct, data)) { 242 ctx.strokeStyle = '#232c5d' 243 ctx.lineWidth = 2 244 ctx.strokeRect(data.frame.x, data.frame.y, width - 2, data.frame.height) 245 } 246 } 247 } 248 249 static setCpuFrame(node: any, pns: number, startNS: number, endNS: number, frame: any) { 250 if ((node.startTime || 0) < startNS) { 251 node.frame.x = 0; 252 } else { 253 node.frame.x = Math.floor(((node.startTime || 0) - startNS) / pns); 254 } 255 if ((node.startTime || 0) + (node.dur || 0) > endNS) { 256 node.frame.width = frame.width - node.frame.x; 257 } else { 258 node.frame.width = Math.ceil(((node.startTime || 0) + (node.dur || 0) - startNS) / pns - node.frame.x); 259 } 260 if (node.frame.width < 1) { 261 node.frame.width = 1; 262 } 263 } 264 265 static equals(d1: CpuStruct, d2: CpuStruct): boolean { 266 if (d1 && d2 && d1.cpu == d2.cpu && 267 d1.tid == d2.tid && 268 d1.processId == d2.processId && 269 d1.startTime == d2.startTime && 270 d1.dur == d2.dur) { 271 return true; 272 } else { 273 return false; 274 } 275 } 276} 277 278export class WakeupBean { 279 wakeupTime: number | undefined 280 cpu: number | undefined 281 process: string | undefined 282 pid: number | undefined 283 thread: string | undefined 284 tid: number | undefined 285 schedulingLatency: number | undefined 286 schedulingDesc: string | undefined 287 288} 289 290const textPadding = 2;