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 {
17  BaseStruct,
18  dataFilterHandler,
19  drawLoadingFrame,
20  ns2x,
21  PerfRender,
22  Rect,
23  RequestMessage,
24} from '../ProcedureWorkerCommon';
25import { TraceRow } from '../../../component/trace/base/TraceRow';
26import { ColorUtils } from '../../../component/trace/base/ColorUtils';
27import { SpSystemTrace } from '../../../component/SpSystemTrace';
28
29export class CpuStateRender extends PerfRender {
30  renderMainThread(
31    req: {
32      useCache: boolean;
33      cpuStateContext: CanvasRenderingContext2D;
34      type: string;
35      cpu: number;
36    },
37    cpuStateRow: TraceRow<CpuStateStruct>
38  ): void {
39    let list = cpuStateRow.dataList;
40    let filter = cpuStateRow.dataListCache;
41    dataFilterHandler(list, filter, {
42      startKey: 'startTs',
43      durKey: 'dur',
44      startNS: TraceRow.range?.startNS ?? 0,
45      endNS: TraceRow.range?.endNS ?? 0,
46      totalNS: TraceRow.range?.totalNS ?? 0,
47      frame: cpuStateRow.frame,
48      paddingTop: 5,
49      useCache: req.useCache || !(TraceRow.range?.refresh ?? false),
50    });
51    drawLoadingFrame(req.cpuStateContext, filter, cpuStateRow);
52    let path = new Path2D();
53    let find = false;
54    let offset = 3;
55    let heights = [4, 8, 12, 16, 20, 24, 28, 32];
56    for (let re of filter) {
57      //@ts-ignore
58      re.height = heights[re.value];
59      CpuStateStruct.draw(req.cpuStateContext, path, re);
60      if (cpuStateRow.isHover) {
61        if (
62          re.frame &&
63          cpuStateRow.hoverX >= re.frame.x - offset &&
64          cpuStateRow.hoverX <= re.frame.x + re.frame.width + offset
65        ) {
66          CpuStateStruct.hoverStateStruct = re;
67          find = true;
68        }
69      }
70    }
71    if (!find && cpuStateRow.isHover) {
72      CpuStateStruct.hoverStateStruct = undefined;
73    }
74    req.cpuStateContext.fill(path);
75  }
76
77  render(
78    cpuStateReq: RequestMessage,
79    list: Array<CpuStateStruct>,
80    filter: Array<CpuStateStruct>,
81    dataList2: Array<CpuStateStruct>
82  ): void { }
83
84  setFrameByArr(
85    cpuStateRes: CpuStateStruct[],
86    startNS: number,
87    endNS: number,
88    totalNS: number,
89    frame: Rect,
90    arr2: CpuStateStruct[]
91  ): void {
92    let list: CpuStateStruct[] = arr2;
93    cpuStateRes.length = 0;
94    let pns = (endNS - startNS) / frame.width;
95    let y = frame.y + 5;
96    let frameHeight = frame.height - 10;
97    let left = 0;
98    let right = 0;
99    for (let i = 0, j = list.length - 1, ib = true, jb = true; i < list.length, j >= 0; i++, j--) {
100      if (list[j].startTs! <= endNS && jb) {
101        right = j;
102        jb = false;
103      }
104      if (list[i].startTs! + list[i].dur! >= startNS && ib) {
105        left = i;
106        ib = false;
107      }
108      if (!ib && !jb) {
109        break;
110      }
111    }
112    let slice = list.slice(left, right + 1);
113    let sum = 0;
114    for (let i = 0; i < slice.length; i++) {
115      if (!slice[i].frame) {
116        slice[i].frame = new Rect(0, 0, 0, 0);
117        slice[i].frame!.y = y;
118        slice[i].frame!.height = frameHeight;
119      }
120      if (slice[i].dur! >= pns) {
121        //@ts-ignore
122        slice[i].v = true;
123        CpuStateStruct.setFrame(slice[i], 5, startNS, endNS, totalNS, frame);
124      } else {
125        if (i > 0) {
126          //@ts-ignore
127          let c = slice[i].startTs - slice[i - 1].startTs - slice[i - 1].dur;
128          if (c < pns && sum < pns) {
129            //@ts-ignore
130            sum += c + slice[i - 1].dur;
131            //@ts-ignore
132            slice[i].v = false;
133          } else {
134            //@ts-ignore
135            slice[i].v = true;
136            CpuStateStruct.setFrame(slice[i], 5, startNS, endNS, totalNS, frame);
137            sum = 0;
138          }
139        }
140      }
141    }
142    //@ts-ignore
143    cpuStateRes.push(...slice.filter((it) => it.v));
144  }
145
146  setFrameByFilter(cpuStateRes: CpuStateStruct[], startNS: number, endNS: number, totalNS: number, frame: Rect): void {
147    for (let i = 0, len = cpuStateRes.length; i < len; i++) {
148      if (
149        (cpuStateRes[i].startTs || 0) + (cpuStateRes[i].dur || 0) >= startNS &&
150        (cpuStateRes[i].startTs || 0) <= endNS
151      ) {
152        CpuStateStruct.setFrame(cpuStateRes[i], 5, startNS, endNS, totalNS, frame);
153      } else {
154        cpuStateRes[i].frame = undefined;
155      }
156    }
157  }
158
159  cpuState(
160    arr: CpuStateStruct[],
161    arr2: CpuStateStruct[],
162    type: string,
163    cpuStateRes: CpuStateStruct[],
164    cpu: number,
165    startNS: number,
166    endNS: number,
167    totalNS: number,
168    frame: Rect,
169    use: boolean
170  ): void {
171    if (use && cpuStateRes.length > 0) {
172      this.setFrameByFilter(cpuStateRes, startNS, endNS, totalNS, frame);
173      return;
174    }
175    cpuStateRes.length = 0;
176    if (arr) {
177      this.setFrameByArr(cpuStateRes, startNS, endNS, totalNS, frame, arr2);
178    }
179  }
180}
181export function CpuStateStructOnClick(
182  clickRowType: string,
183  sp: SpSystemTrace,
184  entry?: CpuStateStruct,
185): Promise<unknown> {
186  return new Promise((resolve, reject) => {
187    if (clickRowType === TraceRow.ROW_TYPE_CPU_STATE && (CpuStateStruct.hoverStateStruct || entry)) {
188      CpuStateStruct.selectStateStruct = entry || CpuStateStruct.hoverStateStruct;
189      sp.traceSheetEL?.displayCpuStateData();
190      sp.timerShaftEL?.modifyFlagList(undefined);
191      reject(new Error());
192    } else {
193      resolve(null);
194    }
195  });
196}
197export class CpuStateStruct extends BaseStruct {
198  static hoverStateStruct: CpuStateStruct | undefined;
199  static selectStateStruct: CpuStateStruct | undefined;
200  dur: number | undefined;
201  value: string | undefined;
202  startTs: number | undefined;
203  height: number | undefined;
204  cpu: number | undefined;
205
206  static draw(ctx: CanvasRenderingContext2D, path: Path2D, data: CpuStateStruct): void {
207    if (data.frame) {
208      let chartColor = ColorUtils.colorForTid(data.cpu!);
209      ctx.font = '11px sans-serif';
210      ctx.fillStyle = chartColor;
211      ctx.strokeStyle = chartColor;
212      ctx.globalAlpha = 0.6;
213      if (data === CpuStateStruct.hoverStateStruct || data === CpuStateStruct.selectStateStruct) {
214        path.rect(data.frame.x, 35 - (data.height || 0), data.frame.width, data.height || 0);
215        ctx.lineWidth = 1;
216        ctx.globalAlpha = 1.0;
217        ctx.beginPath();
218        ctx.arc(data.frame.x, 35 - (data.height || 0), 3, 0, 2 * Math.PI, true);
219        ctx.stroke();
220        ctx.beginPath();
221        ctx.moveTo(data.frame.x + 3, 35 - (data.height || 0));
222        ctx.lineWidth = 3;
223        ctx.lineTo(data.frame.x + data.frame.width, 35 - (data.height || 0));
224        ctx.stroke();
225        ctx.lineWidth = 1;
226        ctx.globalAlpha = 0.6;
227        ctx.fillRect(data.frame.x, 35 - (data.height || 0), data.frame.width, data.height || 0);
228      } else {
229        ctx.globalAlpha = 0.6;
230        ctx.fillRect(data.frame.x, 35 - (data.height || 0), data.frame.width, data.height || 0);
231      }
232    }
233  }
234
235  static setCpuFrame(cpuStateNode: CpuStateStruct, pns: number, startNS: number, endNS: number, frame: Rect): void {
236    if (!cpuStateNode.frame) {
237      return;
238    }
239    //@ts-ignore
240    if ((cpuStateNode.startTime || 0) < startNS) {
241      cpuStateNode.frame.x = 0;
242    } else {
243      cpuStateNode.frame.x = Math.floor(((cpuStateNode.startTs || 0) - startNS) / pns);
244    }
245    //@ts-ignore
246    if ((cpuStateNode.startTime || 0) + (cpuStateNode.dur || 0) > endNS) {
247      cpuStateNode.frame.width = frame.width - cpuStateNode.frame.x;
248    } else {
249      cpuStateNode.frame.width = Math.ceil(
250        ((cpuStateNode.startTs || 0) + (cpuStateNode.dur || 0) - startNS) / pns - cpuStateNode.frame.x
251      );
252    }
253    if (cpuStateNode.frame.width < 1) {
254      cpuStateNode.frame.width = 1;
255    }
256  }
257  static setFrame(
258    cpuStateNode: CpuStateStruct,
259    padding: number,
260    startNS: number,
261    endNS: number,
262    totalNS: number,
263    frame: Rect
264  ): void {
265    let x1: number;
266    let x2: number;
267    if ((cpuStateNode.startTs || 0) < startNS) {
268      x1 = 0;
269    } else {
270      x1 = ns2x(cpuStateNode.startTs || 0, startNS, endNS, totalNS, frame);
271    }
272    if ((cpuStateNode.startTs || 0) + (cpuStateNode.dur || 0) > endNS) {
273      x2 = frame.width;
274    } else {
275      x2 = ns2x((cpuStateNode.startTs || 0) + (cpuStateNode.dur || 0), startNS, endNS, totalNS, frame);
276    }
277    let cpuStateGetV: number = x2 - x1 <= 1 ? 1 : x2 - x1;
278    if (!cpuStateNode.frame) {
279      cpuStateNode.frame = new Rect(0, 0, 0, 0);
280    }
281    cpuStateNode.frame.x = Math.ceil(x1);
282    cpuStateNode.frame.y = frame.y + padding;
283    cpuStateNode.frame.width = Math.floor(cpuStateGetV);
284    cpuStateNode.frame.height = cpuStateNode.height!;
285  }
286}
287