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/schedulingAnalysis/SpSchedulingAnalysis.ts b/ide/src/trace/component/schedulingAnalysis/SpSchedulingAnalysis.ts index 4257627db0dda5ad8c013dd9819442b4ffe9e30b..e26a142988457f75708759eeeadb402859cef2e2 100644 --- a/ide/src/trace/component/schedulingAnalysis/SpSchedulingAnalysis.ts +++ b/ide/src/trace/component/schedulingAnalysis/SpSchedulingAnalysis.ts @@ -16,6 +16,7 @@ import { BaseElement, element } from '../../../base-ui/BaseElement'; import './TabThreadAnalysis'; import './TabCpuAnalysis'; +import './processAnalysis/TabProcessAnalysis'; import { TabCpuAnalysis } from './TabCpuAnalysis'; import { TabThreadAnalysis } from './TabThreadAnalysis'; import { LitTabs } from '../../../base-ui/tabs/lit-tabs'; @@ -23,6 +24,7 @@ import { CheckCpuSetting } from './CheckCpuSetting'; import { Top20FrequencyThread } from './Top20FrequencyThread'; import { procedurePool } from '../../database/Procedure'; import { Utils } from '../trace/base/Utils'; +import { TabProcessAnalysis } from './processAnalysis/TabProcessAnalysis'; @element('sp-scheduling-analysis') export class SpSchedulingAnalysis extends BaseElement { @@ -34,11 +36,13 @@ export class SpSchedulingAnalysis extends BaseElement { private tabs: LitTabs | null | undefined; private tabCpuAnalysis: TabCpuAnalysis | null | undefined; private tabThreadAnalysis: TabThreadAnalysis | null | undefined; + private tabProcessAnalysis: TabProcessAnalysis | null | undefined; initElements(): void { this.tabs = this.shadowRoot?.querySelector('#tabs'); this.tabCpuAnalysis = this.shadowRoot?.querySelector('#cpu-analysis'); this.tabThreadAnalysis = this.shadowRoot?.querySelector('#thread-analysis'); + this.tabProcessAnalysis = this.shadowRoot?.querySelector('#process-analysis'); } static resetCpu(): void { @@ -58,6 +62,7 @@ export class SpSchedulingAnalysis extends BaseElement { SpSchedulingAnalysis.cpuCount = Utils.getInstance().getWinCpuCount(); this.tabCpuAnalysis?.init(); this.tabThreadAnalysis?.init(); + this.tabProcessAnalysis?.init(); } } @@ -101,6 +106,9 @@ export class SpSchedulingAnalysis extends BaseElement { + + + `; diff --git a/ide/src/trace/component/schedulingAnalysis/processAnalysis/TabProcessAnalysis.ts b/ide/src/trace/component/schedulingAnalysis/processAnalysis/TabProcessAnalysis.ts new file mode 100644 index 0000000000000000000000000000000000000000..8498257d3309b0c18cf38b1719f1e4764bbc247b --- /dev/null +++ b/ide/src/trace/component/schedulingAnalysis/processAnalysis/TabProcessAnalysis.ts @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2022 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 { BaseElement, element } from '../../../../base-ui/BaseElement'; +import { SpStatisticsHttpUtil } from '../../../../statistics/util/SpStatisticsHttpUtil'; +import './Top10LongestRunTimeProcess'; +import './Top10ProcessSwitchCount'; +import { Top10LongestRunTimeProcess } from './Top10LongestRunTimeProcess'; +import { Top10ProcessSwitchCount } from './Top10ProcessSwitchCount'; + +@element("tab-process-analysis") +export class TabProcessAnalysis extends BaseElement { + private currentTabID: string | undefined; + private currentTab: BaseElement | undefined; + private btn1: HTMLDivElement | null | undefined; + private btn2: HTMLDivElement | null | undefined; + private Top10LongestRunTimeProcess: Top10LongestRunTimeProcess | undefined | null; + private Top10ProcessSwitchCount: Top10ProcessSwitchCount | undefined | null; + + /** + * 元素初始化,将html节点与内部变量进行绑定 + */ + initElements(): void { + this.btn1 = this.shadowRoot!.querySelector('#btn1'); + this.btn2 = this.shadowRoot!.querySelector('#btn2'); + this.Top10LongestRunTimeProcess = this.shadowRoot!.querySelector('#top10_process_runTime'); + this.Top10ProcessSwitchCount = this.shadowRoot!.querySelector('#top10_process_switchCount'); + this.btn1!.addEventListener('click', (event) => { + this.setClickTab(this.btn1!, this.Top10ProcessSwitchCount!); + }); + this.btn2!.addEventListener('click', (event) => { + this.setClickTab(this.btn2!, this.Top10LongestRunTimeProcess!); + }); + } + + /** + * 初始化操作,清空该标签页下所有面板数据,只有首次导入新trace后执行一次 + */ + init(): void { + this.Top10ProcessSwitchCount?.clearData(); + this.Top10LongestRunTimeProcess?.clearData(); + this.hideCurrentTab(); + this.currentTabID = undefined; + this.setClickTab(this.btn1!, this.Top10ProcessSwitchCount!); + } + + /** + * 隐藏当前面板,将按钮样式设置为未选中状态 + */ + hideCurrentTab(): void { + if (this.currentTabID) { + let clickTab = this.shadowRoot!.querySelector(`#${this.currentTabID}`); + if (clickTab) { + clickTab.className = 'tag_bt'; + } + } + if (this.currentTab) { + this.currentTab.style.display = 'none'; + } + } + + /** + * + * @param btn 当前点击的数据类型按钮 + * @param showContent 需要展示的面板对象 + */ + setClickTab(btn: HTMLDivElement, showContent: Top10ProcessSwitchCount | Top10LongestRunTimeProcess): void { + // 将前次点击的按钮样式设置为未选中样式状态 + if (this.currentTabID) { + let clickTab = this.shadowRoot!.querySelector(`#${this.currentTabID}`); + if (clickTab) { + clickTab.className = 'tag_bt'; + } + } + // 切换当前点击按钮的类名,应用点击样式 + btn.className = 'tab_click'; + // 切换记录的好的当前点击面板id,并设置其显隐 + if (btn.id !== this.currentTabID) { + this.currentTabID = btn.id; + if (this.currentTab) { + this.currentTab.style.display = 'none'; + } + this.currentTab = showContent; + showContent.style.display = 'inline'; + showContent.init(); + } + } + + /** + * 用于将元素节点挂载 + * @returns 返回字符串形式的元素节点 + */ + initHtml(): string { + return ` + +
+
Top10切换次数进程
+
Top10运行超长进程
+
+
+ + +
+ `; + } +} diff --git a/ide/src/trace/component/schedulingAnalysis/processAnalysis/Top10LongestRunTimeProcess.ts b/ide/src/trace/component/schedulingAnalysis/processAnalysis/Top10LongestRunTimeProcess.ts new file mode 100644 index 0000000000000000000000000000000000000000..586b9147f8f5cf3d3faf236466c3ea8c9a6f58f7 --- /dev/null +++ b/ide/src/trace/component/schedulingAnalysis/processAnalysis/Top10LongestRunTimeProcess.ts @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2022 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 { BaseElement, element } from "../../../../base-ui/BaseElement"; +import { LitTable } from '../../../../base-ui/table/lit-table'; +import { procedurePool } from '../../../database/Procedure'; +import { info } from '../../../../log/Log'; +import { TableNoData } from '../TableNoData'; +import '../TableNoData'; +import { LitProgressBar } from '../../../../base-ui/progress-bar/LitProgressBar'; +import '../../../../base-ui/progress-bar/LitProgressBar'; +import { LitChartColumn } from '../../../../base-ui/chart/column/LitChartColumn'; +import '../../../../base-ui/chart/column/LitChartColumn'; +import { Utils } from '../../trace/base/Utils'; + +@element("top10-longest-runtime-process") +export class Top10LongestRunTimeProcess extends BaseElement { + traceChange: boolean = false; + private processRunTimeTbl: LitTable | null | undefined; + private threadRunTimeTbl: LitTable | null | undefined; + private processRunTimeProgress: LitProgressBar | null | undefined; + private nodataPro: TableNoData | null | undefined; + private processRunTimeData: Array = []; + private threadRunTimeData: Array = []; + private processSwitchCountChart: LitChartColumn | null | undefined; + private threadSwitchCountChart: LitChartColumn | null | undefined; + private nodataThr: TableNoData | null | undefined; + private display_pro: HTMLDivElement | null | undefined; + private display_thr: HTMLDivElement | null | undefined; + private processId: number | undefined; + private display_flag: boolean = true; + private back: HTMLDivElement | null | undefined; + private processMap: Map = new Map(); + private threadMap: Map = new Map(); + + /** + * 初始化操作,若trace发生改变,将所有变量设置为默认值并重新请求数据。若trace未改变,跳出初始化 + */ + init() { + if (!this.traceChange) { + if (this.processRunTimeTbl!.recycleDataSource.length > 0) { + this.processRunTimeTbl?.reMeauseHeight(); + } + return; + } + this.traceChange = false; + this.processRunTimeProgress!.loading = true; + this.display_flag = true; + this.display_pro!.style.display = 'block'; + this.display_thr!.style.display = 'none'; + this.processMap = Utils.getInstance().getProcessMap(); + this.threadMap = Utils.getInstance().getThreadMap(); + this.queryLogicWorker( + "scheduling-Process Top10RunTime", + "query Process Top10 Run Time Analysis Time:", + this.callBack.bind(this) + ); + } + + /** + * 清除已存储数据 + */ + clearData() { + this.traceChange = true; + this.processSwitchCountChart!.dataSource = []; + this.processRunTimeTbl!.recycleDataSource = []; + this.threadSwitchCountChart!.dataSource = []; + this.threadRunTimeTbl!.recycleDataSource = []; + this.processRunTimeData = []; + this.threadRunTimeData = []; + this.processMap = new Map(); + this.threadMap = new Map(); + } + + /** + * 提交worker线程,进行数据库查询 + * @param option 操作的key值,用于找到并执行对应方法 + * @param log 日志打印内容 + * @param handler 结果回调函数 + * @param pid 需要查询某一进程下线程数据的进程id + */ + queryLogicWorker(option: string, log: string, handler: (res: Array) => void, pid?: number): void { + let processThreadCountTime = new Date().getTime(); + procedurePool.submitWithName('logic0', option, {pid: pid}, undefined, handler); + let durTime = new Date().getTime() - processThreadCountTime; + info(log, durTime); + } + + /** + * 提交worker线程,进行数据库查询 + * @param option 操作的key值,用于找到并执行对应方法 + * @param log 日志打印内容 + * @param handler 结果回调函数 + * @param pid 需要查询某一进程下线程数据的进程id + */ + organizationData(arr: Array): Array { + let result: Array = []; + for (let i = 0; i < arr.length; i++) { + const pStr: string | null = this.processMap.get(arr[i].pid!)!; + const tStr: string | null = this.threadMap.get(arr[i].tid!)!; + result.push({ + NO: i + 1, + pid: arr[i].pid || this.processId, + pName: pStr === null ? 'Process ' : pStr, + dur: arr[i].dur, + tid: arr[i].tid, + tName: tStr === null ? 'Thread ' : tStr + }); + } + return result; + } + + /** + * 提交线程后,结果返回后的回调函数 + * @param res 数据库查询结果 + */ + callBack(res: Array): void { + let result: Array = this.organizationData(res); + if (this.display_flag === true) { + this.processCallback(result); + } else { + this.threadCallback(result); + } + this.processRunTimeProgress!.loading = false; + } + + /** + * 大函数块拆解分为两部分,此部分为Top10进程数据 + * @param result 需要显示在表格中的数据 + */ + processCallback(result: Array): void { + this.nodataPro!.noData = result === undefined || result.length === 0; + this.processRunTimeTbl!.recycleDataSource = result; + this.processRunTimeTbl!.reMeauseHeight(); + this.processRunTimeData = result; + this.processSwitchCountChart!.config = { + data: result, + appendPadding: 10, + xField: 'pid', + yField: 'dur', + seriesField: 'size', + color: (a) => { + return '#0a59f7'; + }, + hoverHandler: (data) => { + if (data) { + this.processRunTimeTbl!.setCurrentHover(data); + } else { + this.processRunTimeTbl!.mouseOut(); + } + }, + tip: (obj) => { + return ` +
+
Process_Id:${ + // @ts-ignore + obj[0].obj.pid}
+
Process_Name:${ + // @ts-ignore + obj[0].obj.pName}
+
Run_Time:${ + // @ts-ignore + obj[0].obj.dur}
+
+ `; + }, + label: null, + }; + } + + /** + * 大函数块拆解分为两部分,此部分为Top10线程数据 + * @param result 需要显示在表格中的数据 + */ + threadCallback(result: Array):void { + this.nodataThr!.noData = result === undefined || result.length === 0; + this.threadRunTimeTbl!.recycleDataSource = result; + this.threadRunTimeTbl!.reMeauseHeight(); + this.threadRunTimeData = result; + this.threadSwitchCountChart!.config = { + data: result, + appendPadding: 10, + xField: 'tid', + yField: 'dur', + seriesField: 'size', + color: (a) => { + return '#0a59f7'; + }, + hoverHandler: (data) => { + if (data) { + this.threadRunTimeTbl!.setCurrentHover(data); + } else { + this.threadRunTimeTbl!.mouseOut(); + } + }, + tip: (obj) => { + return ` +
+
Process_Id:${ + // @ts-ignore + obj[0].obj.pid}
+
Thread_Id:${ + // @ts-ignore + obj[0].obj.tid}
+
Thread_Name:${ + // @ts-ignore + obj[0].obj.tName}
+
Run_Time:${ + // @ts-ignore + obj[0].obj.dur}
+
+ `; + }, + label: null, + }; + } + + /** + * 元素初始化,将html节点与内部变量进行绑定 + */ + initElements(): void { + this.processRunTimeProgress = this.shadowRoot!.querySelector('#loading'); + this.nodataPro = this.shadowRoot!.querySelector('#nodata_Pro'); + this.processRunTimeTbl = this.shadowRoot!.querySelector('#tb-process-run-time'); + this.processSwitchCountChart = this.shadowRoot!.querySelector('#chart_pro'); + this.nodataThr = this.shadowRoot!.querySelector('#nodata_Thr'); + this.threadRunTimeTbl = this.shadowRoot!.querySelector('#tb-thread-run-time'); + this.threadSwitchCountChart = this.shadowRoot!.querySelector('#chart_thr'); + this.display_pro = this.shadowRoot!.querySelector('#display_pro'); + this.display_thr = this.shadowRoot!.querySelector('#display_thr'); + this.back = this.shadowRoot!.querySelector('#back'); + this.clickEventListener(); + this.hoverEventListener(); + } + + /** + * 点击监听事件函数块 + */ + clickEventListener(): void { + // @ts-ignore + this.processRunTimeTbl!.addEventListener('row-click', (evt: CustomEvent) => { + this.display_flag = false; + let data = evt.detail.data; + this.processId = data.pid; + this.display_thr!.style.display = 'block'; + this.display_pro!.style.display = 'none'; + this.queryLogicWorker( + 'scheduling-Process Top10RunTime', + 'query Thread Top10 Run Time Analysis Time:', + this.callBack.bind(this), + data.pid + ); + data.isSelected = true; + if (evt.detail.callBack) { + evt.detail.callBack(true); + } + }); + this.processRunTimeTbl!.addEventListener('column-click', (evt) => { + // @ts-ignore + this.sortByColumn(evt.detail, this.processRunTimeData); + this.processRunTimeTbl!.recycleDataSource = this.processRunTimeData; + }); + this.threadRunTimeTbl!.addEventListener('column-click', (evt) => { + // @ts-ignore + this.sortByColumn(evt.detail, this.threadRunTimeData); + this.threadRunTimeTbl!.recycleDataSource = this.threadRunTimeData; + }); + this.back?.addEventListener('click', (event) => { + this.display_flag = true; + this.display_pro!.style.display = 'block'; + this.display_thr!.style.display = 'none'; + }); + } + + /** + * 移入事件监听函数块 + */ + hoverEventListener(): void { + // @ts-ignore + this.processRunTimeTbl!.addEventListener('row-hover', (evt: CustomEvent) => { + if (evt.detail.data) { + let data = evt.detail.data; + data.isHover = true; + if (evt.detail.callBack) { + evt.detail.callBack(true); + } + this.processSwitchCountChart?.showHoverColumn(data.no); + } + }); + // @ts-ignore + this.threadRunTimeTbl!.addEventListener('row-hover', (evt: CustomEvent) => { + if (evt.detail.data) { + let data = evt.detail.data; + data.isHover = true; + if (evt.detail.callBack) { + evt.detail.callBack(true); + } + this.threadSwitchCountChart?.showHoverColumn(data.no); + } + }); + } + + /** + * 表格数据排序 + * @param detail 点击的列名,以及排序状态0 1 2分别代表不排序、升序排序、降序排序 + * @param data 表格中需要排序的数据 + */ + sortByColumn(detail: any, data: Array): void { + // @ts-ignore + function compare(processThreadCountProperty, sort, type) { + return function (a: any, b: any) { + if (type === 'number') { + // @ts-ignore + return sort === 2 + ? parseFloat(b[processThreadCountProperty]) - + parseFloat(a[processThreadCountProperty]) + : parseFloat(a[processThreadCountProperty]) - + parseFloat(b[processThreadCountProperty]); + } else { + if (sort === 2) { + return b[processThreadCountProperty] + .toString() + .localeCompare(a[processThreadCountProperty].toString()); + } else { + return a[processThreadCountProperty] + .toString() + .localeCompare(b[processThreadCountProperty].toString()); + } + } + }; + } + if ( detail.key === 'pName' || detail.key === 'tName') { + data.sort( + compare(detail.key, detail.sort, 'string') + ); + } else { + data.sort( + compare(detail.key, detail.sort, 'number') + ); + } + } + + /** + * 用于将元素节点挂载,大函数块拆分为样式、节点 + * @returns 返回字符串形式的元素节点 + */ + initHtml(): string { + return this.initStyleHtml() + this.initTagHtml(); + } + + /** + * 样式html代码块 + * @returns 返回样式代码块字符串 + */ + initStyleHtml(): string { + return ` + + ` + } + + /** + * 节点html代码块 + * @returns 返回节点代码块字符串 + */ + initTagHtml(): string { + return ` + +
+ +
+
+
+
+
Top10运行超长进程
+ +
+
+ + + + + + +
+
+
+
+
+ + `; + } +} + +interface Top10RunTimeData { + NO?: number, + pid?: number, + tid?: number, + pName?: string, + tName?: string, + switchCount?: number, + dur?: number +} diff --git a/ide/src/trace/component/schedulingAnalysis/processAnalysis/Top10ProcessSwitchCount.ts b/ide/src/trace/component/schedulingAnalysis/processAnalysis/Top10ProcessSwitchCount.ts new file mode 100644 index 0000000000000000000000000000000000000000..a43fe0fdcb18833416f2540c821473b296b8fc5b --- /dev/null +++ b/ide/src/trace/component/schedulingAnalysis/processAnalysis/Top10ProcessSwitchCount.ts @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2022 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 { BaseElement, element } from '../../../../base-ui/BaseElement'; +import { LitTable } from '../../../../base-ui/table/lit-table'; +import { procedurePool } from '../../../database/Procedure'; +import { info } from '../../../../log/Log'; +import { TableNoData } from '../TableNoData'; +import '../TableNoData'; +import { LitProgressBar } from '../../../../base-ui/progress-bar/LitProgressBar'; +import '../../../../base-ui/progress-bar/LitProgressBar'; +import { LitChartPie } from '../../../../base-ui/chart/pie/LitChartPie'; +import '../../../../base-ui/chart/pie/LitChartPie'; +import { Utils } from '../../trace/base/Utils'; + +@element('top10-process-switch-count') +export class Top10ProcessSwitchCount extends BaseElement { + traceChange: boolean = false; + private processSwitchCountTbl: LitTable | null | undefined; + private threadSwitchCountTbl: LitTable | null | undefined; + private nodataPro: TableNoData | null | undefined; + private nodataThr: TableNoData | null | undefined; + private processSwitchCountData: Array = []; + private threadSwitchCountData: Array = []; + private threadSwitchCountPie: LitChartPie | null | undefined; + private processSwitchCountPie: LitChartPie | null | undefined; + private display_pro: HTMLDivElement | null | undefined; + private display_thr: HTMLDivElement | null | undefined; + private processSwitchCountProgress: LitProgressBar | null | undefined; + private processId: number | undefined; + private display_flag: boolean = true; + private back: HTMLDivElement | null | undefined; + private processMap: Map = new Map(); + private threadMap: Map = new Map(); + + /** + * 初始化操作,若trace发生改变,将所有变量设置为默认值并重新请求数据。若trace未改变,跳出初始化 + */ + init(): void { + if (!this.traceChange) { + if (this.processSwitchCountTbl!.recycleDataSource.length > 0) { + this.processSwitchCountTbl?.reMeauseHeight(); + } + return; + } + this.traceChange = false; + this.processSwitchCountProgress!.loading = true; + this.display_flag = true; + this.display_pro!.style.display = 'block'; + this.display_thr!.style.display = 'none'; + this.processMap = Utils.getInstance().getProcessMap(); + this.threadMap = Utils.getInstance().getThreadMap(); + this.queryLogicWorker( + 'scheduling-Process Top10Swicount', + 'query Process Top10 Switch Count Analysis Time:', + this.callBack.bind(this) + ); + } + + /** + * 清除已存储数据 + */ + clearData(): void { + this.traceChange = true; + this.processSwitchCountPie!.dataSource = []; + this.processSwitchCountTbl!.recycleDataSource = []; + this.threadSwitchCountPie!.dataSource = []; + this.threadSwitchCountTbl!.recycleDataSource = []; + this.processSwitchCountData = []; + this.threadSwitchCountData = []; + this.processMap = new Map(); + this.threadMap = new Map(); + } + + /** + * 提交worker线程,进行数据库查询 + * @param option 操作的key值,用于找到并执行对应方法 + * @param log 日志打印内容 + * @param handler 结果回调函数 + * @param pid 需要查询某一进程下线程数据的进程id + */ + queryLogicWorker(option: string, log: string, handler: (res: Array) => void, pid?: number): void { + let processThreadCountTime = new Date().getTime(); + procedurePool.submitWithName('logic0', option, {pid: pid}, undefined, handler); + let durTime = new Date().getTime() - processThreadCountTime; + info(log, durTime); + } + + /** + * 抽取公共方法,提取数据,用于展示到表格中 + * @param arr 数据库查询结果 + * @returns 整理好的数据,包含进程名,线程名等相关信息 + */ + organizationData(arr: Array): Array { + let result: Array = []; + for (let i = 0; i < arr.length; i++) { + const pStr: string | null = this.processMap.get(arr[i].pid!)!; + const tStr: string | null = this.threadMap.get(arr[i].tid!)!; + result.push({ + NO: i + 1, + pid: arr[i].pid || this.processId, + pName: pStr === null ? 'Process ' : pStr, + switchCount: arr[i].occurrences, + tid: arr[i].tid, + tName: tStr === null ? 'Thread ' : tStr + }); + } + return result; + } + + /** + * 提交线程后,结果返回后的回调函数 + * @param res 数据库查询结果 + */ + callBack(res: Array): void { + let result: Array = this.organizationData(res); + // 判断当前显示的是进程组还是线程组 + if (this.display_flag === true) { + this.processCallback(result); + } else { + this.threadCallback(result); + } + this.processSwitchCountProgress!.loading = false; + } + + /** + * 大函数块拆解分为两部分,此部分为Top10进程数据 + * @param result 需要显示在表格中的数据 + */ + processCallback(result: Array): void { + this.nodataPro!.noData = result === undefined || result.length === 0; + this.processSwitchCountData = result; + this.processSwitchCountPie!.config = { + appendPadding: 10, + data: result, + angleField: 'switchCount', + colorField: 'pid', + radius: 0.8, + label: { + type: 'outer', + }, + hoverHandler: (data) => { + if (data) { + this.processSwitchCountTbl!.setCurrentHover(data); + } else { + this.processSwitchCountTbl!.mouseOut(); + } + }, + tip: (obj) => { + return ` +
+
Process_Id:${ + // @ts-ignore + obj.obj.pid}
+
Process_Name:${ + // @ts-ignore + obj.obj.pName}
+
Switch Count:${ + // @ts-ignore + obj.obj.switchCount}
+
+ `; + }, + interactions: [ + { + type: 'element-active', + }, + ], + }; + this.processSwitchCountTbl!.recycleDataSource = result; + this.processSwitchCountTbl!.reMeauseHeight(); + } + + /** + * 大函数块拆解分为两部分,此部分为Top10线程数据 + * @param result 需要显示在表格中的数据 + */ + threadCallback(result: Array): void { + this.nodataThr!.noData = result === undefined || result.length === 0; + this.threadSwitchCountTbl!.recycleDataSource = result; + this.threadSwitchCountTbl!.reMeauseHeight(); + this.threadSwitchCountData = result; + this.threadSwitchCountPie!.config = { + appendPadding: 10, + data: result, + angleField: 'switchCount', + colorField: 'tid', + radius: 0.8, + label: { + type: 'outer', + }, + hoverHandler: (data) => { + if (data) { + this.threadSwitchCountTbl!.setCurrentHover(data); + } else { + this.threadSwitchCountTbl!.mouseOut(); + } + }, + tip: (obj) => { + return ` +
+
Thread_Id:${ + // @ts-ignore + obj.obj.tid}
+
Thread_Name:${ + // @ts-ignore + obj.obj.tName}
+
Switch Count:${ + // @ts-ignore + obj.obj.switchCount}
+
Process_Id:${ + // @ts-ignore + obj.obj.pid}
+
+ `; + }, + interactions: [ + { + type: 'element-active', + }, + ], + }; + } + + /** + * 元素初始化,将html节点与内部变量进行绑定 + */ + initElements(): void { + this.processSwitchCountProgress = this.shadowRoot!.querySelector('#loading'); + this.processSwitchCountTbl = this.shadowRoot!.querySelector('#tb-process-switch-count'); + this.threadSwitchCountTbl = this.shadowRoot!.querySelector('#tb-thread-switch-count'); + this.processSwitchCountPie = this.shadowRoot!.querySelector('#pie_pro'); + this.threadSwitchCountPie = this.shadowRoot!.querySelector('#pie_thr'); + this.nodataPro = this.shadowRoot!.querySelector('#nodata_pro'); + this.nodataThr = this.shadowRoot!.querySelector('#nodata_thr'); + this.display_pro = this.shadowRoot!.querySelector('#display_pro'); + this.display_thr = this.shadowRoot!.querySelector('#display_thr'); + this.back = this.shadowRoot!.querySelector('#back'); + this.clickEventListener(); + this.hoverEventListener(); + } + + /** + * 点击监听事件函数块 + */ + clickEventListener(): void { + // @ts-ignore + this.processSwitchCountTbl!.addEventListener('row-click', (evt: CustomEvent) => { + this.display_flag = false; + let data = evt.detail.data; + this.processId = data.pid; + this.display_thr!.style.display = 'block'; + this.display_pro!.style.display = 'none'; + this.queryLogicWorker( + 'scheduling-Process Top10Swicount', + 'query Process Top10 Switch Count Analysis Time:', + this.callBack.bind(this), + data.pid + ); + data.isSelected = true; + if (evt.detail.callBack) { + evt.detail.callBack(true); + } + }); + // @ts-ignore + this.threadSwitchCountTbl!.addEventListener('row-click', (evt: CustomEvent) => { + let data = evt.detail.data; + data.isSelected = true; + if (evt.detail.callBack) { + evt.detail.callBack(true); + } + }); + this.processSwitchCountTbl!.addEventListener('column-click', (evt) => { + // @ts-ignore + this.sortByColumn(evt.detail, this.processSwitchCountData); + this.processSwitchCountTbl!.recycleDataSource = this.processSwitchCountData; + }); + this.threadSwitchCountTbl!.addEventListener('column-click', (evt) => { + // @ts-ignore + this.sortByColumn(evt.detail, this.threadSwitchCountData); + this.threadSwitchCountTbl!.recycleDataSource = this.threadSwitchCountData; + }); + this.back!.addEventListener('click', (event) => { + this.display_flag = true; + this.display_pro!.style.display = 'block'; + this.display_thr!.style.display = 'none'; + }); + + } + + /** + * 移入事件监听函数块 + */ + hoverEventListener(): void { + // @ts-ignore + this.processSwitchCountTbl!.addEventListener('row-hover', (evt: CustomEvent) => { + if (evt.detail.data) { + let data = evt.detail.data; + data.isHover = true; + if (evt.detail.callBack) { + evt.detail.callBack(true); + } + } + this.processSwitchCountPie?.showHover(); + }); + // @ts-ignore + this.threadSwitchCountTbl!.addEventListener('row-hover', (evt: CustomEvent) => { + if (evt.detail.data) { + let data = evt.detail.data; + data.isHover = true; + if (evt.detail.callBack) { + evt.detail.callBack(true); + } + } + this.threadSwitchCountPie?.showHover(); + }); + this.addEventListener('mouseenter', () => { + if (this.processSwitchCountTbl!.recycleDataSource.length > 0) { + this.processSwitchCountTbl?.reMeauseHeight(); + } + }); + } + + /** + * 表格数据排序 + * @param detail 点击的列名,以及排序状态0 1 2分别代表不排序、升序排序、降序排序 + * @param data 表格中需要排序的数据 + */ + sortByColumn(detail: {key: string, sort: number}, data: Array): void { + // @ts-ignore + function compare(processThreadCountProperty, sort, type) { + return function (a: any, b: any) { + if (type === 'number') { + // @ts-ignore + return sort === 2 + ? parseFloat(b[processThreadCountProperty]) - + parseFloat(a[processThreadCountProperty]) + : parseFloat(a[processThreadCountProperty]) - + parseFloat(b[processThreadCountProperty]); + } else { + if (sort === 2) { + return b[processThreadCountProperty] + .toString() + .localeCompare(a[processThreadCountProperty].toString()); + } else { + return a[processThreadCountProperty] + .toString() + .localeCompare(b[processThreadCountProperty].toString()); + } + } + }; + } + if (detail.key === 'pName' || detail.key === 'tName') { + data.sort( + compare(detail.key, detail.sort, 'string') + ); + } else { + data.sort( + compare(detail.key, detail.sort, 'number') + ); + } + } + + /** + * 用于将元素节点挂载,大函数块拆分为样式、节点 + * @returns 返回字符串形式的元素节点 + */ + initHtml(): string { + return this.initStyleHtml() + this.initTagHtml(); + } + + /** + * 样式html代码块 + * @returns 返回样式代码块字符串 + */ + initStyleHtml(): string { + return ` + + `; + } + + /** + * 节点html代码块 + * @returns 返回节点代码块字符串 + */ + initTagHtml() :string { + return ` + +
+ +
+
+
+
+
Statistics By Process's Switch Count
+ +
+
+ + + + + + +
+
+
+
+
+ + `; + } +} + +interface Top10ProcSwiCount { + NO?: number, + pid?: number, + tid?: number, + pName?: string, + tName?: string, + switchCount?: number, + occurrences?: number +} 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 diff --git a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerSchedulingAnalysis.ts b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerSchedulingAnalysis.ts index 18a48438f1af674c217619280bc8a5f5cea8cdfb..207ec401ee9e8162f21a3e21c5e087062a3b90c8 100644 --- a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerSchedulingAnalysis.ts +++ b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerSchedulingAnalysis.ts @@ -104,6 +104,12 @@ export class ProcedureLogicWorkerSchedulingAnalysis extends LogicHandler { case 'scheduling-Thread Freq': this.schedulingThreadFreq(data); break; + case 'scheduling-Process Top10Swicount': + this.schedulingProTop10Swicount(data); + break; + case 'scheduling-Process Top10RunTime': + this.schedulingProcessRunTime(data); + break; } } private schedulingClearData(data: { id: string; action: string; params: unknown }): void { @@ -352,6 +358,40 @@ export class ProcedureLogicWorkerSchedulingAnalysis extends LogicHandler { this.queryThreadStateByTid(data.params.tid); } } + private schedulingProTop10Swicount(data: any): void { + if (data.params.list) { + let arr = convertJSON(data.params.list) || []; + self.postMessage({ + id: data.id, + action: data.action, + results: arr, + }); + arr = []; + } else { + if (data.params.pid) { + this.queryThrTop10Swicount(data.params.pid); + } else { + this.queryProTop10Swicount(); + } + } + } + private schedulingProcessRunTime(data: any): void { + if (data.params.list) { + let arr = convertJSON(data.params.list) || []; + self.postMessage({ + id: data.id, + action: data.action, + results: arr, + }); + arr = []; + } else { + if (data.params.pid) { + this.queryThrTop10RunTime(data.params.pid); + } else { + this.queryProTop10RunTime(); + } + } + } getProcessAndThread(): void { this.queryData( this.currentEventId, @@ -538,6 +578,88 @@ where cpu not null this.queryData(this.currentEventId, 'scheduling-Thread Freq', sql, {}); } + queryProTop10Swicount() { + this.queryData( + this.currentEventId, + 'scheduling-Process Top10Swicount', + ` + select + pid, + count(tid) as occurrences + from + thread_state + where + state = 'Running' + group by + pid + ORDER BY occurrences desc + LIMIT 10 + `, + {} + ); + } + queryThrTop10Swicount(pid: number) { + this.queryData( + this.currentEventId, + 'scheduling-Process Top10Swicount', + ` + select + tid, + count(tid) as occurrences + from + thread_state + where + state = 'Running' + and pid = ${pid} + group by + tid + ORDER BY occurrences desc + LIMIT 10 + `, + {} + ); + } + queryProTop10RunTime() { + this.queryData( + this.currentEventId, + 'scheduling-Process Top10RunTime', + ` + select + pid, + SUM(dur) As dur + from + thread_state + where + state = 'Running' + GROUP BY pid + ORDER BY dur desc + LIMIT 10 + `, + {} + ); + } + queryThrTop10RunTime(pid: number) { + this.queryData( + this.currentEventId, + 'scheduling-Process Top10RunTime', + ` + select + tid, + SUM(dur) As dur + from + thread_state + where + state = 'Running' + and + pid = ${pid} + GROUP BY tid + ORDER BY dur desc + LIMIT 10 + `, + {} + ); + } + groupIrgDataByCpu(arr: Irq[]): Map { //首先计算 每个频点的持续时间,并根据Cpu来分组 let map: Map> = new Map>();