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