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