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 x2 = brokenLineEnd.x - wid ?? 0; 1401 x1 = brokenLineEnd.x - wid ?? 0; 1402 y1 = brokenLineStart.y ?? 0; 1403 leftEndpointX = x2 - wid; 1404 leftEndpointY = y2 + wid; 1405 rightEndpointX = x2 + wid; 1406 rightEndpointY = y2 + wid; 1407 } 1408 x1 = drawDistributedLineTime(brokenLineStart.business, brokenLineStart.rangeTime!, [x0, y0, x1, y1, x2, y2], context); 1409 context.moveTo(x0 - 2, y0); 1410 context.lineTo(x1, y1); 1411 context.lineTo(x2, y2); 1412 context.stroke(); 1413 context.closePath(); 1414 context.beginPath(); 1415 context.lineWidth = 2; 1416 context.fillStyle = brokenLineStart.lineColor ? brokenLineStart.lineColor : '#46B1E3'; 1417 context.strokeStyle = brokenLineStart.lineColor ? brokenLineStart.lineColor : '#46B1E3'; 1418 context.moveTo(x2, y2); 1419 context.lineTo(leftEndpointX, leftEndpointY); 1420 context.lineTo(rightEndpointX, rightEndpointY); 1421 context.lineTo(x2, y2); 1422 context.fill(); 1423 context.closePath(); 1424} 1425 1426let loadingText = 'Loading...'; 1427let loadingTextWidth = 0; 1428let loadingBackground = '#f1f1f1'; 1429let loadingFont = 'bold 11pt Arial'; 1430let loadingFontColor = '#696969'; 1431 1432function drawDistributedLineTime( 1433 business: string, 1434 rangeTime: string, 1435 [x0, y0, x1, y1, x2, y2]: [number, number, number, number, number, number], 1436 context: CanvasRenderingContext2D 1437): number { 1438 if (business === 'distributed') { 1439 if (y0 === y1) { 1440 drawString(context, rangeTime, 0, 1441 new Rect(x0, y0 + 2, x1 - x0, 12), { textMetricsWidth: undefined }); 1442 } else { 1443 drawString(context, rangeTime, 0, 1444 new Rect(x1, y1 + 2, x2 - x1, 12), { textMetricsWidth: undefined }); 1445 x1 = x1 - 2; 1446 } 1447 } 1448 return x1; 1449} 1450 1451export function drawLoadingFrame( 1452 ctx: CanvasRenderingContext2D, 1453 list: Array<unknown>, 1454 traceRow: unknown, 1455 sort: boolean = false 1456): void { 1457 const row = traceRow as TraceRow<BaseStruct>; 1458 ctx.beginPath(); 1459 ctx.clearRect(0, 0, row.frame.width, row.frame.height); 1460 drawLines(ctx, TraceRow.range?.xs || [], row.frame.height, '#dadada'); 1461 drawVSync(ctx, row.frame.width, row.frame.height); 1462 if (row.loadingFrame) { 1463 if (loadingTextWidth === 0) { 1464 loadingTextWidth = ctx.measureText(loadingText).width; 1465 } 1466 let firstPx = nsx(row.loadingPin1, row.frame.width); 1467 let lastPx = nsx(row.loadingPin2, row.frame.width); 1468 ctx.fillStyle = loadingBackground; 1469 ctx.fillRect(0, 1, firstPx, row.frame.height - 2); 1470 ctx.fillRect(lastPx, 1, row.frame.width - lastPx, row.frame.height - 2); 1471 ctx.fillStyle = loadingFontColor; 1472 if (firstPx > loadingTextWidth) { 1473 ctx.fillText(loadingText, (firstPx - loadingTextWidth) / 2, row.frame.height / 2); 1474 } 1475 if (row.frame.width - lastPx > loadingTextWidth) { 1476 ctx.fillText(loadingText, lastPx + (row.frame.width - lastPx) / 2 - loadingTextWidth / 2, row.frame.height / 2); 1477 } 1478 } 1479 ctx.closePath(); 1480} 1481 1482export function drawString( 1483 ctx: CanvasRenderingContext2D, 1484 str: string, 1485 textPadding: number, 1486 frame: Rect, 1487 data: unknown 1488): void { 1489 //@ts-ignore 1490 if (data.textMetricsWidth === undefined) { 1491 //@ts-ignore 1492 data.textMetricsWidth = ctx.measureText(str); 1493 } 1494 //@ts-ignore 1495 const textMetricsWidth = (data.textMetricsWidth as TextMetrics).width; 1496 const yPos = 1.5; 1497 let charWidth = Math.round(textMetricsWidth / str.length); 1498 let fillTextWidth = frame.width - textPadding * 2; 1499 if (textMetricsWidth < fillTextWidth) { 1500 let x2 = Math.floor(frame.width / 2 - textMetricsWidth / 2 + frame.x + textPadding); 1501 ctx.fillText(str, x2, Math.floor(frame.y + frame.height / yPos), fillTextWidth); 1502 } else { 1503 if (fillTextWidth >= charWidth) { 1504 let chatNum = fillTextWidth / charWidth; 1505 let x1 = frame.x + textPadding; 1506 1507 if (chatNum < 2) { 1508 ctx.fillText(str.substring(0, 1), x1, Math.floor(frame.y + frame.height / yPos), fillTextWidth); 1509 } else { 1510 ctx.fillText( 1511 `${str.substring(0, chatNum - 1)}...`, 1512 x1, 1513 Math.floor(frame.y + frame.height / yPos), 1514 fillTextWidth 1515 ); 1516 } 1517 } 1518 } 1519} 1520 1521export function drawFunString( 1522 ctx: CanvasRenderingContext2D, 1523 str: string, 1524 textPadding: number, 1525 frame: Rect, 1526 data: FuncStruct 1527): void { 1528 if (data.textMetricsWidth === undefined) { 1529 data.textMetricsWidth = ctx.measureText(str).width; 1530 } 1531 let charWidth = Math.round(data.textMetricsWidth / str.length); 1532 let fillTextWidth = frame.width - textPadding * 2; 1533 if (data.textMetricsWidth < fillTextWidth) { 1534 let x2 = Math.floor(frame.width / 2 - data.textMetricsWidth / 2 + frame.x + textPadding); 1535 ctx.fillText(str, x2, Math.floor(data.frame!.height * (data.depth! + 0.5) + 3), fillTextWidth); 1536 } else { 1537 if (fillTextWidth >= charWidth) { 1538 let chatNum = fillTextWidth / charWidth; 1539 let x1 = frame.x + textPadding; 1540 if (chatNum < 2) { 1541 ctx.fillText(str.substring(0, 1), x1, Math.floor(data.frame!.height * (data.depth! + 0.5) + 3), fillTextWidth); 1542 } else { 1543 ctx.fillText( 1544 `${str.substring(0, chatNum - 1)}...`, 1545 x1, 1546 Math.floor(data.frame!.height * (data.depth! + 0.5) + 3), 1547 fillTextWidth 1548 ); 1549 } 1550 } 1551 } 1552} 1553 1554export function hiPerf( 1555 arr: Array<HiPerfStruct>, 1556 arr2: Array<HiPerfStruct>, 1557 res: Array<HiPerfStruct>, 1558 startNS: number, 1559 endNS: number, 1560 frame: Rect, 1561 groupBy10MS: boolean, 1562 use: boolean 1563): void { 1564 if (use && res.length > 0) { 1565 setFrameByRes(res, startNS, endNS, frame); 1566 return; 1567 } 1568 res.length = 0; 1569 if (arr) { 1570 setFrameByArr(arr, arr2, res, startNS, endNS, frame, groupBy10MS); 1571 } 1572} 1573 1574function setFrameByRes(res: Array<HiPerfStruct>, startNS: number, endNS: number, frame: Rect): void { 1575 let pns = (endNS - startNS) / frame.width; 1576 let y = frame.y; 1577 for (let i = 0; i < res.length; i++) { 1578 let item = res[i]; 1579 if ((item.startNS || 0) + (item.dur || 0) > startNS && (item.startNS || 0) < endNS) { 1580 if (!item.frame) { 1581 item.frame = new Rect(0, 0, 0, 0); 1582 item.frame.y = y; 1583 } 1584 item.frame.height = item.height!; 1585 HiPerfStruct.setFrame(item, pns, startNS, endNS, frame); 1586 } else { 1587 item.frame = undefined; 1588 } 1589 } 1590} 1591 1592function setFrameByArr( 1593 arr: Array<HiPerfStruct>, 1594 arr2: Array<HiPerfStruct>, 1595 res: Array<HiPerfStruct>, 1596 startNS: number, 1597 endNS: number, 1598 frame: Rect, 1599 groupBy10MS: boolean 1600): void { 1601 let list = groupBy10MS ? arr2 : arr; 1602 let pns = (endNS - startNS) / frame.width; 1603 let y = frame.y; 1604 for (let i = 0, len = list.length; i < len; i++) { 1605 let it = list[i]; 1606 if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) { 1607 if (!list[i].frame) { 1608 list[i].frame = new Rect(0, 0, 0, 0); 1609 list[i].frame!.y = y; 1610 } 1611 list[i].frame!.height = it.height!; 1612 HiPerfStruct.setFrame(list[i], pns, startNS, endNS, frame); 1613 setResultArr(groupBy10MS, list, i, res); 1614 } 1615 } 1616} 1617 1618function setResultArr(groupBy10MS: boolean, list: Array<HiPerfStruct>, i: number, res: Array<HiPerfStruct>): void { 1619 const itemI = list[i]; 1620 const itemBeforeI = list[i - 1]; 1621 if (itemI.frame && itemBeforeI.frame) { 1622 if (groupBy10MS) { 1623 let flag: boolean = 1624 i > 0 && 1625 (itemBeforeI.frame.x || 0) === (itemI.frame.x || 0) && 1626 (itemBeforeI.frame.width || 0) === (itemI.frame.width || 0) && 1627 (itemBeforeI.frame.height || 0) === (itemI.frame.height || 0); 1628 if (!flag) { 1629 res.push(itemI); 1630 } 1631 } else { 1632 if (!(i > 0 && Math.abs((itemBeforeI.frame.x || 0) - (itemI.frame.x || 0)) < 4)) { 1633 res.push(itemI); 1634 } 1635 } 1636 } 1637} 1638 1639export function hiPerf2(filter: Array<HiPerfStruct>, startNS: number, endNS: number, frame: Rect): void { 1640 if (filter.length > 0) { 1641 let pns = (endNS - startNS) / frame.width; 1642 let y = frame.y; 1643 for (let i = 0; i < filter.length; i++) { 1644 let it = filter[i]; 1645 if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) { 1646 if (!it.frame) { 1647 it.frame = new Rect(0, 0, 0, 0); 1648 it.frame.y = y; 1649 } 1650 it.frame.height = it.height!; 1651 HiPerfStruct.setFrame(it, pns, startNS, endNS, frame); 1652 } else { 1653 it.frame = undefined; 1654 } 1655 } 1656 return; 1657 } 1658} 1659 1660export class HiPerfStruct extends BaseStruct { 1661 static hoverStruct: HiPerfStruct | undefined; 1662 static selectStruct: HiPerfStruct | undefined; 1663 id: number | undefined; 1664 callchain_id: number | undefined; 1665 timestamp: number | undefined; 1666 thread_id: number | undefined; 1667 event_count: number | undefined; 1668 event_type_id: number | undefined; 1669 cpu_id: number | undefined; 1670 thread_state: string | undefined; 1671 startNS: number | undefined; 1672 endNS: number | undefined; 1673 dur: number | undefined; 1674 height: number | undefined; 1675 eventCount: number | undefined; 1676 sampleCount: number | undefined; 1677 1678 static drawRoundRectPath(cxt: Path2D, x: number, y: number, width: number, height: number, radius: number): void { 1679 cxt.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2); 1680 cxt.lineTo(x + radius, y + height); 1681 cxt.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI); 1682 cxt.lineTo(x, y + radius); 1683 cxt.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2); 1684 cxt.lineTo(x + width - radius, y); 1685 cxt.arc(x + width - radius, y + radius, radius, (Math.PI * 3) / 2, Math.PI * 2); 1686 cxt.lineTo(x + width, y + height - radius); 1687 cxt.moveTo(x + width / 3, y + height / 5); 1688 cxt.lineTo(x + width / 3, y + (height / 5) * 4); 1689 cxt.moveTo(x + width / 3, y + height / 5); 1690 cxt.bezierCurveTo( 1691 x + width / 3 + 7, 1692 y + height / 5 - 2, 1693 x + width / 3 + 7, 1694 y + height / 5 + 6, 1695 x + width / 3, 1696 y + height / 5 + 4 1697 ); 1698 } 1699 1700 static draw( 1701 ctx: CanvasRenderingContext2D, 1702 normalPath: Path2D, 1703 specPath: Path2D, 1704 data: HiPerfStruct, 1705 groupBy10MS: boolean, 1706 textMetrics?: TextMetrics 1707 ): void { 1708 if (data.frame) { 1709 if (groupBy10MS) { 1710 let width = data.frame.width; 1711 normalPath.rect(data.frame.x, 40 - (data.height || 0), width, data.height || 0); 1712 } else { 1713 data.frame.width > 4 ? (data.frame.width = 4) : (data.frame.width = data.frame.width); 1714 let path = data.callchain_id === -1 ? specPath : normalPath; 1715 path.moveTo(data.frame.x + 7, 20); 1716 if (textMetrics) { 1717 ctx.fillText('', data.frame.x - textMetrics!.width / 2, 26); //℗©®℗® 1718 } else { 1719 HiPerfStruct.drawRoundRectPath(path, data.frame.x - 7, 20 - 7, 14, 14, 3); 1720 } 1721 path.moveTo(data.frame.x, 27); 1722 path.lineTo(data.frame.x, 33); 1723 } 1724 } 1725 } 1726 1727 static drawSpecialPath(ctx: CanvasRenderingContext2D, specPath: Path2D): void { 1728 ctx.strokeStyle = '#9fafc4'; 1729 ctx.globalAlpha = 0.5; 1730 ctx.stroke(specPath); 1731 ctx.globalAlpha = 1; 1732 } 1733 1734 static setFrame(node: HiPerfStruct, pns: number, startNS: number, endNS: number, frame: Rect): void { 1735 if (!node.frame) { 1736 return; 1737 } 1738 if ((node.startNS || 0) < startNS) { 1739 node.frame.x = 0; 1740 } else { 1741 node.frame.x = Math.floor(((node.startNS || 0) - startNS) / pns); 1742 } 1743 if ((node.startNS || 0) + (node.dur || 0) > endNS) { 1744 node.frame.width = frame.width - node.frame.x; 1745 } else { 1746 node.frame.width = Math.ceil(((node.startNS || 0) + (node.dur || 0) - startNS) / pns - node.frame.x); 1747 } 1748 if (node.frame.width < 1) { 1749 node.frame.width = 1; 1750 } 1751 } 1752 1753 static groupBy10MS( 1754 groupArray: Array<HiPerfStruct>, 1755 intervalPerf: number, 1756 maxCpu?: number | undefined, 1757 usage?: boolean, 1758 event?: number 1759 ): Array<HiPerfStruct> { 1760 let maxEventCount = 0; 1761 let obj = filterGroupArray(groupArray, maxEventCount, usage, event); 1762 let arr = []; 1763 for (let aKey in obj) { 1764 let ns = parseInt(aKey); 1765 let height: number = 0; 1766 if (usage) { 1767 if (maxCpu !== undefined) { 1768 //@ts-ignore 1769 height = Math.floor((obj[aKey].sampleCount / (10 / intervalPerf) / maxCpu) * 40); 1770 } else { 1771 //@ts-ignore 1772 height = Math.floor((obj[aKey].sampleCount / (10 / intervalPerf)) * 40); 1773 } 1774 } else { 1775 //@ts-ignore 1776 height = Math.floor((obj[aKey].eventCount / maxEventCount) * 40); 1777 } 1778 arr.push({ 1779 startNS: ns, 1780 dur: 10_000_000, 1781 //@ts-ignore 1782 eventCount: obj[aKey].eventCount, 1783 //@ts-ignore 1784 sampleCount: obj[aKey].sampleCount, 1785 height: height, 1786 }); 1787 } 1788 return arr as HiPerfStruct[]; 1789 } 1790} 1791 1792function filterGroupArray( 1793 groupArray: Array<HiPerfStruct>, 1794 maxEventCount: number, 1795 usage?: boolean, 1796 event?: number 1797): HiPerfStruct { 1798 const map = groupArray.map((it) => { 1799 //@ts-ignore 1800 it.timestamp_group = Math.trunc(it.startNS / 10_000_000) * 10_000_000; 1801 return it; 1802 }); 1803 const reduce = map.reduce((pre: HiPerfStruct, current: HiPerfStruct) => { 1804 if (usage || current.event_type_id === event || event === -1) { 1805 //@ts-ignore 1806 if (pre[current.timestamp_group]) { 1807 //@ts-ignore 1808 pre[current.timestamp_group].sampleCount += 1; 1809 //@ts-ignore 1810 pre[current.timestamp_group].eventCount += current.event_count; 1811 } else { 1812 //@ts-ignore 1813 pre[current.timestamp_group] = { 1814 sampleCount: 1, 1815 eventCount: current.event_count, 1816 }; 1817 } 1818 //@ts-ignore 1819 maxEventCount = Math.max(pre[current.timestamp_group].eventCount, maxEventCount); 1820 } 1821 return pre; 1822 }, new HiPerfStruct()); 1823 return reduce; 1824} 1825 1826function setMemFrame( 1827 node: ProcessMemStruct, 1828 padding: number, 1829 startNS: number, 1830 endNS: number, 1831 totalNS: number, 1832 frame: Rect 1833): void { 1834 let x1: number; 1835 let x2: number; 1836 if ((node.startTime || 0) <= startNS) { 1837 x1 = 0; 1838 } else { 1839 x1 = ns2x(node.startTime || 0, startNS, endNS, totalNS, frame); 1840 } 1841 if ((node.startTime || 0) + (node.duration || 0) >= endNS) { 1842 x2 = frame.width; 1843 } else { 1844 x2 = ns2x((node.startTime || 0) + (node.duration || 0), startNS, endNS, totalNS, frame); 1845 } 1846 let getV: number = x2 - x1 <= 1 ? 1 : x2 - x1; 1847 if (!node.frame) { 1848 node.frame = new Rect(0, 0, 0, 0); 1849 } 1850 node.frame.x = Math.floor(x1); 1851 node.frame.y = Math.floor(frame.y + padding); 1852 node.frame.width = Math.ceil(getV); 1853 node.frame.height = Math.floor(frame.height - padding * 2); 1854} 1855 1856export function mem( 1857 list: Array<unknown>, 1858 memFilter: Array<unknown>, 1859 startNS: number, 1860 endNS: number, 1861 totalNS: number, 1862 frame: Rect, 1863 use: boolean 1864): void { 1865 if (use && memFilter.length > 0) { 1866 for (let i = 0, len = memFilter.length; i < len; i++) { 1867 if ( 1868 //@ts-ignore 1869 (memFilter[i].startTime || 0) + (memFilter[i].duration || 0) > startNS && 1870 //@ts-ignore 1871 (memFilter[i].startTime || 0) < endNS 1872 ) { 1873 //@ts-ignore 1874 setMemFrame(memFilter[i], 5, startNS, endNS, totalNS, frame); 1875 } else { 1876 //@ts-ignore 1877 memFilter[i].frame = undefined; 1878 } 1879 } 1880 return; 1881 } 1882 memFilter.length = 0; 1883 //@ts-ignore 1884 setMemFilter(list, memFilter, startNS, endNS, totalNS, frame); 1885} 1886 1887function setMemFilter( 1888 list: Array<ProcessMemStruct>, 1889 memFilter: Array<ProcessMemStruct>, 1890 startNS: number, 1891 endNS: number, 1892 totalNS: number, 1893 frame: Rect 1894): void { 1895 if (list) { 1896 for (let i = 0, len = list.length; i < len; i++) { 1897 let it = list[i]; 1898 if ((it.startTime || 0) + (it.duration || 0) > startNS && (it.startTime || 0) < endNS) { 1899 setMemFrame(list[i], 5, startNS, endNS, totalNS, frame); 1900 if ( 1901 i > 0 && 1902 (list[i - 1].frame?.x || 0) === (list[i].frame?.x || 0) && 1903 (list[i - 1].frame?.width || 0) === (list[i].frame?.width || 0) 1904 ) { 1905 } else { 1906 memFilter.push(list[i]); 1907 } 1908 } 1909 } 1910 } 1911} 1912 1913export function drawWakeUpList( 1914 wakeUpListContext: CanvasRenderingContext2D | undefined | null, 1915 wake: WakeupBean | undefined | null, 1916 startNS: number, 1917 endNS: number, 1918 totalNS: number, 1919 frame: Rect, 1920 wakeup: WakeupBean | undefined = undefined, 1921 currentCpu: number | undefined | null = undefined, 1922 noVerticalLine = false 1923): void { 1924 if (!wakeUpListContext) { 1925 return; 1926 } 1927 if (wake) { 1928 let x1 = Math.floor(ns2x(wake.wakeupTime || 0, startNS, endNS, totalNS, frame)); 1929 wakeUpListContext.beginPath(); 1930 wakeUpListContext.lineWidth = 2; 1931 wakeUpListContext.fillStyle = '#000000'; 1932 if (x1 > 0 && x1 < frame.x + frame.width) { 1933 if (!noVerticalLine) { 1934 wakeUpListContext.moveTo(x1, frame.y); 1935 wakeUpListContext.lineTo(x1, frame.y + frame.height); 1936 } 1937 if (currentCpu === wake.cpu) { 1938 let centerY = Math.floor(frame.y + frame.height / 2); 1939 wakeUpListContext.moveTo(x1, centerY - 6); 1940 wakeUpListContext.lineTo(x1 + 4, centerY); 1941 wakeUpListContext.lineTo(x1, centerY + 6); 1942 wakeUpListContext.lineTo(x1 - 4, centerY); 1943 wakeUpListContext.lineTo(x1, centerY - 6); 1944 wakeUpListContext.fill(); 1945 } 1946 } 1947 if (wakeup) { 1948 drawWakeUpListIfWakeUp(wakeUpListContext, wake, startNS, endNS, totalNS, frame, wakeup, x1); 1949 } 1950 wakeUpListContext.strokeStyle = '#000000'; 1951 wakeUpListContext.stroke(); 1952 wakeUpListContext.closePath(); 1953 } 1954} 1955 1956function drawWakeUpListIfWakeUp( 1957 wakeUpListContext: CanvasRenderingContext2D, 1958 wake: WakeupBean, 1959 startNS: number, 1960 endNS: number, 1961 totalNS: number, 1962 frame: Rect, 1963 wakeup: WakeupBean, 1964 x1: number 1965): void { 1966 let x2 = Math.floor(ns2x(wakeup.ts || 0, startNS, endNS, totalNS, frame)); 1967 let y = frame.y + frame.height - 10; 1968 wakeUpListContext.moveTo(x1, y); 1969 wakeUpListContext.lineTo(x2, y); 1970 wakeUpListContext.moveTo(x2, y - 25); 1971 wakeUpListContext.lineTo(x2, y + 5); 1972 let s = ns2s((wakeup.ts || 0) - (wake.wakeupTime || 0)); 1973 let wakeUpListDistance = x2 - x1; 1974 if (wakeUpListDistance > 12) { 1975 wakeUpListContext.moveTo(x1, y); 1976 wakeUpListContext.lineTo(x1 + 6, y - 3); 1977 wakeUpListContext.moveTo(x1, y); 1978 wakeUpListContext.lineTo(x1 + 6, y + 3); 1979 wakeUpListContext.moveTo(x2, y); 1980 wakeUpListContext.lineTo(x2 - 6, y - 3); 1981 wakeUpListContext.moveTo(x2, y); 1982 wakeUpListContext.lineTo(x2 - 6, y + 3); 1983 let measure = wakeUpListContext.measureText(s); 1984 let tHeight = measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent; 1985 let xStart = x1 + Math.floor(wakeUpListDistance / 2 - measure.width / 2); 1986 if (wakeUpListDistance > measure.width + 4) { 1987 wakeUpListContext.fillStyle = '#ffffff'; 1988 wakeUpListContext.fillRect(xStart - 2, y - 4 - tHeight, measure.width + 4, tHeight + 4); 1989 wakeUpListContext.font = '10px solid'; 1990 wakeUpListContext.fillStyle = '#000000'; 1991 wakeUpListContext.textBaseline = 'bottom'; 1992 wakeUpListContext.fillText(s, xStart, y - 2); 1993 } 1994 } 1995} 1996interface SearchNode { 1997 symbolName?: string; 1998 children: SearchNode[]; 1999 searchShow?: boolean; 2000 isSearch?: boolean; 2001 parent?: SearchNode; 2002} 2003 2004export function findSearchNode(data: unknown[], search: string, parentSearch: boolean): void { 2005 search = search.toLocaleLowerCase(); 2006 data.forEach((nodeIt) => { 2007 const node = nodeIt as SearchNode; 2008 if ((node.symbolName && node.symbolName.toLocaleLowerCase().includes(search) && search !== '') || parentSearch) { 2009 node.searchShow = true; 2010 node.isSearch = node.symbolName !== undefined && node.symbolName.toLocaleLowerCase().includes(search); 2011 let parentNode = node.parent; 2012 while (parentNode && !parentNode.searchShow) { 2013 parentNode.searchShow = true; 2014 parentNode = parentNode.parent; 2015 } 2016 } else { 2017 node.searchShow = false; 2018 node.isSearch = false; 2019 } 2020 if (node.children.length > 0) { 2021 findSearchNode(node.children, search, node.searchShow); 2022 } 2023 }); 2024} 2025 2026// draw prio curve 2027// @ts-ignore 2028export function prioClickHandlerFun(param: unknown, row: TraceRow<unknown>, threadFilter: Array<ThreadStruct>, arr: unknown, oldVal: number): void { 2029 //@ts-ignore 2030 let maxCount = Math.max(...param.map((obj: unknown) => obj.count)); 2031 //@ts-ignore 2032 let maxCountPrio = param.find((obj: unknown) => obj.count === maxCount).prio;//找出出现次数最多的优先级,为中位值 2033 //@ts-ignore 2034 let maxPrioDiff = Math.max(...param.map((obj: unknown) => Math.abs(obj.prio - Number(maxCountPrio))));//找出与中位值的最大diff 2035 let maxPointInterval = Math.ceil(maxPrioDiff / 4);//diff分成4份,每一份占多少px 2036 2037 for (let i = 0; i < threadFilter.length; i++) { 2038 const item = threadFilter[i]; 2039 const preItem = threadFilter[i - 1]; 2040 //给原始数据添加prio值 2041 let slice = Utils.getInstance().getSchedSliceMap().get(`${item.id}-${item.startTime}`); 2042 if (slice) { 2043 item.prio = slice!.priority; 2044 } 2045 //合并prio值相同的项提高画图速度 2046 if ( 2047 item.prio && 2048 (oldVal !== item.prio || i === threadFilter.length - 2 || i === threadFilter.length - 1) 2049 ) { 2050 configCurveY(row, item, maxCountPrio, maxPointInterval); 2051 //处理prio值变化前的 2052 if (i !== 0) { 2053 configCurveY(row, preItem, maxCountPrio, maxPointInterval); 2054 //@ts-ignore 2055 arr.push(preItem); 2056 } 2057 //@ts-ignore 2058 arr.push(item); 2059 oldVal = item.prio; 2060 } 2061 } 2062} 2063 2064//确定曲线波动时的y轴 2065//@ts-ignore 2066function configCurveY(row: TraceRow<unknown>, item: ThreadStruct, maxCountPrio: number, maxPointInterval: number): void { 2067 if (item.prio === Number(maxCountPrio)) { 2068 item.curveFloatY = 3 + 12 / 2 + row.translateY; 2069 } else if (item.prio! > Number(maxCountPrio)) { 2070 let prioHeight = Math.floor((item.prio! - Number(maxCountPrio)) / maxPointInterval) * 2; 2071 item.curveFloatY = 3 + 12 / 2 - prioHeight + row.translateY; 2072 } else if (item.prio! < Number(maxCountPrio)) { 2073 let prioHeight = Math.floor((Number(maxCountPrio) - item.prio!) / maxPointInterval) * 2; 2074 item.curveFloatY = 3 + 12 / 2 + prioHeight + row.translateY; 2075 } 2076} 2077 2078export function drawThreadCurve(context: CanvasRenderingContext2D, threadFilter: ThreadStruct, nextFilter: ThreadStruct): void { 2079 // 绘制曲线 2080 if (threadFilter.frame && nextFilter.frame) { 2081 let p1 = threadFilter; 2082 let p2 = nextFilter; 2083 let diff = p2.curveFloatY! >= p1.curveFloatY! ? p2.curveFloatY! - p1.curveFloatY! : p1.curveFloatY! - p2.curveFloatY!; 2084 let cp1x = p1.frame!.x + (p2.frame!.x - p1.frame!.x) / 5; 2085 let cp1y = p2.curveFloatY! >= p1.curveFloatY! ? p1.curveFloatY! - diff / 5 : p1.curveFloatY! + diff / 5; 2086 let cp2x = p2.frame!.x - (p2.frame!.x - p1.frame!.x) / 5; 2087 let cp2y = p2.curveFloatY! >= p1.curveFloatY! ? p2.curveFloatY! + diff / 5 : p2.curveFloatY! - diff / 5; 2088 context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.frame!.x, p2.curveFloatY!); 2089 context.lineWidth = 1; 2090 context.strokeStyle = '#ffc90e'; 2091 context.lineCap = 'round'; 2092 } 2093 context.stroke(); 2094} 2095