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