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 './ProcedureWorkerCPU.js'; 17import { TraceRow } from '../../component/trace/base/TraceRow.js'; 18import { TimerShaftElement } from '../../component/trace/TimerShaftElement'; 19 20export abstract class Render { 21 abstract renderMainThread(req: any, row: TraceRow<any>): void; 22} 23 24export abstract class PerfRender { 25 abstract render(req: RequestMessage, list: Array<any>, filter: Array<any>, dataList2: Array<any>): void; 26} 27 28export class RequestMessage { 29 type: string | undefined | null; 30 lazyRefresh: boolean | undefined; 31 intervalPerf: any; 32 canvas: any; 33 context: any; 34 params: any; 35 online: any; 36 buf: any; 37 isRangeSelect: any; 38 isHover: any; 39 xs: any; 40 frame: any; 41 flagMoveInfo: any; 42 flagSelectedInfo: any; 43 hoverX: any; 44 hoverY: any; 45 startNS: any; 46 endNS: any; 47 totalNS: any; 48 slicesTime: 49 | { 50 startTime: number | null; 51 endTime: number | null; 52 color: string | null; 53 } 54 | undefined; 55 range: any; 56 scale: any; 57 chartColor: any; 58 canvasWidth: any; 59 canvasHeight: any; 60 useCache: any; 61 lineColor: any; 62 wakeupBean: WakeupBean | undefined | null; 63 id: any; 64 postMessage: 65 | { 66 (message: any, targetOrigin: string, transfer?: Transferable[]): void; 67 (message: any, options?: WindowPostMessageOptions): void; 68 } 69 | undefined; 70} 71 72export function ns2s(ns: number): string { 73 let second1 = 1_000_000_000; // 1 second 74 let millisecond = 1_000_000; // 1 millisecond 75 let microsecond = 1_000; // 1 microsecond 76 let res; 77 if (ns >= second1) { 78 res = (ns / 1000 / 1000 / 1000).toFixed(1) + ' s'; 79 } else if (ns >= millisecond) { 80 res = (ns / 1000 / 1000).toFixed(1) + ' ms'; 81 } else if (ns >= microsecond) { 82 res = (ns / 1000).toFixed(1) + ' μs'; 83 } else if (ns > 0) { 84 res = ns.toFixed(1) + ' ns'; 85 } else { 86 res = ns.toFixed(1) + ' s'; 87 } 88 return res; 89} 90 91export function isFrameContainPoint(frame: Rect, x: number, y: number): boolean { 92 return x >= frame.x && x <= frame.x + frame.width && y >= frame.y && y <= frame.y + frame.height; 93} 94export const isSurroundingPoint = function (pointX: number, currentRect: Rect, unitPointXRange: number): boolean { 95 return (pointX >= currentRect?.x - unitPointXRange) && pointX <= currentRect?.x + unitPointXRange; 96}; 97 98export const computeUnitWidth = function ( 99 preTs: number, 100 currentTs: number, 101 frameWidth: number, 102 selectUnitWidth: number 103): number { 104 let max = 150; 105 let unitWidth = ((currentTs - preTs) * frameWidth) / (TraceRow.range!.endNS - TraceRow.range!.startNS); 106 if (unitWidth < selectUnitWidth) { 107 return unitWidth > max || unitWidth === 0 ? max : unitWidth; 108 } 109 return selectUnitWidth > max || selectUnitWidth === 0 ? max : selectUnitWidth; 110}; 111 112class FilterConfig { 113 startNS: number = 0; 114 endNS: number = 0; 115 totalNS: number = 0; 116 frame: any = null; 117 useCache: boolean = false; 118 startKey: string = 'startNS'; 119 durKey: string = 'dur'; 120 paddingTop: number = 0; 121} 122 123export function fillCacheData(filterList: Array<any>, condition: FilterConfig): boolean { 124 if (condition.useCache && filterList.length > 0) { 125 let pns = (condition.endNS - condition.startNS) / condition.frame.width; 126 let y = condition.frame.y + condition.paddingTop; 127 let height = condition.frame.height - condition.paddingTop * 2; 128 for (let i = 0, len = filterList.length; i < len; i++) { 129 let it = filterList[i]; 130 if ( 131 (it[condition.startKey] || 0) + (it[condition.durKey] || 0) > condition.startNS && 132 (it[condition.startKey] || 0) < condition.endNS 133 ) { 134 if (!filterList[i].frame) { 135 filterList[i].frame = {}; 136 filterList[i].frame.y = y; 137 filterList[i].frame.height = height; 138 } 139 setNodeFrame( 140 filterList[i], 141 pns, 142 condition.startNS, 143 condition.endNS, 144 condition.frame, 145 condition.startKey, 146 condition.durKey 147 ); 148 } else { 149 filterList[i].frame = null; 150 } 151 } 152 return true; 153 } 154 return false; 155} 156 157export function findRange(fullData: Array<any>, condition: FilterConfig): Array<any> { 158 let left = 0, 159 right = 0; 160 for (let i = 0, j = fullData.length - 1, ib = true, jb = true; i < fullData.length, j >= 0; i++, j--) { 161 if (fullData[j][condition.startKey] <= condition.endNS && jb) { 162 right = j; 163 jb = false; 164 } 165 if (fullData[i][condition.startKey] + fullData[i][condition.durKey] >= condition.startNS && ib) { 166 left = i; 167 ib = false; 168 } 169 if (!ib && !jb) { 170 break; 171 } 172 } 173 let slice = fullData.slice(left, right + 1); 174 return slice; 175} 176 177export function dataFilterHandler(fullData: Array<any>, filterData: Array<any>, condition: FilterConfig): void { 178 if (fillCacheData(filterData, condition)) { 179 return; 180 } 181 if (fullData) { 182 filterData.length = 0; 183 let pns = (condition.endNS - condition.startNS) / condition.frame.width; //每个像素多少ns 184 let y = condition.frame.y + condition.paddingTop; 185 let height = condition.frame.height - condition.paddingTop * 2; 186 let slice = findRange(fullData, condition); 187 let sum = 0; 188 for (let i = 0; i < slice.length; i++) { 189 if (!slice[i].frame) { 190 slice[i].frame = {}; 191 slice[i].frame.y = y; 192 slice[i].frame.height = height; 193 } 194 if (i === slice.length - 1) { 195 if (slice[i][condition.durKey] === undefined || slice[i][condition.durKey] === null) { 196 slice[i][condition.durKey] = (condition.endNS || 0) - (slice[i][condition.startKey] || 0); 197 } 198 } else { 199 if (slice[i][condition.durKey] === undefined || slice[i][condition.durKey] === null) { 200 slice[i][condition.durKey] = (slice[i + 1][condition.startKey] || 0) - (slice[i][condition.startKey] || 0); 201 } 202 } 203 if (slice[i][condition.durKey] >= pns || slice.length < 100) { 204 slice[i].v = true; 205 setNodeFrame( 206 slice[i], 207 pns, 208 condition.startNS, 209 condition.endNS, 210 condition.frame, 211 condition.startKey, 212 condition.durKey 213 ); 214 } else { 215 if (i > 0) { 216 let c = slice[i][condition.startKey] - slice[i - 1][condition.startKey] - slice[i - 1][condition.durKey]; 217 if (c < pns && sum < pns) { 218 sum += c + slice[i - 1][condition.durKey]; 219 slice[i].v = false; 220 } else { 221 slice[i].v = true; 222 setNodeFrame( 223 slice[i], 224 pns, 225 condition.startNS, 226 condition.endNS, 227 condition.frame, 228 condition.startKey, 229 condition.durKey 230 ); 231 sum = 0; 232 } 233 } 234 } 235 } 236 filterData.push(...slice.filter((it) => it.v)); 237 } 238} 239 240function setNodeFrame( 241 node: any, 242 pns: number, 243 startNS: number, 244 endNS: number, 245 frame: any, 246 startKey: string, 247 durKey: string 248) { 249 if ((node[startKey] || 0) < startNS) { 250 node.frame.x = 0; 251 } else { 252 node.frame.x = Math.floor(((node[startKey] || 0) - startNS) / pns); 253 } 254 if ((node[startKey] || 0) + (node[durKey] || 0) > endNS) { 255 node.frame.width = frame.width - node.frame.x; 256 } else { 257 node.frame.width = Math.ceil(((node[startKey] || 0) + (node[durKey] || 0) - startNS) / pns - node.frame.x); 258 } 259 if (node.frame.width < 1) { 260 node.frame.width = 1; 261 } 262} 263 264export function ns2x(ns: number, startNS: number, endNS: number, duration: number, rect: any) { 265 // @ts-ignore 266 if (endNS == 0) { 267 endNS = duration; 268 } 269 let xSize: number = ((ns - startNS) * rect.width) / (endNS - startNS); 270 if (xSize < 0) { 271 xSize = 0; 272 } else if (xSize > rect.width) { 273 xSize = rect.width; 274 } 275 return xSize; 276} 277 278export function ns2xByTimeShaft(ns: number, tse: TimerShaftElement) { 279 let startNS = tse.getRange()!.startNS; 280 let endNS = tse.getRange()!.endNS; 281 let duration = tse.getRange()!.totalNS; 282 if (endNS == 0) { 283 endNS = duration; 284 } 285 let width = tse.getBoundingClientRect().width - 258; 286 let xSize: number = ((ns - startNS) * width) / (endNS - startNS); 287 if (xSize < 0) { 288 xSize = 0; 289 } else if (xSize > width) { 290 xSize = width; 291 } 292 return xSize; 293} 294 295export class Rect { 296 x: number = 0; 297 y: number = 0; 298 width: number = 0; 299 height: number = 0; 300 constructor(x: number, y: number, width: number, height: number) { 301 this.x = x; 302 this.y = y; 303 this.width = width; 304 this.height = height; 305 } 306 307 static intersect(r1: Rect, rect: Rect): boolean { 308 let minX = r1.x <= rect.x ? r1.x : rect.x; 309 let minY = r1.y <= rect.y ? r1.y : rect.y; 310 let maxX = r1.x + r1.width >= rect.x + rect.width ? r1.x + r1.width : rect.x + rect.width; 311 let maxY = r1.y + r1.height >= rect.y + rect.height ? r1.y + r1.height : rect.y + rect.height; 312 if (maxX - minX <= rect.width + r1.width && maxY - minY <= r1.height + rect.height) { 313 return true; 314 } else { 315 return false; 316 } 317 } 318 319 static contains(rect: Rect, x: number, y: number): boolean { 320 return rect.x <= x && x <= rect.x + rect.width && rect.y <= y && y <= rect.y + rect.height; 321 } 322 323 static containsWithMargin(rect: Rect, x: number, y: number, t: number, r: number, b: number, l: number): boolean { 324 return rect.x - l <= x && x <= rect.x + rect.width + r && rect.y - t <= y && y <= rect.y + rect.height + b; 325 } 326 327 static containsWithPadding( 328 rect: Rect, 329 x: number, 330 y: number, 331 paddingLeftRight: number, 332 paddingTopBottom: number 333 ): boolean { 334 return ( 335 rect.x + paddingLeftRight <= x && 336 rect.y + paddingTopBottom <= y && 337 x <= rect.x + rect.width - paddingLeftRight && 338 y <= rect.y + rect.height - paddingTopBottom 339 ); 340 } 341 342 /** 343 * 判断是否相交 344 * @param rect 345 */ 346 intersect(rect: Rect): boolean { 347 let minX = this.x <= rect.x ? this.x : rect.x; 348 let minY = this.y <= rect.y ? this.y : rect.y; 349 let maxX = this.x + this.width >= rect.x + rect.width ? this.x + this.width : rect.x + rect.width; 350 let maxY = this.y + this.height >= rect.y + rect.height ? this.y + this.height : rect.y + rect.height; 351 if (maxX - minX <= rect.width + this.width && maxY - minY <= this.height + rect.height) { 352 return true; 353 } else { 354 return false; 355 } 356 } 357 358 contains(x: number, y: number): boolean { 359 return this.x <= x && x <= this.x + this.width && this.y <= y && y <= this.y + this.height; 360 } 361 362 containsWithMargin(x: number, y: number, t: number, r: number, b: number, l: number): boolean { 363 return this.x - l <= x && x <= this.x + this.width + r && this.y - t <= y && y <= this.y + this.height + b; 364 } 365 366 containsWithPadding(x: number, y: number, paddingLeftRight: number, paddingTopBottom: number): boolean { 367 return ( 368 this.x + paddingLeftRight <= x && 369 x <= this.x + this.width - paddingLeftRight && 370 this.y + paddingTopBottom <= y && 371 y <= this.y + this.height - paddingTopBottom 372 ); 373 } 374} 375 376export class Point { 377 x: number = 0; 378 y: number = 0; 379 isRight: boolean = true; 380 381 constructor(x: number, y: number, isRight: boolean = true) { 382 this.x = x; 383 this.y = y; 384 this.isRight = isRight; 385 } 386} 387 388export enum LineType { 389 brokenLine, 390 bezierCurve, 391} 392 393export class PairPoint { 394 x: number = 0; 395 ns: number = 0; 396 y: number = 0; 397 offsetY: number = 0; 398 rowEL: TraceRow<any>; 399 isRight: boolean = true; 400 lineType?: LineType; 401 business: string = ''; 402 hidden?: boolean = false; 403 constructor( 404 rowEL: TraceRow<any>, 405 x: number, 406 y: number, 407 ns: number, 408 offsetY: number, 409 isRight: boolean, 410 business: string 411 ) { 412 this.rowEL = rowEL; 413 this.x = x; 414 this.y = y; 415 this.ns = ns; 416 this.offsetY = offsetY; 417 this.isRight = isRight; 418 this.business = business; 419 } 420} 421 422export class BaseStruct { 423 translateY: number | undefined; 424 frame: Rect | undefined; 425 isHover: boolean = false; 426} 427 428export function drawLines(ctx: CanvasRenderingContext2D, xs: Array<any>, height: number, lineColor: string) { 429 if (ctx) { 430 ctx.beginPath(); 431 ctx.lineWidth = 1; 432 ctx.strokeStyle = lineColor || '#dadada'; 433 xs?.forEach((it) => { 434 ctx.moveTo(Math.floor(it), 0); 435 ctx.lineTo(Math.floor(it), height); 436 }); 437 ctx.stroke(); 438 ctx.closePath(); 439 } 440} 441 442export function drawFlagLine( 443 commonCtx: any, 444 hoverFlag: any, 445 selectFlag: any, 446 startNS: number, 447 endNS: number, 448 totalNS: number, 449 frame: any, 450 slicesTime: 451 | { 452 startTime: number | null | undefined; 453 endTime: number | null | undefined; 454 color: string | null | undefined; 455 } 456 | undefined 457) { 458 if (commonCtx) { 459 if (hoverFlag) { 460 commonCtx.beginPath(); 461 commonCtx.lineWidth = 2; 462 commonCtx.strokeStyle = hoverFlag?.color || '#dadada'; 463 commonCtx.moveTo(Math.floor(hoverFlag.x), 0); 464 commonCtx.lineTo(Math.floor(hoverFlag.x), frame.height); 465 commonCtx.stroke(); 466 commonCtx.closePath(); 467 } 468 if (selectFlag) { 469 commonCtx.beginPath(); 470 commonCtx.lineWidth = 2; 471 commonCtx.strokeStyle = selectFlag?.color || '#dadada'; 472 selectFlag.x = ns2x(selectFlag.time, startNS, endNS, totalNS, frame); 473 commonCtx.moveTo(Math.floor(selectFlag.x), 0); 474 commonCtx.lineTo(Math.floor(selectFlag.x), frame.height); 475 commonCtx.stroke(); 476 commonCtx.closePath(); 477 } 478 if (slicesTime && slicesTime.startTime && slicesTime.endTime) { 479 commonCtx.beginPath(); 480 commonCtx.lineWidth = 1; 481 commonCtx.strokeStyle = slicesTime.color || '#dadada'; 482 let x1 = ns2x(slicesTime.startTime, startNS, endNS, totalNS, frame); 483 let x2 = ns2x(slicesTime.endTime, startNS, endNS, totalNS, frame); 484 commonCtx.moveTo(Math.floor(x1), 0); 485 commonCtx.lineTo(Math.floor(x1), frame.height); 486 commonCtx.moveTo(Math.floor(x2), 0); 487 commonCtx.lineTo(Math.floor(x2), frame.height); 488 commonCtx.stroke(); 489 commonCtx.closePath(); 490 } 491 } 492} 493 494export function drawFlagLineSegment(ctx: any, hoverFlag: any, selectFlag: any, frame: any, tse: TimerShaftElement) { 495 if (ctx) { 496 if (hoverFlag) { 497 ctx.beginPath(); 498 ctx.lineWidth = 2; 499 ctx.strokeStyle = hoverFlag?.color || '#dadada'; 500 ctx.moveTo(Math.floor(hoverFlag.x), 0); 501 ctx.lineTo(Math.floor(hoverFlag.x), frame.height); 502 ctx.stroke(); 503 ctx.closePath(); 504 } 505 if (selectFlag) { 506 ctx.beginPath(); 507 ctx.lineWidth = 2; 508 ctx.strokeStyle = selectFlag?.color || '#dadada'; 509 selectFlag.x = ns2x( 510 selectFlag.time, 511 TraceRow.range!.startNS, 512 TraceRow.range!.endNS, 513 TraceRow.range!.totalNS, 514 frame 515 ); 516 ctx.moveTo(Math.floor(selectFlag.x), 0); 517 ctx.lineTo(Math.floor(selectFlag.x), frame.height); 518 ctx.stroke(); 519 ctx.closePath(); 520 } 521 tse.sportRuler!.slicesTimeList.forEach((slicesTime) => { 522 if (slicesTime && slicesTime.startTime && slicesTime.endTime) { 523 ctx.beginPath(); 524 ctx.lineWidth = 1; 525 ctx.strokeStyle = slicesTime.color || '#dadada'; 526 let x1 = ns2x(slicesTime.startTime, TraceRow.range!.startNS, TraceRow.range!.endNS, TraceRow.range!.totalNS, frame); 527 let x2 = ns2x(slicesTime.endTime, TraceRow.range!.startNS, TraceRow.range!.endNS, TraceRow.range!.totalNS, frame); 528 // 划线逻辑 529 ctx.moveTo(Math.floor(x1), 0); 530 ctx.lineTo(Math.floor(x1), frame.height); //左边的线 531 ctx.moveTo(Math.floor(x2), 0); 532 ctx.lineTo(Math.floor(x2), frame.height); // 右边的线 533 ctx.stroke(); 534 ctx.closePath(); 535 } 536 }); 537 } 538} 539 540export function drawSelection(ctx: any, params: any) { 541 if (params.isRangeSelect && params.rangeSelectObject) { 542 params.rangeSelectObject!.startX = Math.floor( 543 ns2x(params.rangeSelectObject!.startNS!, params.startNS, params.endNS, params.totalNS, params.frame) 544 ); 545 params.rangeSelectObject!.endX = Math.floor( 546 ns2x(params.rangeSelectObject!.endNS!, params.startNS, params.endNS, params.totalNS, params.frame) 547 ); 548 if (ctx) { 549 ctx.globalAlpha = 0.5; 550 ctx.fillStyle = '#666666'; 551 ctx.fillRect( 552 params.rangeSelectObject!.startX!, 553 params.frame.y, 554 params.rangeSelectObject!.endX! - params.rangeSelectObject!.startX!, 555 params.frame.height 556 ); 557 ctx.globalAlpha = 1; 558 } 559 } 560} 561 562// draw range select 563export function drawSelectionRange(context: any, params: TraceRow<any>) { 564 if (params.rangeSelect && TraceRow.rangeSelectObject) { 565 TraceRow.rangeSelectObject!.startX = Math.floor( 566 ns2x( 567 TraceRow.rangeSelectObject!.startNS!, 568 TraceRow.range?.startNS ?? 0, 569 TraceRow.range?.endNS ?? 0, 570 TraceRow.range?.totalNS ?? 0, 571 params.frame 572 ) 573 ); 574 TraceRow.rangeSelectObject!.endX = Math.floor( 575 ns2x( 576 TraceRow.rangeSelectObject!.endNS!, 577 TraceRow.range?.startNS ?? 0, 578 TraceRow.range?.endNS ?? 0, 579 TraceRow.range?.totalNS ?? 0, 580 params.frame 581 ) 582 ); 583 if (context) { 584 context.globalAlpha = 0.5; 585 context.fillStyle = '#666666'; 586 context.fillRect( 587 TraceRow.rangeSelectObject!.startX!, 588 params.frame.y, 589 TraceRow.rangeSelectObject!.endX! - TraceRow.rangeSelectObject!.startX!, 590 params.frame.height 591 ); 592 context.globalAlpha = 1; 593 } 594 } 595} 596 597export function drawWakeUp( 598 wakeUpContext: CanvasRenderingContext2D | any, 599 wake: WakeupBean | undefined | null, 600 startNS: number, 601 endNS: number, 602 totalNS: number, 603 frame: Rect, 604 selectCpuStruct: CpuStruct | undefined = undefined, 605 wakeUpCurrentCpu: number | undefined = undefined, 606 noVerticalLine = false 607) { 608 if (wake) { 609 let x1 = Math.floor(ns2x(wake.wakeupTime || 0, startNS, endNS, totalNS, frame)); 610 wakeUpContext.beginPath(); 611 wakeUpContext.lineWidth = 2; 612 wakeUpContext.fillStyle = '#000000'; 613 if (x1 > 0 && x1 < frame.x + frame.width) { 614 if (!noVerticalLine) { 615 wakeUpContext.moveTo(x1, frame.y); 616 wakeUpContext.lineTo(x1, frame.y + frame.height); 617 } 618 if (wakeUpCurrentCpu == wake.cpu) { 619 let centerY = Math.floor(frame.y + frame.height / 2); 620 wakeUpContext.moveTo(x1, centerY - 6); 621 wakeUpContext.lineTo(x1 + 4, centerY); 622 wakeUpContext.lineTo(x1, centerY + 6); 623 wakeUpContext.lineTo(x1 - 4, centerY); 624 wakeUpContext.lineTo(x1, centerY - 6); 625 wakeUpContext.fill(); 626 } 627 } 628 if (selectCpuStruct) { 629 let x2 = Math.floor(ns2x(selectCpuStruct.startTime || 0, startNS, endNS, totalNS, frame)); 630 let y = frame.y + frame.height - 10; 631 wakeUpContext.moveTo(x1, y); 632 wakeUpContext.lineTo(x2, y); 633 634 let s = ns2s((selectCpuStruct.startTime || 0) - (wake.wakeupTime || 0)); 635 let distance = x2 - x1; 636 if (distance > 12) { 637 wakeUpContext.moveTo(x1, y); 638 wakeUpContext.lineTo(x1 + 6, y - 3); 639 wakeUpContext.moveTo(x1, y); 640 wakeUpContext.lineTo(x1 + 6, y + 3); 641 wakeUpContext.moveTo(x2, y); 642 wakeUpContext.lineTo(x2 - 6, y - 3); 643 wakeUpContext.moveTo(x2, y); 644 wakeUpContext.lineTo(x2 - 6, y + 3); 645 let measure = wakeUpContext.measureText(s); 646 let tHeight = measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent; 647 let xStart = x1 + Math.floor(distance / 2 - measure.width / 2); 648 if (distance > measure.width + 4) { 649 wakeUpContext.fillStyle = '#ffffff'; 650 wakeUpContext.fillRect(xStart - 2, y - 4 - tHeight, measure.width + 4, tHeight + 4); 651 wakeUpContext.font = '10px solid'; 652 wakeUpContext.fillStyle = '#000000'; 653 wakeUpContext.textBaseline = 'bottom'; 654 wakeUpContext.fillText(s, xStart, y - 2); 655 } 656 } 657 } 658 wakeUpContext.strokeStyle = '#000000'; 659 wakeUpContext.stroke(); 660 wakeUpContext.closePath(); 661 } 662} 663 664const wid = 5; 665const linkLineColor = '#ff0000'; 666export function drawLinkLines( 667 context: CanvasRenderingContext2D, 668 nodes: PairPoint[][], 669 tm: TimerShaftElement, 670 isFavorite: boolean 671) { 672 let percentage = 673 (tm.getRange()!.totalNS - Math.abs(tm.getRange()!.endNS - tm.getRange()!.startNS)) / tm.getRange()!.totalNS; 674 let maxWidth = tm.getBoundingClientRect().width - 268; 675 for (let i = 0; i < nodes.length; i++) { 676 let it = nodes[i]; 677 if (it[0].hidden) { 678 continue; 679 } 680 if (isFavorite) { 681 if (!it[0].rowEL.collect && !it[1].rowEL.collect) { 682 continue; 683 } 684 } 685 switch (it[0].lineType) { 686 case LineType.brokenLine: 687 drawBrokenLine(it, maxWidth, context); 688 break; 689 case LineType.bezierCurve: 690 drawBezierCurve(it, maxWidth, context, percentage); 691 break; 692 default: 693 drawBezierCurve(it, maxWidth, context, percentage); 694 } 695 } 696} 697 698function drawBezierCurve(it: PairPoint[], maxWidth: number, context: CanvasRenderingContext2D, percentage: number) { 699 let bezierCurveStart = it[0].x > it[1].x ? it[1] : it[0]; 700 let bezierCurveEnd = it[0].x > it[1].x ? it[0] : it[1]; 701 if (bezierCurveStart && bezierCurveEnd) { 702 //左移到边界,不画线 703 if (bezierCurveStart.x <= 0) { 704 bezierCurveStart.x = -100; 705 } 706 if (bezierCurveEnd.x <= 0) { 707 bezierCurveEnd.x = -100; 708 } 709 //右移到边界,不画线 710 if (bezierCurveStart.x >= maxWidth) { 711 bezierCurveStart.x = maxWidth + 100; 712 } 713 if (bezierCurveEnd.x >= maxWidth) { 714 bezierCurveEnd.x = maxWidth + 100; 715 } 716 context.beginPath(); 717 context.lineWidth = 2; 718 context.fillStyle = linkLineColor; 719 context.strokeStyle = linkLineColor; 720 let x0; 721 let y0; 722 let x1; 723 let x2; 724 let y1; 725 let y2; 726 let x3; 727 let y3; 728 x0 = bezierCurveStart.x ?? 0; 729 y0 = bezierCurveStart.y ?? 0; 730 x3 = bezierCurveEnd.x ?? 0; 731 y3 = bezierCurveEnd.y ?? 0; 732 if (bezierCurveEnd.isRight) { 733 x2 = x3 - 100 * percentage; 734 } else { 735 x2 = x3 + 100 * percentage; 736 } 737 y2 = y3 - 40 * percentage; 738 if (bezierCurveStart.isRight) { 739 x1 = x0 - 100 * percentage; 740 } else { 741 x1 = x0 + 100 * percentage; 742 } 743 y1 = y0 + 40 * percentage; 744 //向右箭头终点在x轴正向有偏移 745 if (!bezierCurveStart.isRight) { 746 x0 -= 5; 747 } 748 context.moveTo(x0, y0); 749 //箭头向左还是向右 750 if (bezierCurveStart.isRight) { 751 context.lineTo(x0 - wid, y0 + wid); 752 context.moveTo(x0, y0); 753 context.lineTo(x0 - wid, y0 - wid); 754 } else { 755 context.lineTo(x0 + wid, y0 + wid); 756 context.moveTo(x0, y0); 757 context.lineTo(x0 + wid, y0 - wid); 758 } 759 context.moveTo(x0, y0); 760 context.bezierCurveTo(x1, y1, x2, y2, x3, y3); 761 context.moveTo(x3, y3); 762 //箭头向左还是向右 763 if (bezierCurveEnd.isRight) { 764 context.lineTo(x3 - wid, y3 + wid); 765 context.moveTo(x3, y3); 766 context.lineTo(x3 - wid, y3 - wid); 767 } else { 768 context.lineTo(x3 + wid, y3 + wid); 769 context.moveTo(x3, y3); 770 context.lineTo(x3 + wid, y3 - wid); 771 } 772 context.moveTo(x3, y3); 773 context.stroke(); 774 context.closePath(); 775 } 776} 777 778function drawBrokenLine(it: PairPoint[], maxWidth: number, context: CanvasRenderingContext2D) { 779 let brokenLineStart = it[0].x > it[1].x ? it[1] : it[0]; 780 let brokenLineEnd = it[0].x > it[1].x ? it[0] : it[1]; 781 if (brokenLineStart && brokenLineEnd) { 782 if (brokenLineStart.x <= 0) { 783 brokenLineStart.x = -100; 784 } 785 if (brokenLineEnd.x <= 0) { 786 brokenLineEnd.x = -100; 787 } 788 //右移到边界,不画线 789 if (brokenLineStart.x >= maxWidth) { 790 brokenLineStart.x = maxWidth + 100; 791 } 792 if (brokenLineEnd.x >= maxWidth) { 793 brokenLineEnd.x = maxWidth + 100; 794 } 795 context.beginPath(); 796 context.lineWidth = 2; 797 context.fillStyle = '#46B1E3'; 798 context.strokeStyle = '#46B1E3'; 799 let x0; 800 let y0; 801 let x1; 802 let y1; 803 let x2; 804 let y2; 805 x0 = brokenLineStart.x ?? 0; 806 y0 = brokenLineStart.y ?? 0; 807 y2 = brokenLineEnd.y ?? 0; 808 x2 = brokenLineEnd.x ?? 0; 809 let leftEndpointX; 810 let leftEndpointY; 811 let rightEndpointX; 812 let rightEndpointY; 813 814 if (brokenLineStart.y < brokenLineEnd.y) { 815 x1 = brokenLineStart.x ?? 0; 816 y1 = brokenLineEnd.y ?? 0; 817 leftEndpointX = x2 - wid; 818 leftEndpointY = y2 - wid; 819 rightEndpointX = x2 - wid; 820 rightEndpointY = y2 + wid; 821 } else { 822 x2 = brokenLineEnd.x - wid ?? 0; 823 x1 = brokenLineEnd.x - wid ?? 0; 824 y1 = brokenLineStart.y ?? 0; 825 leftEndpointX = x2 - wid; 826 leftEndpointY = y2 + wid; 827 rightEndpointX = x2 + wid; 828 rightEndpointY = y2 + wid; 829 } 830 context.moveTo(x0, y0); 831 context.lineTo(x1, y1); 832 context.lineTo(x2, y2); 833 context.stroke(); 834 context.closePath(); 835 context.beginPath(); 836 context.lineWidth = 2; 837 context.fillStyle = '#46B1E3'; 838 context.strokeStyle = '#46B1E3'; 839 context.moveTo(x2, y2); 840 context.lineTo(leftEndpointX, leftEndpointY); 841 context.lineTo(rightEndpointX, rightEndpointY); 842 context.lineTo(x2, y2); 843 context.fill(); 844 context.closePath(); 845 } 846} 847 848export function drawLoading( 849 ctx: CanvasRenderingContext2D, 850 startNS: number, 851 endNS: number, 852 totalNS: number, 853 frame: any, 854 left: number, 855 right: number 856) {} 857 858export function drawString(ctx: CanvasRenderingContext2D, str: string, textPadding: number, frame: Rect, data: any) { 859 if (data.textMetricsWidth === undefined) { 860 data.textMetricsWidth = ctx.measureText(str).width; 861 } 862 let charWidth = Math.round(data.textMetricsWidth / str.length); 863 let fillTextWidth = frame.width - textPadding * 2; 864 if (data.textMetricsWidth < fillTextWidth) { 865 let x2 = Math.floor(frame.width / 2 - data.textMetricsWidth / 2 + frame.x + textPadding); 866 ctx.fillText(str, x2, Math.floor(frame.y + frame.height / 2), fillTextWidth); 867 } else { 868 if (fillTextWidth >= charWidth) { 869 let chatNum = fillTextWidth / charWidth; 870 let x1 = frame.x + textPadding; 871 if (chatNum < 2) { 872 ctx.fillText(str.substring(0, 1), x1, Math.floor(frame.y + frame.height / 2), fillTextWidth); 873 } else { 874 ctx.fillText(str.substring(0, chatNum - 1) + '...', x1, Math.floor(frame.y + frame.height / 2), fillTextWidth); 875 } 876 } 877 } 878} 879 880export function drawString2Line( 881 ctx: CanvasRenderingContext2D, 882 str1: string, 883 str2: string, 884 textPadding: number, 885 frame: Rect, 886 data: any 887) { 888 if (frame.height < 30) return; 889 if (data.textMetrics1Width === undefined) { 890 data.textMetrics1Width = ctx.measureText(str1).width; 891 data.textMetrics2Width = ctx.measureText(str2).width; 892 } 893 let charWidth = Math.round(data.textMetrics1Width / str1.length); 894 let fillTextWidth = frame.width - textPadding * 2; 895 let y1 = frame.y + 12; 896 let y2 = y1 + 14; 897 if (data.textMetrics1Width < fillTextWidth) { 898 let x = Math.floor(frame.width / 2 - data.textMetrics1Width / 2 + frame.x + textPadding - 1); 899 ctx.fillText(str1, x, y1, fillTextWidth); 900 } else { 901 if (fillTextWidth >= charWidth) { 902 let chatNum = fillTextWidth / charWidth; 903 let x = frame.x + textPadding - 1; 904 if (chatNum < 2) { 905 ctx.fillText(str1.substring(0, 1), x, y1, fillTextWidth); 906 } else { 907 ctx.fillText(str1.substring(0, chatNum - 1) + '...', x, y1, fillTextWidth); 908 } 909 } 910 } 911 if (data.textMetrics2Width < fillTextWidth) { 912 let x = Math.floor(frame.width / 2 - data.textMetrics2Width / 2 + frame.x + textPadding - 1); 913 ctx.fillText(str2, x, y2, fillTextWidth); 914 } else { 915 if (fillTextWidth >= charWidth) { 916 let chatNum = fillTextWidth / charWidth; 917 let x = frame.x + textPadding - 1; 918 if (chatNum < 2) { 919 ctx.fillText(str2.substring(0, 1), x, y2, fillTextWidth); 920 } else { 921 ctx.fillText(str2.substring(0, chatNum - 1) + '...', x, y2, fillTextWidth); 922 } 923 } 924 } 925} 926 927export function hiPerf( 928 arr: Array<any>, 929 arr2: Array<any>, 930 res: Array<any>, 931 startNS: number, 932 endNS: number, 933 frame: any, 934 groupBy10MS: boolean, 935 use: boolean 936) { 937 if (use && res.length > 0) { 938 let pns = (endNS - startNS) / frame.width; 939 let y = frame.y; 940 for (let i = 0; i < res.length; i++) { 941 let it = res[i]; 942 if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) { 943 if (!it.frame) { 944 it.frame = {}; 945 it.frame.y = y; 946 } 947 it.frame.height = it.height; 948 HiPerfStruct.setFrame(it, pns, startNS, endNS, frame); 949 } else { 950 it.frame = null; 951 } 952 } 953 return; 954 } 955 res.length = 0; 956 if (arr) { 957 let list = groupBy10MS ? arr2 : arr; 958 let pns = (endNS - startNS) / frame.width; 959 let y = frame.y; 960 for (let i = 0, len = list.length; i < len; i++) { 961 let it = list[i]; 962 if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) { 963 if (!list[i].frame) { 964 list[i].frame = {}; 965 list[i].frame.y = y; 966 } 967 list[i].frame.height = it.height; 968 HiPerfStruct.setFrame(list[i], pns, startNS, endNS, frame); 969 if (groupBy10MS) { 970 if ( 971 i > 0 && 972 (list[i - 1].frame?.x || 0) == (list[i].frame?.x || 0) && 973 (list[i - 1].frame?.width || 0) == (list[i].frame?.width || 0) && 974 (list[i - 1].frame?.height || 0) == (list[i].frame?.height || 0) 975 ) { 976 } else { 977 res.push(list[i]); 978 } 979 } else { 980 if (i > 0 && Math.abs((list[i - 1].frame?.x || 0) - (list[i].frame?.x || 0)) < 4) { 981 } else { 982 res.push(list[i]); 983 } 984 } 985 } 986 } 987 } 988} 989 990export class HiPerfStruct extends BaseStruct { 991 static hoverStruct: HiPerfStruct | undefined; 992 static selectStruct: HiPerfStruct | undefined; 993 id: number | undefined; 994 callchain_id: number | undefined; 995 timestamp: number | undefined; 996 thread_id: number | undefined; 997 event_count: number | undefined; 998 event_type_id: number | undefined; 999 cpu_id: number | undefined; 1000 thread_state: string | undefined; 1001 startNS: number | undefined; 1002 endNS: number | undefined; 1003 dur: number | undefined; 1004 height: number | undefined; 1005 1006 static drawRoundRectPath(cxt: Path2D, x: number, y: number, width: number, height: number, radius: number) { 1007 cxt.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2); 1008 cxt.lineTo(x + radius, y + height); 1009 cxt.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI); 1010 cxt.lineTo(x + 0, y + radius); 1011 cxt.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2); 1012 cxt.lineTo(x + width - radius, y + 0); 1013 cxt.arc(x + width - radius, y + radius, radius, (Math.PI * 3) / 2, Math.PI * 2); 1014 cxt.lineTo(x + width, y + height - radius); 1015 cxt.moveTo(x + width / 3, y + height / 5); 1016 cxt.lineTo(x + width / 3, y + (height / 5) * 4); 1017 cxt.moveTo(x + width / 3, y + height / 5); 1018 cxt.bezierCurveTo( 1019 x + width / 3 + 7, 1020 y + height / 5 - 2, 1021 x + width / 3 + 7, 1022 y + height / 5 + 6, 1023 x + width / 3, 1024 y + height / 5 + 4 1025 ); 1026 } 1027 1028 static draw(ctx: CanvasRenderingContext2D, normalPath: Path2D, specPath: Path2D, data: any, groupBy10MS: boolean) { 1029 if (data.frame) { 1030 if (groupBy10MS) { 1031 let width = data.frame.width; 1032 normalPath.rect(data.frame.x, 40 - (data.height || 0), width, data.height || 0); 1033 } else { 1034 let path = data.callchain_id === -1 ? specPath : normalPath; 1035 path.moveTo(data.frame.x + 7, 20); 1036 HiPerfStruct.drawRoundRectPath(path, data.frame.x - 7, 20 - 7, 14, 14, 3); 1037 path.moveTo(data.frame.x, 27); 1038 path.lineTo(data.frame.x, 33); 1039 } 1040 } 1041 } 1042 1043 static drawSpecialPath(ctx: CanvasRenderingContext2D, specPath: Path2D) { 1044 ctx.strokeStyle = '#9fafc4'; 1045 ctx.globalAlpha = 0.5; 1046 ctx.stroke(specPath); 1047 ctx.globalAlpha = 1; 1048 } 1049 1050 static setFrame(node: any, pns: number, startNS: number, endNS: number, frame: any) { 1051 if ((node.startNS || 0) < startNS) { 1052 node.frame.x = 0; 1053 } else { 1054 node.frame.x = Math.floor(((node.startNS || 0) - startNS) / pns); 1055 } 1056 if ((node.startNS || 0) + (node.dur || 0) > endNS) { 1057 node.frame.width = frame.width - node.frame.x; 1058 } else { 1059 node.frame.width = Math.ceil(((node.startNS || 0) + (node.dur || 0) - startNS) / pns - node.frame.x); 1060 } 1061 if (node.frame.width < 1) { 1062 node.frame.width = 1; 1063 } 1064 } 1065 1066 static groupBy10MS(groupArray: Array<any>, intervalPerf: number, maxCpu?: number | undefined): Array<any> { 1067 let obj = groupArray 1068 .map((it) => { 1069 it.timestamp_group = Math.trunc(it.startNS / 1_000_000_0) * 1_000_000_0; 1070 return it; 1071 }) 1072 .reduce((pre, current) => { 1073 (pre[current['timestamp_group']] = pre[current['timestamp_group']] || []).push(current); 1074 return pre; 1075 }, {}); 1076 let arr = []; 1077 for (let aKey in obj) { 1078 let ns = parseInt(aKey); 1079 let height: number = 0; 1080 if (maxCpu != undefined) { 1081 height = Math.floor((obj[aKey].length / (10 / intervalPerf) / maxCpu) * 40); 1082 } else { 1083 height = Math.floor((obj[aKey].length / (10 / intervalPerf)) * 40); 1084 } 1085 arr.push({ 1086 startNS: ns, 1087 dur: 1_000_000_0, 1088 height: height, 1089 }); 1090 } 1091 return arr; 1092 } 1093} 1094 1095function setMemFrame(node: any, padding: number, startNS: number, endNS: number, totalNS: number, frame: any) { 1096 let x1: number; 1097 let x2: number; 1098 if ((node.startTime || 0) <= startNS) { 1099 x1 = 0; 1100 } else { 1101 x1 = ns2x(node.startTime || 0, startNS, endNS, totalNS, frame); 1102 } 1103 if ((node.startTime || 0) + (node.duration || 0) >= endNS) { 1104 x2 = frame.width; 1105 } else { 1106 x2 = ns2x((node.startTime || 0) + (node.duration || 0), startNS, endNS, totalNS, frame); 1107 } 1108 let getV: number = x2 - x1 <= 1 ? 1 : x2 - x1; 1109 if (!node.frame) { 1110 node.frame = {}; 1111 } 1112 node.frame.x = Math.floor(x1); 1113 node.frame.y = Math.floor(frame.y + padding); 1114 node.frame.width = Math.ceil(getV); 1115 node.frame.height = Math.floor(frame.height - padding * 2); 1116} 1117 1118export function mem( 1119 list: Array<any>, 1120 memFilter: Array<any>, 1121 startNS: number, 1122 endNS: number, 1123 totalNS: number, 1124 frame: any, 1125 use: boolean 1126) { 1127 if (use && memFilter.length > 0) { 1128 for (let i = 0, len = memFilter.length; i < len; i++) { 1129 if ( 1130 (memFilter[i].startTime || 0) + (memFilter[i].duration || 0) > startNS && 1131 (memFilter[i].startTime || 0) < endNS 1132 ) { 1133 setMemFrame(memFilter[i], 5, startNS, endNS, totalNS, frame); 1134 } else { 1135 memFilter[i].frame = null; 1136 } 1137 } 1138 return; 1139 } 1140 memFilter.length = 0; 1141 if (list) { 1142 for (let i = 0, len = list.length; i < len; i++) { 1143 let it = list[i]; 1144 if ((it.startTime || 0) + (it.duration || 0) > startNS && (it.startTime || 0) < endNS) { 1145 setMemFrame(list[i], 5, startNS, endNS, totalNS, frame); 1146 if ( 1147 i > 0 && 1148 (list[i - 1].frame?.x || 0) == (list[i].frame?.x || 0) && 1149 (list[i - 1].frame?.width || 0) == (list[i].frame?.width || 0) 1150 ) { 1151 } else { 1152 memFilter.push(list[i]); 1153 } 1154 } 1155 } 1156 } 1157} 1158 1159export function drawWakeUpList( 1160 wakeUpListContext: CanvasRenderingContext2D | any, 1161 wake: WakeupBean | undefined | null, 1162 startNS: number, 1163 endNS: number, 1164 totalNS: number, 1165 frame: Rect, 1166 wakeup: WakeupBean | undefined = undefined, 1167 currentCpu: number | undefined | null = undefined, 1168 noVerticalLine = false, 1169) { 1170 if (wake) { 1171 let x1 = Math.floor( 1172 ns2x(wake.wakeupTime || 0, startNS, endNS, totalNS, frame) 1173 ); 1174 wakeUpListContext.beginPath(); 1175 wakeUpListContext.lineWidth = 2; 1176 wakeUpListContext.fillStyle = '#000000'; 1177 if (x1 > 0 && x1 < frame.x + frame.width) { 1178 if (!noVerticalLine) { 1179 wakeUpListContext.moveTo(x1, frame.y); 1180 wakeUpListContext.lineTo(x1, frame.y + frame.height); 1181 } 1182 if (currentCpu == wake.cpu) { 1183 let centerY = Math.floor(frame.y + frame.height / 2); 1184 wakeUpListContext.moveTo(x1, centerY - 6); 1185 wakeUpListContext.lineTo(x1 + 4, centerY); 1186 wakeUpListContext.lineTo(x1, centerY + 6); 1187 wakeUpListContext.lineTo(x1 - 4, centerY); 1188 wakeUpListContext.lineTo(x1, centerY - 6); 1189 wakeUpListContext.fill(); 1190 } 1191 } 1192 if (wakeup) { 1193 let x2 = Math.floor( 1194 ns2x( 1195 wakeup.ts || 0, 1196 startNS, 1197 endNS, 1198 totalNS, 1199 frame 1200 ) 1201 ); 1202 let y = frame.y + frame.height - 10; 1203 wakeUpListContext.moveTo(x1, y); 1204 wakeUpListContext.lineTo(x2, y); 1205 wakeUpListContext.moveTo(x2, y - 25); 1206 wakeUpListContext.lineTo(x2, y + 5); 1207 1208 let s = ns2s( 1209 (wakeup.ts || 0) - (wake.wakeupTime || 0) 1210 ); 1211 let wakeUpListDistance = x2 - x1; 1212 if (wakeUpListDistance > 12) { 1213 wakeUpListContext.moveTo(x1, y); 1214 wakeUpListContext.lineTo(x1 + 6, y - 3); 1215 wakeUpListContext.moveTo(x1, y); 1216 wakeUpListContext.lineTo(x1 + 6, y + 3); 1217 wakeUpListContext.moveTo(x2, y); 1218 wakeUpListContext.lineTo(x2 - 6, y - 3); 1219 wakeUpListContext.moveTo(x2, y); 1220 wakeUpListContext.lineTo(x2 - 6, y + 3); 1221 let measure = wakeUpListContext.measureText(s); 1222 let tHeight = 1223 measure.actualBoundingBoxAscent + 1224 measure.actualBoundingBoxDescent; 1225 let xStart = x1 + Math.floor(wakeUpListDistance / 2 - measure.width / 2); 1226 if (wakeUpListDistance > measure.width + 4) { 1227 wakeUpListContext.fillStyle = '#ffffff'; 1228 wakeUpListContext.fillRect( 1229 xStart - 2, 1230 y - 4 - tHeight, 1231 measure.width + 4, 1232 tHeight + 4 1233 ); 1234 wakeUpListContext.font = '10px solid'; 1235 wakeUpListContext.fillStyle = '#000000'; 1236 wakeUpListContext.textBaseline = 'bottom'; 1237 wakeUpListContext.fillText(s, xStart, y - 2); 1238 } 1239 } 1240 } 1241 wakeUpListContext.strokeStyle = '#000000'; 1242 wakeUpListContext.stroke(); 1243 wakeUpListContext.closePath(); 1244 } 1245} 1246