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 { BaseStruct, drawLoadingFrame, isFrameContainPoint, ns2x, Rect, Render } from './ProcedureWorkerCommon'; 17import { TraceRow } from '../../component/trace/base/TraceRow'; 18import { ColorUtils } from '../../component/trace/base/ColorUtils'; 19import { THREAD_ENERGY, THREAD_LOAD } from '../../component/chart/SpXpowerChart'; 20import { SpSystemTrace } from '../../component/SpSystemTrace'; 21 22export class XpowerThreadInfoRender extends Render { 23 renderMainThread( 24 xpowerReq: { 25 context: CanvasRenderingContext2D; 26 useCache: boolean; 27 type: string; 28 }, 29 row: TraceRow<XpowerThreadInfoStruct> 30 ): void { 31 let xpowerThreadInfoList = row.dataList; 32 let xpowerThreadInfoFilter = row.dataListCache; 33 threadInfo( 34 xpowerThreadInfoList, 35 xpowerThreadInfoFilter, 36 TraceRow.range!.startNS, 37 TraceRow.range!.endNS, 38 TraceRow.range!.totalNS, 39 row.frame, 40 xpowerReq.useCache || !TraceRow.range!.refresh 41 ); 42 drawLoadingFrame(xpowerReq.context, xpowerThreadInfoFilter, row); 43 xpowerReq.context.beginPath(); 44 let find = false; 45 let maxValue = 0; 46 let maxValueStr = ''; 47 if (xpowerReq.type === THREAD_ENERGY) { 48 maxValue = XpowerThreadInfoStruct.energyMaxValue; 49 maxValueStr = String(maxValue) + ' mAh'; 50 } else if (xpowerReq.type === THREAD_LOAD) { 51 maxValue = XpowerThreadInfoStruct.loadMaxValue; 52 maxValueStr = String(maxValue) + ' %'; 53 } 54 for (let i = 0; i < xpowerThreadInfoFilter.length; i++) { 55 XpowerThreadInfoStruct.draw(xpowerReq, xpowerThreadInfoFilter[i], maxValue, row); 56 if ( 57 row.isHover && 58 xpowerThreadInfoFilter[i].frame && 59 isFrameContainPoint(xpowerThreadInfoFilter[i].frame!, row.hoverX, row.hoverY) 60 ) { 61 XpowerThreadInfoStruct.hoverXpowerStruct = xpowerThreadInfoFilter[i]; 62 XpowerThreadInfoStruct.drawStroke(xpowerReq, xpowerThreadInfoFilter[i], row); 63 find = true; 64 } 65 } 66 if (!find) { 67 XpowerThreadInfoStruct.hoverXpowerStruct = undefined; 68 } 69 drawMaxValue(xpowerReq, maxValueStr); 70 } 71} 72 73export function threadInfo( 74 list: Array<XpowerThreadInfoStruct>, 75 res: Array<XpowerThreadInfoStruct>, 76 startNS: number, 77 endNS: number, 78 totalNS: number, 79 frame: Rect, 80 use: boolean 81): void { 82 list.length = 0; 83 if (use && res.length > 0) { 84 for (let index = 0; index < res.length; index++) { 85 let item = res[index]; 86 XpowerThreadInfoStruct.setThreadInfoFrame(item, 5, startNS || 0, endNS || 0, totalNS || 0, frame); 87 } 88 } 89} 90 91export function drawMaxValue( 92 xpowerReq: { 93 context: CanvasRenderingContext2D; 94 useCache: boolean; 95 type: string; 96 }, 97 maxValueStr: string 98): void { 99 xpowerReq.context.closePath(); 100 let textMetrics = xpowerReq.context.measureText(maxValueStr); 101 xpowerReq.context.globalAlpha = 0.8; 102 xpowerReq.context.fillStyle = '#f0f0f0'; 103 xpowerReq.context.fillRect(0, 5, textMetrics.width + 8, 18); 104 xpowerReq.context.globalAlpha = 1; 105 xpowerReq.context.fillStyle = '#333'; 106 xpowerReq.context.textBaseline = 'middle'; 107 xpowerReq.context.fillText(maxValueStr, 4, 5 + 9); 108} 109 110export function XpowerThreadInfoStructOnClick( 111 clickRowType: string, 112 sp: SpSystemTrace, 113 entry?: XpowerThreadInfoStruct 114): Promise<unknown> { 115 return new Promise((resolve, reject) => { 116 if (clickRowType === TraceRow.ROW_TYPE_XPOWER_THREAD_INFO && (XpowerThreadInfoStruct.hoverXpowerStruct || entry)) { 117 XpowerThreadInfoStruct.selectXpowerStruct = entry || XpowerThreadInfoStruct.hoverXpowerStruct; 118 let startNs = XpowerThreadInfoStruct.selectXpowerStruct!.startNS; 119 let map = new Map(); 120 if (XpowerThreadInfoStruct.selectXpowerStruct?.valueType === THREAD_ENERGY) { 121 map = XpowerThreadInfoStruct.threadEnergyStructMap; 122 } else if (XpowerThreadInfoStruct.selectXpowerStruct?.valueType === THREAD_LOAD) { 123 map = XpowerThreadInfoStruct.threadLoadStructMap; 124 } 125 sp.traceSheetEL?.displayXpowerThreadInfoData(map.get(startNs) || []); 126 sp.timerShaftEL?.modifyFlagList(undefined); 127 reject(new Error()); 128 } else { 129 resolve(null); 130 } 131 }); 132} 133 134export class XpowerThreadInfoStruct extends BaseStruct { 135 static energyMaxValue: number = 0; 136 static loadMaxValue: number = 0; 137 static hoverXpowerStruct: XpowerThreadInfoStruct | undefined; 138 static selectXpowerStruct: XpowerThreadInfoStruct | undefined; 139 static histogramHeightMap = new Map<string, number>(); 140 static threadEnergyStructMap = new Map<number, Array<XpowerThreadInfoStruct>>(); 141 static threadLoadStructMap = new Map<number, Array<XpowerThreadInfoStruct>>(); 142 static rowHeight: number = 200; 143 static flagTime: number = -1; 144 static flagType: string = ''; 145 static height: number = -1; 146 static drawY: number = 0; 147 value: number = 0; 148 startNS: number = 0; 149 startMS: number = 0; 150 dur: number = 0; 151 threadTime: number = 0; 152 threadName: string = ''; 153 threadNameId: number = -1; 154 valueType: string = ''; 155 156 static setThreadInfoFrame( 157 powerNode: XpowerThreadInfoStruct, 158 padding: number, 159 startNS: number, 160 endNS: number, 161 totalNS: number, 162 frame: Rect 163 ): void { 164 let startPointX: number; 165 let endPointX: number; 166 //@ts-ignore 167 if ((powerNode.startNS || 0) < startNS) { 168 startPointX = 0; 169 } else { 170 startPointX = ns2x(powerNode.startNS || 0, startNS, endNS, totalNS, frame); 171 } 172 //@ts-ignore 173 if (powerNode.startNS + 3000000000 > endNS) { 174 //@ts-ignore 175 endPointX = frame.width; 176 } else { 177 //@ts-ignore 178 endPointX = ns2x(powerNode.startNS + 3000000000, startNS, endNS, totalNS, frame); 179 } 180 let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX; 181 //@ts-ignore 182 if (!powerNode.frame) { 183 //@ts-ignore 184 powerNode.frame = {}; 185 } 186 //@ts-ignore 187 powerNode.frame.x = Math.floor(startPointX); 188 //@ts-ignore 189 powerNode.frame.y = frame.y + padding; 190 //@ts-ignore 191 powerNode.frame.width = Math.ceil(frameWidth); 192 //@ts-ignore 193 powerNode.frame.height = Math.floor(frame.height - padding * 2); 194 } 195 196 static draw( 197 req: { useCache: boolean; context: CanvasRenderingContext2D }, 198 data: XpowerThreadInfoStruct, 199 maxValue: number, 200 row: TraceRow<XpowerThreadInfoStruct> 201 ): void { 202 if (data.frame) { 203 req!.context.globalAlpha = 0.8; 204 if (data.startNS !== XpowerThreadInfoStruct.flagTime) { 205 this.height = -1; 206 if (XpowerThreadInfoStruct.flagTime > -1) { 207 this.histogramHeightMap.set(XpowerThreadInfoStruct.flagTime + this.flagType, this.rowHeight - this.drawY); 208 } 209 } else { 210 this.height = this.drawY; 211 } 212 XpowerThreadInfoStruct.flagTime = data.startNS; 213 this.flagType = data.valueType; 214 this.drawY = this.drawHistogram(req, data, maxValue, row); 215 XpowerThreadInfoStruct.drawStroke(req, data, row); 216 } 217 } 218 219 static drawStroke( 220 req: { useCache: boolean; context: CanvasRenderingContext2D }, 221 data: XpowerThreadInfoStruct, 222 row: TraceRow<XpowerThreadInfoStruct> 223 ): void { 224 let startNS = TraceRow.range!.startNS; 225 let endNS = TraceRow.range!.endNS; 226 let totalNS = TraceRow.range!.totalNS; 227 if ( 228 (XpowerThreadInfoStruct.hoverXpowerStruct && 229 XpowerThreadInfoStruct.equals(XpowerThreadInfoStruct.hoverXpowerStruct!, data)) || 230 (XpowerThreadInfoStruct.selectXpowerStruct && 231 XpowerThreadInfoStruct.equals(XpowerThreadInfoStruct.selectXpowerStruct!, data)) 232 ) { 233 let startPointX = ns2x(data.startNS || 0, startNS, endNS, totalNS, row.frame); 234 let endPointX = ns2x((data.startNS || 0) + 3000000000, startNS, endNS, totalNS, row.frame); 235 let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX; 236 req!.context.lineWidth = 1; 237 req!.context.strokeStyle = '#9899a0'; 238 let height = this.histogramHeightMap.get(data.startNS + data.valueType) || 0; 239 req!.context.strokeRect(startPointX, this.rowHeight - height - 1, Math.floor(frameWidth), height); 240 } 241 } 242 243 static drawHistogram( 244 req: { useCache: boolean; context: CanvasRenderingContext2D }, 245 data: XpowerThreadInfoStruct, 246 maxValue: number, 247 row: TraceRow<XpowerThreadInfoStruct> 248 ): number { 249 let endPointX = ns2x( 250 (data.startNS || 0) + 3000000000, 251 TraceRow.range!.startNS, 252 TraceRow.range!.endNS, 253 TraceRow.range!.totalNS, 254 row.frame 255 ); 256 let startPointX = ns2x( 257 data.startNS || 0, 258 TraceRow.range!.startNS, 259 TraceRow.range!.endNS, 260 TraceRow.range!.totalNS, 261 row.frame 262 ); 263 let frameWidth = endPointX - startPointX <= 1 ? 1 : endPointX - startPointX; 264 let histogramColor = ColorUtils.colorForTid(data.threadNameId); 265 req!.context.fillStyle = histogramColor; 266 let drawStartY = 0; 267 let dataHeight: number = ((data.value || 0) * (this.rowHeight - 28)) / maxValue; 268 if (data.value !== 0 && dataHeight < 1) { 269 dataHeight = 1; 270 } 271 data.frame!.x = startPointX; 272 data.frame!.width = frameWidth; 273 data.frame!.height = dataHeight; 274 if (this.height === -1) { 275 drawStartY = this.rowHeight - dataHeight; 276 data.frame!.y = drawStartY; 277 req!.context.fillRect(startPointX, drawStartY, frameWidth, dataHeight); 278 return drawStartY; 279 } else { 280 drawStartY = this.height - dataHeight; 281 data.frame!.y = drawStartY; 282 req!.context.fillRect(startPointX, drawStartY, frameWidth, dataHeight); 283 return drawStartY; 284 } 285 } 286 287 static isHover(xpower: XpowerThreadInfoStruct): boolean { 288 return xpower === XpowerThreadInfoStruct.hoverXpowerStruct || xpower === XpowerThreadInfoStruct.selectXpowerStruct; 289 } 290 291 static equals(baseStruct: XpowerThreadInfoStruct, targetStruct: XpowerThreadInfoStruct): boolean { 292 return baseStruct === targetStruct; 293 } 294} 295