diff --git a/ide/src/trace/component/chart/SpSegmentationChart.ts b/ide/src/trace/component/chart/SpSegmentationChart.ts index b0d6adc2f88ba1cd6e642307bd5aa1509fd4420b..e1db3d5f60babf4038c25c3a69efa2a7f792811b 100644 --- a/ide/src/trace/component/chart/SpSegmentationChart.ts +++ b/ide/src/trace/component/chart/SpSegmentationChart.ts @@ -24,6 +24,7 @@ import { type AllStatesRender, AllstatesStruct } from '../../database/ui-worker/ import { StateGroup } from '../../bean/StateModle'; import { queryAllFuncNames } from '../../database/sql/Func.sql'; import { Utils } from '../trace/base/Utils'; +import { TabPaneFreqUsage } from "../trace/sheet/frequsage/TabPaneFreqUsage"; const UNIT_HEIGHT: number = 20; const MS_TO_US: number = 1000000; const MIN_HEIGHT: number = 2; @@ -221,6 +222,7 @@ export class SpSegmentationChart { SpSegmentationChart.freqInfoMapData.set(v.cpuId, mapData); mapData = new Map(); }); + TabPaneFreqUsage.refresh(); } }; SpSegmentationChart.cpuRow.focusHandler = (ev): void => { diff --git a/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsage.ts b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsage.ts index 70dadfc3e707f3a32632b131bbe9de6eab099e6d..c219847754b5cf452af73bfb69b8941f7c3c6562 100644 --- a/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsage.ts +++ b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsage.ts @@ -13,36 +13,52 @@ * limitations under the License. */ -import { BaseElement, element } from '../../../../../base-ui/BaseElement'; -import { LitTable, RedrawTreeForm } from '../../../../../base-ui/table/lit-table'; -import { SelectionParam } from '../../../../bean/BoxSelection'; -import '../../../StackBar'; -import { getTabRunningPercent } from '../../../../database/sql/ProcessThread.sql'; -import { queryCpuFreqUsageData, queryCpuFreqFilterId } from '../../../../database/sql/Cpu.sql'; -import { Utils } from '../../base/Utils'; -import { resizeObserver } from '../SheetUtils'; -import { SpSegmentationChart } from '../../../chart/SpSegmentationChart'; -import { type CpuFreqData, type RunningFreqData, type RunningData, type CpuFreqTd } from './TabPaneFreqUsageConfig'; +import { BaseElement, element } from "../../../../../base-ui/BaseElement"; +import { + LitTable, + RedrawTreeForm, +} from "../../../../../base-ui/table/lit-table"; +import { SelectionParam } from "../../../../bean/BoxSelection"; +import "../../../StackBar"; +import { getTabRunningPercent } from "../../../../database/sql/ProcessThread.sql"; +import { + queryCpuFreqUsageData, + queryCpuFreqFilterId, +} from "../../../../database/sql/Cpu.sql"; +import { Utils } from "../../base/Utils"; +import { resizeObserver } from "../SheetUtils"; +import { SpSegmentationChart } from "../../../chart/SpSegmentationChart"; +import { + type CpuFreqData, + type RunningFreqData, + type RunningData, + type CpuFreqTd, +} from "./TabPaneFreqUsageConfig"; -@element('tabpane-frequsage') +@element("tabpane-frequsage") export class TabPaneFreqUsage extends BaseElement { private threadStatesTbl: LitTable | null | undefined; private currentSelectionParam: SelectionParam | undefined; - private result: Array = []; + private worker: Worker | undefined; + static element: TabPaneFreqUsage; set data(threadStatesParam: SelectionParam) { if (this.currentSelectionParam === threadStatesParam) { return; } - this.threadStatesTbl!.loading = true; this.currentSelectionParam = threadStatesParam; this.threadStatesTbl!.recycleDataSource = []; // @ts-ignore this.threadStatesTbl.value = []; - this.result = []; this.queryAllData(threadStatesParam); } + + static refresh(): void { + this.prototype.queryAllData(TabPaneFreqUsage.element.currentSelectionParam!); + } + async queryAllData(threadStatesParam: SelectionParam): Promise { + TabPaneFreqUsage.element.threadStatesTbl!.loading = true; let runningResult: Array = await getTabRunningPercent( threadStatesParam.threadIds, threadStatesParam.processIds, @@ -50,7 +66,8 @@ export class TabPaneFreqUsage extends BaseElement { threadStatesParam.rightNs ); // 查询cpu及id信息 - let cpuIdResult: Array<{ id: number; cpu: number }> = await queryCpuFreqFilterId(); + let cpuIdResult: Array<{ id: number; cpu: number }> = + await queryCpuFreqFilterId(); // 以键值对形式将cpu及id进行对应,后续会将频点数据与其对应cpu进行整合 let IdMap: Map = new Map(); let queryId: Array = []; @@ -73,85 +90,47 @@ export class TabPaneFreqUsage extends BaseElement { } const LEFT_TIME: number = threadStatesParam.leftNs + threadStatesParam.recordStartNs; const RIGHT_TIME: number = threadStatesParam.rightNs + threadStatesParam.recordStartNs; - const args = { leftNs: LEFT_TIME, rightNs: RIGHT_TIME, cpuArray: cpuArray }; - let resultArr: Array = orgnazitionMap(runningResult, cpuFreqData, args); - // 递归拿出来最底层的数据,并以进程层级的数据作为分割 - this.recursion(resultArr); - this.result = JSON.parse(JSON.stringify(this.result)); - mergeTotal(resultArr, fixTotal(this.result)); - this.fixedDeal(resultArr, threadStatesParam.traceId); - this.threadClick(resultArr); - this.threadStatesTbl!.recycleDataSource = resultArr; - this.threadStatesTbl!.loading = false; - } - - /** - * 递归整理数据小数位 - */ - fixedDeal(arr: Array, traceId?: string | null): void { - if (arr == undefined) { - return; - } - const TIME_MUTIPLE: number = 1000000; - // KHz->MHz * ns->ms - const CONS_MUTIPLE: number = 1000000000; - const FREQ_MUTIPLE: number = 1000; - const MIN_PERCENT: number = 2; - const MIN_FREQ: number = 3; - for (let i = 0; i < arr.length; i++) { - let trackId: number; - // 若存在空位元素则进行删除处理 - if (arr[i] === undefined) { - arr.splice(i, 1); - i--; - continue; - } - if (arr[i].thread?.indexOf('P') !== -1) { - trackId = Number(arr[i].thread?.slice(1)!); - arr[i].thread = `${Utils.getInstance().getProcessMap(traceId).get(trackId) || 'Process'} ${trackId}`; - } else if (arr[i].thread === 'summary data') { - } else { - trackId = Number(arr[i].thread!.split('_')[1]); - arr[i].thread = `${Utils.getInstance().getThreadMap(traceId).get(trackId) || 'Thread'} ${trackId}`; - } - if (arr[i].cpu < 0) { - // @ts-ignore - arr[i].cpu = ''; - } - // @ts-ignore - if (arr[i].frequency < 0) { - arr[i].frequency = ''; - } - // @ts-ignore - arr[i].percent = arr[i].percent.toFixed(MIN_PERCENT); - // @ts-ignore - arr[i].dur = (arr[i].dur / TIME_MUTIPLE).toFixed(MIN_FREQ); - // @ts-ignore - arr[i].consumption = (arr[i].consumption / CONS_MUTIPLE).toFixed(MIN_FREQ); - if (arr[i].frequency !== '') { - if (arr[i].frequency === 'unknown') { - arr[i].frequency = 'unknown'; - } else { - arr[i].frequency = Number(arr[i].frequency) / FREQ_MUTIPLE; - } - } - this.fixedDeal(arr[i].children!, traceId); - } + const comPower = + SpSegmentationChart.freqInfoMapData.size > 0 + ? SpSegmentationChart.freqInfoMapData + : undefined; + const args = { + runData: runningResult, + cpuFreqData: cpuFreqData, + leftNs: LEFT_TIME, + rightNs: RIGHT_TIME, + cpuArray: cpuArray, + comPower: comPower, + }; + TabPaneFreqUsage.element.worker!.postMessage(args); + TabPaneFreqUsage.element.worker!.onmessage = (event: MessageEvent): void => { + let resultArr: Array = event.data; + TabPaneFreqUsage.element.fixedDeal(resultArr, threadStatesParam.traceId); + TabPaneFreqUsage.element.threadClick(resultArr); + TabPaneFreqUsage.element.threadStatesTbl!.recycleDataSource = resultArr; + TabPaneFreqUsage.element.threadStatesTbl!.loading = false; + }; } /** * 表头点击事件 */ private threadClick(data: Array): void { - let labels = this.threadStatesTbl?.shadowRoot?.querySelector('.th > .td')!.querySelectorAll('label'); + let labels = this.threadStatesTbl?.shadowRoot + ?.querySelector(".th > .td")! + .querySelectorAll("label"); if (labels) { for (let i = 0; i < labels.length; i++) { let label = labels[i].innerHTML; - labels[i].addEventListener('click', (e) => { - if (label.includes('Process') && i === 0) { + labels[i].addEventListener("click", (e) => { + if (label.includes("Process") && i === 0) { this.threadStatesTbl!.setStatus(data, false); - this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); - } else if (label.includes('Thread') && i === 1) { + this.threadStatesTbl!.recycleDs = + this.threadStatesTbl!.meauseTreeRowElement( + data, + RedrawTreeForm.Retract + ); + } else if (label.includes("Thread") && i === 1) { for (let item of data) { // @ts-ignore item.status = true; @@ -159,421 +138,130 @@ export class TabPaneFreqUsage extends BaseElement { this.threadStatesTbl!.setStatus(item.children, false); } } - this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); - } else if (label.includes('CPU') && i === 2) { + this.threadStatesTbl!.recycleDs = + this.threadStatesTbl!.meauseTreeRowElement( + data, + RedrawTreeForm.Retract + ); + } else if (label.includes("CPU") && i === 2) { this.threadStatesTbl!.setStatus(data, true); - this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Expand); + this.threadStatesTbl!.recycleDs = + this.threadStatesTbl!.meauseTreeRowElement( + data, + RedrawTreeForm.Expand + ); } }); } } } - /** - * - * @param arr 待整理的数组,会经过递归取到最底层的数据 - */ - recursion(arr: Array): void { - for (let idx = 0; idx < arr.length; idx++) { - if (arr[idx].cpu === -1) { - this.result.push(arr[idx]); - } - if (arr[idx].children) { - this.recursion(arr[idx].children!); - } else { - this.result.push(arr[idx]); - } - } - } - initElements(): void { - this.threadStatesTbl = this.shadowRoot?.querySelector('#tb-running-percent'); + this.threadStatesTbl = this.shadowRoot?.querySelector( + "#tb-running-percent" + ); + //开启一个线程计算busyTime + this.worker = new Worker( + new URL("../../../../database/TabPaneFreqUsageWorker", import.meta.url) + ); + TabPaneFreqUsage.element = this; } + connectedCallback(): void { super.connectedCallback(); - resizeObserver(this.parentElement!, this.threadStatesTbl!); + resizeObserver(this.parentElement!, this.threadStatesTbl!, 20); } + initHtml(): string { return ` - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + `; } -} - -/** - * - * @param runData 数据库查询上来的running数据,此函数会将数据整理成map结构,分组规则:'pid_tid'为键,running数据数字为值 - * @returns 返回map对象及所有running数据的dur和,后续会依此计算百分比 - */ -function orgnazitionMap( - runData: Array, - cpuFreqData: Array, - args: { - leftNs: number, - rightNs: number, - cpuArray: number[] - } -): Array { - let result: Map> = new Map(); - let sum: number = 0; - // 循环分组 - for (let i = 0; i < runData.length; i++) { - let mapKey: string = runData[i].pid + '_' + runData[i].tid; - // 该running数据若在map对象中不包含其'pid_tid'构成的键,则新加key-value值 - if (!result.has(mapKey)) { - result.set(mapKey, new Array()); - } - // 整理左右边界数据问题, 因为涉及多线程,所以必须放在循环里 - if (runData[i].ts < args.leftNs && runData[i].ts + runData[i].dur > args.leftNs) { - runData[i].dur = runData[i].ts + runData[i].dur - args.leftNs; - runData[i].ts = args.leftNs; - } - if (runData[i].ts + runData[i].dur > args.rightNs) { - runData[i].dur = args.rightNs - runData[i].ts; - } - // 特殊处理数据表中dur为负值的情况 - if (runData[i].dur < 0) { - runData[i].dur = 0; - } - // 分组整理数据 - result.get(mapKey)?.push({ - pid: runData[i].pid, - tid: runData[i].tid, - cpu: runData[i].cpu, - dur: runData[i].dur, - ts: runData[i].ts, - }); - sum += runData[i].dur; - } - return dealCpuFreqData(cpuFreqData, result, sum, args.cpuArray); -} - -/** - * - * @param cpuFreqData cpu频点数据的数组 - * @param result running数据的map对象 - * @param sum running数据的时间和 - * @returns 返回cpu频点数据map,'pid_tid'为键,频点算力值数据的数组为值 - */ -function dealCpuFreqData( - cpuFreqData: Array, - result: Map>, - sum: number, - cpuList: number[] -): Array { - let runningFreqData: Map> = new Map(); - result.forEach((item, key) => { - let resultList: Array = new Array(); - for (let i = 0; i < item.length; i++) { - for (let j = 0; j < cpuFreqData.length; j++) { - let flag: number; - if (item[i].cpu === cpuFreqData[j].cpu) { - // 当running状态数据的开始时间大于频点数据开始时间,小于频点结束时间。且running数据的持续时间小于频点结束时间减去running数据开始时间的差值的情况 - if ( - item[i].ts > cpuFreqData[j].ts && - item[i].ts < cpuFreqData[j].ts + cpuFreqData[j].dur && - item[i].dur < cpuFreqData[j].ts + cpuFreqData[j].dur - item[i].ts - ) { - resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 1))!); - item.splice(i, 1); - i--; - break; - } - if ( - item[i].ts > cpuFreqData[j].ts && - item[i].ts < cpuFreqData[j].ts + cpuFreqData[j].dur && - item[i].dur >= cpuFreqData[j].ts + cpuFreqData[j].dur - item[i].ts - ) { - // 当running状态数据的开始时间大于频点数据开始时间,小于频点结束时间。且running数据的持续时间大于等于频点结束时间减去running数据开始时间的差值的情况 - resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 2))!); - } - // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间大于频点开始时间。且running数据的持续时间减去频点数据开始时间的差值小于频点数据持续时间的情况 - if ( - item[i].ts <= cpuFreqData[j].ts && - item[i].ts + item[i].dur > cpuFreqData[j].ts && - item[i].dur + item[i].ts - cpuFreqData[j].ts < cpuFreqData[j].dur - ) { - resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 3))!); - item.splice(i, 1); - i--; - break; - } - if ( - item[i].ts <= cpuFreqData[j].ts && - item[i].ts + item[i].dur > cpuFreqData[j].ts && - item[i].dur + item[i].ts - cpuFreqData[j].ts >= cpuFreqData[j].dur - ) { - // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间大于频点开始时间。且running数据的持续时间减去频点数据开始时间的差值大于等于频点数据持续时间的情况 - resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 4))!); - } - if (item[i].ts <= cpuFreqData[j].ts && item[i].ts + item[i].dur <= cpuFreqData[j].ts) { - // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间小于等于频点开始时间的情况 - resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 5))!); - item.splice(i, 1); - i--; - break; - } - } else { - if (!cpuList.includes(item[i].cpu)) { - resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 5))!); - item.splice(i, 1); - i--; - break; - } - } - } - } - runningFreqData.set(key, mergeSameData(resultList)); - }); - return dealTree(runningFreqData); -} - -/** - * - * @param item running数据 - * @param cpuFreqData 频点数据 - * @param sum running总和 - * @param flag 标志位,根据不同值返回不同结果 - * @returns 返回新的对象 - */ -function returnObj( - item: RunningData, - cpuFreqData: CpuFreqData, - sum: number, - flag: number -): RunningFreqData | undefined { - const PERCENT: number = 100; - const consumption: number = ( - SpSegmentationChart.freqInfoMapData.size > 0 - ? SpSegmentationChart.freqInfoMapData.get(item.cpu)?.get(cpuFreqData.value) - : cpuFreqData.value - )!; - let result: RunningFreqData | undefined; - switch (flag) { - case 1: - result = { - thread: item.pid + '_' + item.tid, - consumption: consumption * item.dur, - cpu: item.cpu, - frequency: cpuFreqData.value, - dur: item.dur, - percent: (item.dur / sum) * PERCENT, - }; - case 2: - result = { - thread: item.pid + '_' + item.tid, - consumption: consumption * (cpuFreqData.ts + cpuFreqData.dur - item.ts), - cpu: item.cpu, - frequency: cpuFreqData.value, - dur: cpuFreqData.ts + cpuFreqData.dur - item.ts, - percent: ((cpuFreqData.ts + cpuFreqData.dur - item.ts) / sum) * PERCENT, - }; - case 3: - result = { - thread: item.pid + '_' + item.tid, - consumption: consumption * (item.dur + item.ts - cpuFreqData.ts), - cpu: item.cpu, - frequency: cpuFreqData.value, - dur: item.dur + item.ts - cpuFreqData.ts, - percent: ((item.dur + item.ts - cpuFreqData.ts) / sum) * PERCENT, - }; - case 4: - result = { - thread: item.pid + '_' + item.tid, - consumption: consumption * cpuFreqData.dur, - cpu: item.cpu, - frequency: cpuFreqData.value, - dur: cpuFreqData.dur, - percent: (cpuFreqData.dur / sum) * PERCENT, - }; - case 5: - result = { - thread: item.pid + '_' + item.tid, - consumption: 0, - cpu: item.cpu, - frequency: 'unknown', - dur: item.dur, - percent: (item.dur / sum) * PERCENT, - }; - } - return result; -} -/** - * - * @param resultList 单线程内running数据与cpu频点数据整合成的数组 - */ -function mergeSameData(resultList: Array): Array { - let cpuFreqArr: Array = []; - let cpuArr: Array = []; - //合并同一线程内,当运行所在cpu和频点相同时,dur及percent进行累加求和 - for (let i = 0; i < resultList.length; i++) { - if (!cpuArr.includes(resultList[i].cpu)) { - cpuArr.push(resultList[i].cpu); - cpuFreqArr.push(creatNewObj(resultList[i].cpu)); + /** + * 递归整理数据小数位 + */ + fixedDeal(arr: Array, traceId?: string | null): void { + if (arr == undefined) { + return; } - for (let j = i + 1; j < resultList.length; j++) { - if (resultList[i].cpu === resultList[j].cpu && resultList[i].frequency === resultList[j].frequency) { - resultList[i].dur += resultList[j].dur; - resultList[i].percent += resultList[j].percent; - resultList[i].consumption += resultList[j].consumption; - resultList.splice(j, 1); - j--; + const TIME_MUTIPLE: number = 1000000; + // KHz->MHz * ns->ms + const CONS_MUTIPLE: number = 1000000000; + const MIN_PERCENT: number = 2; + const MIN_FREQ: number = 3; + const MIN_POWER: number = 6; + for (let i = 0; i < arr.length; i++) { + let trackId: number; + // 若存在空位元素则进行删除处理 + if (arr[i] === undefined) { + arr.splice(i, 1); + i--; + continue; } - } - cpuFreqArr.find(function (item) { - if (item.cpu === resultList[i].cpu) { - item.children?.push(resultList[i]); - item.children?.sort((a, b) => b.consumption - a.consumption); - item.dur += resultList[i].dur; - item.percent += resultList[i].percent; - item.consumption += resultList[i].consumption; - item.thread = resultList[i].thread; + if (arr[i].thread?.indexOf("P") !== -1) { + trackId = Number(arr[i].thread?.slice(1)!); + arr[i].thread = `${Utils.getInstance().getProcessMap(traceId).get(trackId) || "Process"} ${trackId}`; + } else if (arr[i].thread === "summary data") { + } else { + trackId = Number(arr[i].thread!.split("_")[1]); + arr[i].thread = `${Utils.getInstance().getThreadMap(traceId).get(trackId) || "Thread"} ${trackId}`; } - }); - } - cpuFreqArr.sort((a, b) => a.cpu - b.cpu); - return cpuFreqArr; -} - -/** - * - * @param params cpu层级的数据 - * @returns 整理好的进程级数据 - */ -function dealTree(params: Map>): Array { - let result: Array = []; - params.forEach((item, key) => { - let process: RunningFreqData = creatNewObj(-1, false); - let thread: RunningFreqData = creatNewObj(-2); - for (let i = 0; i < item.length; i++) { - thread.children?.push(item[i]); - thread.dur += item[i].dur; - thread.percent += item[i].percent; - thread.consumption += item[i].consumption; - thread.thread = item[i].thread; - } - process.children?.push(thread); - process.dur += thread.dur; - process.percent += thread.percent; - process.consumption += thread.consumption; - process.thread = process.thread! + key.split('_')[0]; - result.push(process); - }); - for (let i = 0; i < result.length; i++) { - for (let j = i + 1; j < result.length; j++) { - if (result[i].thread === result[j].thread) { - result[i].children?.push(result[j].children![0]); - result[i].dur += result[j].dur; - result[i].percent += result[j].percent; - result[i].consumption += result[j].consumption; - result.splice(j, 1); - j--; + if (arr[i].cpu < 0) { + // @ts-ignore + arr[i].cpu = ""; } - } - } - return result; -} - -/** - * - * @param cpu 根据cpu值创建层级结构,cpu < 0为线程、进程层级,其余为cpu层级 - * @returns - */ -function creatNewObj(cpu: number, flag: boolean = true): RunningFreqData { - return { - thread: flag ? '' : 'P', - consumption: 0, - cpu: cpu, - frequency: -1, - dur: 0, - percent: 0, - children: [], - }; -} - -/** - * - * @param arr 需要整理汇总的频点级数据 - * @returns 返回一个total->cpu->频点的三级树结构数组 - */ -function fixTotal(arr: Array): Array { - let result: Array = []; - let flag: number = -1; - // 数据入参的情况是,第一条为进程数据,其后是该进程下所有线程的数据。以进程数据做分割 - for (let i = 0; i < arr.length; i++) { - // 判断如果是进程数据,则将其children的数组清空,并以其作为最顶层数据 - if (arr[i].thread?.indexOf('P') !== -1) { - arr[i].children = []; - arr[i].thread = arr[i].thread + '-summary data'; - result.push(arr[i]); - // 标志判定当前数组的长度,也可用.length判断 - flag++; - } else { - // 非进程数据会进入到else中,去判断当前线程数据的cpu分组是否存在,不存在则进行创建 - if (result[flag].children![arr[i].cpu] === undefined) { - result[flag].children![arr[i].cpu] = { - thread: 'summary data', - consumption: 0, - cpu: arr[i].cpu, - frequency: -1, - dur: 0, - percent: 0, - children: [], - }; + // @ts-ignore + if (arr[i].frequency < 0) { + arr[i].frequency = ""; } - // 每有一条数据要放到cpu分组下时,则将该cpu分组的各项数据累和 - result[flag].children![arr[i].cpu].consumption += arr[i].consumption; - result[flag].children![arr[i].cpu].dur += arr[i].dur; - result[flag].children![arr[i].cpu].percent += arr[i].percent; - // 查找当前cpu分组下是否存在与当前数据的频点相同的数据,返回相同数据的索引值 - let index: number = result[flag].children![arr[i].cpu].children?.findIndex( - (item) => item.frequency === arr[i].frequency - )!; - // 若存在相同频点的数据,则进行合并,不同直接push - if (index === -1) { - arr[i].thread = 'summary data'; - result[flag].children![arr[i].cpu].children?.push(arr[i]); + if (!arr[i].cpuload) { + // @ts-ignore + arr[i].cpuload = '0.000000'; } else { - result[flag].children![arr[i].cpu].children![index].consumption += arr[i].consumption; - result[flag].children![arr[i].cpu].children![index].dur += arr[i].dur; - result[flag].children![arr[i].cpu].children![index].percent += arr[i].percent; + // @ts-ignore + arr[i].cpuload = arr[i].cpuload.toFixed(MIN_POWER); + } + // @ts-ignore + arr[i].percent = arr[i].percent.toFixed(MIN_PERCENT); + // @ts-ignore + arr[i].dur = (arr[i].dur / TIME_MUTIPLE).toFixed(MIN_FREQ); + // @ts-ignore + arr[i].consumption = (arr[i].consumption / CONS_MUTIPLE).toFixed(MIN_FREQ); + // @ts-ignore + arr[i].consumpower = (arr[i].consumpower / TIME_MUTIPLE).toFixed(MIN_FREQ); + if (arr[i].frequency !== "") { + if (arr[i].frequency === "unknown") { + arr[i].frequency = "unknown"; + } else { + arr[i].frequency = arr[i].frequency; + } } + this.fixedDeal(arr[i].children!, traceId); } } - return result; -} - -/** - * - * @param arr1 前次整理好的区分线程的数据 - * @param arr2 不区分线程的Total数据 - */ -function mergeTotal(arr1: Array, arr2: Array): void { - for (let i = 0; i < arr1.length; i++) { - const num: number = arr2.findIndex((item) => item.thread?.includes(arr1[i].thread!)); - arr2[num].thread = 'summary data'; - arr1[i].children?.unshift(arr2[num]); - arr2.splice(num, 1); - } } diff --git a/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsageConfig.ts b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsageConfig.ts index c1b929cc34b3ebcc80220be41086b8f1a73479d9..02df4c38c446cd0484d1a8ad771f94c34e5cb725 100644 --- a/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsageConfig.ts +++ b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsageConfig.ts @@ -111,6 +111,8 @@ export interface RunningFreqData { consumption: number; frequency: number | string; percent: number; + consumpower: number; + cpuload: number; children?: Array; } diff --git a/ide/src/trace/database/TabPaneFreqUsageWorker.ts b/ide/src/trace/database/TabPaneFreqUsageWorker.ts new file mode 100644 index 0000000000000000000000000000000000000000..6ec42caf282ba2ef94d867b2e9f98fed1981ebb8 --- /dev/null +++ b/ide/src/trace/database/TabPaneFreqUsageWorker.ts @@ -0,0 +1,463 @@ +/* + * 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 { type CpuFreqData, type RunningFreqData, type RunningData } from '../component/trace/sheet/frequsage/TabPaneFreqUsageConfig'; + +let comPower = new Map>(); +let resultArray: Array = []; +let timeZones: number = 0; +let maxCommpuPower: number = 0; + +/** + * + * @param args.runData 数据库查询上来的running数据,此函数会将数据整理成map结构,分组规则:'pid_tid'为键,running数据数字为值 + * @returns 返回map对象及所有running数据的dur和,后续会依此计算百分比 + */ +function orgnazitionMap( + args: { + runData: Array; + cpuFreqData: Array; + leftNs: number; + rightNs: number; + cpuArray: number[]; + } +): Array { + let result: Map> = new Map(); + let sum: number = 0; + // 循环分组 + for (let i = 0; i < args.runData.length; i++) { + let mapKey: string = args.runData[i].pid + "_" + args.runData[i].tid; + // 该running数据若在map对象中不包含其'pid_tid'构成的键,则新加key-value值 + if (!result.has(mapKey)) { + result.set(mapKey, new Array()); + } + // 整理左右边界数据问题, 因为涉及多线程,所以必须放在循环里 + if ( + args.runData[i].ts < args.leftNs && + args.runData[i].ts + args.runData[i].dur > args.leftNs + ) { + args.runData[i].dur = args.runData[i].ts + args.runData[i].dur - args.leftNs; + args.runData[i].ts = args.leftNs; + } + if (args.runData[i].ts + args.runData[i].dur > args.rightNs) { + args.runData[i].dur = args.rightNs - args.runData[i].ts; + } + // 特殊处理数据表中dur为负值的情况 + if (args.runData[i].dur < 0) { + args.runData[i].dur = 0; + } + // 分组整理数据 + result.get(mapKey)?.push({ + pid: args.runData[i].pid, + tid: args.runData[i].tid, + cpu: args.runData[i].cpu, + dur: args.runData[i].dur, + ts: args.runData[i].ts, + }); + sum += args.runData[i].dur; + } + return dealCpuFreqData(args.cpuFreqData, result, sum, args.cpuArray); +} + +/** + * + * @param cpuFreqData cpu频点数据的数组 + * @param result running数据的map对象 + * @param sum running数据的时间和 + * @returns 返回cpu频点数据map,'pid_tid'为键,频点算力值数据的数组为值 + */ +function dealCpuFreqData( + cpuFreqData: Array, + result: Map>, + sum: number, + cpuList: number[] +): Array { + let runningFreqData: Map> = new Map(); + result.forEach((item, key) => { + let resultList: Array = new Array(); + for (let i = 0; i < item.length; i++) { + for (let j = 0; j < cpuFreqData.length; j++) { + let flag: number; + if (item[i].cpu === cpuFreqData[j].cpu) { + // 当running状态数据的开始时间大于频点数据开始时间,小于频点结束时间。且running数据的持续时间小于频点结束时间减去running数据开始时间的差值的情况 + if ( + item[i].ts > cpuFreqData[j].ts && + item[i].ts < cpuFreqData[j].ts + cpuFreqData[j].dur && + item[i].dur < cpuFreqData[j].ts + cpuFreqData[j].dur - item[i].ts + ) { + resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 1))!); + item.splice(i, 1); + i--; + break; + } + if ( + item[i].ts > cpuFreqData[j].ts && + item[i].ts < cpuFreqData[j].ts + cpuFreqData[j].dur && + item[i].dur >= cpuFreqData[j].ts + cpuFreqData[j].dur - item[i].ts + ) { + // 当running状态数据的开始时间大于频点数据开始时间,小于频点结束时间。且running数据的持续时间大于等于频点结束时间减去running数据开始时间的差值的情况 + resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 2))!); + } + // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间大于频点开始时间。且running数据的持续时间减去频点数据开始时间的差值小于频点数据持续时间的情况 + if ( + item[i].ts <= cpuFreqData[j].ts && + item[i].ts + item[i].dur > cpuFreqData[j].ts && + item[i].dur + item[i].ts - cpuFreqData[j].ts < cpuFreqData[j].dur + ) { + resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 3))!); + item.splice(i, 1); + i--; + break; + } + if ( + item[i].ts <= cpuFreqData[j].ts && + item[i].ts + item[i].dur > cpuFreqData[j].ts && + item[i].dur + item[i].ts - cpuFreqData[j].ts >= cpuFreqData[j].dur + ) { + // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间大于频点开始时间。且running数据的持续时间减去频点数据开始时间的差值大于等于频点数据持续时间的情况 + resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 4))!); + } + if ( + item[i].ts <= cpuFreqData[j].ts && + item[i].ts + item[i].dur <= cpuFreqData[j].ts + ) { + // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间小于等于频点开始时间的情况 + resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 5))!); + item.splice(i, 1); + i--; + break; + } + } else { + if (!cpuList.includes(item[i].cpu)) { + resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 5))!); + item.splice(i, 1); + i--; + break; + } + } + } + } + runningFreqData.set(key, mergeSameData(resultList)); + }); + return dealTree(runningFreqData); +} + +/** + * + * @param item running数据 + * @param cpuFreqData 频点数据 + * @param sum running总和 + * @param flag 标志位,根据不同值返回不同结果 + * @returns 返回新的对象 + */ +function returnObj( + item: RunningData, + cpuFreqData: CpuFreqData, + sum: number, + flag: number +): RunningFreqData | undefined { + const PERCENT: number = 100; + const FREQ_MUTIPLE: number = 1000; + const computorPower: number = comPower ? comPower.get(item.cpu)?.get(cpuFreqData.value)! : 0; + switch (flag) { + case 1: + return { + thread: item.pid + "_" + item.tid, + consumption: cpuFreqData.value * item.dur, + cpu: item.cpu, + frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE, + dur: item.dur, + percent: (item.dur / sum) * PERCENT, + consumpower: computorPower * item.dur, + cpuload: (computorPower * item.dur) / (timeZones * maxCommpuPower) * PERCENT + }; + case 2: + return { + thread: item.pid + "_" + item.tid, + consumption: cpuFreqData.value * (cpuFreqData.ts + cpuFreqData.dur - item.ts), + cpu: item.cpu, + frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE, + dur: cpuFreqData.ts + cpuFreqData.dur - item.ts, + percent: ((cpuFreqData.ts + cpuFreqData.dur - item.ts) / sum) * PERCENT, + consumpower: computorPower * (cpuFreqData.ts + cpuFreqData.dur - item.ts), + cpuload: (computorPower * (cpuFreqData.ts + cpuFreqData.dur - item.ts)) / (timeZones * maxCommpuPower) * PERCENT + }; + case 3: + return { + thread: item.pid + "_" + item.tid, + consumption: cpuFreqData.value * (item.dur + item.ts - cpuFreqData.ts), + cpu: item.cpu, + frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE, + dur: item.dur + item.ts - cpuFreqData.ts, + percent: ((item.dur + item.ts - cpuFreqData.ts) / sum) * PERCENT, + consumpower: computorPower * (item.dur + item.ts - cpuFreqData.ts), + cpuload: (computorPower * (item.dur + item.ts - cpuFreqData.ts)) / (timeZones * maxCommpuPower) * PERCENT + }; + case 4: + return { + thread: item.pid + "_" + item.tid, + consumption: cpuFreqData.value * cpuFreqData.dur, + cpu: item.cpu, + frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE, + dur: cpuFreqData.dur, + percent: (cpuFreqData.dur / sum) * PERCENT, + consumpower: computorPower * cpuFreqData.dur, + cpuload: (computorPower * cpuFreqData.dur) / (timeZones * maxCommpuPower) * PERCENT + }; + case 5: + return { + thread: item.pid + "_" + item.tid, + consumption: 0, + cpu: item.cpu, + frequency: "unknown", + dur: item.dur, + percent: (item.dur / sum) * PERCENT, + consumpower: 0, + cpuload: 0 + }; + } +} + +/** + * + * @param resultList 单线程内running数据与cpu频点数据整合成的数组 + */ +function mergeSameData( + resultList: Array +): Array { + let cpuFreqArr: Array = []; + let cpuArr: Array = []; + //合并同一线程内,当运行所在cpu和频点相同时,dur及percent进行累加求和 + for (let i = 0; i < resultList.length; i++) { + if (!cpuArr.includes(resultList[i].cpu)) { + cpuArr.push(resultList[i].cpu); + cpuFreqArr.push(creatNewObj(resultList[i].cpu)); + } + for (let j = i + 1; j < resultList.length; j++) { + if ( + resultList[i].cpu === resultList[j].cpu && + resultList[i].frequency === resultList[j].frequency + ) { + resultList[i].dur += resultList[j].dur; + resultList[i].percent += resultList[j].percent; + resultList[i].consumption += resultList[j].consumption; + resultList[i].consumpower += resultList[j].consumpower; + resultList[i].cpuload += resultList[j].cpuload; + resultList.splice(j, 1); + j--; + } + } + cpuFreqArr.find(function (item) { + if (item.cpu === resultList[i].cpu) { + item.children?.push(resultList[i]); + item.children?.sort((a, b) => b.consumption - a.consumption); + item.dur += resultList[i].dur; + item.percent += resultList[i].percent; + item.consumption += resultList[i].consumption; + item.consumpower += resultList[i].consumpower; + item.cpuload += resultList[i].cpuload; + item.thread = resultList[i].thread; + } + }); + } + cpuFreqArr.sort((a, b) => a.cpu - b.cpu); + return cpuFreqArr; +} + +/** + * + * @param params cpu层级的数据 + * @returns 整理好的进程级数据 + */ +function dealTree( + params: Map> +): Array { + let result: Array = []; + params.forEach((item, key) => { + let process: RunningFreqData = creatNewObj(-1, false); + let thread: RunningFreqData = creatNewObj(-2); + for (let i = 0; i < item.length; i++) { + thread.children?.push(item[i]); + thread.dur += item[i].dur; + thread.percent += item[i].percent; + thread.consumption += item[i].consumption; + thread.consumpower += item[i].consumpower; + thread.cpuload += item[i].cpuload; + thread.thread = item[i].thread; + } + process.children?.push(thread); + process.dur += thread.dur; + process.percent += thread.percent; + process.consumption += thread.consumption; + process.consumpower += thread.consumpower; + process.cpuload += thread.cpuload; + process.thread = process.thread! + key.split("_")[0]; + result.push(process); + }); + for (let i = 0; i < result.length; i++) { + for (let j = i + 1; j < result.length; j++) { + if (result[i].thread === result[j].thread) { + result[i].children?.push(result[j].children![0]); + result[i].dur += result[j].dur; + result[i].percent += result[j].percent; + result[i].consumption += result[j].consumption; + result[i].consumpower += result[j].consumpower; + result[i].cpuload += result[j].cpuload; + result.splice(j, 1); + j--; + } + } + } + return result; +} + +/** + * + * @param cpu 根据cpu值创建层级结构,cpu < 0为线程、进程层级,其余为cpu层级 + * @returns + */ +function creatNewObj(cpu: number, flag: boolean = true): RunningFreqData { + return { + thread: flag ? "" : "P", + consumption: 0, + cpu: cpu, + frequency: -1, + dur: 0, + percent: 0, + children: [], + consumpower: 0, + cpuload: 0 + }; +} + +/** + * + * @param arr 需要整理汇总的频点级数据 + * @returns 返回一个total->cpu->频点的三级树结构数组 + */ +function fixTotal(arr: Array): Array { + let result: Array = []; + let flag: number = -1; + // 数据入参的情况是,第一条为进程数据,其后是该进程下所有线程的数据。以进程数据做分割 + for (let i = 0; i < arr.length; i++) { + // 判断如果是进程数据,则将其children的数组清空,并以其作为最顶层数据 + if (arr[i].thread?.indexOf("P") !== -1) { + arr[i].children = []; + arr[i].thread = arr[i].thread + "-summary data"; + result.push(arr[i]); + // 标志判定当前数组的长度,也可用.length判断 + flag++; + } else { + // 非进程数据会进入到else中,去判断当前线程数据的cpu分组是否存在,不存在则进行创建 + if (result[flag].children![arr[i].cpu] === undefined) { + result[flag].children![arr[i].cpu] = { + thread: "summary data", + consumption: 0, + cpu: arr[i].cpu, + frequency: -1, + dur: 0, + percent: 0, + children: [], + consumpower: 0, + cpuload: 0 + }; + } + // 每有一条数据要放到cpu分组下时,则将该cpu分组的各项数据累和 + result[flag].children![arr[i].cpu].consumption += arr[i].consumption; + result[flag].children![arr[i].cpu].consumpower += arr[i].consumpower; + result[flag].children![arr[i].cpu].cpuload += arr[i].cpuload; + result[flag].children![arr[i].cpu].dur += arr[i].dur; + result[flag].children![arr[i].cpu].percent += arr[i].percent; + // 查找当前cpu分组下是否存在与当前数据的频点相同的数据,返回相同数据的索引值 + let index: number = result[flag].children![ + arr[i].cpu + ].children?.findIndex((item) => item.frequency === arr[i].frequency)!; + // 若存在相同频点的数据,则进行合并,不同直接push + if (index === -1) { + arr[i].thread = "summary data"; + result[flag].children![arr[i].cpu].children?.push(arr[i]); + } else { + result[flag].children![arr[i].cpu].children![index].consumption += arr[i].consumption; + result[flag].children![arr[i].cpu].children![index].consumpower += arr[i].consumpower; + result[flag].children![arr[i].cpu].children![index].dur += arr[i].dur; + result[flag].children![arr[i].cpu].children![index].percent += arr[i].percent; + result[flag].children![arr[i].cpu].children![index].cpuload += arr[i].cpuload; + } + } + } + return result; +} + +/** + * + * @param arr1 前次整理好的区分线程的数据 + * @param arr2 不区分线程的Total数据 + */ +function mergeTotal( + arr1: Array, + arr2: Array +): void { + for (let i = 0; i < arr1.length; i++) { + const num: number = arr2.findIndex((item) => + item.thread?.includes(arr1[i].thread!) + ); + arr2[num].thread = "summary data"; + arr1[i].children?.unshift(arr2[num]); + arr2.splice(num, 1); + } +} + + + /** + * + * @param arr 待整理的数组,会经过递归取到最底层的数据 + */ +function recursion(arr: Array): void { + for (let idx = 0; idx < arr.length; idx++) { + if (arr[idx].cpu === -1) { + resultArray.push(arr[idx]); + } + if (arr[idx].children) { + recursion(arr[idx].children!); + } else { + resultArray.push(arr[idx]); + } + } + } + +self.onmessage = (e: MessageEvent): void => { + comPower = e.data.comPower; + resultArray = []; + timeZones = e.data.rightNs - e.data.leftNs; + maxCommpuPower = 0; + if (comPower) { + comPower.forEach(item => { + let maxFreq = 0; + let commpuPower = 0; + for (const i of item.entries()) { + if (i[0] > maxFreq) { + maxFreq = i[0]; + commpuPower = i[1]; + } + } + maxCommpuPower += commpuPower; + }); + } + let result = orgnazitionMap(e.data); + recursion(result); + resultArray = JSON.parse(JSON.stringify(resultArray)); + mergeTotal(result, fixTotal(resultArray)); + self.postMessage(result); +}; \ No newline at end of file