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, drawLoadingFrame, PerfRender, Rect, RequestMessage } from './ProcedureWorkerCommon'; 17import { TraceRow } from '../../component/trace/base/TraceRow'; 18 19export class EBPFRender extends PerfRender { 20 renderMainThread( 21 req: { 22 context: CanvasRenderingContext2D; 23 useCache: boolean; 24 type: string; 25 chartColor: string; 26 }, 27 eBPFtemRow: TraceRow<EBPFChartStruct> 28 ): void { 29 let filter = eBPFtemRow.dataListCache; 30 let groupBy10MS = (TraceRow.range?.scale || 50) > 40_000_000; 31 let isDiskIO: boolean = req.type.includes('disk-io'); 32 eBPFChart( 33 filter, 34 TraceRow.range?.startNS ?? 0, 35 TraceRow.range?.endNS ?? 0, 36 TraceRow.range?.totalNS ?? 0, // @ts-ignore 37 eBPFtemRow.frame, 38 groupBy10MS, 39 isDiskIO, 40 req.useCache || (TraceRow.range?.refresh ?? false) 41 ); 42 drawLoadingFrame(req.context, filter, eBPFtemRow); 43 drawEBPF(req, filter, groupBy10MS, eBPFtemRow); 44 } 45 46 render( 47 eBPFRequest: RequestMessage, 48 list: Array<EBPFChartStruct>, 49 filter: Array<EBPFChartStruct>, 50 dataList2: Array<EBPFChartStruct> 51 ): void {} 52} 53 54function drawEBPF( 55 req: { 56 context: CanvasRenderingContext2D; 57 useCache: boolean; 58 type: string; 59 chartColor: string; 60 }, 61 filter: EBPFChartStruct[], 62 groupBy10MS: boolean, 63 eBPFtemRow: TraceRow<EBPFChartStruct> 64): void { 65 req.context.beginPath(); 66 let find = false; 67 let hoverRect: EBPFChartStruct | undefined = undefined; 68 for (let re of filter) { 69 re.group10Ms = groupBy10MS; 70 if ( 71 eBPFtemRow.isHover && 72 re.frame && 73 eBPFtemRow.hoverX >= re.frame.x && 74 eBPFtemRow.hoverX <= re.frame.x + re.frame.width 75 ) { 76 if (hoverRect === undefined || re.size! > hoverRect.size!) { 77 hoverRect = re; 78 find = true; 79 } 80 } 81 if (re.frame && re.frame!.x > eBPFtemRow.hoverX + 3) { 82 break; 83 } 84 } 85 if (hoverRect) { 86 EBPFChartStruct.hoverEBPFStruct = hoverRect; 87 } 88 89 for (let re of filter) { 90 EBPFChartStruct.draw(req.context, re, req.chartColor); 91 } 92 if (!find && eBPFtemRow.isHover) { 93 EBPFChartStruct.hoverEBPFStruct = undefined; 94 } 95 req.context.closePath(); 96} 97 98export function eBPFChart( 99 eBPFFilters: Array<EBPFChartStruct>, 100 startNS: number, 101 endNS: number, 102 totalNS: number, 103 frame: Rect, 104 groupBy10MS: boolean, 105 isDiskIO: boolean, 106 use: boolean 107): void { 108 if (use && eBPFFilters.length > 0 && groupBy10MS) { 109 setFrameGroupBy10MS(eBPFFilters, startNS, endNS, frame); 110 return; 111 } 112 if (!groupBy10MS && eBPFFilters[0] && eBPFFilters[0].dur && eBPFFilters[0].endNS) { 113 setFrameByArr(eBPFFilters, startNS, endNS, frame, totalNS, isDiskIO); 114 } 115} 116 117function setFrameGroupBy10MS(eBPFFilters: Array<EBPFChartStruct>, startNS: number, endNS: number, frame: Rect): void { 118 let pns = (endNS - startNS) / frame.width; 119 let y = frame.y; 120 for (let i = 0; i < eBPFFilters.length; i++) { 121 let it = eBPFFilters[i]; 122 if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) { 123 if (!it.frame) { 124 it.frame = new Rect(0, 0, 0, 0); 125 it.frame.y = y; 126 } 127 it.frame.height = it.height!; 128 EBPFChartStruct.setFrame(it, pns, startNS, endNS, frame, true); 129 } else { 130 it.frame = undefined; 131 } 132 } 133} 134 135function setFrameByArr( 136 eBPFFilters: Array<EBPFChartStruct>, 137 startNS: number, 138 endNS: number, 139 frame: Rect, 140 totalNS: number, 141 isDiskIO: boolean 142): void { 143 let list: Array<EBPFChartStruct> = []; 144 let pns = (endNS - startNS) / frame.width; 145 let y = frame.y; 146 let filter: EBPFChartStruct[] = []; 147 for (let index = 0; index < eBPFFilters.length; index++) { 148 if ( 149 eBPFFilters[index].endNS! > startNS && 150 (eBPFFilters[index].startNS || 0) < endNS && 151 eBPFFilters[index].dur! > 0 152 ) { 153 if (index >= 1 && eBPFFilters[index - 1].endNS === eBPFFilters[index].startNS) { 154 continue; 155 } else { 156 eBPFFilters[index].size = 0; 157 filter.push(eBPFFilters[index]); 158 } 159 } 160 } 161 eBPFFilters.length = 0; 162 list = isDiskIO 163 ? (EBPFChartStruct.computeHeightNoGroupLatency(filter, totalNS) as Array<EBPFChartStruct>) 164 : (EBPFChartStruct.computeHeightNoGroup(filter, totalNS) as Array<EBPFChartStruct>); 165 list.map((it) => { 166 if (!it.frame) { 167 it.frame = new Rect(0, 0, 0, 0); 168 it.frame.y = y; 169 } 170 if (it.size && it.size > 0) { 171 EBPFChartStruct.setFrame(it, pns, startNS, endNS, frame, false); 172 eBPFFilters.push(it); 173 } 174 }); 175} 176 177export class EBPFChartStruct extends BaseStruct { 178 static hoverEBPFStruct: EBPFChartStruct | undefined; 179 startNS: number | undefined; 180 endNS: number | undefined; 181 dur: number | undefined; 182 size: number | undefined; 183 height: number | undefined; 184 group10Ms: boolean | undefined; 185 186 static draw(ctx: CanvasRenderingContext2D, data: EBPFChartStruct, chartColor: string): void { 187 if (data.frame) { 188 ctx.fillStyle = chartColor; 189 ctx.strokeStyle = chartColor; 190 ctx.fillRect(data.frame.x, 40 - (data.height || 0), data.frame.width, data.height || 0); 191 } 192 } 193 194 static setFrame( 195 eBPFtemNode: EBPFChartStruct, 196 pns: number, 197 startNS: number, 198 endNS: number, 199 frame: Rect, 200 groupBy10MS: boolean 201 ): void { 202 if ((eBPFtemNode.startNS || 0) < startNS) { 203 eBPFtemNode.frame!.x = 0; 204 } else { 205 eBPFtemNode.frame!.x = Math.floor(((eBPFtemNode.startNS || 0) - startNS) / pns); 206 } 207 if ((eBPFtemNode.startNS || 0) + (eBPFtemNode.dur || 0) > endNS) { 208 eBPFtemNode.frame!.width = frame.width - eBPFtemNode.frame!.x; 209 } else { 210 if (groupBy10MS) { 211 eBPFtemNode.frame!.width = Math.ceil(((eBPFtemNode.endNS || 0) - (eBPFtemNode.startNS || 0)) / pns); 212 } else { 213 eBPFtemNode.frame!.width = Math.ceil( 214 ((eBPFtemNode.startNS || 0) + (eBPFtemNode.dur || 0) - startNS) / pns - eBPFtemNode.frame!.x 215 ); 216 } 217 } 218 if (eBPFtemNode.frame!.width < 1) { 219 eBPFtemNode.frame!.width = 1; 220 } 221 } 222 223 static computeHeightNoGroup(array: Array<EBPFChartStruct>, totalNS: number): Array<unknown> { 224 if (array.length > 0) { 225 let time: Array<{ time: number; type: number }> = []; 226 array.map((item) => { 227 time.push({ time: item.startNS!, type: 1 }); 228 time.push({ time: item.endNS || totalNS, type: -1 }); 229 }); 230 time = time.sort((a, b) => a.time - b.time); 231 let arr: Array<{ 232 startNS: number; 233 dur: number; 234 size: number; 235 group10Ms: boolean; 236 height: number; 237 }> = []; 238 let first = { 239 startNS: time[0].time ?? 0, 240 dur: 0, 241 size: 1, 242 group10Ms: false, 243 height: 1, 244 }; 245 arr.push(first); 246 let max = 2; 247 for (let i = 1, len = time.length; i < len; i++) { 248 let heap = { 249 startNS: time[i].time, 250 dur: 0, 251 size: 0, 252 group10Ms: false, 253 height: 0, 254 }; 255 arr[i - 1].dur = heap.startNS - arr[i - 1].startNS; 256 if (i === len - 1) { 257 heap.dur = totalNS - heap.startNS; 258 } 259 heap.size = arr[i - 1].size + time[i].type; 260 heap.height = Math.floor((heap.size / 6) * 36); 261 max = max > heap.size ? max : heap.size; 262 arr.push(heap); 263 } 264 arr.map((it) => (it.height = Math.floor((it.size / max) * 36))); 265 return arr; 266 } else { 267 return []; 268 } 269 } 270 271 static computeHeightNoGroupLatency(array: Array<EBPFChartStruct>, totalNS: number): Array<unknown> { 272 if (array.length > 0) { 273 let max = 0; 274 let arr: Array<{ startNS: number; dur: number; size: number; group10Ms: boolean; height: number }> = []; 275 for (let io of array) { 276 let ioItem = { 277 startNS: io.startNS!, 278 dur: io.endNS! > totalNS ? totalNS - io.startNS! : io.endNS! - io.startNS!, 279 size: io.dur!, 280 group10Ms: false, 281 height: 0, 282 }; 283 max = max > ioItem.size! ? max : ioItem.size!; 284 arr.push(ioItem); 285 } 286 arr.map((it) => { 287 let height = Math.floor((it.size / max) * 36); 288 it.height = height < 1 ? 1 : height; 289 }); 290 return arr; 291 } else { 292 return []; 293 } 294 } 295} 296