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, 23 isFrameContainPoint, 24 PerfRender, 25 RequestMessage, 26} from './ProcedureWorkerCommon.js'; 27import { TraceRow } from '../../component/trace/base/TraceRow.js'; 28 29export class EnergyAnomalyRender extends PerfRender { 30 renderMainThread( 31 req: { 32 useCache: boolean; 33 context: CanvasRenderingContext2D; 34 type: string; 35 appName: string; 36 canvasWidth: number; 37 }, 38 row: TraceRow<EnergyAnomalyStruct> 39 ) { 40 let list = row.dataList; 41 let filter = row.dataListCache; 42 anomaly( 43 list, 44 filter, 45 TraceRow.range!.startNS, 46 TraceRow.range!.endNS, 47 TraceRow.range!.totalNS, 48 row.frame, 49 req.appName, 50 req.useCache || !TraceRow.range!.refresh 51 ); 52 req.context.beginPath(); 53 let find = false; 54 let spApplication = document.getElementsByTagName('sp-application')[0]; 55 let isDark = spApplication.hasAttribute('dark'); 56 drawLegend(req, isDark); 57 for (let re of filter) { 58 EnergyAnomalyStruct.draw(req.context, re); 59 if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { 60 EnergyAnomalyStruct.hoverEnergyAnomalyStruct = re; 61 find = true; 62 } 63 } 64 if (!find && row.isHover) EnergyAnomalyStruct.hoverEnergyAnomalyStruct = undefined; 65 req.context.fillStyle = ColorUtils.FUNC_COLOR[0]; 66 req.context.strokeStyle = ColorUtils.FUNC_COLOR[0]; 67 req.context.closePath(); 68 } 69 70 render(energyAnomalyRequest: RequestMessage, list: Array<any>, filter: Array<any>, dataList2: Array<any>) { 71 if (energyAnomalyRequest.lazyRefresh) { 72 anomaly( 73 list, 74 filter, 75 energyAnomalyRequest.startNS, 76 energyAnomalyRequest.endNS, 77 energyAnomalyRequest.totalNS, 78 energyAnomalyRequest.frame, 79 energyAnomalyRequest.params.appName, 80 energyAnomalyRequest.useCache || !energyAnomalyRequest.range.refresh 81 ); 82 } else { 83 if (!energyAnomalyRequest.useCache) { 84 anomaly( 85 list, 86 filter, 87 energyAnomalyRequest.startNS, 88 energyAnomalyRequest.endNS, 89 energyAnomalyRequest.totalNS, 90 energyAnomalyRequest.frame, 91 energyAnomalyRequest.params.appName, 92 false 93 ); 94 } 95 } 96 if (energyAnomalyRequest.canvas) { 97 energyAnomalyRequest.context.clearRect( 98 0, 99 0, 100 energyAnomalyRequest.canvas.width, 101 energyAnomalyRequest.canvas.height 102 ); 103 let energyAnomlyArr = filter; 104 if ( 105 energyAnomlyArr.length > 0 && 106 !energyAnomalyRequest.range.refresh && 107 !energyAnomalyRequest.useCache && 108 energyAnomalyRequest.lazyRefresh 109 ) { 110 drawLoading( 111 energyAnomalyRequest.context, 112 energyAnomalyRequest.startNS, 113 energyAnomalyRequest.endNS, 114 energyAnomalyRequest.totalNS, 115 energyAnomalyRequest.frame, 116 energyAnomlyArr[0].startNS, 117 energyAnomlyArr[energyAnomlyArr.length - 1].startNS 118 ); 119 } 120 drawLines( 121 energyAnomalyRequest.context, 122 energyAnomalyRequest.xs, 123 energyAnomalyRequest.frame.height, 124 energyAnomalyRequest.lineColor 125 ); 126 energyAnomalyRequest.context.stroke(); 127 energyAnomalyRequest.context.beginPath(); 128 EnergyAnomalyStruct.hoverEnergyAnomalyStruct = undefined; 129 if (energyAnomalyRequest.isHover) { 130 let offset = 3; 131 for (let re of filter) { 132 if ( 133 re.frame && 134 energyAnomalyRequest.hoverX >= re.frame.x - offset && 135 energyAnomalyRequest.hoverX <= re.frame.x + re.frame.width + offset 136 ) { 137 EnergyAnomalyStruct.hoverEnergyAnomalyStruct = re; 138 break; 139 } 140 } 141 } else { 142 EnergyAnomalyStruct.hoverEnergyAnomalyStruct = energyAnomalyRequest.params.hoverStruct; 143 } 144 EnergyAnomalyStruct.selectEnergyAnomalyStruct = energyAnomalyRequest.params.selectEnergyAnomalyStruct; 145 energyAnomalyRequest.context.fillStyle = ColorUtils.FUNC_COLOR[0]; 146 energyAnomalyRequest.context.strokeStyle = ColorUtils.FUNC_COLOR[0]; 147 for (let re of filter) { 148 EnergyAnomalyStruct.draw(energyAnomalyRequest.context, re); 149 } 150 drawLegend(energyAnomalyRequest); 151 drawSelection(energyAnomalyRequest.context, energyAnomalyRequest.params); 152 energyAnomalyRequest.context.closePath(); 153 drawFlagLine( 154 energyAnomalyRequest.context, 155 energyAnomalyRequest.flagMoveInfo, 156 energyAnomalyRequest.flagSelectedInfo, 157 energyAnomalyRequest.startNS, 158 energyAnomalyRequest.endNS, 159 energyAnomalyRequest.totalNS, 160 energyAnomalyRequest.frame, 161 energyAnomalyRequest.slicesTime 162 ); 163 } 164 // @ts-ignore 165 self.postMessage({ 166 id: energyAnomalyRequest.id, 167 type: energyAnomalyRequest.type, 168 results: energyAnomalyRequest.canvas ? undefined : filter, 169 hover: EnergyAnomalyStruct.hoverEnergyAnomalyStruct, 170 }); 171 } 172} 173 174export function drawLegend(req: any, isDark?: boolean) { 175 req.context.font = '12px Arial'; 176 let text = req.context.measureText('System Abnormality'); 177 req.context.fillStyle = '#E64566'; 178 req.context.strokeStyle = '#E64566'; 179 let textColor = isDark ? '#FFFFFF' : '#333'; 180 let canvasEndX = req.context.canvas.clientWidth - EnergyAnomalyStruct.OFFSET_WIDTH; 181 let rectPadding: number; 182 let textPadding: number; 183 let textMargin: number; 184 let currentTextWidth: number; 185 let lastTextMargin: number; 186 rectPadding = 280; 187 textPadding = 270; 188 textMargin = 250; 189 currentTextWidth = canvasEndX - textMargin + text.width; 190 lastTextMargin = currentTextWidth + 12; 191 req!.context.fillRect(canvasEndX - rectPadding, 12, 8, 8); 192 req.context.globalAlpha = 1; 193 req.context.fillStyle = textColor; 194 req.context.textBaseline = 'middle'; 195 req.context.fillText('System Abnormality', canvasEndX - textPadding, 18); 196 req.context.fillStyle = '#FFC880'; 197 req.context.strokeStyle = '#FFC880'; 198 req.context.fillRect(currentTextWidth, 12, 8, 8); 199 req.context.globalAlpha = 1; 200 req.context.fillStyle = textColor; 201 req.context.textBaseline = 'middle'; 202 req.context.fillText('Application Abnormality', lastTextMargin, 18); 203 req.context.fillStyle = '#333'; 204} 205 206export function anomaly( 207 arr: Array<any>, 208 res: Array<any>, 209 startNS: number, 210 endNS: number, 211 totalNS: number, 212 frame: any, 213 appName: string | undefined, 214 use: boolean 215) { 216 if (use && res.length > 0) { 217 let pns = (endNS - startNS) / frame.width; 218 let y = frame.y; 219 for (let i = 0; i < res.length; i++) { 220 let it = res[i]; 221 if ((it.startNS || 0) > startNS && (it.startNS || 0) < endNS) { 222 if (!it.frame) { 223 it.frame = {}; 224 it.frame.y = y; 225 } 226 it.frame.height = it.height; 227 EnergyAnomalyStruct.setAnomalyFrame(it, pns, startNS, endNS, frame); 228 } else { 229 it.frame = null; 230 } 231 } 232 return; 233 } 234 235 res.length = 0; 236 if (arr) { 237 let y = frame.y; 238 let pns = (endNS - startNS) / frame.width; 239 for (let index = 0; index < arr.length; index++) { 240 let item = arr[index]; 241 if (!item.frame) { 242 item.frame = {}; 243 item.frame.y = y; 244 } 245 item.frame.height = item.height; 246 if (item.startNS + 50000 > (startNS || 0) && (item.startNS || 0) < (endNS || 0)) { 247 EnergyAnomalyStruct.setAnomalyFrame(item, pns, startNS || 0, endNS || 0, frame); 248 if (item.appKey === 'APPNAME' && item.Value.split(',').indexOf(appName) >= 0) { 249 res.push(item); 250 } 251 if (item.appKey != 'APPNAME') { 252 res.push(item); 253 } 254 } 255 } 256 } 257} 258 259export class EnergyAnomalyStruct extends BaseStruct { 260 static hoverEnergyAnomalyStruct: EnergyAnomalyStruct | undefined; 261 static selectEnergyAnomalyStruct: EnergyAnomalyStruct | undefined; 262 static SYSTEM_EXCEPTION = new Set([ 263 'ANOMALY_SCREEN_OFF_ENERGY', 264 'ANOMALY_ALARM_WAKEUP', 265 'ANOMALY_KERNEL_WAKELOCK', 266 'ANOMALY_CPU_HIGH_FREQUENCY', 267 'ANOMALY_WAKEUP', 268 ]); 269 static OFFSET_WIDTH: number = 266; 270 type: number | undefined; 271 startNS: number | undefined; 272 height: number | undefined; 273 eventName: string | undefined; 274 275 static draw(ctx: CanvasRenderingContext2D, data: EnergyAnomalyStruct) { 276 if (data.frame) { 277 EnergyAnomalyStruct.drawRoundRectPath(ctx, data.frame.x - 7, 20 - 7, 12, data); 278 } 279 } 280 281 static drawRoundRectPath( 282 ctx: CanvasRenderingContext2D, 283 x: number, 284 y: number, 285 radius: number, 286 data: EnergyAnomalyStruct 287 ) { 288 ctx.beginPath(); 289 ctx.arc(x + 7, y + 22, radius, 0, Math.PI * 2); 290 ctx.closePath(); 291 let color = ''; 292 if (EnergyAnomalyStruct.SYSTEM_EXCEPTION.has(<string>data.eventName)) { 293 color = '#E64566'; 294 } else { 295 color = '#FFC880'; 296 } 297 // 填充背景颜色 298 ctx.fillStyle = color; 299 ctx.fill(); 300 ctx.stroke(); 301 // 填充文字颜色 302 ctx.font = '12px Arial'; 303 ctx.fillStyle = ColorUtils.GREY_COLOR; 304 ctx.textAlign = 'center'; 305 ctx.fillText('E', x + 7, y + 23); 306 } 307 308 static setAnomalyFrame(node: any, pns: number, startNS: number, endNS: number, frame: any) { 309 if ((node.startNS || 0) < startNS) { 310 node.frame.x = 0; 311 } else { 312 node.frame.x = Math.floor(((node.startNS || 0) - startNS) / pns); 313 } 314 if ((node.startNS || 0) > endNS) { 315 node.frame.width = frame.width - node.frame.x; 316 } else { 317 node.frame.width = Math.ceil(((node.startNS || 0) - startNS) / pns - node.frame.x); 318 } 319 if (node.frame.width < 1) { 320 node.frame.width = 1; 321 } 322 } 323} 324