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