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 { BaseElement, element } from '../../../../base-ui/BaseElement'; 17import { type LitTable } from '../../../../base-ui/table/lit-table'; 18import '../../../../base-ui/table/lit-table-column'; 19import { AllAppStartupStruct } from '../../../database/ui-worker/ProcedureWorkerAllAppStartup'; 20 21import { type WakeupBean } from '../../../bean/WakeupBean'; 22import { SpApplication } from '../../../SpApplication'; 23import { TraceRow } from '../base/TraceRow'; 24import { CpuStruct } from '../../../database/ui-worker/cpu/ProcedureWorkerCPU'; 25import { ThreadStruct } from '../../../database/ui-worker/ProcedureWorkerThread'; 26import { FuncStruct } from '../../../database/ui-worker/ProcedureWorkerFunc'; 27import { ProcessMemStruct } from '../../../database/ui-worker/ProcedureWorkerMem'; 28import { ClockStruct } from '../../../database/ui-worker/ProcedureWorkerClock'; 29import { ColorUtils } from '../base/ColorUtils'; 30import { IrqStruct } from '../../../database/ui-worker/ProcedureWorkerIrq'; 31import { BinderArgBean } from '../../../bean/BinderArgBean'; 32import { JankStruct } from '../../../database/ui-worker/ProcedureWorkerJank'; 33import { Utils } from '../base/Utils'; 34import { SpSystemTrace } from '../../SpSystemTrace'; 35import { AppStartupStruct } from '../../../database/ui-worker/ProcedureWorkerAppStartup'; 36import { SoStruct } from '../../../database/ui-worker/ProcedureWorkerSoInit'; 37import { type SelectionParam } from '../../../bean/BoxSelection'; 38import { type FrameAnimationStruct } from '../../../database/ui-worker/ProcedureWorkerFrameAnimation'; 39import { 40 queryBinderByArgsId, 41 queryBinderBySliceId, 42 queryFlowsData, 43 queryPrecedingData, 44 queryThreadByItid, 45 queryFpsSourceList, 46 queryStateFreqList, 47} from '../../../database/sql/SqlLite.sql'; 48import { 49 queryBinderArgsByArgset, 50 queryRunnableTimeByRunning, 51 queryThreadNearData, 52 queryThreadStateArgs, 53 queryThreadWakeUp, 54 queryThreadWakeUpFrom, 55} from '../../../database/sql/ProcessThread.sql'; 56import { queryGpuDur } from '../../../database/sql/Gpu.sql'; 57import { queryWakeupListPriority } from '../../../database/sql/Cpu.sql'; 58import { TabPaneCurrentSelectionHtml } from './TabPaneCurrentSelection.html'; 59import { queryRealTime } from '../../../database/sql/Clock.sql'; 60import { PerfToolStruct } from '../../../database/ui-worker/ProcedureWorkerPerfTool'; 61 62const INPUT_WORD = 63 'This is the interval from when the task became eligible to run \n(e.g.because of notifying a wait queue it was a suspended on) to\n when it started running.'; 64 65const CLOCK_STARTTIME_ABSALUTED_ID: string = 'clockStartTimeAbsaluteId'; 66const CLOCK_TRANSF_BTN_ID: string = 'clockTransfBtnId'; 67const CPU_STARTTIME_ABSALUTED_ID: string = 'cpuStartTimeAbsaluteId'; 68const CPU_TRANSF_BTN_ID: string = 'cpuTransfBtnId'; 69const THREAD_STARTTIME_ABSALUTED_ID: string = 'threadStartTimeAbsaluteId'; 70const THREAD_TRANSF_BTN_ID: string = 'threadTransfBtnId'; 71const FUN_STARTTIME_ABSALUTED_ID: string = 'funStartTimeAbsaluteId'; 72const FUN_TRANSF_BTN_ID: string = 'funTransfBtnId'; 73 74export function getTimeString(ns: number): string { 75 if (ns === 0) { 76 return '0'; 77 } 78 let currentTimeNs = ns; 79 let hour1 = 3600_000_000_000; 80 let minute1 = 60_000_000_000; 81 let second1 = 1_000_000_000; // 1 second 82 let millisecond1 = 1_000_000; // 1 millisecond 83 let microsecond1 = 1_000; // 1 microsecond 84 let res = ''; 85 if (currentTimeNs >= hour1) { 86 res += Math.floor(currentTimeNs / hour1) + 'h '; 87 currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / hour1) * hour1; 88 } 89 if (currentTimeNs >= minute1) { 90 res += Math.floor(currentTimeNs / minute1) + 'm '; 91 currentTimeNs = currentTimeNs - Math.floor(ns / minute1) * minute1; 92 } 93 if (currentTimeNs >= second1) { 94 res += Math.floor(currentTimeNs / second1) + 's '; 95 currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / second1) * second1; 96 } 97 if (currentTimeNs >= millisecond1) { 98 res += Math.floor(currentTimeNs / millisecond1) + 'ms '; 99 currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / millisecond1) * millisecond1; 100 } 101 if (currentTimeNs >= microsecond1) { 102 res += Math.floor(currentTimeNs / microsecond1) + 'μs '; 103 currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / microsecond1) * microsecond1; 104 } 105 if (currentTimeNs > 0) { 106 res += currentTimeNs.toFixed(0) + 'ns '; 107 } 108 return res; 109} 110 111@element('tabpane-current-selection') 112export class TabPaneCurrentSelection extends BaseElement { 113 weakUpBean: WakeupBean | null | undefined; 114 selectWakeupBean: unknown; 115 private currentSelectionTbl: LitTable | null | undefined; 116 private tableObserver: MutationObserver | undefined; 117 private wakeupListTbl: LitTable | undefined | null; 118 private scrollView: HTMLDivElement | null | undefined; 119 // @ts-ignore 120 private dpr = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1; 121 private wakeUp: string = ''; 122 private isFpsAvailable: boolean = true; 123 private realTime: number = 0; 124 private bootTime: number = 0; 125 126 set data(selection: unknown) { 127 // @ts-ignore 128 if (selection !== undefined && selection.constructor && selection.constructor.name !== 'SelectionParam') { 129 // @ts-ignore 130 this.setCpuData(selection); 131 } 132 } 133 134 /** 135 * 创建StartTime的Dom节点 136 * @param selectTable面板的dom树对象 137 * @param startTs 开始时间 138 * @param transfBtnId 转换按钮id 139 * @param startTimeAbsaluteId 开始时间的id 140 */ 141 createStartTimeNode(list: unknown[], startTs: number, transfBtnId: string, startTimeAbsaluteId: string): void { 142 let timeStr: string = ''; 143 let startTimeValue: string = ''; 144 // @ts-ignore 145 let startTimeAbsolute = startTs + (window as unknown).recordStartNS; 146 if (this.realTime > 0) { 147 if (Utils.isTransformed) { 148 timeStr = this.getRealTimeStr(startTimeAbsolute); 149 } else { 150 timeStr = startTimeAbsolute / 1000000000 + 's'; 151 } 152 startTimeValue = `<div style="white-space: nowrap;display: flex;align-items: center"> 153 <div id="${startTimeAbsaluteId}" style="white-space:pre-wrap" >${timeStr}</div> 154 <lit-icon id="${transfBtnId}" class="temp-icon" title="Convert to realtime" name="restore" size="30" 155 style="position: relative; top: 5px; left: 10px;"></lit-icon> 156 </div>`; 157 } else { 158 startTimeValue = startTimeAbsolute / 1000000000 + 's'; 159 } 160 list.push({ 161 name: 'StartTime(Absolute)', 162 value: startTimeValue, 163 }); 164 } 165 166 /** 167 * 给转换按钮添加点击事件 168 * @param startTimeAbsolute 开始时间 169 * @param transfBtnId 转换按钮id 170 * @param startTimeAbsaluteId 开始时间ID 171 */ 172 addClickToTransfBtn(startTimeAbsolute: number, transfBtnId: string, startTimeAbsaluteId: string): void { 173 let transfBtn = this.currentSelectionTbl?.shadowRoot?.querySelector(`#${transfBtnId}`); 174 transfBtn?.addEventListener('click', () => { 175 let startTimeAbsalute = this.currentSelectionTbl?.shadowRoot?.querySelector(`#${startTimeAbsaluteId}`); 176 if (startTimeAbsalute) { 177 if (Utils.isTransformed) { 178 startTimeAbsalute!.innerHTML = startTimeAbsolute / 1000000000 + 's'; 179 Utils.isTransformed = false; 180 } else { 181 startTimeAbsalute!.innerHTML = this.getRealTimeStr(startTimeAbsolute); 182 Utils.isTransformed = true; 183 } 184 } 185 }); 186 } 187 188 async setCpuData( 189 data: CpuStruct, 190 callback: ((data: WakeupBean | null) => void) | undefined = undefined, 191 scrollCallback?: (data: CpuStruct) => void 192 ): Promise<void> { 193 await this.setRealTime(); 194 this.setTableHeight('650px'); 195 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 196 if (leftTitle) { 197 leftTitle.innerText = 'Slice Details'; 198 } 199 let list: unknown[] = []; 200 this.updateUI(data, list); 201 Promise.all([this.queryThreadStateDArgs(data.argSetID), this.queryCPUWakeUpFromData(data)]).then((resArr) => { 202 let args = resArr[0]; 203 let bean = resArr[1]; 204 if (callback) { 205 callback(bean); 206 } 207 if (args.length > 0) { 208 args.forEach((arg) => { 209 list.push({ name: arg.keyName, value: arg.strValue }); 210 }); 211 } 212 this.currentSelectionTbl!.dataSource = list; 213 // @ts-ignore 214 let startTimeAbsolute = (data.startTime || 0) + (window as unknown).recordStartNS; 215 this.addClickToTransfBtn(startTimeAbsolute, CPU_TRANSF_BTN_ID, CPU_STARTTIME_ABSALUTED_ID); 216 217 let rightArea: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#table-right'); 218 let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle'); 219 let rightButton: HTMLElement | null | undefined = this?.shadowRoot 220 ?.querySelector('#rightButton') 221 ?.shadowRoot?.querySelector('#custom-button'); 222 let rightStar: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#right-star'); 223 this.threadClickEvent(scrollCallback, data); 224 let canvas = this.initCanvas(); 225 if (bean !== null) { 226 this.selectWakeupBean = { 227 process: `${this.transferString(data.processName || 'Process')}(${data.processId})`, 228 thread: `${this.transferString(data.name || 'Thread')}(${data.tid})`, 229 cpu: data.cpu, 230 dur: data.dur, 231 priority: data.priority, 232 isSelected: false, 233 }; 234 235 this.weakUpBean = bean; 236 this.updateRightTitleUI(rightArea!, rightTitle!, rightButton!, rightStar!); 237 this.drawRight(canvas, bean); 238 } else { 239 this.handleNullBeanUI(rightArea!, rightTitle!, rightButton!, rightStar!); 240 } 241 }); 242 } 243 244 private threadClickEvent(scrollCallback: ((data: CpuStruct) => void) | undefined, data: CpuStruct): void { 245 let threadClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#thread-id'); 246 threadClick?.addEventListener('click', () => { 247 //cpu点击 248 if (scrollCallback) { 249 data.state = 'Running'; 250 scrollCallback(data); 251 } 252 }); 253 } 254 255 private updateRightTitleUI( 256 rightArea: HTMLElement, 257 rightTitle: HTMLElement, 258 rightButton: HTMLElement, 259 rightStar: HTMLElement 260 ): void { 261 if (rightArea !== null && rightArea) { 262 rightArea.style.visibility = 'visible'; 263 } 264 if (rightTitle !== null && rightTitle) { 265 rightTitle.style.visibility = 'visible'; 266 rightButton!.style.visibility = 'visible'; 267 rightStar!.style.visibility = 'hidden'; 268 SpSystemTrace.btnTimer = null; 269 } 270 } 271 272 private handleNullBeanUI( 273 rightArea: HTMLElement, 274 rightTitle: HTMLElement, 275 rightButton: HTMLElement, 276 rightStar: HTMLElement 277 ): void { 278 this.weakUpBean = null; 279 if (rightArea !== null && rightArea) { 280 rightArea.style.visibility = 'hidden'; 281 } 282 if (rightTitle !== null && rightTitle) { 283 rightTitle.style.visibility = 'hidden'; 284 rightButton!.style.visibility = 'hidden'; 285 rightStar!.style.visibility = 'hidden'; 286 } 287 } 288 289 private updateUI(data: CpuStruct, list: unknown[]): void { 290 let process = this.transferString(data.processName || 'Process'); 291 let processId = data.processId || data.tid; 292 let state = ''; 293 if (data.end_state) { 294 state = Utils.getEndState(data.end_state); 295 } else if (data.end_state === '' || data.end_state === null) { 296 state = ''; 297 } else { 298 state = 'Unknown State'; 299 } 300 list.push({ 301 name: 'Process', 302 value: `${process || 'Process'} [${processId}]`, 303 }); 304 let name = this.transferString(data.name ?? ''); 305 if (data.processId) { 306 list.push({ 307 name: 'Thread', 308 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 309<div style="white-space:pre-wrap">${name || 'Process'} [${data.tid}]</div> 310<lit-icon style="cursor:pointer;margin-left: 5px" id="thread-id" name="select" color="#7fa1e7" size="20"></lit-icon> 311</div>`, 312 }); 313 } else { 314 list.push({ 315 name: 'Thread', 316 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 317<div style="white-space:pre-wrap">${name || 'Process'} [${data.tid}]</div> 318</div>`, 319 }); 320 } 321 322 list.push({ name: 'StartTime(Relative)', value: getTimeString(data.startTime || 0) }); 323 this.createStartTimeNode(list, data.startTime || 0, CPU_TRANSF_BTN_ID, CPU_STARTTIME_ABSALUTED_ID); 324 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 325 list.push({ name: 'Prio', value: data.priority || 0 }); 326 list.push({ name: 'End State', value: state }); 327 } 328 // 设置真实时间和启动时间的值 329 private async setRealTime(): Promise<unknown> { 330 return queryRealTime().then((result) => { 331 if (result && result.length > 0) { 332 result.forEach((item) => { 333 if (item.name === 'realtime') { 334 this.realTime = item.ts; 335 } else { 336 this.bootTime = item.ts; 337 } 338 }); 339 } 340 }); 341 } 342 343 async setFunctionData(data: FuncStruct, scrollCallback: Function): Promise<void> { 344 //方法信息 345 await this.setRealTime(); 346 this.tabCurrentSelectionInit('Slice Details'); 347 let list: unknown[] = []; 348 let name = this.transferString(data.funName ?? ''); 349 let isBinder = FuncStruct.isBinder(data); 350 let isAsyncBinder = isBinder && FuncStruct.isBinderAsync(data); 351 if (data.argsetid !== undefined && data.argsetid !== null && data.argsetid >= 0) { 352 this.setTableHeight('700px'); 353 if (isAsyncBinder) { 354 this.handleAsyncBinder(data, list, name, scrollCallback); 355 } else if (isBinder) { 356 this.handleBinder(data, list, name, scrollCallback); 357 } else { 358 this.handleNonBinder(data, list, name); 359 } 360 } else { 361 this.setTableHeight('auto'); 362 list.push({ name: 'Name', value: name }); 363 list.push({ 364 name: 'StartTime(Relative)', 365 value: getTimeString(data.startTs || 0), 366 }); 367 this.createStartTimeNode(list, data.startTs || 0, FUN_TRANSF_BTN_ID, FUN_STARTTIME_ABSALUTED_ID); 368 list.push({ 369 name: 'Duration', 370 value: getTimeString(data.dur || 0), 371 }); 372 list.push({ name: 'depth', value: data.depth }); 373 this.currentSelectionTbl!.dataSource = list; 374 // @ts-ignore 375 let startTimeAbsolute = (data.startTs || 0) + (window as unknown).recordStartNS; 376 this.addClickToTransfBtn(startTimeAbsolute, FUN_TRANSF_BTN_ID, FUN_STARTTIME_ABSALUTED_ID); 377 } 378 } 379 380 // 计算真实时间 381 private getRealTimeStr(startTs: number): string { 382 // @ts-ignore 383 let time = (startTs || 0) + (window as unknown).recordStartNS - this.bootTime + this.realTime; 384 const formateDateStr = 385 this.getDate(parseInt(time.toString().substring(0, 13))) + '.' + time.toString().substring(10); 386 return formateDateStr; 387 } 388 389 // 格式化时间戳为字符串格式 yyyy/mm/dd hh:mi:ss 390 private getDate(timestamp: number): string { 391 let date = new Date(timestamp); 392 let gmt = date.toLocaleString(); 393 return gmt; 394 } 395 396 private handleNonBinder(data: FuncStruct, list: unknown[], name: string): void { 397 queryBinderArgsByArgset(data.argsetid!).then((argset) => { 398 list.push({ name: 'Name', value: name }); 399 argset.forEach((item) => { 400 list.push({ name: item.keyName, value: item.strValue }); 401 }); 402 this.addTabPanelContent(list, data); 403 this.currentSelectionTbl!.dataSource = list; 404 }); 405 } 406 407 private handleBinder(data: FuncStruct, list: unknown[], name: string, scrollCallback: Function): void { 408 queryBinderArgsByArgset(data.argsetid!).then((argset) => { 409 let binderSliceId = -1; 410 argset.forEach((item) => { 411 if (item.keyName === 'destination slice id') { 412 binderSliceId = Number(item.strValue); 413 list.unshift({ 414 name: 'Name', 415 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 416<div style="white-space:pre-wrap">${name || 'binder'}</div> 417<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="function-jump" name="select" color="#7fa1e7" size="20"></lit-icon> 418</div>`, 419 }); 420 } 421 list.push({ name: item.keyName, value: item.strValue }); 422 }); 423 if (binderSliceId === -1) { 424 list.unshift({ name: 'Name', value: name }); 425 } 426 this.addTabPanelContent(list, data); 427 this.currentSelectionTbl!.dataSource = list; 428 let funcClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#function-jump'); 429 funcClick?.addEventListener('click', () => { 430 if (!Number.isNaN(binderSliceId) && binderSliceId !== -1) { 431 queryBinderBySliceId(binderSliceId).then((result: unknown[]) => { 432 if (result.length > 0) { 433 // @ts-ignore 434 result[0].type = TraceRow.ROW_TYPE_FUNC; 435 scrollCallback(result[0]); 436 } 437 }); 438 } 439 }); 440 }); 441 } 442 443 private handleAsyncBinder(data: FuncStruct, list: unknown[], name: string, scrollCallback: Function): void { 444 Promise.all([ 445 queryBinderByArgsId(data.argsetid!, data.startTs!, !data.funName!.endsWith('rcv')), 446 queryBinderArgsByArgset(data.argsetid!), 447 ]).then((result) => { 448 let asyncBinderRes = result[0]; 449 let argsBinderRes = result[1]; 450 let asyncBinderStract: unknown; 451 if (asyncBinderRes.length > 0) { 452 //@ts-ignore 453 asyncBinderRes[0].type = TraceRow.ROW_TYPE_FUNC; 454 asyncBinderStract = asyncBinderRes[0]; 455 } 456 if (argsBinderRes.length > 0) { 457 argsBinderRes.forEach((item) => { 458 list.push({ 459 name: item.keyName, 460 value: item.strValue, 461 }); 462 }); 463 } 464 if (asyncBinderStract !== undefined) { 465 list.unshift({ 466 name: 'Name', 467 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 468<div style="white-space:pre-wrap">${name || 'binder'}</div> 469<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="function-jump" name="select" color="#7fa1e7" size="20"></lit-icon> 470</div>`, 471 }); 472 } else { 473 list.unshift({ name: 'Name', value: name }); 474 } 475 this.addTabPanelContent(list, data); 476 this.currentSelectionTbl!.dataSource = list; 477 let funcClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#function-jump'); 478 funcClick?.addEventListener('click', () => { 479 scrollCallback(asyncBinderStract); 480 }); 481 }); 482 } 483 484 private addTabPanelContent(contentList: unknown[], data: FuncStruct): void { 485 contentList.push({ 486 name: 'StartTime(Relative)', 487 value: getTimeString(data.startTs || 0), 488 }); 489 contentList.push({ 490 name: 'StartTime(Absolute)', 491 // @ts-ignore 492 value: ((data.startTs || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 493 }); 494 contentList.push({ 495 name: 'Duration', 496 value: getTimeString(data.dur || 0), 497 }); 498 contentList.push({ name: 'depth', value: data.depth }); 499 if (data.argsetid && data.argsetid > -1) { 500 contentList.push({ name: 'arg_set_id', value: data.argsetid }); 501 } 502 } 503 504 private tabCurrentSelectionInit(leftTitleStr: string): void { 505 this.initCanvas(); 506 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 507 this.setTitleAndButtonStyle(); 508 if (leftTitle) { 509 leftTitle.innerText = leftTitleStr; 510 } 511 } 512 513 private setTitleAndButtonStyle(): void { 514 let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle'); 515 let rightButton: HTMLElement | null | undefined = this?.shadowRoot 516 ?.querySelector('#rightButton') 517 ?.shadowRoot?.querySelector('#custom-button'); 518 let rightStar: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#right-star'); 519 if (rightTitle) { 520 rightTitle.style.visibility = 'hidden'; 521 rightButton!.style.visibility = 'hidden'; 522 rightStar!.style.visibility = 'hidden'; 523 } 524 } 525 526 async setClockData(data: ClockStruct): Promise<void> { 527 await this.setRealTime(); 528 this.setTableHeight('auto'); 529 //时钟信息 530 this.tabCurrentSelectionInit('Counter Details'); 531 let list: unknown[] = []; 532 list.push({ 533 name: 'StartTime(Relative)', 534 value: getTimeString(data.startNS || 0), 535 }); 536 this.createStartTimeNode(list, data.startNS || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 537 list.push({ 538 name: 'Value', 539 value: ColorUtils.formatNumberComma(data.value || 0), 540 }); 541 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 542 this.currentSelectionTbl!.dataSource = list; 543 // @ts-ignore 544 let startTimeAbsolute = (data.startNS || 0) + (window as unknown).recordStartNS; 545 this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 546 } 547 548 setPerfToolsData(data: PerfToolStruct): void { 549 this.setTableHeight('auto'); 550 //Perf Tools info 551 this.tabCurrentSelectionInit('Slice Details'); 552 let list: unknown[] = []; 553 list.push({ 554 name: 'Name', 555 value: data.name, 556 }); 557 list.push({ 558 name: 'StartTime(Relative)', 559 value: getTimeString(data.startNS || 0), 560 }); 561 list.push({ 562 name: 'StartTime(Absolute)', 563 // @ts-ignore 564 value: ((data.startNS || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 565 }); 566 list.push({ 567 name: 'Value', 568 value: Number(data.count), 569 }); 570 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 571 this.currentSelectionTbl!.dataSource = list; 572 } 573 574 setMemData(data: ProcessMemStruct): void { 575 this.setTableHeight('auto'); 576 //时钟信息 577 this.initCanvas(); 578 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 579 if (leftTitle) { 580 leftTitle.innerText = 'Counter Details'; 581 } 582 let list: object[] = []; 583 list.push({ 584 name: 'StartTime(Relative)', 585 value: getTimeString(data.startTime || 0), 586 }); 587 list.push({ 588 name: 'StartTime(Absolute)', 589 // @ts-ignore 590 value: ((data.startTime || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 591 }); 592 list.push({ name: 'Value', value: data.value }); 593 list.push({ name: 'Delta', value: data.delta }); 594 list.push({ 595 name: 'Duration', 596 value: getTimeString(data.duration || 0), 597 }); 598 this.currentSelectionTbl!.dataSource = list; 599 } 600 601 setIrqData(data: IrqStruct): void { 602 this.setTableHeight('550px'); 603 this.initCanvas(); 604 this.setTitleAndButtonStyle(); 605 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 606 if (leftTitle) { 607 leftTitle.innerText = 'Counter Details'; 608 } 609 let list: object[] = []; 610 list.push({ 611 name: 'StartTime(Relative)', 612 value: getTimeString(data.startNS || 0), 613 }); 614 list.push({ 615 name: 'StartTime(Absolute)', 616 // @ts-ignore 617 value: ((data.startNS || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 618 }); 619 list.push({ name: 'Name', value: data.name }); 620 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 621 queryBinderArgsByArgset(data.argSetId || 0).then((argsBinderRes) => { 622 if (argsBinderRes.length > 0) { 623 argsBinderRes.forEach((item) => { 624 list.push({ name: item.keyName, value: item.strValue }); 625 }); 626 } 627 this.currentSelectionTbl!.dataSource = list; 628 }); 629 } 630 631 async setThreadData( 632 data: ThreadStruct, 633 scrollCallback: ((d: unknown) => void) | undefined, 634 scrollWakeUp: (d: unknown) => void | undefined, 635 callback?: (data: Array<unknown>, str: string) => void 636 ): Promise<void> { 637 //线程信息 638 await this.setRealTime(); 639 this.setTableHeight('550px'); 640 this.initCanvas(); 641 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 642 this.setTitleAndButtonStyle(); 643 if (leftTitle) { 644 leftTitle.innerText = 'Thread State'; 645 } 646 let list: unknown[] = []; 647 let jankJumperList: Array<ThreadTreeNode> = []; 648 this.prepareThreadInfo(list, data); 649 let cpu = new CpuStruct(); 650 cpu.id = data.id; 651 cpu.startTime = data.startTime; 652 this.queryThreadDetails(data, list, jankJumperList, callback, scrollWakeUp, scrollCallback); 653 } 654 655 private sortByNearData(nearData: unknown[], data: ThreadStruct, list: unknown[]): unknown[] { 656 let preData: unknown = undefined; 657 let nextData: unknown = undefined; 658 659 nearData 660 // @ts-ignore 661 .sort((near1, near2) => near1.startTime - near2.startTime) 662 .forEach((near) => { 663 // @ts-ignore 664 if (near.itid === data.id) { 665 // @ts-ignore 666 if (near.startTime < data.startTime!) { 667 preData = near; 668 list.push({ 669 name: 'Previous State', 670 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 671 <div style="white-space:pre-wrap">${ 672 // @ts-ignore 673 Utils.getEndState(near.state) 674 }</div> 675 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="previous-state-click" name="select" color="#7fa1e7" size="20"></lit-icon> 676 </div>`, 677 }); 678 } else { 679 nextData = near; 680 list.push({ 681 name: 'Next State', 682 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 683 <div style="white-space:pre-wrap">${ 684 // @ts-ignore 685 Utils.getEndState(near.state) 686 }</div> 687 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="next-state-click" name="select" color="#7fa1e7" size="20"></lit-icon> 688 </div>`, 689 }); 690 } 691 } 692 }); 693 return [preData, nextData]; 694 } 695 696 private setWakeupData(fromBean: WakeupBean | undefined, wakeUps: WakeupBean[], list: unknown[]): void { 697 if (fromBean !== null && fromBean !== undefined && fromBean.pid !== 0 && fromBean.tid !== 0) { 698 list.push({ 699 name: 'wakeup from tid', 700 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 701 <div style="white-space:pre-wrap">${fromBean.tid}</div> 702 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="wakeup-from" 703 class="wakeup-click" name="select" color="#7fa1e7" size="20"></lit-icon> 704 </div>`, 705 }); 706 } 707 if (wakeUps !== null) { 708 wakeUps.map((e) => { 709 list.push({ 710 name: 'wakeup tid', 711 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 712 <div style="white-space:pre-wrap">${e.tid}</div> 713 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="wakeup-${e.tid}" 714 class="wakeup-click" name="select" color="#7fa1e7" size="20"></lit-icon> 715 </div>`, 716 }); 717 }); 718 } 719 } 720 721 private queryThreadDetails( 722 data: ThreadStruct, 723 list: unknown[], 724 jankJumperList: ThreadTreeNode[], 725 callback: ((data: Array<unknown>, str: string) => void) | undefined, 726 scrollWakeUp: (d: unknown) => void | undefined, 727 scrollCallback: ((d: unknown) => void) | undefined 728 ): void { 729 Promise.all([ 730 this.queryThreadWakeUpFromData(data.id!, data.startTime!, data.dur!), 731 this.queryThreadWakeUpData(data.id!, data.startTime!, data.dur!), 732 this.queryThreadStateDArgs(data.argSetID), 733 queryThreadNearData(data.id!, data.startTime!), 734 ]).then((result) => { 735 let fromBean = result[0] as WakeupBean; 736 let wakeUps = result[1] as WakeupBean[]; 737 let args = result[2]; 738 let [preData, nextData] = this.sortByNearData(result[3], data, list); 739 this.setWakeupData(fromBean, wakeUps, list); 740 if (args.length > 0) { 741 args.forEach((arg) => { 742 list.push({ name: arg.keyName, value: arg.strValue }); 743 }); 744 } 745 this.currentSelectionTbl!.dataSource = list; 746 // @ts-ignore 747 let startTimeAbsolute = (data.startTime || 0) + (window as unknown).recordStartNS; 748 this.addClickToTransfBtn(startTimeAbsolute, THREAD_TRANSF_BTN_ID, THREAD_STARTTIME_ABSALUTED_ID); 749 let timeLineNode = new ThreadTreeNode(data.tid!, data.pid!, data.startTime!); 750 jankJumperList.push(timeLineNode); 751 if (callback) { 752 callback(jankJumperList, this.wakeUp); 753 this.wakeUp = ''; 754 } 755 this.stateClickHandler(preData, nextData, data, scrollWakeUp, scrollCallback); 756 this.wakeupClickHandler(wakeUps, fromBean, scrollWakeUp); 757 }); 758 } 759 760 private wakeupClickHandler( 761 wakeUps: WakeupBean[], 762 fromBean: WakeupBean | undefined, 763 scrollWakeUp: (d: unknown) => void | undefined 764 ): void { 765 this.currentSelectionTbl?.shadowRoot?.querySelector('#wakeup-from')?.addEventListener('click', () => { 766 //点击跳转,唤醒和被唤醒的 线程 767 if (fromBean && scrollWakeUp) { 768 scrollWakeUp({ 769 processId: fromBean.pid, 770 tid: fromBean.tid, 771 startTime: fromBean.ts, 772 dur: fromBean.dur, 773 cpu: fromBean.cpu, 774 id: fromBean.itid, 775 state: 'Running', 776 argSetID: fromBean.argSetID, 777 }); 778 } 779 this.wakeUp = 'wakeup tid'; 780 }); 781 if (wakeUps) { 782 wakeUps.map((up) => { 783 this.currentSelectionTbl?.shadowRoot?.querySelector(`#wakeup-${up.tid}`)?.addEventListener('click', () => { 784 //点击跳转,唤醒和被唤醒的 线程 785 if (up && scrollWakeUp !== undefined) { 786 scrollWakeUp({ 787 processId: up.pid, 788 tid: up.tid, 789 startTime: up.ts, 790 dur: up.dur, 791 cpu: up.cpu, 792 id: up.itid, 793 state: up.state, 794 argSetID: up.argSetID, 795 }); 796 } 797 this.wakeUp = 'wakeup tid'; 798 }); 799 }); 800 } 801 } 802 803 private stateClickHandler( 804 preData: unknown, 805 nextData: unknown, 806 data: ThreadStruct, 807 scrollWakeUp: (d: unknown) => void | undefined, 808 scrollCallback: ((d: unknown) => void) | undefined 809 ): void { 810 this.currentSelectionTbl?.shadowRoot?.querySelector('#next-state-click')?.addEventListener('click', () => { 811 if (nextData && scrollWakeUp !== undefined) { 812 scrollWakeUp({ 813 // @ts-ignore 814 processId: nextData.pid, 815 // @ts-ignore 816 tid: nextData.tid, 817 // @ts-ignore 818 startTime: nextData.startTime, 819 // @ts-ignore 820 dur: nextData.dur, 821 // @ts-ignore 822 cpu: nextData.cpu, 823 // @ts-ignore 824 id: nextData.itid, 825 // @ts-ignore 826 state: nextData.state, 827 // @ts-ignore 828 argSetID: nextData.argSetID, 829 }); 830 } 831 }); 832 833 this.currentSelectionTbl?.shadowRoot?.querySelector('#previous-state-click')?.addEventListener('click', () => { 834 if (preData && scrollWakeUp !== undefined) { 835 scrollWakeUp({ 836 // @ts-ignore 837 processId: preData.pid, 838 // @ts-ignore 839 tid: preData.tid, 840 // @ts-ignore 841 startTime: preData.startTime, 842 // @ts-ignore 843 dur: preData.dur, 844 // @ts-ignore 845 cpu: preData.cpu, 846 // @ts-ignore 847 id: preData.itid, 848 // @ts-ignore 849 state: preData.state, 850 // @ts-ignore 851 argSetID: preData.argSetID, 852 }); 853 } 854 }); 855 856 this.currentSelectionTbl?.shadowRoot?.querySelector('#state-click')?.addEventListener('click', () => { 857 //线程点击 858 if (scrollCallback) { 859 scrollCallback(data); 860 } 861 }); 862 } 863 864 private async prepareThreadInfo(list: unknown[], data: ThreadStruct): Promise<void> { 865 list.push({ 866 name: 'StartTime(Relative)', 867 value: getTimeString(data.startTime || 0), 868 }); 869 this.createStartTimeNode(list, data.startTime || 0, THREAD_TRANSF_BTN_ID, THREAD_STARTTIME_ABSALUTED_ID); 870 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 871 let state; 872 if (data.state) { 873 state = Utils.getEndState(data.state); 874 } else if (data.state === '' || data.state === null) { 875 state = ''; 876 } else { 877 state = 'Unknown State'; 878 } 879 if ('Running' === state) { 880 state = state + ' on CPU ' + data.cpu; 881 list.push({ 882 name: 'State', 883 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 884 <div style="white-space:pre-wrap">${state}</div> 885 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="state-click" name="select" color="#7fa1e7" size="20"></lit-icon> 886 </div>`, 887 }); 888 } else { 889 list.push({ name: 'State', value: `${state}` }); 890 } 891 if (state.includes('Running')) { 892 let startTime: number = data.startTime || 0; 893 let endTime: number = (data.startTime || 0) + (data.dur || 0); 894 let freqList: Array<unknown> = []; 895 let str = ''; 896 freqList = await queryStateFreqList(startTime, endTime, data.cpu || 0); 897 freqList.forEach((it) => { 898 // @ts-ignore 899 if (it.startTime < startTime! && it.endTime > endTime!) { 900 // @ts-ignore 901 it.stateDur = data.dur; 902 // @ts-ignore 903 } else if (it.startTime < startTime! && startTime! < it.endTime && it.endTime < endTime!) { 904 // @ts-ignore 905 it.stateDur = it.endTime - startTime!; 906 // @ts-ignore 907 } else if (it.startTime > startTime! && startTime! < it.endTime && it.endTime < endTime!) { 908 // @ts-ignore 909 it.stateDur = it.dur; 910 // @ts-ignore 911 } else if (it.startTime > startTime! && endTime! > it.startTime && it.endTime > endTime!) { 912 // @ts-ignore 913 it.stateDur = endTime! - it.startTime; 914 } 915 // @ts-ignore 916 str += '[' + it.value + ': ' + (it.stateDur || 0) / 1000 + ']' + ','; 917 }); 918 list.push({ name: 'Freq [KHz,μs]', value: str.substring(0, str.length - 1) }); 919 } 920 let slice = Utils.SCHED_SLICE_MAP.get(`${data.id}-${data.startTime}`); 921 if (slice) { 922 list.push({ name: 'Prio', value: `${slice.priority}` }); 923 } 924 let processName = Utils.PROCESS_MAP.get(data.pid!); 925 if ( 926 processName === null || 927 processName === undefined || 928 processName === '' || 929 processName.toLowerCase() === 'null' 930 ) { 931 processName = Utils.THREAD_MAP.get(data.tid!) || 'null'; 932 } 933 list.push({ 934 name: 'Process', 935 value: this.transferString(processName ?? '') + ' [' + data.pid + '] ', 936 }); 937 } 938 939 setJankData( 940 data: JankStruct, 941 callback: ((data: Array<unknown>) => void) | undefined = undefined, 942 scrollCallback: ((d: unknown) => void) | undefined 943 ): void { 944 //线程信息 945 this.setTableHeight('750px'); 946 this.tabCurrentSelectionInit('Slice Details'); 947 let list: unknown[] = []; 948 this.setJankCommonMessage(list, data); 949 if (data.type === '0') { 950 this.handleTypeJank(data, list, scrollCallback, callback); 951 } else { 952 this.currentSelectionTbl!.dataSource = list; 953 } 954 } 955 956 private handleTypeJank( 957 data: JankStruct, 958 list: unknown[], 959 scrollCallback: ((d: unknown) => void) | undefined, 960 callback: ((data: Array<unknown>) => void) | undefined 961 ): void { 962 this.setJankType(data, list); 963 let jankJumperList: Array<JankTreeNode> = []; 964 if (data.frame_type === 'render_service') { 965 queryGpuDur(data.id!).then((it) => { 966 if (it.length > 0) { 967 //@ts-ignore 968 list.push({ name: 'Gpu Duration', value: getTimeString(it[0].gpu_dur) }); 969 } 970 }); 971 this.handleRenderServiceJank(data, list, jankJumperList, scrollCallback, callback); 972 } else if (data.frame_type === 'app') { 973 this.handleAppJank(list, data, jankJumperList, scrollCallback, callback); 974 } else if (data.frame_type === 'frameTime') { 975 this.handleFrameTimeJank(data, list, jankJumperList, scrollCallback, callback); 976 } 977 } 978 979 private handleFrameTimeJank( 980 data: JankStruct, 981 list: unknown[], 982 jankJumperList: JankTreeNode[], 983 scrollCallback: ((d: unknown) => void) | undefined, 984 callback: ((data: Array<unknown>) => void) | undefined 985 ): void { 986 queryGpuDur(data.id!).then((it) => { 987 if (it.length > 0) { 988 list.push({ 989 name: 'Gpu Duration', 990 //@ts-ignore 991 value: getTimeString(it[0].gpu_dur), 992 }); 993 } 994 this.addAppFrameDetails(data, list); 995 this.addRenderServiceFrameDetails(data, list); 996 this.addFollowingDetails(list, data); 997 let appNode = new JankTreeNode(data.name!, data.pid!, 'app'); 998 let rsNode = new JankTreeNode(data.rs_vsync!, data.rs_pid!, 'render_service'); 999 appNode.children.push(rsNode); 1000 jankJumperList.push(appNode); 1001 this.currentSelectionTbl!.dataSource = list; 1002 this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList); 1003 }); 1004 } 1005 1006 private addRenderServiceFrameDetails(data: JankStruct, list: unknown[]): void { 1007 if (data.rs_name) { 1008 list.push({ 1009 name: 'RenderService Frame', 1010 value: '', 1011 }); 1012 list.push({ 1013 name: 'Process', 1014 value: 'render_service ' + data.rs_pid, 1015 }); 1016 list.push({ 1017 name: 'StartTime(Relative)', 1018 value: getTimeString(data.rs_ts || 0), 1019 }); 1020 list.push({ 1021 name: 'StartTime(Absolute)', 1022 // @ts-ignore 1023 value: ((data.rs_ts || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1024 }); 1025 list.push({ 1026 name: 'end time', 1027 value: getTimeString(data.rs_ts! + data.rs_dur! || 0), 1028 }); 1029 } 1030 } 1031 1032 private addFollowingDetails(list: unknown[], data: JankStruct): void { 1033 list.push({ 1034 name: 'Following', 1035 value: '', 1036 }); 1037 list.push({ 1038 name: 'Slice', 1039 value: 1040 data.cmdline + 1041 ' [' + 1042 data.name + 1043 ']' + 1044 `<lit-icon class="jank_cla" style="display: inline-flex;cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="${data.type}-${data.pid}" slice_name="${data.name}" pid="${data.pid}" name="select" color="#7fa1e7" size="20"></lit-icon>`, 1045 }); 1046 } 1047 1048 private addAppFrameDetails(data: JankStruct, list: unknown[]): void { 1049 if (data.name) { 1050 list.push({ 1051 name: 'App Frame', 1052 value: '', 1053 }); 1054 list.push({ 1055 name: 'Process', 1056 value: data.cmdline + ' ' + data.pid, 1057 }); 1058 list.push({ 1059 name: 'StartTime(Relative)', 1060 value: getTimeString(data.ts || 0), 1061 }); 1062 list.push({ 1063 name: 'StartTime(Absolute)', 1064 // @ts-ignore 1065 value: ((data.ts || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1066 }); 1067 list.push({ 1068 name: 'end time', 1069 value: getTimeString(data!.ts! + data.dur! || 0), 1070 }); 1071 } 1072 } 1073 1074 private handleAppJank( 1075 list: unknown[], 1076 data: JankStruct, 1077 jankJumperList: JankTreeNode[], 1078 scrollCallback: ((d: unknown) => void) | undefined, 1079 callback: ((data: Array<unknown>) => void) | undefined 1080 ): void { 1081 list.push({ 1082 name: 'FrameTimeLine flows', 1083 value: '', 1084 }); 1085 list.push({ 1086 name: 'Slice', 1087 value: 1088 data.cmdline + 1089 ' [' + 1090 data.name + 1091 ']' + 1092 `<lit-icon class="jank_cla" style="display: inline-flex;cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="actual frameTime" slice_name="${data.name}" pid="${data.pid}" name="select" color="#7fa1e7" size="20"></lit-icon>`, 1093 }); 1094 let timeLineNode = new JankTreeNode(data.name!, data.pid!, 'frameTime'); 1095 jankJumperList.push(timeLineNode); 1096 if (data.dst_slice) { 1097 queryPrecedingData(data.dst_slice).then((it) => { 1098 if (it.length > 0) { 1099 list.push({ 1100 name: 'Preceding flows', 1101 value: '', 1102 }); 1103 it.forEach((a: unknown) => { 1104 // @ts-ignore 1105 let rsNode = new JankTreeNode(a.name, a.pid, 'render_service'); 1106 jankJumperList.push(rsNode); 1107 list.push({ 1108 name: 'Slice', 1109 value: 1110 // @ts-ignore 1111 a.cmdline + 1112 ' [' + 1113 // @ts-ignore 1114 a.name + 1115 ']' + 1116 // @ts-ignore 1117 `<lit-icon class="jank_cla" style="display: inline-flex;cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="${a.type}-${a.pid}" slice_name="${a.name}" pid="${a.pid}" name="select" color="#7fa1e7" size="20"></lit-icon>`, 1118 }); 1119 }); 1120 this.currentSelectionTbl!.dataSource = list; 1121 this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList); 1122 } 1123 }); 1124 } else { 1125 this.currentSelectionTbl!.dataSource = list; 1126 this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList); 1127 } 1128 } 1129 1130 private handleRenderServiceJank( 1131 data: JankStruct, 1132 list: unknown[], 1133 jankJumperList: JankTreeNode[], 1134 scrollCallback: ((d: unknown) => void) | undefined, 1135 callback: ((data: Array<unknown>) => void) | undefined 1136 ): void { 1137 if (data.src_slice) { 1138 queryFlowsData(data.src_slice!.split(',')).then((it) => { 1139 if (it.length > 0) { 1140 list.push({ 1141 name: 'FrameTimeLine flows', 1142 value: '', 1143 }); 1144 it.forEach((a: unknown) => { 1145 // @ts-ignore 1146 let appNode = new JankTreeNode(a.name, a.pid, 'app'); 1147 // @ts-ignore 1148 appNode.children.push(new JankTreeNode(a.name, a.pid, 'frameTime')); 1149 jankJumperList.push(appNode); 1150 list.push({ 1151 name: 'Slice', 1152 value: 1153 // @ts-ignore 1154 a.cmdline + 1155 ' [' + 1156 // @ts-ignore 1157 a.name + 1158 ']' + 1159 // @ts-ignore 1160 `<lit-icon class="jank_cla" style="display: inline-flex;cursor: pointer;transform: scaleX(-1);margin-left: 5px" id="actual frameTime" slice_name="${a.name}" pid="${a.pid}" name="select" color="#7fa1e7" size="20"></lit-icon>`, 1161 }); 1162 }); 1163 list.push({ 1164 name: 'Following flows', 1165 value: '', 1166 }); 1167 it.forEach((a: unknown) => { 1168 list.push({ 1169 name: 'Slice', 1170 value: 1171 // @ts-ignore 1172 a.cmdline + 1173 ' [' + 1174 // @ts-ignore 1175 a.name + 1176 ']' + 1177 // @ts-ignore 1178 `<lit-icon class="jank_cla" style="display: inline-flex;cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="${a.type}-${a.pid}" slice_name="${a.name}" pid="${a.pid}" name="select" color="#7fa1e7" size="20"></lit-icon>`, 1179 }); 1180 }); 1181 this.currentSelectionTbl!.dataSource = list; 1182 this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList); 1183 } 1184 }); 1185 } else { 1186 this.currentSelectionTbl!.dataSource = list; 1187 } 1188 } 1189 1190 setAllStartupData(data: AllAppStartupStruct, scrollCallback: Function): void { 1191 this.setTableHeight('550px'); 1192 this.initCanvas(); 1193 let allStartUpLeftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 1194 let allStartUpmiddleTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightText'); 1195 let allStartUpRightButton: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightButton'); 1196 if (allStartUpmiddleTitle) { 1197 allStartUpmiddleTitle.style.visibility = 'hidden'; 1198 } 1199 if (allStartUpRightButton) { 1200 allStartUpRightButton.style.visibility = 'hidden'; 1201 } 1202 if (allStartUpLeftTitle) { 1203 allStartUpLeftTitle.innerText = 'Details'; 1204 } 1205 let list: unknown[] = []; 1206 list.push({ name: 'Name', value: data.stepName! }); 1207 list.push({ 1208 name: 'StartTime(Relative)', 1209 value: getTimeString(data.startTs || 0), 1210 }); 1211 list.push({ 1212 name: 'StartTime(Absolute)', 1213 // @ts-ignore 1214 value: ((data.startTs || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1215 }); 1216 list.push({ 1217 name: 'EndTime(Relative)', 1218 value: getTimeString((data.startTs || 0) + (data.dur || 0)), 1219 }); 1220 list.push({ 1221 name: 'EndTime(Abslute)', 1222 // @ts-ignore 1223 value: ((data.startTs || 0) + (data.dur || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1224 }); 1225 list.push({ 1226 name: 'Dur', 1227 value: getTimeString(data.dur || 0), 1228 }); 1229 this.currentSelectionTbl!.dataSource = list; 1230 } 1231 1232 setStartupData(data: AppStartupStruct, scrollCallback: Function, rowData: unknown): void { 1233 this.setTableHeight('550px'); 1234 this.initCanvas(); 1235 this.setStartUpStyle(); 1236 let list: unknown[] = []; 1237 list.push({ name: 'Name', value: AppStartupStruct.getStartupName(data.startName) }); 1238 list.push({ 1239 name: 'StartTime(Relative)', 1240 value: ` 1241 <div style="display: flex;white-space: nowrap;align-items: center"> 1242<div style="white-space:pre-wrap">${getTimeString(data.startTs || 0)}</div> 1243<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="start-jump" name="select" color="#7fa1e7" size="20"></lit-icon> 1244</div>`, 1245 }); 1246 list.push({ 1247 name: 'StartTime(Absolute)', 1248 // @ts-ignore 1249 value: ((data.startTs || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1250 }); 1251 if (data.dur && data.dur > 0) { 1252 list.push({ 1253 name: 'EndTime(Relative)', 1254 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1255<div style="white-space:pre-wrap">${getTimeString((data.startTs || 0) + (data.dur || 0))}</div> 1256<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="end-jump" name="select" color="#7fa1e7" size="20"></lit-icon> 1257</div>`, 1258 }); 1259 list.push({ 1260 name: 'EndTime(Absolute)', 1261 // @ts-ignore 1262 value: ((data.startTs || 0) + (data.dur || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1263 }); 1264 } else { 1265 list.push({ 1266 name: 'EndTime(Relative)', 1267 value: 'Unknown Time', 1268 }); 1269 list.push({ 1270 name: 'EndTime(Absolute)', 1271 value: 'Unknown Time', 1272 }); 1273 } 1274 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 1275 // @ts-ignore 1276 let sortedArray = rowData.slice().sort(function (a: { startTs: number }, b: { startTs: number }) { 1277 return a.startTs - b.startTs; 1278 }); 1279 sortedArray.forEach((item: unknown, index: number) => { 1280 // @ts-ignore 1281 if (item.startName === data.startName) { 1282 list.push({ 1283 name: 'StartSlice', 1284 value: 1285 index === 0 1286 ? 'NULL' 1287 : `${AppStartupStruct.getStartupName(sortedArray[index - 1].startName)} ${getTimeString( 1288 sortedArray[index - 1].startTs + sortedArray[index - 1].dur 1289 )}`, 1290 }); 1291 list.push({ 1292 name: 'EndSlice', 1293 value: 1294 index === sortedArray.length - 1 1295 ? 'NULL' 1296 : `${AppStartupStruct.getStartupName(sortedArray[index + 1].startName)} ${getTimeString( 1297 sortedArray[index + 1].startTs 1298 )}`, 1299 }); 1300 } 1301 }); 1302 this.currentSelectionTbl!.dataSource = list; 1303 this.attachScrollHandlers(data, scrollCallback); 1304 } 1305 1306 private setStartUpStyle(): void { 1307 let rightButton: HTMLElement | null | undefined = this?.shadowRoot 1308 ?.querySelector('#rightButton') 1309 ?.shadowRoot?.querySelector('#custom-button'); 1310 let startUpRightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle'); 1311 if (startUpRightTitle) { 1312 startUpRightTitle.style.visibility = 'hidden'; 1313 rightButton!.style.visibility = 'hidden'; 1314 } 1315 let startUpLeftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 1316 if (startUpLeftTitle) { 1317 startUpLeftTitle.innerText = 'Details'; 1318 } 1319 } 1320 1321 private attachScrollHandlers(data: AppStartupStruct, scrollCallback: Function): void { 1322 let startIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#start-jump'); 1323 let endIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#end-jump'); 1324 let scrollClick = (type: number): void => { 1325 // @ts-ignore 1326 let recordNs: number = (window as unknown).recordStartNS; 1327 let useEnd = type === 1 && data.startName! < 6; 1328 queryThreadByItid( 1329 useEnd ? data.endItid! : data.itid!, 1330 useEnd ? recordNs + data.startTs! + data.dur! : recordNs + data.startTs! 1331 ).then((result) => { 1332 if (result.length > 0) { 1333 //@ts-ignore 1334 let pt: { 1335 pid: number; 1336 tid: number; 1337 dur: number; 1338 name: string; 1339 depth: number; 1340 } = result[0]; 1341 scrollCallback({ 1342 pid: pt.pid, 1343 tid: pt.tid, 1344 type: 'func', 1345 dur: pt.dur, 1346 depth: pt.depth, 1347 funName: pt.name, 1348 startTs: useEnd ? (data.startTs || 0) + (data.dur || 0) : data.startTs, 1349 keepOpen: true, 1350 }); 1351 } 1352 }); 1353 }; 1354 if (startIcon) { 1355 startIcon.addEventListener('click', () => scrollClick(0)); 1356 } 1357 if (endIcon) { 1358 endIcon.addEventListener('click', () => scrollClick(1)); 1359 } 1360 } 1361 1362 setStaticInitData(data: SoStruct, scrollCallback: Function): void { 1363 this.setTableHeight('550px'); 1364 this.initCanvas(); 1365 this.setStaticInitStyle(); 1366 let list: unknown[] = []; 1367 list.push({ name: 'Name', value: data.soName }); 1368 list.push({ 1369 name: 'StartTime(Relative)', 1370 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1371<div style="white-space:pre-wrap">${getTimeString(data.startTs || 0)}</div> 1372<lit-icon id="start-jump" style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" name="select" color="#7fa1e7" size="20"></lit-icon> 1373</div>`, 1374 }); 1375 list.push({ 1376 name: 'StartTime(Absolute)', 1377 // @ts-ignore 1378 value: ((data.startTs || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1379 }); 1380 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 1381 this.currentSelectionTbl!.dataSource = list; 1382 this.startIconClickEvent(data, scrollCallback); 1383 } 1384 1385 private setStaticInitStyle(): void { 1386 let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle'); 1387 let rightButton: HTMLElement | null | undefined = this?.shadowRoot 1388 ?.querySelector('#rightButton') 1389 ?.shadowRoot?.querySelector('#custom-button'); 1390 if (rightTitle) { 1391 rightTitle.style.visibility = 'hidden'; 1392 rightButton!.style.visibility = 'hidden'; 1393 } 1394 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 1395 if (leftTitle) { 1396 leftTitle.innerText = 'Details'; 1397 } 1398 } 1399 1400 private startIconClickEvent(data: SoStruct, scrollCallback: Function): void { 1401 let startIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#start-jump'); 1402 if (startIcon) { 1403 startIcon.addEventListener('click', () => { 1404 // @ts-ignore 1405 let recordNs: number = (window as unknown).recordStartNS; 1406 queryThreadByItid(data.itid!, recordNs + data.startTs!).then((result) => { 1407 if (result.length > 0) { 1408 //@ts-ignore 1409 let pt: { 1410 pid: number; 1411 tid: number; 1412 dur: number; 1413 name: string; 1414 depth: number; 1415 } = result[0]; 1416 scrollCallback({ 1417 pid: pt.pid, 1418 tid: pt.tid, 1419 type: 'func', 1420 dur: pt.dur, 1421 depth: pt.depth, 1422 funName: pt.name, 1423 startTs: data.startTs, 1424 keepOpen: true, 1425 }); 1426 } 1427 }); 1428 }); 1429 } 1430 } 1431 1432 async setFrameAnimationData(data: FrameAnimationStruct, scrollCallback: Function): Promise<void> { 1433 this.setTableHeight('550px'); 1434 this.tabCurrentSelectionInit('Animation Details'); 1435 let list = []; 1436 let dataTs: number = data.startTs < 0 ? 0 : data.startTs; 1437 list.push({ name: 'Name', value: data.name }); 1438 list.push({ name: 'Start time(Relative)', value: `${Utils.getTimeString(dataTs)}` }); 1439 list.push({ 1440 name: 'Start time(Absolute)', 1441 // @ts-ignore 1442 value: ((dataTs || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1443 }); 1444 list.push({ 1445 name: 'End time(Relative)', 1446 value: `${Utils.getTimeString(dataTs + (data.dur || 0))}`, 1447 }); 1448 list.push({ 1449 name: 'End time(Absolute)', 1450 // @ts-ignore 1451 value: (dataTs + (data.dur || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1452 }); 1453 list.push({ name: 'Duration', value: `${Utils.getTimeString(data.dur || 0)}` }); 1454 if (data.status === 'Completion delay') { 1455 let frameFpsMessage = data.frameInfo?.split(':'); 1456 if (frameFpsMessage) { 1457 if (frameFpsMessage[1] !== '0') { 1458 if (this.isFpsAvailable) { 1459 list.push({ 1460 name: 'FPS', 1461 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1462 <div style="white-space:pre-wrap">${frameFpsMessage[1]}</div> 1463 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="fps-jump" name="select" color="#7fa1e7" size="20"></lit-icon> 1464 </div>`, 1465 }); 1466 } else { 1467 list.push({ name: 'FPS', value: `${frameFpsMessage[1]}` }); 1468 } 1469 } else { 1470 let fixedNumber: number = 2; 1471 let fpsValue: number = Number(frameFpsMessage[0]) / (data.dur / 1000_000_000); 1472 list.push({ name: 'FPS', value: `${fpsValue.toFixed(fixedNumber) || 0}` }); 1473 } 1474 } 1475 } 1476 this.currentSelectionTbl!.dataSource = list; 1477 this.fpsClickEvent(data, scrollCallback); 1478 } 1479 1480 private fpsClickEvent(data: FrameAnimationStruct, scrollCallback: Function): void { 1481 let queryJoinName = `${data.frameInfo?.split(':')[1]}: ${data.name?.split(':')![1]}`; 1482 // @ts-ignore 1483 let recordNs: number = (window as unknown).recordStartNS; 1484 this.currentSelectionTbl?.shadowRoot?.querySelector('#fps-jump')?.addEventListener('click', () => { 1485 queryFpsSourceList(data.inputTime, data.endTime, queryJoinName).then((result) => { 1486 if (result.length > 0) { 1487 this.isFpsAvailable = true; 1488 let pt: { 1489 pid: number; 1490 tid: number; 1491 name: string; 1492 ts: number; 1493 dur: number; 1494 depth: number; 1495 } = result[0]; 1496 scrollCallback({ 1497 pid: pt.tid, 1498 tid: pt.tid, 1499 dur: pt.dur, 1500 type: 'func', 1501 depth: pt.depth, 1502 funName: pt.name, 1503 startTs: pt.ts - recordNs, 1504 keepOpen: true, 1505 }); 1506 } else { 1507 this.isFpsAvailable = false; 1508 } 1509 }); 1510 }); 1511 } 1512 1513 private setJankType(data: JankStruct, list: unknown[]): void { 1514 if (data.jank_tag === 1) { 1515 if (data.frame_type === 'render_service') { 1516 list.push({ name: 'Jank Type', value: 'RenderService Deadline Missed' }); 1517 } else if (data.frame_type === 'app') { 1518 list.push({ name: 'Jank Type', value: 'APP Deadline Missed' }); 1519 } else if (data.frame_type === 'frameTime') { 1520 list.push({ name: 'Jank Type', value: 'Deadline Missed' }); 1521 } 1522 } else if (data.jank_tag === 3) { 1523 list.push({ name: 'Jank Type', value: 'Deadline Missed' }); 1524 } else { 1525 list.push({ name: 'Jank Type', value: 'NONE' }); 1526 } 1527 } 1528 1529 private setJankCommonMessage(list: unknown[], data: JankStruct): void { 1530 list.push({ name: 'Name', value: data.name }); 1531 list.push({ name: 'StartTime(Relative)', value: getTimeString(data.ts || 0) }); 1532 list.push({ 1533 name: 'StartTime(Absolute)', 1534 // @ts-ignore 1535 value: ((data.ts || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1536 }); 1537 list.push({ name: 'Duration', value: data.dur ? getTimeString(data.dur) : ' ' }); 1538 if (data.frame_type !== 'frameTime') { 1539 list.push({ name: 'Process', value: data.cmdline + ' ' + data.pid }); 1540 } 1541 } 1542 1543 private setTableHeight(height: string): void { 1544 this.scrollView!.scrollTop = 0; 1545 this.currentSelectionTbl!.style.height = height; 1546 this.wakeupListTbl!.style.display = 'none'; 1547 } 1548 1549 private addJankScrollCallBackEvent( 1550 scrollCallback: ((d: unknown) => void) | undefined, 1551 callback: ((data: Array<unknown>) => void) | undefined, 1552 jankJumperList: JankTreeNode[] 1553 ): void { 1554 let all = this.currentSelectionTbl?.shadowRoot?.querySelectorAll('.jank_cla'); 1555 all!.forEach((a) => { 1556 a.addEventListener('click', () => { 1557 if (scrollCallback) { 1558 scrollCallback({ 1559 rowId: a.id, 1560 name: a.getAttribute('slice_name'), 1561 pid: a.getAttribute('pid'), 1562 }); 1563 } 1564 }); 1565 }); 1566 if (callback) { 1567 callback(jankJumperList); 1568 } 1569 } 1570 1571 async queryThreadStateDArgs(argSetID: number | undefined): Promise<BinderArgBean[]> { 1572 let list: Array<BinderArgBean> = []; 1573 if (argSetID !== undefined && argSetID > 0) { 1574 list = await queryThreadStateArgs(argSetID); 1575 } 1576 return list; 1577 } 1578 1579 /** 1580 * 查询出 线程被唤醒的 线程信息 1581 * @param data 1582 */ 1583 async queryCPUWakeUpFromData(data: CpuStruct): Promise<WakeupBean | null> { 1584 let wb: WakeupBean | null = null; 1585 if (data.id === undefined || data.startTime === undefined) { 1586 return null; 1587 } 1588 let wakeup = await queryRunnableTimeByRunning(data.tid!, data.startTime); 1589 if (wakeup && wakeup[0]) { 1590 let wakeupTs = wakeup[0].ts as number; 1591 let recordStartTs = window.recordStartNS; 1592 let wf = await queryThreadWakeUpFrom(data.id, wakeupTs); 1593 if (wf && wf[0]) { 1594 wb = wf[0]; 1595 if (wb !== null) { 1596 wb.wakeupTime = wakeupTs - recordStartTs; 1597 wb.process = Utils.PROCESS_MAP.get(wb.pid!) || 'Process'; 1598 wb.thread = Utils.THREAD_MAP.get(wb.tid!) || 'Thread'; 1599 wb.schedulingLatency = (data.startTime || 0) - (wb.wakeupTime || 0); 1600 wb.schedulingDesc = INPUT_WORD; 1601 } 1602 } 1603 } 1604 return wb; 1605 } 1606 1607 /** 1608 * 查询出 线程被唤醒的 线程链信息 1609 * @param data 1610 */ 1611 static async queryCPUWakeUpListFromBean(data: WakeupBean): Promise<WakeupBean | null> { 1612 let wb: WakeupBean | null = null; 1613 let wakeup = await queryRunnableTimeByRunning(data.tid!, data.ts!); 1614 if (wakeup && wakeup[0]) { 1615 let wakeupTs = wakeup[0].ts as number; 1616 // @ts-ignore 1617 let recordStartTs = (window as unknown).recordStartNS; 1618 let wf = await queryThreadWakeUpFrom(data.itid!, wakeupTs); 1619 if (wf && wf[0]) { 1620 wb = wf[0]; 1621 if (wb !== null) { 1622 wb.wakeupTime = wakeupTs - recordStartTs; 1623 wb.process = Utils.PROCESS_MAP.get(wb.pid!) || 'Process'; 1624 wb.thread = Utils.THREAD_MAP.get(wb.tid!) || 'Thread'; 1625 wb.schedulingLatency = (data.ts || 0) - (wb.wakeupTime || 0); 1626 wb.schedulingDesc = INPUT_WORD; 1627 } 1628 } 1629 } 1630 return wb; 1631 } 1632 1633 /** 1634 * 查询出 线程唤醒了哪些线程信息 1635 */ 1636 async queryThreadWakeUpFromData(itid: number, startTime: number, dur: number): Promise<unknown> { 1637 // @ts-ignore 1638 let wakeUps = await queryThreadWakeUpFrom(itid, startTime + (window as unknown).recordStartNS); 1639 if (wakeUps !== undefined && wakeUps.length > 0) { 1640 return wakeUps[0]; 1641 } else { 1642 return; 1643 } 1644 } 1645 1646 /** 1647 * 查询出 线程唤醒了哪些线程信息 1648 */ 1649 async queryThreadWakeUpData(itid: number, startTime: number, dur: number): Promise<Array<WakeupBean>> { 1650 let list: Array<WakeupBean> = []; 1651 if (itid === undefined || startTime === undefined) { 1652 return list; 1653 } 1654 let wakeUps = await queryThreadWakeUp(itid, startTime, dur); // 3,4835380000 1655 if (wakeUps !== undefined && wakeUps.length > 0) { 1656 list.push(...wakeUps); 1657 } 1658 return list; 1659 } 1660 1661 initCanvas(): HTMLCanvasElement | null { 1662 let canvas = this.shadowRoot!.querySelector<HTMLCanvasElement>('#rightDraw'); 1663 let width = getComputedStyle(this.currentSelectionTbl!).getPropertyValue('width'); 1664 if (canvas !== null) { 1665 canvas.width = Math.round(Number(width.replace('px', '')) * this.dpr); 1666 canvas.height = Math.round(Number(200 * this.dpr)); 1667 canvas.style.width = width; 1668 canvas.style.height = '200px'; 1669 canvas.getContext('2d')!.scale(this.dpr, this.dpr); 1670 } 1671 SpApplication.skinChange = (val: boolean): void => { 1672 this.drawRight(canvas, this.weakUpBean!); 1673 }; 1674 return canvas; 1675 } 1676 1677 drawRight(cavs: HTMLCanvasElement | null, wakeupBean: WakeupBean | null): void { 1678 if (cavs === null) { 1679 return; 1680 } 1681 let context = cavs.getContext('2d'); 1682 if (context !== null) { 1683 //绘制竖线 1684 this.drawVerticalLine(context); 1685 //绘制菱形 1686 context.lineWidth = 1; 1687 context.beginPath(); 1688 context.moveTo(10, 30); 1689 context.lineTo(4, 40); 1690 context.lineTo(10, 50); 1691 context.lineTo(16, 40); 1692 context.lineTo(10, 30); 1693 context.closePath(); 1694 context.fill(); 1695 context.font = 12 + 'px sans-serif'; 1696 //绘制wake up 文字 1697 let strList = []; 1698 strList.push('wakeup @ ' + getTimeString(wakeupBean?.wakeupTime || 0) + ' on CPU ' + wakeupBean?.cpu + ' by'); 1699 strList.push('P:' + wakeupBean?.process + ' [ ' + wakeupBean?.pid + ' ]'); 1700 strList.push('T:' + wakeupBean?.thread + ' [ ' + wakeupBean?.tid + ' ]'); 1701 strList.forEach((str, index) => { 1702 if (context !== null) { 1703 context.fillText(str, 40, 40 + 16 * index); 1704 } 1705 }); 1706 context.lineWidth = 2; 1707 context.lineJoin = 'bevel'; 1708 context.moveTo(10, 95); 1709 context.lineTo(20, 90); 1710 context.moveTo(10, 95); 1711 context.lineTo(20, 100); 1712 context.moveTo(10, 95); 1713 context.lineTo(80, 95); 1714 context.lineTo(70, 90); 1715 context.moveTo(80, 95); 1716 context.lineTo(70, 100); 1717 context.stroke(); 1718 //绘制latency 1719 context.font = 12 + 'px sans-serif'; 1720 context.fillText('Scheduling latency:' + getTimeString(wakeupBean?.schedulingLatency || 0), 90, 100); 1721 //绘制最下方提示语句 1722 context.font = 10 + 'px sans-serif'; 1723 INPUT_WORD.split('\n').forEach((str, index) => { 1724 context?.fillText(str, 90, 120 + 12 * index); 1725 }); 1726 } 1727 } 1728 1729 private drawVerticalLine(context: CanvasRenderingContext2D): void { 1730 if (document.querySelector<SpApplication>('sp-application')!.dark) { 1731 context.strokeStyle = '#ffffff'; 1732 context.fillStyle = '#ffffff'; 1733 } else { 1734 context.strokeStyle = '#000000'; 1735 context.fillStyle = '#000000'; 1736 } 1737 context.lineWidth = 2; 1738 context.moveTo(10, 15); 1739 context.lineTo(10, 125); 1740 context.stroke(); 1741 } 1742 1743 transferString(str: string): string { 1744 let s = ''; 1745 if (str.length === 0) { 1746 return ''; 1747 } 1748 s = str.replace(/&/g, '&'); 1749 s = s.replace(/</g, '<'); 1750 s = s.replace(/>/g, '>'); 1751 s = s.replace(/\'/g, '''); 1752 s = s.replace(/\"/g, '&#quat;'); 1753 return s; 1754 } 1755 1756 initElements(): void { 1757 this.currentSelectionTbl = this.shadowRoot?.querySelector<LitTable>('#selectionTbl'); 1758 this.wakeupListTbl = this.shadowRoot?.querySelector<LitTable>('#wakeupListTbl'); 1759 this.scrollView = this.shadowRoot?.querySelector<HTMLDivElement>('#scroll_view'); 1760 this.currentSelectionTbl?.addEventListener('column-click', (ev: unknown): void => {}); //@ts-ignore 1761 window.subscribe(window.SmartEvent.UI.WakeupList, (data: Array<WakeupBean>) => this.showWakeupListTableData(data)); 1762 } 1763 1764 showWakeupListTableData(data: Array<WakeupBean>): void { 1765 this.wakeupListTbl!.style.display = 'flex'; 1766 let cpus: number[] = []; 1767 let itids: number[] = []; 1768 let ts: number[] = []; 1769 let maxPriority = 0; 1770 let maxPriorityDuration = 0; 1771 let maxDuration = 0; 1772 data.forEach((it) => { 1773 cpus.push(it.cpu!); 1774 itids.push(it.itid!); 1775 ts.push(it.ts!); 1776 }); 1777 queryWakeupListPriority(itids, ts, cpus).then((res) => { 1778 let resource = data.map((it) => { 1779 let wake = { 1780 process: `${it.process}(${it.pid})`, 1781 thread: `${it.thread}(${it.tid})`, 1782 cpu: it.cpu, 1783 dur: it.dur, 1784 priority: 0, 1785 isSelected: false, 1786 }; 1787 //@ts-ignore 1788 let find = res.find((re) => re.cpu === it.cpu && re.itid === it.itid && re.ts === it.ts); 1789 if (find) { 1790 //@ts-ignore 1791 wake.priority = find.priority; 1792 } 1793 maxDuration = Math.max(maxDuration, it.dur!); 1794 maxPriority = Math.max(maxPriority, wake.priority); 1795 return wake; 1796 }); 1797 if (this.selectWakeupBean) { 1798 // 点击第一层唤醒树时向数组头部添加当前点击信息 1799 if (data[0].schedulingLatency) { 1800 // @ts-ignore 1801 resource.unshift(this.selectWakeupBean); 1802 } 1803 // @ts-ignore 1804 maxDuration = Math.max(maxDuration, this.selectWakeupBean.dur); 1805 // @ts-ignore 1806 maxPriority = Math.max(maxPriority, this.selectWakeupBean.priority); 1807 } 1808 resource.forEach((it) => { 1809 if (it.priority === maxPriority) { 1810 maxPriorityDuration = Math.max(it.dur || 0, maxPriorityDuration); 1811 } 1812 }); 1813 this.updateTableSettings(maxPriority, maxPriorityDuration, maxDuration); 1814 this.wakeupListTbl!.recycleDataSource = resource; 1815 }); 1816 } 1817 1818 private updateTableSettings(maxPriority: number, maxPriorityDuration: number, maxDuration: number): void { 1819 this.wakeupListTbl!.getItemTextColor = (data: unknown): string => { 1820 // @ts-ignore 1821 if ((data.priority === maxPriority && data.dur === maxPriorityDuration) || data.dur === maxDuration) { 1822 return '#f44336'; 1823 } else { 1824 return '#262626'; 1825 } 1826 }; 1827 } 1828 1829 addTableObserver(): void { 1830 let leftTable = this.shadowRoot?.querySelector('.table-left'); 1831 this.tableObserver?.observe(leftTable!, { 1832 attributes: true, 1833 attributeFilter: ['style'], 1834 attributeOldValue: true, 1835 }); 1836 } 1837 1838 initHtml(): string { 1839 return TabPaneCurrentSelectionHtml; 1840 } 1841} 1842 1843export class JankTreeNode { 1844 name: string = ''; 1845 pid: number = -1; 1846 frameType: string = ''; 1847 type: number = 0; 1848 1849 constructor(name: string, pid: number, frameType: string) { 1850 this.name = name; 1851 this.pid = pid; 1852 this.frameType = frameType; 1853 } 1854 1855 children: Array<JankTreeNode> = []; 1856} 1857 1858export class ThreadTreeNode { 1859 tid: number = 0; 1860 pid: number = -1; 1861 startTime: number = 1; 1862 1863 constructor(tid: number, pid: number, startTime: number) { 1864 this.tid = tid; 1865 this.pid = pid; 1866 this.startTime = startTime; 1867 } 1868} 1869