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 { ColorUtils } from '../../component/trace/base/ColorUtils';
17import {
18  BaseStruct,
19  Render,
20  ns2x,
21  Rect,
22  drawString,
23  isFrameContainPoint,
24  drawLoadingFrame,
25} from './ProcedureWorkerCommon';
26import { TraceRow } from '../../component/trace/base/TraceRow';
27import { SpSystemTrace } from '../../component/SpSystemTrace';
28import { SpUserFileChart } from '../../component/chart/SpUserPluginChart';
29
30const SAMPLE_STRUCT_HEIGHT = 20;
31const Y_PADDING = 2;
32
33export class SampleRender extends Render {
34  renderMainThread(
35    req: {
36      context: CanvasRenderingContext2D;
37      useCache: boolean;
38      type: string;
39      start_ts: number;
40      uniqueProperty: Array<unknown>;
41      flattenTreeArray: Array<SampleStruct>;
42    },
43    row: TraceRow<SampleStruct>
44  ): void {
45    let startTs = req.start_ts;
46    let sampleList = row.dataList;
47    let sampleFilter = row.dataListCache;
48    SampleStruct.reqProperty = req;
49    func(
50      sampleList,
51      sampleFilter,
52      TraceRow.range!.startNS,
53      TraceRow.range!.endNS,
54      TraceRow.range!.totalNS,
55      startTs,
56      row.frame,
57      req.useCache || TraceRow.range!.refresh
58    );
59    drawLoadingFrame(req.context, sampleFilter, row, true);
60    req.context.beginPath();
61    let find = false;
62    for (let re of sampleFilter) {
63      SampleStruct.draw(req.context, re);
64      if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) {
65        SampleStruct.hoverSampleStruct = re;
66        find = true;
67      }
68    }
69    if (!find && row.isHover) {
70      SampleStruct.hoverSampleStruct = undefined;
71    }
72    req.context.closePath();
73  }
74}
75
76export function func(
77  sampleList: Array<SampleStruct>,
78  sampleFilter: Array<SampleStruct>,
79  startNS: number,
80  endNS: number,
81  totalNS: number,
82  startTS: number,
83  frame: Rect,
84  use: boolean
85): void {
86  if (use && sampleFilter.length > 0) {
87    for (let i = 0, len = sampleFilter.length; i < len; i++) {
88      //@ts-ignore
89      if ((sampleFilter[i].end - startTS || 0) >= startNS && (sampleFilter[i].begin - startTS || 0) <= endNS) {
90        SampleStruct.setSampleFrame(sampleFilter[i], 0, startNS, endNS, totalNS, startTS, frame);
91      } else {
92        sampleFilter[i].frame = undefined;
93      }
94    }
95    return;
96  }
97  sampleFilter.length = 0;
98  setSampleFilter(sampleList, sampleFilter, startNS, startTS, endNS, totalNS, frame);
99}
100
101function setSampleFilter(
102  sampleList: Array<SampleStruct>,
103  sampleFilter: Array<SampleStruct>,
104  startNS: number,
105  startTS: number,
106  endNS: number,
107  totalNS: number,
108  frame: Rect
109): void {
110  if (sampleList) {
111    sampleList.forEach((func) => {
112      let funcProperty: Array<unknown> = func.property!;
113      let groups = funcProperty
114        //@ts-ignore
115        .filter((it) => (it.end - startTS ?? 0) >= startNS && (it.begin - startTS ?? 0) <= endNS)
116        .map((it) => {
117          //@ts-ignore
118          SampleStruct.setSampleFrame(it, 0, startNS, endNS, totalNS, startTS, frame);
119          return it;
120        })
121        .reduce((pre, current) => {
122          //@ts-ignore
123          (pre[`${current.frame.x}-${current.depth}`] = pre[`${current.frame.x}-${current.depth}`] || []).push(current);
124          return pre;
125        }, {});
126      //@ts-ignore
127      Reflect.ownKeys(groups).map((kv) => {
128        //@ts-ignore
129        let arr = groups[kv].sort((a: unknown, b: unknown) => b.end - b.start - (a.end - a.start));
130        sampleFilter.push(arr[0]);
131      });
132    });
133  }
134}
135
136export function sampleStructOnClick(
137  clickRowType: string,
138  sp: SpSystemTrace,
139  row: TraceRow<SampleStruct> | undefined,
140  entry?: SampleStruct,
141): Promise<unknown> {
142  return new Promise((resolve, reject) => {
143    if (clickRowType === TraceRow.ROW_TYPE_SAMPLE && (SampleStruct.hoverSampleStruct || entry)) {
144      SampleStruct.selectSampleStruct = entry || SampleStruct.hoverSampleStruct;
145      if (row?.rowId === 'userPlugin') {
146        SpUserFileChart.userPluginData!.map((v: unknown) => {
147          //@ts-ignore
148          if (v.func_name === SampleStruct.selectSampleStruct!.name &&
149            //@ts-ignore
150            v.begin === SampleStruct.selectSampleStruct?.begin) {
151            sp.traceSheetEL?.displayUserPlugin(v)
152          }
153        })
154      } else {
155        sp.traceSheetEL?.displaySampleData(SampleStruct.selectSampleStruct!, SampleStruct.reqProperty);
156        sp.timerShaftEL?.modifyFlagList(undefined);
157      }
158      reject(new Error());
159    } else {
160      resolve(null);
161    }
162  });
163}
164
165export class SampleStruct extends BaseStruct {
166  static hoverSampleStruct: SampleStruct | undefined;
167  static selectSampleStruct: SampleStruct | undefined;
168  static reqProperty: unknown | undefined;
169  name: string | undefined;
170  detail: string | undefined;
171  property: Array<unknown> | undefined;
172  begin: number | undefined;
173  end: number | undefined;
174  depth: number | undefined;
175  startTs: number | undefined;
176  instructions: number | undefined;
177  cycles: number | undefined;
178  static setSampleFrame(
179    sampleNode: SampleStruct,
180    padding: number,
181    startNS: number,
182    endNS: number,
183    totalNS: number,
184    startTS: number,
185    frame: Rect
186  ): void {
187    let x1: number, x2: number;
188    if ((sampleNode.begin! - startTS || 0) > startNS && (sampleNode.begin! - startTS || 0) < endNS) {
189      x1 = ns2x(sampleNode.begin! - startTS || 0, startNS, endNS, totalNS, frame);
190    } else {
191      x1 = 0;
192    }
193    if ((sampleNode.end! - startTS || 0) > startNS && (sampleNode.end! - startTS || 0) < endNS) {
194      x2 = ns2x(sampleNode.end! - startTS || 0, startNS, endNS, totalNS, frame);
195    } else {
196      x2 = frame.width;
197    }
198    if (!sampleNode.frame) {
199      sampleNode.frame! = new Rect(0, 0, 0, 0);
200    }
201    let getV: number = x2 - x1 < 1 ? 1 : x2 - x1;
202    sampleNode.frame!.x = Math.floor(x1);
203    sampleNode.frame!.y = sampleNode.depth! * SAMPLE_STRUCT_HEIGHT;
204    sampleNode.frame!.width = Math.ceil(getV);
205    sampleNode.frame!.height = SAMPLE_STRUCT_HEIGHT;
206    sampleNode.startTs = startTS;
207  }
208  static draw(ctx: CanvasRenderingContext2D, data: SampleStruct): void {
209    if (data.depth === undefined || data.depth === null) {
210      return;
211    }
212    if (data.frame) {
213      ctx.globalAlpha = 1;
214      ctx.fillStyle =
215        ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.name || '', data.depth, ColorUtils.FUNC_COLOR.length)];
216      const textColor =
217        ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.name || '', data.depth, ColorUtils.FUNC_COLOR.length)];
218      if (SampleStruct.hoverSampleStruct && data.name === SampleStruct.hoverSampleStruct.name) {
219        ctx.globalAlpha = 0.7;
220      }
221      ctx.strokeStyle = '#fff';
222      ctx.lineWidth = 1;
223      ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, SAMPLE_STRUCT_HEIGHT - Y_PADDING);
224      ctx.fillStyle = ColorUtils.funcTextColor(textColor);
225      drawString(ctx, `${data.detail + '(' + data.name + ')' || ''}`, 5, data.frame, data);
226      if (data === SampleStruct.selectSampleStruct) {
227        ctx.strokeStyle = '#000';
228        ctx.lineWidth = 2;
229        ctx.strokeRect(data.frame.x, data.frame.y + 1, data.frame.width, SAMPLE_STRUCT_HEIGHT - Y_PADDING - 2);
230      }
231    }
232  }
233  static equals(d1: SampleStruct, d2: SampleStruct): boolean {
234    return d1 && d2 && d1.name === d2.name && d1.begin === d2.begin;
235  }
236}
237