• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 { TraceRow } from '../../component/trace/base/TraceRow';
18import {
19  isFrameContainPoint,
20  ns2x,
21  Render,
22  RequestMessage,
23  drawFunString,
24  drawLoadingFrame,
25  Rect,
26} from './ProcedureWorkerCommon';
27import { FuncStruct as BaseFuncStruct } from '../../bean/FuncStruct';
28import { FlagsConfig } from '../../component/SpFlags';
29import { TabPaneTaskFrames } from '../../component/trace/sheet/task/TabPaneTaskFrames';
30import { SpSystemTrace } from '../../component/SpSystemTrace';
31export class FuncRender {
32  renderMainThread(
33    req: {
34      useCache: boolean;
35      context: CanvasRenderingContext2D;
36      type: string;
37    },
38    row: TraceRow<FuncStruct>
39  ): void {
40    let funcList = row.dataList;
41    let funcFilter = row.dataListCache;
42    func(
43      funcList,
44      funcFilter,
45      TraceRow.range!.startNS,
46      TraceRow.range!.endNS,
47      TraceRow.range!.totalNS,
48      row.frame,
49      req.useCache || !TraceRow.range!.refresh,
50      row.funcExpand
51    );
52    drawLoadingFrame(req.context, funcFilter, row, true);
53    req.context.beginPath();
54    let funcFind = false;
55    for (let re of funcFilter) {
56      FuncStruct.draw(req.context, re);
57      if (row.isHover) {
58        if (re.dur === 0 || re.dur === null || re.dur === undefined) {
59          if (
60            re.frame &&
61            re.itid &&
62            row.hoverX >= re.frame.x - 5 &&
63            row.hoverX <= re.frame.x + 5 &&
64            row.hoverY >= re.frame.y &&
65            row.hoverY <= re.frame.y + re.frame.height
66          ) {
67            FuncStruct.hoverFuncStruct = re;
68            funcFind = true;
69          }
70        } else {
71          if (re.frame && re.itid && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) {
72            FuncStruct.hoverFuncStruct = re;
73            funcFind = true;
74          }
75        }
76      }
77    }
78    if (!funcFind && row.isHover) {
79      FuncStruct.hoverFuncStruct = undefined;
80    }
81    req.context.closePath();
82  }
83
84  render(req: RequestMessage, list: Array<FuncStruct>, filter: Array<FuncStruct>): void {}
85}
86
87export function func(
88  funcList: Array<FuncStruct>,
89  funcFilter: Array<FuncStruct>,
90  startNS: number,
91  endNS: number,
92  totalNS: number,
93  frame: Rect,
94  use: boolean,
95  expand: boolean
96): void {
97  if (use && funcFilter.length > 0) {
98    for (let i = 0, len = funcFilter.length; i < len; i++) {
99      if ((funcFilter[i].startTs || 0) + (funcFilter[i].dur || 0) >= startNS && (funcFilter[i].startTs || 0) <= endNS) {
100        FuncStruct.setFuncFrame(funcFilter[i], 0, startNS, endNS, totalNS, frame);
101      } else {
102        funcFilter[i].frame = undefined;
103      }
104    }
105    return;
106  }
107  funcFilter.length = 0;
108  if (funcList) {
109    let groups = funcList
110      .filter(
111        (it) =>
112          (it.startTs ?? 0) + (it.dur ?? 0) >= startNS &&
113          (it.startTs ?? 0) <= endNS &&
114          ((!expand && it.depth === 0) || expand)
115      )
116      .map((it) => {
117        FuncStruct.setFuncFrame(it, 0, startNS, endNS, totalNS, frame);
118        return it;
119      })
120      .reduce((pre, current, index, arr) => {
121        //@ts-ignore
122        (pre[`${current.frame.x}-${current.depth}`] = pre[`${current.frame.x}-${current.depth}`] || []).push(current);
123        return pre;
124      }, {});
125    Reflect.ownKeys(groups).map((kv) => {
126      //@ts-ignore
127      let arr = groups[kv].sort((a: FuncStruct, b: FuncStruct) => b.dur - a.dur);
128      funcFilter.push(arr[0]);
129    });
130  }
131}
132export function funcStructOnClick(
133  clickRowType: string,
134  sp: SpSystemTrace,
135  row: TraceRow<FuncStruct> | undefined,
136  scrollToFuncHandler: Function,
137  entry?: FuncStruct
138): Promise<unknown> {
139  return new Promise((resolve, reject) => {
140    if (clickRowType === TraceRow.ROW_TYPE_FUNC && (FuncStruct.hoverFuncStruct || entry)) {
141      if (FuncStruct.funcSelect) {
142        TabPaneTaskFrames.TaskArray = [];
143        sp.removeLinkLinesByBusinessType('task');
144        let hoverFuncStruct = entry || FuncStruct.hoverFuncStruct;
145        FuncStruct.selectFuncStruct = hoverFuncStruct;
146        sp.timerShaftEL?.drawTriangle(FuncStruct.selectFuncStruct!.startTs || 0, 'inverted');
147        let flagConfig = FlagsConfig.getFlagsConfig('TaskPool');
148        let showTabArray: Array<string> = ['current-selection'];
149        if (flagConfig!.TaskPool === 'Enabled') {
150          if (FuncStruct.selectFuncStruct?.funName) {
151            if (FuncStruct.selectFuncStruct.funName.indexOf('H:Task ') >= 0) {
152              showTabArray.push('box-task-frames');
153              sp.drawTaskPollLine(row);
154            }
155          }
156        }
157        sp.traceSheetEL?.displayFuncData(showTabArray, FuncStruct.selectFuncStruct!, scrollToFuncHandler);
158        sp.timerShaftEL?.modifyFlagList(undefined);
159      }
160      reject(new Error());
161    } else {
162      resolve(null);
163    }
164  });
165}
166export class FuncStruct extends BaseFuncStruct {
167  static hoverFuncStruct: FuncStruct | undefined;
168  static selectFuncStruct: FuncStruct | undefined;
169  flag: string | undefined; // 570000
170  textMetricsWidth: number | undefined;
171  static funcSelect: boolean = true;
172  static setFuncFrame(
173    funcNode: FuncStruct,
174    padding: number,
175    startNS: number,
176    endNS: number,
177    totalNS: number,
178    frame: Rect
179  ): void {
180    let x1: number;
181    let x2: number;
182    if ((funcNode.startTs || 0) > startNS && (funcNode.startTs || 0) <= endNS) {
183      x1 = ns2x(funcNode.startTs || 0, startNS, endNS, totalNS, frame);
184    } else {
185      x1 = 0;
186    }
187    if (
188      (funcNode.startTs || 0) + (funcNode.dur || 0) > startNS &&
189      (funcNode.startTs || 0) + (funcNode.dur || 0) <= endNS
190    ) {
191      x2 = ns2x((funcNode.startTs || 0) + (funcNode.dur || 0), startNS, endNS, totalNS, frame);
192    } else {
193      x2 = frame.width;
194    }
195    if (!funcNode.frame) {
196      funcNode.frame = new Rect(0, 0, 0, 0);
197    }
198    let getV: number = x2 - x1 < 1 ? 1 : x2 - x1;
199    funcNode.frame.x = Math.floor(x1);
200    funcNode.frame.y = funcNode.depth! * 18 + 3;
201    funcNode.frame.width = Math.ceil(getV);
202    funcNode.frame.height = 18;
203  }
204
205  static draw(ctx: CanvasRenderingContext2D, data: FuncStruct): void {
206    if (data.frame) {
207      let isBinder = FuncStruct.isBinder(data);
208      if (data.dur === undefined || data.dur === null) {
209      } else {
210        ctx.globalAlpha = 1;
211        ctx.fillStyle = ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.funName || '', 0, ColorUtils.FUNC_COLOR.length)];
212        let textColor = ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.funName || '', 0, ColorUtils.FUNC_COLOR.length)];
213        if (FuncStruct.hoverFuncStruct && data.funName === FuncStruct.hoverFuncStruct.funName) {
214          ctx.globalAlpha = 0.7;
215        }
216        ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, data.frame.height);
217        if (data.frame.width > 10) {
218          ctx.fillStyle = ColorUtils.funcTextColor(textColor);
219          ctx.textBaseline = 'middle';
220          drawFunString(ctx, `${data.funName || ''}`, 5, data.frame, data);
221        }
222        if (
223          data.callid === FuncStruct.selectFuncStruct?.callid &&
224          data.startTs === FuncStruct.selectFuncStruct?.startTs &&
225          data.depth === FuncStruct.selectFuncStruct?.depth
226        ) {
227          ctx.strokeStyle = '#000';
228          ctx.lineWidth = 2;
229          ctx.strokeRect(data.frame.x, data.frame.y + 1, data.frame.width, data.frame.height - 2);
230        }
231        let flagConfig = FlagsConfig.getFlagsConfig('TaskPool');
232        if (
233          flagConfig!.TaskPool === 'Enabled' &&
234          data.funName!.indexOf('H:Task PerformTask End:') >= 0 &&
235          data.funName!.indexOf('Successful') < 0
236        ) {
237          if (data.frame!.width < 10) {
238            FuncStruct.drawTaskPoolUnSuccessFlag(ctx, data.frame!.x, (data.depth! + 0.5) * 18, 3, data!);
239          } else {
240            FuncStruct.drawTaskPoolUnSuccessFlag(ctx, data.frame!.x, (data.depth! + 0.5) * 18, 6, data!);
241          }
242        }
243        if (flagConfig!.TaskPool === 'Enabled' && data.funName!.indexOf('H:Thread Timeout Exit') >= 0) {
244          FuncStruct.drawTaskPoolTimeOutFlag(ctx, data.frame!.x, (data.depth! + 0.5) * 18, 10, data!);
245        }
246        // 如果该函数没有结束时间,则绘制锯齿。
247        if (data.nofinish && data.frame!.width > 4) {
248          FuncStruct.drawRupture(ctx, data.frame.x, data.frame.y, data.frame.width, data.frame.height);
249        }
250      }
251    }
252  }
253
254  /**
255   * 绘制锯齿
256   * @param ctx 绘图上下文环境
257   * @param x 水平坐标
258   * @param y 垂直坐标
259   * @param width 函数矩形框的宽度
260   * @param height 函数矩形框的高度
261   */
262  static drawRupture(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number): void {
263    ctx.fillStyle = '#fff'; // 白色: '#fff' , 红色: '#FF0000';
264    let ruptureWidth = 5;
265    let ruptureNode = height / ruptureWidth;
266    let len = height / ruptureNode;
267    ctx.moveTo(x + width - 1, y);
268    for (let i = 1; i <= ruptureNode; i++) {
269      ctx.lineTo(x + width - 1 - (i % 2 === 0 ? 0 : ruptureWidth), y + len * i - 2);
270    }
271    ctx.closePath();
272    ctx.fill();
273  }
274
275  static drawTaskPoolUnSuccessFlag(
276    ctx: CanvasRenderingContext2D,
277    x: number,
278    y: number,
279    radius: number,
280    data: FuncStruct
281  ): void {
282    ctx.strokeStyle = '#FFC880';
283    ctx.lineWidth = 1;
284    ctx.beginPath();
285    ctx.arc(x + data.frame!.width, y, radius, 0, Math.PI * 2);
286    ctx.closePath();
287    ctx.fillStyle = '#E64566';
288    ctx.fill();
289    ctx.stroke();
290  }
291
292  static drawTaskPoolTimeOutFlag(
293    canvas: CanvasRenderingContext2D,
294    x: number,
295    y: number,
296    radius: number,
297    data: FuncStruct
298  ): void {
299    canvas.strokeStyle = '#FFC880';
300    canvas.lineWidth = 1;
301    canvas.beginPath();
302    canvas.arc(x + data.frame!.width + 20, y, radius, 0, Math.PI * 2);
303    canvas.closePath();
304    canvas.fillStyle = '#FFC880';
305    canvas.fill();
306    canvas.stroke();
307    canvas.font = '18px Arial';
308    canvas.fillStyle = ColorUtils.GREY_COLOR;
309    canvas.textAlign = 'center';
310    canvas.fillText('¡', x + data.frame!.width + 20, y);
311  }
312
313  static isSelected(data: FuncStruct): boolean {
314    return (
315      FuncStruct.selectFuncStruct !== undefined &&
316      FuncStruct.selectFuncStruct.startTs === data.startTs &&
317      FuncStruct.selectFuncStruct.depth === data.depth
318    );
319  }
320}
321