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 { TraceRow } from '../trace/base/TraceRow';
18import { renders } from '../../database/ui-worker/ProcedureWorker';
19import { info } from '../../../log/Log';
20import { ClockRender, ClockStruct } from '../../database/ui-worker/ProcedureWorkerClock';
21import { ColorUtils } from '../trace/base/ColorUtils';
22import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
23import { Utils } from '../trace/base/Utils';
24import { clockDataSender } from '../../database/data-trafic/ClockDataSender';
25import { queryClockData } from '../../database/sql/Clock.sql';
26import { DmaFenceRender, DmaFenceStruct } from '../../database/ui-worker/ProcedureWorkerDmaFence';
27import { dmaFenceSender } from '../../database/data-trafic/dmaFenceSender';
28import { queryDmaFenceName } from '../../database/sql/dmaFence.sql';
29import { BaseStruct } from '../../bean/BaseStruct';
30import { promises } from 'dns';
31
32export class SpClockChart {
33  private readonly trace: SpSystemTrace;
34
35  constructor(trace: SpSystemTrace) {
36    this.trace = trace;
37  }
38
39  async init(parentRow?: TraceRow<BaseStruct>, traceId?: string): Promise<void> {
40    let clockList = await queryClockData(traceId);
41    if (clockList.length === 0) {
42      return;
43    }
44    let folder = await this.initFolder(traceId);
45    if (parentRow) {
46      parentRow.addChildTraceRow(folder);
47    } else {
48      this.trace.rowsEL?.appendChild(folder);
49    }
50    await this.initData(folder, clockList, traceId);
51    await this.initDmaFence(folder);
52  }
53
54  private clockSupplierFrame(
55    traceRow: TraceRow<ClockStruct>,
56    it: {
57      name: string;
58      num: number;
59      srcname: string;
60      maxValue?: number;
61    },
62    isState: boolean,
63    isScreenState: boolean,
64  ): void {
65    traceRow.supplierFrame = (): Promise<ClockStruct[]> => {
66      let promiseData = null;
67      if (it.name.endsWith(' Frequency')) {
68        promiseData = clockDataSender(it.srcname, 'clockFrequency', traceRow);
69      } else if (isState) {
70        promiseData = clockDataSender(it.srcname, 'clockState', traceRow);
71      } else if (isScreenState) {
72        promiseData = clockDataSender('', 'screenState', traceRow);
73      }
74      if (promiseData === null) {
75        // @ts-ignore
76        return new Promise<Array<unknown>>((resolve) => resolve([]));
77      } else {
78        // @ts-ignore
79        return promiseData.then((resultClock: Array<unknown>) => {
80          for (let j = 0; j < resultClock.length; j++) {
81            // @ts-ignore
82            resultClock[j].type = 'measure'; // @ts-ignore
83            if ((resultClock[j].value || 0) > it.maxValue!) {
84              // @ts-ignore
85              it.maxValue = resultClock[j].value || 0;
86            }
87            if (j > 0) {
88              // @ts-ignore
89              resultClock[j].delta = (resultClock[j].value || 0) - (resultClock[j - 1].value || 0);
90            } else {
91              // @ts-ignore
92              resultClock[j].delta = 0;
93            }
94          }
95          return resultClock;
96        });
97      }
98    };
99  }
100
101  private clockThreadHandler(
102    traceRow: TraceRow<ClockStruct>,
103    it: {
104      name: string;
105      num: number;
106      srcname: string;
107      maxValue?: number;
108    },
109    isState: boolean,
110    isScreenState: boolean,
111    clockId: number
112  ): void {
113    traceRow.onThreadHandler = (useCache): void => {
114      let context: CanvasRenderingContext2D;
115      if (traceRow.currentContext) {
116        context = traceRow.currentContext;
117      } else {
118        context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
119      }
120      traceRow.canvasSave(context);
121      (renders.clock as ClockRender).renderMainThread(
122        {
123          context: context,
124          useCache: useCache,
125          type: it.name,
126          maxValue: it.maxValue === 0 ? 1 : it.maxValue!,
127          index: clockId,
128          maxName:
129            isState || isScreenState
130              ? it.maxValue!.toString()
131              : Utils.getFrequencyWithUnit(it.maxValue! / 1000).maxFreqName,
132        },
133        traceRow
134      );
135      traceRow.canvasRestore(context, this.trace);
136    };
137  }
138
139  async initData(folder: TraceRow<BaseStruct>, clockList: Array<{
140    name: string;
141    num: number;
142    srcname: string;
143    maxValue?: number;
144  }>, traceId?: string): Promise<void> {
145    let clockStartTime = new Date().getTime();
146    info('clockList data size is: ', clockList!.length);
147    if (!traceId) {
148      this.trace.rowsEL?.appendChild(folder);
149    }
150    ClockStruct.maxValue = clockList.map((item) => item.num).reduce((a, b) => Math.max(a, b));
151    for (let i = 0; i < clockList.length; i++) {
152      const it = clockList[i];
153      it.maxValue = 0;
154      let traceRow = TraceRow.skeleton<ClockStruct>(traceId);
155      let isState = it.name.endsWith(' State');
156      let isScreenState = it.name.endsWith('ScreenState');
157      traceRow.rowId = it.name;
158      traceRow.rowType = TraceRow.ROW_TYPE_CLOCK;
159      traceRow.rowParentId = folder.rowId;
160      traceRow.style.height = '40px';
161      traceRow.name = it.name;
162      traceRow.rowHidden = !folder.expansion;
163      traceRow.setAttribute('children', '');
164      traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
165      traceRow.selectChangeHandler = this.trace.selectChangeHandler;
166      this.clockSupplierFrame(traceRow, it, isState, isScreenState);
167      traceRow.getCacheData = (args: unknown): Promise<ClockStruct[]> | undefined => {
168        let result: Promise<ClockStruct[]> | undefined;
169        if (it.name.endsWith(' Frequency')) {
170          result = clockDataSender(it.srcname, 'clockFrequency', traceRow, args);
171        } else if (isState) {
172          result = clockDataSender(it.srcname, 'clockState', traceRow, args);
173        } else if (isScreenState) {
174          result = clockDataSender('', 'screenState', traceRow, args);
175        }
176        return result;
177      };
178      traceRow.focusHandler = (ev): void => {
179        this.trace?.displayTip(
180          traceRow,
181          ClockStruct.hoverClockStruct,
182          `<span>${ColorUtils.formatNumberComma(ClockStruct.hoverClockStruct?.value!)}</span>`
183        );
184      };
185      traceRow.findHoverStruct = (): void => {
186        ClockStruct.hoverClockStruct = traceRow.getHoverStruct();
187      };
188      this.clockThreadHandler(traceRow, it, isState, isScreenState, i);
189      folder.addChildTraceRow(traceRow);
190    }
191    let durTime = new Date().getTime() - clockStartTime;
192    info('The time to load the ClockData is: ', durTime);
193  }
194
195  // @ts-ignore
196  async initDmaFence(folder: TraceRow<unknown>): Promise<void> {
197    let dmaFenceNameList = await queryDmaFenceName();
198    if (dmaFenceNameList.length) {
199      let dmaFenceList = [];
200      const timelineValues = dmaFenceNameList.map(obj => obj.timeline);
201      for (let i = 0; i < timelineValues.length; i++) {
202        let traceRow: TraceRow<DmaFenceStruct> = TraceRow.skeleton<DmaFenceStruct>();
203        traceRow.rowId = timelineValues[i];
204        traceRow.rowType = TraceRow.ROW_TYPE_DMA_FENCE;
205        traceRow.rowParentId = folder.rowId;
206        traceRow.style.height = 40 + 'px';
207        traceRow.name = `${timelineValues[i]}`;
208        traceRow.folder = false;
209        traceRow.rowHidden = !folder.expansion;
210        traceRow.setAttribute('children', '');
211        traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
212        traceRow.selectChangeHandler = this.trace.selectChangeHandler;
213        // @ts-ignore
214        traceRow.supplierFrame = (): Promise<DmaFenceStruct[]> => {
215          return dmaFenceSender('dma_fence_init', `${timelineValues[i]}`, traceRow).then((res) => {
216            res.forEach((item: unknown) => {
217              // @ts-ignore
218              let detail = Utils.DMAFENCECAT_MAP.get(item.id!);
219              if (detail) {
220                let catValue = (detail.cat.match(/^dma_(.*)$/))![1];
221                // @ts-ignore
222                item.sliceName = catValue.endsWith('ed') ? `${catValue.slice(0, -2)}(${detail.seqno})` : `${catValue}(${detail.seqno})`;
223                // @ts-ignore
224                item.driver = detail.driver;
225                // @ts-ignore
226                item.context = detail.context;
227                // @ts-ignore
228                item.depth = 0;
229              }
230
231            });
232            return dmaFenceList = res;
233          });
234
235        };
236        traceRow.onThreadHandler = (useCache): void => {
237          let context: CanvasRenderingContext2D;
238          if (traceRow.currentContext) {
239            context = traceRow.currentContext;
240          } else {
241            context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
242          }
243          traceRow.canvasSave(context);
244          (renders.dmaFence as DmaFenceRender).renderMainThread(
245            {
246              dmaFenceContext: context,
247              useCache: useCache,
248              type: 'dmaFence',
249              maxValue: 20,
250              index: 1,
251              maxName: ''
252            },
253            traceRow
254          );
255          traceRow.canvasRestore(context, this.trace);
256        };
257        folder.addChildTraceRow(traceRow);
258      }
259
260    }
261
262  }
263
264
265  async initFolder(traceId?: string): Promise<TraceRow<BaseStruct>> {
266    let clockFolder = TraceRow.skeleton(traceId);
267    clockFolder.rowId = 'Clocks';
268    clockFolder.index = 0;
269    clockFolder.rowType = TraceRow.ROW_TYPE_CLOCK_GROUP;
270    clockFolder.rowParentId = '';
271    clockFolder.style.height = '40px';
272    clockFolder.folder = true;
273    clockFolder.name = 'Clocks';
274    clockFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler;
275    clockFolder.selectChangeHandler = this.trace.selectChangeHandler;
276    clockFolder.supplier = (): Promise<BaseStruct[]> => new Promise<Array<BaseStruct>>((resolve) => resolve([]));
277    clockFolder.onThreadHandler = (useCache): void => {
278      clockFolder.canvasSave(this.trace.canvasPanelCtx!);
279      if (clockFolder.expansion) {
280        this.trace.canvasPanelCtx?.clearRect(0, 0, clockFolder.frame.width, clockFolder.frame.height);
281      } else {
282        (renders.empty as EmptyRender).renderMainThread(
283          {
284            context: this.trace.canvasPanelCtx,
285            useCache: useCache,
286            type: '',
287          },
288          clockFolder
289        );
290      }
291      clockFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace);
292    };
293    return clockFolder;
294  }
295}
296