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