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