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