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.js'; 17import { TraceRow } from '../trace/base/TraceRow.js'; 18import { procedurePool } from '../../database/Procedure.js'; 19import { 20 getDiskIOLatencyChartDataByProcess, 21 getDiskIOProcess, 22 getFileSysChartDataByType, 23 getFileSysVirtualMemoryChartData, 24 hasFileSysData, 25} from '../../database/SqlLite.js'; 26import { FileSysChartStruct, FileSystemRender } from '../../database/ui-worker/ProcedureWorkerFileSystem.js'; 27import { ColorUtils } from '../trace/base/ColorUtils.js'; 28import { Utils } from '../trace/base/Utils.js'; 29import { renders } from '../../database/ui-worker/ProcedureWorker.js'; 30import { EmptyRender } from '../../database/ui-worker/ProcedureWorkerCPU.js'; 31 32export class SpFileSystemChart { 33 private trace: SpSystemTrace; 34 35 constructor(trace: SpSystemTrace) { 36 this.trace = trace; 37 } 38 39 async init() { 40 let sys = await hasFileSysData(); 41 if (sys.length > 0) { 42 let fsCount = sys[0]['fsCount'] ?? 0; 43 let vmCount = sys[0]['vmCount'] ?? 0; 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() { 64 return new Promise<any>((resolve, reject) => { 65 procedurePool.submitWithName('logic0', 'fileSystem-init', SpSystemTrace.DATA_DICT, undefined, (res: any) => { 66 resolve(res); 67 }); 68 }); 69 } 70 71 async initFolder(): Promise<TraceRow<any>> { 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.favoriteChangeHandler = this.trace.favoriteChangeHandler; 81 fsFolder.selectChangeHandler = this.trace.selectChangeHandler; 82 fsFolder.supplier = () => new Promise<Array<any>>((resolve) => resolve([])); 83 fsFolder.onThreadHandler = (useCache) => { 84 fsFolder.canvasSave(this.trace.canvasPanelCtx!); 85 if (fsFolder.expansion) { 86 this.trace.canvasPanelCtx?.clearRect(0, 0, fsFolder.frame.width, fsFolder.frame.height); 87 } else { 88 (renders['empty'] as EmptyRender).renderMainThread( 89 { 90 context: this.trace.canvasPanelCtx, 91 useCache: useCache, 92 type: ``, 93 }, 94 fsFolder 95 ); 96 } 97 fsFolder.canvasRestore(this.trace.canvasPanelCtx!); 98 }; 99 this.trace.rowsEL?.appendChild(fsFolder); 100 return fsFolder; 101 } 102 103 async initLogicalRead(folder: TraceRow<any>) { 104 let logicalReadRow = TraceRow.skeleton<FileSysChartStruct>(); 105 logicalReadRow.rowId = `FileSystemLogicalRead`; 106 logicalReadRow.index = 1; 107 logicalReadRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 108 logicalReadRow.rowParentId = folder.rowId; 109 logicalReadRow.rowHidden = !folder.expansion; 110 logicalReadRow.style.height = '40px'; 111 logicalReadRow.setAttribute('children', ''); 112 logicalReadRow.name = `FileSystem Logical Read`; 113 logicalReadRow.supplier = () => getFileSysChartDataByType(2); 114 logicalReadRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 115 logicalReadRow.selectChangeHandler = this.trace.selectChangeHandler; 116 logicalReadRow.focusHandler = () => this.focusHandler(logicalReadRow); 117 logicalReadRow.onThreadHandler = (useCache) => { 118 let context = logicalReadRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 119 logicalReadRow.canvasSave(context); 120 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as FileSystemRender).renderMainThread( 121 { 122 context: context, 123 useCache: useCache, 124 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-logical-read`, 125 chartColor: ColorUtils.MD_PALETTE[0], 126 }, 127 logicalReadRow 128 ); 129 logicalReadRow.canvasRestore(context); 130 }; 131 folder.addChildTraceRow(logicalReadRow); 132 } 133 134 async initLogicalWrite(folder: TraceRow<any>) { 135 let logicalWriteRow = TraceRow.skeleton<FileSysChartStruct>(); 136 logicalWriteRow.rowId = `FileSystemLogicalWrite`; 137 logicalWriteRow.index = 2; 138 logicalWriteRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 139 logicalWriteRow.rowParentId = folder.rowId; 140 logicalWriteRow.rowHidden = !folder.expansion; 141 logicalWriteRow.style.height = '40px'; 142 logicalWriteRow.setAttribute('children', ''); 143 logicalWriteRow.name = `FileSystem Logical Write`; 144 logicalWriteRow.supplier = () => getFileSysChartDataByType(3); 145 logicalWriteRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 146 logicalWriteRow.selectChangeHandler = this.trace.selectChangeHandler; 147 logicalWriteRow.focusHandler = () => this.focusHandler(logicalWriteRow); 148 logicalWriteRow.onThreadHandler = (useCache) => { 149 let context = logicalWriteRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 150 logicalWriteRow.canvasSave(context); 151 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as FileSystemRender).renderMainThread( 152 { 153 context: context, 154 useCache: useCache, 155 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-logical-write`, 156 chartColor: ColorUtils.MD_PALETTE[8], 157 }, 158 logicalWriteRow 159 ); 160 logicalWriteRow.canvasRestore(context); 161 }; 162 folder.addChildTraceRow(logicalWriteRow); 163 } 164 165 async initDiskIOLatency(folder: TraceRow<any>) { 166 let diskIoRow = TraceRow.skeleton<FileSysChartStruct>(); 167 diskIoRow.rowId = `FileSystemDiskIOLatency`; 168 diskIoRow.index = 4; 169 diskIoRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 170 diskIoRow.rowParentId = folder.rowId; 171 diskIoRow.rowHidden = !folder.expansion; 172 diskIoRow.style.height = '40px'; 173 diskIoRow.style.width = `100%`; 174 diskIoRow.setAttribute('children', ''); 175 diskIoRow.name = `Disk I/O Latency`; 176 diskIoRow.supplier = () => getDiskIOLatencyChartDataByProcess(true, 0, [1, 2, 3, 4]); 177 diskIoRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 178 diskIoRow.selectChangeHandler = this.trace.selectChangeHandler; 179 diskIoRow.focusHandler = () => this.focusHandler(diskIoRow); 180 diskIoRow.onThreadHandler = (useCache) => { 181 let context = diskIoRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 182 diskIoRow.canvasSave(context); 183 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as FileSystemRender).renderMainThread( 184 { 185 context: context, 186 useCache: useCache, 187 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-disk-io`, 188 chartColor: ColorUtils.MD_PALETTE[0], 189 }, 190 diskIoRow 191 ); 192 diskIoRow.canvasRestore(context); 193 }; 194 folder.addChildTraceRow(diskIoRow); 195 } 196 197 async initProcessDiskIOLatency(folder: TraceRow<any>) { 198 let processes = (await getDiskIOProcess()) || []; 199 for (let i = 0, len = processes.length; i < len; i++) { 200 let process = processes[i]; 201 let rowRead = TraceRow.skeleton<FileSysChartStruct>(); 202 rowRead.index = 5 + 2 * i; 203 rowRead.rowId = `FileSystemDiskIOLatency-read-${process['ipid']}`; 204 rowRead.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 205 rowRead.rowParentId = folder.rowId; 206 rowRead.rowHidden = !folder.expansion; 207 rowRead.style.height = '40px'; 208 rowRead.style.width = `100%`; 209 rowRead.setAttribute('children', ''); 210 rowRead.name = `${process['name'] ?? 'Process'}(${process['pid']}) Max Read Latency`; 211 rowRead.supplier = () => getDiskIOLatencyChartDataByProcess(false, process['ipid'], [1, 3]); 212 rowRead.favoriteChangeHandler = this.trace.favoriteChangeHandler; 213 rowRead.selectChangeHandler = this.trace.selectChangeHandler; 214 rowRead.focusHandler = () => this.focusHandler(rowRead); 215 rowRead.onThreadHandler = (useCache) => { 216 let context = rowRead.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 217 rowRead.canvasSave(context); 218 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as FileSystemRender).renderMainThread( 219 { 220 context: context, 221 useCache: useCache, 222 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-disk-io-process-read-${process['pid']}`, 223 chartColor: ColorUtils.MD_PALETTE[0], 224 }, 225 rowRead 226 ); 227 rowRead.canvasRestore(context); 228 }; 229 folder.addChildTraceRow(rowRead); 230 let rowWrite = TraceRow.skeleton<FileSysChartStruct>(); 231 rowWrite.index = 5 + 2 * i + 1; 232 rowWrite.rowId = `FileSystemDiskIOLatency-write-${process['ipid']}`; 233 rowWrite.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 234 rowWrite.rowParentId = folder.rowId; 235 rowWrite.rowHidden = !folder.expansion; 236 rowWrite.style.height = '40px'; 237 rowWrite.style.width = `100%`; 238 rowWrite.setAttribute('children', ''); 239 rowWrite.name = `${process['name'] ?? 'Process'}(${process['pid']}) Max Write Latency`; 240 rowWrite.supplier = () => getDiskIOLatencyChartDataByProcess(false, process['ipid'], [2, 4]); 241 rowWrite.favoriteChangeHandler = this.trace.favoriteChangeHandler; 242 rowWrite.selectChangeHandler = this.trace.selectChangeHandler; 243 rowWrite.focusHandler = () => this.focusHandler(rowWrite); 244 rowWrite.onThreadHandler = (useCache) => { 245 let context = rowWrite.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 246 rowWrite.canvasSave(context); 247 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as FileSystemRender).renderMainThread( 248 { 249 context: context, 250 useCache: useCache, 251 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-disk-io-process-write-${process['pid']}`, 252 chartColor: ColorUtils.MD_PALETTE[8], 253 }, 254 rowWrite 255 ); 256 rowWrite.canvasRestore(context); 257 }; 258 folder.addChildTraceRow(rowWrite); 259 } 260 } 261 262 async initVirtualMemoryTrace(folder: TraceRow<any>) { 263 let vmTraceRow = TraceRow.skeleton<FileSysChartStruct>(); 264 vmTraceRow.rowId = `FileSystemVirtualMemory`; 265 vmTraceRow.index = 3; 266 vmTraceRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; 267 vmTraceRow.rowParentId = folder.rowId; 268 vmTraceRow.rowHidden = !folder.expansion; 269 vmTraceRow.rangeSelect = true; 270 vmTraceRow.style.height = '40px'; 271 vmTraceRow.style.width = `100%`; 272 vmTraceRow.setAttribute('children', ''); 273 vmTraceRow.name = `Page Fault Trace`; 274 vmTraceRow.supplier = () => getFileSysVirtualMemoryChartData(); 275 vmTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 276 vmTraceRow.selectChangeHandler = this.trace.selectChangeHandler; 277 vmTraceRow.focusHandler = () => this.focusHandler(vmTraceRow); 278 vmTraceRow.onThreadHandler = (useCache) => { 279 let context = vmTraceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 280 vmTraceRow.canvasSave(context); 281 (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as FileSystemRender).renderMainThread( 282 { 283 context: context, 284 useCache: useCache, 285 type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-virtual-memory`, 286 chartColor: ColorUtils.MD_PALETTE[0], 287 }, 288 vmTraceRow 289 ); 290 vmTraceRow.canvasRestore(context); 291 }; 292 folder.addChildTraceRow(vmTraceRow); 293 } 294 295 focusHandler(row: TraceRow<FileSysChartStruct>) { 296 let num = 0; 297 let tip = ''; 298 if (FileSysChartStruct.hoverFileSysStruct) { 299 num = FileSysChartStruct.hoverFileSysStruct.size ?? 0; 300 let group10Ms = FileSysChartStruct.hoverFileSysStruct.group10Ms ?? false; 301 if (row.rowId!.startsWith('FileSystemDiskIOLatency')) { 302 if (num > 0) { 303 let tipStr = Utils.getProbablyTime(num); 304 if (group10Ms) { 305 tip = `<span>${tipStr} (10.00ms)</span>`; 306 } else { 307 tip = `<span>${tipStr}</span>`; 308 } 309 } 310 } else { 311 if (num > 0) { 312 if (group10Ms) { 313 tip = `<span>${num} (10.00ms)</span>`; 314 } else { 315 tip = `<span>${num}</span>`; 316 } 317 } 318 } 319 } 320 this.trace?.displayTip(row, FileSysChartStruct.hoverFileSysStruct, tip); 321 } 322} 323