1/*
2 * Copyright (C) 2022 Huawei Device 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 { info } from '../../../log/Log';
18import { TraceRow } from '../trace/base/TraceRow';
19import { procedurePool } from '../../database/Procedure';
20import { CpuRender, CpuStruct } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
21import { renders } from '../../database/ui-worker/ProcedureWorker';
22import { Utils } from '../trace/base/Utils';
23import { cpuDataSender } from '../../database/data-trafic/CpuDataSender';
24import { queryCpuCount, queryCpuMax, queryCpuSchedSlice } from '../../database/sql/Cpu.sql';
25import { rowThreadHandler } from './SpChartManager';
26import { SpStatisticsHttpUtil } from '../../../statistics/util/SpStatisticsHttpUtil';
27
28export class SpCpuChart {
29  private trace: SpSystemTrace;
30
31  constructor(trace: SpSystemTrace) {
32    this.trace = trace;
33  }
34
35  private cpuSupplierFrame(traceRow: TraceRow<CpuStruct>, cpuId: number): void {
36    traceRow.supplierFrame = async (): Promise<CpuStruct[]> => {
37      const res = await cpuDataSender(cpuId, traceRow);
38      const filterList = SpSystemTrace.keyPathList.filter((item) => {
39        return item.cpu === cpuId;
40      });
41      res.push(...filterList);
42      res.forEach((it, i, arr) => {
43        let p = Utils.getInstance().getProcessMap().get(it.processId!);
44        let t = Utils.getInstance().getThreadMap().get(it.tid!);
45        let slice = Utils.getInstance().getSchedSliceMap().get(`${it.id}-${it.startTime}`);
46        if (slice) {
47          it.end_state = slice.endState;
48          it.priority = slice.priority;
49        }
50        it.processName = p;
51        it.processCmdLine = p;
52        it.name = t;
53        it.type = 'thread';
54      });
55      return res;
56    };
57  }
58
59  private cpuThreadHandler(traceRow: TraceRow<CpuStruct>, i1: number): void {
60    traceRow.onThreadHandler = (useCache: boolean, buf: ArrayBuffer | undefined | null): void => {
61      let context: CanvasRenderingContext2D;
62      if (traceRow.currentContext) {
63        context = traceRow.currentContext;
64      } else {
65        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
66      }
67      traceRow.canvasSave(context);
68      (renders['cpu-data'] as CpuRender).renderMainThread(
69        {
70          ctx: context,
71          useCache: useCache,
72          type: `cpu-data-${i1}`,
73          translateY: traceRow.translateY,
74        },
75        traceRow
76      );
77      traceRow.canvasRestore(context, this.trace);
78    };
79  }
80
81  // @ts-ignore
82  async init(cpuDataCount?: Map<number, number>, parentRow?: TraceRow<unknown>, traceId?: string): Promise<void> {
83    let CpuStartTime = new Date().getTime();
84    let array = await queryCpuMax(traceId);
85    let cpuCountResult = await queryCpuCount(traceId);
86    if (cpuCountResult && cpuCountResult.length > 0 && cpuCountResult[0]) {
87      // @ts-ignore
88      Utils.getInstance().setWinCpuCount(cpuCountResult[0].cpuCount, traceId);
89    } else {
90      Utils.getInstance().setWinCpuCount(0, traceId);
91    }
92    let cpuSchedSlice = await queryCpuSchedSlice(traceId);
93    this.initSchedSliceData(cpuSchedSlice, traceId);
94    info('Cpu trace row data size is: ', array.length);
95    if (array && array.length > 0 && array[0]) {
96      // 有cpu泳道,统计ftrace插件,cpu插件
97      let requestBody = {
98        eventData: {
99          plugin: ['ftrace-plugin', 'cpu-plugin']
100        }
101      };
102      SpStatisticsHttpUtil.recordPluginUsage(requestBody);
103      //@ts-ignore
104      let cpuMax = array[0].cpu + 1;
105      Utils.getInstance().setCpuCount(cpuMax, traceId);
106      for (let i1 = 0; i1 < cpuMax; i1++) {
107        if (cpuDataCount && (cpuDataCount.get(i1) || 0) > 0) {
108          let traceRow = this.createCpuRow(i1, traceId);
109          if (parentRow) {
110            parentRow.addChildTraceRow(traceRow);
111          } else {
112            this.trace.rowsEL?.appendChild(traceRow);
113          }
114        }
115      }
116    }
117    let CpuDurTime = new Date().getTime() - CpuStartTime;
118    info('The time to load the Cpu data is: ', CpuDurTime);
119  }
120
121  createCpuRow(cpuId: number, traceId?: string): TraceRow<CpuStruct> {
122    let traceRow = TraceRow.skeleton<CpuStruct>(traceId);
123    traceRow.rowId = `${cpuId}`;
124    traceRow.rowType = TraceRow.ROW_TYPE_CPU;
125    traceRow.rowParentId = '';
126    traceRow.style.height = '30px';
127    traceRow.name = `Cpu ${cpuId}`;
128    traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
129    traceRow.selectChangeHandler = this.trace.selectChangeHandler;
130    traceRow.supplierFrame = async (): Promise<CpuStruct[]> => {
131      let res = await cpuDataSender(cpuId, traceRow, traceId);
132      const filterList = SpSystemTrace.keyPathList.filter((item): boolean => {
133        return item.cpu === cpuId;
134      });
135      res.push(...filterList);
136      res.forEach((it, i, arr): void => {
137        let p = Utils.getInstance().getProcessMap(traceId).get(it.processId!);
138        let t = Utils.getInstance().getThreadMap(traceId).get(it.tid!);
139        let slice = Utils.getInstance().getSchedSliceMap(traceId).get(`${it.id}-${it.startTime}`);
140        if (slice) {
141          it.end_state = slice.endState;
142          it.priority = slice.priority;
143        }
144        it.processName = p;
145        it.processCmdLine = p;
146        it.name = t;
147        it.type = 'thread';
148      });
149      return res;
150    };
151    traceRow.focusHandler = (): void => {
152      this.trace?.displayTip(
153        traceRow,
154        CpuStruct.hoverCpuStruct,
155        `<span>P:${CpuStruct.hoverCpuStruct?.processName || 'Process'} [${CpuStruct.hoverCpuStruct?.processId
156        }]</span><span>T:${CpuStruct.hoverCpuStruct?.name} [${CpuStruct.hoverCpuStruct?.tid}] [Prio:${CpuStruct.hoverCpuStruct?.priority || 0
157        }]</span>`
158      );
159    };
160    traceRow.findHoverStruct = (): void => {
161      CpuStruct.hoverCpuStruct = traceRow.getHoverStruct();
162    };
163    traceRow.onThreadHandler = rowThreadHandler<CpuRender>('cpu-data', 'ctx', {
164      type: `cpu-data-${cpuId}`,
165      translateY: traceRow.translateY,
166    }, traceRow, this.trace);
167    return traceRow;
168  }
169
170  initProcessThreadStateData = async (progress: Function): Promise<void> => {
171    let time = new Date().getTime();
172    progress('StateProcessThread', 93);
173    procedurePool.submitWithName('logic0', 'spt-init', {}, undefined, (res: unknown) => { });
174    let durTime = new Date().getTime() - time;
175    info('The time to load the first ProcessThreadState data is: ', durTime);
176  };
177
178  initCpuIdle0Data = async (progress: Function): Promise<void> => {
179    let time = new Date().getTime();
180    progress('CPU Idle', 94);
181    procedurePool.submitWithName(
182      'logic0',
183      'scheduling-getCpuIdle0',
184      {
185        // @ts-ignore
186        endTs: (window as unknown).recordEndNS, // @ts-ignore
187        total: (window as unknown).totalNS,
188      },
189      undefined,
190      (res: unknown) => { }
191    );
192    let durTime = new Date().getTime() - time;
193    info('The time to load the first CPU Idle0 data is: ', durTime);
194  };
195
196  initSchedSliceData(arr: unknown[], traceId?: string): void {
197    Utils.getInstance().getSchedSliceMap(traceId).clear();
198    arr.forEach((value) => {
199      Utils.getInstance().getSchedSliceMap(traceId). // @ts-ignore
200        set(`${value.itid}-${value.ts}`, { endState: value.endState, priority: value.priority });
201    });
202  }
203
204  initSchedulingPTData = async (progress: Function): Promise<void> => {
205    let time = new Date().getTime();
206    progress('CPU Idle', 94);
207    procedurePool.submitWithName('logic0', 'scheduling-getProcessAndThread', {}, undefined, (res: unknown) => { });
208    let durTime = new Date().getTime() - time;
209    info('The time to load the first CPU Idle0 data is: ', durTime);
210  };
211
212  initSchedulingFreqData = async (progress: Function): Promise<void> => {
213    let time = new Date().getTime();
214    progress('CPU Scheduling Freq', 94);
215    procedurePool.submitWithName('logic0', 'scheduling-initFreqData', {}, undefined, (res: unknown) => { });
216    let durTime = new Date().getTime() - time;
217    info('The time to load the first CPU Idle0 data is: ', durTime);
218  };
219}
220