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, drawWakeUp, 23 ns2x, 24 Rect, Render, 25 RequestMessage 26} from "./ProcedureWorkerCommon.js"; 27 28export class FuncRender extends Render{ 29 render(req: RequestMessage, list: Array<any>, filter: Array<any>) { 30 if (!req.params.isLive) { 31 self.postMessage({ 32 id: req.id, 33 type: req.type, 34 results: req.canvas ? undefined : filter, 35 hover: undefined 36 }); 37 return; 38 } 39 if (req.lazyRefresh) { 40 func(list, filter, req.startNS, req.endNS, req.totalNS, req.frame, req.useCache || !req.range.refresh); 41 } else { 42 if (!req.useCache) { 43 func(list, filter, req.startNS, req.endNS, req.totalNS, req.frame, false); 44 } 45 } 46 if (req.canvas) { 47 if (req.canvas.height == 150) { 48 req.canvas.width = req.frame.width; 49 req.canvas.height = req.params.maxHeight; 50 req.context.scale(req.params.dpr, req.params.dpr); 51 } 52 req.context.clearRect(0, 0, req.frame.width, req.frame.height); 53 let arr = filter; 54 if (arr.length > 0 && !req.range.refresh && !req.useCache && req.lazyRefresh) { 55 drawLoading(req.context, req.startNS, req.endNS, req.totalNS, req.frame, arr[0].startTs, arr[arr.length - 1].startTs + arr[arr.length - 1].dur) 56 } 57 req.context.beginPath(); 58 drawLines(req.context, req.xs, req.frame.height, req.lineColor) 59 FuncStruct.hoverFuncStruct = undefined; 60 if (req.isHover) { 61 for (let re of filter) { 62 if (re.dur == 0 || re.dur == null || re.dur == undefined) { 63 if (re.frame && req.hoverX >= re.frame.x - 5 && req.hoverX <= re.frame.x + 5 && req.hoverY >= re.frame.y + (re.depth * 20) && req.hoverY <= re.frame.y + re.frame.height + (re.depth * 20)) { 64 FuncStruct.hoverFuncStruct = re; 65 break; 66 } 67 } else { 68 if (re.frame && req.hoverX >= re.frame.x && req.hoverX <= re.frame.x + re.frame.width && req.hoverY >= re.frame.y + (re.depth * 20) && req.hoverY <= re.frame.y + re.frame.height + (re.depth * 20)) { 69 FuncStruct.hoverFuncStruct = re; 70 break; 71 } 72 } 73 } 74 } else { 75 FuncStruct.hoverFuncStruct = req.params.hoverFuncStruct; 76 } 77 FuncStruct.selectFuncStruct = req.params.selectFuncStruct; 78 for (let re of filter) { 79 FuncStruct.draw(req.context, re, req.totalNS) 80 } 81 drawSelection(req.context, req.params); 82 drawWakeUp(req.context, req.wakeupBean, req.startNS, req.endNS, req.totalNS, req.frame); 83 req.context.closePath(); 84 drawFlagLine(req.context, req.flagMoveInfo, req.flagSelectedInfo, req.startNS, req.endNS, req.totalNS, req.frame, req.slicesTime); 85 } 86 // @ts-ignore 87 self.postMessage({ 88 id: req.id, 89 type: req.type, 90 results: req.canvas ? undefined : filter, 91 hover: FuncStruct.hoverFuncStruct 92 }); 93 } 94} 95 96export function func(list: Array<any>, res: Array<any>, startNS: number, endNS: number, totalNS: number, frame: any, use: boolean) { 97 if (use && res.length > 0) { 98 for (let i = 0, len = res.length; i < len; i++) { 99 if ((res[i].startTs || 0) + (res[i].dur || 0) >= startNS && (res[i].startTs || 0) <= endNS) { 100 FuncStruct.setFuncFrame(res[i], 0, startNS, endNS, totalNS, frame) 101 } else { 102 res[i].frame = null; 103 } 104 } 105 return; 106 } 107 res.length = 0; 108 if (list) { 109 let groups = list.filter(it => (it.startTs || 0) + (it.dur || 0) >= startNS && (it.startTs || 0) <= endNS).map(it => { 110 FuncStruct.setFuncFrame(it, 0, startNS, endNS, totalNS, frame) 111 return it; 112 }).reduce((pre, current, index, arr) => { 113 (pre[`${current.frame.x}`] = pre[`${current.frame.x}`] || []).push(current); 114 return pre; 115 }, {}); 116 Reflect.ownKeys(groups).map((kv => { 117 let arr = (groups[kv].sort((a: any, b: any) => b.dur - a.dur)); 118 res.push(arr[0]); 119 })); 120 } 121} 122 123export class FuncStruct extends BaseStruct { 124 static hoverFuncStruct: FuncStruct | undefined; 125 static selectFuncStruct: FuncStruct | undefined; 126 argsetid: number | undefined // 53161 127 depth: number | undefined // 0 128 dur: number | undefined // 570000 129 funName: string | undefined //"binder transaction" 130 id: number | undefined // 92749 131 is_main_thread: number | undefined // 0 132 parent_id: number | undefined // null 133 startTs: number | undefined // 9729867000 134 threadName: string | undefined // "Thread-15" 135 tid: number | undefined // 2785 136 identify: number | undefined 137 track_id: number | undefined // 414 138 139 static setFuncFrame(node: any, padding: number, startNS: number, endNS: number, totalNS: number, frame: any) { 140 let x1: number, x2: number; 141 if ((node.startTs || 0) > startNS && (node.startTs || 0) < endNS) { 142 x1 = ns2x((node.startTs || 0), startNS, endNS, totalNS, frame); 143 } else { 144 x1 = 0; 145 } 146 if ((node.startTs || 0) + (node.dur || 0) > startNS && (node.startTs || 0) + (node.dur || 0) < endNS) { 147 x2 = ns2x((node.startTs || 0) + (node.dur || 0), startNS, endNS, totalNS, frame); 148 } else { 149 x2 = frame.width; 150 } 151 if (!node.frame) { 152 node.frame = {}; 153 } 154 let getV: number = x2 - x1 <= 1 ? 1 : x2 - x1; 155 node.frame.x = Math.floor(x1); 156 node.frame.y = 0; 157 node.frame.width = Math.floor(getV); 158 node.frame.height = 20; 159 } 160 161 static getInt(data: FuncStruct): number { 162 let str = data.funName || ""; 163 let sum = 0; 164 for (let i = 0; i < str.length; i++) { 165 sum += str.charCodeAt(i) 166 } 167 return (sum + (data?.depth || 0)) % ColorUtils.FUNC_COLOR.length; 168 } 169 170 static draw(ctx: CanvasRenderingContext2D, data: FuncStruct, totalNS: number) { 171 if (data.frame) { 172 let isBinder = FuncStruct.isBinder(data); 173 if (data.dur == undefined || data.dur == null || data.dur == 0) { 174 } else { 175 ctx.fillStyle = ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.funName || '', 0, ColorUtils.FUNC_COLOR.length)];//data.depth || 176 let miniHeight = 20 177 ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - padding * 2) 178 if (data.frame.width > 10) { 179 ctx.fillStyle = "#fff" 180 FuncStruct.drawString(ctx, `${data.funName || ''}`, 5, data.frame) 181 } 182 if (FuncStruct.isSelected(data)) { 183 ctx.strokeStyle = "#000" 184 ctx.lineWidth = 2 185 ctx.strokeRect(data.frame.x, data.frame.y + 1, data.frame.width, miniHeight - padding * 2 - 2) 186 } 187 } 188 } 189 } 190 191 static drawString(ctx: CanvasRenderingContext2D, str: string, textPadding: number, frame: Rect): boolean { 192 let textMetrics = ctx.measureText(str); 193 let charWidth = Math.round(textMetrics.width / str.length) 194 if (textMetrics.width < frame.width - textPadding * 2) { 195 let x2 = Math.floor(frame.width / 2 - textMetrics.width / 2 + frame.x + textPadding) 196 ctx.fillText(str, x2, Math.floor(frame.y + frame.height / 2 + 2), frame.width - textPadding * 2) 197 return true; 198 } 199 if (frame.width - textPadding * 2 > charWidth * 4) { 200 let chatNum = (frame.width - textPadding * 2) / charWidth; 201 let x1 = frame.x + textPadding 202 ctx.fillText(str.substring(0, chatNum - 4) + '...', x1, Math.floor(frame.y + frame.height / 2 + 2), frame.width - textPadding * 2) 203 return true; 204 } 205 return false; 206 } 207 208 static isSelected(data: FuncStruct): boolean { 209 return (FuncStruct.selectFuncStruct != undefined && 210 FuncStruct.selectFuncStruct.startTs == data.startTs && 211 FuncStruct.selectFuncStruct.depth == data.depth) 212 } 213 214 static isBinder(data: FuncStruct): boolean { 215 if (data.funName != null && 216 ( 217 data.funName.toLowerCase().startsWith("binder transaction async") 218 || data.funName.toLowerCase().startsWith("binder async") 219 ) 220 ) { 221 return true; 222 } else { 223 return false; 224 } 225 } 226} 227 228const padding = 1; 229 230