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