1/*
2 * Copyright (C) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { SpSystemTrace } from '../SpSystemTrace';
17import { TraceRow } from '../trace/base/TraceRow';
18import { renders } from '../../database/ui-worker/ProcedureWorker';
19import { info } from '../../../log/Log';
20import { HangStruct } from '../../database/ui-worker/ProcedureWorkerHang';
21import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
22import { queryHangData } from '../../database/sql/Hang.sql';
23import { hangDataSender } from '../../database/data-trafic/HangDataSender';
24import { BaseStruct } from '../../bean/BaseStruct';
25import { Utils } from '../trace/base/Utils';
26
27export type HangType = 'Instant' | 'Circumstantial' | 'Micro' | 'Severe' | '';
28const TIME_MS = 1000000;
29/// Hangs聚合泳道
30export class SpHangChart {
31  private trace: SpSystemTrace;
32  static funcNameMap: Map<number | string, string> = new Map();
33
34  constructor(trace: SpSystemTrace) {
35    this.trace = trace;
36  }
37
38  static calculateHangType(dur: number): HangType {
39    const durMS = dur / TIME_MS;
40    if (durMS < 33) {
41      return '';
42    }
43    else if (durMS < 100) {
44      return 'Instant';
45    }
46    else if (durMS < 250) {
47      return 'Circumstantial';
48    }
49    else if (durMS < 500) {
50      return 'Micro';
51    }
52    else {
53      return 'Severe';
54    }
55  }
56
57  async init(): Promise<void> {
58    SpHangChart.funcNameMap = Utils.getInstance().getCallStatckMap();
59    let folder = await this.initFolder();
60    await this.initData(folder);
61  }
62
63  private hangSupplierFrame(
64    traceRow: TraceRow<HangStruct>,
65    it: {
66      id: number;
67      name: string;
68      num: number;
69    }
70  ): void {
71    traceRow.supplierFrame = (): Promise<HangStruct[]> => {
72      let promiseData = hangDataSender(it.id, traceRow);
73      if (promiseData === null) {
74        return new Promise<Array<HangStruct>>((resolve) => resolve([]));
75      } else {
76        return promiseData.then((resultHang: Array<HangStruct>) =>
77          resultHang.map(hangItem => ({
78            ...hangItem,
79            pname: it.name,
80            type: SpHangChart.calculateHangType(hangItem.dur!),
81            content: SpHangChart.funcNameMap.get(hangItem.id!)
82          }))
83        );
84      }
85    };
86  }
87
88  private hangThreadHandler(
89    traceRow: TraceRow<HangStruct>,
90    it: {
91      id: number;
92      name: string;
93      num: number;
94    },
95    hangId: number
96  ): void {
97    traceRow.onThreadHandler = (useCache): void => {
98      let context: CanvasRenderingContext2D;
99      if (traceRow.currentContext) {
100        context = traceRow.currentContext;
101      } else {
102        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
103      }
104      traceRow.canvasSave(context);
105      renders.hang.renderMainThread(
106        {
107          context: context,
108          useCache: useCache,
109          type: it.name,
110          index: hangId,
111        },
112        traceRow
113      );
114      traceRow.canvasRestore(context, this.trace);
115    };
116  }
117
118  /// 初始化聚合泳道信息
119  async initData(folder: TraceRow<BaseStruct>): Promise<void> {
120    let hangStartTime = new Date().getTime();
121    let hangList = await queryHangData();
122    if (hangList.length === 0) {
123      return;
124    }
125    this.trace.rowsEL?.appendChild(folder);
126    for (let i = 0; i < hangList.length; i++) {
127      const it: {
128        id: number,
129        name: string,
130        num: number,
131      } = hangList[i];
132      let traceRow = TraceRow.skeleton<HangStruct>();
133      traceRow.rowId = `${it.name ?? 'Process'} ${it.id}`;
134      traceRow.rowType = TraceRow.ROW_TYPE_HANG;
135      traceRow.rowParentId = folder.rowId;
136      traceRow.style.height = '40px';
137      traceRow.name = `${it.name ?? 'Process'} ${it.id}`;
138      traceRow.rowHidden = !folder.expansion;
139      traceRow.setAttribute('children', '');
140      traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
141      traceRow.selectChangeHandler = this.trace.selectChangeHandler;
142      this.hangSupplierFrame(traceRow, it);
143      traceRow.getCacheData = (args: unknown): Promise<Array<unknown>> => hangDataSender(it.id, traceRow, args);
144      traceRow.focusHandler = (ev): void => {
145        let hangStruct = HangStruct.hoverHangStruct;
146        this.trace?.displayTip(
147          traceRow, hangStruct,
148          `<span>${hangStruct?.type} ${hangStruct?.dur! / TIME_MS}ms</span>`
149        );
150      };
151      traceRow.findHoverStruct = (): void => {
152        HangStruct.hoverHangStruct = traceRow.getHoverStruct();
153      };
154      this.hangThreadHandler(traceRow, it, i);
155      folder.addChildTraceRow(traceRow);
156    }
157    let durTime = new Date().getTime() - hangStartTime;
158    info('The time to load the HangData is: ', durTime);
159  }
160
161  async initFolder(): Promise<TraceRow<BaseStruct>> {
162    let hangFolder = TraceRow.skeleton();
163    hangFolder.rowId = 'Hangs';
164    hangFolder.index = 0;
165    hangFolder.rowType = TraceRow.ROW_TYPE_HANG_GROUP;
166    hangFolder.rowParentId = '';
167    hangFolder.style.height = '40px';
168    hangFolder.folder = true;
169    hangFolder.name = 'Hangs';
170    hangFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler;
171    hangFolder.selectChangeHandler = this.trace.selectChangeHandler; // @ts-ignore
172    hangFolder.supplier = (): Promise<unknown[]> => new Promise<Array<unknown>>((resolve) => resolve([]));
173    hangFolder.onThreadHandler = (useCache): void => {
174      hangFolder.canvasSave(this.trace.canvasPanelCtx!);
175      if (hangFolder.expansion) {
176        // @ts-ignore
177        this.trace.canvasPanelCtx?.clearRect(0, 0, hangFolder.frame.width, hangFolder.frame.height);
178      } else {
179        (renders.empty as EmptyRender).renderMainThread(
180          {
181            context: this.trace.canvasPanelCtx,
182            useCache: useCache,
183            type: '',
184          },
185          hangFolder
186        );
187      }
188      hangFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace);
189    };
190    return hangFolder;
191  }
192}
193