* 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 { type BaseStruct } from '../../bean/BaseStruct';
import { renders } from '../../database/ui-worker/ProcedureWorker';
import { Utils } from '../trace/base/Utils';
import { type EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
import { info } from '../../../log/Log';
import { type SnapshotRender, SnapshotStruct } from '../../database/ui-worker/ProcedureWorkerSnapshot';
import { type TreeItemData } from '../../../base-ui/tree/LitTree';
import { MemoryConfig } from '../../bean/MemoryConfig';
import { TabPaneSmapsRecord } from '../trace/sheet/smaps/TabPaneSmapsRecord';
import {
dmaDataSender,
gpuGpuDataSender,
gpuMemoryDataSender,
gpuResourceDataSender,
gpuTotalDataSender,
gpuWindowDataSender,
purgeableDataSender,
sMapsDataSender,
shmDataSender,
} from '../../database/data-trafic/VmTrackerDataSender';
import { resetVmTracker } from '../../database/data-trafic/VmTrackerDataReceiver';
import { querySmapsExits } from '../../database/sql/Smaps.sql';
import {
queryisExistsGpuMemoryData,
queryisExistsPurgeableData,
queryisExistsShmData,
} from '../../database/sql/Memory.sql';
import { queryisExistsDmaData } from '../../database/sql/Dma.sql';
import {
queryGpuTotalType,
queryGpuWindowType,
queryisExistsGpuData,
queryisExistsGpuResourceData,
} from '../../database/sql/Gpu.sql';
import { SpStatisticsHttpUtil } from '../../../statistics/util/SpStatisticsHttpUtil';
export class VmTrackerChart {
private trace: SpSystemTrace;
private rowFolder!: TraceRow<BaseStruct>;
private sMapsFolder!: TraceRow<BaseStruct>;
private gpuFolder!: TraceRow<BaseStruct>;
private memoryConfig: MemoryConfig = MemoryConfig.getInstance();
static gpuTotalModule: number | null = null;
static gpuWindow: number | null = null;
static gpuWindowModule: number | null = null;
private smapsRecordTab: TabPaneSmapsRecord | undefined | null;
private scratchId = -1;
private isExistsPurgeableTotal: Array<unknown> = [];
private isExistsPurgeablePin: Array<unknown> = [];
private isExistsGpuMemory: Array<unknown> = [];
private isExistsGpuResource: Array<unknown> = [];
private isExistsGraph: Array<unknown> = [];
private isExistsGl: Array<unknown> = [];
constructor(trace: SpSystemTrace) {
this.trace = trace;
}
async init(): Promise<void> {
this.smapsRecordTab = this.trace
.shadowRoot!.querySelector('div > trace-sheet')!
.shadowRoot!.querySelector<TabPaneSmapsRecord>('#box-smaps-record > tabpane-smaps-record');
if (this.scratchId === -1) {
for (let [key, value] of SpSystemTrace.DATA_DICT) {
if (value === 'Scratch') {
this.scratchId = key;
break;
}
}
}
const result = await querySmapsExits();
this.isExistsPurgeableTotal = await queryisExistsPurgeableData(this.memoryConfig.iPid, false);
this.isExistsPurgeablePin = await queryisExistsPurgeableData(this.memoryConfig.iPid, true);
this.isExistsGpuMemory = await queryisExistsGpuMemoryData(this.memoryConfig.iPid);
this.isExistsGpuResource = await queryisExistsGpuResourceData(this.scratchId);
this.isExistsGraph = await queryisExistsGpuData(MemoryConfig.getInstance().iPid, "'mem.graph_pss'");
this.isExistsGl = await queryisExistsGpuData(MemoryConfig.getInstance().iPid, "'mem.gl_pss'");
const isExistsShm = await queryisExistsShmData(this.memoryConfig.iPid);
const isExistsDma = await queryisExistsDmaData(this.memoryConfig.iPid);
if (result.length === 0 && isExistsShm[0].data_exists === 0 && isExistsDma[0].data_exists === 0 && this.isExistsPurgeableTotal[0].data_exists === 0 &&
this.isExistsPurgeablePin[0].data_exists === 0 && this.isExistsGpuMemory[0].data_exists === 0 && this.isExistsGpuResource[0].data_exists === 0 &&
this.isExistsGraph[0].data_exists === 0 && this.isExistsGl[0].data_exists === 0) {
return;
}
await this.initVmTrackerFolder();
if (result.length > 0) {
await this.initSMapsFolder();
const rowNameList: Array<string> = ['Dirty', 'Swapped', 'RSS', 'PSS', 'USS'];
for (const rowName of rowNameList) {
await this.initSmapsRows(rowName);
}
}
if (isExistsShm[0].data_exists) {
await this.initShmRows();
}
await this.initPurgeableVM();
if (isExistsDma[0].data_exists) {
await this.initDmaRow();
}
await this.initGpuData();
}
private async initGpuData(): Promise<void> {
if (
this.isExistsGpuMemory[0].data_exists ||
this.isExistsGpuResource[0].data_exists ||
this.isExistsGraph[0].data_exists ||
this.isExistsGl[0].data_exists
) {
await this.initGpuFolder();
if (this.isExistsGpuMemory[0].data_exists) {
await this.initGpuMemoryRow();
}
if (this.isExistsGpuResource[0].data_exists) {
await this.initGpuResourceRow(this.scratchId);
} else {
this.smapsRecordTab!.GLESHostCache = [];
}
if (this.isExistsGraph[0].data_exists) {
await this.addGpuGraphRow();
}
if (this.isExistsGl[0].data_exists) {
await this.addGpuGLRow();
await this.addGpuTotalRow();
await this.addGpuWindowRow();
}
}
}
private initVmTrackerFolder = async (): Promise<void> => {
let VmTrackerRow = TraceRow.skeleton();
VmTrackerRow.rowId = 'VmTrackerRow';
VmTrackerRow.rowType = TraceRow.ROW_TYPE_VM_TRACKER;
VmTrackerRow.addTemplateTypes('ProcessMemory');
VmTrackerRow.addTemplateTypes('Memory');
VmTrackerRow.rowParentId = '';
VmTrackerRow.style.height = '40px';
VmTrackerRow.index = 0;
VmTrackerRow.folder = true;
VmTrackerRow.name = `VM Tracker (${this.memoryConfig.processName} ${this.memoryConfig.pid})`;
VmTrackerRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
VmTrackerRow.selectChangeHandler = this.trace.selectChangeHandler;
VmTrackerRow.supplierFrame = (): Promise<Array<SnapshotStruct>> =>
new Promise<Array<SnapshotStruct>>((resolve) => resolve([]));
VmTrackerRow.onThreadHandler = (useCache): void => {
let context: CanvasRenderingContext2D;
if (VmTrackerRow.currentContext) {
context = VmTrackerRow.currentContext;
} else {
context = VmTrackerRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
}
VmTrackerRow.canvasSave(context);
if (VmTrackerRow.expansion) {
context?.clearRect(0, 0, VmTrackerRow.frame.width, VmTrackerRow.frame.height);
} else {
(renders.empty as EmptyRender).renderMainThread(
{
context: context,
useCache: useCache,
type: '',
},
VmTrackerRow
);
}
VmTrackerRow.canvasRestore(context, this.trace);
};
this.rowFolder = VmTrackerRow;
this.trace.rowsEL?.appendChild(VmTrackerRow);
};
private initSMapsFolder = async (): Promise<void> => {
let sMapsRow = TraceRow.skeleton<SnapshotStruct>();
sMapsRow.rowId = 'smapsRow';
sMapsRow.rowParentId = 'VmTrackerRow';
sMapsRow.rowHidden = !this.rowFolder.expansion;
sMapsRow.rowType = TraceRow.ROW_TYPE_VM_TRACKER_SMAPS;
sMapsRow.folder = true;
sMapsRow.name = 'Smaps';
sMapsRow.folderPaddingLeft = 20;
sMapsRow.style.height = '40px';
sMapsRow.style.width = '100%';
sMapsRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
sMapsRow.selectChangeHandler = this.trace.selectChangeHandler;
sMapsRow.supplierFrame = (): Promise<Array<SnapshotStruct>> =>
new Promise<Array<SnapshotStruct>>((resolve) => resolve([]));
sMapsRow.onThreadHandler = (useCache): void => {
let context: CanvasRenderingContext2D;
if (sMapsRow.currentContext) {
context = sMapsRow.currentContext;
} else {
context = sMapsRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
}
sMapsRow.canvasSave(context);
if (sMapsRow.expansion) {
context?.clearRect(0, 0, sMapsRow.frame.width, sMapsRow.frame.height);
} else {
(renders.empty as EmptyRender).renderMainThread(
{
context: context,
useCache: useCache,
type: '',
},
sMapsRow
);
}
sMapsRow.canvasRestore(context, this.trace);
};
this.sMapsFolder = sMapsRow;
this.rowFolder?.addChildTraceRow(sMapsRow);
};
private initGpuFolder = async (): Promise<TraceRow<SnapshotStruct>> => {
let gpuTraceRow = TraceRow.skeleton<SnapshotStruct>();
gpuTraceRow.rowId = 'skiaGpuTraceRow';
gpuTraceRow.rowType = TraceRow.ROW_TYPE_SYS_MEMORY_GPU;
gpuTraceRow.rowParentId = 'VmTrackerRow';
gpuTraceRow.style.height = '40px';
gpuTraceRow.folder = true;
gpuTraceRow.folderPaddingLeft = 20;
gpuTraceRow.rowHidden = !this.rowFolder.expansion;
gpuTraceRow.name = 'GPU';
gpuTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
gpuTraceRow.selectChangeHandler = this.trace.selectChangeHandler;
gpuTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> =>
new Promise<Array<SnapshotStruct>>((resolve) => resolve([]));
gpuTraceRow.onThreadHandler = (useCache): void => {
let context: CanvasRenderingContext2D;
if (gpuTraceRow.currentContext) {
context = gpuTraceRow.currentContext;
} else {
context = gpuTraceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
}
gpuTraceRow.canvasSave(context);
if (gpuTraceRow.expansion) {
context?.clearRect(0, 0, gpuTraceRow.frame.width, gpuTraceRow.frame.height);
} else {
(renders.empty as EmptyRender).renderMainThread(
{
context: context,
useCache: useCache,
type: '',
},
gpuTraceRow
);
}
gpuTraceRow.canvasRestore(context, this.trace);
};
this.gpuFolder = gpuTraceRow;
this.rowFolder.addChildTraceRow(gpuTraceRow);
return gpuTraceRow;
};
private getSmapsKeyName(rowName: string): string {
let columnName = rowName.toLowerCase();
let keyName = '';
switch (rowName) {
case 'USS':
keyName = 'private_clean + private_dirty';
break;
case 'RSS':
keyName = 'resident_size';
break;
case 'Swapped':
keyName = 'swap + swap_pss';
break;
default:
keyName = columnName;
}
return keyName;
}
private initSmapsRows = async (rowName: string): Promise<void> => {
let sMapsTraceRow = this.initTraceRow(rowName, TraceRow.ROW_TYPE_VM_TRACKER_SMAPS, 'smapsRow');
sMapsTraceRow.rowHidden = !this.sMapsFolder.expansion;
sMapsTraceRow.folderTextLeft = 40;
sMapsTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => {
return sMapsDataSender(this.getSmapsKeyName(rowName), sMapsTraceRow).then((sMaps: unknown[]) => {
this.setName(sMaps);
return sMaps;
});
};
this.sMapsFolder.addChildTraceRow(sMapsTraceRow);
};
private initShmRows = async (): Promise<void> => {
let shmTraceRow = this.initTraceRow('SHM', TraceRow.ROW_TYPE_VMTRACKER_SHM, 'VmTrackerRow');
shmTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => {
return shmDataSender(this.memoryConfig.iPid, shmTraceRow).then((shmData: unknown[]) => {
this.setName(shmData);
return shmData;
});
};
this.rowFolder.addChildTraceRow(shmTraceRow);
};
private async initPurgeableTotal(): Promise<void> {
let totalTraceRow = this.initTraceRow('Purgeable Total', TraceRow.ROW_TYPE_PURGEABLE_TOTAL_VM, 'VmTrackerRow');
totalTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => {
return purgeableDataSender(this.memoryConfig.iPid, totalTraceRow, false).then((purgeableTotalData: unknown[]) => {
this.setName(purgeableTotalData);
return purgeableTotalData;
});
};
this.rowFolder.addChildTraceRow(totalTraceRow);
}
private async initPurgeablePin(): Promise<void> {
let pinTraceRow = this.initTraceRow('Purgeable Pin', TraceRow.ROW_TYPE_PURGEABLE_PIN_VM, 'VmTrackerRow');
pinTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => {
return purgeableDataSender(this.memoryConfig.iPid, pinTraceRow, true).then((purgeablePinData: unknown[]) => {
this.setName(purgeablePinData);
return purgeablePinData;
});
};
this.rowFolder.addChildTraceRow(pinTraceRow);
}
private initPurgeableVM = async (): Promise<void> => {
let time = new Date().getTime();
if (this.isExistsPurgeableTotal[0].data_exists) {
await this.initPurgeableTotal();
}
if (this.isExistsPurgeablePin[0].data_exists) {
await this.initPurgeablePin();
}
let durTime = new Date().getTime() - time;
info('The time to load the VM Purgeable is: ', durTime);
};
private initDmaRow = async (): Promise<void> => {
let dmaTraceRow = this.initTraceRow('DMA', TraceRow.ROW_TYPE_DMA_VMTRACKER, 'VmTrackerRow');
dmaTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => {
return dmaDataSender(this.memoryConfig.iPid, dmaTraceRow).then((dmaData: unknown[]) => {
this.setName(dmaData);
return dmaData;
});
};
this.rowFolder.addChildTraceRow(dmaTraceRow);
};
private initGpuMemoryRow = async (): Promise<void> => {
let gpuMemoryTraceRow = this.initTraceRow(
'Skia Gpu Memory',
TraceRow.ROW_TYPE_GPU_MEMORY_VMTRACKER,
'skiaGpuTraceRow'
);
gpuMemoryTraceRow.rowHidden = !this.gpuFolder.expansion;
gpuMemoryTraceRow.folderTextLeft = 40;
gpuMemoryTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => {
return gpuMemoryDataSender(this.memoryConfig.iPid, gpuMemoryTraceRow).then((gpuMemoryData: unknown[]) => {
this.setName(gpuMemoryData);
return gpuMemoryData;
});
};
this.gpuFolder.addChildTraceRow(gpuMemoryTraceRow);
};
private initGpuResourceRow = async (scratchId: number): Promise<void> => {
let gpuMemoryTraceRow = this.initTraceRow(
'Gpu Resource',
TraceRow.ROW_TYPE_GPU_RESOURCE_VMTRACKER,
this.gpuFolder.rowId!
);
gpuMemoryTraceRow.rowHidden = !this.gpuFolder.expansion;
gpuMemoryTraceRow.folderTextLeft = 40;
gpuMemoryTraceRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => {
return gpuResourceDataSender(scratchId, gpuMemoryTraceRow).then((gpuResourceData: unknown[]) => {
this.setName(gpuResourceData);
this.smapsRecordTab!.GLESHostCache = gpuResourceData;
return gpuResourceData;
});
};
this.gpuFolder.addChildTraceRow(gpuMemoryTraceRow);
};
private async addGpuGraphRow(): Promise<void> {
let graphRow = this.initTraceRow('Graph', TraceRow.ROW_TYPE_SYS_MEMORY_GPU_GRAPH, this.gpuFolder.rowId!);
graphRow.addTemplateTypes('sys-memory');
graphRow.folderTextLeft = 40;
graphRow.supplierFrame = (): Promise<SnapshotStruct[]> => {
return gpuGpuDataSender(this.memoryConfig.iPid, "'mem.graph_pss'", graphRow).then((graphData: unknown[]) => {
this.setName(graphData);
return graphData;
});
};
this.gpuFolder.addChildTraceRow(graphRow);
}
private async addGpuGLRow(): Promise<void> {
let glRow = this.initTraceRow('GL', TraceRow.ROW_TYPE_SYS_MEMORY_GPU_GL, this.gpuFolder.rowId!);
glRow.addTemplateTypes('sys-memory');
glRow.folderTextLeft = 40;
glRow.supplierFrame = (): Promise<SnapshotStruct[]> => {
return gpuGpuDataSender(this.memoryConfig.iPid, "'mem.gl_pss'", glRow).then((glData: unknown[]) => {
this.setName(glData);
return glData;
});
};
this.gpuFolder.addChildTraceRow(glRow);
}
private async addGpuTotalRow(): Promise<void> {
let types = await queryGpuTotalType();
if (!types || types.length === 0) {
return;
}
let gpuTotalRow = this.initTraceRow(
'Skia Gpu Dump Total',
TraceRow.ROW_TYPE_SYS_MEMORY_GPU_TOTAL,
this.gpuFolder.rowId!
);
gpuTotalRow.folderTextLeft = 40;
gpuTotalRow.addTemplateTypes('sys-memory');
gpuTotalRow.addRowSettingPop();
gpuTotalRow.rowSetting = 'enable';
gpuTotalRow.rowSettingList = [
{
key: 'total',
title: 'Total',
checked: true,
},
...types.map(
(
it
): {
key: string;
title: string;
} => {
return {
key: `${it.id}`,
title: it.data,
};
}
),
];
this.addHandleEventByGpuTotalRow(gpuTotalRow);
this.gpuFolder.addChildTraceRow(gpuTotalRow);
}
private addHandleEventByGpuTotalRow(gpuTotalRow: TraceRow<SnapshotStruct>): void {
gpuTotalRow.onRowSettingChangeHandler = (setting): void => {
if (setting && setting.length > 0) {
gpuTotalRow.dataListCache = [];
gpuTotalRow.dataList = [];
gpuTotalRow.isComplete = false;
VmTrackerChart.gpuTotalModule = setting[0] === 'total' ? null : parseInt(setting[0]);
gpuTotalRow.needRefresh = true;
gpuTotalRow.drawFrame();
}
};
gpuTotalRow.supplierFrame = (): Promise<Array<SnapshotStruct>> => {
return gpuTotalDataSender(VmTrackerChart.gpuTotalModule, gpuTotalRow).then((gpuTotalData: unknown[]) => {
this.setName(gpuTotalData);
return gpuTotalData;
});
};
}
private async addGpuWindowRow(): Promise<void> {
let types = await queryGpuWindowType();
if (!types || types.length === 0) {
return;
}
let settings: TreeItemData[] = types
.filter((it) => it.pid === null)
.map((it) => {
return {
key: `${it.id}`,
title: it.data,
children: [],
};
});
settings.forEach((it) => {
it.children = types
.filter((child) => `${child.pid}` === it.key)
.map((item) => {
return {
key: `${it.key}-${item.id}`,
title: item.data,
};
});
});
settings[0].checked = true;
VmTrackerChart.gpuWindow = parseInt(settings[0].key);
VmTrackerChart.gpuWindowModule = null;
let gpuWindowRow = this.initTraceRow(
'Skia Gpu Dump Window',
TraceRow.ROW_TYPE_SYS_MEMORY_GPU_WINDOW,
this.gpuFolder.rowId!
);
gpuWindowRow.folderTextLeft = 40;
gpuWindowRow.addRowSettingPop();
gpuWindowRow.rowSetting = 'enable';
gpuWindowRow.rowSettingList = settings;
gpuWindowRow.addTemplateTypes('sys-memory');
this.addHandleEventByGpuWindowRow(gpuWindowRow);
this.gpuFolder.addChildTraceRow(gpuWindowRow);
}
private addHandleEventByGpuWindowRow(gpuWindowRow: TraceRow<SnapshotStruct>): void {
gpuWindowRow.onRowSettingChangeHandler = (setting): void => {
if (setting && setting.length > 0) {
let split = setting[0].split('-');
VmTrackerChart.gpuWindow = parseInt(split[0]);
VmTrackerChart.gpuWindowModule = split.length > 1 ? parseInt(split[1]) : null;
gpuWindowRow.dataListCache = [];
gpuWindowRow.dataList = [];
gpuWindowRow.isComplete = false;
gpuWindowRow.needRefresh = true;
gpuWindowRow.drawFrame();
}
};
gpuWindowRow.supplierFrame = (): Promise<SnapshotStruct[]> => {
return gpuWindowDataSender(VmTrackerChart.gpuWindow!, VmTrackerChart.gpuWindowModule, gpuWindowRow).then(
(gpuWindowData: unknown[]) => {
this.setName(gpuWindowData);
return gpuWindowData;
}
);
};
}
private initTraceRow(rowName: string, type: string, rowParentId: string): TraceRow<SnapshotStruct> {
let vmTrackerTraceRow = TraceRow.skeleton<SnapshotStruct>();
vmTrackerTraceRow.rowParentId = rowParentId;
vmTrackerTraceRow.rowId = rowName;
vmTrackerTraceRow.rowType = type;
vmTrackerTraceRow.folderTextLeft = 20;
vmTrackerTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
vmTrackerTraceRow.selectChangeHandler = this.trace.selectChangeHandler;
vmTrackerTraceRow.style.height = '40px';
vmTrackerTraceRow.style.width = '100%';
vmTrackerTraceRow.setAttribute('children', '');
vmTrackerTraceRow.name = rowName;
vmTrackerTraceRow.focusHandler = (): void => {
this.showTip(vmTrackerTraceRow);
};
vmTrackerTraceRow.findHoverStruct = (): void => {
SnapshotStruct.hoverSnapshotStruct = vmTrackerTraceRow.getHoverStruct();
};
vmTrackerTraceRow.onThreadHandler = (useCache): void => {
let context: CanvasRenderingContext2D;
if (vmTrackerTraceRow.currentContext) {
context = vmTrackerTraceRow.currentContext;
} else {
context = vmTrackerTraceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
}
vmTrackerTraceRow.canvasSave(context);
(renders.snapshot as SnapshotRender).renderMainThread(
{
context: context,
useCache: useCache,
type: 'snapshot',
},
vmTrackerTraceRow
);
vmTrackerTraceRow.canvasRestore(context, this.trace);
};
return vmTrackerTraceRow;
}
private showTip(traceRow: TraceRow<SnapshotStruct>): void {
this.trace?.displayTip(
traceRow,
SnapshotStruct.hoverSnapshotStruct,
`<span>Name: ${SnapshotStruct.hoverSnapshotStruct?.name || ''}</span>
<span>Size: ${Utils.getBinaryByteWithUnit(SnapshotStruct.hoverSnapshotStruct?.value || 0)}</span>`
);
}
private setName(data: Array<unknown>): void {
if (data.length > 0) {
data.forEach((item, index) => {
item.name = `SnapShot ${index}`;
});
}
}
}