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, PerfRender, 23 RequestMessage 24} from "./ProcedureWorkerCommon.js"; 25 26 27export class HiperfEventRender extends PerfRender{ 28 render(req: RequestMessage, list: Array<any>, filter: Array<any>, dataList2: Array<any>) { 29 let groupBy10MS = req.scale > 100_000_000; 30 if (req.lazyRefresh) { 31 HiPerfEvent(list, dataList2, req.type!, filter, req.startNS, req.endNS, req.totalNS, req.frame, groupBy10MS, req.intervalPerf, req.useCache || !req.range.refresh); 32 } else { 33 if (!req.useCache) { 34 HiPerfEvent(list, dataList2, req.type!, filter, req.startNS, req.endNS, req.totalNS, req.frame, groupBy10MS, req.intervalPerf, false); 35 } 36 } 37 if (req.canvas) { 38 req.context.clearRect(0, 0, req.frame.width, req.frame.height); 39 let arr = filter; 40 if (arr.length > 0 && !req.range.refresh && !req.useCache && req.lazyRefresh) { 41 drawLoading(req.context, req.startNS, req.endNS, req.totalNS, req.frame, arr[0].startNS, arr[arr.length - 1].startNS + arr[arr.length - 1].dur) 42 } 43 drawLines(req.context, req.xs, req.frame.height, req.lineColor) 44 req.context.stroke(); 45 req.context.beginPath(); 46 HiPerfEventStruct.hoverStruct = undefined; 47 req.context.fillStyle = ColorUtils.FUNC_COLOR[0]; 48 req.context.strokeStyle = ColorUtils.FUNC_COLOR[0]; 49 if (req.isHover) { 50 let offset = groupBy10MS ? 0 : 3; 51 for (let re of filter) { 52 if (re.frame && req.hoverX >= re.frame.x - offset && req.hoverX <= re.frame.x + re.frame.width + offset) {//&& req.hoverY >= re.frame.y && req.hoverY <= re.frame.y + re.frame.height 53 HiPerfEventStruct.hoverStruct = re; 54 break; 55 } 56 } 57 } else { 58 HiPerfEventStruct.hoverStruct = req.params.hoverStruct; 59 } 60 HiPerfEventStruct.selectStruct = req.params.selectStruct; 61 let path = new Path2D(); 62 for (let re of filter) { 63 HiPerfEventStruct.draw(req.context, path, re, groupBy10MS); 64 } 65 groupBy10MS ? req.context.fill(path) : req.context.stroke(path); 66 drawSelection(req.context, req.params); 67 let maxEvent = HiPerfEventStruct.maxEvent!.get(req.type!) || 0; 68 let textMetrics = req.context.measureText(maxEvent); 69 req.context.globalAlpha = 0.8 70 req.context.fillStyle = "#f0f0f0" 71 req.context.fillRect(0, 5, textMetrics.width + 8, 18) 72 req.context.globalAlpha = 1 73 req.context.fillStyle = "#333" 74 req.context.textBaseline = "middle" 75 req.context.fillText(maxEvent, 4, 5 + 9); 76 req.context.stroke(); 77 req.context.closePath(); 78 drawFlagLine(req.context, req.flagMoveInfo, req.flagSelectedInfo, req.startNS, req.endNS, req.totalNS, req.frame, req.slicesTime); 79 } 80 // @ts-ignore 81 self.postMessage({ 82 id: req.id, 83 type: req.type, 84 results: req.canvas ? undefined : filter, 85 hover: HiPerfEventStruct.hoverStruct 86 }); 87 } 88} 89export function HiPerfEvent(arr: Array<any>, arr2: any, type: string, res: Array<any>, startNS: number, endNS: number, totalNS: number, frame: any, groupBy10MS: boolean, intervalPerf: number, use: boolean) { 90 if (use && res.length > 0) { 91 let pns = (endNS - startNS) / frame.width; 92 let y = frame.y; 93 for (let i = 0; i < res.length; i++) { 94 let it = res[i]; 95 if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) { 96 if (!it.frame) { 97 it.frame = {}; 98 it.frame.y = y; 99 } 100 it.frame.height = it.height; 101 HiPerfEventStruct.setFrame(it, pns, startNS, endNS, frame); 102 } else { 103 it.frame = null; 104 } 105 } 106 return; 107 } 108 res.length = 0; 109 if (arr) { 110 let list: Array<any>; 111 if (groupBy10MS) { 112 if (arr2[type] && arr2[type].length > 0) { 113 list = arr2[type]; 114 } else { 115 list = HiPerfEventStruct.groupBy10MS(arr, intervalPerf, type); 116 arr2[type] = list; 117 } 118 } else { 119 HiPerfEventStruct.groupBy10MS(arr, intervalPerf, type); 120 list = arr; 121 } 122 let pns = (endNS - startNS) / frame.width; 123 let y = frame.y; 124 125 let groups = list.filter(it => (it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS).map(it => { 126 if (!it.frame) { 127 it.frame = {}; 128 it.frame.y = y; 129 } 130 it.frame.height = it.height; 131 HiPerfEventStruct.setFrame(it, pns, startNS, endNS, frame); 132 return it; 133 }).reduce((pre, current, index, arr) => { 134 if (!pre[`${current.frame.x}`]) { 135 pre[`${current.frame.x}`] = []; 136 pre[`${current.frame.x}`].push(current); 137 if (groupBy10MS) { 138 res.push(current); 139 } else { 140 if (res.length == 0) { 141 res.push(current); 142 } 143 if (res[res.length - 1] && Math.abs(current.frame.x - res[res.length - 1].frame.x) > 4) { 144 res.push(current); 145 } 146 } 147 } 148 return pre; 149 }, {}); 150 } 151} 152 153export class HiPerfEventStruct extends BaseStruct { 154 static hoverStruct: HiPerfEventStruct | undefined; 155 static selectStruct: HiPerfEventStruct | undefined; 156 static path = new Path2D('M 100,100 h 50 v 50 h 50'); 157 id: number | undefined; 158 callchain_id: number | undefined; 159 timestamp: number | undefined; 160 thread_id: number | undefined; 161 event_count: number | undefined; 162 event_type_id: number | undefined; 163 cpu_id: number | undefined; 164 thread_state: string | undefined; 165 startNS: number | undefined; 166 endNS: number | undefined; 167 dur: number | undefined; 168 height: number | undefined; 169 cpu: number | undefined; 170 static maxEvent: Map<string, number> | undefined = new Map(); 171 sum: number | undefined; 172 max: number | undefined; 173 174 static draw(ctx: CanvasRenderingContext2D, path: Path2D, data: HiPerfEventStruct, groupBy10MS: boolean) { 175 if (data.frame) { 176 if (groupBy10MS) { 177 let width = data.frame.width; 178 path.rect(data.frame.x, 40 - (data.height || 0), width, data.height || 0) 179 } else { 180 path.moveTo(data.frame.x + 7, 20); 181 HiPerfEventStruct.drawRoundRectPath(path, data.frame.x - 7, 20 - 7, 14, 14, 3) 182 path.moveTo(data.frame.x, 27); 183 path.lineTo(data.frame.x, 33); 184 } 185 } 186 } 187 188 static drawRoundRectPath(cxt: Path2D, x: number, y: number, width: number, height: number, radius: number) { 189 cxt.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2); 190 cxt.lineTo(x + radius, y + height); 191 cxt.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI); 192 cxt.lineTo(x + 0, y + radius); 193 cxt.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2); 194 cxt.lineTo(x + width - radius, y + 0); 195 cxt.arc(x + width - radius, y + radius, radius, Math.PI * 3 / 2, Math.PI * 2); 196 cxt.lineTo(x + width, y + height - radius); 197 cxt.moveTo(x + width / 3, y + height / 5); 198 cxt.lineTo(x + width / 3, y + height / 5 * 4); 199 cxt.moveTo(x + width / 3, y + height / 5); 200 cxt.bezierCurveTo(x + width / 3 + 7, y + height / 5 - 2, x + width / 3 + 7, y + height / 5 + 6, x + width / 3, y + height / 5 + 4); 201 } 202 203 static setFrame(node: any, pns: number, startNS: number, endNS: number, frame: any) { 204 if ((node.startNS || 0) < startNS) { 205 node.frame.x = 0; 206 } else { 207 node.frame.x = Math.floor(((node.startNS || 0) - startNS) / pns); 208 } 209 if ((node.startNS || 0) + (node.dur || 0) > endNS) { 210 node.frame.width = frame.width - node.frame.x; 211 } else { 212 node.frame.width = Math.ceil(((node.startNS || 0) + (node.dur || 0) - startNS) / pns - node.frame.x); 213 } 214 if (node.frame.width < 1) { 215 node.frame.width = 1; 216 } 217 } 218 219 static groupBy10MS(array: Array<any>, intervalPerf: number, type: string): Array<any> { 220 let obj = array.map(it => { 221 it.timestamp_group = Math.trunc(it.startNS / 1_000_000_0) * 1_000_000_0; 222 return it; 223 }).reduce((pre, current) => { 224 (pre[current["timestamp_group"]] = pre[current["timestamp_group"]] || []).push(current); 225 return pre; 226 }, {}); 227 let arr: any[] = []; 228 let max = 0; 229 for (let aKey in obj) { 230 let sum = obj[aKey].reduce((pre: any, cur: any) => { 231 return pre + cur.event_count 232 }, 0) 233 if (sum > max) max = sum; 234 let ns = parseInt(aKey); 235 arr.push({ 236 startNS: ns, 237 dur: 1_000_000_0, 238 height: 0, 239 sum: sum, 240 }) 241 } 242 if (typeof (HiPerfEventStruct.maxEvent!.get(type)) === "undefined") { 243 HiPerfEventStruct.maxEvent!.set(type, max); 244 } 245 arr.map(it => { 246 it.height = Math.floor(40 * it.sum / max); 247 it.max = max; 248 return it; 249 }) 250 return arr; 251 } 252} 253