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