diff --git a/ide/src/trace/component/SpFlags.ts b/ide/src/trace/component/SpFlags.ts index 431e913c8678d80c0d37ef18c3e3df77bd074c06..d50ac6e715acb79355f6e93e382aca4abfb77881 100644 --- a/ide/src/trace/component/SpFlags.ts +++ b/ide/src/trace/component/SpFlags.ts @@ -248,7 +248,12 @@ export class FlagsConfig { title: 'UserPluginsRow', switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], describeContent: 'User Upload Plugin To Draw', - } + }, + { + title: 'CPU by Irq', + switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], + describeContent: 'The real CPU after being split by irq and softirq', + }, ]; static getAllFlagConfig(): Array { diff --git a/ide/src/trace/component/trace/sheet/cpu/CpuAndIrqBean.ts b/ide/src/trace/component/trace/sheet/cpu/CpuAndIrqBean.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ffd51ee06e4e2f279e5c58af98ed82cd0a6681a --- /dev/null +++ b/ide/src/trace/component/trace/sheet/cpu/CpuAndIrqBean.ts @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export class CpuAndIrqBean { + startTime: number = 0; + dur: number = 0; + endTime: number = 0; + cat: string = ''; + cpu: number = 0; + occurrences: number = 1; + priority:number = 0; + pid?: number | string; + tid?: number | string; + isFirstObject?:number = 1; +} + +export class finalResultBean { + wallDuration?:number | string; + dur?: number = 0; + cat?: string = ''; + cpu?: number = 0; + occurrences?: number = 1; + pid?: number | string; + tid?: number | string; +} + +export class byCpuGroupBean { + CPU: CpuAndIrqBean[] = []; +} + +export class softirqAndIrq { + occurrences: number = 0; + wallDuration: number = 0; + avgDuration: number = 0; + //@ts-ignore + cpus: { [cpu: number]: number } = {}; // 初始化 cpus 属性为一个空对象 + [cpuDurKey: string]: number; + //@ts-ignore + tid?: number|string; + //@ts-ignore + pid?: number|string; + //@ts-ignore + process?: string; + //@ts-ignore + thread?: string; +} \ No newline at end of file diff --git a/ide/src/trace/component/trace/sheet/cpu/TabPaneCpuByThread.ts b/ide/src/trace/component/trace/sheet/cpu/TabPaneCpuByThread.ts index e4d73d549e32296fa512184e0405b5b81b9d6490..69aa8ce5e10fe25f67d5299f23543098c33d100f 100644 --- a/ide/src/trace/component/trace/sheet/cpu/TabPaneCpuByThread.ts +++ b/ide/src/trace/component/trace/sheet/cpu/TabPaneCpuByThread.ts @@ -21,6 +21,9 @@ import { getProbablyTime } from '../../../../database/logic-worker/ProcedureLogi import { Utils } from '../../base/Utils'; import { resizeObserver } from '../SheetUtils'; import { getTabCpuByThread } from '../../../../database/sql/Cpu.sql'; +import { getCpuData, getIrqAndSoftIrqData } from "../../../../database/sql/CpuAndIrq.sql"; +import { byCpuGroupBean, CpuAndIrqBean, softirqAndIrq, finalResultBean } from "./CpuAndIrqBean"; +import { FlagsConfig } from '../../../SpFlags'; @element('tabpane-cpu-thread') export class TabPaneCpuByThread extends BaseElement { @@ -28,6 +31,8 @@ export class TabPaneCpuByThread extends BaseElement { private range: HTMLLabelElement | null | undefined; private cpuByThreadSource: Array = []; private currentSelectionParam: SelectionParam | undefined; + private cpuByIrqSource: Array = []; + private loadIrq: boolean = false;//flag开关 private pubColumns = ` @@ -58,21 +63,342 @@ export class TabPaneCpuByThread extends BaseElement { // @ts-ignore `Selected range: ${parseFloat(((cpuByThreadValue.rightNs - cpuByThreadValue.leftNs) / 1000000.0).toFixed(5))} ms`; this.cpuByThreadTbl!.loading = true; - this.handleAsyncRequest(cpuByThreadValue); + this.loadIrq = FlagsConfig.getFlagsConfigEnableStatus('CPU by Irq');//flag开关 + this.handleAsyncRequest(cpuByThreadValue, this.loadIrq); } - private handleAsyncRequest(cpuByThreadValue: unknown): void { - // @ts-ignore - getTabCpuByThread(cpuByThreadValue.cpus, cpuByThreadValue.leftNs, cpuByThreadValue.rightNs).then((result): void => { - this.cpuByThreadTbl!.loading = false; - if (result !== null && result.length > 0) { - log(`getTabCpuByThread size :${result.length}`); - this.processResult(result, cpuByThreadValue); + private handleAsyncRequest(cpuByThreadValue: unknown, loadIrq: boolean): void { + if (loadIrq) { + //查询cpu数据和irq数据 + Promise.all([ + // @ts-ignore + getCpuData(cpuByThreadValue.cpus, cpuByThreadValue.leftNs, cpuByThreadValue.rightNs), + // @ts-ignore + getIrqAndSoftIrqData(cpuByThreadValue.cpus, cpuByThreadValue.leftNs, cpuByThreadValue.rightNs) + ]).then(([cpuData, interruptData]) => { + this.cpuByThreadTbl!.loading = false; + const resArr = cpuData.concat(interruptData); + if (resArr != null && resArr.length > 0) { + const cutData: finalResultBean[] = this.groupByCpu(resArr);//切割后数据 + this.aggregateData(cutData, cpuByThreadValue);//整合数据 + } else { + this.cpuByIrqSource = []; + this.cpuByThreadTbl!.recycleDataSource = this.cpuByIrqSource; + } + }); + } else { + // @ts-ignore + getTabCpuByThread(cpuByThreadValue.cpus, cpuByThreadValue.leftNs, // @ts-ignore + cpuByThreadValue.rightNs, cpuByThreadValue.traceId).then((result): void => { + this.cpuByThreadTbl!.loading = false; + if (result !== null && result.length > 0) { + log(`getTabCpuByThread size :${result.length}`); + this.processResult(result, cpuByThreadValue); + } else { + this.cpuByThreadSource = []; + this.cpuByThreadTbl!.recycleDataSource = this.cpuByThreadSource; + } + }); + } + + } + + //将所有数据按cpu重新分组 + private groupByCpu(data: Array): finalResultBean[] { + const cpuObject: { [cpu: number]: byCpuGroupBean } = + data.reduce((groups, item) => { + const { cpu, ...restProps } = item; + const newCpuAndIrqBean: CpuAndIrqBean = { cpu, ...restProps }; + + if (!groups[cpu]) { + groups[cpu] = { CPU: [] }; + } + groups[cpu].CPU!.push(newCpuAndIrqBean); + + return groups; + }, {} as { [cpu: number]: byCpuGroupBean }) + const cutObj: { [cpu: number]: finalResultBean[] } = {}; + Object.entries(cpuObject).forEach(([cpuStr, { CPU }]) => { + const cpu = Number(cpuStr); + cutObj[cpu] = this.cpuByIrq(CPU); + }) + const cutList: finalResultBean[] = Object.values(cutObj).flat(); + return cutList; + } + + //具体切割方法 + private cpuByIrq(data: CpuAndIrqBean[]): finalResultBean[] { + let sourceData = data.sort((a, b) => a.startTime - b.startTime); + let waitArr: CpuAndIrqBean[] = []; + let completedArr: finalResultBean[] = []; + let globalTs: number = 0; + let index: number = 0; + while (index < sourceData.length || waitArr.length > 0) { + let minEndTs = Math.min(...waitArr.map((item: CpuAndIrqBean) => item.endTime)); + let minIndex = waitArr.findIndex((item: CpuAndIrqBean) => item.endTime === minEndTs); + //当waitArr为空时 + if (waitArr.length === 0) { + globalTs = sourceData[index].startTime; + waitArr.push(sourceData[index]); + index++; + continue; + } + //当全局Ts等于minEndTs时,只做删除处理 + if (globalTs === minEndTs) { + if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; + continue; + } + let obj: finalResultBean = { + cat: '', + dur: 0, + cpu: 0, + pid: 0, + tid: 0, + occurrences: 0 + }; + if (index < sourceData.length) { + if (sourceData[index].startTime < minEndTs) { + if (globalTs === sourceData[index].startTime) { + waitArr.push(sourceData[index]); + index++; + continue; + } else { + const maxPriorityItem = this.findMaxPriority(waitArr); + obj = { + cat: maxPriorityItem.cat, + dur: sourceData[index].startTime - globalTs, + cpu: maxPriorityItem.cpu, + pid: maxPriorityItem.pid ? maxPriorityItem.pid : '[NULL]', + tid: maxPriorityItem.tid ? maxPriorityItem.tid : '[NULL]', + occurrences: maxPriorityItem.isFirstObject === 1 ? 1 : 0 + } + completedArr.push(obj); + maxPriorityItem.isFirstObject = 0; + waitArr.push(sourceData[index]); + globalTs = sourceData[index].startTime; + index++; + } + } else { + const maxPriorityItem = this.findMaxPriority(waitArr); + obj = { + cat: maxPriorityItem.cat, + dur: minEndTs - globalTs, + cpu: maxPriorityItem.cpu, + pid: maxPriorityItem.pid ? maxPriorityItem.pid : '[NULL]', + tid: maxPriorityItem.tid ? maxPriorityItem.tid : '[NULL]', + occurrences: maxPriorityItem.isFirstObject === 1 ? 1 : 0 + } + completedArr.push(obj); + maxPriorityItem.isFirstObject = 0; + globalTs = minEndTs; + if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; + } } else { - this.cpuByThreadSource = []; - this.cpuByThreadTbl!.recycleDataSource = this.cpuByThreadSource; + const maxPriorityItem = this.findMaxPriority(waitArr); + obj = { + cat: maxPriorityItem.cat, + dur: minEndTs - globalTs, + cpu: maxPriorityItem.cpu, + pid: maxPriorityItem.pid ? maxPriorityItem.pid : '[NULL]', + tid: maxPriorityItem.tid ? maxPriorityItem.tid : '[NULL]', + occurrences: maxPriorityItem.isFirstObject === 1 ? 1 : 0 + } + completedArr.push(obj); + maxPriorityItem.isFirstObject = 0; + globalTs = minEndTs; + if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; + } + } + return completedArr; + } + + private findMaxPriority(arr: CpuAndIrqBean[]): CpuAndIrqBean { + return arr.reduce((maxItem: CpuAndIrqBean, currentItem: CpuAndIrqBean) => { + return maxItem.priority > currentItem.priority ? maxItem : currentItem; + }, arr[0]); + } + + // 聚合数据 + private aggregateData(data: any[], cpuByThreadValue: SelectionParam | any): void { + const cpuAggregations: { [tidPidKey: string]: softirqAndIrq } = {}; + //@ts-ignore + let softirqAggregations: softirqAndIrq = { + occurrences: 0, + wallDuration: 0, + avgDuration: 0, + cpus: {} + }; + //@ts-ignore + let irqAggregations: softirqAndIrq = { + occurrences: 0, + wallDuration: 0, + avgDuration: 0, + cpus: {} + }; + data.forEach((obj) => { + // 聚合 cpu 数据 + if (obj.cat === "cpu" && obj.dur !== 0) { + const tidPidKey = `${obj.tid}-${obj.pid}`; + const cpuDurationKey = `cpu${obj.cpu}`; + const cpuPercentKey = `cpu${obj.cpu}Ratio`; + + if (!cpuAggregations[tidPidKey]) { + cpuAggregations[tidPidKey] = { + tid: obj.tid, + pid: obj.pid, + wallDuration: 0, + occurrences: 0, + avgDuration: 0, + cpus: {}, + [cpuDurationKey]: 0, + [cpuPercentKey]: 100, + }; + } + + cpuAggregations[tidPidKey].wallDuration += obj.dur; + cpuAggregations[tidPidKey].occurrences += obj.occurrences; + cpuAggregations[tidPidKey].avgDuration = cpuAggregations[tidPidKey].wallDuration / cpuAggregations[tidPidKey].occurrences; + cpuAggregations[tidPidKey].cpus[obj.cpu] = (cpuAggregations[tidPidKey].cpus[obj.cpu] || 0) + obj.dur; + cpuAggregations[tidPidKey][cpuDurationKey] = cpuAggregations[tidPidKey].cpus[obj.cpu]; + cpuAggregations[tidPidKey][cpuPercentKey] = (cpuAggregations[tidPidKey][cpuDurationKey] / (cpuByThreadValue.rightNs - cpuByThreadValue.leftNs)) * 100; + } + + // 聚合 softirq 数据 + if (obj.cat === "softirq" && obj.dur !== 0) { + this.updateIrqAndSoftirq(softirqAggregations, obj, cpuByThreadValue); + } + // 聚合 irq 数据 + if (obj.cat === "irq" && obj.dur !== 0) { + this.updateIrqAndSoftirq(irqAggregations, obj, cpuByThreadValue); } + + }); + + // 将聚合数据转换为最终结果格式 + const result: Array<{ [key: string]: any }> = []; + + // 添加 CPU 数据 + for (const tidPidKey in cpuAggregations) { + const aggregation = cpuAggregations[tidPidKey]; + const { tid, pid, occurrences, wallDuration, avgDuration, ...cpuDurations } = aggregation; + result.push({ tid, pid, occurrences, wallDuration, avgDuration, ...cpuDurations }); + } + + // 添加softirq + if (softirqAggregations.wallDuration > 0) { + result.push({ process: 'softirq', thread: 'softirq', tid: '[NULL]', pid: '[NULL]', ...softirqAggregations, }); + } + + // 添加 irq 数据 + if (irqAggregations.wallDuration) { + result.push({ process: 'irq', thread: 'irq', tid: '[NULL]', pid: '[NULL]', ...irqAggregations }); + } + this.handleFunction(result, cpuByThreadValue); + + } + + //irq和softirq聚合方法 + private updateIrqAndSoftirq(aggregation: softirqAndIrq, obj: CpuAndIrqBean, cpuByThreadValue: SelectionParam): void { + const callid = obj.cpu; + const callidDurKey = `cpu${callid}`; + const callidPercentKey = `cpu${callid}Ratio`; + + aggregation.wallDuration += obj.dur; + aggregation.occurrences += obj.occurrences; + aggregation.avgDuration = aggregation.wallDuration / aggregation.occurrences; + + if (!aggregation.cpus[callid]) { + aggregation.cpus[callid] = 0; + } + aggregation.cpus[callid] += obj.dur; + + if (!(callidDurKey in aggregation)) { + aggregation[callidDurKey] = 0; + } + aggregation[callidDurKey] += obj.dur; + + aggregation[callidPercentKey] = (aggregation[callidDurKey] / (cpuByThreadValue.rightNs - cpuByThreadValue.leftNs)) * 100; + } + + //最后将所有数据进行统一整理 + private handleFunction(data: Array<{ [key: string]: any }>, cpuByThreadValue: SelectionParam): void { + let index = 0; + let totalWallDuration = 0; + let totalOccurrences = 0; + const finalData: Array = []; + while (index < data.length) { + const obj = data[index]; + totalWallDuration += obj.wallDuration; + totalOccurrences += obj.occurrences; + if (obj.tid !== '[NULL]' && obj.pid !== '[NULL]') { + let process = Utils.getInstance().getProcessMap(cpuByThreadValue.traceId).get(obj.pid); + let thread = Utils.getInstance().getThreadMap(cpuByThreadValue.traceId).get(obj.tid); + obj.thread = thread == null || thread.length === 0 ? '[NULL]' : thread; + obj.process = process == null || process.length === 0 ? '[NULL]' : process; + } + obj.wallDuration /= 1000000; + obj.wallDuration = obj.wallDuration.toFixed(6); + obj.avgDuration /= 1000000; + obj.avgDuration = obj.avgDuration.toFixed(6); + for (const cpu in obj.cpus) { + if (obj.cpus.hasOwnProperty(cpu)) { + obj[`cpu${cpu}TimeStr`] = getProbablyTime(obj[`cpu${cpu}`]); + obj[`cpu${cpu}Ratio`] = obj[`cpu${cpu}Ratio`].toFixed(2); + } + } + for (const cpuNumber of cpuByThreadValue.cpus) { + const cpuKey = `cpu${cpuNumber}TimeStr`; + const percentKey = `cpu${cpuNumber}Ratio`; + if (!obj.hasOwnProperty(cpuKey)) { + obj[cpuKey] = "0.00"; + obj[percentKey] = "0.00"; + } + } + delete obj.cpus; + finalData.push(obj); + index++; + } + finalData.unshift({ + wallDuration: (totalWallDuration / 1000000).toFixed(6), + occurrences: totalOccurrences, }); + this.cpuByIrqSource = finalData; + this.cpuByThreadTbl!.recycleDataSource = this.cpuByIrqSource; + } + + //点击表头进行排序 + private reSortByColum(key: string, type: number): void { + // 如果数组为空,则直接返回 + if (!this.cpuByIrqSource.length) return; + let sortObject: finalResultBean[] = JSON.parse(JSON.stringify(this.cpuByIrqSource)).splice(1); + let sortList: Array = []; + sortList.push(...sortObject); + if (type === 0) { + this.cpuByThreadTbl!.recycleDataSource = this.cpuByIrqSource; + } else { + sortList.sort((a, b) => { + let aValue: number | string, bValue: number | string; + if (key === 'process' || key === 'thread') { + // @ts-ignore + aValue = a[key]; + // @ts-ignore + bValue = b[key]; + } else { + // @ts-ignore + aValue = parseFloat(a[key]); + // @ts-ignore + bValue = parseFloat(b[key]); + } + if (typeof aValue === 'string' && typeof bValue === 'string') { + return type === 1 ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); + } else if (typeof aValue === 'number' && typeof bValue === 'number') { + return type === 1 ? aValue - bValue : bValue - aValue; + } else { + return 0; + } + }); + this.cpuByThreadTbl!.recycleDataSource = [this.cpuByIrqSource[0]].concat(sortList); + } } private processResult(result: Array, cpuByThreadValue: unknown): void { @@ -199,8 +525,13 @@ export class TabPaneCpuByThread extends BaseElement { this.cpuByThreadTbl = this.shadowRoot?.querySelector('#tb-cpu-thread'); this.range = this.shadowRoot?.querySelector('#time-range'); this.cpuByThreadTbl!.addEventListener('column-click', (evt): void => { - // @ts-ignore - this.sortByColumn(evt.detail); + if (!this.loadIrq) { + // @ts-ignore + this.sortByColumn(evt.detail); + } else { + // @ts-ignore + this.reSortByColum(evt.detail.key, evt.detail.sort); + } }); this.cpuByThreadTbl!.addEventListener('row-click', (evt: unknown): void => { // @ts-ignore diff --git a/ide/src/trace/component/trace/sheet/irq/TabPaneIrqCounter.ts b/ide/src/trace/component/trace/sheet/irq/TabPaneIrqCounter.ts index e279a1415f36faf308335c8852baccd9f7c6f628..d15ff7305c9b37c13342870b00d625561ad51b4f 100644 --- a/ide/src/trace/component/trace/sheet/irq/TabPaneIrqCounter.ts +++ b/ide/src/trace/component/trace/sheet/irq/TabPaneIrqCounter.ts @@ -17,7 +17,9 @@ import { BaseElement, element } from '../../../../../base-ui/BaseElement'; import { LitTable } from '../../../../../base-ui/table/lit-table'; import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; import { initSort, resizeObserver } from '../SheetUtils'; -import { queryIrqDataBoxSelect, querySoftIrqDataBoxSelect } from '../../../../database/sql/Irq.sql'; +import { queryIrqDataBoxSelect, querySoftIrqDataBoxSelect, queryIrqSelectData, querySoftirqSelectData } from '../../../../database/sql/Irq.sql'; +import { FlagsConfig } from '../../../SpFlags'; +import { IrqAndSoftirqBean, byCallidGroupBean, finalResultBean } from './irqAndSoftirqBean'; @element('tabpane-irq-counter') export class TabPaneIrqCounter extends BaseElement { @@ -26,6 +28,8 @@ export class TabPaneIrqCounter extends BaseElement { private irqCounterSource: Array = []; private sortColumn: string = 'wallDurationFormat'; private sortType: number = 2; + private loadIrq: boolean = false;//flag开关 + private irqAndSoftirqSource: Array = []; set data(irqParam: SelectionParam | unknown) { if (this.irqCounterTbl) { @@ -36,45 +40,74 @@ export class TabPaneIrqCounter extends BaseElement { // @ts-ignore ((irqParam.rightNs - irqParam.leftNs) / 1000000.0).toFixed(5) )} ms`; + this.irqCounterTbl!.loading = true; let dataSource: Array = []; - Promise.all([ - // @ts-ignore - queryIrqDataBoxSelect(irqParam.irqCallIds, irqParam.leftNs, irqParam.rightNs), // @ts-ignore - querySoftIrqDataBoxSelect(irqParam.softIrqCallIds, irqParam.leftNs, irqParam.rightNs), - ]).then((resArr) => { - resArr.forEach((res) => { - res.forEach((item) => { - let selectData = new SelectionData(); - //@ts-ignore - selectData.name = item.irqName; - //@ts-ignore - selectData.count = item.count; - //@ts-ignore - selectData.wallDuration = item.wallDuration; - //@ts-ignore - selectData.wallDurationFormat = (item.wallDuration / 1000).toFixed(2); - //@ts-ignore - selectData.maxDuration = item.wallDuration; - //@ts-ignore - selectData.maxDurationFormat = (item.maxDuration / 1000).toFixed(2); - //@ts-ignore - selectData.avgDuration = (item.avgDuration / 1000).toFixed(2); - dataSource.push(selectData); + this.loadIrq = FlagsConfig.getFlagsConfigEnableStatus('CPU by Irq');//flag开关 + if (this.loadIrq) {//@ts-ignore + let irqCallIds = irqParam.softIrqCallIds.length > 0 ? irqParam.softIrqCallIds : irqParam.irqCallIds; + Promise.all([//@ts-ignore + queryIrqSelectData(irqCallIds, irqParam.leftNs, irqParam.rightNs),//@ts-ignore + querySoftirqSelectData(irqParam.softIrqCallIds, irqParam.leftNs, irqParam.rightNs), + ]).then(([irqData, softirqData]) => { + this.irqCounterTbl!.loading = false; + const resArr = irqData.concat(softirqData); + if (resArr != null && resArr.length > 0) {//@ts-ignore + let isSelectIrq = irqParam.irqCallIds.length > 0 ? true : false; + const cutData: finalResultBean[] = this.groupByCallid(resArr); + this.aggregateData(cutData, isSelectIrq);//整合数据 + } else { + this.irqAndSoftirqSource = []; + this.irqCounterTbl!.recycleDataSource = this.irqAndSoftirqSource; + } + }); + } else { + Promise.all([ + // @ts-ignore + queryIrqDataBoxSelect(irqParam.irqCallIds, irqParam.leftNs, irqParam.rightNs), // @ts-ignore + querySoftIrqDataBoxSelect(irqParam.softIrqCallIds, irqParam.leftNs, irqParam.rightNs), + ]).then((resArr) => { + this.irqCounterTbl!.loading = false; + resArr.forEach((res) => { + res.forEach((item) => { + let selectData = new SelectionData(); + //@ts-ignore + selectData.name = item.irqName; + //@ts-ignore + selectData.cat = item.cat; + //@ts-ignore + selectData.count = item.count; + //@ts-ignore + selectData.wallDuration = item.wallDuration; + //@ts-ignore + selectData.wallDurationFormat = (item.wallDuration / 1000).toFixed(2); + //@ts-ignore + selectData.maxDuration = item.wallDuration; + //@ts-ignore + selectData.maxDurationFormat = (item.maxDuration / 1000).toFixed(2); + //@ts-ignore + selectData.avgDuration = (item.avgDuration / 1000).toFixed(2); + dataSource.push(selectData); + }); }); + initSort(this.irqCounterTbl!, this.sortColumn, this.sortType); + this.irqCounterSource = dataSource; + this.irqCounterTbl!.recycleDataSource = dataSource; + this.sortByColumn(this.sortColumn, this.sortType); }); - initSort(this.irqCounterTbl!, this.sortColumn, this.sortType); - this.irqCounterSource = dataSource; - this.irqCounterTbl!.recycleDataSource = dataSource; - this.sortByColumn(this.sortColumn, this.sortType); - }); + } } initElements(): void { this.irqCounterTbl = this.shadowRoot?.querySelector('#tb-irq-counter'); this.irqRange = this.shadowRoot?.querySelector('#time-range'); this.irqCounterTbl!.addEventListener('column-click', (event) => { - // @ts-ignore - this.sortByColumn(event.detail.key, event.detail.sort); + if (!this.loadIrq) { + // @ts-ignore + this.sortByColumn(event.detail.key, event.detail.sort); + } else { + // @ts-ignore + this.reSortByColum(event.detail.key, event.detail.sort); + } }); } @@ -99,6 +132,8 @@ export class TabPaneIrqCounter extends BaseElement { + + @@ -136,4 +171,178 @@ export class TabPaneIrqCounter extends BaseElement { }); this.irqCounterTbl!.recycleDataSource = arr; } + + //将所有数据按callid重新分组 + private groupByCallid(data: Array): finalResultBean[] { + const callidObject: { [callid: number]: byCallidGroupBean } = + data.reduce((groups, item) => { + const { callid, ...restProps } = item; + const newIrqAndSoftirqBean: IrqAndSoftirqBean = { callid, ...restProps }; + + if (!groups[callid]) { + groups[callid] = { Callid: [] }; + } + groups[callid].Callid!.push(newIrqAndSoftirqBean); + + return groups; + }, {} as { [callid: number]: byCallidGroupBean }); + const cutObj: { [callid: number]: finalResultBean[] } = {}; + Object.entries(callidObject).forEach(([callidStr, { Callid }]) => { + const callid = Number(callidStr); + cutObj[callid] = this.callidByIrq(Callid); + }); + const cutList: finalResultBean[] = Object.values(cutObj).flat(); + return cutList; + } + + //具体切割方法 + private callidByIrq(data: IrqAndSoftirqBean[]): finalResultBean[] { + let sourceData = data.sort((a, b) => a.startTime - b.startTime); + let waitArr: IrqAndSoftirqBean[] = []; + let completedArr: finalResultBean[] = []; + let globalTs: number = 0; + let index: number = 0; + while (index < sourceData.length || waitArr.length > 0) { + let minEndTs = Math.min(...waitArr.map((item: IrqAndSoftirqBean) => item.endTime)); + let minIndex = waitArr.findIndex((item: IrqAndSoftirqBean) => item.endTime === minEndTs); + //当waitArr为空时 + if (waitArr.length === 0) { + globalTs = sourceData[index].startTime; + waitArr.push(sourceData[index]); + index++; + continue; + } + //当全局Ts等于minEndTs时,只做删除处理 + if (globalTs === minEndTs) { + if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; + continue; + } + let obj: finalResultBean = { + cat: '', + name: '', + wallDuration: 0, + count: 0, + }; + if (index < sourceData.length) { + if (sourceData[index].startTime < minEndTs) { + if (globalTs === sourceData[index].startTime) { + waitArr.push(sourceData[index]); + index++; + continue; + } else { + const maxPriorityItem = this.findMaxPriority(waitArr); + obj = { + cat: maxPriorityItem.cat, + name: maxPriorityItem.name, + wallDuration: sourceData[index].startTime - globalTs, + count: maxPriorityItem.isFirstObject === 1 ? 1 : 0 + } + completedArr.push(obj); + maxPriorityItem.isFirstObject = 0; + waitArr.push(sourceData[index]); + globalTs = sourceData[index].startTime; + index++; + } + } else { + const maxPriorityItem = this.findMaxPriority(waitArr); + obj = { + cat: maxPriorityItem.cat, + name: maxPriorityItem.name, + wallDuration: minEndTs - globalTs, + count: maxPriorityItem.isFirstObject === 1 ? 1 : 0 + } + completedArr.push(obj); + maxPriorityItem.isFirstObject = 0; + globalTs = minEndTs; + if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; + } + } else { + const maxPriorityItem = this.findMaxPriority(waitArr); + obj = { + cat: maxPriorityItem.cat, + name: maxPriorityItem.name, + wallDuration: minEndTs - globalTs, + count: maxPriorityItem.isFirstObject === 1 ? 1 : 0 + } + completedArr.push(obj); + maxPriorityItem.isFirstObject = 0; + globalTs = minEndTs; + if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; + } + } + return completedArr; + } + + private findMaxPriority(arr: IrqAndSoftirqBean[]): IrqAndSoftirqBean { + return arr.reduce((maxItem: IrqAndSoftirqBean, currentItem: IrqAndSoftirqBean) => { + return maxItem.priority > currentItem.priority ? maxItem : currentItem; + }, arr[0]);; + } + + // 聚合数据 + private aggregateData(data: finalResultBean[], isSelectIrq: boolean): void { + function groupAndSumDurations(items: finalResultBean[]): finalResultBean[] { + const grouped: Record = items.reduce((acc, item) => { + if (item.wallDuration !== 0) { + if (item.cat === 'irq' && !isSelectIrq) {//若没有框选irq,则不对其进行处理 + return acc; + } + if (!acc[item.name]) { + acc[item.name] = { + wallDuration: 0, + maxDuration: 0, + name: item.name, + cat: item.cat, + count: 0, + avgDuration: 0 + }; + } + acc[item.name].wallDuration += item.wallDuration; + acc[item.name].wallDurationFormat = (acc[item.name].wallDuration / 1000).toFixed(2); + acc[item.name].count += item.count; + acc[item.name].avgDuration = (acc[item.name].wallDuration / acc[item.name].count / 1000).toFixed(2); + if (item.wallDuration > acc[item.name].maxDuration!) { + acc[item.name].maxDuration = item.wallDuration; + acc[item.name].maxDurationFormat = (acc[item.name].maxDuration! / 1000).toFixed(2); + } + } + return acc; + }, {} as Record); + return Object.values(grouped); + } + this.irqAndSoftirqSource = groupAndSumDurations(data); + this.irqCounterTbl!.recycleDataSource = this.irqAndSoftirqSource; + } + + private reSortByColum(key: string, type: number): void { + // 如果数组为空,则直接返回 + if (!this.irqAndSoftirqSource.length) return; + let sortObject: finalResultBean[] = JSON.parse(JSON.stringify(this.irqAndSoftirqSource)); + let sortList: Array = []; + sortList.push(...sortObject); + if (type === 0) { + this.irqCounterTbl!.recycleDataSource = this.irqAndSoftirqSource; + } else { + sortList.sort((a, b) => { + let aValue: number | string, bValue: number | string; + if (key === 'name' || key === 'cat') { + aValue = a[key]; + bValue = b[key]; + } else { + // @ts-ignore + aValue = parseFloat(a[key]); + // @ts-ignore + bValue = parseFloat(b[key]); + } + if (typeof aValue === 'string' && typeof bValue === 'string') { + return type === 1 ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); + } else if (typeof aValue === 'number' && typeof bValue === 'number') { + return type === 1 ? aValue - bValue : bValue - aValue; + } else { + return 0; + } + }); + this.irqCounterTbl!.recycleDataSource = sortList; + } + } } diff --git a/ide/src/trace/component/trace/sheet/irq/irqAndSoftirqBean.ts b/ide/src/trace/component/trace/sheet/irq/irqAndSoftirqBean.ts new file mode 100644 index 0000000000000000000000000000000000000000..947b26c26163800de66d22683a68d51bf2b41635 --- /dev/null +++ b/ide/src/trace/component/trace/sheet/irq/irqAndSoftirqBean.ts @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export class IrqAndSoftirqBean { + cat: string = ''; + name: string = ''; + callid: number = 0; + count: number = 0; + isFirstObject: number = 1; + startTime: number = 0; + endTime: number = 0; + wallDuration: number = 0; + priority: number = 0; +} + +export class byCallidGroupBean { + Callid: IrqAndSoftirqBean[] = []; +} + +export class finalResultBean { + cat: string = ''; + wallDuration: number = 0; + maxDuration?: number = 0; + name: string = ''; + count: number = 0; + avgDuration?: number | string; + wallDurationFormat?: number | string; + maxDurationFormat?: number | string; +} \ No newline at end of file diff --git a/ide/src/trace/database/sql/CpuAndIrq.sql.ts b/ide/src/trace/database/sql/CpuAndIrq.sql.ts new file mode 100644 index 0000000000000000000000000000000000000000..af37a12fe393d6caebee0bd20d9ea49d3fc1a3ba --- /dev/null +++ b/ide/src/trace/database/sql/CpuAndIrq.sql.ts @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { query } from "../SqlLite"; +import { CpuAndIrqBean} from "../../component/trace/sheet/cpu/CpuAndIrqBean"; +export const getCpuData = (cpus: Array, leftNS: number, rightNS: number): Promise> => + query( + 'getCpuData', + ` + SELECT + TS.pid AS pid, + TS.tid AS tid, + TS.cpu, + 'cpu' AS cat, + 1 As occurrences, + 1 As priority, + true As isFirstObject, + MAX(${leftNS},TS.ts - TR.start_ts) AS startTime, + MIN(${rightNS},(TS.ts - TR.start_ts + iif(TS.dur = -1 OR TS.dur IS NULL, 0, TS.dur))) AS endTime, + MIN(${rightNS},(TS.ts - TR.start_ts + iif(TS.dur = -1 OR TS.dur IS NULL, 0, TS.dur))) - MAX(${leftNS},TS.ts - TR.start_ts) AS dur + FROM + thread_state AS TS + LEFT JOIN + trace_range AS TR + WHERE + TS.cpu IN (${cpus.join(',')}) + AND + NOT ((TS.ts - TR.start_ts + iif(TS.dur = -1 OR TS.dur IS NULL, 0, TS.dur) < ${leftNS}) OR (TS.ts - TR.start_ts > ${rightNS}))` + ); +export const getIrqAndSoftIrqData = (cpus: Array, leftNS: number, rightNS: number): Promise> =>{ + return query( + 'getIrqAndSoftIrqData', + ` + SELECT + CASE + WHEN i.cat = 'softirq' + THEN 'softirq' + ELSE 'irq' + END AS cat, + CASE + WHEN i.cat = 'softirq' + THEN 2 + ELSE 3 + END AS priority, + i.callid as cpu, + 1 As occurrences, + true As isFirstObject, + MAX(${leftNS},i.ts - TR.start_ts) AS startTime, + MIN(${rightNS},(i.ts - TR.start_ts + iif(i.dur = -1 OR i.dur IS NULL, 0, i.dur))) AS endTime, + MIN(${rightNS},(i.ts - TR.start_ts + iif(i.dur = -1 OR i.dur IS NULL, 0, i.dur))) - MAX(${leftNS},i.ts - TR.start_ts) AS dur + FROM + irq i + LEFT JOIN + trace_range AS TR + WHERE + i.callid IN (${cpus.join(',')}) + AND + NOT ((i.ts - TR.start_ts + iif(i.dur = -1 OR i.dur IS NULL, 0, i.dur) < ${leftNS}) OR (i.ts - TR.start_ts > ${rightNS})) + AND ( ( i.cat = 'irq' AND i.flag = '1' ) OR i.cat = 'ipi' OR i.cat = 'softirq' )` + ); +} \ No newline at end of file diff --git a/ide/src/trace/database/sql/Irq.sql.ts b/ide/src/trace/database/sql/Irq.sql.ts index 7009b3e3c00010b3ff91e8de213ea0ccad9a9487..760f2ea9e0e95859471bd222fae6d9456fbd05ec 100644 --- a/ide/src/trace/database/sql/Irq.sql.ts +++ b/ide/src/trace/database/sql/Irq.sql.ts @@ -14,6 +14,7 @@ */ import { query } from '../SqlLite'; import { IrqStruct } from '../ui-worker/ProcedureWorkerIrq'; +import {IrqAndSoftirqBean} from '../../component/trace/sheet/irq/irqAndSoftirqBean' export const queryIrqList = (): Promise> => query('queryIrqList', `select cat as name,callid as cpu from irq where cat!= 'ipi' group by cat,callid`); @@ -49,7 +50,10 @@ export const queryIrqDataBoxSelect = ( Promise> => { let sqlIrq = ` select case when i.cat = 'ipi' then 'IPI' || i.name else i.name end as irqName, - sum(dur) as wallDuration, + 'irq' as cat, + sum( + min(${endNS},(i.ts - t.start_ts + iif(i.dur = -1 OR i.dur is null, 0, i.dur))) - min(${startNS},i.ts - t.start_ts) + ) as wallDuration, max(dur) as maxDuration, count(1) as count, avg(ifnull(dur, 0)) as avgDuration @@ -71,7 +75,10 @@ export const querySoftIrqDataBoxSelect = ( Promise> => { let sqlIrq = ` select i.name as irqName, - sum(dur) as wallDuration, + i.cat, + sum( + min(${endNS},(i.ts - t.start_ts + iif(i.dur = -1 OR i.dur is null, 0, i.dur))) - min(${startNS},i.ts - t.start_ts) + ) as wallDuration, max(dur) as maxDuration, count(1) as count, avg(ifnull(dur, 0)) as avgDuration @@ -84,3 +91,58 @@ group by irqName; `; return query('querySoftIrqDataBoxSelect', callIds.length > 0 ? sqlIrq : '', {}); }; + +export const queryIrqSelectData = (callIds: Array, startNS: number, endNS: number): Promise> => + query( + 'getIrqSelectData', + ` + SELECT + 'irq' AS cat, + i.callid, + CASE + WHEN i.cat = 'ipi' + THEN 'IPI' || i.name + ELSE i.name + END AS name, + 1 As count, + true As isFirstObject, + 2 AS priority, + MAX(${startNS},i.ts - TR.start_ts) AS startTime, + MIN(${endNS},(i.ts - TR.start_ts + iif(i.dur = -1 OR i.dur IS NULL, 0, i.dur))) AS endTime, + MIN(${endNS},(i.ts - TR.start_ts + iif(i.dur = -1 OR i.dur IS NULL, 0, i.dur))) - MAX(${startNS},i.ts - TR.start_ts) AS wallDuration + FROM + irq i + LEFT JOIN + trace_range AS TR + WHERE + i.callid IN (${callIds.join(',')}) + AND + NOT ((i.ts - TR.start_ts + iif(i.dur = -1 OR i.dur IS NULL, 0, i.dur) < ${startNS}) OR (i.ts - TR.start_ts > ${endNS})) + AND ( ( i.cat = 'irq' AND i.flag = '1' ) OR i.cat = 'ipi' )` + ); + + export const querySoftirqSelectData = (callIds: Array, startNS: number, endNS: number): Promise> => + query( + 'getSoftirqSelectData', + ` + SELECT + 'softirq' AS cat, + i.callid, + i.name, + 1 As count, + true As isFirstObject, + 1 AS priority, + MAX(${startNS},i.ts - TR.start_ts) AS startTime, + MIN(${endNS},(i.ts - TR.start_ts + iif(i.dur = -1 OR i.dur IS NULL, 0, i.dur))) AS endTime, + MIN(${endNS},(i.ts - TR.start_ts + iif(i.dur = -1 OR i.dur IS NULL, 0, i.dur))) - MAX(${startNS},i.ts - TR.start_ts) AS wallDuration + FROM + irq i + LEFT JOIN + trace_range AS TR + WHERE + i.callid IN (${callIds.join(',')}) + AND + NOT ((i.ts - TR.start_ts + iif(i.dur = -1 OR i.dur IS NULL, 0, i.dur) < ${startNS}) OR (i.ts - TR.start_ts > ${endNS})) + AND + i.cat = 'softirq' ` + ); \ No newline at end of file