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 560 constructor( 561 rowEL: TraceRow<BaseStruct>, 562 x: number, 563 y: number, 564 ns: number, 565 offsetY: number, 566 isRight: boolean, 567 business: string 568 ) { 569 this.rowEL = rowEL; 570 this.x = x; 571 this.y = y; 572 this.ns = ns; 573 this.offsetY = offsetY; 574 this.isRight = isRight; 575 this.business = business; 576 } 577} 578 579export class BaseStruct { 580 translateY: number | undefined; 581 frame: Rect | undefined; 582 isHover: boolean = false; 583} 584 585export function drawLines(ctx: CanvasRenderingContext2D, xs: Array<number>, height: number, lineColor: string): void { 586 if (ctx) { 587 ctx.beginPath(); 588 ctx.lineWidth = 1; 589 ctx.strokeStyle = lineColor || '#dadada'; 590 xs?.forEach((it) => { 591 ctx.moveTo(Math.floor(it), 0); 592 ctx.lineTo(Math.floor(it), height); 593 }); 594 ctx.stroke(); 595 ctx.closePath(); 596 } 597} 598 599export function drawFlagLine( 600 commonCtx: CanvasRenderingContext2D, 601 hoverFlag: Flag, 602 selectFlag: Flag, 603 startNS: number, 604 endNS: number, 605 totalNS: number, 606 frame: Rect, 607 slicesTime: 608 | { 609 startTime: number | null | undefined; 610 endTime: number | null | undefined; 611 color: string | null | undefined; 612 } 613 | undefined 614): void { 615 if (commonCtx) { 616 if (hoverFlag) { 617 setHoverFlag(hoverFlag, commonCtx, frame); 618 } 619 if (selectFlag) { 620 commonCtx.beginPath(); 621 commonCtx.lineWidth = 2; 622 commonCtx.strokeStyle = selectFlag?.color || '#dadada'; 623 selectFlag.x = ns2x(selectFlag.time, startNS, endNS, totalNS, frame); 624 commonCtx.moveTo(Math.floor(selectFlag.x), 0); 625 commonCtx.lineTo(Math.floor(selectFlag.x), frame.height); 626 commonCtx.stroke(); 627 commonCtx.closePath(); 628 } 629 if (slicesTime && slicesTime.startTime && slicesTime.endTime) { 630 commonCtx.beginPath(); 631 commonCtx.lineWidth = 1; 632 commonCtx.strokeStyle = slicesTime.color || '#dadada'; 633 let x1 = ns2x(slicesTime.startTime, startNS, endNS, totalNS, frame); 634 let x2 = ns2x(slicesTime.endTime, startNS, endNS, totalNS, frame); 635 commonCtx.moveTo(Math.floor(x1), 0); 636 commonCtx.lineTo(Math.floor(x1), frame.height); 637 commonCtx.moveTo(Math.floor(x2), 0); 638 commonCtx.lineTo(Math.floor(x2), frame.height); 639 commonCtx.stroke(); 640 commonCtx.closePath(); 641 } 642 } 643} 644 645export function drawFlagLineSegment( 646 ctx: CanvasRenderingContext2D | null | undefined, 647 hoverFlag: Flag | null | undefined, 648 selectFlag: Flag | null | undefined, 649 frame: Rect, 650 tse: TimerShaftElement 651): void { 652 if (ctx) { 653 setHoverFlag(hoverFlag, ctx, frame); 654 setSelectFlag(selectFlag, ctx, frame); 655 tse.sportRuler!.slicesTimeList.forEach((slicesTime) => { 656 if (slicesTime && slicesTime.startTime && slicesTime.endTime) { 657 ctx.beginPath(); 658 ctx.lineWidth = 1; 659 ctx.strokeStyle = slicesTime.color || '#dadada'; 660 let x1 = ns2x( 661 slicesTime.startTime, 662 TraceRow.range!.startNS, 663 TraceRow.range!.endNS, 664 TraceRow.range!.totalNS, 665 frame 666 ); 667 let x2 = ns2x( 668 slicesTime.endTime, 669 TraceRow.range!.startNS, 670 TraceRow.range!.endNS, 671 TraceRow.range!.totalNS, 672 frame 673 ); 674 // 划线逻辑 675 ctx.moveTo(Math.floor(x1), 0); 676 ctx.lineTo(Math.floor(x1), frame.height!); //左边的线 677 ctx.moveTo(Math.floor(x2), 0); 678 ctx.lineTo(Math.floor(x2), frame.height!); // 右边的线 679 ctx.stroke(); 680 ctx.closePath(); 681 } 682 }); 683 } 684} 685 686function setHoverFlag(hoverFlag: Flag | null | undefined, ctx: CanvasRenderingContext2D, frame: Rect): void { 687 if (hoverFlag) { 688 ctx.beginPath(); 689 ctx.lineWidth = 2; 690 ctx.strokeStyle = hoverFlag?.color || '#dadada'; 691 ctx.moveTo(Math.floor(hoverFlag.x), 0); 692 ctx.lineTo(Math.floor(hoverFlag.x), frame.height); 693 ctx.stroke(); 694 ctx.closePath(); 695 } 696} 697 698function setSelectFlag(selectFlag: Flag | null | undefined, ctx: CanvasRenderingContext2D, frame: Rect): void { 699 if (selectFlag) { 700 ctx.beginPath(); 701 ctx.lineWidth = 2; 702 ctx.strokeStyle = selectFlag?.color || '#dadada'; 703 selectFlag.x = ns2x( 704 selectFlag.time, 705 TraceRow.range!.startNS, 706 TraceRow.range!.endNS, 707 TraceRow.range!.totalNS, 708 frame 709 ); 710 ctx.moveTo(Math.floor(selectFlag.x), 0); 711 ctx.lineTo(Math.floor(selectFlag.x), frame.height); 712 ctx.stroke(); 713 ctx.closePath(); 714 } 715} 716 717export function drawLogsLineSegment( 718 ctx: CanvasRenderingContext2D | undefined | null, 719 systemLogFlag: Flag | undefined | null, 720 frame: { 721 x: number; 722 y: number; 723 width: number | undefined; 724 height: number | undefined; 725 }, 726 timerShaftEl: TimerShaftElement 727): void { 728 timerShaftEl.sportRuler?.draw(); 729 if (systemLogFlag) { 730 if (ctx) { 731 ctx.beginPath(); 732 ctx.lineWidth = 2; 733 ctx.strokeStyle = systemLogFlag?.color || '#dadada'; 734 ctx.moveTo(Math.floor(systemLogFlag.x), 0); 735 ctx.lineTo(Math.floor(systemLogFlag.x), frame.height || 0); 736 ctx.stroke(); 737 ctx.closePath(); 738 } 739 if (timerShaftEl.ctx) { 740 let timeText = `| ${ns2Timestamp(systemLogFlag.time)}`; 741 let textPointX = systemLogFlag.x; 742 let textMetrics = timerShaftEl.ctx.measureText(timeText); 743 if (timerShaftEl.ctx.canvas.width - systemLogFlag.x <= textMetrics.width) { 744 textPointX = systemLogFlag.x - textMetrics.width; 745 timeText = `${ns2Timestamp(systemLogFlag.time)} |`; 746 } 747 let locationY = 120; 748 timerShaftEl.ctx.beginPath(); 749 timerShaftEl.ctx.lineWidth = 0; 750 timerShaftEl.ctx.fillStyle = '#FFFFFF'; 751 let textHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent; 752 timerShaftEl.ctx.fillRect(textPointX, locationY - textHeight, textMetrics.width, textHeight); 753 timerShaftEl.ctx.lineWidth = 2; 754 timerShaftEl.ctx.fillStyle = systemLogFlag?.color || '#dadada'; 755 timerShaftEl.ctx.fillText(timeText, textPointX, locationY); 756 timerShaftEl.ctx.stroke(); 757 timerShaftEl.ctx.closePath(); 758 } 759 } 760} 761 762interface SelectionParams { 763 isRangeSelect: boolean; 764 rangeSelectObject?: RangeSelectStruct; 765 startNS: number; 766 endNS: number; 767 totalNS: number; 768 frame: Rect; 769} 770 771export function drawSelection(ctx: CanvasRenderingContext2D, params: unknown): void { 772 const param = params as SelectionParams; 773 if (param.isRangeSelect && param.rangeSelectObject) { 774 param.rangeSelectObject!.startX = Math.floor( 775 ns2x(param.rangeSelectObject!.startNS!, param.startNS, param.endNS, param.totalNS, param.frame) 776 ); 777 param.rangeSelectObject!.endX = Math.floor( 778 ns2x(param.rangeSelectObject!.endNS!, param.startNS, param.endNS, param.totalNS, param.frame) 779 ); 780 if (ctx) { 781 ctx.globalAlpha = 0.5; 782 ctx.fillStyle = '#666666'; 783 ctx.fillRect( 784 param.rangeSelectObject!.startX!, 785 param.frame.y, 786 param.rangeSelectObject!.endX! - param.rangeSelectObject!.startX!, 787 param.frame.height 788 ); 789 ctx.globalAlpha = 1; 790 } 791 } 792} 793 794// draw range select 795export function drawSelectionRange(context: CanvasRenderingContext2D, params: unknown): void { 796 const param = params as TraceRow<BaseStruct>; 797 if (param.rangeSelect && TraceRow.rangeSelectObject) { 798 setStartXEndX(param); 799 if (context) { 800 context.globalAlpha = 0.5; 801 context.fillStyle = '#666666'; 802 context.fillRect( 803 TraceRow.rangeSelectObject!.startX!, 804 param.frame.y, 805 TraceRow.rangeSelectObject!.endX! - TraceRow.rangeSelectObject!.startX!, 806 param.frame.height 807 ); 808 context.globalAlpha = 1; 809 } 810 // 绘制线程中方法平均帧率的箭头指示线条 811 if (param.avgRateTxt && param.frameRateList && param.frameRateList.length) { 812 drawAvgFrameRate(param.frameRateList, context, param); 813 } 814 } 815} 816 817function setStartXEndX(params: TraceRow<BaseStruct>): void { 818 TraceRow.rangeSelectObject!.startX = Math.floor( 819 ns2x( 820 TraceRow.rangeSelectObject!.startNS!, 821 TraceRow.range?.startNS ?? 0, 822 TraceRow.range?.endNS ?? 0, 823 TraceRow.range?.totalNS ?? 0, 824 params.frame 825 ) 826 ); 827 TraceRow.rangeSelectObject!.endX = Math.floor( 828 ns2x( 829 TraceRow.rangeSelectObject!.endNS!, 830 TraceRow.range?.startNS ?? 0, 831 TraceRow.range?.endNS ?? 0, 832 TraceRow.range?.totalNS ?? 0, 833 params.frame 834 ) 835 ); 836} 837 838function setAvgRateStartXEndX(rateList: number[], params: TraceRow<BaseStruct>): number[] { 839 let avgRateStartX = Math.floor( 840 ns2x( 841 rateList[0]!, 842 TraceRow.range?.startNS ?? 0, 843 TraceRow.range?.endNS ?? 0, 844 TraceRow.range?.totalNS ?? 0, 845 params.frame 846 ) 847 ); 848 let avgRateEndX = Math.floor( 849 ns2x( 850 rateList[rateList.length - 1]!, 851 TraceRow.range?.startNS ?? 0, 852 TraceRow.range?.endNS ?? 0, 853 TraceRow.range?.totalNS ?? 0, 854 params.frame 855 ) 856 ); 857 return [avgRateStartX, avgRateEndX]; 858} 859 860function setTextXY(rateList: number[], params: TraceRow<BaseStruct>, textWidth: number): number[] { 861 let textX = 862 Math.floor( 863 ns2x( 864 (rateList[0]! + rateList[rateList.length - 1]!) / 2, 865 TraceRow.range?.startNS ?? 0, 866 TraceRow.range?.endNS ?? 0, 867 TraceRow.range?.totalNS ?? 0, 868 params.frame 869 ) 870 ) - 871 textWidth / 2; // @ts-ignore 872 let textY = params.frame.y + 25; 873 return [textX, textY]; 874} 875 876// 转换起始点坐标 877function changeFrameRatePoint(arrList: Array<number>, selectParams: TraceRow<BaseStruct>): number[] { 878 let avgRateStartX = Math.floor( 879 ns2x( 880 arrList[0]!, 881 TraceRow.range?.startNS ?? 0, 882 TraceRow.range?.endNS ?? 0, 883 TraceRow.range?.totalNS ?? 0, 884 selectParams.frame 885 ) 886 ); // 起始坐标 887 let avgRateEndX = Math.floor( 888 ns2x( 889 arrList[arrList.length - 1]!, 890 TraceRow.range?.startNS ?? 0, 891 TraceRow.range?.endNS ?? 0, 892 TraceRow.range?.totalNS ?? 0, 893 selectParams.frame 894 ) 895 ); // 结束坐标 896 return [avgRateStartX, avgRateEndX]; 897} 898 899// 处理文字坐标 900function handleTextCoordinate(arrList: Array<number>, selectParams: TraceRow<BaseStruct>, textWidth: number): number[] { 901 const TEXT_WIDTH_HALF = 2; 902 let textX = Math.floor( 903 ns2x( 904 (arrList[0]! + arrList[arrList.length - 1]!) / 2, 905 TraceRow.range?.startNS ?? 0, 906 TraceRow.range?.endNS ?? 0, 907 TraceRow.range?.totalNS ?? 0, 908 selectParams.frame 909 ) 910 ); //根据帧率范围的中间值转换文本的起始x坐标 911 textX = textX <= textWidth / TEXT_WIDTH_HALF ? textX : textX - textWidth / TEXT_WIDTH_HALF; // @ts-ignore 912 let textY = selectParams.frame.y + 11; 913 if (selectParams.avgRateTxt?.includes('HitchTime')) { 914 // @ts-ignore 915 textY = selectParams.frame.y + 11; 916 } else { 917 // 展开时显示在第二行,折叠显示第一行 918 if (selectParams.funcExpand) { 919 // @ts-ignore 920 textY = selectParams.frame.y + 29; 921 } else { 922 // @ts-ignore 923 textY = selectParams.frame.y + 11; 924 } 925 } 926 return [textX, textY]; 927} 928 929// 绘制平均帧率箭头指示线条 930export function drawAvgFrameRate( 931 arrList: Array<number>, 932 ctx: CanvasRenderingContext2D, 933 selectParams: TraceRow<BaseStruct> 934): void { 935 let rateList: Array<number> = [...new Set(arrList)]; 936 let startX = changeFrameRatePoint(rateList, selectParams)[0]; 937 let endX = changeFrameRatePoint(rateList, selectParams)[1]; 938 const textWidth = ctx.measureText(selectParams.avgRateTxt!).width; 939 940 const textHeight = 25; 941 const padding = 5; 942 let textX = handleTextCoordinate(rateList, selectParams, textWidth)[0]; 943 let textY = handleTextCoordinate(rateList, selectParams, textWidth)[1]; 944 //左移到边界,不画线和文字 945 startX = startX <= 0 ? -100 : startX; 946 endX = endX <= 0 ? -100 : endX; 947 textX = textX <= 0 ? -200 : textX; 948 //右移到边界,不画线和文字 949 const ADD_DISTANCE = 100; 950 textX = textX + textWidth / 2 >= selectParams.frame.width ? 951 selectParams.frame.width + ADD_DISTANCE : textX; 952 startX = startX >= selectParams.frame.width ? 953 selectParams.frame.width + ADD_DISTANCE : startX; 954 endX = endX >= selectParams.frame.width ? 955 selectParams.frame.width + ADD_DISTANCE : endX; 956 957 ctx.lineWidth = 2; 958 ctx.strokeStyle = 'yellow'; 959 ctx.beginPath(); 960 ctx.moveTo(startX, textY); 961 ctx.lineTo(endX, textY); 962 ctx.stroke(); 963 964 const arrowSize = 5.5; 965 const arrowHead = (x: number, y: number, direction: 'left' | 'right'): void => { 966 ctx.beginPath(); 967 const headX = x + (direction === 'left' ? arrowSize : -arrowSize); 968 const headY = y - arrowSize / 2; 969 ctx.moveTo(x, y); 970 ctx.lineTo(headX, headY); 971 ctx.lineTo(headX, y + arrowSize); 972 ctx.closePath(); 973 ctx.fillStyle = 'yellow'; 974 ctx.fill(); 975 }; 976 arrowHead(startX, textY - 1, 'left'); 977 arrowHead(endX, textY - 1, 'right'); 978 979 const TEXT_RECT_PADDING = 2; 980 ctx.fillStyle = 'red'; 981 ctx.fillRect( 982 textX - padding, 983 textY - textHeight / TEXT_RECT_PADDING + padding, 984 textWidth + padding * TEXT_RECT_PADDING, 985 textHeight - padding * TEXT_RECT_PADDING 986 ); 987 988 ctx.fillStyle = 'white'; 989 ctx.fillText(selectParams.avgRateTxt!, textX, textY + 4); 990} 991 992function drawAvgFrameRateArrow( 993 ctx: CanvasRenderingContext2D, 994 textX: number, 995 textY: number, 996 textWidth: number, 997 startX: number, 998 endX: number, 999 avgFrameRate: string 1000): void { 1001 const textHeight = 25; 1002 const padding = 5; 1003 const TEXT_RECT_PADDING = 2; 1004 ctx.fillStyle = 'red'; 1005 ctx.fillRect( 1006 textX - padding, 1007 textY - textHeight + padding, 1008 textWidth + padding * TEXT_RECT_PADDING, 1009 textHeight - padding * TEXT_RECT_PADDING 1010 ); 1011 ctx.lineWidth = 2; 1012 ctx.strokeStyle = 'yellow'; 1013 ctx.beginPath(); 1014 ctx.moveTo(startX, textY); 1015 ctx.lineTo(endX, textY); 1016 ctx.stroke(); 1017 arrowHead(ctx, startX, textY - 1, 'left'); 1018 arrowHead(ctx, endX, textY - 1, 'right'); 1019 ctx.fillStyle = 'white'; 1020 ctx.fillText(avgFrameRate, textX, textY - 8); 1021} 1022 1023const arrowSize = 5.5; 1024const arrowHead = (ctx: CanvasRenderingContext2D, x: number, y: number, direction: 'left' | 'right'): void => { 1025 ctx.beginPath(); 1026 const headX = x + (direction === 'left' ? arrowSize : -arrowSize); 1027 const headY = y - arrowSize / 2; 1028 ctx.moveTo(x, y); 1029 ctx.lineTo(headX, headY); 1030 ctx.lineTo(headX, y + arrowSize); 1031 ctx.closePath(); 1032 ctx.fillStyle = 'yellow'; 1033 ctx.fill(); 1034}; 1035 1036export function drawWakeUp( 1037 wakeUpContext: CanvasRenderingContext2D | undefined | null, 1038 wake: WakeupBean | undefined | null, 1039 startNS: number, 1040 endNS: number, 1041 totalNS: number, 1042 frame: Rect, 1043 selectCpuStruct: CpuStruct | undefined = undefined, 1044 wakeUpCurrentCpu: number | undefined = undefined, 1045 noVerticalLine = false 1046): void { 1047 if (wake && wakeUpContext) { 1048 let x1 = Math.floor(ns2x(wake.wakeupTime || 0, startNS, endNS, totalNS, frame)); 1049 wakeUpContext.beginPath(); 1050 wakeUpContext.lineWidth = 2; 1051 wakeUpContext.fillStyle = '#000000'; 1052 if (x1 > 0 && x1 < frame.x + frame.width) { 1053 if (!noVerticalLine) { 1054 wakeUpContext.moveTo(x1, frame.y); 1055 wakeUpContext.lineTo(x1, frame.y + frame.height); 1056 } 1057 if (wakeUpCurrentCpu === wake.cpu) { 1058 let centerY = Math.floor(frame.y + frame.height / 2); 1059 wakeUpContext.moveTo(x1, centerY - 6); 1060 wakeUpContext.lineTo(x1 + 4, centerY); 1061 wakeUpContext.lineTo(x1, centerY + 6); 1062 wakeUpContext.lineTo(x1 - 4, centerY); 1063 wakeUpContext.lineTo(x1, centerY - 6); 1064 wakeUpContext.fill(); 1065 } 1066 } 1067 if (selectCpuStruct) { 1068 drawWakeUpIfSelect(selectCpuStruct, startNS, endNS, totalNS, frame, wakeUpContext, wake, x1); 1069 } 1070 wakeUpContext.strokeStyle = '#000000'; 1071 wakeUpContext.stroke(); 1072 wakeUpContext.closePath(); 1073 } 1074} 1075 1076function drawWakeUpIfSelect( 1077 selectCpuStruct: CpuStruct, 1078 startNS: number, 1079 endNS: number, 1080 totalNS: number, 1081 frame: Rect, 1082 wakeUpContext: CanvasRenderingContext2D, 1083 wake: unknown, 1084 x1: number 1085): void { 1086 let x2 = Math.floor(ns2x(selectCpuStruct.startTime || 0, startNS, endNS, totalNS, frame)); 1087 let y = frame.y + frame.height - 10; 1088 wakeUpContext.moveTo(x1, y); 1089 wakeUpContext.lineTo(x2, y); 1090 //@ts-ignore 1091 let s = ns2s((selectCpuStruct.startTime || 0) - (wake.wakeupTime || 0)); 1092 let distance = x2 - x1; 1093 if (distance > 12) { 1094 wakeUpContext.moveTo(x1, y); 1095 wakeUpContext.lineTo(x1 + 6, y - 3); 1096 wakeUpContext.moveTo(x1, y); 1097 wakeUpContext.lineTo(x1 + 6, y + 3); 1098 wakeUpContext.moveTo(x2, y); 1099 wakeUpContext.lineTo(x2 - 6, y - 3); 1100 wakeUpContext.moveTo(x2, y); 1101 wakeUpContext.lineTo(x2 - 6, y + 3); 1102 let measure = wakeUpContext.measureText(s); 1103 let tHeight = measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent; 1104 let xStart = x1 + Math.floor(distance / 2 - measure.width / 2); 1105 if (distance > measure.width + 4) { 1106 wakeUpContext.fillStyle = '#ffffff'; 1107 wakeUpContext.fillRect(xStart - 2, y - 4 - tHeight, measure.width + 4, tHeight + 4); 1108 wakeUpContext.font = '10px solid'; 1109 wakeUpContext.fillStyle = '#000000'; 1110 wakeUpContext.textBaseline = 'bottom'; 1111 wakeUpContext.fillText(s, xStart, y - 2); 1112 } 1113 } 1114} 1115 1116const wid = 5; 1117const linkLineColor = '#ff0000'; 1118 1119export function drawLinkLines( 1120 context: CanvasRenderingContext2D, 1121 nodes: PairPoint[][], 1122 tm: TimerShaftElement, 1123 isFavorite: boolean, 1124 favoriteHeight: number 1125): void { 1126 let percentage = 1127 (tm.getRange()!.totalNS - Math.abs(tm.getRange()!.endNS - tm.getRange()!.startNS)) / tm.getRange()!.totalNS; 1128 let maxWidth = tm.getBoundingClientRect().width - 258; 1129 setLinkLinesNodes(nodes, isFavorite, favoriteHeight, maxWidth, context, percentage); 1130} 1131 1132function setLinkLinesNodes( 1133 nodes: PairPoint[][], 1134 isFav: boolean, 1135 favH: number, 1136 max: number, 1137 context: CanvasRenderingContext2D, 1138 perc: number 1139): void { 1140 for (let i = 0; i < nodes.length; i++) { 1141 let it = nodes[i]; 1142 const traceRow0 = it[0].rowEL as TraceRow<BaseStruct>; 1143 const traceRow1 = it[1].rowEL as TraceRow<BaseStruct>; 1144 it[0].y = traceRow0.translateY + it[0].offsetY; 1145 it[1].y = traceRow1.translateY + it[1].offsetY; 1146 let newFirstNode = new PairPoint( 1147 traceRow0, 1148 it[0].x, 1149 it[0].y, 1150 it[0].ns, 1151 it[0].offsetY, 1152 it[0].isRight, 1153 it[0].business 1154 ); 1155 let newSecondNode = new PairPoint( 1156 traceRow1, 1157 it[1].x, 1158 it[1].y, 1159 it[1].ns, 1160 it[1].offsetY, 1161 it[1].isRight, 1162 it[1].business 1163 ); 1164 if (it[0].lineColor) { 1165 newFirstNode.lineColor = it[0].lineColor; 1166 newSecondNode.lineColor = it[0].lineColor; 1167 } 1168 if (it[0].rangeTime) { 1169 newFirstNode.rangeTime = it[0].rangeTime; 1170 } 1171 if (it[0].hidden) { 1172 continue; 1173 } 1174 if (isFav) { 1175 if (traceRow0.collect && traceRow1.collect) { 1176 } else if (!traceRow0.collect && !traceRow1.collect) { 1177 continue; 1178 } else { 1179 traceRow0.collect ? (newSecondNode.y = Math.max(it[1].y + favH, favH)) : 1180 (newFirstNode.y = Math.max(it[0].y + favH, favH)); 1181 } 1182 } else { 1183 if (traceRow0.collect && traceRow1.collect) { 1184 continue; 1185 } else if (!traceRow0.collect && !traceRow1.collect) { 1186 } else { 1187 traceRow0.collect ? (newFirstNode.y = it[0].y - favH) : (newSecondNode.y = it[1].y - favH); 1188 } 1189 } 1190 drawLinesByType(it[0].lineType, newFirstNode, newSecondNode, max, context, perc); 1191 } 1192} 1193 1194function drawLinesByType( 1195 lineType: LineType | undefined, 1196 newFirstNode: PairPoint, 1197 newSecondNode: PairPoint, 1198 maxWidth: number, 1199 context: CanvasRenderingContext2D, 1200 percentage: number 1201): void { 1202 switch (lineType) { 1203 case LineType.brokenLine: 1204 drawBrokenLine([newFirstNode, newSecondNode], maxWidth, context); 1205 break; 1206 case LineType.bezierCurve: 1207 drawBezierCurve([newFirstNode, newSecondNode], maxWidth, context, percentage); 1208 break; 1209 case LineType.straightLine: 1210 drawStraightLine([newFirstNode, newSecondNode], maxWidth, context); 1211 break; 1212 default: 1213 drawBezierCurve([newFirstNode, newSecondNode], maxWidth, context, percentage); 1214 } 1215} 1216 1217function drawBezierCurve( 1218 it: PairPoint[], 1219 maxWidth: number, 1220 context: CanvasRenderingContext2D, 1221 percentage: number 1222): void { 1223 let bezierCurveStart = it[0].x > it[1].x ? it[1] : it[0]; 1224 let bezierCurveEnd = it[0].x > it[1].x ? it[0] : it[1]; 1225 if (bezierCurveStart && bezierCurveEnd) { 1226 //左移到边界,不画线 1227 if (bezierCurveStart.x <= 0) { 1228 bezierCurveStart.x = -100; 1229 } 1230 if (bezierCurveEnd.x <= 0) { 1231 bezierCurveEnd.x = -100; 1232 } 1233 //右移到边界,不画线 1234 if (bezierCurveStart.x >= maxWidth) { 1235 bezierCurveStart.x = maxWidth + 100; 1236 } 1237 if (bezierCurveEnd.x >= maxWidth) { 1238 bezierCurveEnd.x = maxWidth + 100; 1239 } 1240 drawBezierCurveContext(context, bezierCurveStart, bezierCurveEnd, percentage); 1241 } 1242} 1243 1244function drawBezierCurveContext( 1245 context: CanvasRenderingContext2D, 1246 bezierCurveStart: PairPoint, 1247 bezierCurveEnd: PairPoint, 1248 percentage: number 1249): void { 1250 context.beginPath(); 1251 context.lineWidth = 2; 1252 context.fillStyle = linkLineColor; 1253 context.strokeStyle = linkLineColor; 1254 let x0 = bezierCurveStart.x ?? 0; 1255 let y0 = bezierCurveStart.y ?? 0; 1256 let x3 = bezierCurveEnd.x ?? 0; 1257 let y3 = bezierCurveEnd.y ?? 0; 1258 let x2 = bezierCurveEnd.isRight ? x3 - 100 * percentage : x3 + 100 * percentage; 1259 let y2 = y3 - 40 * percentage; 1260 let x1 = bezierCurveStart.isRight ? x0 - 100 * percentage : x0 + 100 * percentage; 1261 let y1 = y0 + 40 * percentage; 1262 if (!bezierCurveStart.isRight) { 1263 x0 -= 5; 1264 } 1265 context.moveTo(x0, y0); 1266 if (bezierCurveStart.isRight) { 1267 context.lineTo(x0 - wid, y0 + wid); 1268 context.moveTo(x0, y0); 1269 context.lineTo(x0 - wid, y0 - wid); 1270 } else { 1271 context.lineTo(x0 + wid, y0 + wid); 1272 context.moveTo(x0, y0); 1273 context.lineTo(x0 + wid, y0 - wid); 1274 } 1275 context.moveTo(x0, y0); 1276 context.bezierCurveTo(x1, y1, x2, y2, x3, y3); 1277 context.moveTo(x3, y3); 1278 if (bezierCurveEnd.isRight) { 1279 context.lineTo(x3 - wid, y3 + wid); 1280 context.moveTo(x3, y3); 1281 context.lineTo(x3 - wid, y3 - wid); 1282 } else { 1283 context.lineTo(x3 + wid, y3 + wid); 1284 context.moveTo(x3, y3); 1285 context.lineTo(x3 + wid, y3 - wid); 1286 } 1287 context.moveTo(x3, y3); 1288 context.stroke(); 1289 context.closePath(); 1290} 1291 1292function drawStraightLine(it: PairPoint[], maxWidth: number, context: CanvasRenderingContext2D): void { 1293 let startPoint = it[0].x > it[1].x ? it[1] : it[0]; 1294 let endPoint = it[0].x > it[1].x ? it[0] : it[1]; 1295 let arrowSize = 8; 1296 if (startPoint && endPoint) { 1297 //左移到边界,不画线 1298 if (startPoint.x <= 0) { 1299 startPoint.x = -100; 1300 } 1301 if (endPoint.x <= 0) { 1302 endPoint.x = -100; 1303 } 1304 //右移到边界,不画线 1305 if (startPoint.x >= maxWidth) { 1306 startPoint.x = maxWidth + 100; 1307 } 1308 if (endPoint.x >= maxWidth) { 1309 endPoint.x = maxWidth + 100; 1310 } 1311 drawArrow(context, startPoint, endPoint, arrowSize); 1312 } 1313} 1314 1315function drawArrow( 1316 context: CanvasRenderingContext2D, 1317 startPoint: PairPoint, 1318 endPoint: PairPoint, 1319 arrowSize: number 1320): void { 1321 context.beginPath(); 1322 context.lineWidth = 2; 1323 context.strokeStyle = '#0000FF'; 1324 context.moveTo(startPoint.x, startPoint.y); 1325 context.lineTo(endPoint.x, endPoint.y); 1326 // 绘制箭头 1327 let arrow = Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x); 1328 context.moveTo(endPoint.x, endPoint.y); 1329 context.lineTo( 1330 endPoint.x - arrowSize * Math.cos(arrow - Math.PI / 6), 1331 endPoint.y - arrowSize * Math.sin(arrow - Math.PI / 6) 1332 ); 1333 context.moveTo(endPoint.x, endPoint.y); 1334 context.lineTo( 1335 endPoint.x - arrowSize * Math.cos(arrow + Math.PI / 6), 1336 endPoint.y - arrowSize * Math.sin(arrow + Math.PI / 6) 1337 ); 1338 // 绘制另一端箭头 1339 arrow = Math.atan2(startPoint.y - endPoint.y, startPoint.x - endPoint.x); 1340 context.moveTo(startPoint.x, startPoint.y); 1341 context.lineTo( 1342 startPoint.x - arrowSize * Math.cos(arrow - Math.PI / 6), 1343 startPoint.y - arrowSize * Math.sin(arrow - Math.PI / 6) 1344 ); 1345 context.moveTo(startPoint.x, startPoint.y); 1346 context.lineTo( 1347 startPoint.x - arrowSize * Math.cos(arrow + Math.PI / 6), 1348 startPoint.y - arrowSize * Math.sin(arrow + Math.PI / 6) 1349 ); 1350 context.stroke(); 1351 context.closePath(); 1352} 1353 1354function drawBrokenLine(it: PairPoint[], maxWidth: number, context: CanvasRenderingContext2D): void { 1355 let brokenLineStart = it[0].x > it[1].x ? it[1] : it[0]; 1356 let brokenLineEnd = it[0].x > it[1].x ? it[0] : it[1]; 1357 if (brokenLineStart && brokenLineEnd) { 1358 if (brokenLineStart.x <= 0) { 1359 brokenLineStart.x = -100; 1360 } 1361 if (brokenLineEnd.x <= 0) { 1362 brokenLineEnd.x = -100; 1363 } 1364 if (brokenLineStart.x >= maxWidth) { 1365 brokenLineStart.x = maxWidth + 100; 1366 } 1367 if (brokenLineEnd.x >= maxWidth) { 1368 brokenLineEnd.x = maxWidth + 100; 1369 } 1370 drawBrokenLineContext(context, brokenLineStart, brokenLineEnd); 1371 } 1372} 1373 1374function drawBrokenLineContext( 1375 context: CanvasRenderingContext2D, 1376 brokenLineStart: PairPoint, 1377 brokenLineEnd: PairPoint 1378): void { 1379 context.beginPath(); 1380 context.lineWidth = 2; 1381 context.fillStyle = brokenLineStart.lineColor ? brokenLineStart.lineColor : '#46B1E3'; 1382 context.strokeStyle = brokenLineStart.lineColor ? brokenLineStart.lineColor : '#46B1E3'; 1383 let x0 = brokenLineStart.x ?? 0; 1384 let y0 = brokenLineStart.y ?? 0; 1385 let y2 = brokenLineEnd.y ?? 0; 1386 let x2 = brokenLineEnd.x ?? 0; 1387 let x1; 1388 let y1; 1389 let leftEndpointX; 1390 let leftEndpointY; 1391 let rightEndpointX; 1392 let rightEndpointY; 1393 if (brokenLineStart.y < brokenLineEnd.y) { 1394 x1 = brokenLineStart.x ?? 0; 1395 y1 = brokenLineEnd.y ?? 0; 1396 leftEndpointX = x2 - wid; 1397 leftEndpointY = y2 - wid; 1398 rightEndpointX = x2 - wid; 1399 rightEndpointY = y2 + wid; 1400 } else { 1401 // @ts-ignore 1402 x2 = brokenLineEnd.x - wid ?? 0; 1403 // @ts-ignore 1404 x1 = brokenLineEnd.x - wid ?? 0; 1405 y1 = brokenLineStart.y ?? 0; 1406 leftEndpointX = x2 - wid; 1407 leftEndpointY = y2 + wid; 1408 rightEndpointX = x2 + wid; 1409 rightEndpointY = y2 + wid; 1410 } 1411 x1 = drawDistributedLineTime(brokenLineStart.business, brokenLineStart.rangeTime!, [x0, y0, x1, y1, x2, y2], context); 1412 context.moveTo(x0 - 2, y0); 1413 context.lineTo(x1, y1); 1414 context.lineTo(x2, y2); 1415 context.stroke(); 1416 context.closePath(); 1417 context.beginPath(); 1418 context.lineWidth = 2; 1419 context.fillStyle = brokenLineStart.lineColor ? brokenLineStart.lineColor : '#46B1E3'; 1420 context.strokeStyle = brokenLineStart.lineColor ? brokenLineStart.lineColor : '#46B1E3'; 1421 context.moveTo(x2, y2); 1422 context.lineTo(leftEndpointX, leftEndpointY); 1423 context.lineTo(rightEndpointX, rightEndpointY); 1424 context.lineTo(x2, y2); 1425 context.fill(); 1426 context.closePath(); 1427} 1428 1429let loadingText = 'Loading...'; 1430let loadingTextWidth = 0; 1431let loadingBackground = '#f1f1f1'; 1432let loadingFont = 'bold 11pt Arial'; 1433let loadingFontColor = '#696969'; 1434 1435function drawDistributedLineTime( 1436 business: string, 1437 rangeTime: string, 1438 [x0, y0, x1, y1, x2, y2]: [number, number, number, number, number, number], 1439 context: CanvasRenderingContext2D 1440): number { 1441 if (business === 'distributed') { 1442 if (y0 === y1) { 1443 drawString(context, rangeTime, 0, 1444 new Rect(x0, y0 + 2, x1 - x0, 12), { textMetricsWidth: undefined }); 1445 } else { 1446 drawString(context, rangeTime, 0, 1447 new Rect(x1, y1 + 2, x2 - x1, 12), { textMetricsWidth: undefined }); 1448 x1 = x1 - 2; 1449 } 1450 } 1451 return x1; 1452} 1453 1454export function drawLoadingFrame( 1455 ctx: CanvasRenderingContext2D, 1456 list: Array<unknown>, 1457 traceRow: unknown, 1458 sort: boolean = false 1459): void { 1460 const row = traceRow as TraceRow<BaseStruct>; 1461 ctx.beginPath(); 1462 ctx.clearRect(0, 0, row.frame.width, row.frame.height); 1463 drawLines(ctx, TraceRow.range?.xs || [], row.frame.height, '#dadada'); 1464 drawVSync(ctx, row.frame.width, row.frame.height); 1465 if (row.loadingFrame) { 1466 if (loadingTextWidth === 0) { 1467 loadingTextWidth = ctx.measureText(loadingText).width; 1468 } 1469 let firstPx = nsx(row.loadingPin1, row.frame.width); 1470 let lastPx = nsx(row.loadingPin2, row.frame.width); 1471 ctx.fillStyle = loadingBackground; 1472 ctx.fillRect(0, 1, firstPx, row.frame.height - 2); 1473 ctx.fillRect(lastPx, 1, row.frame.width - lastPx, row.frame.height - 2); 1474 ctx.fillStyle = loadingFontColor; 1475 if (firstPx > loadingTextWidth) { 1476 ctx.fillText(loadingText, (firstPx - loadingTextWidth) / 2, row.frame.height / 2); 1477 } 1478 if (row.frame.width - lastPx > loadingTextWidth) { 1479 ctx.fillText(loadingText, lastPx + (row.frame.width - lastPx) / 2 - loadingTextWidth / 2, row.frame.height / 2); 1480 } 1481 } 1482 ctx.closePath(); 1483} 1484 1485export function drawString( 1486 ctx: CanvasRenderingContext2D, 1487 str: string, 1488 textPadding: number, 1489 frame: Rect, 1490 data: unknown 1491): void { 1492 //@ts-ignore 1493 if (data.textMetricsWidth === undefined) { 1494 //@ts-ignore 1495 data.textMetricsWidth = ctx.measureText(str); 1496 } 1497 //@ts-ignore 1498 const textMetricsWidth = (data.textMetricsWidth as TextMetrics).width; 1499 const yPos = 1.5; 1500 let charWidth = Math.round(textMetricsWidth / str.length); 1501 let fillTextWidth = frame.width - textPadding * 2; 1502 if (textMetricsWidth < fillTextWidth) { 1503 let x2 = Math.floor(frame.width / 2 - textMetricsWidth / 2 + frame.x + textPadding); 1504 ctx.fillText(str, x2, Math.floor(frame.y + frame.height / yPos), fillTextWidth); 1505 } else { 1506 if (fillTextWidth >= charWidth) { 1507 let chatNum = fillTextWidth / charWidth; 1508 let x1 = frame.x + textPadding; 1509 1510 if (chatNum < 2) { 1511 ctx.fillText(str.substring(0, 1), x1, Math.floor(frame.y + frame.height / yPos), fillTextWidth); 1512 } else { 1513 ctx.fillText( 1514 `${str.substring(0, chatNum - 1)}...`, 1515 x1, 1516 Math.floor(frame.y + frame.height / yPos), 1517 fillTextWidth 1518 ); 1519 } 1520 } 1521 } 1522} 1523 1524export function drawFunString( 1525 ctx: CanvasRenderingContext2D, 1526 str: string, 1527 textPadding: number, 1528 frame: Rect, 1529 data: FuncStruct 1530): void { 1531 if (data.textMetricsWidth === undefined) { 1532 data.textMetricsWidth = ctx.measureText(str).width; 1533 } 1534 let charWidth = Math.round(data.textMetricsWidth / str.length); 1535 let fillTextWidth = frame.width - textPadding * 2; 1536 if (data.textMetricsWidth < fillTextWidth) { 1537 let x2 = Math.floor(frame.width / 2 - data.textMetricsWidth / 2 + frame.x + textPadding); 1538 ctx.fillText(str, x2, Math.floor(data.frame!.height * (data.depth! + 0.5) + 3), fillTextWidth); 1539 } else { 1540 if (fillTextWidth >= charWidth) { 1541 let chatNum = fillTextWidth / charWidth; 1542 let x1 = frame.x + textPadding; 1543 if (chatNum < 2) { 1544 ctx.fillText(str.substring(0, 1), x1, Math.floor(data.frame!.height * (data.depth! + 0.5) + 3), fillTextWidth); 1545 } else { 1546 ctx.fillText( 1547 `${str.substring(0, chatNum - 1)}...`, 1548 x1, 1549 Math.floor(data.frame!.height * (data.depth! + 0.5) + 3), 1550 fillTextWidth 1551 ); 1552 } 1553 } 1554 } 1555} 1556 1557export function hiPerf( 1558 arr: Array<HiPerfStruct>, 1559 arr2: Array<HiPerfStruct>, 1560 res: Array<HiPerfStruct>, 1561 startNS: number, 1562 endNS: number, 1563 frame: Rect, 1564 groupBy10MS: boolean, 1565 use: boolean 1566): void { 1567 if (use && res.length > 0) { 1568 setFrameByRes(res, startNS, endNS, frame); 1569 return; 1570 } 1571 res.length = 0; 1572 if (arr) { 1573 setFrameByArr(arr, arr2, res, startNS, endNS, frame, groupBy10MS); 1574 } 1575} 1576 1577function setFrameByRes(res: Array<HiPerfStruct>, startNS: number, endNS: number, frame: Rect): void { 1578 let pns = (endNS - startNS) / frame.width; 1579 let y = frame.y; 1580 for (let i = 0; i < res.length; i++) { 1581 let item = res[i]; 1582 if ((item.startNS || 0) + (item.dur || 0) > startNS && (item.startNS || 0) < endNS) { 1583 if (!item.frame) { 1584 item.frame = new Rect(0, 0, 0, 0); 1585 item.frame.y = y; 1586 } 1587 item.frame.height = item.height!; 1588 HiPerfStruct.setFrame(item, pns, startNS, endNS, frame); 1589 } else { 1590 item.frame = undefined; 1591 } 1592 } 1593} 1594 1595function setFrameByArr( 1596 arr: Array<HiPerfStruct>, 1597 arr2: Array<HiPerfStruct>, 1598 res: Array<HiPerfStruct>, 1599 startNS: number, 1600 endNS: number, 1601 frame: Rect, 1602 groupBy10MS: boolean 1603): void { 1604 let list = groupBy10MS ? arr2 : arr; 1605 let pns = (endNS - startNS) / frame.width; 1606 let y = frame.y; 1607 for (let i = 0, len = list.length; i < len; i++) { 1608 let it = list[i]; 1609 if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) { 1610 if (!list[i].frame) { 1611 list[i].frame = new Rect(0, 0, 0, 0); 1612 list[i].frame!.y = y; 1613 } 1614 list[i].frame!.height = it.height!; 1615 HiPerfStruct.setFrame(list[i], pns, startNS, endNS, frame); 1616 setResultArr(groupBy10MS, list, i, res); 1617 } 1618 } 1619} 1620 1621function setResultArr(groupBy10MS: boolean, list: Array<HiPerfStruct>, i: number, res: Array<HiPerfStruct>): void { 1622 const itemI = list[i]; 1623 const itemBeforeI = list[i - 1]; 1624 if (itemI.frame && itemBeforeI.frame) { 1625 if (groupBy10MS) { 1626 let flag: boolean = 1627 i > 0 && 1628 (itemBeforeI.frame.x || 0) === (itemI.frame.x || 0) && 1629 (itemBeforeI.frame.width || 0) === (itemI.frame.width || 0) && 1630 (itemBeforeI.frame.height || 0) === (itemI.frame.height || 0); 1631 if (!flag) { 1632 res.push(itemI); 1633 } 1634 } else { 1635 if (!(i > 0 && Math.abs((itemBeforeI.frame.x || 0) - (itemI.frame.x || 0)) < 4)) { 1636 res.push(itemI); 1637 } 1638 } 1639 } 1640} 1641 1642export function hiPerf2(filter: Array<HiPerfStruct>, startNS: number, endNS: number, frame: Rect): void { 1643 if (filter.length > 0) { 1644 let pns = (endNS - startNS) / frame.width; 1645 let y = frame.y; 1646 for (let i = 0; i < filter.length; i++) { 1647 let it = filter[i]; 1648 if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) { 1649 if (!it.frame) { 1650 it.frame = new Rect(0, 0, 0, 0); 1651 it.frame.y = y; 1652 } 1653 it.frame.height = it.height!; 1654 HiPerfStruct.setFrame(it, pns, startNS, endNS, frame); 1655 } else { 1656 it.frame = undefined; 1657 } 1658 } 1659 return; 1660 } 1661} 1662 1663export class HiPerfStruct extends BaseStruct { 1664 static hoverStruct: HiPerfStruct | undefined; 1665 static selectStruct: HiPerfStruct | undefined; 1666 static bottomFindCount: number = 0; 1667 id: number | undefined; 1668 callchain_id: number | undefined; 1669 timestamp: number | undefined; 1670 thread_id: number | undefined; 1671 event_count: number | undefined; 1672 event_type_id: number | undefined; 1673 cpu_id: number | undefined; 1674 thread_state: string | undefined; 1675 startNS: number | undefined; 1676 endNS: number | undefined; 1677 dur: number | undefined; 1678 height: number | undefined; 1679 eventCount: number | undefined; 1680 sampleCount: number | undefined; 1681 1682 static drawRoundRectPath(cxt: Path2D, x: number, y: number, width: number, height: number, radius: number): void { 1683 cxt.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2); 1684 cxt.lineTo(x + radius, y + height); 1685 cxt.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI); 1686 cxt.lineTo(x, y + radius); 1687 cxt.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2); 1688 cxt.lineTo(x + width - radius, y); 1689 cxt.arc(x + width - radius, y + radius, radius, (Math.PI * 3) / 2, Math.PI * 2); 1690 cxt.lineTo(x + width, y + height - radius); 1691 cxt.moveTo(x + width / 3, y + height / 5); 1692 cxt.lineTo(x + width / 3, y + (height / 5) * 4); 1693 cxt.moveTo(x + width / 3, y + height / 5); 1694 cxt.bezierCurveTo( 1695 x + width / 3 + 7, 1696 y + height / 5 - 2, 1697 x + width / 3 + 7, 1698 y + height / 5 + 6, 1699 x + width / 3, 1700 y + height / 5 + 4 1701 ); 1702 } 1703 1704 static draw( 1705 ctx: CanvasRenderingContext2D, 1706 normalPath: Path2D, 1707 specPath: Path2D, 1708 data: HiPerfStruct, 1709 groupBy10MS: boolean, 1710 textMetrics?: TextMetrics 1711 ): void { 1712 if (data.frame) { 1713 if (groupBy10MS) { 1714 let width = data.frame.width; 1715 normalPath.rect(data.frame.x, 40 - (data.height || 0), width, data.height || 0); 1716 } else { 1717 data.frame.width > 4 ? (data.frame.width = 4) : (data.frame.width = data.frame.width); 1718 let path = data.callchain_id === -1 ? specPath : normalPath; 1719 path.moveTo(data.frame.x + 7, 20); 1720 if (textMetrics) { 1721 ctx.fillText('', data.frame.x - textMetrics!.width / 2, 26); //℗©®℗® 1722 } else { 1723 HiPerfStruct.drawRoundRectPath(path, data.frame.x - 7, 20 - 7, 14, 14, 3); 1724 } 1725 path.moveTo(data.frame.x, 27); 1726 path.lineTo(data.frame.x, 33); 1727 } 1728 } 1729 } 1730 1731 static drawSpecialPath(ctx: CanvasRenderingContext2D, specPath: Path2D): void { 1732 ctx.strokeStyle = '#9fafc4'; 1733 ctx.globalAlpha = 0.5; 1734 ctx.stroke(specPath); 1735 ctx.globalAlpha = 1; 1736 } 1737 1738 static setFrame(node: HiPerfStruct, pns: number, startNS: number, endNS: number, frame: Rect): void { 1739 if (!node.frame) { 1740 return; 1741 } 1742 if ((node.startNS || 0) < startNS) { 1743 node.frame.x = 0; 1744 } else { 1745 node.frame.x = Math.floor(((node.startNS || 0) - startNS) / pns); 1746 } 1747 if ((node.startNS || 0) + (node.dur || 0) > endNS) { 1748 node.frame.width = frame.width - node.frame.x; 1749 } else { 1750 node.frame.width = Math.ceil(((node.startNS || 0) + (node.dur || 0) - startNS) / pns - node.frame.x); 1751 } 1752 if (node.frame.width < 1) { 1753 node.frame.width = 1; 1754 } 1755 } 1756 1757 static groupBy10MS( 1758 groupArray: Array<HiPerfStruct>, 1759 intervalPerf: number, 1760 maxCpu?: number | undefined, 1761 usage?: boolean, 1762 event?: number 1763 ): Array<HiPerfStruct> { 1764 let maxEventCount = 0; 1765 let obj = filterGroupArray(groupArray, maxEventCount, usage, event); 1766 let arr = []; 1767 for (let aKey in obj) { 1768 let ns = parseInt(aKey); 1769 let height: number = 0; 1770 if (usage) { 1771 if (maxCpu !== undefined) { 1772 //@ts-ignore 1773 height = Math.floor((obj[aKey].sampleCount / (10 / intervalPerf) / maxCpu) * 40); 1774 } else { 1775 //@ts-ignore 1776 height = Math.floor((obj[aKey].sampleCount / (10 / intervalPerf)) * 40); 1777 } 1778 } else { 1779 //@ts-ignore 1780 height = Math.floor((obj[aKey].eventCount / maxEventCount) * 40); 1781 } 1782 arr.push({ 1783 startNS: ns, 1784 dur: 10_000_000, 1785 //@ts-ignore 1786 eventCount: obj[aKey].eventCount, 1787 //@ts-ignore 1788 sampleCount: obj[aKey].sampleCount, 1789 height: height, 1790 }); 1791 } 1792 return arr as HiPerfStruct[]; 1793 } 1794} 1795 1796function filterGroupArray( 1797 groupArray: Array<HiPerfStruct>, 1798 maxEventCount: number, 1799 usage?: boolean, 1800 event?: number 1801): HiPerfStruct { 1802 const map = groupArray.map((it) => { 1803 //@ts-ignore 1804 it.timestamp_group = Math.trunc(it.startNS / 10_000_000) * 10_000_000; 1805 return it; 1806 }); 1807 const reduce = map.reduce((pre: HiPerfStruct, current: HiPerfStruct) => { 1808 if (usage || current.event_type_id === event || event === -1) { 1809 //@ts-ignore 1810 if (pre[current.timestamp_group]) { 1811 //@ts-ignore 1812 pre[current.timestamp_group].sampleCount += 1; 1813 //@ts-ignore 1814 pre[current.timestamp_group].eventCount += current.event_count; 1815 } else { 1816 //@ts-ignore 1817 pre[current.timestamp_group] = { 1818 sampleCount: 1, 1819 eventCount: current.event_count, 1820 }; 1821 } 1822 //@ts-ignore 1823 maxEventCount = Math.max(pre[current.timestamp_group].eventCount, maxEventCount); 1824 } 1825 return pre; 1826 }, new HiPerfStruct()); 1827 return reduce; 1828} 1829 1830function setMemFrame( 1831 node: ProcessMemStruct, 1832 padding: number, 1833 startNS: number, 1834 endNS: number, 1835 totalNS: number, 1836 frame: Rect 1837): void { 1838 let x1: number; 1839 let x2: number; 1840 if ((node.startTime || 0) <= startNS) { 1841 x1 = 0; 1842 } else { 1843 x1 = ns2x(node.startTime || 0, startNS, endNS, totalNS, frame); 1844 } 1845 if ((node.startTime || 0) + (node.duration || 0) >= endNS) { 1846 x2 = frame.width; 1847 } else { 1848 x2 = ns2x((node.startTime || 0) + (node.duration || 0), startNS, endNS, totalNS, frame); 1849 } 1850 let getV: number = x2 - x1 <= 1 ? 1 : x2 - x1; 1851 if (!node.frame) { 1852 node.frame = new Rect(0, 0, 0, 0); 1853 } 1854 node.frame.x = Math.floor(x1); 1855 node.frame.y = Math.floor(frame.y + padding); 1856 node.frame.width = Math.ceil(getV); 1857 node.frame.height = Math.floor(frame.height - padding * 2); 1858} 1859 1860export function mem( 1861 list: Array<unknown>, 1862 memFilter: Array<unknown>, 1863 startNS: number, 1864 endNS: number, 1865 totalNS: number, 1866 frame: Rect, 1867 use: boolean 1868): void { 1869 if (use && memFilter.length > 0) { 1870 for (let i = 0, len = memFilter.length; i < len; i++) { 1871 if ( 1872 //@ts-ignore 1873 (memFilter[i].startTime || 0) + (memFilter[i].duration || 0) > startNS && 1874 //@ts-ignore 1875 (memFilter[i].startTime || 0) < endNS 1876 ) { 1877 //@ts-ignore 1878 setMemFrame(memFilter[i], 5, startNS, endNS, totalNS, frame); 1879 } else { 1880 //@ts-ignore 1881 memFilter[i].frame = undefined; 1882 } 1883 } 1884 return; 1885 } 1886 memFilter.length = 0; 1887 //@ts-ignore 1888 setMemFilter(list, memFilter, startNS, endNS, totalNS, frame); 1889} 1890 1891function setMemFilter( 1892 list: Array<ProcessMemStruct>, 1893 memFilter: Array<ProcessMemStruct>, 1894 startNS: number, 1895 endNS: number, 1896 totalNS: number, 1897 frame: Rect 1898): void { 1899 if (list) { 1900 for (let i = 0, len = list.length; i < len; i++) { 1901 let it = list[i]; 1902 if ((it.startTime || 0) + (it.duration || 0) > startNS && (it.startTime || 0) < endNS) { 1903 setMemFrame(list[i], 5, startNS, endNS, totalNS, frame); 1904 if ( 1905 i > 0 && 1906 (list[i - 1].frame?.x || 0) === (list[i].frame?.x || 0) && 1907 (list[i - 1].frame?.width || 0) === (list[i].frame?.width || 0) 1908 ) { 1909 } else { 1910 memFilter.push(list[i]); 1911 } 1912 } 1913 } 1914 } 1915} 1916 1917export function drawWakeUpList( 1918 wakeUpListContext: CanvasRenderingContext2D | undefined | null, 1919 wake: WakeupBean | undefined | null, 1920 startNS: number, 1921 endNS: number, 1922 totalNS: number, 1923 frame: Rect, 1924 wakeup: WakeupBean | undefined = undefined, 1925 currentCpu: number | undefined | null = undefined, 1926 noVerticalLine = false 1927): void { 1928 if (!wakeUpListContext) { 1929 return; 1930 } 1931 if (wake) { 1932 let x1 = Math.floor(ns2x(wake.wakeupTime || 0, startNS, endNS, totalNS, frame)); 1933 wakeUpListContext.beginPath(); 1934 wakeUpListContext.lineWidth = 2; 1935 wakeUpListContext.fillStyle = '#000000'; 1936 if (x1 > 0 && x1 < frame.x + frame.width) { 1937 if (!noVerticalLine) { 1938 wakeUpListContext.moveTo(x1, frame.y); 1939 wakeUpListContext.lineTo(x1, frame.y + frame.height); 1940 } 1941 if (currentCpu === wake.cpu) { 1942 let centerY = Math.floor(frame.y + frame.height / 2); 1943 wakeUpListContext.moveTo(x1, centerY - 6); 1944 wakeUpListContext.lineTo(x1 + 4, centerY); 1945 wakeUpListContext.lineTo(x1, centerY + 6); 1946 wakeUpListContext.lineTo(x1 - 4, centerY); 1947 wakeUpListContext.lineTo(x1, centerY - 6); 1948 wakeUpListContext.fill(); 1949 } 1950 } 1951 if (wakeup) { 1952 drawWakeUpListIfWakeUp(wakeUpListContext, wake, startNS, endNS, totalNS, frame, wakeup, x1); 1953 } 1954 wakeUpListContext.strokeStyle = '#000000'; 1955 wakeUpListContext.stroke(); 1956 wakeUpListContext.closePath(); 1957 } 1958} 1959 1960function drawWakeUpListIfWakeUp( 1961 wakeUpListContext: CanvasRenderingContext2D, 1962 wake: WakeupBean, 1963 startNS: number, 1964 endNS: number, 1965 totalNS: number, 1966 frame: Rect, 1967 wakeup: WakeupBean, 1968 x1: number 1969): void { 1970 let x2 = Math.floor(ns2x(wakeup.ts || 0, startNS, endNS, totalNS, frame)); 1971 let y = frame.y + frame.height - 10; 1972 wakeUpListContext.moveTo(x1, y); 1973 wakeUpListContext.lineTo(x2, y); 1974 wakeUpListContext.moveTo(x2, y - 25); 1975 wakeUpListContext.lineTo(x2, y + 5); 1976 let s = ns2s((wakeup.ts || 0) - (wake.wakeupTime || 0)); 1977 let wakeUpListDistance = x2 - x1; 1978 if (wakeUpListDistance > 12) { 1979 wakeUpListContext.moveTo(x1, y); 1980 wakeUpListContext.lineTo(x1 + 6, y - 3); 1981 wakeUpListContext.moveTo(x1, y); 1982 wakeUpListContext.lineTo(x1 + 6, y + 3); 1983 wakeUpListContext.moveTo(x2, y); 1984 wakeUpListContext.lineTo(x2 - 6, y - 3); 1985 wakeUpListContext.moveTo(x2, y); 1986 wakeUpListContext.lineTo(x2 - 6, y + 3); 1987 let measure = wakeUpListContext.measureText(s); 1988 let tHeight = measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent; 1989 let xStart = x1 + Math.floor(wakeUpListDistance / 2 - measure.width / 2); 1990 if (wakeUpListDistance > measure.width + 4) { 1991 wakeUpListContext.fillStyle = '#ffffff'; 1992 wakeUpListContext.fillRect(xStart - 2, y - 4 - tHeight, measure.width + 4, tHeight + 4); 1993 wakeUpListContext.font = '10px solid'; 1994 wakeUpListContext.fillStyle = '#000000'; 1995 wakeUpListContext.textBaseline = 'bottom'; 1996 wakeUpListContext.fillText(s, xStart, y - 2); 1997 } 1998 } 1999} 2000interface SearchNode { 2001 symbolName?: string; 2002 children: SearchNode[]; 2003 searchShow?: boolean; 2004 isSearch?: boolean; 2005 parent?: SearchNode; 2006} 2007 2008export function findSearchNode(data: unknown[], search: string, parentSearch: boolean): void { 2009 search = search.toLocaleLowerCase(); 2010 data.forEach((nodeIt) => { 2011 const node = nodeIt as SearchNode; 2012 if ((node.symbolName && node.symbolName.toLocaleLowerCase().includes(search) && search !== '') || parentSearch) { 2013 node.searchShow = true; 2014 node.isSearch = node.symbolName !== undefined && node.symbolName.toLocaleLowerCase().includes(search); 2015 let parentNode = node.parent; 2016 while (parentNode && !parentNode.searchShow) { 2017 parentNode.searchShow = true; 2018 parentNode = parentNode.parent; 2019 } 2020 if (node.isSearch && search !== '') { 2021 HiPerfStruct.bottomFindCount += 1; 2022 } 2023 } else { 2024 node.searchShow = false; 2025 node.isSearch = false; 2026 } 2027 if (node.children.length > 0) { 2028 findSearchNode(node.children, search, node.searchShow); 2029 } 2030 }); 2031} 2032 2033// draw prio curve 2034// @ts-ignore 2035export function prioClickHandlerFun(param: unknown, row: TraceRow<unknown>, threadFilter: Array<ThreadStruct>, arr: unknown, oldVal: number): void { 2036 //@ts-ignore 2037 let maxCount = Math.max(...param.map((obj: unknown) => obj.count)); 2038 //@ts-ignore 2039 let maxCountPrio = param.find((obj: unknown) => obj.count === maxCount).prio;//找出出现次数最多的优先级,为中位值 2040 //@ts-ignore 2041 let maxPrioDiff = Math.max(...param.map((obj: unknown) => Math.abs(obj.prio - Number(maxCountPrio))));//找出与中位值的最大diff 2042 let maxPointInterval = Math.ceil(maxPrioDiff / 4);//diff分成4份,每一份占多少px 2043 2044 for (let i = 0; i < threadFilter.length; i++) { 2045 const item = threadFilter[i]; 2046 const preItem = threadFilter[i - 1]; 2047 //给原始数据添加prio值 2048 let slice = Utils.getInstance().getSchedSliceMap().get(`${item.id}-${item.startTime}`); 2049 if (slice) { 2050 item.prio = slice!.priority; 2051 } 2052 //合并prio值相同的项提高画图速度 2053 if ( 2054 item.prio && 2055 (oldVal !== item.prio || i === threadFilter.length - 2 || i === threadFilter.length - 1) 2056 ) { 2057 configCurveY(row, item, maxCountPrio, maxPointInterval); 2058 //处理prio值变化前的 2059 if (i !== 0) { 2060 configCurveY(row, preItem, maxCountPrio, maxPointInterval); 2061 //@ts-ignore 2062 arr.push(preItem); 2063 } 2064 //@ts-ignore 2065 arr.push(item); 2066 oldVal = item.prio; 2067 } 2068 } 2069} 2070 2071//确定曲线波动时的y轴 2072//@ts-ignore 2073function configCurveY(row: TraceRow<unknown>, item: ThreadStruct, maxCountPrio: number, maxPointInterval: number): void { 2074 if (item.prio === Number(maxCountPrio)) { 2075 item.curveFloatY = 3 + 12 / 2 + row.translateY; 2076 } else if (item.prio! > Number(maxCountPrio)) { 2077 let prioHeight = Math.floor((item.prio! - Number(maxCountPrio)) / maxPointInterval) * 2; 2078 item.curveFloatY = 3 + 12 / 2 - prioHeight + row.translateY; 2079 } else if (item.prio! < Number(maxCountPrio)) { 2080 let prioHeight = Math.floor((Number(maxCountPrio) - item.prio!) / maxPointInterval) * 2; 2081 item.curveFloatY = 3 + 12 / 2 + prioHeight + row.translateY; 2082 } 2083} 2084 2085export function drawThreadCurve(context: CanvasRenderingContext2D, threadFilter: ThreadStruct, nextFilter: ThreadStruct): void { 2086 // 绘制曲线 2087 if (threadFilter.frame && nextFilter.frame) { 2088 let p1 = threadFilter; 2089 let p2 = nextFilter; 2090 let diff = p2.curveFloatY! >= p1.curveFloatY! ? p2.curveFloatY! - p1.curveFloatY! : p1.curveFloatY! - p2.curveFloatY!; 2091 let cp1x = p1.frame!.x + (p2.frame!.x - p1.frame!.x) / 5; 2092 let cp1y = p2.curveFloatY! >= p1.curveFloatY! ? p1.curveFloatY! - diff / 5 : p1.curveFloatY! + diff / 5; 2093 let cp2x = p2.frame!.x - (p2.frame!.x - p1.frame!.x) / 5; 2094 let cp2y = p2.curveFloatY! >= p1.curveFloatY! ? p2.curveFloatY! + diff / 5 : p2.curveFloatY! - diff / 5; 2095 context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.frame!.x, p2.curveFloatY!); 2096 context.lineWidth = 1; 2097 context.strokeStyle = '#ffc90e'; 2098 context.lineCap = 'round'; 2099 } 2100 context.stroke(); 2101} 2102