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