* Copyright (C) 2024 Shenzhen Kaihong Digital Industry Development 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 { renders } from '../../database/ui-worker/ProcedureWorker';
import { info } from '../../../log/Log';
import { HangStruct } from '../../database/ui-worker/ProcedureWorkerHang';
import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
import { queryHangData } from '../../database/sql/Hang.sql';
import { hangDataSender } from '../../database/data-trafic/HangDataSender';
import { BaseStruct } from '../../bean/BaseStruct';
import { Utils } from '../trace/base/Utils';
export type HangType = 'Instant' | 'Circumstantial' | 'Micro' | 'Severe' | '';
const TIME_MS = 1000000;
export class SpHangChart {
private trace: SpSystemTrace;
static funcNameMap: Map<number | string, string> = new Map();
constructor(trace: SpSystemTrace) {
this.trace = trace;
}
static calculateHangType(dur: number): HangType {
const durMS = dur / TIME_MS;
if (durMS < 33) {
return '';
}
else if (durMS < 100) {
return 'Instant';
}
else if (durMS < 250) {
return 'Circumstantial';
}
else if (durMS < 500) {
return 'Micro';
}
else {
return 'Severe';
}
}
async init(): Promise<void> {
SpHangChart.funcNameMap = Utils.getInstance().getCallStatckMap();
let folder = await this.initFolder();
await this.initData(folder);
}
private hangSupplierFrame(
traceRow: TraceRow<HangStruct>,
it: {
id: number;
name: string;
num: number;
}
): void {
traceRow.supplierFrame = (): Promise<HangStruct[]> => {
let promiseData = hangDataSender(it.id, traceRow);
if (promiseData === null) {
return new Promise<Array<HangStruct>>((resolve) => resolve([]));
} else {
return promiseData.then((resultHang: Array<HangStruct>) =>
resultHang.map(hangItem => ({
...hangItem,
pname: it.name,
type: SpHangChart.calculateHangType(hangItem.dur!),
content: SpHangChart.funcNameMap.get(hangItem.id!)
}))
);
}
};
}
private hangThreadHandler(
traceRow: TraceRow<HangStruct>,
it: {
id: number;
name: string;
num: number;
},
hangId: number
): void {
traceRow.onThreadHandler = (useCache): void => {
let context: CanvasRenderingContext2D;
if (traceRow.currentContext) {
context = traceRow.currentContext;
} else {
context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
}
traceRow.canvasSave(context);
renders.hang.renderMainThread(
{
context: context,
useCache: useCache,
type: it.name,
index: hangId,
},
traceRow
);
traceRow.canvasRestore(context, this.trace);
};
}
async initData(folder: TraceRow<BaseStruct>): Promise<void> {
let hangStartTime = new Date().getTime();
let hangList = await queryHangData();
if (hangList.length === 0) {
return;
}
this.trace.rowsEL?.appendChild(folder);
for (let i = 0; i < hangList.length; i++) {
const it: {
id: number,
name: string,
num: number,
} = hangList[i];
let traceRow = TraceRow.skeleton<HangStruct>();
traceRow.rowId = `${it.name ?? 'Process'} ${it.id}`;
traceRow.rowType = TraceRow.ROW_TYPE_HANG;
traceRow.rowParentId = folder.rowId;
traceRow.style.height = '40px';
traceRow.name = `${it.name ?? 'Process'} ${it.id}`;
traceRow.rowHidden = !folder.expansion;
traceRow.setAttribute('children', '');
traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
traceRow.selectChangeHandler = this.trace.selectChangeHandler;
this.hangSupplierFrame(traceRow, it);
traceRow.getCacheData = (args: unknown): Promise<Array<unknown>> => hangDataSender(it.id, traceRow, args);
traceRow.focusHandler = (ev): void => {
let hangStruct = HangStruct.hoverHangStruct;
this.trace?.displayTip(
traceRow, hangStruct,
`<span>${hangStruct?.type} ${hangStruct?.dur! / TIME_MS}ms</span>`
);
};
traceRow.findHoverStruct = (): void => {
HangStruct.hoverHangStruct = traceRow.getHoverStruct();
};
this.hangThreadHandler(traceRow, it, i);
folder.addChildTraceRow(traceRow);
}
let durTime = new Date().getTime() - hangStartTime;
info('The time to load the HangData is: ', durTime);
}
async initFolder(): Promise<TraceRow<BaseStruct>> {
let hangFolder = TraceRow.skeleton();
hangFolder.rowId = 'Hangs';
hangFolder.index = 0;
hangFolder.rowType = TraceRow.ROW_TYPE_HANG_GROUP;
hangFolder.rowParentId = '';
hangFolder.style.height = '40px';
hangFolder.folder = true;
hangFolder.name = 'Hangs';
hangFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler;
hangFolder.selectChangeHandler = this.trace.selectChangeHandler;
hangFolder.supplier = (): Promise<unknown[]> => new Promise<Array<unknown>>((resolve) => resolve([]));
hangFolder.onThreadHandler = (useCache): void => {
hangFolder.canvasSave(this.trace.canvasPanelCtx!);
if (hangFolder.expansion) {
this.trace.canvasPanelCtx?.clearRect(0, 0, hangFolder.frame.width, hangFolder.frame.height);
} else {
(renders.empty as EmptyRender).renderMainThread(
{
context: this.trace.canvasPanelCtx,
useCache: useCache,
type: '',
},
hangFolder
);
}
hangFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace);
};
return hangFolder;
}
}