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 { DmaFenceStruct } from '../../../database/ui-worker/ProcedureWorkerDmaFence'; 30import { ColorUtils } from '../base/ColorUtils'; 31import { IrqStruct } from '../../../database/ui-worker/ProcedureWorkerIrq'; 32import { BinderArgBean } from '../../../bean/BinderArgBean'; 33import { JankStruct } from '../../../database/ui-worker/ProcedureWorkerJank'; 34import { Utils } from '../base/Utils'; 35import { SpSystemTrace } from '../../SpSystemTrace'; 36import { AppStartupStruct } from '../../../database/ui-worker/ProcedureWorkerAppStartup'; 37import { SoStruct } from '../../../database/ui-worker/ProcedureWorkerSoInit'; 38import { type SelectionParam } from '../../../bean/BoxSelection'; 39import { type FrameAnimationStruct } from '../../../database/ui-worker/ProcedureWorkerFrameAnimation'; 40import { 41 queryBinderByArgsId, 42 queryBinderBySliceId, 43 queryFlowsData, 44 queryPrecedingData, 45 queryThreadByItid, 46 queryFpsSourceList, 47 queryStateFreqList, 48} from '../../../database/sql/SqlLite.sql'; 49import { 50 queryBinderArgsByArgset, 51 queryDistributedRelationAllData, 52 queryRWakeUpFrom, 53 queryRunnableTimeByRunning, 54 queryThreadStateArgs, 55 queryThreadWakeUp, 56 queryThreadWakeUpFrom, 57 sqlPrioCount, 58} from '../../../database/sql/ProcessThread.sql'; 59import { queryGpuDur } from '../../../database/sql/Gpu.sql'; 60import { queryWakeupListPriority } from '../../../database/sql/Cpu.sql'; 61import { TabPaneCurrentSelectionHtml } from './TabPaneCurrentSelection.html'; 62import { queryRealTime } from '../../../database/sql/Clock.sql'; 63import { PerfToolStruct } from '../../../database/ui-worker/ProcedureWorkerPerfTool'; 64import { TraceMode } from '../../../SpApplicationPublicFunc'; 65import { threadPool, threadPool2 } from '../../../database/SqlLite'; 66import { threadNearData } from '../../../database/data-trafic/SliceSender'; 67import { HangStruct } from '../../../database/ui-worker/ProcedureWorkerHang'; 68import { XpowerStruct } from '../../../database/ui-worker/ProcedureWorkerXpower'; 69import { XpowerAppDetailStruct } from '../../../database/ui-worker/ProcedureWorkerXpowerAppDetail'; 70import { XpowerWifiStruct } from '../../../database/ui-worker/ProcedureWorkerXpowerWifi'; 71 72const INPUT_WORD = 73 '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.'; 74 75const CLOCK_STARTTIME_ABSALUTED_ID: string = 'clockStartTimeAbsaluteId'; 76const CLOCK_TRANSF_BTN_ID: string = 'clockTransfBtnId'; 77const CPU_STARTTIME_ABSALUTED_ID: string = 'cpuStartTimeAbsaluteId'; 78const CPU_TRANSF_BTN_ID: string = 'cpuTransfBtnId'; 79const THREAD_STARTTIME_ABSALUTED_ID: string = 'threadStartTimeAbsaluteId'; 80const THREAD_TRANSF_BTN_ID: string = 'threadTransfBtnId'; 81const FUN_STARTTIME_ABSALUTED_ID: string = 'funStartTimeAbsaluteId'; 82const FUN_TRANSF_BTN_ID: string = 'funTransfBtnId'; 83 84export function getTimeString(ns: number): string { 85 if (ns === 0) { 86 return '0'; 87 } 88 let currentTimeNs = ns; 89 let hour1 = 3600_000_000_000; 90 let minute1 = 60_000_000_000; 91 let second1 = 1_000_000_000; // 1 second 92 let millisecond1 = 1_000_000; // 1 millisecond 93 let microsecond1 = 1_000; // 1 microsecond 94 let res = ''; 95 if (currentTimeNs >= hour1) { 96 res += Math.floor(currentTimeNs / hour1) + 'h '; 97 currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / hour1) * hour1; 98 } 99 if (currentTimeNs >= minute1) { 100 res += Math.floor(currentTimeNs / minute1) + 'm '; 101 currentTimeNs = currentTimeNs - Math.floor(ns / minute1) * minute1; 102 } 103 if (currentTimeNs >= second1) { 104 res += Math.floor(currentTimeNs / second1) + 's '; 105 currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / second1) * second1; 106 } 107 if (currentTimeNs >= millisecond1) { 108 res += Math.floor(currentTimeNs / millisecond1) + 'ms '; 109 currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / millisecond1) * millisecond1; 110 } 111 if (currentTimeNs >= microsecond1) { 112 res += Math.floor(currentTimeNs / microsecond1) + 'μs '; 113 currentTimeNs = currentTimeNs - Math.floor(currentTimeNs / microsecond1) * microsecond1; 114 } 115 if (currentTimeNs > 0) { 116 res += currentTimeNs.toFixed(0) + 'ns '; 117 } 118 return res; 119} 120 121function compare(property: string, sort: number, type: string) { 122 return function (xpowerSortLeftData: SortData, xpowerSortRightData: SortData): number { 123 if (type === 'number') { 124 return sort === 2 // @ts-ignore 125 ? parseFloat(xpowerSortRightData[property]) - parseFloat(xpowerSortLeftData[property]) // @ts-ignore 126 : parseFloat(xpowerSortLeftData[property]) - parseFloat(xpowerSortRightData[property]); 127 } else if (type === 'duration') { 128 return sort === 2 129 ? xpowerSortRightData.dur - xpowerSortLeftData.dur 130 : xpowerSortLeftData.dur - xpowerSortRightData.dur; 131 } else if (type === 'bytes') { 132 return sort === 2 133 ? xpowerSortRightData.bytes - xpowerSortLeftData.bytes 134 : xpowerSortLeftData.bytes - xpowerSortRightData.bytes; 135 } { 136 // @ts-ignore 137 if (xpowerSortRightData[property] > xpowerSortLeftData[property]) { 138 return sort === 2 ? 1 : -1; 139 } else { 140 // @ts-ignore 141 if (xpowerSortRightData[property] === xpowerSortLeftData[property]) { 142 return 0; 143 } else { 144 return sort === 2 ? -1 : 1; 145 } 146 } 147 } 148 }; 149} 150 151@element('tabpane-current-selection') 152export class TabPaneCurrentSelection extends BaseElement { 153 weakUpBean: WakeupBean | null | undefined; 154 selectWakeupBean: unknown; 155 private currentSelectionTbl: LitTable | null | undefined; 156 private tableObserver: MutationObserver | undefined; 157 private wakeupListTbl: LitTable | undefined | null; 158 private scrollView: HTMLDivElement | null | undefined; 159 // @ts-ignore 160 private dpr = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1; 161 private wakeUp: string = ''; 162 private isFpsAvailable: boolean = true; 163 private realTime: number = 0; 164 private bootTime: number = 0; 165 private funcDetailMap: Map<string, Array<object>> = new Map(); 166 private topChainStr: string = ''; 167 private fpsResult: Array<unknown> = []; 168 169 set data(selection: unknown) { 170 // @ts-ignore 171 if (selection !== undefined && selection.constructor && selection.constructor.name !== 'SelectionParam') { 172 // @ts-ignore 173 this.setCpuData(selection); 174 } 175 } 176 177 /** 178 * 创建StartTime的Dom节点 179 * @param selectTable面板的dom树对象 180 * @param startTs 开始时间 181 * @param transfBtnId 转换按钮id 182 * @param startTimeAbsaluteId 开始时间的id 183 */ 184 createStartTimeNode(list: unknown[], startTs: number, transfBtnId: string, startTimeAbsaluteId: string): void { 185 let timeStr: string = ''; 186 let startTimeValue: string = ''; 187 let startTimeAbsolute = startTs + Utils.getInstance().getRecordStartNS(); 188 if (this.realTime > 0) { 189 if (Utils.isTransformed) { 190 timeStr = this.getRealTimeStr(startTimeAbsolute); 191 } else { 192 timeStr = startTimeAbsolute / 1000000000 + 's'; 193 } 194 startTimeValue = `<div style="white-space: nowrap;display: flex;align-items: center"> 195 <div id="${startTimeAbsaluteId}" style="white-space:pre-wrap" >${timeStr}</div> 196 <lit-icon id="${transfBtnId}" class="temp-icon" title="Convert to realtime" name="restore" size="30" 197 style="position: relative; top: 5px; left: 10px;"></lit-icon> 198 </div>`; 199 } else { 200 startTimeValue = startTimeAbsolute / 1000000000 + 's'; 201 } 202 list.push({ 203 name: 'StartTime(Absolute)', 204 value: startTimeValue, 205 }); 206 } 207 208 /** 209 * 给转换按钮添加点击事件 210 * @param startTimeAbsolute 开始时间 211 * @param transfBtnId 转换按钮id 212 * @param startTimeAbsaluteId 开始时间ID 213 */ 214 addClickToTransfBtn(startTimeAbsolute: number, transfBtnId: string, startTimeAbsaluteId: string): void { 215 let transfBtn = this.currentSelectionTbl?.shadowRoot?.querySelector(`#${transfBtnId}`); 216 transfBtn?.addEventListener('click', () => { 217 let startTimeAbsalute = this.currentSelectionTbl?.shadowRoot?.querySelector(`#${startTimeAbsaluteId}`); 218 if (startTimeAbsalute) { 219 if (Utils.isTransformed) { 220 startTimeAbsalute!.innerHTML = startTimeAbsolute / 1000000000 + 's'; 221 Utils.isTransformed = false; 222 } else { 223 startTimeAbsalute!.innerHTML = this.getRealTimeStr(startTimeAbsolute); 224 Utils.isTransformed = true; 225 } 226 } 227 }); 228 } 229 230 async setCpuData( 231 data: CpuStruct, 232 callback: ((data: WakeupBean | null) => void) | undefined = undefined, 233 scrollCallback?: (data: CpuStruct) => void 234 ): Promise<void> { 235 if (SpApplication.traceType.indexOf('SQLite') === -1) { 236 await this.setRealTime(); 237 } 238 this.setTableHeight('650px'); 239 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 240 if (leftTitle) { 241 leftTitle.innerText = 'Slice Details'; 242 } 243 this.currentSelectionTbl!.loading = true; 244 let list: unknown[] = []; 245 this.updateUI(data, list); 246 Promise.all([this.queryThreadStateDArgs(data.argSetID), this.queryCPUWakeUpFromData(data)]).then((resArr) => { 247 let args = resArr[0]; 248 let bean = resArr[1]; 249 if (callback) { 250 callback(bean); 251 } 252 if (args.length > 0) { 253 args.forEach((arg) => { 254 list.push({ name: arg.keyName, value: arg.strValue }); 255 }); 256 } 257 this.currentSelectionTbl!.dataSource = list; 258 this.currentSelectionTbl!.loading = false; 259 let startTimeAbsolute = (data.startTime || 0) + Utils.getInstance().getRecordStartNS(); 260 this.addClickToTransfBtn(startTimeAbsolute, CPU_TRANSF_BTN_ID, CPU_STARTTIME_ABSALUTED_ID); 261 262 let rightArea: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#table-right'); 263 let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle'); 264 let rightButton: HTMLElement | null | undefined = this?.shadowRoot 265 ?.querySelector('#rightButton') 266 ?.shadowRoot?.querySelector('#custom-button'); 267 let rightStar: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#right-star'); 268 this.threadClickEvent(scrollCallback, data); 269 let canvas = this.initCanvas(); 270 if (bean !== null) { 271 this.selectWakeupBean = { 272 process: `${this.transferString(data.processName || 'Process')}(${data.processId})`, 273 thread: `${this.transferString(data.name || 'Thread')}(${data.tid})`, 274 cpu: data.cpu, 275 dur: data.dur, 276 priority: data.priority, 277 isSelected: false, 278 }; 279 280 this.weakUpBean = bean; 281 this.updateRightTitleUI(rightArea!, rightTitle!, rightButton!, rightStar!); 282 this.drawRight(canvas, bean); 283 } else { 284 this.handleNullBeanUI(rightArea!, rightTitle!, rightButton!, rightStar!); 285 } 286 }); 287 } 288 289 private threadClickEvent(scrollCallback: ((data: CpuStruct) => void) | undefined, data: CpuStruct): void { 290 let threadClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#thread-id'); 291 threadClick?.addEventListener('click', () => { 292 //cpu点击 293 if (scrollCallback) { 294 data.state = 'Running'; 295 scrollCallback(data); 296 } 297 }); 298 } 299 300 private updateRightTitleUI( 301 rightArea: HTMLElement, 302 rightTitle: HTMLElement, 303 rightButton: HTMLElement, 304 rightStar: HTMLElement 305 ): void { 306 if (rightArea !== null && rightArea) { 307 rightArea.style.visibility = 'visible'; 308 } 309 if (rightTitle !== null && rightTitle) { 310 rightTitle.style.visibility = 'visible'; 311 rightButton!.style.visibility = 'visible'; 312 rightStar!.style.visibility = 'hidden'; 313 SpSystemTrace.btnTimer = null; 314 } 315 } 316 317 private handleNullBeanUI( 318 rightArea: HTMLElement, 319 rightTitle: HTMLElement, 320 rightButton: HTMLElement, 321 rightStar: HTMLElement 322 ): void { 323 this.weakUpBean = null; 324 if (rightArea !== null && rightArea) { 325 rightArea.style.visibility = 'hidden'; 326 } 327 if (rightTitle !== null && rightTitle) { 328 rightTitle.style.visibility = 'hidden'; 329 rightButton!.style.visibility = 'hidden'; 330 rightStar!.style.visibility = 'hidden'; 331 } 332 } 333 334 private updateUI(data: CpuStruct, list: unknown[]): void { 335 let process = this.transferString(data.processName || 'Process'); 336 let processId = data.processId || data.tid; 337 let state = ''; 338 if (data.end_state) { 339 state = Utils.getEndState(data.end_state); 340 } else if (data.end_state === '' || data.end_state === null) { 341 state = ''; 342 } else { 343 state = 'Unknown State'; 344 } 345 list.push({ 346 name: 'Process', 347 value: `${process || 'Process'} [${processId}]`, 348 }); 349 let name = this.transferString(data.name ?? ''); 350 if (data.processId) { 351 list.push({ 352 name: 'Thread', 353 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 354<div style="white-space:pre-wrap">${name || 'Process'} [${data.tid}]</div> 355<lit-icon style="cursor:pointer;margin-left: 5px" id="thread-id" name="select" color="#7fa1e7" size="20"></lit-icon> 356</div>`, 357 }); 358 } else { 359 list.push({ 360 name: 'Thread', 361 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 362<div style="white-space:pre-wrap">${name || 'Process'} [${data.tid}]</div> 363</div>`, 364 }); 365 } 366 367 list.push({ name: 'StartTime(Relative)', value: getTimeString(data.startTime || 0) }); 368 this.createStartTimeNode(list, data.startTime || 0, CPU_TRANSF_BTN_ID, CPU_STARTTIME_ABSALUTED_ID); 369 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 370 list.push({ name: 'Prio', value: data.priority || 0 }); 371 list.push({ name: 'End State', value: state }); 372 } 373 // 设置真实时间和启动时间的值 374 private async setRealTime(): Promise<unknown> { 375 return queryRealTime().then((result) => { 376 if (result && result.length > 0) { 377 result.forEach((item) => { 378 if (item.name === 'realtime') { 379 this.realTime = item.ts; 380 } else { 381 this.bootTime = item.ts; 382 } 383 }); 384 } 385 }); 386 } 387 388 async setFunctionData( 389 data: FuncStruct, 390 threadName: string, 391 scrollCallback: Function, 392 callback?: (data: Array<unknown>, str: string, binderTid: number) => void, 393 distributedCallback?: Function, 394 ): Promise<void> { 395 //方法信息 396 if (SpApplication.traceType.indexOf('SQLite') === -1) { 397 await this.setRealTime(); 398 } 399 this.tabCurrentSelectionInit('Slice Details'); 400 let list: unknown[] = []; 401 let name = this.transferString(data.funName ?? ''); 402 // 从缓存中拿到详细信息的表 403 let information: string = ''; 404 let FunDetailList: Array<FunDetail> = new Array(); 405 if (this.funcDetailMap.size === 0) { 406 await caches.match('/funDetail').then((res) => { 407 let result: Promise<string> | undefined; 408 if (res) { 409 result = res!.text(); 410 } 411 return result; 412 }).then((res) => { 413 if (res) { 414 let funcDetail = JSON.parse(res); 415 for (let key in funcDetail) { 416 this.funcDetailMap.set(key, funcDetail[key]); 417 } 418 } 419 }); 420 } 421 // @ts-ignore 422 FunDetailList = this.funcDetailMap.has(threadName) ? this.funcDetailMap.get('threadName') : this.funcDetailMap.get('anothers'); 423 if (Array.isArray(FunDetailList)) { 424 // 筛选当前函数块的信息项 425 let informationList: Array<FunDetail> = FunDetailList.filter((v: FunDetail) => { 426 return v.slice.indexOf(name) > -1 || name.indexOf(v.slice) > -1; 427 }); 428 information = informationList && informationList.length > 0 ? informationList[0].CN : 429 `<div style="white-space: nowrap;display: flex;align-items: center"> 430 <div style="white-space:pre-wrap">无相关描述,如您知道具体含义可点击反馈</div> 431 <lit-icon style="cursor:pointer;margin-left: 5px; margin-top:5px" id="informationJump" name="select" color="#7fa1e7" size="20"> 432 </lit-icon> 433 </a> 434 </div>`; 435 } 436 let isBinder = FuncStruct.isBinder(data); 437 let jankJumperList = new Array<ThreadTreeNode>(); 438 let isAsyncBinder = isBinder && FuncStruct.isBinderAsync(data); 439 if (data.argsetid !== undefined && data.argsetid !== null && data.argsetid >= 0) { 440 this.setTableHeight('700px'); 441 if (isAsyncBinder) { 442 this.handleAsyncBinder(data, list, jankJumperList, name, scrollCallback, information, callback); 443 } else if (isBinder) { 444 this.handleBinder(data, list, jankJumperList, name, scrollCallback, information, callback); 445 } else { 446 this.handleNonBinder(data, list, name, information); 447 } 448 } else if (data.funName && data.funName!.startsWith('H:Et') && (data.depth === 1 || data.depth === 0)) { 449 list.push({ 450 name: 'StartTime(Relative)', 451 value: getTimeString(data.startTs || 0), 452 }); 453 this.createStartTimeNode(list, data.startTs || 0, FUN_TRANSF_BTN_ID, FUN_STARTTIME_ABSALUTED_ID); 454 list.push({ 455 name: 'Duration', 456 value: getTimeString(data.dur || 0), 457 }); 458 data.funName!.split(',').map((item, index) => ({ 459 name: [ 460 'Sender tid', 461 'Send time', 462 'Expect handle time', 463 'Task name/ID', 464 'Prio', 465 'Sender' 466 ][index], 467 value: item, 468 })).forEach((item, index) => { 469 if (index === 0) { 470 item.value = item.value.split(':').at(-1)!; 471 } 472 list.push(item); 473 }); 474 this.currentSelectionTbl!.dataSource = list; 475 // @ts-ignore 476 let startTimeAbsolute = (data.startTs || 0) + (window as unknown).recordStartNS; 477 this.addClickToTransfBtn(startTimeAbsolute, FUN_TRANSF_BTN_ID, FUN_STARTTIME_ABSALUTED_ID); 478 } else { 479 this.setTableHeight('auto'); 480 list.push({ name: 'Name', value: name }); 481 if (data.cookie || data.cookie === 0) { 482 list.push({ name: 'TaskId', value: data.cookie }); 483 } 484 let processName = Utils.getInstance().getProcessMap().get(data.pid!); 485 let threadName = Utils.getInstance().getThreadMap().get(data.tid!); 486 let dataTid = isNaN(data.tid!) ? 'NULL' : data.tid; 487 list.push({ 488 name: 'Process', 489 value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ', 490 }); 491 list.push({ 492 name: 'Thread', 493 value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + dataTid + '] ', 494 }); 495 list.push({ 496 name: 'StartTime(Relative)', 497 value: getTimeString(data.startTs || 0), 498 }); 499 this.createStartTimeNode(list, data.startTs || 0, FUN_TRANSF_BTN_ID, FUN_STARTTIME_ABSALUTED_ID); 500 list.push({ 501 name: 'Duration', 502 value: getTimeString(data.dur || 0), 503 }); 504 list.push({ name: 'depth', value: data.depth }); 505 list.push({ name: 'information:', value: information }); 506 if (data.chainId) { 507 list.push({ name: 'ChainId', value: data.chainId }); 508 list.push({ name: 'SpanId', value: data.spanId }); 509 list.push({ name: 'ParentSpanId', value: data.parentSpanId }); 510 list.push({ name: 'ChainFlag', value: data.chainFlag }); 511 await this.chainSpanListCallBackHandle(data, distributedCallback); 512 } 513 this.currentSelectionTbl!.dataSource = list; 514 // @ts-ignore 515 let startTimeAbsolute = (data.startTs || 0) + (window as unknown).recordStartNS; 516 this.addClickToTransfBtn(startTimeAbsolute, FUN_TRANSF_BTN_ID, FUN_STARTTIME_ABSALUTED_ID); 517 } 518 } 519 520 // 计算真实时间 521 private getRealTimeStr(startTs: number): string { 522 let time = (startTs || 0) - Utils.getInstance().getRecordStartNS() - this.bootTime + this.realTime; 523 const formateDateStr = 524 this.getDate(parseInt(time.toString().substring(0, 13))) + '.' + time.toString().substring(10); 525 return formateDateStr; 526 } 527 528 // 格式化时间戳为字符串格式 yyyy/mm/dd hh:mi:ss 529 private getDate(timestamp: number): string { 530 let date = new Date(timestamp); 531 let gmt = date.toLocaleString(); 532 return gmt; 533 } 534 535 private async chainSpanListCallBackHandle(data: FuncStruct, callBack: Function | undefined): Promise<void> { 536 let allMainChainList: FuncStruct[]; 537 if (Utils.currentTraceMode === TraceMode.DISTRIBUTED) { 538 let chainAllData = await Promise.all([ 539 queryDistributedRelationAllData(data.chainId!, threadPool.traceId), 540 queryDistributedRelationAllData(data.chainId!, threadPool2.traceId), 541 ]); 542 allMainChainList = [...chainAllData[0], ...chainAllData[1]]; 543 } else { 544 allMainChainList = await queryDistributedRelationAllData(data.chainId!); 545 } 546 let finalChainSpanList: FuncStruct[] = allMainChainList.filter((spanNode) => spanNode.spanId === data.spanId); 547 let setParentChainList = (currentData: FuncStruct): void => { 548 let currentParentSpanList = allMainChainList.filter((spanNode) => spanNode.spanId === currentData.parentSpanId); 549 for (let index = currentParentSpanList.length - 1; index >= 0; index--) { 550 let spanStruct = currentParentSpanList[index]; 551 if (finalChainSpanList.indexOf(spanStruct) < 0) { 552 finalChainSpanList.unshift(spanStruct); 553 } 554 } 555 if ( 556 currentParentSpanList.length > 0 && 557 currentParentSpanList[0].parentSpanId !== '' && 558 currentParentSpanList[0].parentSpanId !== '0' 559 ) { 560 setParentChainList(currentParentSpanList[0]); 561 } 562 }; 563 setParentChainList(data); 564 let setChildChainList = (currentData: FuncStruct): void => { 565 let currentChildSpanList = allMainChainList.filter((spanNode) => spanNode.parentSpanId === currentData.spanId); 566 currentChildSpanList.forEach((childSpan) => { 567 if (finalChainSpanList.indexOf(childSpan) < 0) { 568 finalChainSpanList.push(childSpan); 569 } 570 }); 571 if ( 572 currentChildSpanList.length > 0 && 573 currentChildSpanList[0].spanId !== '' && 574 currentChildSpanList[0].spanId !== '0' 575 ) { 576 setChildChainList(currentChildSpanList[0]); 577 } 578 }; 579 setChildChainList(data); 580 if (callBack) { 581 callBack(finalChainSpanList); 582 } 583 } 584 585 private handleNonBinder(data: FuncStruct, list: unknown[], name: string, information: string): void { 586 queryBinderArgsByArgset(data.argsetid!).then((argset) => { 587 list.push({ name: 'Name', value: name }); 588 let processName = Utils.getInstance().getProcessMap().get(data.pid!); 589 let threadName = Utils.getInstance().getThreadMap().get(data.tid!); 590 list.push({ name: 'Name', value: name }, { 591 name: 'Process', 592 value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ', 593 }, { 594 name: 'Thread', 595 value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ', 596 }); 597 argset.forEach((item) => { 598 list.push({ name: item.keyName, value: item.strValue }); 599 }); 600 this.addTabPanelContent(list, data, information); 601 this.currentSelectionTbl!.dataSource = list; 602 }); 603 } 604 605 private handleBinder( 606 data: FuncStruct, 607 list: unknown[], 608 jankJumperList: ThreadTreeNode[], 609 name: string, 610 scrollCallback: Function, 611 information: string, 612 callback?: (data: Array<unknown>, str: string, binderTid: number) => void 613 ): void { 614 queryBinderArgsByArgset(data.argsetid!).then((argset) => { 615 let binderSliceId = -1; 616 let binderTid = -1; 617 let processName = Utils.getInstance().getProcessMap().get(data.pid!); 618 let threadName = Utils.getInstance().getThreadMap().get(data.tid!); 619 argset.forEach((item) => { 620 if (item.keyName === 'calling tid') { 621 binderTid = Number(item.strValue); 622 } 623 if (item.keyName === 'destination slice id') { 624 binderSliceId = Number(item.strValue); 625 list.unshift({ 626 name: 'Name', 627 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 628<div style="white-space:pre-wrap">${name || 'binder'}</div> 629<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="function-jump" name="select" color="#7fa1e7" size="20"></lit-icon> 630</div>`, 631 }, { 632 name: 'Process', 633 value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ', 634 }, { 635 name: 'Thread', 636 value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ', 637 } 638 ); 639 } 640 list.push({ name: item.keyName, value: item.strValue }); 641 }); 642 if (binderSliceId === -1) { 643 list.unshift({ 644 name: 'Name', value: name 645 }, { 646 name: 'Process', 647 value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ', 648 }, { 649 name: 'Thread', 650 value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ', 651 } 652 ); 653 } 654 this.addTabPanelContent(list, data, information); 655 this.currentSelectionTbl!.dataSource = list; 656 let funcClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#function-jump'); 657 funcClick?.addEventListener('click', () => { 658 if (!Number.isNaN(binderSliceId) && binderSliceId !== -1) { 659 queryBinderBySliceId(binderSliceId).then((result: unknown[]) => { 660 if (result.length > 0) { 661 // @ts-ignore 662 result[0].type = TraceRow.ROW_TYPE_FUNC; 663 scrollCallback(result[0]); 664 let timeLineNode = new ThreadTreeNode( 665 // @ts-ignore 666 result[0]?.tid, 667 // @ts-ignore 668 result[0]?.pid, 669 // @ts-ignore 670 result[0].startTs, 671 // @ts-ignore 672 result[0]?.depth 673 ); 674 jankJumperList.push(timeLineNode); 675 if (callback) { 676 let linkTo = 'binder-to'; 677 callback(jankJumperList, linkTo, binderTid); 678 linkTo = ''; 679 } 680 } 681 }); 682 } 683 }); 684 }); 685 } 686 687 private handleAsyncBinder( 688 data: FuncStruct, 689 list: unknown[], 690 jankJumperList: ThreadTreeNode[], 691 name: string, 692 scrollCallback: Function, 693 information: string, 694 callback?: (data: Array<unknown>, str: string, binderTid: number) => void 695 ): void { 696 let binderTid = -1; 697 Promise.all([ 698 queryBinderByArgsId(data.argsetid!, data.startTs!, !data.funName!.endsWith('rcv')), 699 queryBinderArgsByArgset(data.argsetid!), 700 ]).then((result) => { 701 let asyncBinderRes = result[0]; 702 let argsBinderRes = result[1]; 703 let asyncBinderStract: unknown; 704 let processName = Utils.getInstance().getProcessMap().get(data.pid!); 705 let threadName = Utils.getInstance().getThreadMap().get(data.tid!); 706 if (asyncBinderRes.length > 0) { 707 //@ts-ignore 708 asyncBinderRes[0].type = TraceRow.ROW_TYPE_FUNC; 709 asyncBinderStract = asyncBinderRes[0]; 710 } 711 if (argsBinderRes.length > 0) { 712 argsBinderRes.forEach((item) => { 713 list.push({ 714 name: item.keyName, 715 value: item.strValue, 716 }); 717 if (item.keyName === 'calling tid') { 718 binderTid = Number(item.strValue); 719 } 720 }); 721 } 722 723 if (asyncBinderStract !== undefined) { 724 list.unshift({ 725 name: 'Name', 726 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 727<div style="white-space:pre-wrap">${name || 'binder'}</div> 728<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="function-jump" name="select" color="#7fa1e7" size="20"></lit-icon> 729</div>`, 730 }, { 731 name: 'Process', 732 value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ', 733 }, { 734 name: 'Thread', 735 value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ', 736 }); 737 } else { 738 list.unshift({ 739 name: 'Name', 740 value: name 741 }, { 742 name: 'Process', 743 value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ', 744 }, { 745 name: 'Thread', 746 value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ', 747 }); 748 } 749 this.addTabPanelContent(list, data, information); 750 this.currentSelectionTbl!.dataSource = list; 751 let funcClick = this.currentSelectionTbl?.shadowRoot?.querySelector('#function-jump'); 752 funcClick?.addEventListener('click', () => { 753 scrollCallback(asyncBinderStract); 754 let timeLineNode = new ThreadTreeNode( 755 // @ts-ignore 756 asyncBinderStract.tid, 757 // @ts-ignore 758 asyncBinderStract.pid, 759 // @ts-ignore 760 asyncBinderStract.startTs, 761 // @ts-ignore 762 asyncBinderStract.depth 763 ); 764 jankJumperList.push(timeLineNode); 765 if (callback) { 766 let linkTo = 'binder-to'; 767 callback(jankJumperList, linkTo, binderTid); 768 linkTo = ''; 769 } 770 }); 771 772 }); 773 } 774 775 private addTabPanelContent(contentList: unknown[], data: FuncStruct, information: string): void { 776 contentList.push({ 777 name: 'StartTime(Relative)', 778 value: getTimeString(data.startTs || 0), 779 }); 780 contentList.push({ 781 name: 'StartTime(Absolute)', 782 // @ts-ignore 783 value: ((data.startTs || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 784 }); 785 contentList.push({ 786 name: 'Duration', 787 value: getTimeString(data.dur || 0), 788 }); 789 contentList.push({ name: 'depth', value: data.depth }); 790 if (data.argsetid && data.argsetid > -1) { 791 contentList.push({ name: 'arg_set_id', value: data.argsetid }); 792 } 793 contentList.push({ name: 'information', value: information }); 794 } 795 796 private tabCurrentSelectionInit(leftTitleStr: string): void { 797 this.initCanvas(); 798 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 799 this.setTitleAndButtonStyle(); 800 if (leftTitle) { 801 leftTitle.innerText = leftTitleStr; 802 } 803 } 804 805 private setTitleAndButtonStyle(): void { 806 let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle'); 807 let rightButton: HTMLElement | null | undefined = this?.shadowRoot 808 ?.querySelector('#rightButton') 809 ?.shadowRoot?.querySelector('#custom-button'); 810 let rightStar: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#right-star'); 811 if (rightTitle) { 812 rightTitle.style.visibility = 'hidden'; 813 rightButton!.style.visibility = 'hidden'; 814 rightStar!.style.visibility = 'hidden'; 815 } 816 } 817 818 async setClockData(data: ClockStruct): Promise<void> { 819 if (SpApplication.traceType.indexOf('SQLite') === -1) { 820 await this.setRealTime(); 821 } 822 this.setTableHeight('auto'); 823 //时钟信息 824 this.tabCurrentSelectionInit('Counter Details'); 825 let list: unknown[] = []; 826 list.push({ 827 name: 'StartTime(Relative)', 828 value: getTimeString(data.startNS || 0), 829 }); 830 this.createStartTimeNode(list, data.startNS || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 831 list.push({ 832 name: 'Value', 833 value: ColorUtils.formatNumberComma(data.value || 0), 834 }); 835 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 836 this.currentSelectionTbl!.dataSource = list; 837 let startTimeAbsolute = (data.startNS || 0) + Utils.getInstance().getRecordStartNS(); 838 this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 839 } 840 841 setDmaFenceData(data: DmaFenceStruct, rowData: unknown[]): void { 842 this.setTableHeight('auto'); 843 this.tabCurrentSelectionInit('Slice Details'); 844 let list: unknown[] = []; 845 list.push({ 846 name: 'Title', 847 value: data.sliceName, 848 }); 849 list.push({ 850 name: 'StartTime(Relative)', 851 value: getTimeString(data.startTime || 0), 852 }); 853 list.push({ 854 name: 'StartTime(Absolute)', 855 // @ts-ignore 856 value: ((data.startTime || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 857 }); 858 if (data.dur !== 0) { 859 list.push({ 860 name: 'Wall Duration', 861 value: getTimeString(data.dur || 0) 862 }); 863 } 864 list.push({ 865 name: 'driver', 866 value: data.driver, 867 }); 868 list.push({ 869 name: 'context', 870 value: data.context, 871 }); 872 this.currentSelectionTbl!.dataSource = list; 873 } 874 875 async setXpowerData(data: XpowerStruct): Promise<void> { 876 if (SpApplication.traceType.indexOf('SQLite') === -1) { 877 await this.setRealTime(); 878 } 879 this.setTableHeight('auto'); 880 this.tabCurrentSelectionInit('Counter Details'); 881 let list: unknown[] = []; 882 list.push({ 883 name: 'StartTime(Relative)', 884 value: getTimeString(data.startNS || 0), 885 }); 886 this.createStartTimeNode(list, data.startNS || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 887 list.push({ 888 name: 'Value', 889 value: String(data.value).indexOf('.') > -1 ? data.value || 0 : ColorUtils.formatNumberComma(data.value || 0), 890 }); 891 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 892 this.currentSelectionTbl!.dataSource = list; 893 let startTimeAbsolute = (data.startNS || 0) + Utils.getInstance().getRecordStartNS(); 894 this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 895 } 896 897 async setXpowerDisplayData(data: XpowerAppDetailStruct): Promise<void> { 898 if (SpApplication.traceType.indexOf('SQLite') === -1) { 899 await this.setRealTime(); 900 } 901 this.setTableHeight('auto'); 902 this.tabCurrentSelectionInit('Display Details'); 903 let list: unknown[] = []; 904 list.push({ 905 name: 'StartTime(Relative)', 906 value: getTimeString(data.startTime || 0), 907 }); 908 this.createStartTimeNode(list, data.startTime || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 909 let vals: { name: string, dur: number, value: string }[] = []; 910 let column = ['1hz', '5hz', '10hz', '15hz', '24hz', '30hz', '45hz', '60hz', '90hz', '120hz', '180hz']; 911 column.forEach((item) => { 912 // @ts-ignore 913 data['c' + item] !== 0 && vals.push({ 914 name: item, 915 // @ts-ignore 916 dur: data['c' + item], 917 // @ts-ignore 918 value: data['c' + item] + ' ms', 919 }); 920 }); 921 // @ts-ignore 922 vals.sort(compare('value', 2, 'duration')); 923 list.push(...vals); 924 this.currentSelectionTbl!.dataSource = list; 925 let startTimeAbsolute = (data.startTime || 0) + Utils.getInstance().getRecordStartNS(); 926 this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 927 } 928 929 async setXpowerWifiBytesData(data: XpowerWifiStruct): Promise<void> { 930 if (SpApplication.traceType.indexOf('SQLite') === -1) { 931 await this.setRealTime(); 932 } 933 this.setTableHeight('auto'); 934 this.tabCurrentSelectionInit('WIFIBytes Details'); 935 let list: unknown[] = []; 936 list.push({ 937 name: 'StartTime(Relative)', 938 value: getTimeString(data.startTime || 0), 939 }); 940 this.createStartTimeNode(list, data.startTime || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 941 let vals: { name: string, bytes: number, value: string }[] = []; 942 data.tx !== 0 && vals.push({ 943 name: 'send', 944 bytes: data.tx, 945 value: data.tx + ' B', 946 }); 947 data.rx !== 0 && vals.push({ 948 name: 'receiver', 949 bytes: data.rx, 950 value: data.rx + ' B', 951 }); 952 // @ts-ignore 953 vals.sort(compare('value', 2, 'bytes')); 954 list.push(...vals); 955 this.currentSelectionTbl!.dataSource = list; 956 let startTimeAbsolute = (data.startTime || 0) + Utils.getInstance().getRecordStartNS(); 957 this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 958 } 959 960 async setXpowerWifiPacketsData(data: XpowerWifiStruct): Promise<void> { 961 if (SpApplication.traceType.indexOf('SQLite') === -1) { 962 await this.setRealTime(); 963 } 964 this.setTableHeight('auto'); 965 this.tabCurrentSelectionInit('WIFIPackets Details'); 966 let list: unknown[] = []; 967 list.push({ 968 name: 'StartTime(Relative)', 969 value: getTimeString(data.startTime || 0), 970 }); 971 this.createStartTimeNode(list, data.startTime || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 972 let vals: { name: string, value: string }[] = []; 973 data.tx !== 0 && vals.push({ 974 name: 'send', 975 value: data.tx.toString(), 976 }); 977 data.rx !== 0 && vals.push({ 978 name: 'receiver', 979 value: data.rx.toString(), 980 }); 981 // @ts-ignore 982 vals.sort(compare('value', 2, 'number')); 983 list.push(...vals); 984 this.currentSelectionTbl!.dataSource = list; 985 let startTimeAbsolute = (data.startTime || 0) + Utils.getInstance().getRecordStartNS(); 986 this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 987 } 988 989 async setHangData(data: HangStruct, sp: SpSystemTrace, scrollCallback: Function): Promise<void> { 990 await this.setRealTime(); 991 this.setTableHeight('auto'); 992 this.tabCurrentSelectionInit('Hang Details'); 993 let list: unknown[] = []; 994 list.push({ 995 name: 'StartTime(Relative)', 996 value: getTimeString(data.startTime || 0), 997 }); 998 this.createStartTimeNode(list, data.startTime || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 999 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 1000 list.push({ 1001 name: 'Hang type', 1002 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1003<div style="white-space:pre-wrap">${data.type}</div> 1004<lit-icon style="cursor:pointer;margin-left: 5px" id="scroll-to-process" name="select" color="#7fa1e7" size="20"></lit-icon> 1005</div>` 1006 }); 1007 data.content!.split(',').map((item, index) => ({ 1008 name: [ 1009 'Sender tid', 1010 'Send time', 1011 'Expect handle time', 1012 'Task name/ID', 1013 'Prio', 1014 'Sender' 1015 ][index], 1016 value: item, 1017 })).forEach((item, index) => { 1018 if (index === 0) { 1019 item.value = item.value.split(':').at(-1)!; 1020 } 1021 list.push(item); 1022 }); 1023 1024 this.currentSelectionTbl!.dataSource = list; 1025 // @ts-ignore 1026 let startTimeAbsolute = (data.startTime || 0) + window.recordStartNS; 1027 this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); 1028 this.hangScrollHandler(data, sp, scrollCallback); 1029 } 1030 1031 private hangScrollHandler(data: HangStruct, sp: SpSystemTrace, scrollCallback: Function): void { 1032 let scrollIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#scroll-to-process'); 1033 scrollIcon?.addEventListener('click', async () => { 1034 //@ts-ignore 1035 let folderRow = sp.shadowRoot?.querySelector<TraceRow<unknown>>( 1036 `trace-row[row-id='${Utils.getDistributedRowId(data.pid)}'][row-type='process']` 1037 ); 1038 if (folderRow) { 1039 folderRow.expansion = true; 1040 } 1041 let funcRow = sp.queryAllTraceRow<TraceRow<FuncStruct>>( 1042 `trace-row[row-id='${Utils.getDistributedRowId(data.tid)}'][row-type='func']`, 1043 (row) => row.rowId === `${data.tid}` && row.rowType === 'func' 1044 )[0]; 1045 sp.currentRow = funcRow; 1046 if (!funcRow.dataListCache || funcRow.dataListCache.length === 0) { 1047 funcRow.dataListCache = await funcRow.supplierFrame!(); 1048 } 1049 const findEntry = funcRow?.dataListCache.find((funcstruct: unknown) => { 1050 //@ts-ignore 1051 return (funcstruct.startTs === data.startTime && funcstruct.funName === data.content); 1052 }) 1053 scrollCallback({ 1054 //@ts-ignore 1055 pid: findEntry.pid, 1056 //@ts-ignore 1057 tid: findEntry.tid, 1058 type: 'func', 1059 //@ts-ignore 1060 dur: findEntry.dur, 1061 //@ts-ignore 1062 depth: findEntry.depth, 1063 //@ts-ignore 1064 funName: findEntry.funName, 1065 //@ts-ignore 1066 startTs: findEntry.startTs, 1067 keepOpen: true 1068 }) 1069 }) 1070 } 1071 1072 setPerfToolsData(data: PerfToolStruct): void { 1073 this.setTableHeight('auto'); 1074 //Perf Tools info 1075 this.tabCurrentSelectionInit('Slice Details'); 1076 let list: unknown[] = []; 1077 list.push({ 1078 name: 'Name', 1079 value: data.name, 1080 }); 1081 list.push({ 1082 name: 'StartTime(Relative)', 1083 value: getTimeString(data.startTs || 0), 1084 }); 1085 list.push({ 1086 name: 'StartTime(Absolute)', 1087 // @ts-ignore 1088 value: ((data.startTs || 0) + (window as unknown).recordStartNS) / 1000000000 + 's', 1089 }); 1090 list.push({ 1091 name: 'Value', 1092 value: data.count, 1093 }); 1094 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 1095 this.currentSelectionTbl!.dataSource = list; 1096 } 1097 1098 setMemData(data: ProcessMemStruct): void { 1099 this.setTableHeight('auto'); 1100 //时钟信息 1101 this.initCanvas(); 1102 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 1103 if (leftTitle) { 1104 leftTitle.innerText = 'Counter Details'; 1105 } 1106 let list: object[] = []; 1107 list.push({ 1108 name: 'StartTime(Relative)', 1109 value: getTimeString(data.startTime || 0), 1110 }); 1111 list.push({ 1112 name: 'StartTime(Absolute)', 1113 value: ((data.startTime || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 1114 }); 1115 list.push({ name: 'Value', value: data.value }); 1116 list.push({ name: 'Delta', value: data.delta }); 1117 list.push({ 1118 name: 'Duration', 1119 value: getTimeString(data.duration || 0), 1120 }); 1121 this.currentSelectionTbl!.dataSource = list; 1122 } 1123 1124 setIrqData(data: IrqStruct): void { 1125 this.setTableHeight('550px'); 1126 this.initCanvas(); 1127 this.setTitleAndButtonStyle(); 1128 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 1129 if (leftTitle) { 1130 leftTitle.innerText = 'Counter Details'; 1131 } 1132 let list: object[] = []; 1133 list.push({ 1134 name: 'StartTime(Relative)', 1135 value: getTimeString(data.startNS || 0), 1136 }); 1137 list.push({ 1138 name: 'StartTime(Absolute)', 1139 value: ((data.startNS || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 1140 }); 1141 list.push({ name: 'Name', value: data.name }); 1142 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 1143 queryBinderArgsByArgset(data.argSetId || 0).then((argsBinderRes) => { 1144 if (argsBinderRes.length > 0) { 1145 argsBinderRes.forEach((item) => { 1146 list.push({ name: item.keyName, value: item.strValue }); 1147 }); 1148 } 1149 this.currentSelectionTbl!.dataSource = list; 1150 }); 1151 } 1152 1153 async setThreadData( 1154 data: ThreadStruct, 1155 scrollCallback: ((d: unknown) => void) | undefined, 1156 scrollWakeUp: (d: unknown) => void | undefined, 1157 scrollPrio: (d: unknown) => void | undefined, 1158 callback?: (data: Array<unknown>, str: string) => void 1159 ): Promise<void> { 1160 //线程信息 1161 if (SpApplication.traceType.indexOf('SQLite') === -1) { 1162 await this.setRealTime(); 1163 } 1164 this.setTableHeight('550px'); 1165 this.initCanvas(); 1166 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 1167 this.setTitleAndButtonStyle(); 1168 if (leftTitle) { 1169 leftTitle.innerText = 'Thread State'; 1170 } 1171 let list: unknown[] = []; 1172 let jankJumperList: Array<ThreadTreeNode> = []; 1173 this.prepareThreadInfo(list, data); 1174 let cpu = new CpuStruct(); 1175 cpu.id = data.id; 1176 cpu.startTime = data.startTime; 1177 this.queryThreadDetails(data, list, jankJumperList, callback, scrollWakeUp, scrollPrio, scrollCallback); 1178 } 1179 1180 private sortByNearData(nearData: unknown[], data: ThreadStruct, list: unknown[]): unknown[] { 1181 let preData: unknown = undefined; 1182 let nextData: unknown = undefined; 1183 1184 nearData 1185 // @ts-ignore 1186 .sort((near1, near2) => near1.startTime - near2.startTime) 1187 .forEach((near) => { 1188 // @ts-ignore 1189 if (near.id === data.id) { 1190 // @ts-ignore 1191 if (near.startTime < data.startTime!) { 1192 preData = near; 1193 list.push({ 1194 name: 'Previous State', 1195 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1196 <div style="white-space:pre-wrap">${ 1197 // @ts-ignore 1198 Utils.getEndState(near.state) 1199 }</div> 1200 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="previous-state-click" name="select" color="#7fa1e7" size="20"></lit-icon> 1201 </div>`, 1202 }); 1203 } else { 1204 nextData = near; 1205 list.push({ 1206 name: 'Next State', 1207 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1208 <div style="white-space:pre-wrap">${ 1209 // @ts-ignore 1210 Utils.getEndState(near.state) 1211 }</div> 1212 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="next-state-click" name="select" color="#7fa1e7" size="20"></lit-icon> 1213 </div>`, 1214 }); 1215 } 1216 } 1217 }); 1218 return [preData, nextData]; 1219 } 1220 1221 private setWakeupData(fromBean: WakeupBean | undefined, wakeUps: WakeupBean[], list: unknown[]): void { 1222 if (fromBean !== null && fromBean !== undefined && fromBean.pid !== 0 && fromBean.tid !== 0) { 1223 list.push({ 1224 name: 'wakeup from tid', 1225 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1226 <div style="white-space:pre-wrap">${fromBean.tid}</div> 1227 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="wakeup-from" 1228 class="wakeup-click" name="select" color="#7fa1e7" size="20"></lit-icon> 1229 </div>`, 1230 }); 1231 list.push({ 1232 name: 'wakeup from top', 1233 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1234 <div style="white-space:pre-wrap" id="wakeup-top-content" style="display: none"></div> 1235 <lit-icon id="wakeup-top" class="temp-icon" title="Get to chain" name="restore" size="30" 1236 style="position: relative; top: 5px; left: 10px;"></lit-icon> 1237 </div>`, 1238 }); 1239 } 1240 if (wakeUps !== null) { 1241 wakeUps.map((e) => { 1242 list.push({ 1243 name: 'wakeup tid', 1244 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1245 <div style="white-space:pre-wrap">${e.tid}</div> 1246 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="wakeup-${e.tid}" 1247 class="wakeup-click" name="select" color="#7fa1e7" size="20"></lit-icon> 1248 </div>`, 1249 }); 1250 }); 1251 } 1252 } 1253 1254 private queryThreadDetails( 1255 data: ThreadStruct, 1256 list: unknown[], 1257 jankJumperList: ThreadTreeNode[], 1258 callback: ((data: Array<unknown>, str: string) => void) | undefined, 1259 scrollWakeUp: (d: unknown) => void | undefined, 1260 scrollPrio: (d: unknown) => void | undefined, 1261 scrollCallback: ((d: unknown) => void) | undefined 1262 ): void { 1263 Promise.all([ 1264 this.queryThreadWakeUpFromData(data.id!, data.startTime!, data.dur!), 1265 this.queryThreadWakeUpData(data.id!, data.startTime!, data.dur!), 1266 this.queryThreadStateDArgs(data.argSetID), 1267 threadNearData('near-data', data.pid!, data.tid!, data.startTime!), 1268 ]).then((result) => { 1269 let fromBean = result[0]; 1270 let wakeUps = result[1]; 1271 let args = result[2]; 1272 let [preData, nextData] = this.sortByNearData(result[3], data, list); 1273 this.setWakeupData(fromBean, wakeUps, list); 1274 if (args.length > 0) { 1275 args.forEach((arg: unknown) => { 1276 // @ts-ignore 1277 list.push({ name: arg.keyName, value: arg.strValue }); 1278 }); 1279 } 1280 this.currentSelectionTbl!.dataSource = list; 1281 let startTimeAbsolute = (data.startTime || 0) + Utils.getInstance().getRecordStartNS(); 1282 this.addClickToTransfBtn(startTimeAbsolute, THREAD_TRANSF_BTN_ID, THREAD_STARTTIME_ABSALUTED_ID); 1283 let timeLineNode = new ThreadTreeNode(data.tid!, data.pid!, data.startTime!); 1284 jankJumperList.push(timeLineNode); 1285 if (callback) { 1286 callback(jankJumperList, this.wakeUp); 1287 this.wakeUp = ''; 1288 } 1289 this.stateClickHandler(preData, nextData, data, scrollWakeUp, scrollCallback, scrollPrio); 1290 this.wakeupClickHandler(wakeUps, fromBean, scrollWakeUp); 1291 this.getWakeupChainClickHandler(fromBean, list); 1292 }); 1293 } 1294 1295 private wakeupClickHandler( 1296 wakeUps: WakeupBean[], 1297 fromBean: WakeupBean | undefined, 1298 scrollWakeUp: (d: unknown) => void | undefined 1299 ): void { 1300 this.currentSelectionTbl?.shadowRoot?.querySelector('#wakeup-from')?.addEventListener('click', () => { 1301 //点击跳转,唤醒和被唤醒的 线程 1302 if (fromBean && scrollWakeUp) { 1303 scrollWakeUp({ 1304 processId: fromBean.pid, 1305 tid: fromBean.tid, 1306 startTime: fromBean.ts, 1307 dur: fromBean.dur, 1308 cpu: fromBean.cpu, 1309 id: fromBean.itid, 1310 state: 'Running', 1311 argSetID: fromBean.argSetID, 1312 }); 1313 } 1314 this.wakeUp = 'wakeup tid'; 1315 }); 1316 if (wakeUps) { 1317 wakeUps.map((up) => { 1318 this.currentSelectionTbl?.shadowRoot?.querySelector(`#wakeup-${up.tid}`)?.addEventListener('click', () => { 1319 //点击跳转,唤醒和被唤醒的 线程 1320 if (up && scrollWakeUp !== undefined) { 1321 scrollWakeUp({ 1322 processId: up.pid, 1323 tid: up.tid, 1324 startTime: up.ts, 1325 dur: up.dur, 1326 cpu: up.cpu, 1327 id: up.itid, 1328 state: up.state, 1329 argSetID: up.argSetID, 1330 }); 1331 } 1332 this.wakeUp = 'wakeup tid'; 1333 }); 1334 }); 1335 } 1336 } 1337 1338 private stateClickHandler( 1339 preData: unknown, 1340 nextData: unknown, 1341 data: ThreadStruct, 1342 scrollWakeUp: (d: unknown) => void | undefined, 1343 scrollCallback: ((d: unknown) => void) | undefined, 1344 scrollPrio: (d: unknown) => void | undefined 1345 ): void { 1346 this.currentSelectionTbl?.shadowRoot?.querySelector('#next-state-click')?.addEventListener('click', () => { 1347 if (nextData && scrollWakeUp !== undefined) { 1348 scrollWakeUp({ 1349 // @ts-ignore 1350 processId: nextData.pid, 1351 // @ts-ignore 1352 tid: nextData.tid, 1353 // @ts-ignore 1354 startTime: nextData.startTime, 1355 // @ts-ignore 1356 dur: nextData.dur, 1357 // @ts-ignore 1358 cpu: nextData.cpu, 1359 // @ts-ignore 1360 id: nextData.id, 1361 // @ts-ignore 1362 state: nextData.state, 1363 // @ts-ignore 1364 argSetID: nextData.argSetID, 1365 }); 1366 } 1367 }); 1368 1369 this.currentSelectionTbl?.shadowRoot?.querySelector('#previous-state-click')?.addEventListener('click', () => { 1370 if (preData && scrollWakeUp !== undefined) { 1371 scrollWakeUp({ 1372 // @ts-ignore 1373 processId: preData.pid, 1374 // @ts-ignore 1375 tid: preData.tid, 1376 // @ts-ignore 1377 startTime: preData.startTime, 1378 // @ts-ignore 1379 dur: preData.dur, 1380 // @ts-ignore 1381 cpu: preData.cpu, 1382 // @ts-ignore 1383 id: preData.id, 1384 // @ts-ignore 1385 state: preData.state, 1386 // @ts-ignore 1387 argSetID: preData.argSetID, 1388 }); 1389 } 1390 }); 1391 1392 this.currentSelectionTbl?.shadowRoot?.querySelector('#state-click')?.addEventListener('click', () => { 1393 //线程点击 1394 if (scrollCallback) { 1395 scrollCallback(data); 1396 } 1397 }); 1398 this.currentSelectionTbl?.shadowRoot?.querySelector('#prio-click')?.addEventListener('click', (ev) => { 1399 if (scrollPrio) { 1400 sqlPrioCount(data).then((res: unknown) => { 1401 scrollPrio(res); 1402 }); 1403 } 1404 }); 1405 } 1406 //点击事件获取唤醒链 1407 private getWakeupChainClickHandler(fromBean: WakeupBean | undefined, list: unknown[]): void { 1408 this.currentSelectionTbl?.shadowRoot?.querySelector('#wakeup-top')?.addEventListener('click', async () => { 1409 this.topChainStr = ''; 1410 //@ts-ignore 1411 let currentThread = list.filter((item) => item.name === 'Thread')?.[0].value;//点击的当前线程 1412 let previouosWakeupThread = Utils.getInstance().getThreadMap().get(fromBean!.tid!) || 'Thread';//唤醒当前线程的上个线程 1413 this.topChainStr = `-->${previouosWakeupThread} [${fromBean!.tid}]-->${currentThread}`; 1414 this.getRWakeUpChain(fromBean); 1415 }) 1416 } 1417 private async prepareThreadInfo(list: unknown[], data: ThreadStruct): Promise<void> { 1418 let processName = Utils.getInstance().getProcessMap().get(data.pid!); 1419 let threadName = Utils.getInstance().getThreadMap().get(data.tid!); 1420 list.push({ 1421 name: 'Process', 1422 value: (this.transferString(processName ?? '') || 'NULL') + ' [' + data.pid + '] ', 1423 }); 1424 list.push({ 1425 name: 'Thread', 1426 value: (this.transferString(threadName ?? '') || 'NULL') + ' [' + data.tid + '] ', 1427 }); 1428 list.push({ 1429 name: 'StartTime(Relative)', 1430 value: getTimeString(data.startTime || 0), 1431 }); 1432 this.createStartTimeNode(list, data.startTime || 0, THREAD_TRANSF_BTN_ID, THREAD_STARTTIME_ABSALUTED_ID); 1433 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 1434 let state; 1435 if (data.state) { 1436 state = Utils.getEndState(data.state); 1437 } else if (data.state === '' || data.state === null) { 1438 state = ''; 1439 } else { 1440 state = 'Unknown State'; 1441 } 1442 if ('Running' === state) { 1443 state = state + ' on CPU ' + data.cpu; 1444 list.push({ 1445 name: 'State', 1446 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1447 <div style="white-space:pre-wrap">${state}</div> 1448 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="state-click" name="select" color="#7fa1e7" size="20"></lit-icon> 1449 </div>`, 1450 }); 1451 } else { 1452 list.push({ name: 'State', value: `${state}` }); 1453 } 1454 if (state.includes('Running')) { 1455 let startTime: number = data.startTime || 0; 1456 let endTime: number = (data.startTime || 0) + (data.dur || 0); 1457 let freqList: Array<unknown> = []; 1458 let str = ''; 1459 freqList = await queryStateFreqList(startTime, endTime, data.cpu || 0); 1460 freqList.forEach((it) => { 1461 // @ts-ignore 1462 if (it.startTime < startTime! && it.endTime > endTime!) { 1463 // @ts-ignore 1464 it.stateDur = data.dur; 1465 // @ts-ignore 1466 } else if (it.startTime < startTime! && startTime! < it.endTime && it.endTime < endTime!) { 1467 // @ts-ignore 1468 it.stateDur = it.endTime - startTime!; 1469 // @ts-ignore 1470 } else if (it.startTime > startTime! && startTime! < it.endTime && it.endTime < endTime!) { 1471 // @ts-ignore 1472 it.stateDur = it.dur; 1473 // @ts-ignore 1474 } else if (it.startTime > startTime! && endTime! > it.startTime && it.endTime > endTime!) { 1475 // @ts-ignore 1476 it.stateDur = endTime! - it.startTime; 1477 } 1478 // @ts-ignore 1479 str += '[' + it.value + ', ' + (it.stateDur || 0) / 1000 + ']' + ','; 1480 }); 1481 list.push({ name: 'Freq [KHz, μs]', value: str.substring(0, str.length - 1) }); 1482 } 1483 let slice = Utils.getInstance().getSchedSliceMap().get(`${data.id}-${data.startTime}`); 1484 if (slice) { 1485 list.push({ 1486 name: 'Prio', 1487 value: `<div style="white-space: nowrap;display: flex;align-item: center"> 1488 <div style="white-space: pre-wrap">${slice.priority}</div> 1489 <lit-icon style="cursor:pointer;transform: scalex(-1);margin-left: 5px" id="prio-click" name="select" color="#7fa1e7" size="20"></lit-icon> 1490 </div>`, 1491 }); 1492 } 1493 } 1494 1495 setJankData( 1496 data: JankStruct, 1497 callback: ((data: Array<unknown>) => void) | undefined = undefined, 1498 scrollCallback: ((d: unknown) => void) | undefined 1499 ): void { 1500 //线程信息 1501 this.setTableHeight('750px'); 1502 this.tabCurrentSelectionInit('Slice Details'); 1503 let list: unknown[] = []; 1504 this.setJankCommonMessage(list, data); 1505 if (`${data.type}` === '0') { 1506 this.handleTypeJank(data, list, scrollCallback, callback); 1507 } else { 1508 this.currentSelectionTbl!.dataSource = list; 1509 } 1510 } 1511 1512 private handleTypeJank( 1513 data: JankStruct, 1514 list: unknown[], 1515 scrollCallback: ((d: unknown) => void) | undefined, 1516 callback: ((data: Array<unknown>) => void) | undefined 1517 ): void { 1518 this.setJankType(data, list); 1519 let jankJumperList: Array<JankTreeNode> = []; 1520 if (data.frameType === 'render_service') { 1521 queryGpuDur(data.id!).then((it) => { 1522 if (it.length > 0) { 1523 //@ts-ignore 1524 list.push({ name: 'Gpu Duration', value: getTimeString(it[0].gpu_dur) }); 1525 } 1526 }); 1527 this.handleRenderServiceJank(data, list, jankJumperList, scrollCallback, callback); 1528 } else if (data.frameType === 'app') { 1529 this.handleAppJank(list, data, jankJumperList, scrollCallback, callback); 1530 } else if (data.frameType === 'frameTime') { 1531 this.handleFrameTimeJank(data, list, jankJumperList, scrollCallback, callback); 1532 } 1533 } 1534 1535 private handleFrameTimeJank( 1536 data: JankStruct, 1537 list: unknown[], 1538 jankJumperList: JankTreeNode[], 1539 scrollCallback: ((d: unknown) => void) | undefined, 1540 callback: ((data: Array<unknown>) => void) | undefined 1541 ): void { 1542 queryGpuDur(data.id!).then((it) => { 1543 if (it.length > 0) { 1544 list.push({ 1545 name: 'Gpu Duration', 1546 //@ts-ignore 1547 value: getTimeString(it[0].gpu_dur), 1548 }); 1549 } 1550 this.addAppFrameDetails(data, list); 1551 this.addRenderServiceFrameDetails(data, list); 1552 this.addFollowingDetails(list, data); 1553 let appNode = new JankTreeNode(data.name!, data.pid!, 'app'); 1554 let rsNode = new JankTreeNode(data.rs_vsync!, data.rs_pid!, 'render_service'); 1555 appNode.children.push(rsNode); 1556 jankJumperList.push(appNode); 1557 this.currentSelectionTbl!.dataSource = list; 1558 this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList); 1559 }); 1560 } 1561 1562 private addRenderServiceFrameDetails(data: JankStruct, list: unknown[]): void { 1563 if (data.rs_name) { 1564 list.push({ 1565 name: 'RenderService Frame', 1566 value: '', 1567 }); 1568 list.push({ 1569 name: 'Process', 1570 value: 'render_service ' + data.rs_pid, 1571 }); 1572 list.push({ 1573 name: 'StartTime(Relative)', 1574 value: getTimeString(data.rs_ts || 0), 1575 }); 1576 list.push({ 1577 name: 'StartTime(Absolute)', 1578 value: ((data.rs_ts || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 1579 }); 1580 list.push({ 1581 name: 'end time', 1582 value: getTimeString(data.rs_ts! + data.rs_dur! || 0), 1583 }); 1584 } 1585 } 1586 1587 private addFollowingDetails(list: unknown[], data: JankStruct): void { 1588 list.push({ 1589 name: 'Following', 1590 value: '', 1591 }); 1592 list.push({ 1593 name: 'Slice', 1594 value: 1595 data.cmdline + 1596 ' [' + 1597 data.name + 1598 ']' + 1599 `<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>`, 1600 }); 1601 } 1602 1603 private addAppFrameDetails(data: JankStruct, list: unknown[]): void { 1604 if (data.name) { 1605 list.push({ 1606 name: 'App Frame', 1607 value: '', 1608 }); 1609 list.push({ 1610 name: 'Process', 1611 value: data.cmdline + ' ' + data.pid, 1612 }); 1613 list.push({ 1614 name: 'StartTime(Relative)', 1615 value: getTimeString(data.ts || 0), 1616 }); 1617 list.push({ 1618 name: 'StartTime(Absolute)', 1619 value: ((data.ts || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 1620 }); 1621 list.push({ 1622 name: 'end time', 1623 value: getTimeString(data!.ts! + data.dur! || 0), 1624 }); 1625 } 1626 } 1627 1628 private handleAppJank( 1629 list: unknown[], 1630 data: JankStruct, 1631 jankJumperList: JankTreeNode[], 1632 scrollCallback: ((d: unknown) => void) | undefined, 1633 callback: ((data: Array<unknown>) => void) | undefined 1634 ): void { 1635 list.push({ 1636 name: 'FrameTimeLine flows', 1637 value: '', 1638 }); 1639 list.push({ 1640 name: 'Slice', 1641 value: 1642 data.cmdline + 1643 ' [' + 1644 data.name + 1645 ']' + 1646 `<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>`, 1647 }); 1648 let timeLineNode = new JankTreeNode(data.name!, data.pid!, 'frameTime'); 1649 jankJumperList.push(timeLineNode); 1650 if (data.dst_slice) { 1651 queryPrecedingData(data.dst_slice).then((it) => { 1652 if (it.length > 0) { 1653 list.push({ 1654 name: 'Preceding flows', 1655 value: '', 1656 }); 1657 it.forEach((a: unknown) => { 1658 // @ts-ignore 1659 let rsNode = new JankTreeNode(a.name, a.pid, 'render_service'); 1660 jankJumperList.push(rsNode); 1661 list.push({ 1662 name: 'Slice', 1663 value: 1664 // @ts-ignore 1665 a.cmdline + 1666 ' [' + 1667 // @ts-ignore 1668 a.name + 1669 ']' + 1670 // @ts-ignore 1671 `<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>`, 1672 }); 1673 }); 1674 this.currentSelectionTbl!.dataSource = list; 1675 this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList); 1676 } 1677 }); 1678 } else { 1679 this.currentSelectionTbl!.dataSource = list; 1680 this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList); 1681 } 1682 } 1683 1684 private handleRenderServiceJank( 1685 data: JankStruct, 1686 list: unknown[], 1687 jankJumperList: JankTreeNode[], 1688 scrollCallback: ((d: unknown) => void) | undefined, 1689 callback: ((data: Array<unknown>) => void) | undefined 1690 ): void { 1691 if (data.src_slice) { 1692 queryFlowsData(data.src_slice!.split(',')).then((it) => { 1693 if (it.length > 0) { 1694 list.push({ 1695 name: 'FrameTimeLine flows', 1696 value: '', 1697 }); 1698 it.forEach((a: unknown) => { 1699 // @ts-ignore 1700 let appNode = new JankTreeNode(a.name, a.pid, 'app'); 1701 // @ts-ignore 1702 appNode.children.push(new JankTreeNode(a.name, a.pid, 'frameTime')); 1703 jankJumperList.push(appNode); 1704 list.push({ 1705 name: 'Slice', 1706 value: 1707 // @ts-ignore 1708 a.cmdline + 1709 ' [' + 1710 // @ts-ignore 1711 a.name + 1712 ']' + 1713 // @ts-ignore 1714 `<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>`, 1715 }); 1716 }); 1717 list.push({ 1718 name: 'Following flows', 1719 value: '', 1720 }); 1721 it.forEach((a: unknown) => { 1722 list.push({ 1723 name: 'Slice', 1724 value: 1725 // @ts-ignore 1726 a.cmdline + 1727 ' [' + 1728 // @ts-ignore 1729 a.name + 1730 ']' + 1731 // @ts-ignore 1732 `<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>`, 1733 }); 1734 }); 1735 this.currentSelectionTbl!.dataSource = list; 1736 this.addJankScrollCallBackEvent(scrollCallback, callback, jankJumperList); 1737 } 1738 }); 1739 } else { 1740 this.currentSelectionTbl!.dataSource = list; 1741 } 1742 } 1743 1744 setAllStartupData(data: AllAppStartupStruct, scrollCallback: Function): void { 1745 this.setTableHeight('550px'); 1746 this.initCanvas(); 1747 let allStartUpLeftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 1748 let allStartUpmiddleTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightText'); 1749 let allStartUpRightButton: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightButton'); 1750 let rightButton: HTMLElement | null | undefined = this?.shadowRoot 1751 ?.querySelector('#rightButton') 1752 ?.shadowRoot?.querySelector('#custom-button'); 1753 let rightStar: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#right-star'); 1754 if (rightButton) { 1755 rightButton!.style.visibility = 'hidden'; 1756 } 1757 if (rightStar) { 1758 rightStar!.style.visibility = 'hidden'; 1759 } 1760 if (allStartUpmiddleTitle) { 1761 allStartUpmiddleTitle.style.visibility = 'hidden'; 1762 } 1763 if (allStartUpRightButton) { 1764 allStartUpRightButton.style.visibility = 'hidden'; 1765 } 1766 if (allStartUpLeftTitle) { 1767 allStartUpLeftTitle.innerText = 'Details'; 1768 } 1769 let list: unknown[] = []; 1770 list.push({ name: 'Name', value: data.stepName! }); 1771 list.push({ 1772 name: 'StartTime(Relative)', 1773 value: getTimeString(data.startTs || 0), 1774 }); 1775 list.push({ 1776 name: 'StartTime(Absolute)', 1777 value: ((data.startTs || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 1778 }); 1779 list.push({ 1780 name: 'EndTime(Relative)', 1781 value: getTimeString((data.startTs || 0) + (data.dur || 0)), 1782 }); 1783 list.push({ 1784 name: 'EndTime(Abslute)', 1785 value: ((data.startTs || 0) + (data.dur || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 1786 }); 1787 list.push({ 1788 name: 'Dur', 1789 value: getTimeString(data.dur || 0), 1790 }); 1791 this.currentSelectionTbl!.dataSource = list; 1792 } 1793 1794 setStartupData(data: AppStartupStruct, scrollCallback: Function, rowData: unknown): void { 1795 this.setTableHeight('550px'); 1796 this.initCanvas(); 1797 this.setStartUpStyle(); 1798 let list: unknown[] = []; 1799 list.push({ name: 'Name', value: AppStartupStruct.getStartupName(data.startName) }); 1800 list.push({ 1801 name: 'StartTime(Relative)', 1802 value: ` 1803 <div style="display: flex;white-space: nowrap;align-items: center"> 1804<div style="white-space:pre-wrap">${getTimeString(data.startTs || 0)}</div> 1805<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="start-jump" name="select" color="#7fa1e7" size="20"></lit-icon> 1806</div>`, 1807 }); 1808 list.push({ 1809 name: 'StartTime(Absolute)', 1810 value: ((data.startTs || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 1811 }); 1812 if (data.dur && data.dur > 0) { 1813 list.push({ 1814 name: 'EndTime(Relative)', 1815 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1816<div style="white-space:pre-wrap">${getTimeString((data.startTs || 0) + (data.dur || 0))}</div> 1817<lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="end-jump" name="select" color="#7fa1e7" size="20"></lit-icon> 1818</div>`, 1819 }); 1820 list.push({ 1821 name: 'EndTime(Absolute)', 1822 value: ((data.startTs || 0) + (data.dur || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 1823 }); 1824 } else { 1825 list.push({ 1826 name: 'EndTime(Relative)', 1827 value: 'Unknown Time', 1828 }); 1829 list.push({ 1830 name: 'EndTime(Absolute)', 1831 value: 'Unknown Time', 1832 }); 1833 } 1834 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 1835 // @ts-ignore 1836 let sortedArray = rowData.slice().sort(function (a: { startTs: number }, b: { startTs: number }) { 1837 return a.startTs - b.startTs; 1838 }); 1839 sortedArray.forEach((item: unknown, index: number) => { 1840 // @ts-ignore 1841 if (item.startName === data.startName) { 1842 list.push({ 1843 name: 'StartSlice', 1844 value: 1845 index === 0 1846 ? 'NULL' 1847 : `${AppStartupStruct.getStartupName(sortedArray[index - 1].startName)} ${getTimeString( 1848 sortedArray[index - 1].startTs + sortedArray[index - 1].dur 1849 )}`, 1850 }); 1851 list.push({ 1852 name: 'EndSlice', 1853 value: 1854 index === sortedArray.length - 1 1855 ? 'NULL' 1856 : `${AppStartupStruct.getStartupName(sortedArray[index + 1].startName)} ${getTimeString( 1857 sortedArray[index + 1].startTs 1858 )}`, 1859 }); 1860 } 1861 }); 1862 this.currentSelectionTbl!.dataSource = list; 1863 this.attachScrollHandlers(data, scrollCallback); 1864 } 1865 1866 private setStartUpStyle(): void { 1867 let rightButton: HTMLElement | null | undefined = this?.shadowRoot 1868 ?.querySelector('#rightButton') 1869 ?.shadowRoot?.querySelector('#custom-button'); 1870 let startUpRightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle'); 1871 if (startUpRightTitle) { 1872 startUpRightTitle.style.visibility = 'hidden'; 1873 rightButton!.style.visibility = 'hidden'; 1874 } 1875 let startUpLeftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 1876 if (startUpLeftTitle) { 1877 startUpLeftTitle.innerText = 'Details'; 1878 } 1879 } 1880 1881 private attachScrollHandlers(data: AppStartupStruct, scrollCallback: Function): void { 1882 let startIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#start-jump'); 1883 let endIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#end-jump'); 1884 let scrollClick = (type: number): void => { 1885 let recordNs: number = Utils.getInstance().getRecordStartNS(); 1886 let useEnd = type === 1 && data.startName! < 6; 1887 queryThreadByItid( 1888 useEnd ? data.endItid! : data.itid!, 1889 useEnd ? recordNs + data.startTs! + data.dur! : recordNs + data.startTs! 1890 ).then((result) => { 1891 if (result.length > 0) { 1892 //@ts-ignore 1893 let pt: { 1894 pid: number; 1895 tid: number; 1896 dur: number; 1897 name: string; 1898 depth: number; 1899 } = result[0]; 1900 scrollCallback({ 1901 pid: pt.pid, 1902 tid: pt.tid, 1903 type: 'func', 1904 dur: pt.dur, 1905 depth: pt.depth, 1906 funName: pt.name, 1907 startTs: useEnd ? (data.startTs || 0) + (data.dur || 0) : data.startTs, 1908 keepOpen: true, 1909 }); 1910 } 1911 }); 1912 }; 1913 if (startIcon) { 1914 startIcon.addEventListener('click', () => scrollClick(0)); 1915 } 1916 if (endIcon) { 1917 endIcon.addEventListener('click', () => scrollClick(1)); 1918 } 1919 } 1920 1921 setStaticInitData(data: SoStruct, scrollCallback: Function): void { 1922 this.setTableHeight('550px'); 1923 this.initCanvas(); 1924 this.setStaticInitStyle(); 1925 let list: unknown[] = []; 1926 list.push({ name: 'Name', value: data.soName }); 1927 list.push({ 1928 name: 'StartTime(Relative)', 1929 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 1930<div style="white-space:pre-wrap">${getTimeString(data.startTs || 0)}</div> 1931<lit-icon id="start-jump" style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" name="select" color="#7fa1e7" size="20"></lit-icon> 1932</div>`, 1933 }); 1934 list.push({ 1935 name: 'StartTime(Absolute)', 1936 value: ((data.startTs || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 1937 }); 1938 list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); 1939 this.currentSelectionTbl!.dataSource = list; 1940 this.startIconClickEvent(data, scrollCallback); 1941 } 1942 1943 private setStaticInitStyle(): void { 1944 let rightTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#rightTitle'); 1945 let rightButton: HTMLElement | null | undefined = this?.shadowRoot 1946 ?.querySelector('#rightButton') 1947 ?.shadowRoot?.querySelector('#custom-button'); 1948 if (rightTitle) { 1949 rightTitle.style.visibility = 'hidden'; 1950 rightButton!.style.visibility = 'hidden'; 1951 } 1952 let leftTitle: HTMLElement | null | undefined = this?.shadowRoot?.querySelector('#leftTitle'); 1953 if (leftTitle) { 1954 leftTitle.innerText = 'Details'; 1955 } 1956 } 1957 1958 private startIconClickEvent(data: SoStruct, scrollCallback: Function): void { 1959 let startIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#start-jump'); 1960 if (startIcon) { 1961 startIcon.addEventListener('click', () => { 1962 let recordNs: number = Utils.getInstance().getRecordStartNS(); 1963 queryThreadByItid(data.itid!, recordNs + data.startTs!).then((result) => { 1964 if (result.length > 0) { 1965 //@ts-ignore 1966 let pt: { 1967 pid: number; 1968 tid: number; 1969 dur: number; 1970 name: string; 1971 depth: number; 1972 } = result[0]; 1973 scrollCallback({ 1974 pid: pt.pid, 1975 tid: pt.tid, 1976 type: 'func', 1977 dur: pt.dur, 1978 depth: pt.depth, 1979 funName: pt.name, 1980 startTs: data.startTs, 1981 keepOpen: true, 1982 }); 1983 } 1984 }); 1985 }); 1986 } 1987 } 1988 1989 async setFrameAnimationData(data: FrameAnimationStruct, scrollCallback: Function): Promise<void> { 1990 this.setTableHeight('550px'); 1991 this.tabCurrentSelectionInit('Animation Details'); 1992 let list = []; 1993 let dataTs: number = data.startTs < 0 ? 0 : data.startTs; 1994 list.push({ name: 'Name', value: data.name }); 1995 list.push({ name: 'Start time(Relative)', value: `${Utils.getTimeString(dataTs)}` }); 1996 list.push({ 1997 name: 'Start time(Absolute)', 1998 value: ((dataTs || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 1999 }); 2000 list.push({ 2001 name: 'End time(Relative)', 2002 value: `${Utils.getTimeString(dataTs + (data.dur || 0))}`, 2003 }); 2004 list.push({ 2005 name: 'End time(Absolute)', 2006 value: (dataTs + (data.dur || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 2007 }); 2008 list.push({ name: 'Duration', value: `${Utils.getTimeString(data.dur || 0)}` }); 2009 if (data.status === 'Completion delay') { 2010 let queryJoinName = `${data.frameInfo?.split(':')[1]}: ${data.name?.split(':')![1]}`; 2011 let frameFpsMessage = data.frameInfo?.split(':'); 2012 await queryFpsSourceList(data.inputTime, data.endTime, queryJoinName).then((result) => { 2013 if (result.length > 0) { 2014 this.isFpsAvailable = true; 2015 this.fpsResult = result; 2016 } else { 2017 this.isFpsAvailable = false; 2018 } 2019 }); 2020 if (frameFpsMessage) { 2021 if (frameFpsMessage[1] !== '0') { 2022 if (this.isFpsAvailable) { 2023 list.push({ 2024 name: 'FPS', 2025 value: `<div style="white-space: nowrap;display: flex;align-items: center"> 2026 <div style="white-space:pre-wrap">${frameFpsMessage[1]}</div> 2027 <lit-icon style="cursor:pointer;transform: scaleX(-1);margin-left: 5px" id="fps-jump" name="select" color="#7fa1e7" size="20"></lit-icon> 2028 </div>`, 2029 }); 2030 } else { 2031 list.push({ name: 'FPS', value: `${frameFpsMessage[1]}` }); 2032 } 2033 } else { 2034 let fixedNumber: number = 2; 2035 let fpsValue: number = Number(frameFpsMessage[0]) / (data.dur / 1000_000_000); 2036 list.push({ name: 'FPS', value: `${fpsValue.toFixed(fixedNumber) || 0}` }); 2037 } 2038 } 2039 } 2040 this.currentSelectionTbl!.dataSource = list; 2041 this.fpsClickEvent(data, scrollCallback); 2042 } 2043 2044 private fpsClickEvent(data: FrameAnimationStruct, scrollCallback: Function): void { 2045 let recordNs: number = Utils.getInstance().getRecordStartNS(); 2046 this.currentSelectionTbl?.shadowRoot?.querySelector('#fps-jump')?.addEventListener('click', () => {// @ts-ignore 2047 let pt: { 2048 pid: number; 2049 tid: number; 2050 name: string; 2051 ts: number; 2052 dur: number; 2053 depth: number; 2054 } = this.fpsResult[0]; 2055 scrollCallback({ 2056 pid: pt.tid, 2057 tid: pt.tid, 2058 dur: pt.dur, 2059 type: 'func', 2060 depth: pt.depth, 2061 funName: pt.name, 2062 startTs: pt.ts - recordNs, 2063 keepOpen: true, 2064 }); 2065 }); 2066 } 2067 2068 private setJankType(data: JankStruct, list: unknown[]): void { 2069 if (data.jank_tag === 1) { 2070 if (data.frameType === 'render_service') { 2071 list.push({ name: 'Jank Type', value: 'RenderService Deadline Missed' }); 2072 } else if (data.frameType === 'app') { 2073 list.push({ name: 'Jank Type', value: 'APP Deadline Missed' }); 2074 } else if (data.frameType === 'frameTime') { 2075 list.push({ name: 'Jank Type', value: 'Deadline Missed' }); 2076 } 2077 } else if (data.jank_tag === 3) { 2078 list.push({ name: 'Jank Type', value: 'Deadline Missed' }); 2079 } else { 2080 list.push({ name: 'Jank Type', value: 'NONE' }); 2081 } 2082 } 2083 2084 private setJankCommonMessage(list: unknown[], data: JankStruct): void { 2085 list.push({ name: 'Name', value: data.name }); 2086 list.push({ name: 'StartTime(Relative)', value: getTimeString(data.ts || 0) }); 2087 list.push({ 2088 name: 'StartTime(Absolute)', 2089 value: ((data.ts || 0) + Utils.getInstance().getRecordStartNS()) / 1000000000 + 's', 2090 }); 2091 list.push({ name: 'Duration', value: data.dur ? getTimeString(data.dur) : ' ' }); 2092 if (data.frameType !== 'frameTime') { 2093 list.push({ name: 'Process', value: data.cmdline + ' ' + data.pid }); 2094 } 2095 } 2096 2097 private setTableHeight(height: string): void { 2098 this.scrollView!.scrollTop = 0; 2099 this.currentSelectionTbl!.style.height = height; 2100 this.wakeupListTbl!.style.display = 'none'; 2101 } 2102 2103 private addJankScrollCallBackEvent( 2104 scrollCallback: ((d: unknown) => void) | undefined, 2105 callback: ((data: Array<unknown>) => void) | undefined, 2106 jankJumperList: JankTreeNode[] 2107 ): void { 2108 let all = this.currentSelectionTbl?.shadowRoot?.querySelectorAll('.jank_cla'); 2109 all!.forEach((a) => { 2110 a.addEventListener('click', () => { 2111 if (scrollCallback) { 2112 scrollCallback({ 2113 rowId: a.id, 2114 name: a.getAttribute('slice_name'), 2115 pid: a.getAttribute('pid'), 2116 }); 2117 } 2118 }); 2119 }); 2120 if (callback) { 2121 callback(jankJumperList); 2122 } 2123 } 2124 2125 async queryThreadStateDArgs(argSetID: number | undefined): Promise<BinderArgBean[]> { 2126 let list: Array<BinderArgBean> = []; 2127 if (argSetID !== undefined && argSetID > 0) { 2128 list = await queryThreadStateArgs(argSetID); 2129 } 2130 return list; 2131 } 2132 2133 /** 2134 * 查询出 线程被唤醒的 线程信息 2135 * @param data 2136 */ 2137 async queryCPUWakeUpFromData(data: CpuStruct): Promise<WakeupBean | null> { 2138 let wb: WakeupBean | null = null; 2139 if (data.id === undefined || data.startTime === undefined) { 2140 return null; 2141 } 2142 let wakeup = await queryRunnableTimeByRunning(data.tid!, data.startTime); 2143 if (wakeup && wakeup[0]) { 2144 let wakeupTs = wakeup[0].ts as number; 2145 let recordStartTs = Utils.getInstance().getRecordStartNS(); 2146 let wf = await queryThreadWakeUpFrom(data.id, wakeupTs); 2147 // @ts-ignore 2148 if (wf && wf[0]) { 2149 // @ts-ignore 2150 wb = wf[0]; 2151 if (wb !== null) { 2152 wb.wakeupTime = wakeupTs - recordStartTs; 2153 wb.process = Utils.getInstance().getProcessMap().get(wb.pid!) || 'Process'; 2154 wb.thread = Utils.getInstance().getThreadMap().get(wb.tid!) || 'Thread'; 2155 wb.schedulingLatency = (data.startTime || 0) - (wb.wakeupTime || 0); 2156 wb.schedulingDesc = INPUT_WORD; 2157 } 2158 } 2159 } 2160 return wb; 2161 } 2162 2163 /** 2164 * 查询出 线程被唤醒的 线程链信息 2165 * @param data 2166 */ 2167 static async queryCPUWakeUpListFromBean(data: WakeupBean): Promise<WakeupBean | null> { 2168 let wb: WakeupBean | null = null; 2169 let wakeup = await queryRunnableTimeByRunning(data.tid!, data.ts!); 2170 if (wakeup && wakeup[0]) { 2171 let wakeupTs = wakeup[0].ts as number; 2172 let recordStartTs = Utils.getInstance().getRecordStartNS(); 2173 let wf = await queryThreadWakeUpFrom(data.itid!, wakeupTs); 2174 // @ts-ignore 2175 if (wf && wf[0]) { 2176 // @ts-ignore 2177 wb = wf[0]; 2178 if (wb !== null) { 2179 wb.wakeupTime = wakeupTs - recordStartTs; 2180 wb.process = Utils.getInstance().getProcessMap().get(wb.pid!) || 'Process'; 2181 wb.thread = Utils.getInstance().getThreadMap().get(wb.tid!) || 'Thread'; 2182 wb.schedulingLatency = (data.ts || 0) - (wb.wakeupTime || 0); 2183 wb.schedulingDesc = INPUT_WORD; 2184 } 2185 } 2186 } 2187 return wb; 2188 } 2189 2190 /** 2191 * 查询出 线程唤醒了哪些线程信息 2192 */ 2193 async queryThreadWakeUpFromData(itid: number, startTime: number, dur: number): Promise<WakeupBean | undefined> { 2194 let wakeUps = await queryThreadWakeUpFrom(itid, startTime + Utils.getInstance().getRecordStartNS()); 2195 let item; 2196 // @ts-ignore 2197 if (wakeUps !== undefined && wakeUps.length > 0) { 2198 // @ts-ignore 2199 item = wakeUps[0]; 2200 } 2201 return item; 2202 } 2203 2204 /** 2205 * 查询出 线程唤醒了哪些线程信息 2206 */ 2207 async queryThreadWakeUpData(itid: number, startTime: number, dur: number): Promise<Array<WakeupBean>> { 2208 let list: Array<WakeupBean> = []; 2209 if (itid === undefined || startTime === undefined) { 2210 return list; 2211 } 2212 let wakeUps = await queryThreadWakeUp(itid, startTime, dur); // 3,4835380000 2213 if (wakeUps !== undefined && wakeUps.length > 0) { 2214 list.push(...wakeUps); 2215 } 2216 return list; 2217 } 2218 //递归查找R唤醒链 2219 getRWakeUpChain(data: WakeupBean | undefined): void { 2220 this.getRWakeUpChainData(data).then((wakeupFrom: unknown) => { 2221 if (wakeupFrom === null) {//当查不到数据时,处理容器状态与样式,展示内容 2222 let wakeupTopContent = this.currentSelectionTbl?.shadowRoot?.getElementById('wakeup-top-content'); 2223 let wakeupTopIcon = this.currentSelectionTbl?.shadowRoot?.querySelector<HTMLDivElement>('#wakeup-top'); 2224 wakeupTopContent!.innerText = 'idle' + this.topChainStr;//处理链顶部 2225 wakeupTopIcon!.style.display = 'none'; 2226 wakeupTopContent!.style.display = 'block'; 2227 wakeupTopContent!.style.maxHeight = '100px';//设置最大高度,超出出现滚动条 2228 wakeupTopContent!.style.overflow = 'auto'; 2229 return; 2230 } 2231 //@ts-ignore 2232 this.topChainStr = `-->${wakeupFrom!.thread} [${wakeupFrom!.tid}]` + this.topChainStr;//链的拼接 2233 // @ts-ignore 2234 this.getRWakeUpChain(wakeupFrom); 2235 }); 2236 } 2237 2238 /** 2239 * 获取 R的唤醒链 2240 * @param data 2241 */ 2242 async getRWakeUpChainData(data: unknown): Promise<WakeupBean | null> { 2243 let wakeupFrom: WakeupBean | null = null; 2244 //@ts-ignore 2245 let wakeup = await queryRunnableTimeByRunning(data.tid!, data.ts!);//通过链上的Running块,查找前一条R信息 2246 if (wakeup && wakeup[0]) { 2247 let wakeupTs = wakeup[0].ts as number; 2248 //@ts-ignore 2249 let wf = await queryRWakeUpFrom(data.itid!, wakeupTs);//查找到的前一条R信息,对应的唤醒信息 2250 //@ts-ignore 2251 if (wf && wf[0]) { 2252 //@ts-ignore 2253 wakeupFrom = wf[0]; 2254 //@ts-ignore 2255 wakeupFrom.thread = Utils.getInstance().getThreadMap().get(wakeupFrom.tid!) || 'Thread'; 2256 } 2257 } 2258 return wakeupFrom; 2259 } 2260 2261 initCanvas(): HTMLCanvasElement | null { 2262 let canvas = this.shadowRoot!.querySelector<HTMLCanvasElement>('#rightDraw'); 2263 let width = getComputedStyle(this.currentSelectionTbl!).getPropertyValue('width'); 2264 if (canvas !== null) { 2265 canvas.width = Math.round(Number(width.replace('px', '')) * this.dpr); 2266 canvas.height = Math.round(Number(200 * this.dpr)); 2267 canvas.style.width = width; 2268 canvas.style.height = '200px'; 2269 canvas.getContext('2d')!.scale(this.dpr, this.dpr); 2270 } 2271 SpApplication.skinChange = (val: boolean): void => { 2272 this.drawRight(canvas, this.weakUpBean!); 2273 }; 2274 return canvas; 2275 } 2276 2277 drawRight(cavs: HTMLCanvasElement | null, wakeupBean: WakeupBean | null): void { 2278 if (cavs === null) { 2279 return; 2280 } 2281 let context = cavs.getContext('2d'); 2282 if (context !== null) { 2283 //绘制竖线 2284 this.drawVerticalLine(context); 2285 //绘制菱形 2286 context.lineWidth = 1; 2287 context.beginPath(); 2288 context.moveTo(10, 30); 2289 context.lineTo(4, 40); 2290 context.lineTo(10, 50); 2291 context.lineTo(16, 40); 2292 context.lineTo(10, 30); 2293 context.closePath(); 2294 context.fill(); 2295 context.font = 12 + 'px sans-serif'; 2296 //绘制wake up 文字 2297 let strList = []; 2298 strList.push('wakeup @ ' + getTimeString(wakeupBean?.wakeupTime || 0) + ' on CPU ' + wakeupBean?.cpu + ' by'); 2299 strList.push('P:' + wakeupBean?.process + ' [ ' + wakeupBean?.pid + ' ]'); 2300 strList.push('T:' + wakeupBean?.thread + ' [ ' + wakeupBean?.tid + ' ]'); 2301 strList.forEach((str, index) => { 2302 if (context !== null) { 2303 context.fillText(str, 40, 40 + 16 * index); 2304 } 2305 }); 2306 context.lineWidth = 2; 2307 context.lineJoin = 'bevel'; 2308 context.moveTo(10, 95); 2309 context.lineTo(20, 90); 2310 context.moveTo(10, 95); 2311 context.lineTo(20, 100); 2312 context.moveTo(10, 95); 2313 context.lineTo(80, 95); 2314 context.lineTo(70, 90); 2315 context.moveTo(80, 95); 2316 context.lineTo(70, 100); 2317 context.stroke(); 2318 //绘制latency 2319 context.font = 12 + 'px sans-serif'; 2320 context.fillText('Scheduling latency:' + getTimeString(wakeupBean?.schedulingLatency || 0), 90, 100); 2321 //绘制最下方提示语句 2322 context.font = 10 + 'px sans-serif'; 2323 INPUT_WORD.split('\n').forEach((str, index) => { 2324 context?.fillText(str, 90, 120 + 12 * index); 2325 }); 2326 } 2327 } 2328 2329 private drawVerticalLine(context: CanvasRenderingContext2D): void { 2330 if (document.querySelector<SpApplication>('sp-application')!.dark) { 2331 context.strokeStyle = '#ffffff'; 2332 context.fillStyle = '#ffffff'; 2333 } else { 2334 context.strokeStyle = '#000000'; 2335 context.fillStyle = '#000000'; 2336 } 2337 context.lineWidth = 2; 2338 context.moveTo(10, 15); 2339 context.lineTo(10, 125); 2340 context.stroke(); 2341 } 2342 2343 transferString(str: string): string { 2344 let s = ''; 2345 if (str.length === 0) { 2346 return ''; 2347 } 2348 s = str.replace(/&/g, '&'); 2349 s = s.replace(/</g, '<'); 2350 s = s.replace(/>/g, '>'); 2351 s = s.replace(/\'/g, '''); 2352 s = s.replace(/\"/g, '&#quat;'); 2353 return s; 2354 } 2355 2356 initElements(): void { 2357 this.currentSelectionTbl = this.shadowRoot?.querySelector<LitTable>('#selectionTbl'); 2358 this.wakeupListTbl = this.shadowRoot?.querySelector<LitTable>('#wakeupListTbl'); 2359 this.scrollView = this.shadowRoot?.querySelector<HTMLDivElement>('#scroll_view'); 2360 this.currentSelectionTbl?.addEventListener('column-click', (ev: unknown): void => { }); //@ts-ignore 2361 window.subscribe(window.SmartEvent.UI.WakeupList, (data: Array<WakeupBean>) => this.showWakeupListTableData(data)); 2362 } 2363 2364 showWakeupListTableData(data: Array<WakeupBean>): void { 2365 this.wakeupListTbl!.style.display = 'flex'; 2366 let cpus: number[] = []; 2367 let itids: number[] = []; 2368 let ts: number[] = []; 2369 let maxPriority = 0; 2370 let maxPriorityDuration = 0; 2371 let maxDuration = 0; 2372 data.forEach((it) => { 2373 cpus.push(it.cpu!); 2374 itids.push(it.itid!); 2375 ts.push(it.ts!); 2376 }); 2377 queryWakeupListPriority(itids, ts, cpus).then((res) => { 2378 let resource = data.map((it) => { 2379 let wake = { 2380 process: `${it.process}(${it.pid})`, 2381 thread: `${it.thread}(${it.tid})`, 2382 cpu: it.cpu, 2383 dur: it.dur, 2384 priority: 0, 2385 isSelected: false, 2386 }; 2387 //@ts-ignore 2388 let find = res.find((re) => re.cpu === it.cpu && re.itid === it.itid && re.ts === it.ts); 2389 if (find) { 2390 //@ts-ignore 2391 wake.priority = find.priority; 2392 } 2393 maxDuration = Math.max(maxDuration, it.dur!); 2394 maxPriority = Math.max(maxPriority, wake.priority); 2395 return wake; 2396 }); 2397 if (this.selectWakeupBean) { 2398 // 点击第一层唤醒树时向数组头部添加当前点击信息 2399 if (data[0].schedulingLatency) { 2400 // @ts-ignore 2401 resource.unshift(this.selectWakeupBean); 2402 } 2403 // @ts-ignore 2404 maxDuration = Math.max(maxDuration, this.selectWakeupBean.dur); 2405 // @ts-ignore 2406 maxPriority = Math.max(maxPriority, this.selectWakeupBean.priority); 2407 } 2408 resource.forEach((it) => { 2409 if (it.priority === maxPriority) { 2410 maxPriorityDuration = Math.max(it.dur || 0, maxPriorityDuration); 2411 } 2412 }); 2413 this.updateTableSettings(maxPriority, maxPriorityDuration, maxDuration); 2414 this.wakeupListTbl!.recycleDataSource = resource; 2415 }); 2416 } 2417 2418 private updateTableSettings(maxPriority: number, maxPriorityDuration: number, maxDuration: number): void { 2419 this.wakeupListTbl!.getItemTextColor = (data: unknown): string => { 2420 // @ts-ignore 2421 if ((data.priority === maxPriority && data.dur === maxPriorityDuration) || data.dur === maxDuration) { 2422 return '#f44336'; 2423 } else { 2424 return '#262626'; 2425 } 2426 }; 2427 } 2428 2429 addTableObserver(): void { 2430 let leftTable = this.shadowRoot?.querySelector('.table-left'); 2431 this.tableObserver?.observe(leftTable!, { 2432 attributes: true, 2433 attributeFilter: ['style'], 2434 attributeOldValue: true, 2435 }); 2436 } 2437 2438 initHtml(): string { 2439 return TabPaneCurrentSelectionHtml; 2440 } 2441} 2442 2443export class JankTreeNode { 2444 name: string = ''; 2445 pid: number = -1; 2446 frameType: string = ''; 2447 type: number = 0; 2448 2449 constructor(name: string, pid: number, frameType: string) { 2450 this.name = name; 2451 this.pid = pid; 2452 this.frameType = frameType; 2453 } 2454 2455 children: Array<JankTreeNode> = []; 2456} 2457 2458export class ThreadTreeNode { 2459 tid: number = 0; 2460 pid: number = -1; 2461 startTime: number = 1; 2462 depth: number = 0; 2463 2464 constructor(tid: number, pid: number, startTime: number, depth: number = 0) { 2465 this.tid = tid; 2466 this.pid = pid; 2467 this.startTime = startTime; 2468 this.depth = depth; 2469 } 2470} 2471 2472class FunDetail { 2473 slice: string = ''; 2474 CN: string = ''; 2475 EN: string = ''; 2476} 2477 2478class SortData { 2479 value: string = ''; 2480 dur: number = 0; 2481 bytes: number = 0; 2482} 2483