/* * Copyright (C) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { SpSystemTrace } from '../SpSystemTrace'; import { TraceRow } from '../trace/base/TraceRow'; import { procedurePool } from '../../database/Procedure'; import { EBPFChartStruct, EBPFRender } from '../../database/ui-worker/ProcedureWorkerEBPF'; import { ColorUtils } from '../trace/base/ColorUtils'; import { Utils } from '../trace/base/Utils'; import { renders } from '../../database/ui-worker/ProcedureWorker'; import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; import { diskIoSender, fileSysVMSender, fileSystemSender } from '../../database/data-trafic/EBPFSender'; import { hasFileSysData } from '../../database/sql/Memory.sql'; import { getDiskIOProcess } from '../../database/sql/SqlLite.sql'; import { SpStatisticsHttpUtil } from '../../../statistics/util/SpStatisticsHttpUtil'; export class SpEBPFChart { private trace: SpSystemTrace; constructor(trace: SpSystemTrace) { this.trace = trace; } async init(): Promise { let sys = await hasFileSysData(); if (sys.length > 0) { //@ts-ignore let fsCount = sys[0].fsCount ?? 0; //@ts-ignore let vmCount = sys[0].vmCount ?? 0; //@ts-ignore let ioCount = sys[0].ioCount ?? 0; if (sys && sys.length > 0 && (fsCount > 0 || vmCount > 0 || ioCount > 0)) { let folder = await this.initFolder(); await this.initFileCallchain(); if (fsCount > 0) { await this.initLogicalRead(folder); await this.initLogicalWrite(folder); } if (vmCount > 0) { await this.initVirtualMemoryTrace(folder); } if (ioCount > 0) { await this.initDiskIOLatency(folder); await this.initProcessDiskIOLatency(folder); } // 统计ebpf插件 let requsetBody = { eventData: { plugin: ['hiebpf-plugin'] } }; SpStatisticsHttpUtil.recordPluginUsage(requsetBody); } } } async initFileCallchain(): Promise { return new Promise((resolve, reject) => { procedurePool.submitWithName('logic0', 'fileSystem-init', null, undefined, (res: unknown) => { resolve(res); }); }); } //@ts-ignore async initFolder(): Promise> { let fsFolder = TraceRow.skeleton(); fsFolder.rowId = 'FileSystem'; fsFolder.index = 0; fsFolder.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM_GROUP; fsFolder.rowParentId = ''; fsFolder.style.height = '40px'; fsFolder.folder = true; fsFolder.name = 'EBPF'; /* & I/O Latency */ fsFolder.addTemplateTypes('HiEBpf'); fsFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler; fsFolder.selectChangeHandler = this.trace.selectChangeHandler; //@ts-ignore fsFolder.supplierFrame = (): Promise> => new Promise>((resolve) => resolve([])); fsFolder.onThreadHandler = (useCache): void => { fsFolder.canvasSave(this.trace.canvasPanelCtx!); if (fsFolder.expansion) { // @ts-ignore this.trace.canvasPanelCtx?.clearRect(0, 0, fsFolder.frame.width, fsFolder.frame.height); } else { (renders.empty as EmptyRender).renderMainThread( { context: this.trace.canvasPanelCtx, useCache: useCache, type: '', }, fsFolder ); } fsFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace); }; this.trace.rowsEL?.appendChild(fsFolder); return fsFolder; } //@ts-ignore async initLogicalRead(folder: TraceRow): Promise { let logicalReadRow = TraceRow.skeleton(); logicalReadRow.rowId = 'FileSystemLogicalRead'; logicalReadRow.index = 1; logicalReadRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; logicalReadRow.rowParentId = folder.rowId; logicalReadRow.rowHidden = !folder.expansion; logicalReadRow.style.height = '40px'; logicalReadRow.setAttribute('children', ''); logicalReadRow.name = 'FileSystem Logical Read'; logicalReadRow.supplierFrame = async (): Promise => { const res = await fileSystemSender(2, TraceRow.range?.scale || 50, logicalReadRow); return res; }; logicalReadRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; logicalReadRow.selectChangeHandler = this.trace.selectChangeHandler; logicalReadRow.focusHandler = (): void => this.focusHandler(logicalReadRow); logicalReadRow.findHoverStruct = (): void => { EBPFChartStruct.hoverEBPFStruct = logicalReadRow.getHoverStruct(false); }; logicalReadRow.onThreadHandler = (useCache): void => { let context: CanvasRenderingContext2D; if (logicalReadRow.currentContext) { context = logicalReadRow.currentContext; } else { context = logicalReadRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; } logicalReadRow.canvasSave(context); //@ts-ignore (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( { context: context, useCache: useCache, type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-logical-read`, chartColor: ColorUtils.MD_PALETTE[0], }, logicalReadRow ); logicalReadRow.canvasRestore(context, this.trace); }; folder.addChildTraceRow(logicalReadRow); } //@ts-ignore async initLogicalWrite(folder: TraceRow): Promise { let logicalWriteRow = TraceRow.skeleton(); logicalWriteRow.rowId = 'FileSystemLogicalWrite'; logicalWriteRow.index = 2; logicalWriteRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; logicalWriteRow.rowParentId = folder.rowId; logicalWriteRow.rowHidden = !folder.expansion; logicalWriteRow.style.height = '40px'; logicalWriteRow.setAttribute('children', ''); logicalWriteRow.name = 'FileSystem Logical Write'; logicalWriteRow.supplierFrame = async (): Promise => { const res = await fileSystemSender(3, TraceRow.range?.scale || 50, logicalWriteRow); return res; }; logicalWriteRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; logicalWriteRow.selectChangeHandler = this.trace.selectChangeHandler; logicalWriteRow.focusHandler = (): void => this.focusHandler(logicalWriteRow); logicalWriteRow.findHoverStruct = (): void => { EBPFChartStruct.hoverEBPFStruct = logicalWriteRow.getHoverStruct(false); }; logicalWriteRow.onThreadHandler = (useCache): void => { let context: CanvasRenderingContext2D; if (logicalWriteRow.currentContext) { context = logicalWriteRow.currentContext; } else { context = logicalWriteRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; } logicalWriteRow.canvasSave(context); //@ts-ignore (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( { context: context, useCache: useCache, type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-logical-write`, chartColor: ColorUtils.MD_PALETTE[8], }, logicalWriteRow ); logicalWriteRow.canvasRestore(context, this.trace); }; folder.addChildTraceRow(logicalWriteRow); } //@ts-ignore async initDiskIOLatency(folder: TraceRow): Promise { let diskIoRow = TraceRow.skeleton(); diskIoRow.rowId = 'FileSystemDiskIOLatency'; diskIoRow.index = 4; diskIoRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; diskIoRow.rowParentId = folder.rowId; diskIoRow.rowHidden = !folder.expansion; diskIoRow.style.height = '40px'; diskIoRow.style.width = '100%'; diskIoRow.setAttribute('children', ''); diskIoRow.name = 'Disk I/O Latency'; diskIoRow.supplierFrame = async (): Promise => { const res = await diskIoSender(true, 0, [1, 2, 3, 4], TraceRow.range?.scale || 50, diskIoRow); return res; }; diskIoRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; diskIoRow.selectChangeHandler = this.trace.selectChangeHandler; diskIoRow.focusHandler = (): void => this.focusHandler(diskIoRow); diskIoRow.findHoverStruct = (): void => { EBPFChartStruct.hoverEBPFStruct = diskIoRow.getHoverStruct(false); }; diskIoRow.onThreadHandler = (useCache): void => { let context: CanvasRenderingContext2D; if (diskIoRow.currentContext) { context = diskIoRow.currentContext; } else { context = diskIoRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; } diskIoRow.canvasSave(context); //@ts-ignore (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( { context: context, useCache: useCache, type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-disk-io`, chartColor: ColorUtils.MD_PALETTE[0], }, diskIoRow ); diskIoRow.canvasRestore(context, this.trace); }; folder.addChildTraceRow(diskIoRow); } //@ts-ignore initProcessDiskIOLatencyRead(i: number, folder: TraceRow, process: unknown): TraceRow { let rowRead = TraceRow.skeleton(); rowRead.index = 5 + 2 * i; //@ts-ignore rowRead.rowId = `FileSystemDiskIOLatency-read-${process.ipid}`; rowRead.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; rowRead.rowParentId = folder.rowId; rowRead.rowHidden = !folder.expansion; rowRead.style.height = '40px'; rowRead.style.width = '100%'; rowRead.setAttribute('children', ''); //@ts-ignore rowRead.name = `${process.name ?? 'Process'}(${process.ipid}) Max Read Latency`; rowRead.supplierFrame = async (): Promise => { //@ts-ignore const res = await diskIoSender(false, process.ipid, [1, 3], TraceRow.range?.scale || 50, rowRead); return res; }; rowRead.favoriteChangeHandler = this.trace.favoriteChangeHandler; rowRead.selectChangeHandler = this.trace.selectChangeHandler; rowRead.focusHandler = (): void => this.focusHandler(rowRead); rowRead.findHoverStruct = (): void => { EBPFChartStruct.hoverEBPFStruct = rowRead.getHoverStruct(false); }; rowRead.onThreadHandler = (useCache): void => { let context: CanvasRenderingContext2D; if (rowRead.currentContext) { context = rowRead.currentContext; } else { context = rowRead.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; } rowRead.canvasSave(context); //@ts-ignore (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( { context: context, useCache: useCache, //@ts-ignore type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-disk-io-process-read-${process.pid}`, chartColor: ColorUtils.MD_PALETTE[0], }, rowRead ); rowRead.canvasRestore(context, this.trace); }; return rowRead; } //@ts-ignore private initProcessDiskIOWrite(i: number, folder: TraceRow, process: unknown): TraceRow { let rowWrite = TraceRow.skeleton(); rowWrite.index = 5 + 2 * i + 1; //@ts-ignore rowWrite.rowId = `FileSystemDiskIOLatency-write-${process.ipid}`; rowWrite.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; rowWrite.rowParentId = folder.rowId; rowWrite.rowHidden = !folder.expansion; rowWrite.style.height = '40px'; rowWrite.style.width = '100%'; rowWrite.setAttribute('children', ''); //@ts-ignore rowWrite.name = `${process.name ?? 'Process'}(${process.pid}) Max Write Latency`; rowWrite.supplierFrame = async (): Promise => { //@ts-ignore const res = await diskIoSender(false, process.ipid, [2, 4], TraceRow.range?.scale || 50, rowWrite); return res; }; rowWrite.favoriteChangeHandler = this.trace.favoriteChangeHandler; rowWrite.selectChangeHandler = this.trace.selectChangeHandler; rowWrite.focusHandler = (): void => this.focusHandler(rowWrite); rowWrite.findHoverStruct = (): void => { EBPFChartStruct.hoverEBPFStruct = rowWrite.getHoverStruct(false); }; rowWrite.onThreadHandler = (useCache): void => { let context: CanvasRenderingContext2D; if (rowWrite.currentContext) { context = rowWrite.currentContext; } else { context = rowWrite.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; } rowWrite.canvasSave(context); //@ts-ignore (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( { context: context, useCache: useCache, //@ts-ignore type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-disk-io-process-write-${process.pid}`, chartColor: ColorUtils.MD_PALETTE[8], }, rowWrite ); rowWrite.canvasRestore(context, this.trace); }; return rowWrite; } //@ts-ignore async initProcessDiskIOLatency(folder: TraceRow): Promise { let processes = (await getDiskIOProcess()) || []; for (let i = 0, len = processes.length; i < len; i++) { let process = processes[i]; const rowRead = this.initProcessDiskIOLatencyRead(i, folder, process); folder.addChildTraceRow(rowRead); const rowWrite = this.initProcessDiskIOWrite(i, folder, process); folder.addChildTraceRow(rowWrite); } } //@ts-ignore async initVirtualMemoryTrace(folder: TraceRow): Promise { let vmTraceRow = TraceRow.skeleton(); vmTraceRow.rowId = 'FileSystemVirtualMemory'; vmTraceRow.index = 3; vmTraceRow.rowType = TraceRow.ROW_TYPE_FILE_SYSTEM; vmTraceRow.rowParentId = folder.rowId; vmTraceRow.rowHidden = !folder.expansion; vmTraceRow.rangeSelect = true; vmTraceRow.style.height = '40px'; vmTraceRow.style.width = '100%'; vmTraceRow.setAttribute('children', ''); vmTraceRow.name = 'Page Fault Trace'; vmTraceRow.supplierFrame = async (): Promise => { const res = await fileSysVMSender(TraceRow.range?.scale || 50, vmTraceRow); return res; }; vmTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; vmTraceRow.selectChangeHandler = this.trace.selectChangeHandler; vmTraceRow.focusHandler = (): void => this.focusHandler(vmTraceRow); vmTraceRow.findHoverStruct = (): void => { EBPFChartStruct.hoverEBPFStruct = vmTraceRow.getHoverStruct(false, false, 'size'); }; vmTraceRow.onThreadHandler = (useCache): void => { let context: CanvasRenderingContext2D; if (vmTraceRow.currentContext) { context = vmTraceRow.currentContext; } else { context = vmTraceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; } vmTraceRow.canvasSave(context); //@ts-ignore (renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread( { context: context, useCache: useCache, type: `${TraceRow.ROW_TYPE_FILE_SYSTEM}-virtual-memory`, chartColor: ColorUtils.MD_PALETTE[0], }, vmTraceRow ); vmTraceRow.canvasRestore(context, this.trace); }; folder.addChildTraceRow(vmTraceRow); } focusHandler(row: TraceRow): void { let num = 0; let tip = ''; if (EBPFChartStruct.hoverEBPFStruct) { num = EBPFChartStruct.hoverEBPFStruct.size ?? 0; let group10Ms = EBPFChartStruct.hoverEBPFStruct.group10Ms ?? false; if (row.rowId!.startsWith('FileSystemDiskIOLatency')) { if (num > 0) { let tipStr = Utils.getProbablyTime(num); if (group10Ms) { tip = `${tipStr} (10.00ms)`; } else { tip = `${tipStr}`; } } } else { if (num > 0) { if (group10Ms) { tip = `${num} (10.00ms)`; } else { tip = `${num}`; } } } } this.trace?.displayTip(row, EBPFChartStruct.hoverEBPFStruct, tip); } }