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 { SpSystemTrace } from '../SpSystemTrace'; 17import { TraceRow } from '../trace/base/TraceRow'; 18import { procedurePool } from '../../database/Procedure'; 19import { EBPFChartStruct, EBPFRender } from '../../database/ui-worker/ProcedureWorkerEBPF'; 20import { ColorUtils } from '../trace/base/ColorUtils'; 21import { Utils } from '../trace/base/Utils'; 22import { renders } from '../../database/ui-worker/ProcedureWorker'; 23import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; 24import { diskIoSender, fileSysVMSender, fileSystemSender } from '../../database/data-trafic/EBPFSender'; 25import { hasFileSysData } from '../../database/sql/Memory.sql'; 26import { getDiskIOProcess } from '../../database/sql/SqlLite.sql'; 27import { SpStatisticsHttpUtil } from '../../../statistics/util/SpStatisticsHttpUtil'; 28 29export class SpEBPFChart { 30 private trace: SpSystemTrace; 31 32 constructor(trace: SpSystemTrace) { 33 this.trace = trace; 34 } 35 36 async init(): Promise<void> { 37 let sys = await hasFileSysData(); 38 if (sys.length > 0) { 39 //@ts-ignore 40 let fsCount = sys[0].fsCount ?? 0; 41 //@ts-ignore 42 let vmCount = sys[0].vmCount ?? 0; 43 //@ts-ignore 44 let ioCount = sys[0].ioCount ?? 0; 45 if (sys && sys.length > 0 && (fsCount > 0 || vmCount > 0 || ioCount > 0)) { 46 let folder = await this.initFolder(); 47 await this.initFileCallchain(); 48 if (fsCount > 0) { 49 await this.initLogicalRead(folder); 50 await this.initLogicalWrite(folder); 51 } 52 if (vmCount > 0) { 53 await this.initVirtualMemoryTrace(folder); 54 } 55 if (ioCount > 0) { 56 await this.initDiskIOLatency(folder); 57 await this.initProcessDiskIOLatency(folder); 58 } 59 // 统计ebpf插件 60 let requsetBody = { 61 eventData: { 62 plugin: ['hiebpf-plugin'] 63 } 64 }; 65 SpStatisticsHttpUtil.recordPluginUsage(requsetBody); 66 } 67 } 68 } 69 70 async initFileCallchain(): Promise<unknown> { 71 return new Promise<unknown>((resolve, reject) => { 72 procedurePool.submitWithName('logic0', 'fileSystem-init', null, undefined, (res: unknown) => { 73 resolve(res); 74 }); 75 }); 76 } 77 78 //@ts-ignore 79 async initFolder(): Promise<TraceRow<unknown>> { 80 let fsFolder = TraceRow.skeleton(); 81 fsFolder.rowId = 'FileSystem'; 82 fsFolder.index = 0; 83 fsFolder.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM_GROUP; 84 fsFolder.rowParentId = ''; 85 fsFolder.style.height = '40px'; 86 fsFolder.folder = true; 87 fsFolder.name = 'EBPF'; /* & I/O Latency */ 88 fsFolder.addTemplateTypes('HiEBpf'); 89 fsFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler; 90 fsFolder.selectChangeHandler = this.trace.selectChangeHandler; //@ts-ignore 91 fsFolder.supplierFrame = (): Promise<Array<unknown>> => new Promise<Array<unknown>>((resolve) => resolve([])); 92 fsFolder.onThreadHandler = (useCache): void => { 93 fsFolder.canvasSave(this.trace.canvasPanelCtx!); 94 if (fsFolder.expansion) { 95 // @ts-ignore 96 this.trace.canvasPanelCtx?.clearRect(0, 0, fsFolder.frame.width, fsFolder.frame.height); 97 } else { 98 (renders.empty as EmptyRender).renderMainThread( 99 { 100 context: this.trace.canvasPanelCtx, 101 useCache: useCache, 102 type: '', 103 }, 104 fsFolder 105 ); 106 } 107 fsFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace); 108 }; 109 this.trace.rowsEL?.appendChild(fsFolder); 110 return fsFolder; 111 } 112 113 //@ts-ignore 114 async initLogicalRead(folder: TraceRow<unknown>): Promise<void> { 115 let logicalReadRow = TraceRow.skeleton<EBPFChartStruct>(); 116 logicalReadRow.rowId = 'FileSystemLogicalRead'; 117 logicalReadRow.index = 1; 118 logicalReadRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 119 logicalReadRow.rowParentId = folder.rowId; 120 logicalReadRow.rowHidden = !folder.expansion; 121 logicalReadRow.style.height = '40px'; 122 logicalReadRow.setAttribute('children', ''); 123 logicalReadRow.name = 'FileSystem Logical Read'; 124 logicalReadRow.supplierFrame = async (): Promise<EBPFChartStruct[]> => { 125 const res = await fileSystemSender(2, TraceRow.range?.scale || 50, logicalReadRow); 126 return res; 127 }; 128 logicalReadRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 129 logicalReadRow.selectChangeHandler = this.trace.selectChangeHandler; 130 logicalReadRow.focusHandler = (): void => this.focusHandler(logicalReadRow); 131 logicalReadRow.findHoverStruct = (): void => { 132 EBPFChartStruct.hoverEBPFStruct = logicalReadRow.getHoverStruct(false); 133 }; 134 logicalReadRow.onThreadHandler = (useCache): void => { 135 let context: CanvasRenderingContext2D; 136 if (logicalReadRow.currentContext) { 137 context = logicalReadRow.currentContext; 138 } else { 139 context = logicalReadRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 140 } 141 logicalReadRow.canvasSave(context); 142 //@ts-ignore 143 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( 144 { 145 context: context, 146 useCache: useCache, 147 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-logical-read`, 148 chartColor: ColorUtils.MD_PALETTE[0], 149 }, 150 logicalReadRow 151 ); 152 logicalReadRow.canvasRestore(context, this.trace); 153 }; 154 folder.addChildTraceRow(logicalReadRow); 155 } 156 157 //@ts-ignore 158 async initLogicalWrite(folder: TraceRow<unknown>): Promise<void> { 159 let logicalWriteRow = TraceRow.skeleton<EBPFChartStruct>(); 160 logicalWriteRow.rowId = 'FileSystemLogicalWrite'; 161 logicalWriteRow.index = 2; 162 logicalWriteRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 163 logicalWriteRow.rowParentId = folder.rowId; 164 logicalWriteRow.rowHidden = !folder.expansion; 165 logicalWriteRow.style.height = '40px'; 166 logicalWriteRow.setAttribute('children', ''); 167 logicalWriteRow.name = 'FileSystem Logical Write'; 168 logicalWriteRow.supplierFrame = async (): Promise<EBPFChartStruct[]> => { 169 const res = await fileSystemSender(3, TraceRow.range?.scale || 50, logicalWriteRow); 170 return res; 171 }; 172 logicalWriteRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 173 logicalWriteRow.selectChangeHandler = this.trace.selectChangeHandler; 174 logicalWriteRow.focusHandler = (): void => this.focusHandler(logicalWriteRow); 175 logicalWriteRow.findHoverStruct = (): void => { 176 EBPFChartStruct.hoverEBPFStruct = logicalWriteRow.getHoverStruct(false); 177 }; 178 logicalWriteRow.onThreadHandler = (useCache): void => { 179 let context: CanvasRenderingContext2D; 180 if (logicalWriteRow.currentContext) { 181 context = logicalWriteRow.currentContext; 182 } else { 183 context = logicalWriteRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 184 } 185 logicalWriteRow.canvasSave(context); 186 //@ts-ignore 187 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( 188 { 189 context: context, 190 useCache: useCache, 191 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-logical-write`, 192 chartColor: ColorUtils.MD_PALETTE[8], 193 }, 194 logicalWriteRow 195 ); 196 logicalWriteRow.canvasRestore(context, this.trace); 197 }; 198 folder.addChildTraceRow(logicalWriteRow); 199 } 200 201 //@ts-ignore 202 async initDiskIOLatency(folder: TraceRow<unknown>): Promise<void> { 203 let diskIoRow = TraceRow.skeleton<EBPFChartStruct>(); 204 diskIoRow.rowId = 'FileSystemDiskIOLatency'; 205 diskIoRow.index = 4; 206 diskIoRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 207 diskIoRow.rowParentId = folder.rowId; 208 diskIoRow.rowHidden = !folder.expansion; 209 diskIoRow.style.height = '40px'; 210 diskIoRow.style.width = '100%'; 211 diskIoRow.setAttribute('children', ''); 212 diskIoRow.name = 'Disk I/O Latency'; 213 diskIoRow.supplierFrame = async (): Promise<EBPFChartStruct[]> => { 214 const res = await diskIoSender(true, 0, [1, 2, 3, 4], TraceRow.range?.scale || 50, diskIoRow); 215 return res; 216 }; 217 diskIoRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 218 diskIoRow.selectChangeHandler = this.trace.selectChangeHandler; 219 diskIoRow.focusHandler = (): void => this.focusHandler(diskIoRow); 220 diskIoRow.findHoverStruct = (): void => { 221 EBPFChartStruct.hoverEBPFStruct = diskIoRow.getHoverStruct(false); 222 }; 223 diskIoRow.onThreadHandler = (useCache): void => { 224 let context: CanvasRenderingContext2D; 225 if (diskIoRow.currentContext) { 226 context = diskIoRow.currentContext; 227 } else { 228 context = diskIoRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 229 } 230 diskIoRow.canvasSave(context); 231 //@ts-ignore 232 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( 233 { 234 context: context, 235 useCache: useCache, 236 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-disk-io`, 237 chartColor: ColorUtils.MD_PALETTE[0], 238 }, 239 diskIoRow 240 ); 241 diskIoRow.canvasRestore(context, this.trace); 242 }; 243 folder.addChildTraceRow(diskIoRow); 244 } 245 246 //@ts-ignore 247 initProcessDiskIOLatencyRead(i: number, folder: TraceRow<unknown>, process: unknown): TraceRow<EBPFChartStruct> { 248 let rowRead = TraceRow.skeleton<EBPFChartStruct>(); 249 rowRead.index = 5 + 2 * i; //@ts-ignore 250 rowRead.rowId = `FileSystemDiskIOLatency-read-${process.ipid}`; 251 rowRead.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 252 rowRead.rowParentId = folder.rowId; 253 rowRead.rowHidden = !folder.expansion; 254 rowRead.style.height = '40px'; 255 rowRead.style.width = '100%'; 256 rowRead.setAttribute('children', ''); //@ts-ignore 257 rowRead.name = `${process.name ?? 'Process'}(${process.ipid}) Max Read Latency`; 258 rowRead.supplierFrame = async (): Promise<EBPFChartStruct[]> => { 259 //@ts-ignore 260 const res = await diskIoSender(false, process.ipid, [1, 3], TraceRow.range?.scale || 50, rowRead); 261 return res; 262 }; 263 rowRead.favoriteChangeHandler = this.trace.favoriteChangeHandler; 264 rowRead.selectChangeHandler = this.trace.selectChangeHandler; 265 rowRead.focusHandler = (): void => this.focusHandler(rowRead); 266 rowRead.findHoverStruct = (): void => { 267 EBPFChartStruct.hoverEBPFStruct = rowRead.getHoverStruct(false); 268 }; 269 rowRead.onThreadHandler = (useCache): void => { 270 let context: CanvasRenderingContext2D; 271 if (rowRead.currentContext) { 272 context = rowRead.currentContext; 273 } else { 274 context = rowRead.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 275 } 276 rowRead.canvasSave(context); 277 //@ts-ignore 278 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( 279 { 280 context: context, 281 useCache: useCache, //@ts-ignore 282 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-disk-io-process-read-${process.pid}`, 283 chartColor: ColorUtils.MD_PALETTE[0], 284 }, 285 rowRead 286 ); 287 rowRead.canvasRestore(context, this.trace); 288 }; 289 return rowRead; 290 } 291 292 //@ts-ignore 293 private initProcessDiskIOWrite(i: number, folder: TraceRow<unknown>, process: unknown): TraceRow<EBPFChartStruct> { 294 let rowWrite = TraceRow.skeleton<EBPFChartStruct>(); 295 rowWrite.index = 5 + 2 * i + 1; //@ts-ignore 296 rowWrite.rowId = `FileSystemDiskIOLatency-write-${process.ipid}`; 297 rowWrite.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 298 rowWrite.rowParentId = folder.rowId; 299 rowWrite.rowHidden = !folder.expansion; 300 rowWrite.style.height = '40px'; 301 rowWrite.style.width = '100%'; 302 rowWrite.setAttribute('children', ''); //@ts-ignore 303 rowWrite.name = `${process.name ?? 'Process'}(${process.pid}) Max Write Latency`; 304 rowWrite.supplierFrame = async (): Promise<EBPFChartStruct[]> => { 305 //@ts-ignore 306 const res = await diskIoSender(false, process.ipid, [2, 4], TraceRow.range?.scale || 50, rowWrite); 307 return res; 308 }; 309 rowWrite.favoriteChangeHandler = this.trace.favoriteChangeHandler; 310 rowWrite.selectChangeHandler = this.trace.selectChangeHandler; 311 rowWrite.focusHandler = (): void => this.focusHandler(rowWrite); 312 rowWrite.findHoverStruct = (): void => { 313 EBPFChartStruct.hoverEBPFStruct = rowWrite.getHoverStruct(false); 314 }; 315 rowWrite.onThreadHandler = (useCache): void => { 316 let context: CanvasRenderingContext2D; 317 if (rowWrite.currentContext) { 318 context = rowWrite.currentContext; 319 } else { 320 context = rowWrite.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 321 } 322 rowWrite.canvasSave(context); 323 //@ts-ignore 324 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( 325 { 326 context: context, 327 useCache: useCache, //@ts-ignore 328 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-disk-io-process-write-${process.pid}`, 329 chartColor: ColorUtils.MD_PALETTE[8], 330 }, 331 rowWrite 332 ); 333 rowWrite.canvasRestore(context, this.trace); 334 }; 335 return rowWrite; 336 } 337 338 //@ts-ignore 339 async initProcessDiskIOLatency(folder: TraceRow<unknown>): Promise<void> { 340 let processes = (await getDiskIOProcess()) || []; 341 for (let i = 0, len = processes.length; i < len; i++) { 342 let process = processes[i]; 343 const rowRead = this.initProcessDiskIOLatencyRead(i, folder, process); 344 folder.addChildTraceRow(rowRead); 345 const rowWrite = this.initProcessDiskIOWrite(i, folder, process); 346 folder.addChildTraceRow(rowWrite); 347 } 348 } 349 350 //@ts-ignore 351 async initVirtualMemoryTrace(folder: TraceRow<unknown>): Promise<void> { 352 let vmTraceRow = TraceRow.skeleton<EBPFChartStruct>(); 353 vmTraceRow.rowId = 'FileSystemVirtualMemory'; 354 vmTraceRow.index = 3; 355 vmTraceRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 356 vmTraceRow.rowParentId = folder.rowId; 357 vmTraceRow.rowHidden = !folder.expansion; 358 vmTraceRow.rangeSelect = true; 359 vmTraceRow.style.height = '40px'; 360 vmTraceRow.style.width = '100%'; 361 vmTraceRow.setAttribute('children', ''); 362 vmTraceRow.name = 'Page Fault Trace'; 363 vmTraceRow.supplierFrame = async (): Promise<EBPFChartStruct[]> => { 364 const res = await fileSysVMSender(TraceRow.range?.scale || 50, vmTraceRow); 365 return res; 366 }; 367 vmTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 368 vmTraceRow.selectChangeHandler = this.trace.selectChangeHandler; 369 vmTraceRow.focusHandler = (): void => this.focusHandler(vmTraceRow); 370 vmTraceRow.findHoverStruct = (): void => { 371 EBPFChartStruct.hoverEBPFStruct = vmTraceRow.getHoverStruct(false, false, 'size'); 372 }; 373 vmTraceRow.onThreadHandler = (useCache): void => { 374 let context: CanvasRenderingContext2D; 375 if (vmTraceRow.currentContext) { 376 context = vmTraceRow.currentContext; 377 } else { 378 context = vmTraceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 379 } 380 vmTraceRow.canvasSave(context); 381 //@ts-ignore 382 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( 383 { 384 context: context, 385 useCache: useCache, 386 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-virtual-memory`, 387 chartColor: ColorUtils.MD_PALETTE[0], 388 }, 389 vmTraceRow 390 ); 391 vmTraceRow.canvasRestore(context, this.trace); 392 }; 393 folder.addChildTraceRow(vmTraceRow); 394 } 395 396 focusHandler(row: TraceRow<EBPFChartStruct>): void { 397 let num = 0; 398 let tip = ''; 399 if (EBPFChartStruct.hoverEBPFStruct) { 400 num = EBPFChartStruct.hoverEBPFStruct.size ?? 0; 401 let group10Ms = EBPFChartStruct.hoverEBPFStruct.group10Ms ?? false; 402 if (row.rowId!.startsWith('FileSystemDiskIOLatency')) { 403 if (num > 0) { 404 let tipStr = Utils.getProbablyTime(num); 405 if (group10Ms) { 406 tip = `<span>${tipStr} (10.00ms)</span>`; 407 } else { 408 tip = `<span>${tipStr}</span>`; 409 } 410 } 411 } else { 412 if (num > 0) { 413 if (group10Ms) { 414 tip = `<span>${num} (10.00ms)</span>`; 415 } else { 416 tip = `<span>${num}</span>`; 417 } 418 } 419 } 420 } 421 this.trace?.displayTip(row, EBPFChartStruct.hoverEBPFStruct, tip); 422 } 423} 424