• 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 { CpuStruct, WakeupBean } from './ProcedureWorkerCPU.js';
17import { TraceRow } from '../../component/trace/base/TraceRow.js';
18import { TimerShaftElement } from '../../component/trace/TimerShaftElement';
19
20export abstract class Render {
21  abstract renderMainThread(req: any, row: TraceRow<any>): void;
22}
23
24export abstract class PerfRender {
25  abstract render(req: RequestMessage, list: Array<any>, filter: Array<any>, dataList2: Array<any>): void;
26}
27
28export class RequestMessage {
29  type: string | undefined | null;
30  lazyRefresh: boolean | undefined;
31  intervalPerf: any;
32  canvas: any;
33  context: any;
34  params: any;
35  online: any;
36  buf: any;
37  isRangeSelect: any;
38  isHover: any;
39  xs: any;
40  frame: any;
41  flagMoveInfo: any;
42  flagSelectedInfo: any;
43  hoverX: any;
44  hoverY: any;
45  startNS: any;
46  endNS: any;
47  totalNS: any;
48  slicesTime:
49    | {
50        startTime: number | null;
51        endTime: number | null;
52        color: string | null;
53      }
54    | undefined;
55  range: any;
56  scale: any;
57  chartColor: any;
58  canvasWidth: any;
59  canvasHeight: any;
60  useCache: any;
61  lineColor: any;
62  wakeupBean: WakeupBean | undefined | null;
63  id: any;
64  postMessage:
65    | {
66        (message: any, targetOrigin: string, transfer?: Transferable[]): void;
67        (message: any, options?: WindowPostMessageOptions): void;
68      }
69    | undefined;
70}
71
72export function ns2s(ns: number): string {
73  let second1 = 1_000_000_000; // 1 second
74  let millisecond = 1_000_000; // 1 millisecond
75  let microsecond = 1_000; // 1 microsecond
76  let res;
77  if (ns >= second1) {
78    res = (ns / 1000 / 1000 / 1000).toFixed(1) + ' s';
79  } else if (ns >= millisecond) {
80    res = (ns / 1000 / 1000).toFixed(1) + ' ms';
81  } else if (ns >= microsecond) {
82    res = (ns / 1000).toFixed(1) + ' μs';
83  } else if (ns > 0) {
84    res = ns.toFixed(1) + ' ns';
85  } else {
86    res = ns.toFixed(1) + ' s';
87  }
88  return res;
89}
90
91export function isFrameContainPoint(frame: Rect, x: number, y: number): boolean {
92  return x >= frame.x && x <= frame.x + frame.width && y >= frame.y && y <= frame.y + frame.height;
93}
94export const isSurroundingPoint = function (pointX: number, currentRect: Rect, unitPointXRange: number): boolean {
95  return (pointX >= currentRect?.x - unitPointXRange) && pointX <= currentRect?.x + unitPointXRange;
96};
97
98export const computeUnitWidth = function (
99  preTs: number,
100  currentTs: number,
101  frameWidth: number,
102  selectUnitWidth: number
103): number {
104  let max = 150;
105  let unitWidth = ((currentTs - preTs) * frameWidth) / (TraceRow.range!.endNS - TraceRow.range!.startNS);
106  if (unitWidth < selectUnitWidth) {
107    return unitWidth > max || unitWidth === 0 ? max : unitWidth;
108  }
109  return selectUnitWidth > max || selectUnitWidth === 0 ? max : selectUnitWidth;
110};
111
112class FilterConfig {
113  startNS: number = 0;
114  endNS: number = 0;
115  totalNS: number = 0;
116  frame: any = null;
117  useCache: boolean = false;
118  startKey: string = 'startNS';
119  durKey: string = 'dur';
120  paddingTop: number = 0;
121}
122
123export function fillCacheData(filterList: Array<any>, condition: FilterConfig): boolean {
124  if (condition.useCache && filterList.length > 0) {
125    let pns = (condition.endNS - condition.startNS) / condition.frame.width;
126    let y = condition.frame.y + condition.paddingTop;
127    let height = condition.frame.height - condition.paddingTop * 2;
128    for (let i = 0, len = filterList.length; i < len; i++) {
129      let it = filterList[i];
130      if (
131        (it[condition.startKey] || 0) + (it[condition.durKey] || 0) > condition.startNS &&
132        (it[condition.startKey] || 0) < condition.endNS
133      ) {
134        if (!filterList[i].frame) {
135          filterList[i].frame = {};
136          filterList[i].frame.y = y;
137          filterList[i].frame.height = height;
138        }
139        setNodeFrame(
140          filterList[i],
141          pns,
142          condition.startNS,
143          condition.endNS,
144          condition.frame,
145          condition.startKey,
146          condition.durKey
147        );
148      } else {
149        filterList[i].frame = null;
150      }
151    }
152    return true;
153  }
154  return false;
155}
156
157export function findRange(fullData: Array<any>, condition: FilterConfig): Array<any> {
158  let left = 0,
159    right = 0;
160  for (let i = 0, j = fullData.length - 1, ib = true, jb = true; i < fullData.length, j >= 0; i++, j--) {
161    if (fullData[j][condition.startKey] <= condition.endNS && jb) {
162      right = j;
163      jb = false;
164    }
165    if (fullData[i][condition.startKey] + fullData[i][condition.durKey] >= condition.startNS && ib) {
166      left = i;
167      ib = false;
168    }
169    if (!ib && !jb) {
170      break;
171    }
172  }
173  let slice = fullData.slice(left, right + 1);
174  return slice;
175}
176
177export function dataFilterHandler(fullData: Array<any>, filterData: Array<any>, condition: FilterConfig): void {
178  if (fillCacheData(filterData, condition)) {
179    return;
180  }
181  if (fullData) {
182    filterData.length = 0;
183    let pns = (condition.endNS - condition.startNS) / condition.frame.width; //每个像素多少ns
184    let y = condition.frame.y + condition.paddingTop;
185    let height = condition.frame.height - condition.paddingTop * 2;
186    let slice = findRange(fullData, condition);
187    let sum = 0;
188    for (let i = 0; i < slice.length; i++) {
189      if (!slice[i].frame) {
190        slice[i].frame = {};
191        slice[i].frame.y = y;
192        slice[i].frame.height = height;
193      }
194      if (i === slice.length - 1) {
195        if (slice[i][condition.durKey] === undefined || slice[i][condition.durKey] === null) {
196          slice[i][condition.durKey] = (condition.endNS || 0) - (slice[i][condition.startKey] || 0);
197        }
198      } else {
199        if (slice[i][condition.durKey] === undefined || slice[i][condition.durKey] === null) {
200          slice[i][condition.durKey] = (slice[i + 1][condition.startKey] || 0) - (slice[i][condition.startKey] || 0);
201        }
202      }
203      if (slice[i][condition.durKey] >= pns || slice.length < 100) {
204        slice[i].v = true;
205        setNodeFrame(
206          slice[i],
207          pns,
208          condition.startNS,
209          condition.endNS,
210          condition.frame,
211          condition.startKey,
212          condition.durKey
213        );
214      } else {
215        if (i > 0) {
216          let c = slice[i][condition.startKey] - slice[i - 1][condition.startKey] - slice[i - 1][condition.durKey];
217          if (c < pns && sum < pns) {
218            sum += c + slice[i - 1][condition.durKey];
219            slice[i].v = false;
220          } else {
221            slice[i].v = true;
222            setNodeFrame(
223              slice[i],
224              pns,
225              condition.startNS,
226              condition.endNS,
227              condition.frame,
228              condition.startKey,
229              condition.durKey
230            );
231            sum = 0;
232          }
233        }
234      }
235    }
236    filterData.push(...slice.filter((it) => it.v));
237  }
238}
239
240function setNodeFrame(
241  node: any,
242  pns: number,
243  startNS: number,
244  endNS: number,
245  frame: any,
246  startKey: string,
247  durKey: string
248) {
249  if ((node[startKey] || 0) < startNS) {
250    node.frame.x = 0;
251  } else {
252    node.frame.x = Math.floor(((node[startKey] || 0) - startNS) / pns);
253  }
254  if ((node[startKey] || 0) + (node[durKey] || 0) > endNS) {
255    node.frame.width = frame.width - node.frame.x;
256  } else {
257    node.frame.width = Math.ceil(((node[startKey] || 0) + (node[durKey] || 0) - startNS) / pns - node.frame.x);
258  }
259  if (node.frame.width < 1) {
260    node.frame.width = 1;
261  }
262}
263
264export function ns2x(ns: number, startNS: number, endNS: number, duration: number, rect: any) {
265  // @ts-ignore
266  if (endNS == 0) {
267    endNS = duration;
268  }
269  let xSize: number = ((ns - startNS) * rect.width) / (endNS - startNS);
270  if (xSize < 0) {
271    xSize = 0;
272  } else if (xSize > rect.width) {
273    xSize = rect.width;
274  }
275  return xSize;
276}
277
278export function ns2xByTimeShaft(ns: number, tse: TimerShaftElement) {
279  let startNS = tse.getRange()!.startNS;
280  let endNS = tse.getRange()!.endNS;
281  let duration = tse.getRange()!.totalNS;
282  if (endNS == 0) {
283    endNS = duration;
284  }
285  let width = tse.getBoundingClientRect().width - 258;
286  let xSize: number = ((ns - startNS) * width) / (endNS - startNS);
287  if (xSize < 0) {
288    xSize = 0;
289  } else if (xSize > width) {
290    xSize = width;
291  }
292  return xSize;
293}
294
295export class Rect {
296  x: number = 0;
297  y: number = 0;
298  width: number = 0;
299  height: number = 0;
300  constructor(x: number, y: number, width: number, height: number) {
301    this.x = x;
302    this.y = y;
303    this.width = width;
304    this.height = height;
305  }
306
307  static intersect(r1: Rect, rect: Rect): boolean {
308    let minX = r1.x <= rect.x ? r1.x : rect.x;
309    let minY = r1.y <= rect.y ? r1.y : rect.y;
310    let maxX = r1.x + r1.width >= rect.x + rect.width ? r1.x + r1.width : rect.x + rect.width;
311    let maxY = r1.y + r1.height >= rect.y + rect.height ? r1.y + r1.height : rect.y + rect.height;
312    if (maxX - minX <= rect.width + r1.width && maxY - minY <= r1.height + rect.height) {
313      return true;
314    } else {
315      return false;
316    }
317  }
318
319  static contains(rect: Rect, x: number, y: number): boolean {
320    return rect.x <= x && x <= rect.x + rect.width && rect.y <= y && y <= rect.y + rect.height;
321  }
322
323  static containsWithMargin(rect: Rect, x: number, y: number, t: number, r: number, b: number, l: number): boolean {
324    return rect.x - l <= x && x <= rect.x + rect.width + r && rect.y - t <= y && y <= rect.y + rect.height + b;
325  }
326
327  static containsWithPadding(
328    rect: Rect,
329    x: number,
330    y: number,
331    paddingLeftRight: number,
332    paddingTopBottom: number
333  ): boolean {
334    return (
335      rect.x + paddingLeftRight <= x &&
336      rect.y + paddingTopBottom <= y &&
337      x <= rect.x + rect.width - paddingLeftRight &&
338      y <= rect.y + rect.height - paddingTopBottom
339    );
340  }
341
342  /**
343   * 判断是否相交
344   * @param rect
345   */
346  intersect(rect: Rect): boolean {
347    let minX = this.x <= rect.x ? this.x : rect.x;
348    let minY = this.y <= rect.y ? this.y : rect.y;
349    let maxX = this.x + this.width >= rect.x + rect.width ? this.x + this.width : rect.x + rect.width;
350    let maxY = this.y + this.height >= rect.y + rect.height ? this.y + this.height : rect.y + rect.height;
351    if (maxX - minX <= rect.width + this.width && maxY - minY <= this.height + rect.height) {
352      return true;
353    } else {
354      return false;
355    }
356  }
357
358  contains(x: number, y: number): boolean {
359    return this.x <= x && x <= this.x + this.width && this.y <= y && y <= this.y + this.height;
360  }
361
362  containsWithMargin(x: number, y: number, t: number, r: number, b: number, l: number): boolean {
363    return this.x - l <= x && x <= this.x + this.width + r && this.y - t <= y && y <= this.y + this.height + b;
364  }
365
366  containsWithPadding(x: number, y: number, paddingLeftRight: number, paddingTopBottom: number): boolean {
367    return (
368      this.x + paddingLeftRight <= x &&
369      x <= this.x + this.width - paddingLeftRight &&
370      this.y + paddingTopBottom <= y &&
371      y <= this.y + this.height - paddingTopBottom
372    );
373  }
374}
375
376export class Point {
377  x: number = 0;
378  y: number = 0;
379  isRight: boolean = true;
380
381  constructor(x: number, y: number, isRight: boolean = true) {
382    this.x = x;
383    this.y = y;
384    this.isRight = isRight;
385  }
386}
387
388export enum LineType {
389  brokenLine,
390  bezierCurve,
391}
392
393export class PairPoint {
394  x: number = 0;
395  ns: number = 0;
396  y: number = 0;
397  offsetY: number = 0;
398  rowEL: TraceRow<any>;
399  isRight: boolean = true;
400  lineType?: LineType;
401  business: string = '';
402  hidden?: boolean = false;
403  constructor(
404    rowEL: TraceRow<any>,
405    x: number,
406    y: number,
407    ns: number,
408    offsetY: number,
409    isRight: boolean,
410    business: string
411  ) {
412    this.rowEL = rowEL;
413    this.x = x;
414    this.y = y;
415    this.ns = ns;
416    this.offsetY = offsetY;
417    this.isRight = isRight;
418    this.business = business;
419  }
420}
421
422export class BaseStruct {
423  translateY: number | undefined;
424  frame: Rect | undefined;
425  isHover: boolean = false;
426}
427
428export function drawLines(ctx: CanvasRenderingContext2D, xs: Array<any>, height: number, lineColor: string) {
429  if (ctx) {
430    ctx.beginPath();
431    ctx.lineWidth = 1;
432    ctx.strokeStyle = lineColor || '#dadada';
433    xs?.forEach((it) => {
434      ctx.moveTo(Math.floor(it), 0);
435      ctx.lineTo(Math.floor(it), height);
436    });
437    ctx.stroke();
438    ctx.closePath();
439  }
440}
441
442export function drawFlagLine(
443  commonCtx: any,
444  hoverFlag: any,
445  selectFlag: any,
446  startNS: number,
447  endNS: number,
448  totalNS: number,
449  frame: any,
450  slicesTime:
451    | {
452        startTime: number | null | undefined;
453        endTime: number | null | undefined;
454        color: string | null | undefined;
455      }
456    | undefined
457) {
458  if (commonCtx) {
459    if (hoverFlag) {
460      commonCtx.beginPath();
461      commonCtx.lineWidth = 2;
462      commonCtx.strokeStyle = hoverFlag?.color || '#dadada';
463      commonCtx.moveTo(Math.floor(hoverFlag.x), 0);
464      commonCtx.lineTo(Math.floor(hoverFlag.x), frame.height);
465      commonCtx.stroke();
466      commonCtx.closePath();
467    }
468    if (selectFlag) {
469      commonCtx.beginPath();
470      commonCtx.lineWidth = 2;
471      commonCtx.strokeStyle = selectFlag?.color || '#dadada';
472      selectFlag.x = ns2x(selectFlag.time, startNS, endNS, totalNS, frame);
473      commonCtx.moveTo(Math.floor(selectFlag.x), 0);
474      commonCtx.lineTo(Math.floor(selectFlag.x), frame.height);
475      commonCtx.stroke();
476      commonCtx.closePath();
477    }
478    if (slicesTime && slicesTime.startTime && slicesTime.endTime) {
479      commonCtx.beginPath();
480      commonCtx.lineWidth = 1;
481      commonCtx.strokeStyle = slicesTime.color || '#dadada';
482      let x1 = ns2x(slicesTime.startTime, startNS, endNS, totalNS, frame);
483      let x2 = ns2x(slicesTime.endTime, startNS, endNS, totalNS, frame);
484      commonCtx.moveTo(Math.floor(x1), 0);
485      commonCtx.lineTo(Math.floor(x1), frame.height);
486      commonCtx.moveTo(Math.floor(x2), 0);
487      commonCtx.lineTo(Math.floor(x2), frame.height);
488      commonCtx.stroke();
489      commonCtx.closePath();
490    }
491  }
492}
493
494export function drawFlagLineSegment(ctx: any, hoverFlag: any, selectFlag: any, frame: any, tse: TimerShaftElement) {
495  if (ctx) {
496    if (hoverFlag) {
497      ctx.beginPath();
498      ctx.lineWidth = 2;
499      ctx.strokeStyle = hoverFlag?.color || '#dadada';
500      ctx.moveTo(Math.floor(hoverFlag.x), 0);
501      ctx.lineTo(Math.floor(hoverFlag.x), frame.height);
502      ctx.stroke();
503      ctx.closePath();
504    }
505    if (selectFlag) {
506      ctx.beginPath();
507      ctx.lineWidth = 2;
508      ctx.strokeStyle = selectFlag?.color || '#dadada';
509      selectFlag.x = ns2x(
510        selectFlag.time,
511        TraceRow.range!.startNS,
512        TraceRow.range!.endNS,
513        TraceRow.range!.totalNS,
514        frame
515      );
516      ctx.moveTo(Math.floor(selectFlag.x), 0);
517      ctx.lineTo(Math.floor(selectFlag.x), frame.height);
518      ctx.stroke();
519      ctx.closePath();
520    }
521    tse.sportRuler!.slicesTimeList.forEach((slicesTime) => {
522      if (slicesTime && slicesTime.startTime && slicesTime.endTime) {
523        ctx.beginPath();
524        ctx.lineWidth = 1;
525        ctx.strokeStyle = slicesTime.color || '#dadada';
526        let x1 = ns2x(slicesTime.startTime, TraceRow.range!.startNS, TraceRow.range!.endNS, TraceRow.range!.totalNS, frame);
527        let x2 = ns2x(slicesTime.endTime, TraceRow.range!.startNS, TraceRow.range!.endNS, TraceRow.range!.totalNS, frame);
528        // 划线逻辑
529        ctx.moveTo(Math.floor(x1), 0);
530        ctx.lineTo(Math.floor(x1), frame.height); //左边的线
531        ctx.moveTo(Math.floor(x2), 0);
532        ctx.lineTo(Math.floor(x2), frame.height); // 右边的线
533        ctx.stroke();
534        ctx.closePath();
535      }
536    });
537  }
538}
539
540export function drawSelection(ctx: any, params: any) {
541  if (params.isRangeSelect && params.rangeSelectObject) {
542    params.rangeSelectObject!.startX = Math.floor(
543      ns2x(params.rangeSelectObject!.startNS!, params.startNS, params.endNS, params.totalNS, params.frame)
544    );
545    params.rangeSelectObject!.endX = Math.floor(
546      ns2x(params.rangeSelectObject!.endNS!, params.startNS, params.endNS, params.totalNS, params.frame)
547    );
548    if (ctx) {
549      ctx.globalAlpha = 0.5;
550      ctx.fillStyle = '#666666';
551      ctx.fillRect(
552        params.rangeSelectObject!.startX!,
553        params.frame.y,
554        params.rangeSelectObject!.endX! - params.rangeSelectObject!.startX!,
555        params.frame.height
556      );
557      ctx.globalAlpha = 1;
558    }
559  }
560}
561
562// draw range select
563export function drawSelectionRange(context: any, params: TraceRow<any>) {
564  if (params.rangeSelect && TraceRow.rangeSelectObject) {
565    TraceRow.rangeSelectObject!.startX = Math.floor(
566      ns2x(
567        TraceRow.rangeSelectObject!.startNS!,
568        TraceRow.range?.startNS ?? 0,
569        TraceRow.range?.endNS ?? 0,
570        TraceRow.range?.totalNS ?? 0,
571        params.frame
572      )
573    );
574    TraceRow.rangeSelectObject!.endX = Math.floor(
575      ns2x(
576        TraceRow.rangeSelectObject!.endNS!,
577        TraceRow.range?.startNS ?? 0,
578        TraceRow.range?.endNS ?? 0,
579        TraceRow.range?.totalNS ?? 0,
580        params.frame
581      )
582    );
583    if (context) {
584      context.globalAlpha = 0.5;
585      context.fillStyle = '#666666';
586      context.fillRect(
587        TraceRow.rangeSelectObject!.startX!,
588        params.frame.y,
589        TraceRow.rangeSelectObject!.endX! - TraceRow.rangeSelectObject!.startX!,
590        params.frame.height
591      );
592      context.globalAlpha = 1;
593    }
594  }
595}
596
597export function drawWakeUp(
598  wakeUpContext: CanvasRenderingContext2D | any,
599  wake: WakeupBean | undefined | null,
600  startNS: number,
601  endNS: number,
602  totalNS: number,
603  frame: Rect,
604  selectCpuStruct: CpuStruct | undefined = undefined,
605  wakeUpCurrentCpu: number | undefined = undefined,
606  noVerticalLine = false
607) {
608  if (wake) {
609    let x1 = Math.floor(ns2x(wake.wakeupTime || 0, startNS, endNS, totalNS, frame));
610    wakeUpContext.beginPath();
611    wakeUpContext.lineWidth = 2;
612    wakeUpContext.fillStyle = '#000000';
613    if (x1 > 0 && x1 < frame.x + frame.width) {
614      if (!noVerticalLine) {
615        wakeUpContext.moveTo(x1, frame.y);
616        wakeUpContext.lineTo(x1, frame.y + frame.height);
617      }
618      if (wakeUpCurrentCpu == wake.cpu) {
619        let centerY = Math.floor(frame.y + frame.height / 2);
620        wakeUpContext.moveTo(x1, centerY - 6);
621        wakeUpContext.lineTo(x1 + 4, centerY);
622        wakeUpContext.lineTo(x1, centerY + 6);
623        wakeUpContext.lineTo(x1 - 4, centerY);
624        wakeUpContext.lineTo(x1, centerY - 6);
625        wakeUpContext.fill();
626      }
627    }
628    if (selectCpuStruct) {
629      let x2 = Math.floor(ns2x(selectCpuStruct.startTime || 0, startNS, endNS, totalNS, frame));
630      let y = frame.y + frame.height - 10;
631      wakeUpContext.moveTo(x1, y);
632      wakeUpContext.lineTo(x2, y);
633
634      let s = ns2s((selectCpuStruct.startTime || 0) - (wake.wakeupTime || 0));
635      let distance = x2 - x1;
636      if (distance > 12) {
637        wakeUpContext.moveTo(x1, y);
638        wakeUpContext.lineTo(x1 + 6, y - 3);
639        wakeUpContext.moveTo(x1, y);
640        wakeUpContext.lineTo(x1 + 6, y + 3);
641        wakeUpContext.moveTo(x2, y);
642        wakeUpContext.lineTo(x2 - 6, y - 3);
643        wakeUpContext.moveTo(x2, y);
644        wakeUpContext.lineTo(x2 - 6, y + 3);
645        let measure = wakeUpContext.measureText(s);
646        let tHeight = measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent;
647        let xStart = x1 + Math.floor(distance / 2 - measure.width / 2);
648        if (distance > measure.width + 4) {
649          wakeUpContext.fillStyle = '#ffffff';
650          wakeUpContext.fillRect(xStart - 2, y - 4 - tHeight, measure.width + 4, tHeight + 4);
651          wakeUpContext.font = '10px solid';
652          wakeUpContext.fillStyle = '#000000';
653          wakeUpContext.textBaseline = 'bottom';
654          wakeUpContext.fillText(s, xStart, y - 2);
655        }
656      }
657    }
658    wakeUpContext.strokeStyle = '#000000';
659    wakeUpContext.stroke();
660    wakeUpContext.closePath();
661  }
662}
663
664const wid = 5;
665const linkLineColor = '#ff0000';
666export function drawLinkLines(
667  context: CanvasRenderingContext2D,
668  nodes: PairPoint[][],
669  tm: TimerShaftElement,
670  isFavorite: boolean
671) {
672  let percentage =
673    (tm.getRange()!.totalNS - Math.abs(tm.getRange()!.endNS - tm.getRange()!.startNS)) / tm.getRange()!.totalNS;
674  let maxWidth = tm.getBoundingClientRect().width - 268;
675  for (let i = 0; i < nodes.length; i++) {
676    let it = nodes[i];
677    if (it[0].hidden) {
678      continue;
679    }
680    if (isFavorite) {
681      if (!it[0].rowEL.collect && !it[1].rowEL.collect) {
682        continue;
683      }
684    }
685    switch (it[0].lineType) {
686      case LineType.brokenLine:
687        drawBrokenLine(it, maxWidth, context);
688        break;
689      case LineType.bezierCurve:
690        drawBezierCurve(it, maxWidth, context, percentage);
691        break;
692      default:
693        drawBezierCurve(it, maxWidth, context, percentage);
694    }
695  }
696}
697
698function drawBezierCurve(it: PairPoint[], maxWidth: number, context: CanvasRenderingContext2D, percentage: number) {
699  let bezierCurveStart = it[0].x > it[1].x ? it[1] : it[0];
700  let bezierCurveEnd = it[0].x > it[1].x ? it[0] : it[1];
701  if (bezierCurveStart && bezierCurveEnd) {
702    //左移到边界,不画线
703    if (bezierCurveStart.x <= 0) {
704      bezierCurveStart.x = -100;
705    }
706    if (bezierCurveEnd.x <= 0) {
707      bezierCurveEnd.x = -100;
708    }
709    //右移到边界,不画线
710    if (bezierCurveStart.x >= maxWidth) {
711      bezierCurveStart.x = maxWidth + 100;
712    }
713    if (bezierCurveEnd.x >= maxWidth) {
714      bezierCurveEnd.x = maxWidth + 100;
715    }
716    context.beginPath();
717    context.lineWidth = 2;
718    context.fillStyle = linkLineColor;
719    context.strokeStyle = linkLineColor;
720    let x0;
721    let y0;
722    let x1;
723    let x2;
724    let y1;
725    let y2;
726    let x3;
727    let y3;
728    x0 = bezierCurveStart.x ?? 0;
729    y0 = bezierCurveStart.y ?? 0;
730    x3 = bezierCurveEnd.x ?? 0;
731    y3 = bezierCurveEnd.y ?? 0;
732    if (bezierCurveEnd.isRight) {
733      x2 = x3 - 100 * percentage;
734    } else {
735      x2 = x3 + 100 * percentage;
736    }
737    y2 = y3 - 40 * percentage;
738    if (bezierCurveStart.isRight) {
739      x1 = x0 - 100 * percentage;
740    } else {
741      x1 = x0 + 100 * percentage;
742    }
743    y1 = y0 + 40 * percentage;
744    //向右箭头终点在x轴正向有偏移
745    if (!bezierCurveStart.isRight) {
746      x0 -= 5;
747    }
748    context.moveTo(x0, y0);
749    //箭头向左还是向右
750    if (bezierCurveStart.isRight) {
751      context.lineTo(x0 - wid, y0 + wid);
752      context.moveTo(x0, y0);
753      context.lineTo(x0 - wid, y0 - wid);
754    } else {
755      context.lineTo(x0 + wid, y0 + wid);
756      context.moveTo(x0, y0);
757      context.lineTo(x0 + wid, y0 - wid);
758    }
759    context.moveTo(x0, y0);
760    context.bezierCurveTo(x1, y1, x2, y2, x3, y3);
761    context.moveTo(x3, y3);
762    //箭头向左还是向右
763    if (bezierCurveEnd.isRight) {
764      context.lineTo(x3 - wid, y3 + wid);
765      context.moveTo(x3, y3);
766      context.lineTo(x3 - wid, y3 - wid);
767    } else {
768      context.lineTo(x3 + wid, y3 + wid);
769      context.moveTo(x3, y3);
770      context.lineTo(x3 + wid, y3 - wid);
771    }
772    context.moveTo(x3, y3);
773    context.stroke();
774    context.closePath();
775  }
776}
777
778function drawBrokenLine(it: PairPoint[], maxWidth: number, context: CanvasRenderingContext2D) {
779  let brokenLineStart = it[0].x > it[1].x ? it[1] : it[0];
780  let brokenLineEnd = it[0].x > it[1].x ? it[0] : it[1];
781  if (brokenLineStart && brokenLineEnd) {
782    if (brokenLineStart.x <= 0) {
783      brokenLineStart.x = -100;
784    }
785    if (brokenLineEnd.x <= 0) {
786      brokenLineEnd.x = -100;
787    }
788    //右移到边界,不画线
789    if (brokenLineStart.x >= maxWidth) {
790      brokenLineStart.x = maxWidth + 100;
791    }
792    if (brokenLineEnd.x >= maxWidth) {
793      brokenLineEnd.x = maxWidth + 100;
794    }
795    context.beginPath();
796    context.lineWidth = 2;
797    context.fillStyle = '#46B1E3';
798    context.strokeStyle = '#46B1E3';
799    let x0;
800    let y0;
801    let x1;
802    let y1;
803    let x2;
804    let y2;
805    x0 = brokenLineStart.x ?? 0;
806    y0 = brokenLineStart.y ?? 0;
807    y2 = brokenLineEnd.y ?? 0;
808    x2 = brokenLineEnd.x ?? 0;
809    let leftEndpointX;
810    let leftEndpointY;
811    let rightEndpointX;
812    let rightEndpointY;
813
814    if (brokenLineStart.y < brokenLineEnd.y) {
815      x1 = brokenLineStart.x ?? 0;
816      y1 = brokenLineEnd.y ?? 0;
817      leftEndpointX = x2 - wid;
818      leftEndpointY = y2 - wid;
819      rightEndpointX = x2 - wid;
820      rightEndpointY = y2 + wid;
821    } else {
822      x2 = brokenLineEnd.x - wid ?? 0;
823      x1 = brokenLineEnd.x - wid ?? 0;
824      y1 = brokenLineStart.y ?? 0;
825      leftEndpointX = x2 - wid;
826      leftEndpointY = y2 + wid;
827      rightEndpointX = x2 + wid;
828      rightEndpointY = y2 + wid;
829    }
830    context.moveTo(x0, y0);
831    context.lineTo(x1, y1);
832    context.lineTo(x2, y2);
833    context.stroke();
834    context.closePath();
835    context.beginPath();
836    context.lineWidth = 2;
837    context.fillStyle = '#46B1E3';
838    context.strokeStyle = '#46B1E3';
839    context.moveTo(x2, y2);
840    context.lineTo(leftEndpointX, leftEndpointY);
841    context.lineTo(rightEndpointX, rightEndpointY);
842    context.lineTo(x2, y2);
843    context.fill();
844    context.closePath();
845  }
846}
847
848export function drawLoading(
849  ctx: CanvasRenderingContext2D,
850  startNS: number,
851  endNS: number,
852  totalNS: number,
853  frame: any,
854  left: number,
855  right: number
856) {}
857
858export function drawString(ctx: CanvasRenderingContext2D, str: string, textPadding: number, frame: Rect, data: any) {
859  if (data.textMetricsWidth === undefined) {
860    data.textMetricsWidth = ctx.measureText(str).width;
861  }
862  let charWidth = Math.round(data.textMetricsWidth / str.length);
863  let fillTextWidth = frame.width - textPadding * 2;
864  if (data.textMetricsWidth < fillTextWidth) {
865    let x2 = Math.floor(frame.width / 2 - data.textMetricsWidth / 2 + frame.x + textPadding);
866    ctx.fillText(str, x2, Math.floor(frame.y + frame.height / 2), fillTextWidth);
867  } else {
868    if (fillTextWidth >= charWidth) {
869      let chatNum = fillTextWidth / charWidth;
870      let x1 = frame.x + textPadding;
871      if (chatNum < 2) {
872        ctx.fillText(str.substring(0, 1), x1, Math.floor(frame.y + frame.height / 2), fillTextWidth);
873      } else {
874        ctx.fillText(str.substring(0, chatNum - 1) + '...', x1, Math.floor(frame.y + frame.height / 2), fillTextWidth);
875      }
876    }
877  }
878}
879
880export function drawString2Line(
881  ctx: CanvasRenderingContext2D,
882  str1: string,
883  str2: string,
884  textPadding: number,
885  frame: Rect,
886  data: any
887) {
888  if (frame.height < 30) return;
889  if (data.textMetrics1Width === undefined) {
890    data.textMetrics1Width = ctx.measureText(str1).width;
891    data.textMetrics2Width = ctx.measureText(str2).width;
892  }
893  let charWidth = Math.round(data.textMetrics1Width / str1.length);
894  let fillTextWidth = frame.width - textPadding * 2;
895  let y1 = frame.y + 12;
896  let y2 = y1 + 14;
897  if (data.textMetrics1Width < fillTextWidth) {
898    let x = Math.floor(frame.width / 2 - data.textMetrics1Width / 2 + frame.x + textPadding - 1);
899    ctx.fillText(str1, x, y1, fillTextWidth);
900  } else {
901    if (fillTextWidth >= charWidth) {
902      let chatNum = fillTextWidth / charWidth;
903      let x = frame.x + textPadding - 1;
904      if (chatNum < 2) {
905        ctx.fillText(str1.substring(0, 1), x, y1, fillTextWidth);
906      } else {
907        ctx.fillText(str1.substring(0, chatNum - 1) + '...', x, y1, fillTextWidth);
908      }
909    }
910  }
911  if (data.textMetrics2Width < fillTextWidth) {
912    let x = Math.floor(frame.width / 2 - data.textMetrics2Width / 2 + frame.x + textPadding - 1);
913    ctx.fillText(str2, x, y2, fillTextWidth);
914  } else {
915    if (fillTextWidth >= charWidth) {
916      let chatNum = fillTextWidth / charWidth;
917      let x = frame.x + textPadding - 1;
918      if (chatNum < 2) {
919        ctx.fillText(str2.substring(0, 1), x, y2, fillTextWidth);
920      } else {
921        ctx.fillText(str2.substring(0, chatNum - 1) + '...', x, y2, fillTextWidth);
922      }
923    }
924  }
925}
926
927export function hiPerf(
928  arr: Array<any>,
929  arr2: Array<any>,
930  res: Array<any>,
931  startNS: number,
932  endNS: number,
933  frame: any,
934  groupBy10MS: boolean,
935  use: boolean
936) {
937  if (use && res.length > 0) {
938    let pns = (endNS - startNS) / frame.width;
939    let y = frame.y;
940    for (let i = 0; i < res.length; i++) {
941      let it = res[i];
942      if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) {
943        if (!it.frame) {
944          it.frame = {};
945          it.frame.y = y;
946        }
947        it.frame.height = it.height;
948        HiPerfStruct.setFrame(it, pns, startNS, endNS, frame);
949      } else {
950        it.frame = null;
951      }
952    }
953    return;
954  }
955  res.length = 0;
956  if (arr) {
957    let list = groupBy10MS ? arr2 : arr;
958    let pns = (endNS - startNS) / frame.width;
959    let y = frame.y;
960    for (let i = 0, len = list.length; i < len; i++) {
961      let it = list[i];
962      if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) {
963        if (!list[i].frame) {
964          list[i].frame = {};
965          list[i].frame.y = y;
966        }
967        list[i].frame.height = it.height;
968        HiPerfStruct.setFrame(list[i], pns, startNS, endNS, frame);
969        if (groupBy10MS) {
970          if (
971            i > 0 &&
972            (list[i - 1].frame?.x || 0) == (list[i].frame?.x || 0) &&
973            (list[i - 1].frame?.width || 0) == (list[i].frame?.width || 0) &&
974            (list[i - 1].frame?.height || 0) == (list[i].frame?.height || 0)
975          ) {
976          } else {
977            res.push(list[i]);
978          }
979        } else {
980          if (i > 0 && Math.abs((list[i - 1].frame?.x || 0) - (list[i].frame?.x || 0)) < 4) {
981          } else {
982            res.push(list[i]);
983          }
984        }
985      }
986    }
987  }
988}
989
990export class HiPerfStruct extends BaseStruct {
991  static hoverStruct: HiPerfStruct | undefined;
992  static selectStruct: HiPerfStruct | undefined;
993  id: number | undefined;
994  callchain_id: number | undefined;
995  timestamp: number | undefined;
996  thread_id: number | undefined;
997  event_count: number | undefined;
998  event_type_id: number | undefined;
999  cpu_id: number | undefined;
1000  thread_state: string | undefined;
1001  startNS: number | undefined;
1002  endNS: number | undefined;
1003  dur: number | undefined;
1004  height: number | undefined;
1005
1006  static drawRoundRectPath(cxt: Path2D, x: number, y: number, width: number, height: number, radius: number) {
1007    cxt.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2);
1008    cxt.lineTo(x + radius, y + height);
1009    cxt.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI);
1010    cxt.lineTo(x + 0, y + radius);
1011    cxt.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
1012    cxt.lineTo(x + width - radius, y + 0);
1013    cxt.arc(x + width - radius, y + radius, radius, (Math.PI * 3) / 2, Math.PI * 2);
1014    cxt.lineTo(x + width, y + height - radius);
1015    cxt.moveTo(x + width / 3, y + height / 5);
1016    cxt.lineTo(x + width / 3, y + (height / 5) * 4);
1017    cxt.moveTo(x + width / 3, y + height / 5);
1018    cxt.bezierCurveTo(
1019      x + width / 3 + 7,
1020      y + height / 5 - 2,
1021      x + width / 3 + 7,
1022      y + height / 5 + 6,
1023      x + width / 3,
1024      y + height / 5 + 4
1025    );
1026  }
1027
1028  static draw(ctx: CanvasRenderingContext2D, normalPath: Path2D, specPath: Path2D, data: any, groupBy10MS: boolean) {
1029    if (data.frame) {
1030      if (groupBy10MS) {
1031        let width = data.frame.width;
1032        normalPath.rect(data.frame.x, 40 - (data.height || 0), width, data.height || 0);
1033      } else {
1034        let path = data.callchain_id === -1 ? specPath : normalPath;
1035        path.moveTo(data.frame.x + 7, 20);
1036        HiPerfStruct.drawRoundRectPath(path, data.frame.x - 7, 20 - 7, 14, 14, 3);
1037        path.moveTo(data.frame.x, 27);
1038        path.lineTo(data.frame.x, 33);
1039      }
1040    }
1041  }
1042
1043  static drawSpecialPath(ctx: CanvasRenderingContext2D, specPath: Path2D) {
1044    ctx.strokeStyle = '#9fafc4';
1045    ctx.globalAlpha = 0.5;
1046    ctx.stroke(specPath);
1047    ctx.globalAlpha = 1;
1048  }
1049
1050  static setFrame(node: any, pns: number, startNS: number, endNS: number, frame: any) {
1051    if ((node.startNS || 0) < startNS) {
1052      node.frame.x = 0;
1053    } else {
1054      node.frame.x = Math.floor(((node.startNS || 0) - startNS) / pns);
1055    }
1056    if ((node.startNS || 0) + (node.dur || 0) > endNS) {
1057      node.frame.width = frame.width - node.frame.x;
1058    } else {
1059      node.frame.width = Math.ceil(((node.startNS || 0) + (node.dur || 0) - startNS) / pns - node.frame.x);
1060    }
1061    if (node.frame.width < 1) {
1062      node.frame.width = 1;
1063    }
1064  }
1065
1066  static groupBy10MS(groupArray: Array<any>, intervalPerf: number, maxCpu?: number | undefined): Array<any> {
1067    let obj = groupArray
1068      .map((it) => {
1069        it.timestamp_group = Math.trunc(it.startNS / 1_000_000_0) * 1_000_000_0;
1070        return it;
1071      })
1072      .reduce((pre, current) => {
1073        (pre[current['timestamp_group']] = pre[current['timestamp_group']] || []).push(current);
1074        return pre;
1075      }, {});
1076    let arr = [];
1077    for (let aKey in obj) {
1078      let ns = parseInt(aKey);
1079      let height: number = 0;
1080      if (maxCpu != undefined) {
1081        height = Math.floor((obj[aKey].length / (10 / intervalPerf) / maxCpu) * 40);
1082      } else {
1083        height = Math.floor((obj[aKey].length / (10 / intervalPerf)) * 40);
1084      }
1085      arr.push({
1086        startNS: ns,
1087        dur: 1_000_000_0,
1088        height: height,
1089      });
1090    }
1091    return arr;
1092  }
1093}
1094
1095function setMemFrame(node: any, padding: number, startNS: number, endNS: number, totalNS: number, frame: any) {
1096  let x1: number;
1097  let x2: number;
1098  if ((node.startTime || 0) <= startNS) {
1099    x1 = 0;
1100  } else {
1101    x1 = ns2x(node.startTime || 0, startNS, endNS, totalNS, frame);
1102  }
1103  if ((node.startTime || 0) + (node.duration || 0) >= endNS) {
1104    x2 = frame.width;
1105  } else {
1106    x2 = ns2x((node.startTime || 0) + (node.duration || 0), startNS, endNS, totalNS, frame);
1107  }
1108  let getV: number = x2 - x1 <= 1 ? 1 : x2 - x1;
1109  if (!node.frame) {
1110    node.frame = {};
1111  }
1112  node.frame.x = Math.floor(x1);
1113  node.frame.y = Math.floor(frame.y + padding);
1114  node.frame.width = Math.ceil(getV);
1115  node.frame.height = Math.floor(frame.height - padding * 2);
1116}
1117
1118export function mem(
1119  list: Array<any>,
1120  memFilter: Array<any>,
1121  startNS: number,
1122  endNS: number,
1123  totalNS: number,
1124  frame: any,
1125  use: boolean
1126) {
1127  if (use && memFilter.length > 0) {
1128    for (let i = 0, len = memFilter.length; i < len; i++) {
1129      if (
1130        (memFilter[i].startTime || 0) + (memFilter[i].duration || 0) > startNS &&
1131        (memFilter[i].startTime || 0) < endNS
1132      ) {
1133        setMemFrame(memFilter[i], 5, startNS, endNS, totalNS, frame);
1134      } else {
1135        memFilter[i].frame = null;
1136      }
1137    }
1138    return;
1139  }
1140  memFilter.length = 0;
1141  if (list) {
1142    for (let i = 0, len = list.length; i < len; i++) {
1143      let it = list[i];
1144      if ((it.startTime || 0) + (it.duration || 0) > startNS && (it.startTime || 0) < endNS) {
1145        setMemFrame(list[i], 5, startNS, endNS, totalNS, frame);
1146        if (
1147          i > 0 &&
1148          (list[i - 1].frame?.x || 0) == (list[i].frame?.x || 0) &&
1149          (list[i - 1].frame?.width || 0) == (list[i].frame?.width || 0)
1150        ) {
1151        } else {
1152          memFilter.push(list[i]);
1153        }
1154      }
1155    }
1156  }
1157}
1158
1159export function drawWakeUpList(
1160  wakeUpListContext: CanvasRenderingContext2D | any,
1161  wake: WakeupBean | undefined | null,
1162  startNS: number,
1163  endNS: number,
1164  totalNS: number,
1165  frame: Rect,
1166  wakeup: WakeupBean | undefined = undefined,
1167  currentCpu: number | undefined | null = undefined,
1168  noVerticalLine = false,
1169) {
1170  if (wake) {
1171    let x1 = Math.floor(
1172      ns2x(wake.wakeupTime || 0, startNS, endNS, totalNS, frame)
1173    );
1174    wakeUpListContext.beginPath();
1175    wakeUpListContext.lineWidth = 2;
1176    wakeUpListContext.fillStyle = '#000000';
1177    if (x1 > 0 && x1 < frame.x + frame.width) {
1178      if (!noVerticalLine) {
1179        wakeUpListContext.moveTo(x1, frame.y);
1180        wakeUpListContext.lineTo(x1, frame.y + frame.height);
1181      }
1182      if (currentCpu == wake.cpu) {
1183        let centerY = Math.floor(frame.y + frame.height / 2);
1184        wakeUpListContext.moveTo(x1, centerY - 6);
1185        wakeUpListContext.lineTo(x1 + 4, centerY);
1186        wakeUpListContext.lineTo(x1, centerY + 6);
1187        wakeUpListContext.lineTo(x1 - 4, centerY);
1188        wakeUpListContext.lineTo(x1, centerY - 6);
1189        wakeUpListContext.fill();
1190      }
1191    }
1192    if (wakeup) {
1193      let x2 = Math.floor(
1194        ns2x(
1195          wakeup.ts || 0,
1196          startNS,
1197          endNS,
1198          totalNS,
1199          frame
1200        )
1201      );
1202      let y = frame.y + frame.height - 10;
1203      wakeUpListContext.moveTo(x1, y);
1204      wakeUpListContext.lineTo(x2, y);
1205      wakeUpListContext.moveTo(x2, y - 25);
1206      wakeUpListContext.lineTo(x2, y + 5);
1207
1208      let s = ns2s(
1209        (wakeup.ts || 0) - (wake.wakeupTime || 0)
1210      );
1211      let wakeUpListDistance = x2 - x1;
1212      if (wakeUpListDistance > 12) {
1213        wakeUpListContext.moveTo(x1, y);
1214        wakeUpListContext.lineTo(x1 + 6, y - 3);
1215        wakeUpListContext.moveTo(x1, y);
1216        wakeUpListContext.lineTo(x1 + 6, y + 3);
1217        wakeUpListContext.moveTo(x2, y);
1218        wakeUpListContext.lineTo(x2 - 6, y - 3);
1219        wakeUpListContext.moveTo(x2, y);
1220        wakeUpListContext.lineTo(x2 - 6, y + 3);
1221        let measure = wakeUpListContext.measureText(s);
1222        let tHeight =
1223          measure.actualBoundingBoxAscent +
1224          measure.actualBoundingBoxDescent;
1225        let xStart = x1 + Math.floor(wakeUpListDistance / 2 - measure.width / 2);
1226        if (wakeUpListDistance > measure.width + 4) {
1227          wakeUpListContext.fillStyle = '#ffffff';
1228          wakeUpListContext.fillRect(
1229            xStart - 2,
1230            y - 4 - tHeight,
1231            measure.width + 4,
1232            tHeight + 4
1233          );
1234          wakeUpListContext.font = '10px solid';
1235          wakeUpListContext.fillStyle = '#000000';
1236          wakeUpListContext.textBaseline = 'bottom';
1237          wakeUpListContext.fillText(s, xStart, y - 2);
1238        }
1239      }
1240    }
1241    wakeUpListContext.strokeStyle = '#000000';
1242    wakeUpListContext.stroke();
1243    wakeUpListContext.closePath();
1244  }
1245}
1246