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