/* * 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; /// Hangs聚合泳道 export class SpHangChart { private trace: SpSystemTrace; static funcNameMap: Map = 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 { SpHangChart.funcNameMap = Utils.getInstance().getCallStatckMap(); let folder = await this.initFolder(); await this.initData(folder); } private hangSupplierFrame( traceRow: TraceRow, it: { id: number; name: string; num: number; } ): void { traceRow.supplierFrame = (): Promise => { let promiseData = hangDataSender(it.id, traceRow); if (promiseData === null) { return new Promise>((resolve) => resolve([])); } else { return promiseData.then((resultHang: Array) => resultHang.map(hangItem => ({ ...hangItem, pname: it.name, type: SpHangChart.calculateHangType(hangItem.dur!), content: SpHangChart.funcNameMap.get(hangItem.id!) })) ); } }; } private hangThreadHandler( traceRow: TraceRow, 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): Promise { 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(); 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> => hangDataSender(it.id, traceRow, args); traceRow.focusHandler = (ev): void => { let hangStruct = HangStruct.hoverHangStruct; this.trace?.displayTip( traceRow, hangStruct, `${hangStruct?.type} ${hangStruct?.dur! / TIME_MS}ms` ); }; 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> { 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; // @ts-ignore hangFolder.supplier = (): Promise => new Promise>((resolve) => resolve([])); hangFolder.onThreadHandler = (useCache): void => { hangFolder.canvasSave(this.trace.canvasPanelCtx!); if (hangFolder.expansion) { // @ts-ignore 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; } }