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