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