* 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<void> {
let sys = await hasFileSysData();
if (sys.length > 0) {
let fsCount = sys[0].fsCount ?? 0;
let vmCount = sys[0].vmCount ?? 0;
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);
}
}
}
}
async initFileCallchain(): Promise<unknown> {
return new Promise<unknown>((resolve, reject) => {
procedurePool.submitWithName('logic0', 'fileSystem-init', null, undefined, (res: unknown) => {
resolve(res);
});
});
}
async initFolder(): Promise<TraceRow<unknown>> {
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';
fsFolder.addTemplateTypes('HiEBpf');
fsFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler;
fsFolder.selectChangeHandler = this.trace.selectChangeHandler;
fsFolder.supplierFrame = (): Promise<Array<unknown>> => new Promise<Array<unknown>>((resolve) => resolve([]));
fsFolder.onThreadHandler = (useCache): void => {
fsFolder.canvasSave(this.trace.canvasPanelCtx!);
if (fsFolder.expansion) {
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;
}
async initLogicalRead(folder: TraceRow<unknown>): Promise<void> {
let logicalReadRow = TraceRow.skeleton<EBPFChartStruct>();
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<EBPFChartStruct[]> => {
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);
(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);
}
async initLogicalWrite(folder: TraceRow<unknown>): Promise<void> {
let logicalWriteRow = TraceRow.skeleton<EBPFChartStruct>();
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<EBPFChartStruct[]> => {
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);
(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);
}
async initDiskIOLatency(folder: TraceRow<unknown>): Promise<void> {
let diskIoRow = TraceRow.skeleton<EBPFChartStruct>();
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<EBPFChartStruct[]> => {
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);
(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);
}
initProcessDiskIOLatencyRead(i: number, folder: TraceRow<unknown>, process: unknown): TraceRow<EBPFChartStruct> {
let rowRead = TraceRow.skeleton<EBPFChartStruct>();
rowRead.index = 5 + 2 * i;
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', '');
rowRead.name = `${process.name ?? 'Process'}(${process.ipid}) Max Read Latency`;
rowRead.supplierFrame = async (): Promise<EBPFChartStruct[]> => {
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);
(renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread(
{
context: context,
useCache: useCache,
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;
}
private initProcessDiskIOWrite(i: number, folder: TraceRow<unknown>, process: unknown): TraceRow<EBPFChartStruct> {
let rowWrite = TraceRow.skeleton<EBPFChartStruct>();
rowWrite.index = 5 + 2 * i + 1;
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', '');
rowWrite.name = `${process.name ?? 'Process'}(${process.pid}) Max Write Latency`;
rowWrite.supplierFrame = async (): Promise<EBPFChartStruct[]> => {
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);
(renders[TraceRow.ROW_TYPE_FILE_SYSTEM] as EBPFRender).renderMainThread(
{
context: context,
useCache: useCache,
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;
}
async initProcessDiskIOLatency(folder: TraceRow<unknown>): Promise<void> {
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);
}
}
async initVirtualMemoryTrace(folder: TraceRow<unknown>): Promise<void> {
let vmTraceRow = TraceRow.skeleton<EBPFChartStruct>();
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<EBPFChartStruct[]> => {
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);
(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<EBPFChartStruct>): 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 = `<span>${tipStr} (10.00ms)</span>`;
} else {
tip = `<span>${tipStr}</span>`;
}
}
} else {
if (num > 0) {
if (group10Ms) {
tip = `<span>${num} (10.00ms)</span>`;
} else {
tip = `<span>${num}</span>`;
}
}
}
}
this.trace?.displayTip(row, EBPFChartStruct.hoverEBPFStruct, tip);
}
}