• 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 './cpu/ProcedureWorkerCPU';
17import { RangeSelectStruct, TraceRow } from '../../component/trace/base/TraceRow';
18import { TimerShaftElement } from '../../component/trace/TimerShaftElement';
19import { Flag } from '../../component/trace/timer-shaft/Flag';
20import { drawVSync } from '../../component/chart/VSync';
21import { FuncStruct } from './ProcedureWorkerFunc';
22import { ProcessMemStruct } from './ProcedureWorkerMem';
23import { ThreadStruct } from '../../database/ui-worker/ProcedureWorkerThread';
24import { Utils } from '../../component/trace/base/Utils';
25
26export abstract class Render {
27  abstract renderMainThread(req: unknown, row: unknown): void;
28}
29
30export abstract class PerfRender {
31  abstract render(req: RequestMessage, list: Array<unknown>, filter: Array<unknown>, dataList2: Array<unknown>): void;
32}
33
34export class RequestMessage {
35  type: string | undefined | null;
36  lazyRefresh: boolean | undefined;
37  intervalPerf: unknown;
38  canvas: unknown;
39  context!: CanvasRenderingContext2D;
40  params: unknown;
41  online: unknown;
42  buf: unknown;
43  isRangeSelect!: boolean;
44  isHover!: boolean;
45  xs?: Array<number>;
46  frame!: Rect;
47  flagMoveInfo?: Flag;
48  flagSelectedInfo?: Flag;
49  hoverX: unknown;
50  hoverY: unknown;
51  startNS!: number;
52  endNS!: number;
53  totalNS!: number;
54  slicesTime:
55    | {
56      startTime: number | null;
57      endTime: number | null;
58      color: string | null;
59    }
60    | undefined;
61  range: unknown;
62  scale: unknown;
63  chartColor: unknown;
64  canvasWidth: unknown;
65  canvasHeight: unknown;
66  useCache: unknown;
67  lineColor!: string;
68  wakeupBean: WakeupBean | undefined | null;
69  id: unknown;
70  postMessage:
71    | {
72      (message: unknown, targetOrigin: string, transfer?: Transferable[]): void;
73      (message: unknown, options?: WindowPostMessageOptions): void;
74    }
75    | undefined;
76}
77
78export function ns2s(ns: number): string {
79  let second1 = 1_000_000_000; // 1 second
80  let millisecond = 1_000_000; // 1 millisecond
81  let microsecond = 1_000; // 1 microsecond
82  let res;
83  if (ns >= second1) {
84    res = `${(ns / 1000 / 1000 / 1000).toFixed(1)} s`;
85  } else if (ns >= millisecond) {
86    res = `${(ns / 1000 / 1000).toFixed(1)} ms`;
87  } else if (ns >= microsecond) {
88    res = `${(ns / 1000).toFixed(1)} μs`;
89  } else if (ns > 0) {
90    res = `${ns.toFixed(1)} ns`;
91  } else {
92    res = `${ns.toFixed(0)}`;
93  }
94  return res;
95}
96
97export function ns2Timestamp(ns: number): string {
98  let hour = Math.floor(ns / 3600000000000);
99  let minute = Math.floor((ns % 3600000000000) / 60000000000);
100  let second = Math.floor((ns % 60000000000) / 1000000000);
101  let millisecond = Math.floor((ns % 1000000000) / 1000000);
102  let microsecond = Math.floor((ns % 1000000) / 1000);
103  let nanosecond = ns % 1000;
104  return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second
105    .toString()
106    .padStart(2, '0')}:${millisecond.toString().padStart(3, '0')}:${microsecond
107      .toString()
108      .padStart(3, '0')}:${nanosecond.toString().padStart(3, '0')}`;
109}
110
111const offsetX = 5;
112
113export function isFrameContainPoint(
114  frame: Rect,
115  x: number,
116  y: number,
117  strict: boolean = true,
118  offset: boolean = false
119): boolean {
120  if (strict) {
121    if (offset) {
122      return (
123        x >= frame.x - offsetX && x <= frame.x + frame.width + offsetX && y >= frame.y && y <= frame.y + frame.height
124      );
125    } else {
126      return x >= frame.x && x <= frame.x + frame.width && y >= frame.y && y <= frame.y + frame.height;
127    }
128  } else {
129    if (offset) {
130      return x >= frame.x - offsetX && x <= frame.x + frame.width + offsetX;
131    } else {
132      return x >= frame.x && x <= frame.x + frame.width;
133    }
134  }
135}
136
137export const isSurroundingPoint = function (pointX: number, currentRect: Rect, unitPointXRange: number): boolean {
138  return pointX >= currentRect?.x - unitPointXRange && pointX <= currentRect?.x + unitPointXRange;
139};
140
141export const computeUnitWidth = function (
142  preTs: number,
143  currentTs: number,
144  frameWidth: number,
145  selectUnitWidth: number
146): number {
147  let max = 150;
148  let unitWidth = ((currentTs - preTs) * frameWidth) / (TraceRow.range!.endNS - TraceRow.range!.startNS);
149  if (unitWidth < selectUnitWidth) {
150    return unitWidth > max || unitWidth === 0 ? max : unitWidth;
151  }
152  return selectUnitWidth > max || selectUnitWidth === 0 ? max : selectUnitWidth;
153};
154
155class FilterConfig {
156  startNS: number = 0;
157  endNS: number = 0;
158  totalNS: number = 0;
159  frame: Rect = new Rect(0, 0, 0, 0);
160  useCache: boolean = false;
161  startKey: string = 'startNS';
162  durKey: string = 'dur';
163  paddingTop: number = 0;
164}
165
166interface CommonStruct {
167  frame: Rect;
168}
169
170export function fillCacheData(filterList: Array<unknown>, condition: FilterConfig): boolean {
171  if (condition.useCache && filterList.length > 0) {
172    let pns = (condition.endNS - condition.startNS) / condition.frame.width;
173    let y = condition.frame.y + condition.paddingTop;
174    let height = condition.frame.height - condition.paddingTop * 2;
175    for (let i = 0, len = filterList.length; i < len; i++) {
176      let it = filterList[i] as BaseStruct;
177      if (
178        //@ts-ignore
179        (it[condition.startKey] || 0) + (it[condition.durKey] || 0) > condition.startNS &&
180        //@ts-ignore
181        (it[condition.startKey] || 0) < condition.endNS
182      ) {
183        if (!it.frame) {
184          it.frame = new Rect(0, 0, 0, 0);
185          it.frame.y = y;
186          it.frame.height = height;
187        }
188        setNodeFrame(
189          it,
190          pns,
191          condition.startNS,
192          condition.endNS,
193          condition.frame,
194          condition.startKey,
195          condition.durKey
196        );
197      } else {
198        it.frame = undefined;
199      }
200    }
201    return true;
202  }
203  return false;
204}
205
206export function fillCacheDataIdx(filterData: Array<unknown>, slice: number[], condition: FilterConfig): boolean {
207  if (condition.useCache && filterData.length > 0) {
208    let pns = (condition.endNS - condition.startNS) / condition.frame.width;
209    let y = condition.frame.y + condition.paddingTop;
210    let height = condition.frame.height - condition.paddingTop * 2;
211    for (let i = slice[0]; i <= slice[1]; i++) {
212      let it = filterData[i] as BaseStruct;
213      if (!it) {
214        continue;
215      }
216      if (
217        //@ts-ignore
218        (it[condition.startKey] || 0) + (it[condition.durKey] || 0) > condition.startNS &&
219        //@ts-ignore
220        (it[condition.startKey] || 0) < condition.endNS
221      ) {
222        if (!it.frame) {
223          it.frame = new Rect(0, 0, 0, 0);
224          it.frame.y = y;
225          it.frame.height = height;
226        }
227        setNodeFrame(
228          it,
229          pns,
230          condition.startNS,
231          condition.endNS,
232          condition.frame,
233          condition.startKey,
234          condition.durKey
235        );
236      } else {
237        it.frame = undefined;
238      }
239    }
240    return true;
241  }
242  return false;
243}
244
245export function bsearch(haystack: ArrayLike<unknown>, needle: FilterConfig): number {
246  return searchImpl(haystack, needle, 0, haystack.length);
247}
248
249function searchImpl(stack: ArrayLike<unknown>, cfg: FilterConfig, i: number, j: number): number {
250  if (i === j) {
251    return -1;
252  }
253  if (i + 1 === j) {
254    //@ts-ignore
255    return cfg.endNS >= stack[i][cfg.startKey] ? i : -1;
256  }
257  const middle = Math.floor((j - i) / 2) + i;
258  //@ts-ignore
259  const middleValue = stack[middle][cfg.startKey];
260  if (cfg.endNS < middleValue) {
261    return searchImpl(stack, cfg, i, middle);
262  } else {
263    return searchImpl(stack, cfg, middle, j);
264  }
265}
266
267export function findRangeIdx(fullData: Array<unknown>, condition: FilterConfig): number[] {
268  //@ts-ignore
269  let a = fullData.findIndex((it) => it[condition.startKey] + it[condition.durKey] >= condition.startNS);
270  let b = bsearch(fullData, condition);
271  return [a, b + 1];
272}
273
274export function findRange(fullData: Array<unknown>, condition: FilterConfig): Array<unknown> {
275  let left = 0;
276  let right = 0;
277  for (let i = 0, j = fullData.length - 1, ib = true, jb = true; i < fullData.length, j >= 0; i++, j--) {
278    //@ts-ignore
279    if (fullData[j][condition.startKey] <= condition.endNS && jb) {
280      right = j;
281      jb = false;
282    }
283    //@ts-ignore
284    if (fullData[i][condition.startKey] + fullData[i][condition.durKey] >= condition.startNS && ib) {
285      left = i;
286      ib = false;
287    }
288    if (!ib && !jb) {
289      break;
290    }
291  }
292  return fullData.slice(left, right + 1);
293}
294
295export const dataFilterHandler = (
296  fullData: Array<BaseStruct>,
297  filterData: Array<BaseStruct>,
298  condition: FilterConfig
299): void => {
300  if (fillCacheData(filterData, condition)) {
301    return;
302  }
303  if (fullData && fullData.length > 0) {
304    filterData.length = 0;
305    let pns = (condition.endNS - condition.startNS) / condition.frame.width; //每个像素多少ns
306    let y = condition.frame.y + condition.paddingTop;
307    let height = condition.frame.height - condition.paddingTop * 2;
308    let slice = findRange(fullData, condition);
309    for (let i = 0; i < slice.length; i++) {
310      const item = slice[i] as BaseStruct;
311      if (!item.frame) {
312        item.frame = new Rect(0, 0, 0, 0);
313        item.frame.y = y;
314        item.frame.height = height;
315      }
316      //@ts-ignore
317      if (item[condition.durKey] === undefined || item[condition.durKey] === null) {
318        if (i === slice.length - 1) {
319          //@ts-ignore
320          item[condition.durKey] = (condition.endNS || 0) - (item[condition.startKey] || 0);
321        } else {
322          //@ts-ignore
323          item[condition.durKey] = (slice[i + 1][condition.startKey] || 0) - (item[condition.startKey] || 0);
324        }
325      }
326      setSliceFrame(slice, condition, pns, i);
327    }
328    //@ts-ignore
329    filterData.push(...slice.filter((it) => it.v));
330  }
331};
332
333function setSliceFrame(slice: Array<unknown>, condition: FilterConfig, pns: number, i: number): void {
334  let sum = 0;
335  //@ts-ignore
336  if (slice[i][condition.durKey] >= pns || slice.length < 100) {
337    //@ts-ignore
338    slice[i].v = true;
339    setNodeFrame(
340      slice[i],
341      pns,
342      condition.startNS,
343      condition.endNS,
344      condition.frame,
345      condition.startKey,
346      condition.durKey
347    );
348  } else {
349    if (i > 0) {
350      //@ts-ignore
351      let c = slice[i][condition.startKey] - slice[i - 1][condition.startKey] - slice[i - 1][condition.durKey];
352      if (c < pns && sum < pns) {
353        //@ts-ignore
354        sum += c + slice[i - 1][condition.durKey];
355        //@ts-ignore
356        slice[i].v = false;
357      } else {
358        //@ts-ignore
359        slice[i].v = true;
360        setNodeFrame(
361          slice[i],
362          pns,
363          condition.startNS,
364          condition.endNS,
365          condition.frame,
366          condition.startKey,
367          condition.durKey
368        );
369        sum = 0;
370      }
371    }
372  }
373}
374
375function setNodeFrame(
376  nodeItem: unknown,
377  pns: number,
378  startNS: number,
379  endNS: number,
380  frame: Rect,
381  startKey: string,
382  durKey: string
383): void {
384  const node = nodeItem as BaseStruct;
385  if (!node.frame) {
386    return;
387  }
388  //@ts-ignore
389  const start = node[startKey] as number;
390  //@ts-ignore
391  const dur = node[durKey] as number;
392  if ((start || 0) < startNS) {
393    node.frame.x = 0;
394  } else {
395    node.frame.x = Math.floor(((start || 0) - startNS) / pns);
396  }
397  if ((start || 0) + (dur || 0) > endNS) {
398    node.frame.width = frame.width - node.frame.x;
399  } else {
400    node.frame.width = Math.ceil(((start || 0) + (dur || 0) - startNS) / pns - node.frame.x);
401  }
402  if (node.frame.width < 1) {
403    node.frame.width = 1;
404  }
405}
406
407export function ns2x(ns: number, startNS: number, endNS: number, duration: number, rect: Rect): number {
408  if (endNS === 0) {
409    endNS = duration;
410  }
411  let xSize: number = ((ns - startNS) * rect.width) / (endNS - startNS);
412  if (xSize < 0) {
413    xSize = 0;
414  } else if (xSize > rect.width) {
415    xSize = rect.width;
416  }
417  return xSize;
418}
419
420export function nsx(ns: number, width: number): number {
421  let startNS = TraceRow.range?.startNS || 0;
422  let endNS = TraceRow.range?.endNS || 0;
423  let duration = TraceRow.range?.totalNS || 0;
424  if (endNS === 0) {
425    endNS = duration;
426  }
427  let xSize: number = ((ns - startNS) * width) / (endNS - startNS);
428  if (xSize < 0) {
429    xSize = 0;
430  } else if (xSize > width) {
431    xSize = width;
432  }
433  return xSize;
434}
435
436export function ns2xByTimeShaft(ns: number, tse: TimerShaftElement): number {
437  let startNS = tse.getRange()!.startNS;
438  let endNS = tse.getRange()!.endNS;
439  let duration = tse.getRange()!.totalNS;
440  if (endNS === 0) {
441    endNS = duration;
442  }
443  let width = tse.getBoundingClientRect().width - 258;
444  let xSize: number = ((ns - startNS) * width) / (endNS - startNS);
445  if (xSize < 0) {
446    xSize = 0;
447  } else if (xSize > width) {
448    xSize = width;
449  }
450  return xSize;
451}
452
453export class Rect {
454  x: number = 0;
455  y: number = 0;
456  width: number = 0;
457  height: number = 0;
458
459  constructor(x: number, y: number, width: number, height: number) {
460    this.x = x;
461    this.y = y;
462    this.width = width;
463    this.height = height;
464  }
465
466  static intersect(r1: Rect, rect: Rect): boolean {
467    let minX = r1.x <= rect.x ? r1.x : rect.x;
468    let minY = r1.y <= rect.y ? r1.y : rect.y;
469    let maxX = r1.x + r1.width >= rect.x + rect.width ? r1.x + r1.width : rect.x + rect.width;
470    let maxY = r1.y + r1.height >= rect.y + rect.height ? r1.y + r1.height : rect.y + rect.height;
471    return maxX - minX <= rect.width + r1.width && maxY - minY <= r1.height + rect.height;
472  }
473
474  static contains(rect: Rect, x: number, y: number): boolean {
475    return rect.x <= x && x <= rect.x + rect.width && rect.y <= y && y <= rect.y + rect.height;
476  }
477
478  static containsWithMargin(rect: Rect, x: number, y: number, t: number, r: number, b: number, l: number): boolean {
479    return rect.x - l <= x && x <= rect.x + rect.width + r && rect.y - t <= y && y <= rect.y + rect.height + b;
480  }
481
482  static containsWithPadding(
483    rect: Rect,
484    x: number,
485    y: number,
486    paddingLeftRight: number,
487    paddingTopBottom: number
488  ): boolean {
489    return (
490      rect.x + paddingLeftRight <= x &&
491      rect.y + paddingTopBottom <= y &&
492      x <= rect.x + rect.width - paddingLeftRight &&
493      y <= rect.y + rect.height - paddingTopBottom
494    );
495  }
496
497  /**
498   * 判断是否相交
499   * @param rect
500   */
501  intersect(rect: Rect): boolean {
502    let minX = this.x <= rect.x ? this.x : rect.x;
503    let minY = this.y <= rect.y ? this.y : rect.y;
504    let maxX = this.x + this.width >= rect.x + rect.width ? this.x + this.width : rect.x + rect.width;
505    let maxY = this.y + this.height >= rect.y + rect.height ? this.y + this.height : rect.y + rect.height;
506    return maxX - minX <= rect.width + this.width && maxY - minY <= this.height + rect.height;
507  }
508
509  contains(x: number, y: number): boolean {
510    return this.x <= x && x <= this.x + this.width && this.y <= y && y <= this.y + this.height;
511  }
512
513  containsWithMargin(x: number, y: number, t: number, r: number, b: number, l: number): boolean {
514    return this.x - l <= x && x <= this.x + this.width + r && this.y - t <= y && y <= this.y + this.height + b;
515  }
516
517  containsWithPadding(x: number, y: number, paddingLeftRight: number, paddingTopBottom: number): boolean {
518    return (
519      this.x + paddingLeftRight <= x &&
520      x <= this.x + this.width - paddingLeftRight &&
521      this.y + paddingTopBottom <= y &&
522      y <= this.y + this.height - paddingTopBottom
523    );
524  }
525}
526
527export class Point {
528  x: number = 0;
529  y: number = 0;
530  isRight: boolean = true;
531
532  constructor(x: number, y: number, isRight: boolean = true) {
533    this.x = x;
534    this.y = y;
535    this.isRight = isRight;
536  }
537}
538
539export enum LineType {
540  brokenLine,
541  bezierCurve,
542  straightLine,
543}
544
545export class PairPoint {
546  x: number = 0;
547  ns: number = 0;
548  y: number = 0;
549  offsetY: number = 0;
550  rowEL: TraceRow<BaseStruct>;
551  isRight: boolean = true;
552  lineType?: LineType;
553  lineColor?: string;
554  business: string = '';
555  hidden?: boolean = false;
556  backrowEL?: TraceRow<BaseStruct>;
557  rangeTime?: string;
558
559  constructor(
560    rowEL: TraceRow<BaseStruct>,
561    x: number,
562    y: number,
563    ns: number,
564    offsetY: number,
565    isRight: boolean,
566    business: string
567  ) {
568    this.rowEL = rowEL;
569    this.x = x;
570    this.y = y;
571    this.ns = ns;
572    this.offsetY = offsetY;
573    this.isRight = isRight;
574    this.business = business;
575  }
576}
577
578export class BaseStruct {
579  translateY: number | undefined;
580  frame: Rect | undefined;
581  isHover: boolean = false;
582}
583
584export function drawLines(ctx: CanvasRenderingContext2D, xs: Array<number>, height: number, lineColor: string): void {
585  if (ctx) {
586    ctx.beginPath();
587    ctx.lineWidth = 1;
588    ctx.strokeStyle = lineColor || '#dadada';
589    xs?.forEach((it) => {
590      ctx.moveTo(Math.floor(it), 0);
591      ctx.lineTo(Math.floor(it), height);
592    });
593    ctx.stroke();
594    ctx.closePath();
595  }
596}
597
598export function drawFlagLine(
599  commonCtx: CanvasRenderingContext2D,
600  hoverFlag: Flag,
601  selectFlag: Flag,
602  startNS: number,
603  endNS: number,
604  totalNS: number,
605  frame: Rect,
606  slicesTime:
607    | {
608      startTime: number | null | undefined;
609      endTime: number | null | undefined;
610      color: string | null | undefined;
611    }
612    | undefined
613): void {
614  if (commonCtx) {
615    if (hoverFlag) {
616      setHoverFlag(hoverFlag, commonCtx, frame);
617    }
618    if (selectFlag) {
619      commonCtx.beginPath();
620      commonCtx.lineWidth = 2;
621      commonCtx.strokeStyle = selectFlag?.color || '#dadada';
622      selectFlag.x = ns2x(selectFlag.time, startNS, endNS, totalNS, frame);
623      commonCtx.moveTo(Math.floor(selectFlag.x), 0);
624      commonCtx.lineTo(Math.floor(selectFlag.x), frame.height);
625      commonCtx.stroke();
626      commonCtx.closePath();
627    }
628    if (slicesTime && slicesTime.startTime && slicesTime.endTime) {
629      commonCtx.beginPath();
630      commonCtx.lineWidth = 1;
631      commonCtx.strokeStyle = slicesTime.color || '#dadada';
632      let x1 = ns2x(slicesTime.startTime, startNS, endNS, totalNS, frame);
633      let x2 = ns2x(slicesTime.endTime, startNS, endNS, totalNS, frame);
634      commonCtx.moveTo(Math.floor(x1), 0);
635      commonCtx.lineTo(Math.floor(x1), frame.height);
636      commonCtx.moveTo(Math.floor(x2), 0);
637      commonCtx.lineTo(Math.floor(x2), frame.height);
638      commonCtx.stroke();
639      commonCtx.closePath();
640    }
641  }
642}
643
644export function drawFlagLineSegment(
645  ctx: CanvasRenderingContext2D | null | undefined,
646  hoverFlag: Flag | null | undefined,
647  selectFlag: Flag | null | undefined,
648  frame: Rect,
649  tse: TimerShaftElement
650): void {
651  if (ctx) {
652    setHoverFlag(hoverFlag, ctx, frame);
653    setSelectFlag(selectFlag, ctx, frame);
654    tse.sportRuler!.slicesTimeList.forEach((slicesTime) => {
655      if (slicesTime && slicesTime.startTime && slicesTime.endTime) {
656        ctx.beginPath();
657        ctx.lineWidth = 1;
658        ctx.strokeStyle = slicesTime.color || '#dadada';
659        let x1 = ns2x(
660          slicesTime.startTime,
661          TraceRow.range!.startNS,
662          TraceRow.range!.endNS,
663          TraceRow.range!.totalNS,
664          frame
665        );
666        let x2 = ns2x(
667          slicesTime.endTime,
668          TraceRow.range!.startNS,
669          TraceRow.range!.endNS,
670          TraceRow.range!.totalNS,
671          frame
672        );
673        // 划线逻辑
674        ctx.moveTo(Math.floor(x1), 0);
675        ctx.lineTo(Math.floor(x1), frame.height!); //左边的线
676        ctx.moveTo(Math.floor(x2), 0);
677        ctx.lineTo(Math.floor(x2), frame.height!); // 右边的线
678        ctx.stroke();
679        ctx.closePath();
680      }
681    });
682  }
683}
684
685function setHoverFlag(hoverFlag: Flag | null | undefined, ctx: CanvasRenderingContext2D, frame: Rect): void {
686  if (hoverFlag) {
687    ctx.beginPath();
688    ctx.lineWidth = 2;
689    ctx.strokeStyle = hoverFlag?.color || '#dadada';
690    ctx.moveTo(Math.floor(hoverFlag.x), 0);
691    ctx.lineTo(Math.floor(hoverFlag.x), frame.height);
692    ctx.stroke();
693    ctx.closePath();
694  }
695}
696
697function setSelectFlag(selectFlag: Flag | null | undefined, ctx: CanvasRenderingContext2D, frame: Rect): void {
698  if (selectFlag) {
699    ctx.beginPath();
700    ctx.lineWidth = 2;
701    ctx.strokeStyle = selectFlag?.color || '#dadada';
702    selectFlag.x = ns2x(
703      selectFlag.time,
704      TraceRow.range!.startNS,
705      TraceRow.range!.endNS,
706      TraceRow.range!.totalNS,
707      frame
708    );
709    ctx.moveTo(Math.floor(selectFlag.x), 0);
710    ctx.lineTo(Math.floor(selectFlag.x), frame.height);
711    ctx.stroke();
712    ctx.closePath();
713  }
714}
715
716export function drawLogsLineSegment(
717  ctx: CanvasRenderingContext2D | undefined | null,
718  systemLogFlag: Flag | undefined | null,
719  frame: {
720    x: number;
721    y: number;
722    width: number | undefined;
723    height: number | undefined;
724  },
725  timerShaftEl: TimerShaftElement
726): void {
727  timerShaftEl.sportRuler?.draw();
728  if (systemLogFlag) {
729    if (ctx) {
730      ctx.beginPath();
731      ctx.lineWidth = 2;
732      ctx.strokeStyle = systemLogFlag?.color || '#dadada';
733      ctx.moveTo(Math.floor(systemLogFlag.x), 0);
734      ctx.lineTo(Math.floor(systemLogFlag.x), frame.height || 0);
735      ctx.stroke();
736      ctx.closePath();
737    }
738    if (timerShaftEl.ctx) {
739      let timeText = `| ${ns2Timestamp(systemLogFlag.time)}`;
740      let textPointX = systemLogFlag.x;
741      let textMetrics = timerShaftEl.ctx.measureText(timeText);
742      if (timerShaftEl.ctx.canvas.width - systemLogFlag.x <= textMetrics.width) {
743        textPointX = systemLogFlag.x - textMetrics.width;
744        timeText = `${ns2Timestamp(systemLogFlag.time)} |`;
745      }
746      let locationY = 120;
747      timerShaftEl.ctx.beginPath();
748      timerShaftEl.ctx.lineWidth = 0;
749      timerShaftEl.ctx.fillStyle = '#FFFFFF';
750      let textHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent;
751      timerShaftEl.ctx.fillRect(textPointX, locationY - textHeight, textMetrics.width, textHeight);
752      timerShaftEl.ctx.lineWidth = 2;
753      timerShaftEl.ctx.fillStyle = systemLogFlag?.color || '#dadada';
754      timerShaftEl.ctx.fillText(timeText, textPointX, locationY);
755      timerShaftEl.ctx.stroke();
756      timerShaftEl.ctx.closePath();
757    }
758  }
759}
760
761interface SelectionParams {
762  isRangeSelect: boolean;
763  rangeSelectObject?: RangeSelectStruct;
764  startNS: number;
765  endNS: number;
766  totalNS: number;
767  frame: Rect;
768}
769
770export function drawSelection(ctx: CanvasRenderingContext2D, params: unknown): void {
771  const param = params as SelectionParams;
772  if (param.isRangeSelect && param.rangeSelectObject) {
773    param.rangeSelectObject!.startX = Math.floor(
774      ns2x(param.rangeSelectObject!.startNS!, param.startNS, param.endNS, param.totalNS, param.frame)
775    );
776    param.rangeSelectObject!.endX = Math.floor(
777      ns2x(param.rangeSelectObject!.endNS!, param.startNS, param.endNS, param.totalNS, param.frame)
778    );
779    if (ctx) {
780      ctx.globalAlpha = 0.5;
781      ctx.fillStyle = '#666666';
782      ctx.fillRect(
783        param.rangeSelectObject!.startX!,
784        param.frame.y,
785        param.rangeSelectObject!.endX! - param.rangeSelectObject!.startX!,
786        param.frame.height
787      );
788      ctx.globalAlpha = 1;
789    }
790  }
791}
792
793// draw range select
794export function drawSelectionRange(context: CanvasRenderingContext2D, params: unknown): void {
795  const param = params as TraceRow<BaseStruct>;
796  if (param.rangeSelect && TraceRow.rangeSelectObject) {
797    setStartXEndX(param);
798    if (context) {
799      context.globalAlpha = 0.5;
800      context.fillStyle = '#666666';
801      context.fillRect(
802        TraceRow.rangeSelectObject!.startX!,
803        param.frame.y,
804        TraceRow.rangeSelectObject!.endX! - TraceRow.rangeSelectObject!.startX!,
805        param.frame.height
806      );
807      context.globalAlpha = 1;
808    }
809    // 绘制线程中方法平均帧率的箭头指示线条
810    if (param.avgRateTxt && param.frameRateList && param.frameRateList.length) {
811      drawAvgFrameRate(param.frameRateList, context, param);
812    }
813  }
814}
815
816function setStartXEndX(params: TraceRow<BaseStruct>): void {
817  TraceRow.rangeSelectObject!.startX = Math.floor(
818    ns2x(
819      TraceRow.rangeSelectObject!.startNS!,
820      TraceRow.range?.startNS ?? 0,
821      TraceRow.range?.endNS ?? 0,
822      TraceRow.range?.totalNS ?? 0,
823      params.frame
824    )
825  );
826  TraceRow.rangeSelectObject!.endX = Math.floor(
827    ns2x(
828      TraceRow.rangeSelectObject!.endNS!,
829      TraceRow.range?.startNS ?? 0,
830      TraceRow.range?.endNS ?? 0,
831      TraceRow.range?.totalNS ?? 0,
832      params.frame
833    )
834  );
835}
836
837function setAvgRateStartXEndX(rateList: number[], params: TraceRow<BaseStruct>): number[] {
838  let avgRateStartX = Math.floor(
839    ns2x(
840      rateList[0]!,
841      TraceRow.range?.startNS ?? 0,
842      TraceRow.range?.endNS ?? 0,
843      TraceRow.range?.totalNS ?? 0,
844      params.frame
845    )
846  );
847  let avgRateEndX = Math.floor(
848    ns2x(
849      rateList[rateList.length - 1]!,
850      TraceRow.range?.startNS ?? 0,
851      TraceRow.range?.endNS ?? 0,
852      TraceRow.range?.totalNS ?? 0,
853      params.frame
854    )
855  );
856  return [avgRateStartX, avgRateEndX];
857}
858
859function setTextXY(rateList: number[], params: TraceRow<BaseStruct>, textWidth: number): number[] {
860  let textX =
861    Math.floor(
862      ns2x(
863        (rateList[0]! + rateList[rateList.length - 1]!) / 2,
864        TraceRow.range?.startNS ?? 0,
865        TraceRow.range?.endNS ?? 0,
866        TraceRow.range?.totalNS ?? 0,
867        params.frame
868      )
869    ) -
870    textWidth / 2; // @ts-ignore
871  let textY = params.frame.y + 25;
872  return [textX, textY];
873}
874
875// 转换起始点坐标
876function changeFrameRatePoint(arrList: Array<number>, selectParams: TraceRow<BaseStruct>): number[] {
877  let avgRateStartX = Math.floor(
878    ns2x(
879      arrList[0]!,
880      TraceRow.range?.startNS ?? 0,
881      TraceRow.range?.endNS ?? 0,
882      TraceRow.range?.totalNS ?? 0,
883      selectParams.frame
884    )
885  ); // 起始坐标
886  let avgRateEndX = Math.floor(
887    ns2x(
888      arrList[arrList.length - 1]!,
889      TraceRow.range?.startNS ?? 0,
890      TraceRow.range?.endNS ?? 0,
891      TraceRow.range?.totalNS ?? 0,
892      selectParams.frame
893    )
894  ); // 结束坐标
895  return [avgRateStartX, avgRateEndX];
896}
897
898// 处理文字坐标
899function handleTextCoordinate(arrList: Array<number>, selectParams: TraceRow<BaseStruct>, textWidth: number): number[] {
900  const TEXT_WIDTH_HALF = 2;
901  let textX = Math.floor(
902    ns2x(
903      (arrList[0]! + arrList[arrList.length - 1]!) / 2,
904      TraceRow.range?.startNS ?? 0,
905      TraceRow.range?.endNS ?? 0,
906      TraceRow.range?.totalNS ?? 0,
907      selectParams.frame
908    )
909  ); //根据帧率范围的中间值转换文本的起始x坐标
910  textX = textX <= textWidth / TEXT_WIDTH_HALF ? textX : textX - textWidth / TEXT_WIDTH_HALF; // @ts-ignore
911  let textY = selectParams.frame.y + 11;
912  if (selectParams.avgRateTxt?.includes('HitchTime')) {
913    // @ts-ignore
914    textY = selectParams.frame.y + 11;
915  } else {
916    // 展开时显示在第二行,折叠显示第一行
917    if (selectParams.funcExpand) {
918      // @ts-ignore
919      textY = selectParams.frame.y + 29;
920    } else {
921      // @ts-ignore
922      textY = selectParams.frame.y + 11;
923    }
924  }
925  return [textX, textY];
926}
927
928// 绘制平均帧率箭头指示线条
929export function drawAvgFrameRate(
930  arrList: Array<number>,
931  ctx: CanvasRenderingContext2D,
932  selectParams: TraceRow<BaseStruct>
933): void {
934  let rateList: Array<number> = [...new Set(arrList)];
935  let startX = changeFrameRatePoint(rateList, selectParams)[0];
936  let endX = changeFrameRatePoint(rateList, selectParams)[1];
937  const textWidth = ctx.measureText(selectParams.avgRateTxt!).width;
938
939  const textHeight = 25;
940  const padding = 5;
941  let textX = handleTextCoordinate(rateList, selectParams, textWidth)[0];
942  let textY = handleTextCoordinate(rateList, selectParams, textWidth)[1];
943  //左移到边界,不画线和文字
944  startX = startX <= 0 ? -100 : startX;
945  endX = endX <= 0 ? -100 : endX;
946  textX = textX <= 0 ? -200 : textX;
947  //右移到边界,不画线和文字
948  const ADD_DISTANCE = 100;
949  textX = textX + textWidth / 2 >= selectParams.frame.width ?
950    selectParams.frame.width + ADD_DISTANCE : textX;
951  startX = startX >= selectParams.frame.width ?
952    selectParams.frame.width + ADD_DISTANCE : startX;
953  endX = endX >= selectParams.frame.width ?
954    selectParams.frame.width + ADD_DISTANCE : endX;
955
956  ctx.lineWidth = 2;
957  ctx.strokeStyle = 'yellow';
958  ctx.beginPath();
959  ctx.moveTo(startX, textY);
960  ctx.lineTo(endX, textY);
961  ctx.stroke();
962
963  const arrowSize = 5.5;
964  const arrowHead = (x: number, y: number, direction: 'left' | 'right'): void => {
965    ctx.beginPath();
966    const headX = x + (direction === 'left' ? arrowSize : -arrowSize);
967    const headY = y - arrowSize / 2;
968    ctx.moveTo(x, y);
969    ctx.lineTo(headX, headY);
970    ctx.lineTo(headX, y + arrowSize);
971    ctx.closePath();
972    ctx.fillStyle = 'yellow';
973    ctx.fill();
974  };
975  arrowHead(startX, textY - 1, 'left');
976  arrowHead(endX, textY - 1, 'right');
977
978  const TEXT_RECT_PADDING = 2;
979  ctx.fillStyle = 'red';
980  ctx.fillRect(
981    textX - padding,
982    textY - textHeight / TEXT_RECT_PADDING + padding,
983    textWidth + padding * TEXT_RECT_PADDING,
984    textHeight - padding * TEXT_RECT_PADDING
985  );
986
987  ctx.fillStyle = 'white';
988  ctx.fillText(selectParams.avgRateTxt!, textX, textY + 4);
989}
990
991function drawAvgFrameRateArrow(
992  ctx: CanvasRenderingContext2D,
993  textX: number,
994  textY: number,
995  textWidth: number,
996  startX: number,
997  endX: number,
998  avgFrameRate: string
999): void {
1000  const textHeight = 25;
1001  const padding = 5;
1002  const TEXT_RECT_PADDING = 2;
1003  ctx.fillStyle = 'red';
1004  ctx.fillRect(
1005    textX - padding,
1006    textY - textHeight + padding,
1007    textWidth + padding * TEXT_RECT_PADDING,
1008    textHeight - padding * TEXT_RECT_PADDING
1009  );
1010  ctx.lineWidth = 2;
1011  ctx.strokeStyle = 'yellow';
1012  ctx.beginPath();
1013  ctx.moveTo(startX, textY);
1014  ctx.lineTo(endX, textY);
1015  ctx.stroke();
1016  arrowHead(ctx, startX, textY - 1, 'left');
1017  arrowHead(ctx, endX, textY - 1, 'right');
1018  ctx.fillStyle = 'white';
1019  ctx.fillText(avgFrameRate, textX, textY - 8);
1020}
1021
1022const arrowSize = 5.5;
1023const arrowHead = (ctx: CanvasRenderingContext2D, x: number, y: number, direction: 'left' | 'right'): void => {
1024  ctx.beginPath();
1025  const headX = x + (direction === 'left' ? arrowSize : -arrowSize);
1026  const headY = y - arrowSize / 2;
1027  ctx.moveTo(x, y);
1028  ctx.lineTo(headX, headY);
1029  ctx.lineTo(headX, y + arrowSize);
1030  ctx.closePath();
1031  ctx.fillStyle = 'yellow';
1032  ctx.fill();
1033};
1034
1035export function drawWakeUp(
1036  wakeUpContext: CanvasRenderingContext2D | undefined | null,
1037  wake: WakeupBean | undefined | null,
1038  startNS: number,
1039  endNS: number,
1040  totalNS: number,
1041  frame: Rect,
1042  selectCpuStruct: CpuStruct | undefined = undefined,
1043  wakeUpCurrentCpu: number | undefined = undefined,
1044  noVerticalLine = false
1045): void {
1046  if (wake && wakeUpContext) {
1047    let x1 = Math.floor(ns2x(wake.wakeupTime || 0, startNS, endNS, totalNS, frame));
1048    wakeUpContext.beginPath();
1049    wakeUpContext.lineWidth = 2;
1050    wakeUpContext.fillStyle = '#000000';
1051    if (x1 > 0 && x1 < frame.x + frame.width) {
1052      if (!noVerticalLine) {
1053        wakeUpContext.moveTo(x1, frame.y);
1054        wakeUpContext.lineTo(x1, frame.y + frame.height);
1055      }
1056      if (wakeUpCurrentCpu === wake.cpu) {
1057        let centerY = Math.floor(frame.y + frame.height / 2);
1058        wakeUpContext.moveTo(x1, centerY - 6);
1059        wakeUpContext.lineTo(x1 + 4, centerY);
1060        wakeUpContext.lineTo(x1, centerY + 6);
1061        wakeUpContext.lineTo(x1 - 4, centerY);
1062        wakeUpContext.lineTo(x1, centerY - 6);
1063        wakeUpContext.fill();
1064      }
1065    }
1066    if (selectCpuStruct) {
1067      drawWakeUpIfSelect(selectCpuStruct, startNS, endNS, totalNS, frame, wakeUpContext, wake, x1);
1068    }
1069    wakeUpContext.strokeStyle = '#000000';
1070    wakeUpContext.stroke();
1071    wakeUpContext.closePath();
1072  }
1073}
1074
1075function drawWakeUpIfSelect(
1076  selectCpuStruct: CpuStruct,
1077  startNS: number,
1078  endNS: number,
1079  totalNS: number,
1080  frame: Rect,
1081  wakeUpContext: CanvasRenderingContext2D,
1082  wake: unknown,
1083  x1: number
1084): void {
1085  let x2 = Math.floor(ns2x(selectCpuStruct.startTime || 0, startNS, endNS, totalNS, frame));
1086  let y = frame.y + frame.height - 10;
1087  wakeUpContext.moveTo(x1, y);
1088  wakeUpContext.lineTo(x2, y);
1089  //@ts-ignore
1090  let s = ns2s((selectCpuStruct.startTime || 0) - (wake.wakeupTime || 0));
1091  let distance = x2 - x1;
1092  if (distance > 12) {
1093    wakeUpContext.moveTo(x1, y);
1094    wakeUpContext.lineTo(x1 + 6, y - 3);
1095    wakeUpContext.moveTo(x1, y);
1096    wakeUpContext.lineTo(x1 + 6, y + 3);
1097    wakeUpContext.moveTo(x2, y);
1098    wakeUpContext.lineTo(x2 - 6, y - 3);
1099    wakeUpContext.moveTo(x2, y);
1100    wakeUpContext.lineTo(x2 - 6, y + 3);
1101    let measure = wakeUpContext.measureText(s);
1102    let tHeight = measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent;
1103    let xStart = x1 + Math.floor(distance / 2 - measure.width / 2);
1104    if (distance > measure.width + 4) {
1105      wakeUpContext.fillStyle = '#ffffff';
1106      wakeUpContext.fillRect(xStart - 2, y - 4 - tHeight, measure.width + 4, tHeight + 4);
1107      wakeUpContext.font = '10px solid';
1108      wakeUpContext.fillStyle = '#000000';
1109      wakeUpContext.textBaseline = 'bottom';
1110      wakeUpContext.fillText(s, xStart, y - 2);
1111    }
1112  }
1113}
1114
1115const wid = 5;
1116const linkLineColor = '#ff0000';
1117
1118export function drawLinkLines(
1119  context: CanvasRenderingContext2D,
1120  nodes: PairPoint[][],
1121  tm: TimerShaftElement,
1122  isFavorite: boolean,
1123  favoriteHeight: number
1124): void {
1125  let percentage =
1126    (tm.getRange()!.totalNS - Math.abs(tm.getRange()!.endNS - tm.getRange()!.startNS)) / tm.getRange()!.totalNS;
1127  let maxWidth = tm.getBoundingClientRect().width - 258;
1128  setLinkLinesNodes(nodes, isFavorite, favoriteHeight, maxWidth, context, percentage);
1129}
1130
1131function setLinkLinesNodes(
1132  nodes: PairPoint[][],
1133  isFav: boolean,
1134  favH: number,
1135  max: number,
1136  context: CanvasRenderingContext2D,
1137  perc: number
1138): void {
1139  for (let i = 0; i < nodes.length; i++) {
1140    let it = nodes[i];
1141    const traceRow0 = it[0].rowEL as TraceRow<BaseStruct>;
1142    const traceRow1 = it[1].rowEL as TraceRow<BaseStruct>;
1143    it[0].y = traceRow0.translateY + it[0].offsetY;
1144    it[1].y = traceRow1.translateY + it[1].offsetY;
1145    let newFirstNode = new PairPoint(
1146      traceRow0,
1147      it[0].x,
1148      it[0].y,
1149      it[0].ns,
1150      it[0].offsetY,
1151      it[0].isRight,
1152      it[0].business
1153    );
1154    let newSecondNode = new PairPoint(
1155      traceRow1,
1156      it[1].x,
1157      it[1].y,
1158      it[1].ns,
1159      it[1].offsetY,
1160      it[1].isRight,
1161      it[1].business
1162    );
1163    if (it[0].lineColor) {
1164      newFirstNode.lineColor = it[0].lineColor;
1165      newSecondNode.lineColor = it[0].lineColor;
1166    }
1167    if (it[0].rangeTime) {
1168      newFirstNode.rangeTime = it[0].rangeTime;
1169    }
1170    if (it[0].hidden) {
1171      continue;
1172    }
1173    if (isFav) {
1174      if (traceRow0.collect && traceRow1.collect) {
1175      } else if (!traceRow0.collect && !traceRow1.collect) {
1176        continue;
1177      } else {
1178        traceRow0.collect ? (newSecondNode.y = Math.max(it[1].y + favH, favH)) :
1179          (newFirstNode.y = Math.max(it[0].y + favH, favH));
1180      }
1181    } else {
1182      if (traceRow0.collect && traceRow1.collect) {
1183        continue;
1184      } else if (!traceRow0.collect && !traceRow1.collect) {
1185      } else {
1186        traceRow0.collect ? (newFirstNode.y = it[0].y - favH) : (newSecondNode.y = it[1].y - favH);
1187      }
1188    }
1189    drawLinesByType(it[0].lineType, newFirstNode, newSecondNode, max, context, perc);
1190  }
1191}
1192
1193function drawLinesByType(
1194  lineType: LineType | undefined,
1195  newFirstNode: PairPoint,
1196  newSecondNode: PairPoint,
1197  maxWidth: number,
1198  context: CanvasRenderingContext2D,
1199  percentage: number
1200): void {
1201  switch (lineType) {
1202    case LineType.brokenLine:
1203      drawBrokenLine([newFirstNode, newSecondNode], maxWidth, context);
1204      break;
1205    case LineType.bezierCurve:
1206      drawBezierCurve([newFirstNode, newSecondNode], maxWidth, context, percentage);
1207      break;
1208    case LineType.straightLine:
1209      drawStraightLine([newFirstNode, newSecondNode], maxWidth, context);
1210      break;
1211    default:
1212      drawBezierCurve([newFirstNode, newSecondNode], maxWidth, context, percentage);
1213  }
1214}
1215
1216function drawBezierCurve(
1217  it: PairPoint[],
1218  maxWidth: number,
1219  context: CanvasRenderingContext2D,
1220  percentage: number
1221): void {
1222  let bezierCurveStart = it[0].x > it[1].x ? it[1] : it[0];
1223  let bezierCurveEnd = it[0].x > it[1].x ? it[0] : it[1];
1224  if (bezierCurveStart && bezierCurveEnd) {
1225    //左移到边界,不画线
1226    if (bezierCurveStart.x <= 0) {
1227      bezierCurveStart.x = -100;
1228    }
1229    if (bezierCurveEnd.x <= 0) {
1230      bezierCurveEnd.x = -100;
1231    }
1232    //右移到边界,不画线
1233    if (bezierCurveStart.x >= maxWidth) {
1234      bezierCurveStart.x = maxWidth + 100;
1235    }
1236    if (bezierCurveEnd.x >= maxWidth) {
1237      bezierCurveEnd.x = maxWidth + 100;
1238    }
1239    drawBezierCurveContext(context, bezierCurveStart, bezierCurveEnd, percentage);
1240  }
1241}
1242
1243function drawBezierCurveContext(
1244  context: CanvasRenderingContext2D,
1245  bezierCurveStart: PairPoint,
1246  bezierCurveEnd: PairPoint,
1247  percentage: number
1248): void {
1249  context.beginPath();
1250  context.lineWidth = 2;
1251  context.fillStyle = linkLineColor;
1252  context.strokeStyle = linkLineColor;
1253  let x0 = bezierCurveStart.x ?? 0;
1254  let y0 = bezierCurveStart.y ?? 0;
1255  let x3 = bezierCurveEnd.x ?? 0;
1256  let y3 = bezierCurveEnd.y ?? 0;
1257  let x2 = bezierCurveEnd.isRight ? x3 - 100 * percentage : x3 + 100 * percentage;
1258  let y2 = y3 - 40 * percentage;
1259  let x1 = bezierCurveStart.isRight ? x0 - 100 * percentage : x0 + 100 * percentage;
1260  let y1 = y0 + 40 * percentage;
1261  if (!bezierCurveStart.isRight) {
1262    x0 -= 5;
1263  }
1264  context.moveTo(x0, y0);
1265  if (bezierCurveStart.isRight) {
1266    context.lineTo(x0 - wid, y0 + wid);
1267    context.moveTo(x0, y0);
1268    context.lineTo(x0 - wid, y0 - wid);
1269  } else {
1270    context.lineTo(x0 + wid, y0 + wid);
1271    context.moveTo(x0, y0);
1272    context.lineTo(x0 + wid, y0 - wid);
1273  }
1274  context.moveTo(x0, y0);
1275  context.bezierCurveTo(x1, y1, x2, y2, x3, y3);
1276  context.moveTo(x3, y3);
1277  if (bezierCurveEnd.isRight) {
1278    context.lineTo(x3 - wid, y3 + wid);
1279    context.moveTo(x3, y3);
1280    context.lineTo(x3 - wid, y3 - wid);
1281  } else {
1282    context.lineTo(x3 + wid, y3 + wid);
1283    context.moveTo(x3, y3);
1284    context.lineTo(x3 + wid, y3 - wid);
1285  }
1286  context.moveTo(x3, y3);
1287  context.stroke();
1288  context.closePath();
1289}
1290
1291function drawStraightLine(it: PairPoint[], maxWidth: number, context: CanvasRenderingContext2D): void {
1292  let startPoint = it[0].x > it[1].x ? it[1] : it[0];
1293  let endPoint = it[0].x > it[1].x ? it[0] : it[1];
1294  let arrowSize = 8;
1295  if (startPoint && endPoint) {
1296    //左移到边界,不画线
1297    if (startPoint.x <= 0) {
1298      startPoint.x = -100;
1299    }
1300    if (endPoint.x <= 0) {
1301      endPoint.x = -100;
1302    }
1303    //右移到边界,不画线
1304    if (startPoint.x >= maxWidth) {
1305      startPoint.x = maxWidth + 100;
1306    }
1307    if (endPoint.x >= maxWidth) {
1308      endPoint.x = maxWidth + 100;
1309    }
1310    drawArrow(context, startPoint, endPoint, arrowSize);
1311  }
1312}
1313
1314function drawArrow(
1315  context: CanvasRenderingContext2D,
1316  startPoint: PairPoint,
1317  endPoint: PairPoint,
1318  arrowSize: number
1319): void {
1320  context.beginPath();
1321  context.lineWidth = 2;
1322  context.strokeStyle = '#0000FF';
1323  context.moveTo(startPoint.x, startPoint.y);
1324  context.lineTo(endPoint.x, endPoint.y);
1325  // 绘制箭头
1326  let arrow = Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x);
1327  context.moveTo(endPoint.x, endPoint.y);
1328  context.lineTo(
1329    endPoint.x - arrowSize * Math.cos(arrow - Math.PI / 6),
1330    endPoint.y - arrowSize * Math.sin(arrow - Math.PI / 6)
1331  );
1332  context.moveTo(endPoint.x, endPoint.y);
1333  context.lineTo(
1334    endPoint.x - arrowSize * Math.cos(arrow + Math.PI / 6),
1335    endPoint.y - arrowSize * Math.sin(arrow + Math.PI / 6)
1336  );
1337  // 绘制另一端箭头
1338  arrow = Math.atan2(startPoint.y - endPoint.y, startPoint.x - endPoint.x);
1339  context.moveTo(startPoint.x, startPoint.y);
1340  context.lineTo(
1341    startPoint.x - arrowSize * Math.cos(arrow - Math.PI / 6),
1342    startPoint.y - arrowSize * Math.sin(arrow - Math.PI / 6)
1343  );
1344  context.moveTo(startPoint.x, startPoint.y);
1345  context.lineTo(
1346    startPoint.x - arrowSize * Math.cos(arrow + Math.PI / 6),
1347    startPoint.y - arrowSize * Math.sin(arrow + Math.PI / 6)
1348  );
1349  context.stroke();
1350  context.closePath();
1351}
1352
1353function drawBrokenLine(it: PairPoint[], maxWidth: number, context: CanvasRenderingContext2D): void {
1354  let brokenLineStart = it[0].x > it[1].x ? it[1] : it[0];
1355  let brokenLineEnd = it[0].x > it[1].x ? it[0] : it[1];
1356  if (brokenLineStart && brokenLineEnd) {
1357    if (brokenLineStart.x <= 0) {
1358      brokenLineStart.x = -100;
1359    }
1360    if (brokenLineEnd.x <= 0) {
1361      brokenLineEnd.x = -100;
1362    }
1363    if (brokenLineStart.x >= maxWidth) {
1364      brokenLineStart.x = maxWidth + 100;
1365    }
1366    if (brokenLineEnd.x >= maxWidth) {
1367      brokenLineEnd.x = maxWidth + 100;
1368    }
1369    drawBrokenLineContext(context, brokenLineStart, brokenLineEnd);
1370  }
1371}
1372
1373function drawBrokenLineContext(
1374  context: CanvasRenderingContext2D,
1375  brokenLineStart: PairPoint,
1376  brokenLineEnd: PairPoint
1377): void {
1378  context.beginPath();
1379  context.lineWidth = 2;
1380  context.fillStyle = brokenLineStart.lineColor ? brokenLineStart.lineColor : '#46B1E3';
1381  context.strokeStyle = brokenLineStart.lineColor ? brokenLineStart.lineColor : '#46B1E3';
1382  let x0 = brokenLineStart.x ?? 0;
1383  let y0 = brokenLineStart.y ?? 0;
1384  let y2 = brokenLineEnd.y ?? 0;
1385  let x2 = brokenLineEnd.x ?? 0;
1386  let x1;
1387  let y1;
1388  let leftEndpointX;
1389  let leftEndpointY;
1390  let rightEndpointX;
1391  let rightEndpointY;
1392  if (brokenLineStart.y < brokenLineEnd.y) {
1393    x1 = brokenLineStart.x ?? 0;
1394    y1 = brokenLineEnd.y ?? 0;
1395    leftEndpointX = x2 - wid;
1396    leftEndpointY = y2 - wid;
1397    rightEndpointX = x2 - wid;
1398    rightEndpointY = y2 + wid;
1399  } else {
1400    x2 = brokenLineEnd.x - wid ?? 0;
1401    x1 = brokenLineEnd.x - wid ?? 0;
1402    y1 = brokenLineStart.y ?? 0;
1403    leftEndpointX = x2 - wid;
1404    leftEndpointY = y2 + wid;
1405    rightEndpointX = x2 + wid;
1406    rightEndpointY = y2 + wid;
1407  }
1408  x1 = drawDistributedLineTime(brokenLineStart.business, brokenLineStart.rangeTime!, [x0, y0, x1, y1, x2, y2], context);
1409  context.moveTo(x0 - 2, y0);
1410  context.lineTo(x1, y1);
1411  context.lineTo(x2, y2);
1412  context.stroke();
1413  context.closePath();
1414  context.beginPath();
1415  context.lineWidth = 2;
1416  context.fillStyle = brokenLineStart.lineColor ? brokenLineStart.lineColor : '#46B1E3';
1417  context.strokeStyle = brokenLineStart.lineColor ? brokenLineStart.lineColor : '#46B1E3';
1418  context.moveTo(x2, y2);
1419  context.lineTo(leftEndpointX, leftEndpointY);
1420  context.lineTo(rightEndpointX, rightEndpointY);
1421  context.lineTo(x2, y2);
1422  context.fill();
1423  context.closePath();
1424}
1425
1426let loadingText = 'Loading...';
1427let loadingTextWidth = 0;
1428let loadingBackground = '#f1f1f1';
1429let loadingFont = 'bold 11pt Arial';
1430let loadingFontColor = '#696969';
1431
1432function drawDistributedLineTime(
1433  business: string,
1434  rangeTime: string,
1435  [x0, y0, x1, y1, x2, y2]: [number, number, number, number, number, number],
1436  context: CanvasRenderingContext2D
1437): number {
1438  if (business === 'distributed') {
1439    if (y0 === y1) {
1440      drawString(context, rangeTime, 0,
1441        new Rect(x0, y0 + 2, x1 - x0, 12), { textMetricsWidth: undefined });
1442    } else {
1443      drawString(context, rangeTime, 0,
1444        new Rect(x1, y1 + 2, x2 - x1, 12), { textMetricsWidth: undefined });
1445      x1 = x1 - 2;
1446    }
1447  }
1448  return x1;
1449}
1450
1451export function drawLoadingFrame(
1452  ctx: CanvasRenderingContext2D,
1453  list: Array<unknown>,
1454  traceRow: unknown,
1455  sort: boolean = false
1456): void {
1457  const row = traceRow as TraceRow<BaseStruct>;
1458  ctx.beginPath();
1459  ctx.clearRect(0, 0, row.frame.width, row.frame.height);
1460  drawLines(ctx, TraceRow.range?.xs || [], row.frame.height, '#dadada');
1461  drawVSync(ctx, row.frame.width, row.frame.height);
1462  if (row.loadingFrame) {
1463    if (loadingTextWidth === 0) {
1464      loadingTextWidth = ctx.measureText(loadingText).width;
1465    }
1466    let firstPx = nsx(row.loadingPin1, row.frame.width);
1467    let lastPx = nsx(row.loadingPin2, row.frame.width);
1468    ctx.fillStyle = loadingBackground;
1469    ctx.fillRect(0, 1, firstPx, row.frame.height - 2);
1470    ctx.fillRect(lastPx, 1, row.frame.width - lastPx, row.frame.height - 2);
1471    ctx.fillStyle = loadingFontColor;
1472    if (firstPx > loadingTextWidth) {
1473      ctx.fillText(loadingText, (firstPx - loadingTextWidth) / 2, row.frame.height / 2);
1474    }
1475    if (row.frame.width - lastPx > loadingTextWidth) {
1476      ctx.fillText(loadingText, lastPx + (row.frame.width - lastPx) / 2 - loadingTextWidth / 2, row.frame.height / 2);
1477    }
1478  }
1479  ctx.closePath();
1480}
1481
1482export function drawString(
1483  ctx: CanvasRenderingContext2D,
1484  str: string,
1485  textPadding: number,
1486  frame: Rect,
1487  data: unknown
1488): void {
1489  //@ts-ignore
1490  if (data.textMetricsWidth === undefined) {
1491    //@ts-ignore
1492    data.textMetricsWidth = ctx.measureText(str);
1493  }
1494  //@ts-ignore
1495  const textMetricsWidth = (data.textMetricsWidth as TextMetrics).width;
1496  const yPos = 1.5;
1497  let charWidth = Math.round(textMetricsWidth / str.length);
1498  let fillTextWidth = frame.width - textPadding * 2;
1499  if (textMetricsWidth < fillTextWidth) {
1500    let x2 = Math.floor(frame.width / 2 - textMetricsWidth / 2 + frame.x + textPadding);
1501    ctx.fillText(str, x2, Math.floor(frame.y + frame.height / yPos), fillTextWidth);
1502  } else {
1503    if (fillTextWidth >= charWidth) {
1504      let chatNum = fillTextWidth / charWidth;
1505      let x1 = frame.x + textPadding;
1506
1507      if (chatNum < 2) {
1508        ctx.fillText(str.substring(0, 1), x1, Math.floor(frame.y + frame.height / yPos), fillTextWidth);
1509      } else {
1510        ctx.fillText(
1511          `${str.substring(0, chatNum - 1)}...`,
1512          x1,
1513          Math.floor(frame.y + frame.height / yPos),
1514          fillTextWidth
1515        );
1516      }
1517    }
1518  }
1519}
1520
1521export function drawFunString(
1522  ctx: CanvasRenderingContext2D,
1523  str: string,
1524  textPadding: number,
1525  frame: Rect,
1526  data: FuncStruct
1527): void {
1528  if (data.textMetricsWidth === undefined) {
1529    data.textMetricsWidth = ctx.measureText(str).width;
1530  }
1531  let charWidth = Math.round(data.textMetricsWidth / str.length);
1532  let fillTextWidth = frame.width - textPadding * 2;
1533  if (data.textMetricsWidth < fillTextWidth) {
1534    let x2 = Math.floor(frame.width / 2 - data.textMetricsWidth / 2 + frame.x + textPadding);
1535    ctx.fillText(str, x2, Math.floor(data.frame!.height * (data.depth! + 0.5) + 3), fillTextWidth);
1536  } else {
1537    if (fillTextWidth >= charWidth) {
1538      let chatNum = fillTextWidth / charWidth;
1539      let x1 = frame.x + textPadding;
1540      if (chatNum < 2) {
1541        ctx.fillText(str.substring(0, 1), x1, Math.floor(data.frame!.height * (data.depth! + 0.5) + 3), fillTextWidth);
1542      } else {
1543        ctx.fillText(
1544          `${str.substring(0, chatNum - 1)}...`,
1545          x1,
1546          Math.floor(data.frame!.height * (data.depth! + 0.5) + 3),
1547          fillTextWidth
1548        );
1549      }
1550    }
1551  }
1552}
1553
1554export function hiPerf(
1555  arr: Array<HiPerfStruct>,
1556  arr2: Array<HiPerfStruct>,
1557  res: Array<HiPerfStruct>,
1558  startNS: number,
1559  endNS: number,
1560  frame: Rect,
1561  groupBy10MS: boolean,
1562  use: boolean
1563): void {
1564  if (use && res.length > 0) {
1565    setFrameByRes(res, startNS, endNS, frame);
1566    return;
1567  }
1568  res.length = 0;
1569  if (arr) {
1570    setFrameByArr(arr, arr2, res, startNS, endNS, frame, groupBy10MS);
1571  }
1572}
1573
1574function setFrameByRes(res: Array<HiPerfStruct>, startNS: number, endNS: number, frame: Rect): void {
1575  let pns = (endNS - startNS) / frame.width;
1576  let y = frame.y;
1577  for (let i = 0; i < res.length; i++) {
1578    let item = res[i];
1579    if ((item.startNS || 0) + (item.dur || 0) > startNS && (item.startNS || 0) < endNS) {
1580      if (!item.frame) {
1581        item.frame = new Rect(0, 0, 0, 0);
1582        item.frame.y = y;
1583      }
1584      item.frame.height = item.height!;
1585      HiPerfStruct.setFrame(item, pns, startNS, endNS, frame);
1586    } else {
1587      item.frame = undefined;
1588    }
1589  }
1590}
1591
1592function setFrameByArr(
1593  arr: Array<HiPerfStruct>,
1594  arr2: Array<HiPerfStruct>,
1595  res: Array<HiPerfStruct>,
1596  startNS: number,
1597  endNS: number,
1598  frame: Rect,
1599  groupBy10MS: boolean
1600): void {
1601  let list = groupBy10MS ? arr2 : arr;
1602  let pns = (endNS - startNS) / frame.width;
1603  let y = frame.y;
1604  for (let i = 0, len = list.length; i < len; i++) {
1605    let it = list[i];
1606    if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) {
1607      if (!list[i].frame) {
1608        list[i].frame = new Rect(0, 0, 0, 0);
1609        list[i].frame!.y = y;
1610      }
1611      list[i].frame!.height = it.height!;
1612      HiPerfStruct.setFrame(list[i], pns, startNS, endNS, frame);
1613      setResultArr(groupBy10MS, list, i, res);
1614    }
1615  }
1616}
1617
1618function setResultArr(groupBy10MS: boolean, list: Array<HiPerfStruct>, i: number, res: Array<HiPerfStruct>): void {
1619  const itemI = list[i];
1620  const itemBeforeI = list[i - 1];
1621  if (itemI.frame && itemBeforeI.frame) {
1622    if (groupBy10MS) {
1623      let flag: boolean =
1624        i > 0 &&
1625        (itemBeforeI.frame.x || 0) === (itemI.frame.x || 0) &&
1626        (itemBeforeI.frame.width || 0) === (itemI.frame.width || 0) &&
1627        (itemBeforeI.frame.height || 0) === (itemI.frame.height || 0);
1628      if (!flag) {
1629        res.push(itemI);
1630      }
1631    } else {
1632      if (!(i > 0 && Math.abs((itemBeforeI.frame.x || 0) - (itemI.frame.x || 0)) < 4)) {
1633        res.push(itemI);
1634      }
1635    }
1636  }
1637}
1638
1639export function hiPerf2(filter: Array<HiPerfStruct>, startNS: number, endNS: number, frame: Rect): void {
1640  if (filter.length > 0) {
1641    let pns = (endNS - startNS) / frame.width;
1642    let y = frame.y;
1643    for (let i = 0; i < filter.length; i++) {
1644      let it = filter[i];
1645      if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) {
1646        if (!it.frame) {
1647          it.frame = new Rect(0, 0, 0, 0);
1648          it.frame.y = y;
1649        }
1650        it.frame.height = it.height!;
1651        HiPerfStruct.setFrame(it, pns, startNS, endNS, frame);
1652      } else {
1653        it.frame = undefined;
1654      }
1655    }
1656    return;
1657  }
1658}
1659
1660export class HiPerfStruct extends BaseStruct {
1661  static hoverStruct: HiPerfStruct | undefined;
1662  static selectStruct: HiPerfStruct | undefined;
1663  id: number | undefined;
1664  callchain_id: number | undefined;
1665  timestamp: number | undefined;
1666  thread_id: number | undefined;
1667  event_count: number | undefined;
1668  event_type_id: number | undefined;
1669  cpu_id: number | undefined;
1670  thread_state: string | undefined;
1671  startNS: number | undefined;
1672  endNS: number | undefined;
1673  dur: number | undefined;
1674  height: number | undefined;
1675  eventCount: number | undefined;
1676  sampleCount: number | undefined;
1677
1678  static drawRoundRectPath(cxt: Path2D, x: number, y: number, width: number, height: number, radius: number): void {
1679    cxt.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2);
1680    cxt.lineTo(x + radius, y + height);
1681    cxt.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI);
1682    cxt.lineTo(x, y + radius);
1683    cxt.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
1684    cxt.lineTo(x + width - radius, y);
1685    cxt.arc(x + width - radius, y + radius, radius, (Math.PI * 3) / 2, Math.PI * 2);
1686    cxt.lineTo(x + width, y + height - radius);
1687    cxt.moveTo(x + width / 3, y + height / 5);
1688    cxt.lineTo(x + width / 3, y + (height / 5) * 4);
1689    cxt.moveTo(x + width / 3, y + height / 5);
1690    cxt.bezierCurveTo(
1691      x + width / 3 + 7,
1692      y + height / 5 - 2,
1693      x + width / 3 + 7,
1694      y + height / 5 + 6,
1695      x + width / 3,
1696      y + height / 5 + 4
1697    );
1698  }
1699
1700  static draw(
1701    ctx: CanvasRenderingContext2D,
1702    normalPath: Path2D,
1703    specPath: Path2D,
1704    data: HiPerfStruct,
1705    groupBy10MS: boolean,
1706    textMetrics?: TextMetrics
1707  ): void {
1708    if (data.frame) {
1709      if (groupBy10MS) {
1710        let width = data.frame.width;
1711        normalPath.rect(data.frame.x, 40 - (data.height || 0), width, data.height || 0);
1712      } else {
1713        data.frame.width > 4 ? (data.frame.width = 4) : (data.frame.width = data.frame.width);
1714        let path = data.callchain_id === -1 ? specPath : normalPath;
1715        path.moveTo(data.frame.x + 7, 20);
1716        if (textMetrics) {
1717          ctx.fillText('��', data.frame.x - textMetrics!.width / 2, 26); //℗©®℗®��
1718        } else {
1719          HiPerfStruct.drawRoundRectPath(path, data.frame.x - 7, 20 - 7, 14, 14, 3);
1720        }
1721        path.moveTo(data.frame.x, 27);
1722        path.lineTo(data.frame.x, 33);
1723      }
1724    }
1725  }
1726
1727  static drawSpecialPath(ctx: CanvasRenderingContext2D, specPath: Path2D): void {
1728    ctx.strokeStyle = '#9fafc4';
1729    ctx.globalAlpha = 0.5;
1730    ctx.stroke(specPath);
1731    ctx.globalAlpha = 1;
1732  }
1733
1734  static setFrame(node: HiPerfStruct, pns: number, startNS: number, endNS: number, frame: Rect): void {
1735    if (!node.frame) {
1736      return;
1737    }
1738    if ((node.startNS || 0) < startNS) {
1739      node.frame.x = 0;
1740    } else {
1741      node.frame.x = Math.floor(((node.startNS || 0) - startNS) / pns);
1742    }
1743    if ((node.startNS || 0) + (node.dur || 0) > endNS) {
1744      node.frame.width = frame.width - node.frame.x;
1745    } else {
1746      node.frame.width = Math.ceil(((node.startNS || 0) + (node.dur || 0) - startNS) / pns - node.frame.x);
1747    }
1748    if (node.frame.width < 1) {
1749      node.frame.width = 1;
1750    }
1751  }
1752
1753  static groupBy10MS(
1754    groupArray: Array<HiPerfStruct>,
1755    intervalPerf: number,
1756    maxCpu?: number | undefined,
1757    usage?: boolean,
1758    event?: number
1759  ): Array<HiPerfStruct> {
1760    let maxEventCount = 0;
1761    let obj = filterGroupArray(groupArray, maxEventCount, usage, event);
1762    let arr = [];
1763    for (let aKey in obj) {
1764      let ns = parseInt(aKey);
1765      let height: number = 0;
1766      if (usage) {
1767        if (maxCpu !== undefined) {
1768          //@ts-ignore
1769          height = Math.floor((obj[aKey].sampleCount / (10 / intervalPerf) / maxCpu) * 40);
1770        } else {
1771          //@ts-ignore
1772          height = Math.floor((obj[aKey].sampleCount / (10 / intervalPerf)) * 40);
1773        }
1774      } else {
1775        //@ts-ignore
1776        height = Math.floor((obj[aKey].eventCount / maxEventCount) * 40);
1777      }
1778      arr.push({
1779        startNS: ns,
1780        dur: 10_000_000,
1781        //@ts-ignore
1782        eventCount: obj[aKey].eventCount,
1783        //@ts-ignore
1784        sampleCount: obj[aKey].sampleCount,
1785        height: height,
1786      });
1787    }
1788    return arr as HiPerfStruct[];
1789  }
1790}
1791
1792function filterGroupArray(
1793  groupArray: Array<HiPerfStruct>,
1794  maxEventCount: number,
1795  usage?: boolean,
1796  event?: number
1797): HiPerfStruct {
1798  const map = groupArray.map((it) => {
1799    //@ts-ignore
1800    it.timestamp_group = Math.trunc(it.startNS / 10_000_000) * 10_000_000;
1801    return it;
1802  });
1803  const reduce = map.reduce((pre: HiPerfStruct, current: HiPerfStruct) => {
1804    if (usage || current.event_type_id === event || event === -1) {
1805      //@ts-ignore
1806      if (pre[current.timestamp_group]) {
1807        //@ts-ignore
1808        pre[current.timestamp_group].sampleCount += 1;
1809        //@ts-ignore
1810        pre[current.timestamp_group].eventCount += current.event_count;
1811      } else {
1812        //@ts-ignore
1813        pre[current.timestamp_group] = {
1814          sampleCount: 1,
1815          eventCount: current.event_count,
1816        };
1817      }
1818      //@ts-ignore
1819      maxEventCount = Math.max(pre[current.timestamp_group].eventCount, maxEventCount);
1820    }
1821    return pre;
1822  }, new HiPerfStruct());
1823  return reduce;
1824}
1825
1826function setMemFrame(
1827  node: ProcessMemStruct,
1828  padding: number,
1829  startNS: number,
1830  endNS: number,
1831  totalNS: number,
1832  frame: Rect
1833): void {
1834  let x1: number;
1835  let x2: number;
1836  if ((node.startTime || 0) <= startNS) {
1837    x1 = 0;
1838  } else {
1839    x1 = ns2x(node.startTime || 0, startNS, endNS, totalNS, frame);
1840  }
1841  if ((node.startTime || 0) + (node.duration || 0) >= endNS) {
1842    x2 = frame.width;
1843  } else {
1844    x2 = ns2x((node.startTime || 0) + (node.duration || 0), startNS, endNS, totalNS, frame);
1845  }
1846  let getV: number = x2 - x1 <= 1 ? 1 : x2 - x1;
1847  if (!node.frame) {
1848    node.frame = new Rect(0, 0, 0, 0);
1849  }
1850  node.frame.x = Math.floor(x1);
1851  node.frame.y = Math.floor(frame.y + padding);
1852  node.frame.width = Math.ceil(getV);
1853  node.frame.height = Math.floor(frame.height - padding * 2);
1854}
1855
1856export function mem(
1857  list: Array<unknown>,
1858  memFilter: Array<unknown>,
1859  startNS: number,
1860  endNS: number,
1861  totalNS: number,
1862  frame: Rect,
1863  use: boolean
1864): void {
1865  if (use && memFilter.length > 0) {
1866    for (let i = 0, len = memFilter.length; i < len; i++) {
1867      if (
1868        //@ts-ignore
1869        (memFilter[i].startTime || 0) + (memFilter[i].duration || 0) > startNS &&
1870        //@ts-ignore
1871        (memFilter[i].startTime || 0) < endNS
1872      ) {
1873        //@ts-ignore
1874        setMemFrame(memFilter[i], 5, startNS, endNS, totalNS, frame);
1875      } else {
1876        //@ts-ignore
1877        memFilter[i].frame = undefined;
1878      }
1879    }
1880    return;
1881  }
1882  memFilter.length = 0;
1883  //@ts-ignore
1884  setMemFilter(list, memFilter, startNS, endNS, totalNS, frame);
1885}
1886
1887function setMemFilter(
1888  list: Array<ProcessMemStruct>,
1889  memFilter: Array<ProcessMemStruct>,
1890  startNS: number,
1891  endNS: number,
1892  totalNS: number,
1893  frame: Rect
1894): void {
1895  if (list) {
1896    for (let i = 0, len = list.length; i < len; i++) {
1897      let it = list[i];
1898      if ((it.startTime || 0) + (it.duration || 0) > startNS && (it.startTime || 0) < endNS) {
1899        setMemFrame(list[i], 5, startNS, endNS, totalNS, frame);
1900        if (
1901          i > 0 &&
1902          (list[i - 1].frame?.x || 0) === (list[i].frame?.x || 0) &&
1903          (list[i - 1].frame?.width || 0) === (list[i].frame?.width || 0)
1904        ) {
1905        } else {
1906          memFilter.push(list[i]);
1907        }
1908      }
1909    }
1910  }
1911}
1912
1913export function drawWakeUpList(
1914  wakeUpListContext: CanvasRenderingContext2D | undefined | null,
1915  wake: WakeupBean | undefined | null,
1916  startNS: number,
1917  endNS: number,
1918  totalNS: number,
1919  frame: Rect,
1920  wakeup: WakeupBean | undefined = undefined,
1921  currentCpu: number | undefined | null = undefined,
1922  noVerticalLine = false
1923): void {
1924  if (!wakeUpListContext) {
1925    return;
1926  }
1927  if (wake) {
1928    let x1 = Math.floor(ns2x(wake.wakeupTime || 0, startNS, endNS, totalNS, frame));
1929    wakeUpListContext.beginPath();
1930    wakeUpListContext.lineWidth = 2;
1931    wakeUpListContext.fillStyle = '#000000';
1932    if (x1 > 0 && x1 < frame.x + frame.width) {
1933      if (!noVerticalLine) {
1934        wakeUpListContext.moveTo(x1, frame.y);
1935        wakeUpListContext.lineTo(x1, frame.y + frame.height);
1936      }
1937      if (currentCpu === wake.cpu) {
1938        let centerY = Math.floor(frame.y + frame.height / 2);
1939        wakeUpListContext.moveTo(x1, centerY - 6);
1940        wakeUpListContext.lineTo(x1 + 4, centerY);
1941        wakeUpListContext.lineTo(x1, centerY + 6);
1942        wakeUpListContext.lineTo(x1 - 4, centerY);
1943        wakeUpListContext.lineTo(x1, centerY - 6);
1944        wakeUpListContext.fill();
1945      }
1946    }
1947    if (wakeup) {
1948      drawWakeUpListIfWakeUp(wakeUpListContext, wake, startNS, endNS, totalNS, frame, wakeup, x1);
1949    }
1950    wakeUpListContext.strokeStyle = '#000000';
1951    wakeUpListContext.stroke();
1952    wakeUpListContext.closePath();
1953  }
1954}
1955
1956function drawWakeUpListIfWakeUp(
1957  wakeUpListContext: CanvasRenderingContext2D,
1958  wake: WakeupBean,
1959  startNS: number,
1960  endNS: number,
1961  totalNS: number,
1962  frame: Rect,
1963  wakeup: WakeupBean,
1964  x1: number
1965): void {
1966  let x2 = Math.floor(ns2x(wakeup.ts || 0, startNS, endNS, totalNS, frame));
1967  let y = frame.y + frame.height - 10;
1968  wakeUpListContext.moveTo(x1, y);
1969  wakeUpListContext.lineTo(x2, y);
1970  wakeUpListContext.moveTo(x2, y - 25);
1971  wakeUpListContext.lineTo(x2, y + 5);
1972  let s = ns2s((wakeup.ts || 0) - (wake.wakeupTime || 0));
1973  let wakeUpListDistance = x2 - x1;
1974  if (wakeUpListDistance > 12) {
1975    wakeUpListContext.moveTo(x1, y);
1976    wakeUpListContext.lineTo(x1 + 6, y - 3);
1977    wakeUpListContext.moveTo(x1, y);
1978    wakeUpListContext.lineTo(x1 + 6, y + 3);
1979    wakeUpListContext.moveTo(x2, y);
1980    wakeUpListContext.lineTo(x2 - 6, y - 3);
1981    wakeUpListContext.moveTo(x2, y);
1982    wakeUpListContext.lineTo(x2 - 6, y + 3);
1983    let measure = wakeUpListContext.measureText(s);
1984    let tHeight = measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent;
1985    let xStart = x1 + Math.floor(wakeUpListDistance / 2 - measure.width / 2);
1986    if (wakeUpListDistance > measure.width + 4) {
1987      wakeUpListContext.fillStyle = '#ffffff';
1988      wakeUpListContext.fillRect(xStart - 2, y - 4 - tHeight, measure.width + 4, tHeight + 4);
1989      wakeUpListContext.font = '10px solid';
1990      wakeUpListContext.fillStyle = '#000000';
1991      wakeUpListContext.textBaseline = 'bottom';
1992      wakeUpListContext.fillText(s, xStart, y - 2);
1993    }
1994  }
1995}
1996interface SearchNode {
1997  symbolName?: string;
1998  children: SearchNode[];
1999  searchShow?: boolean;
2000  isSearch?: boolean;
2001  parent?: SearchNode;
2002}
2003
2004export function findSearchNode(data: unknown[], search: string, parentSearch: boolean): void {
2005  search = search.toLocaleLowerCase();
2006  data.forEach((nodeIt) => {
2007    const node = nodeIt as SearchNode;
2008    if ((node.symbolName && node.symbolName.toLocaleLowerCase().includes(search) && search !== '') || parentSearch) {
2009      node.searchShow = true;
2010      node.isSearch = node.symbolName !== undefined && node.symbolName.toLocaleLowerCase().includes(search);
2011      let parentNode = node.parent;
2012      while (parentNode && !parentNode.searchShow) {
2013        parentNode.searchShow = true;
2014        parentNode = parentNode.parent;
2015      }
2016    } else {
2017      node.searchShow = false;
2018      node.isSearch = false;
2019    }
2020    if (node.children.length > 0) {
2021      findSearchNode(node.children, search, node.searchShow);
2022    }
2023  });
2024}
2025
2026// draw prio curve
2027// @ts-ignore
2028export function prioClickHandlerFun(param: unknown, row: TraceRow<unknown>, threadFilter: Array<ThreadStruct>, arr: unknown, oldVal: number): void {
2029  //@ts-ignore
2030  let maxCount = Math.max(...param.map((obj: unknown) => obj.count));
2031  //@ts-ignore
2032  let maxCountPrio = param.find((obj: unknown) => obj.count === maxCount).prio;//找出出现次数最多的优先级,为中位值
2033  //@ts-ignore
2034  let maxPrioDiff = Math.max(...param.map((obj: unknown) => Math.abs(obj.prio - Number(maxCountPrio))));//找出与中位值的最大diff
2035  let maxPointInterval = Math.ceil(maxPrioDiff / 4);//diff分成4份,每一份占多少px
2036
2037  for (let i = 0; i < threadFilter.length; i++) {
2038    const item = threadFilter[i];
2039    const preItem = threadFilter[i - 1];
2040    //给原始数据添加prio值
2041    let slice = Utils.getInstance().getSchedSliceMap().get(`${item.id}-${item.startTime}`);
2042    if (slice) {
2043      item.prio = slice!.priority;
2044    }
2045    //合并prio值相同的项提高画图速度
2046    if (
2047      item.prio &&
2048      (oldVal !== item.prio || i === threadFilter.length - 2 || i === threadFilter.length - 1)
2049    ) {
2050      configCurveY(row, item, maxCountPrio, maxPointInterval);
2051      //处理prio值变化前的
2052      if (i !== 0) {
2053        configCurveY(row, preItem, maxCountPrio, maxPointInterval);
2054        //@ts-ignore
2055        arr.push(preItem);
2056      }
2057      //@ts-ignore
2058      arr.push(item);
2059      oldVal = item.prio;
2060    }
2061  }
2062}
2063
2064//确定曲线波动时的y轴
2065//@ts-ignore
2066function configCurveY(row: TraceRow<unknown>, item: ThreadStruct, maxCountPrio: number, maxPointInterval: number): void {
2067  if (item.prio === Number(maxCountPrio)) {
2068    item.curveFloatY = 3 + 12 / 2 + row.translateY;
2069  } else if (item.prio! > Number(maxCountPrio)) {
2070    let prioHeight = Math.floor((item.prio! - Number(maxCountPrio)) / maxPointInterval) * 2;
2071    item.curveFloatY = 3 + 12 / 2 - prioHeight + row.translateY;
2072  } else if (item.prio! < Number(maxCountPrio)) {
2073    let prioHeight = Math.floor((Number(maxCountPrio) - item.prio!) / maxPointInterval) * 2;
2074    item.curveFloatY = 3 + 12 / 2 + prioHeight + row.translateY;
2075  }
2076}
2077
2078export function drawThreadCurve(context: CanvasRenderingContext2D, threadFilter: ThreadStruct, nextFilter: ThreadStruct): void {
2079  // 绘制曲线
2080  if (threadFilter.frame && nextFilter.frame) {
2081    let p1 = threadFilter;
2082    let p2 = nextFilter;
2083    let diff = p2.curveFloatY! >= p1.curveFloatY! ? p2.curveFloatY! - p1.curveFloatY! : p1.curveFloatY! - p2.curveFloatY!;
2084    let cp1x = p1.frame!.x + (p2.frame!.x - p1.frame!.x) / 5;
2085    let cp1y = p2.curveFloatY! >= p1.curveFloatY! ? p1.curveFloatY! - diff / 5 : p1.curveFloatY! + diff / 5;
2086    let cp2x = p2.frame!.x - (p2.frame!.x - p1.frame!.x) / 5;
2087    let cp2y = p2.curveFloatY! >= p1.curveFloatY! ? p2.curveFloatY! + diff / 5 : p2.curveFloatY! - diff / 5;
2088    context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.frame!.x, p2.curveFloatY!);
2089    context.lineWidth = 1;
2090    context.strokeStyle = '#ffc90e';
2091    context.lineCap = 'round';
2092  }
2093  context.stroke();
2094}
2095