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 {JanksStruct} from '../../bean/JanksStruct'; 17import {ColorUtils} from '../../component/trace/base/ColorUtils'; 18import {TraceRow} from '../../component/trace/base/TraceRow'; 19import { 20 drawLoadingFrame, 21 drawString, 22 isFrameContainPoint, 23 ns2x, 24 Render, 25 RequestMessage, 26} from './ProcedureWorkerCommon'; 27import {SpSystemTrace} from "../../component/SpSystemTrace"; 28 29export class JankRender extends Render { 30 renderMainThread( 31 req: { 32 useCache: boolean; 33 context: CanvasRenderingContext2D; 34 type: string; 35 }, 36 row: TraceRow<JankStruct> 37 ): void { 38 let jankList = row.dataList; 39 let jankFilter = row.dataListCache; 40 jank( 41 jankList, 42 jankFilter, 43 TraceRow.range!.startNS, 44 TraceRow.range!.endNS, 45 TraceRow.range!.totalNS, 46 row.frame, 47 req.useCache || !TraceRow.range!.refresh 48 ); 49 drawLoadingFrame(req.context, row.dataListCache, row); 50 req.context.beginPath(); 51 let find = false; 52 let nsScale = ((TraceRow.range!.endNS || 0) - (TraceRow.range!.startNS || 0)) / (TraceRow.range!.totalNS * 9); 53 for (let re of jankFilter) { 54 JankStruct.draw(req.context, re, nsScale); 55 if (row.isHover) { 56 if (re.dur == 0 || re.dur == null || re.dur == undefined) { 57 if ( 58 re.frame && 59 row.hoverX >= re.frame.x - 5 && 60 row.hoverX <= re.frame.x + 5 && 61 row.hoverY >= re.frame.y && 62 row.hoverY <= re.frame.y + re.frame.height 63 ) { 64 JankStruct.hoverJankStruct = re; 65 find = true; 66 } 67 } else { 68 if (re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { 69 JankStruct.hoverJankStruct = re; 70 find = true; 71 } 72 } 73 } 74 } 75 if (!find && row.isHover) JankStruct.hoverJankStruct = undefined; 76 req.context.closePath(); 77 } 78 79 render(req: RequestMessage, list: Array<any>, filter: Array<any>): void { 80 } 81} 82 83export function jank( 84 jankList: Array<any>, 85 jankFilter: Array<any>, 86 startNS: number, 87 endNS: number, 88 totalNS: number, 89 frame: any, 90 use: boolean 91): void { 92 if (use && jankFilter.length > 0) { 93 for (let i = 0, len = jankFilter.length; i < len; i++) { 94 if ((jankFilter[i].ts || 0) + (jankFilter[i].dur || 0) >= startNS && (jankFilter[i].ts || 0) <= endNS) { 95 JankStruct.setJankFrame(jankFilter[i], 0, startNS, endNS, totalNS, frame); 96 } else { 97 jankFilter[i].frame = null; 98 } 99 } 100 return; 101 } 102 jankFilter.length = 0; 103 if (jankList) { 104 let groups = jankList 105 .filter((it) => (it.ts ?? 0) + (it.dur ?? 0) >= startNS && (it.ts ?? 0) <= endNS) 106 .map((it) => { 107 JankStruct.setJankFrame(it, 0, startNS, endNS, totalNS, frame); 108 return it; 109 }) 110 .reduce((pre, current, index, arr) => { 111 (pre[`${current.frame.x}-${current.depth}`] = pre[`${current.frame.x}-${current.depth}`] || []).push(current); 112 return pre; 113 }, {}); 114 Reflect.ownKeys(groups).map((kv) => { 115 let arr = groups[kv].sort((a: any, b: any) => b.dur - a.dur); 116 jankFilter.push(arr[0]); 117 }); 118 } 119} 120 121export function JankStructOnClick(clickRowType: string, sp: SpSystemTrace, jankClickHandler: any) { 122 return new Promise((resolve, reject) => { 123 if (clickRowType === TraceRow.ROW_TYPE_JANK && JankStruct.hoverJankStruct) { 124 JankStruct.selectJankStructList.length = 0; 125 sp.removeLinkLinesByBusinessType('janks'); 126 JankStruct.selectJankStruct = JankStruct.hoverJankStruct; 127 sp.timerShaftEL?.drawTriangle(JankStruct.selectJankStruct!.ts || 0, 'inverted'); 128 sp.traceSheetEL?.displayJankData( 129 JankStruct.selectJankStruct, 130 (datas) => { 131 datas.forEach((data) => { 132 let endParentRow; 133 if (data.frame_type == 'frameTime') { 134 endParentRow = sp.shadowRoot?.querySelector<TraceRow<JankStruct>>( 135 `trace-row[row-id='frameTime'][row-type='janks']` 136 ); 137 } else { 138 endParentRow = sp.shadowRoot?.querySelector<TraceRow<any>>(`trace-row[row-id='${data.pid}'][folder]`); 139 } 140 sp.drawJankLine(endParentRow, JankStruct.selectJankStruct!, data); 141 }); 142 }, 143 jankClickHandler 144 ); 145 reject(); 146 } else { 147 resolve(null); 148 } 149 }); 150 151} 152 153export class JankStruct extends JanksStruct { 154 static hoverJankStruct: JankStruct | undefined; 155 static selectJankStruct: JankStruct | undefined; 156 static selectJankStructList: Array<JankStruct> = new Array<JankStruct>(); 157 158 static setJankFrame( 159 jankNode: any, 160 padding: number, 161 startNS: number, 162 endNS: number, 163 totalNS: number, 164 frame: any 165 ): void { 166 let x1: number, x2: number; 167 if ((jankNode.ts || 0) > startNS && (jankNode.ts || 0) < endNS) { 168 x1 = ns2x(jankNode.ts || 0, startNS, endNS, totalNS, frame); 169 } else { 170 x1 = 0; 171 } 172 if ((jankNode.ts || 0) + (jankNode.dur || 0) > startNS && (jankNode.ts || 0) + (jankNode.dur || 0) < endNS) { 173 x2 = ns2x((jankNode.ts || 0) + (jankNode.dur || 0), startNS, endNS, totalNS, frame); 174 } else { 175 x2 = frame.width; 176 } 177 if (!jankNode.frame) { 178 jankNode.frame = {}; 179 } 180 let getV: number = x2 - x1 < 1 ? 1 : x2 - x1; 181 jankNode.frame.x = Math.floor(x1); 182 jankNode.frame.y = jankNode.depth * 20; 183 jankNode.frame.width = Math.ceil(getV); 184 jankNode.frame.height = 20; 185 } 186 187 static draw(ctx: CanvasRenderingContext2D, data: JankStruct, nsScale: number): void { 188 if (data.frame) { 189 if (data.dur == undefined || data.dur == null || data.dur == 0) { 190 } else { 191 ctx.globalAlpha = 1; 192 ctx.fillStyle = ColorUtils.JANK_COLOR[0]; 193 if (data.jank_tag === 1) { 194 ctx.fillStyle = ColorUtils.JANK_COLOR[2]; 195 } else if (data.jank_tag === 3) { 196 ctx.fillStyle = ColorUtils.JANK_COLOR[3]; 197 } 198 let miniHeight = 20; 199 if ( 200 JankStruct.hoverJankStruct && 201 data.name == JankStruct.hoverJankStruct.name && 202 JankStruct.hoverJankStruct.type == data.type && 203 JankStruct.hoverJankStruct.pid == data.pid && 204 JankStruct.hoverJankStruct.frame_type == data.frame_type 205 ) { 206 ctx.globalAlpha = 0.7; 207 } 208 if (data.type == '0') { 209 this.drawActualFrame(ctx, data, miniHeight); 210 } else { 211 this.drawExpectedFrame(data, nsScale, ctx, miniHeight); 212 } 213 if (data.frame.width > 10) { 214 ctx.fillStyle = '#fff'; 215 drawString(ctx, `${data.name || ''}`, 5, data.frame, data); 216 } 217 if (JankStruct.isSelected(data)) { 218 ctx.strokeStyle = '#000'; 219 ctx.lineWidth = 2; 220 ctx.strokeRect(data.frame.x, data.frame.y + 1, data.frame.width, miniHeight - padding * 2 - 2); 221 } 222 } 223 } 224 } 225 226 private static drawExpectedFrame( 227 data: JankStruct, 228 nsScale: number, 229 ctx: CanvasRenderingContext2D, 230 miniHeight: number 231 ): void { 232 if (data.frame && data.frame.width * nsScale < 1.5) { 233 ctx.fillStyle = '#FFFFFF'; 234 ctx.fillRect(data.frame.x, data.frame.y, data.frame.width * nsScale, miniHeight - padding * 2); 235 ctx.fillStyle = ColorUtils.JANK_COLOR[0]; 236 ctx.fillRect( 237 data.frame.x + data.frame.width * nsScale, 238 data.frame.y, 239 data.frame.width - nsScale * 2, 240 miniHeight - padding * 2 241 ); 242 ctx.fillStyle = '#FFFFFF'; 243 ctx.fillRect( 244 data.frame.x + data.frame.width * nsScale + data.frame.width - nsScale * 2, 245 data.frame.y, 246 data.frame.width * nsScale, 247 miniHeight - padding * 2 248 ); 249 } else { 250 ctx.fillStyle = ColorUtils.JANK_COLOR[0]; 251 if (data.frame) { 252 ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2); 253 } 254 } 255 } 256 257 private static drawActualFrame(ctx: CanvasRenderingContext2D, data: JankStruct, miniHeight: number): void { 258 ctx.fillStyle = ColorUtils.JANK_COLOR[0]; 259 if (data.jank_tag === 1) { 260 ctx.fillStyle = ColorUtils.JANK_COLOR[2]; 261 } else if (data.jank_tag === 3) { 262 ctx.fillStyle = ColorUtils.JANK_COLOR[3]; 263 } 264 if (data.frame) { 265 ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2); 266 } 267 } 268 269 static isSelected(data: JankStruct): boolean { 270 return ( 271 JankStruct.selectJankStruct != undefined && 272 JankStruct.selectJankStruct.ts == data.ts && 273 JankStruct.selectJankStruct.type == data.type && 274 JankStruct.selectJankStruct.pid == data.pid && 275 JankStruct.selectJankStruct.frame_type == data.frame_type 276 ); 277 } 278} 279 280const padding = 1; 281