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 { 17 BaseStruct, 18 dataFilterHandler, 19 drawFlagLine, 20 drawLines, 21 drawLoading, 22 drawSelection, 23 drawWakeUp, 24 ns2x, 25 PerfRender, 26 Render, 27 RequestMessage, 28} from './ProcedureWorkerCommon.js'; 29import { TraceRow } from '../../component/trace/base/TraceRow.js'; 30import { ColorUtils } from '../../component/trace/base/ColorUtils.js'; 31import { convertJSON } from '../logic-worker/ProcedureLogicWorkerCommon.js'; 32 33export class CpuStateRender extends PerfRender { 34 renderMainThread( 35 req: { 36 useCache: boolean; 37 context: CanvasRenderingContext2D; 38 type: string; 39 cpu: number; 40 }, 41 row: TraceRow<CpuStateStruct> 42 ) { 43 let list = row.dataList; 44 let filter = row.dataListCache; 45 let chartColor = ColorUtils.colorForTid(req.cpu); 46 dataFilterHandler(list, filter, { 47 startKey: 'startTs', 48 durKey: 'dur', 49 startNS: TraceRow.range?.startNS ?? 0, 50 endNS: TraceRow.range?.endNS ?? 0, 51 totalNS: TraceRow.range?.totalNS ?? 0, 52 frame: row.frame, 53 paddingTop: 5, 54 useCache: req.useCache || !(TraceRow.range?.refresh ?? false), 55 }); 56 req.context.beginPath(); 57 req.context.font = '11px sans-serif'; 58 req.context.fillStyle = chartColor; 59 req.context.strokeStyle = chartColor; 60 req.context.globalAlpha = 0.6; 61 let path = new Path2D(); 62 let find = false; 63 let offset = 3; 64 let heights = [4, 12, 21, 30]; 65 for (let re of filter) { 66 re.height = heights[(re as any).value]; 67 CpuStateStruct.draw(req.context, path, re); 68 if (row.isHover) { 69 if (re.frame && row.hoverX >= re.frame.x - offset && row.hoverX <= re.frame.x + re.frame.width + offset) { 70 CpuStateStruct.hoverStateStruct = re; 71 find = true; 72 } 73 } 74 } 75 if (!find && row.isHover) CpuStateStruct.hoverStateStruct = undefined; 76 req.context.fill(path); 77 } 78 79 render(cpuStateReq: RequestMessage, list: Array<any>, filter: Array<any>, dataList2: Array<any>) { 80 if (cpuStateReq.lazyRefresh) { 81 this.cpuState( 82 list, 83 dataList2, 84 cpuStateReq.type!, 85 filter, 86 cpuStateReq.params.cpu, 87 cpuStateReq.startNS, 88 cpuStateReq.endNS, 89 cpuStateReq.totalNS, 90 cpuStateReq.frame, 91 cpuStateReq.useCache || !cpuStateReq.range.refresh 92 ); 93 } else { 94 if (!cpuStateReq.useCache) { 95 this.cpuState( 96 list, 97 dataList2, 98 cpuStateReq.type!, 99 filter, 100 cpuStateReq.params.cpu, 101 cpuStateReq.startNS, 102 cpuStateReq.endNS, 103 cpuStateReq.totalNS, 104 cpuStateReq.frame, 105 false 106 ); 107 } 108 } 109 CpuStateStruct.hoverStateStruct = undefined; 110 if (cpuStateReq.canvas) { 111 cpuStateReq.context.clearRect(0, 0, cpuStateReq.frame.width, cpuStateReq.frame.height); 112 if (filter.length > 0 && !cpuStateReq.range.refresh && !cpuStateReq.useCache && cpuStateReq.lazyRefresh) { 113 drawLoading( 114 cpuStateReq.context, 115 cpuStateReq.startNS, 116 cpuStateReq.endNS, 117 cpuStateReq.totalNS, 118 cpuStateReq.frame, 119 filter[0].startTs, 120 filter[filter.length - 1].startTs + filter[filter.length - 1].dur 121 ); 122 } 123 cpuStateReq.context.beginPath(); 124 drawLines(cpuStateReq.context, cpuStateReq.xs, cpuStateReq.frame.height, cpuStateReq.lineColor); 125 if (cpuStateReq.isHover) { 126 let offset = 3; 127 for (let re of filter) { 128 if ( 129 re.frame && 130 cpuStateReq.hoverX >= re.frame.x - offset && 131 cpuStateReq.hoverX <= re.frame.x + re.frame.width + offset 132 ) { 133 CpuStateStruct.hoverStateStruct = re; 134 break; 135 } 136 } 137 } 138 CpuStateStruct.selectStateStruct = cpuStateReq.params.selectStateStruct; 139 cpuStateReq.context.font = '11px sans-serif'; 140 cpuStateReq.context.fillStyle = cpuStateReq.chartColor; 141 cpuStateReq.context.strokeStyle = cpuStateReq.chartColor; 142 cpuStateReq.context.globalAlpha = 0.6; 143 let path = new Path2D(); 144 for (let re of filter) { 145 CpuStateStruct.draw(cpuStateReq.context, path, re); 146 } 147 cpuStateReq.context.fill(path); 148 drawSelection(cpuStateReq.context, cpuStateReq.params); 149 drawFlagLine( 150 cpuStateReq.context, 151 cpuStateReq.flagMoveInfo, 152 cpuStateReq.flagSelectedInfo, 153 cpuStateReq.startNS, 154 cpuStateReq.endNS, 155 cpuStateReq.totalNS, 156 cpuStateReq.frame, 157 cpuStateReq.slicesTime 158 ); 159 } 160 let msg = { 161 id: cpuStateReq.id, 162 type: cpuStateReq.type, 163 results: cpuStateReq.canvas ? undefined : filter, 164 hover: CpuStateStruct.hoverStateStruct, 165 }; 166 self.postMessage(msg); 167 } 168 169 cpuState( 170 arr: Array<any>, 171 arr2: Array<any>, 172 type: string, 173 cpuStateRes: Array<any>, 174 cpu: number, 175 startNS: number, 176 endNS: number, 177 totalNS: number, 178 frame: any, 179 use: boolean 180 ) { 181 if (use && cpuStateRes.length > 0) { 182 for (let i = 0, len = cpuStateRes.length; i < len; i++) { 183 if ( 184 (cpuStateRes[i].startTs || 0) + (cpuStateRes[i].dur || 0) >= startNS && 185 (cpuStateRes[i].startTs || 0) <= endNS 186 ) { 187 CpuStateStruct.setFrame(cpuStateRes[i], 5, startNS, endNS, totalNS, frame); 188 } else { 189 cpuStateRes[i].frame = null; 190 } 191 } 192 return; 193 } 194 cpuStateRes.length = 0; 195 if (arr) { 196 let list: Array<any> = arr2; 197 cpuStateRes.length = 0; 198 let pns = (endNS - startNS) / frame.width; //每个像素多少ns 199 let y = frame.y + 5; 200 let frameHeight = frame.height - 10; 201 let left = 0, 202 right = 0; 203 for (let i = 0, j = list.length - 1, ib = true, jb = true; i < list.length, j >= 0; i++, j--) { 204 if (list[j].startTs <= endNS && jb) { 205 right = j; 206 jb = false; 207 } 208 if (list[i].startTs + list[i].dur >= startNS && ib) { 209 left = i; 210 ib = false; 211 } 212 if (!ib && !jb) { 213 break; 214 } 215 } 216 let slice = list.slice(left, right + 1); 217 let sum = 0; 218 for (let i = 0; i < slice.length; i++) { 219 if (!slice[i].frame) { 220 slice[i].frame = {}; 221 slice[i].frame.y = y; 222 slice[i].frame.height = frameHeight; 223 } 224 if (slice[i].dur >= pns) { 225 slice[i].v = true; 226 CpuStateStruct.setFrame(slice[i], 5, startNS, endNS, totalNS, frame); 227 } else { 228 if (i > 0) { 229 let c = slice[i].startTs - slice[i - 1].startTs - slice[i - 1].dur; 230 if (c < pns && sum < pns) { 231 sum += c + slice[i - 1].dur; 232 slice[i].v = false; 233 } else { 234 slice[i].v = true; 235 CpuStateStruct.setFrame(slice[i], 5, startNS, endNS, totalNS, frame); 236 sum = 0; 237 } 238 } 239 } 240 } 241 cpuStateRes.push(...slice.filter((it) => it.v)); 242 } 243 } 244} 245 246export class CpuStateStruct extends BaseStruct { 247 static hoverStateStruct: CpuStateStruct | undefined; 248 static selectStateStruct: CpuStateStruct | undefined; 249 dur: number | undefined; 250 value: string | undefined; 251 startTs: number | undefined; 252 height: number | undefined; 253 cpu: number | undefined; 254 255 static draw(ctx: CanvasRenderingContext2D, path: Path2D, data: CpuStateStruct) { 256 if (data.frame) { 257 if (data === CpuStateStruct.hoverStateStruct || data === CpuStateStruct.selectStateStruct) { 258 path.rect(data.frame.x, 35 - (data.height || 0), data.frame.width, data.height || 0); 259 ctx.lineWidth = 1; 260 ctx.globalAlpha = 1.0; 261 ctx.beginPath(); 262 ctx.arc(data.frame.x, 35 - (data.height || 0), 3, 0, 2 * Math.PI, true); 263 ctx.stroke(); 264 ctx.beginPath(); 265 ctx.moveTo(data.frame.x + 3, 35 - (data.height || 0)); 266 ctx.lineWidth = 3; 267 ctx.lineTo(data.frame.x + data.frame.width, 35 - (data.height || 0)); 268 ctx.stroke(); 269 ctx.lineWidth = 1; 270 ctx.globalAlpha = 0.6; 271 ctx.fillRect(data.frame.x, 35 - (data.height || 0), data.frame.width, data.height || 0); 272 } else { 273 ctx.globalAlpha = 0.6; 274 path.rect(data.frame.x, 35 - (data.height || 0), data.frame.width, data.height || 0); 275 } 276 } 277 } 278 279 static setCpuFrame(cpuStateNode: any, pns: number, startNS: number, endNS: number, frame: any) { 280 if ((cpuStateNode.startTime || 0) < startNS) { 281 cpuStateNode.frame.x = 0; 282 } else { 283 cpuStateNode.frame.x = Math.floor(((cpuStateNode.startTs || 0) - startNS) / pns); 284 } 285 if ((cpuStateNode.startTime || 0) + (cpuStateNode.dur || 0) > endNS) { 286 cpuStateNode.frame.width = frame.width - cpuStateNode.frame.x; 287 } else { 288 cpuStateNode.frame.width = Math.ceil( 289 ((cpuStateNode.startTs || 0) + (cpuStateNode.dur || 0) - startNS) / pns - cpuStateNode.frame.x 290 ); 291 } 292 if (cpuStateNode.frame.width < 1) { 293 cpuStateNode.frame.width = 1; 294 } 295 } 296 static setFrame(cpuStateNode: any, padding: number, startNS: number, endNS: number, totalNS: number, frame: any) { 297 let x1: number, x2: number; 298 if ((cpuStateNode.startTs || 0) < startNS) { 299 x1 = 0; 300 } else { 301 x1 = ns2x(cpuStateNode.startTs || 0, startNS, endNS, totalNS, frame); 302 } 303 if ((cpuStateNode.startTs || 0) + (cpuStateNode.dur || 0) > endNS) { 304 x2 = frame.width; 305 } else { 306 x2 = ns2x((cpuStateNode.startTs || 0) + (cpuStateNode.dur || 0), startNS, endNS, totalNS, frame); 307 } 308 let cpuStateGetV: number = x2 - x1 <= 1 ? 1 : x2 - x1; 309 if (!cpuStateNode.frame) { 310 cpuStateNode.frame = {}; 311 } 312 cpuStateNode.frame.x = Math.ceil(x1); 313 cpuStateNode.frame.y = frame.y + padding; 314 cpuStateNode.frame.width = Math.floor(cpuStateGetV); 315 cpuStateNode.frame.height = cpuStateNode.height; 316 } 317} 318