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 { LitTable } from '../../../../../base-ui/table/lit-table'; 18import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; 19import { log } from '../../../../../log/Log'; 20import { getProbablyTime } from '../../../../database/logic-worker/ProcedureLogicWorkerCommon'; 21import { Utils } from '../../base/Utils'; 22import { resizeObserver } from '../SheetUtils'; 23import { getTabCpuByThread } from '../../../../database/sql/Cpu.sql'; 24import { getCpuData, getIrqAndSoftIrqData } from "../../../../database/sql/CpuAndIrq.sql"; 25import { byCpuGroupBean, CpuAndIrqBean, softirqAndIrq, finalResultBean } from "./CpuAndIrqBean"; 26import { FlagsConfig } from '../../../SpFlags'; 27 28@element('tabpane-cpu-thread') 29export class TabPaneCpuByThread extends BaseElement { 30 private cpuByThreadTbl: LitTable | null | undefined; 31 private range: HTMLLabelElement | null | undefined; 32 private cpuByThreadSource: Array<SelectionData> = []; 33 private currentSelectionParam: SelectionParam | undefined; 34 private cpuByIrqSource: Array<finalResultBean> = []; 35 private loadIrq: boolean = false;//flag开关 36 private pubColumns = ` 37 <lit-table-column order width="250px" title="Process" data-index="process" key="process" align="flex-start" order > 38 </lit-table-column> 39 <lit-table-column order width="120px" title="PID" data-index="pid" key="pid" align="flex-start" order > 40 </lit-table-column> 41 <lit-table-column order width="250px" title="Thread" data-index="thread" key="thread" align="flex-start" order > 42 </lit-table-column> 43 <lit-table-column order width="120px" title="TID" data-index="tid" key="tid" align="flex-start" order > 44 </lit-table-column> 45 <lit-table-column order width="200px" title="Wall duration(ms)" data-index="wallDuration" key="wallDuration" align="flex-start" order > 46 </lit-table-column> 47 <lit-table-column order width="200px" title="Avg Wall duration(ms)" data-index="avgDuration" key="avgDuration" align="flex-start" order > 48 </lit-table-column> 49 <lit-table-column order width="120px" title="Occurrences" data-index="occurrences" key="occurrences" align="flex-start" order > 50 </lit-table-column> 51 `; 52 53 set data(cpuByThreadValue: SelectionParam | unknown) { 54 if (this.currentSelectionParam === cpuByThreadValue) { 55 return; 56 } 57 // @ts-ignore 58 this.currentSelectionParam = cpuByThreadValue; 59 // @ts-ignore 60 this.cpuByThreadTbl!.innerHTML = this.getTableColumns(cpuByThreadValue.cpus); 61 this.cpuByThreadTbl!.injectColumns(); 62 this.range!.textContent = 63 // @ts-ignore 64 `Selected range: ${parseFloat(((cpuByThreadValue.rightNs - cpuByThreadValue.leftNs) / 1000000.0).toFixed(5))} ms`; 65 this.cpuByThreadTbl!.loading = true; 66 this.loadIrq = FlagsConfig.getFlagsConfigEnableStatus('CPU by Irq');//flag开关 67 this.handleAsyncRequest(cpuByThreadValue, this.loadIrq); 68 } 69 70 private handleAsyncRequest(cpuByThreadValue: unknown, loadIrq: boolean): void { 71 if (loadIrq) { 72 //查询cpu数据和irq数据 73 Promise.all([ 74 // @ts-ignore 75 getCpuData(cpuByThreadValue.cpus, cpuByThreadValue.leftNs, cpuByThreadValue.rightNs), 76 // @ts-ignore 77 getIrqAndSoftIrqData(cpuByThreadValue.cpus, cpuByThreadValue.leftNs, cpuByThreadValue.rightNs) 78 ]).then(([cpuData, interruptData]) => { 79 this.cpuByThreadTbl!.loading = false; 80 const resArr = cpuData.concat(interruptData); 81 if (resArr != null && resArr.length > 0) { 82 const cutData: finalResultBean[] = this.groupByCpu(resArr);//切割后数据 83 this.aggregateData(cutData, cpuByThreadValue);//整合数据 84 } else { 85 this.cpuByIrqSource = []; 86 this.cpuByThreadTbl!.recycleDataSource = this.cpuByIrqSource; 87 } 88 }); 89 } else { 90 // @ts-ignore 91 getTabCpuByThread(cpuByThreadValue.cpus, cpuByThreadValue.leftNs, // @ts-ignore 92 cpuByThreadValue.rightNs, cpuByThreadValue.traceId).then((result): void => { 93 this.cpuByThreadTbl!.loading = false; 94 if (result !== null && result.length > 0) { 95 log(`getTabCpuByThread size :${result.length}`); 96 this.processResult(result, cpuByThreadValue); 97 } else { 98 this.cpuByThreadSource = []; 99 this.cpuByThreadTbl!.recycleDataSource = this.cpuByThreadSource; 100 } 101 }); 102 } 103 104 } 105 106 //将所有数据按cpu重新分组 107 private groupByCpu(data: Array<CpuAndIrqBean>): finalResultBean[] { 108 const cpuObject: { [cpu: number]: byCpuGroupBean } = 109 data.reduce((groups, item) => { 110 const { cpu, ...restProps } = item; 111 const newCpuAndIrqBean: CpuAndIrqBean = { cpu, ...restProps }; 112 113 if (!groups[cpu]) { 114 groups[cpu] = { CPU: [] }; 115 } 116 groups[cpu].CPU!.push(newCpuAndIrqBean); 117 118 return groups; 119 }, {} as { [cpu: number]: byCpuGroupBean }) 120 const cutObj: { [cpu: number]: finalResultBean[] } = {}; 121 Object.entries(cpuObject).forEach(([cpuStr, { CPU }]) => { 122 const cpu = Number(cpuStr); 123 cutObj[cpu] = this.cpuByIrq(CPU); 124 }) 125 const cutList: finalResultBean[] = Object.values(cutObj).flat(); 126 return cutList; 127 } 128 129 //具体切割方法 130 private cpuByIrq(data: CpuAndIrqBean[]): finalResultBean[] { 131 let sourceData = data.sort((a, b) => a.startTime - b.startTime); 132 let waitArr: CpuAndIrqBean[] = []; 133 let completedArr: finalResultBean[] = []; 134 let globalTs: number = 0; 135 let index: number = 0; 136 while (index < sourceData.length || waitArr.length > 0) { 137 let minEndTs = Math.min(...waitArr.map((item: CpuAndIrqBean) => item.endTime)); 138 let minIndex = waitArr.findIndex((item: CpuAndIrqBean) => item.endTime === minEndTs); 139 //当waitArr为空时 140 if (waitArr.length === 0) { 141 globalTs = sourceData[index].startTime; 142 waitArr.push(sourceData[index]); 143 index++; 144 continue; 145 } 146 //当全局Ts等于minEndTs时,只做删除处理 147 if (globalTs === minEndTs) { 148 if (minIndex !== -1) { 149 const item = waitArr[minIndex]; 150 if (item.endTime > item.startTime) { 151 waitArr.splice(minIndex, 1); 152 } else { 153 // wallDuration为0,需要特别处理 154 const obj: finalResultBean = { 155 cat: item.cat, 156 cpu: item.cpu, 157 dur: 0, 158 pid: item.pid ? item.pid : '[NULL]', 159 tid: item.tid ? item.tid : '[NULL]', 160 occurrences: item.isFirstObject === 1 ? 1 : 0 161 }; 162 completedArr.push(obj); 163 waitArr.splice(minIndex, 1); 164 } 165 continue; 166 } 167 } 168 let obj: finalResultBean = { 169 cat: '', 170 dur: 0, 171 cpu: 0, 172 pid: 0, 173 tid: 0, 174 occurrences: 0 175 }; 176 if (index < sourceData.length) { 177 if (sourceData[index].startTime < minEndTs) { 178 if (globalTs === sourceData[index].startTime) { 179 waitArr.push(sourceData[index]); 180 index++; 181 continue; 182 } else { 183 const maxPriorityItem = this.findMaxPriority(waitArr); 184 obj = { 185 cat: maxPriorityItem.cat, 186 dur: sourceData[index].startTime - globalTs, 187 cpu: maxPriorityItem.cpu, 188 pid: maxPriorityItem.pid ? maxPriorityItem.pid : '[NULL]', 189 tid: maxPriorityItem.tid ? maxPriorityItem.tid : '[NULL]', 190 occurrences: maxPriorityItem.isFirstObject === 1 ? 1 : 0 191 } 192 completedArr.push(obj); 193 maxPriorityItem.isFirstObject = 0; 194 waitArr.push(sourceData[index]); 195 globalTs = sourceData[index].startTime; 196 index++; 197 } 198 } else { 199 const maxPriorityItem = this.findMaxPriority(waitArr); 200 obj = { 201 cat: maxPriorityItem.cat, 202 dur: minEndTs - globalTs, 203 cpu: maxPriorityItem.cpu, 204 pid: maxPriorityItem.pid ? maxPriorityItem.pid : '[NULL]', 205 tid: maxPriorityItem.tid ? maxPriorityItem.tid : '[NULL]', 206 occurrences: maxPriorityItem.isFirstObject === 1 ? 1 : 0 207 } 208 completedArr.push(obj); 209 maxPriorityItem.isFirstObject = 0; 210 globalTs = minEndTs; 211 if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; 212 } 213 } else { 214 const maxPriorityItem = this.findMaxPriority(waitArr); 215 obj = { 216 cat: maxPriorityItem.cat, 217 dur: minEndTs - globalTs, 218 cpu: maxPriorityItem.cpu, 219 pid: maxPriorityItem.pid ? maxPriorityItem.pid : '[NULL]', 220 tid: maxPriorityItem.tid ? maxPriorityItem.tid : '[NULL]', 221 occurrences: maxPriorityItem.isFirstObject === 1 ? 1 : 0 222 } 223 completedArr.push(obj); 224 maxPriorityItem.isFirstObject = 0; 225 globalTs = minEndTs; 226 if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; 227 } 228 } 229 return completedArr; 230 } 231 232 private findMaxPriority(arr: CpuAndIrqBean[]): CpuAndIrqBean { 233 return arr.reduce((maxItem: CpuAndIrqBean, currentItem: CpuAndIrqBean) => { 234 return maxItem.priority > currentItem.priority ? maxItem : currentItem; 235 }, arr[0]); 236 } 237 238 // 聚合数据 239 private aggregateData(data: any[], cpuByThreadValue: SelectionParam | any): void { 240 const cpuAggregations: { [tidPidKey: string]: softirqAndIrq } = {}; 241 //@ts-ignore 242 let softirqAggregations: softirqAndIrq = { 243 occurrences: 0, 244 wallDuration: 0, 245 avgDuration: 0, 246 cpus: {} 247 }; 248 //@ts-ignore 249 let irqAggregations: softirqAndIrq = { 250 occurrences: 0, 251 wallDuration: 0, 252 avgDuration: 0, 253 cpus: {} 254 }; 255 data.forEach((obj) => { 256 // 聚合 cpu 数据 257 if (obj.cat === 'cpu') { 258 const tidPidKey = `${obj.tid}-${obj.pid}`; 259 const cpuDurationKey = `cpu${obj.cpu}`; 260 const cpuPercentKey = `cpu${obj.cpu}Ratio`; 261 262 if (!cpuAggregations[tidPidKey]) { 263 cpuAggregations[tidPidKey] = { 264 tid: obj.tid, 265 pid: obj.pid, 266 wallDuration: 0, 267 occurrences: 0, 268 avgDuration: 0, 269 cpus: {}, 270 [cpuDurationKey]: 0, 271 [cpuPercentKey]: 100, 272 }; 273 } 274 275 cpuAggregations[tidPidKey].wallDuration += obj.dur; 276 cpuAggregations[tidPidKey].occurrences += obj.occurrences; 277 cpuAggregations[tidPidKey].avgDuration = cpuAggregations[tidPidKey].wallDuration / cpuAggregations[tidPidKey].occurrences; 278 cpuAggregations[tidPidKey].cpus[obj.cpu] = (cpuAggregations[tidPidKey].cpus[obj.cpu] || 0) + obj.dur; 279 cpuAggregations[tidPidKey][cpuDurationKey] = cpuAggregations[tidPidKey].cpus[obj.cpu]; 280 cpuAggregations[tidPidKey][cpuPercentKey] = (cpuAggregations[tidPidKey][cpuDurationKey] / (cpuByThreadValue.rightNs - cpuByThreadValue.leftNs)) * 100; 281 } 282 283 // 聚合 softirq 数据 284 if (obj.cat === 'softirq') { 285 this.updateIrqAndSoftirq(softirqAggregations, obj, cpuByThreadValue); 286 } 287 // 聚合 irq 数据 288 if (obj.cat === 'irq') { 289 this.updateIrqAndSoftirq(irqAggregations, obj, cpuByThreadValue); 290 } 291 292 }); 293 294 // 将聚合数据转换为最终结果格式 295 const result: Array<{ [key: string]: any }> = []; 296 297 // 添加 CPU 数据 298 for (const tidPidKey in cpuAggregations) { 299 const aggregation = cpuAggregations[tidPidKey]; 300 const { tid, pid, occurrences, wallDuration, avgDuration, ...cpuDurations } = aggregation; 301 result.push({ tid, pid, occurrences, wallDuration, avgDuration, ...cpuDurations }); 302 } 303 304 // 添加softirq 305 if (softirqAggregations.wallDuration >= 0) { 306 result.push({ process: 'softirq', thread: 'softirq', tid: '[NULL]', pid: '[NULL]', ...softirqAggregations, }); 307 } 308 309 // 添加 irq 数据 310 if (irqAggregations.wallDuration >= 0) { 311 result.push({ process: 'irq', thread: 'irq', tid: '[NULL]', pid: '[NULL]', ...irqAggregations }); 312 } 313 this.handleFunction(result, cpuByThreadValue); 314 315 } 316 317 //irq和softirq聚合方法 318 private updateIrqAndSoftirq(aggregation: softirqAndIrq, obj: CpuAndIrqBean, cpuByThreadValue: SelectionParam): void { 319 const callid = obj.cpu; 320 const callidDurKey = `cpu${callid}`; 321 const callidPercentKey = `cpu${callid}Ratio`; 322 323 aggregation.wallDuration += obj.dur; 324 aggregation.occurrences += obj.occurrences; 325 aggregation.avgDuration = aggregation.wallDuration / aggregation.occurrences; 326 327 if (!aggregation.cpus[callid]) { 328 aggregation.cpus[callid] = 0; 329 } 330 aggregation.cpus[callid] += obj.dur; 331 332 if (!(callidDurKey in aggregation)) { 333 aggregation[callidDurKey] = 0; 334 } 335 aggregation[callidDurKey] += obj.dur; 336 337 aggregation[callidPercentKey] = (aggregation[callidDurKey] / (cpuByThreadValue.rightNs - cpuByThreadValue.leftNs)) * 100; 338 } 339 340 //最后将所有数据进行统一整理 341 private handleFunction(data: Array<{ [key: string]: any }>, cpuByThreadValue: SelectionParam): void { 342 let index = 0; 343 let totalWallDuration = 0; 344 let totalOccurrences = 0; 345 const finalData: Array<finalResultBean> = []; 346 while (index < data.length) { 347 const obj = data[index]; 348 totalWallDuration += obj.wallDuration; 349 totalOccurrences += obj.occurrences; 350 if (obj.tid !== '[NULL]' && obj.pid !== '[NULL]') { 351 // @ts-ignore 352 let process = Utils.getInstance().getProcessMap(cpuByThreadValue.traceId).get(obj.pid); 353 // @ts-ignore 354 let thread = Utils.getInstance().getThreadMap(cpuByThreadValue.traceId).get(obj.tid); 355 obj.thread = thread == null || thread.length === 0 ? '[NULL]' : thread; 356 obj.process = process == null || process.length === 0 ? '[NULL]' : process; 357 } 358 obj.wallDuration /= 1000000; 359 obj.wallDuration = obj.wallDuration.toFixed(6); 360 obj.avgDuration /= 1000000; 361 obj.avgDuration = obj.avgDuration.toFixed(6); 362 for (const cpu in obj.cpus) { 363 if (obj.cpus.hasOwnProperty(cpu)) { 364 obj[`cpu${cpu}TimeStr`] = getProbablyTime(obj[`cpu${cpu}`]); 365 obj[`cpu${cpu}Ratio`] = obj[`cpu${cpu}Ratio`].toFixed(2); 366 } 367 } 368 for (const cpuNumber of cpuByThreadValue.cpus) { 369 const cpuKey = `cpu${cpuNumber}TimeStr`; 370 const percentKey = `cpu${cpuNumber}Ratio`; 371 if (!obj.hasOwnProperty(cpuKey)) { 372 obj[cpuKey] = "0.00"; 373 obj[percentKey] = "0.00"; 374 } 375 } 376 delete obj.cpus; 377 finalData.push(obj); 378 index++; 379 } 380 finalData.unshift({ 381 wallDuration: (totalWallDuration / 1000000).toFixed(6), 382 occurrences: totalOccurrences, 383 }); 384 this.cpuByIrqSource = finalData; 385 this.cpuByThreadTbl!.recycleDataSource = this.cpuByIrqSource; 386 } 387 388 //点击表头进行排序 389 private reSortByColum(key: string, type: number): void { 390 // 如果数组为空,则直接返回 391 if (!this.cpuByIrqSource.length) return; 392 let sortObject: finalResultBean[] = JSON.parse(JSON.stringify(this.cpuByIrqSource)).splice(1); 393 let sortList: Array<finalResultBean> = []; 394 sortList.push(...sortObject); 395 if (type === 0) { 396 this.cpuByThreadTbl!.recycleDataSource = this.cpuByIrqSource; 397 } else { 398 sortList.sort((a, b) => { 399 let aValue: number | string, bValue: number | string; 400 if (key === 'process' || key === 'thread') { 401 // @ts-ignore 402 aValue = a[key]; 403 // @ts-ignore 404 bValue = b[key]; 405 } else { 406 // @ts-ignore 407 aValue = parseFloat(a[key]); 408 // @ts-ignore 409 bValue = parseFloat(b[key]); 410 } 411 if (typeof aValue === 'string' && typeof bValue === 'string') { 412 return type === 1 ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); 413 } else if (typeof aValue === 'number' && typeof bValue === 'number') { 414 return type === 1 ? aValue - bValue : bValue - aValue; 415 } else { 416 return 0; 417 } 418 }); 419 this.cpuByThreadTbl!.recycleDataSource = [this.cpuByIrqSource[0]].concat(sortList); 420 } 421 } 422 423 private processResult(result: Array<unknown>, cpuByThreadValue: unknown): void { 424 let sumWall = 0.0; 425 let sumOcc = 0; 426 let map: Map<string, unknown> = new Map<string, unknown>(); 427 for (let e of result) { 428 // @ts-ignore 429 sumWall += e.wallDuration; 430 // @ts-ignore 431 sumOcc += e.occurrences; 432 this.updateThreadMap(e, cpuByThreadValue, map); 433 } 434 this.calculateCount(map, sumWall, sumOcc); 435 } 436 437 private updateThreadMap(e: unknown, cpuByThreadValue: unknown, map: Map<string, unknown>): void { 438 // @ts-ignore 439 if (map.has(`${e.tid}`)) { 440 this.updateExistingThread(e, cpuByThreadValue, map); 441 } else { 442 this.createThread(e, cpuByThreadValue, map); 443 } 444 } 445 446 private updateExistingThread(e: unknown, cpuByThreadValue: unknown, map: Map<string, unknown>): void { 447 // @ts-ignore 448 let thread = map.get(`${e.tid}`)!; 449 // @ts-ignore 450 thread.wallDuration += e.wallDuration; 451 // @ts-ignore 452 thread.occurrences += e.occurrences; 453 this.updateCpuValues(e, cpuByThreadValue, thread); 454 } 455 456 private createThread(e: unknown, cpuByThreadValue: unknown, map: Map<string, unknown>): void { 457 // @ts-ignore 458 let process = Utils.getInstance().getProcessMap(cpuByThreadValue.traceId).get(e.pid); 459 // @ts-ignore 460 let thread = Utils.getInstance().getThreadMap(cpuByThreadValue.traceId).get(e.tid); 461 let cpuByThreadObject: unknown = { 462 // @ts-ignore 463 tid: e.tid, 464 // @ts-ignore 465 pid: e.pid, 466 thread: !thread || thread.length === 0 ? '[NULL]' : thread, 467 process: !process || process.length === 0 ? '[NULL]' : process, 468 // @ts-ignore 469 wallDuration: e.wallDuration || 0, 470 // @ts-ignore 471 occurrences: e.occurrences || 0, 472 avgDuration: 0, 473 }; 474 this.initializeCpuValues(cpuByThreadValue, cpuByThreadObject); 475 this.updateCpuValues(e, cpuByThreadValue, cpuByThreadObject); 476 // @ts-ignore 477 map.set(`${e.tid}`, cpuByThreadObject); 478 } 479 480 private initializeCpuValues(cpuByThreadValue: unknown, cpuByThreadObject: unknown): void { 481 // @ts-ignore 482 for (let i of cpuByThreadValue.cpus) { 483 // @ts-ignore 484 cpuByThreadObject[`cpu${i}`] = 0; 485 // @ts-ignore 486 cpuByThreadObject[`cpu${i}TimeStr`] = '0'; 487 // @ts-ignore 488 cpuByThreadObject[`cpu${i}Ratio`] = '0'; 489 } 490 } 491 492 private updateCpuValues(e: unknown, cpuByThreadValue: unknown, cpuByThreadObject: unknown): void { 493 // @ts-ignore 494 cpuByThreadObject[`cpu${e.cpu}`] = e.wallDuration || 0; 495 // @ts-ignore 496 cpuByThreadObject[`cpu${e.cpu}TimeStr`] = getProbablyTime(e.wallDuration || 0); 497 // @ts-ignore 498 let ratio = ((100.0 * (e.wallDuration || 0)) / (cpuByThreadValue.rightNs - cpuByThreadValue.leftNs)).toFixed(2); 499 if (ratio === '0.00') { 500 ratio = '0'; 501 } 502 // @ts-ignore 503 cpuByThreadObject[`cpu${e.cpu}Ratio`] = ratio; 504 } 505 506 private calculateCount(map: Map<string, unknown>, sumWall: number, sumOcc: number): void { 507 // @ts-ignore 508 let arr = Array.from(map.values()).sort((a, b) => b.wallDuration - a.wallDuration); 509 for (let e of arr) { 510 // @ts-ignore 511 e.avgDuration = (e.wallDuration / (e.occurrences || 1.0) / 1000000.0).toFixed(5); 512 // @ts-ignore 513 e.wallDuration = parseFloat((e.wallDuration / 1000000.0).toFixed(5)); 514 } 515 let count: unknown = {}; 516 // @ts-ignore 517 count.process = ' '; 518 // @ts-ignore 519 count.wallDuration = parseFloat((sumWall / 1000000.0).toFixed(7)); 520 // @ts-ignore 521 count.occurrences = sumOcc; 522 // @ts-ignore 523 arr.splice(0, 0, count); 524 // @ts-ignore 525 this.cpuByThreadSource = arr; 526 this.cpuByThreadTbl!.recycleDataSource = arr; 527 } 528 529 getTableColumns(cpus: Array<number>): string { 530 let cpuByThreadTblHtml = `${this.pubColumns}`; 531 let cpuByThreadList = cpus.sort((cpuByThreadA, cpuByThreadB) => cpuByThreadA - cpuByThreadB); 532 for (let index of cpuByThreadList) { 533 cpuByThreadTblHtml = `${cpuByThreadTblHtml} 534 <lit-table-column width="100px" title="cpu${index}" data-index="cpu${index}TimeStr" key="cpu${index}TimeStr" align="flex-start" order> 535 </lit-table-column> 536 <lit-table-column width="100px" title="%" data-index="cpu${index}Ratio" key="cpu${index}Ratio" align="flex-start" order> 537 </lit-table-column> 538 `; 539 } 540 return cpuByThreadTblHtml; 541 } 542 543 initElements(): void { 544 this.cpuByThreadTbl = this.shadowRoot?.querySelector<LitTable>('#tb-cpu-thread'); 545 this.range = this.shadowRoot?.querySelector('#time-range'); 546 this.cpuByThreadTbl!.addEventListener('column-click', (evt): void => { 547 if (!this.loadIrq) { 548 // @ts-ignore 549 this.sortByColumn(evt.detail); 550 } else { 551 // @ts-ignore 552 this.reSortByColum(evt.detail.key, evt.detail.sort); 553 } 554 }); 555 this.cpuByThreadTbl!.addEventListener('row-click', (evt: unknown): void => { 556 // @ts-ignore 557 let data = evt.detail.data; 558 data.isSelected = true; 559 this.cpuByThreadTbl?.clearAllSelection(data); 560 this.cpuByThreadTbl?.setCurrentSelection(data); 561 }); 562 } 563 564 connectedCallback(): void { 565 super.connectedCallback(); 566 resizeObserver(this.parentElement!, this.cpuByThreadTbl!); 567 } 568 569 initHtml(): string { 570 return ` 571 <style> 572 .cpu-by-thread-label{ 573 width: 100%; 574 height: 20px; 575 } 576 :host{ 577 width: auto; 578 display: flex; 579 flex-direction: column; 580 padding: 10px 10px; 581 } 582 </style> 583 <label id="time-range" class="cpu-by-thread-label" style="text-align: end;font-size: 10pt;margin-bottom: 5px">Selected range:0.0 ms</label> 584 <lit-table id="tb-cpu-thread" style="height:calc( 30vh - 25px )" > 585 586 </lit-table> 587 `; 588 } 589 compare(property: unknown, sort: unknown, type: string) { 590 return function (cpuByThreadLeftData: SelectionData, cpuByThreadRightData: SelectionData): number { 591 if (cpuByThreadLeftData.process === ' ' || cpuByThreadRightData.process === ' ') { 592 return 0; 593 } 594 if (type === 'number') { 595 return sort === 2 // @ts-ignore 596 ? parseFloat(cpuByThreadRightData[property]) - parseFloat(cpuByThreadLeftData[property]) // @ts-ignore 597 : parseFloat(cpuByThreadLeftData[property]) - parseFloat(cpuByThreadRightData[property]); 598 } else { 599 // @ts-ignore 600 if (cpuByThreadRightData[property] > cpuByThreadLeftData[property]) { 601 return sort === 2 ? 1 : -1; 602 } else { 603 // @ts-ignore 604 if (cpuByThreadRightData[property] === cpuByThreadLeftData[property]) { 605 return 0; 606 } else { 607 return sort === 2 ? -1 : 1; 608 } 609 } 610 } 611 }; 612 } 613 sortByColumn(detail: unknown): void { 614 // @ts-ignore 615 if ((detail.key as string).includes('cpu')) { 616 // @ts-ignore 617 if ((detail.key as string).includes('Ratio')) { 618 // @ts-ignore 619 this.cpuByThreadSource.sort(this.compare(detail.key, detail.sort, 'string')); 620 } else { 621 // @ts-ignore 622 this.cpuByThreadSource.sort(this.compare((detail.key as string).replace('TimeStr', ''), detail.sort, 'number')); 623 } 624 } else { 625 if ( 626 // @ts-ignore 627 detail.key === 'pid' || 628 // @ts-ignore 629 detail.key === 'tid' || 630 // @ts-ignore 631 detail.key === 'wallDuration' || 632 // @ts-ignore 633 detail.key === 'avgDuration' || 634 // @ts-ignore 635 detail.key === 'occurrences' 636 ) { 637 // @ts-ignore 638 this.cpuByThreadSource.sort(this.compare(detail.key, detail.sort, 'number')); 639 } else { 640 // @ts-ignore 641 this.cpuByThreadSource.sort(this.compare(detail.key, detail.sort, 'string')); 642 } 643 } 644 645 this.cpuByThreadTbl!.recycleDataSource = this.cpuByThreadSource; 646 } 647} 648