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, Rect, Render, drawString, isFrameContainPoint, ns2x } from './ProcedureWorkerCommon.js'; 17import { TraceRow } from '../../component/trace/base/TraceRow.js'; 18import { ColorUtils } from '../../component/trace/base/ColorUtils.js'; 19import { JsCpuProfilerChartFrame } from '../../bean/JsStruct.js'; 20 21export class JsCpuProfilerRender extends Render { 22 renderMainThread( 23 req: { 24 useCache: boolean; 25 context: CanvasRenderingContext2D; 26 type: string; 27 }, 28 jsCpuProfilerRow: TraceRow<JsCpuProfilerStruct> 29 ) { 30 let list = jsCpuProfilerRow.dataList; 31 let filter = jsCpuProfilerRow.dataListCache; 32 jsCpuProfiler( 33 list, 34 filter, 35 TraceRow.range!.startNS, 36 TraceRow.range!.endNS, 37 TraceRow.range!.totalNS, 38 jsCpuProfilerRow.frame, 39 req.useCache || !TraceRow.range!.refresh 40 ); 41 req.context.beginPath(); 42 let jsCpuProfilerFind = false; 43 for (let re of filter) { 44 JsCpuProfilerStruct.draw(req.context, re); 45 if (jsCpuProfilerRow.isHover) { 46 if ( 47 re.endTime - re.startTime == 0 || 48 re.endTime - re.startTime == null || 49 re.endTime - re.startTime == undefined 50 ) { 51 if ( 52 re.frame && 53 jsCpuProfilerRow.hoverX >= re.frame.x - 5 && 54 jsCpuProfilerRow.hoverX <= re.frame.x + 5 && 55 jsCpuProfilerRow.hoverY >= re.frame.y && 56 jsCpuProfilerRow.hoverY <= re.frame.y + re.frame.height 57 ) { 58 JsCpuProfilerStruct.hoverJsCpuProfilerStruct = re; 59 jsCpuProfilerFind = true; 60 } 61 } else { 62 if (re.frame && isFrameContainPoint(re.frame, jsCpuProfilerRow.hoverX, jsCpuProfilerRow.hoverY)) { 63 JsCpuProfilerStruct.hoverJsCpuProfilerStruct = re; 64 jsCpuProfilerFind = true; 65 } 66 } 67 } 68 } 69 if (!jsCpuProfilerFind && jsCpuProfilerRow.isHover) JsCpuProfilerStruct.hoverJsCpuProfilerStruct = undefined; 70 req.context.closePath(); 71 } 72} 73export function jsCpuProfiler( 74 list: Array<any>, 75 filter: Array<any>, 76 startNS: number, 77 endNS: number, 78 totalNS: number, 79 frame: Rect, 80 use: boolean 81) { 82 if (use && filter.length > 0) { 83 for (let i = 0, len = filter.length; i < len; i++) { 84 if ((filter[i].startTime || 0) + (filter[i].totalTime || 0) >= startNS && (filter[i].startTime || 0) <= endNS) { 85 JsCpuProfilerStruct.setJsCpuProfilerFrame(filter[i], startNS, endNS, totalNS, frame); 86 } else { 87 filter[i].frame = null; 88 } 89 } 90 return; 91 } 92 filter.length = 0; 93 if (list) { 94 let groups = list 95 .filter((it) => (it.startTime ?? 0) + (it.totalTime ?? 0) >= startNS && (it.startTime ?? 0) <= endNS) 96 .map((it) => { 97 JsCpuProfilerStruct.setJsCpuProfilerFrame(it, startNS, endNS, totalNS, frame); 98 return it; 99 }) 100 .reduce((pre, current) => { 101 (pre[`${current.frame.x}-${current.depth}`] = pre[`${current.frame.x}-${current.depth}`] || []).push(current); 102 return pre; 103 }, {}); 104 Reflect.ownKeys(groups).map((kv) => { 105 let arr = groups[kv].sort((a: JsCpuProfilerChartFrame, b: JsCpuProfilerChartFrame) => b.totalTime - a.totalTime); 106 filter.push(arr[0]); 107 }); 108 } 109} 110 111const padding = 1; 112export class JsCpuProfilerStruct extends BaseStruct { 113 static lastSelectJsCpuProfilerStruct: JsCpuProfilerStruct | undefined; 114 static selectJsCpuProfilerStruct: JsCpuProfilerStruct | undefined; 115 static hoverJsCpuProfilerStruct: JsCpuProfilerStruct | undefined; 116 id: number = 0; 117 name: string = ''; 118 startTime: number = 0; 119 endTime: number = 0; 120 selfTime: number = 0; 121 totalTime: number = 0; 122 url: string = ''; 123 depth: number = 0; 124 parentId: number = 0; 125 children!: Array<JsCpuProfilerChartFrame>; 126 isSelect: boolean = false; 127 128 static setJsCpuProfilerFrame(jsCpuProfilerNode: any, startNS: number, endNS: number, totalNS: number, frame: Rect) { 129 let x1: number, x2: number; 130 if ((jsCpuProfilerNode.startTime || 0) > startNS && (jsCpuProfilerNode.startTime || 0) < endNS) { 131 x1 = ns2x(jsCpuProfilerNode.startTime || 0, startNS, endNS, totalNS, frame); 132 } else { 133 x1 = 0; 134 } 135 if ( 136 (jsCpuProfilerNode.startTime || 0) + (jsCpuProfilerNode.totalTime || 0) > startNS && 137 (jsCpuProfilerNode.startTime || 0) + (jsCpuProfilerNode.totalTime || 0) < endNS 138 ) { 139 x2 = ns2x( 140 (jsCpuProfilerNode.startTime || 0) + (jsCpuProfilerNode.totalTime || 0), 141 startNS, 142 endNS, 143 totalNS, 144 frame 145 ); 146 } else { 147 x2 = frame.width; 148 } 149 if (!jsCpuProfilerNode.frame) { 150 jsCpuProfilerNode.frame = {}; 151 } 152 let getV: number = x2 - x1 < 1 ? 1 : x2 - x1; 153 jsCpuProfilerNode.frame.x = Math.floor(x1); 154 jsCpuProfilerNode.frame.y = jsCpuProfilerNode.depth * 20; 155 jsCpuProfilerNode.frame.width = Math.ceil(getV); 156 jsCpuProfilerNode.frame.height = 20; 157 } 158 159 static draw(jsCpuProfilerCtx: CanvasRenderingContext2D, data: JsCpuProfilerStruct) { 160 if (data.frame) { 161 if (data.endTime - data.startTime == undefined || data.endTime - data.startTime == null) { 162 } else { 163 jsCpuProfilerCtx.globalAlpha = 1; 164 if (data.name === '(program)') { 165 jsCpuProfilerCtx.fillStyle = '#ccc'; 166 } else if (data.name === '(idle)') { 167 jsCpuProfilerCtx.fillStyle = '#f0f0f0'; 168 } else { 169 jsCpuProfilerCtx.fillStyle = 170 ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.name || '', 0, ColorUtils.FUNC_COLOR.length)]; 171 } 172 let miniHeight = 20; 173 if (JsCpuProfilerStruct.hoverJsCpuProfilerStruct && data == JsCpuProfilerStruct.hoverJsCpuProfilerStruct) { 174 jsCpuProfilerCtx.globalAlpha = 0.7; 175 } 176 jsCpuProfilerCtx.fillRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2); 177 if (data.frame.width > 8) { 178 jsCpuProfilerCtx.lineWidth = 1; 179 jsCpuProfilerCtx.fillStyle = '#fff'; 180 jsCpuProfilerCtx.textBaseline = 'middle'; 181 drawString(jsCpuProfilerCtx, `${data.name || ''}`, 4, data.frame, data); 182 } 183 if (data === JsCpuProfilerStruct.selectJsCpuProfilerStruct) { 184 jsCpuProfilerCtx.strokeStyle = '#000'; 185 jsCpuProfilerCtx.lineWidth = 2; 186 jsCpuProfilerCtx.strokeRect( 187 data.frame.x + 1, 188 data.frame.y + 1, 189 data.frame.width - 2, 190 miniHeight - padding * 2 - 2 191 ); 192 } 193 } 194 } 195 } 196} 197