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