diff --git a/ide/src/trace/component/trace/base/TraceSheetConfig.ts b/ide/src/trace/component/trace/base/TraceSheetConfig.ts index 1433e45dd01472171b740b8b81b2f3c9441d2104..e636bc5631a27368e177593c17b7eb7088906f25 100644 --- a/ide/src/trace/component/trace/base/TraceSheetConfig.ts +++ b/ide/src/trace/component/trace/base/TraceSheetConfig.ts @@ -118,6 +118,8 @@ import { TabPaneHiLogSummary } from '../sheet/hilog/TabPaneHiLogSummary.js'; import { TabPaneSchedPriority } from '../sheet/cpu/TabPaneSchedPriority.js'; import { TabPaneGpuResourceVmTracker } from '../sheet/vmtracker/TabPaneGpuResourceVmTracker.js'; import { TabPaneGpuGraph } from '../sheet/gpu/TabPaneGraph.js'; +import { TabPaneFreqUsage } from '../sheet/frequsage/TabPaneFreqUsage.js'; +import { TabPaneFreqDataCut } from '../sheet/frequsage/TabPaneFreqDataCut.js'; import { TabpanePerfBinaryTree } from '../sheet/hiperf/TabPerfBinaryTree.js'; export let tabConfig: any = { @@ -626,6 +628,17 @@ export let tabConfig: any = { title: 'M Selection', type: TabPaneCurrent, require: (param: SelectionParam) => param.isCurrentPane, + }, + 'tabpane-frequsage': { + title: 'Freq Usage', + type: TabPaneFreqUsage, + require: (param: SelectionParam) => param.threadIds.length > 0 && param.threadIds.length < 2, + }, + 'tabpane-freqdatacut': { + title: 'Freq DataCut', + type: TabPaneFreqDataCut, + require: (param: SelectionParam) => param.threadIds.length > 0 && param.threadIds.length < 2, + }, }, //current selection 'box-perf-Binary': { title: 'BinaryTree', diff --git a/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqDataCut.ts b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqDataCut.ts new file mode 100644 index 0000000000000000000000000000000000000000..55bd1b18ca2c89b33daf1174fce11d46ee6307cf --- /dev/null +++ b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqDataCut.ts @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2023 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.js'; +import { LitTable } from '../../../../../base-ui/table/lit-table'; +import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; +import '../../../StackBar.js' +import { getTabRunningPercent, querySearchFuncData, queryCpuFreqUsageData, queryCpuFreqFilterId } from '../../../../database/SqlLite.js'; +import { Utils } from '../../base/Utils.js'; +import { log } from '../../../../../log/Log.js'; +import { resizeObserver } from '../SheetUtils.js'; + +@element('tabpane-freqdatacut') +export class TabPaneFreqDataCut extends BaseElement { + private threadStatesTbl: LitTable | null | undefined; + private threadStatesTblSource: Array = []; + private currentSelectionParam: SelectionParam | any; + private threadStatesDIV: Element | null | undefined; + private initData: Array = []; + + set data(threadStatesParam: SelectionParam | any) { + if (this.currentSelectionParam === threadStatesParam) { + return; + } + this.currentSelectionParam = threadStatesParam; + this.threadStatesTblSource = []; + this.threadStatesTbl!.recycleDataSource = []; + let tableValue: any = this.threadStatesTbl; + tableValue.value = []; + let divRoot1: any = this.shadowRoot?.querySelector('#dataCutThreadId'); + divRoot1.value = ''; + let divRoot2: any = this.shadowRoot?.querySelector('#dataCutThreadFunc'); + divRoot2.value = ''; + getTabRunningPercent(threadStatesParam.threadIds, threadStatesParam.leftNs, threadStatesParam.rightNs).then((result) => { + queryCpuFreqFilterId().then(r => { + let IdMap = new Map(); + let queryId = new Array(); + for (let i = 0; i < r.length; i++) { + queryId.push(r[i].id); + IdMap.set(r[i].id, r[i].cpu); + } + queryCpuFreqUsageData(queryId).then((res) => { + if (result != null && result.length > 0) { + log('getTabRunningPercent result size : ' + result.length); + let sum = 0; + let dealArr = new Array(); + for (let i of res) { + dealArr.push({ 'startNS': i.startNS + threadStatesParam.recordStartNs, 'dur': i.dur, 'value': i.value, 'cpu': IdMap.get(i.filter_id) }); + } + let targetList = new Array(); + let cpuArr = new Array(); + let finalResultArr = new Array(); + finalResultArr.push({ 'thread': '', 'count': 0, 'cpu': '', 'freq': '', 'dur': 0, 'percent': '100.00', 'state': 'Running', children: new Array() }); + for (let e of result) { + if (threadStatesParam.processIds.includes(e.pid) && e.state == 'Running') { + let process = Utils.PROCESS_MAP.get(e.pid); + let thread = Utils.THREAD_MAP.get(e.tid); + e.process = process == null || process.length == 0 ? '[NULL]' : process; + e.thread = thread == null || thread.length == 0 ? '[NULL]' : thread; + e.stateJX = e.state; + e.state = Utils.getEndState(e.stateJX); + sum += e.dur; + targetList.push(e); + if (!cpuArr.includes(e.cpu)) { + cpuArr.push(e.cpu); + finalResultArr[0].thread = finalResultArr[0].thread == '' ? e.tid + '_' + e.thread : finalResultArr[0].thread; + finalResultArr[0].children.push({ 'thread': e.tid + '_' + e.thread, 'count': 0, 'cpu': e.cpu, 'freq': '', 'dur': 0, 'percent': 0, 'state': 'Running', children: new Array() }); + } + } + } + // 用来存放数据切割之前的汇总数据 + let resultList = new Array(); + // 通过循环获取每个running状态线程的相关信息,此处或许可以进行算法优化 + const tsMutiple = 1000000000; + for (let i = 0; i < targetList.length; i++) { + for (let j = 0; j < dealArr.length; j++) { + if (targetList[i].cpu == dealArr[j].cpu) { + if (targetList[i].ts > dealArr[j].startNS) { + if (targetList[i].ts < (dealArr[j].startNS + dealArr[j].dur)) { + if (targetList[i].dur < (dealArr[j].startNS + dealArr[j].dur - targetList[i].ts)) { + resultList.push({ 'thread': targetList[i].tid + '_' + targetList[i].thread, 'count': (dealArr[j].value * targetList[i].dur) / 1000, 'cpu': targetList[i].cpu, 'freq': dealArr[j].value, 'dur': targetList[i].dur, 'percent': targetList[i].dur / sum * 100, 'state': 'Running', 'ts': targetList[i].ts / tsMutiple }); + break; + } else { + resultList.push({ 'thread': targetList[i].tid + '_' + targetList[i].thread, 'count': (dealArr[j].value * (dealArr[j].startNS + dealArr[j].dur - targetList[i].ts)) / 1000, 'cpu': targetList[i].cpu, 'freq': dealArr[j].value, 'dur': (dealArr[j].startNS + dealArr[j].dur - targetList[i].ts), 'percent': (dealArr[j].startNS + dealArr[j].dur - targetList[i].ts) / sum * 100, 'state': 'Running', 'ts': targetList[i].ts / tsMutiple }); + } + } + } else { + if ((targetList[i].ts + targetList[i].dur) > dealArr[j].startNS) { + if ((targetList[i].dur + targetList[i].ts - dealArr[j].startNS) < dealArr[j].dur) { + resultList.push({ 'thread': targetList[i].tid + '_' + targetList[i].thread, 'count': (dealArr[j].value * (targetList[i].dur + targetList[i].ts - dealArr[j].startNS)) / 1000, 'cpu': targetList[i].cpu, 'freq': dealArr[j].value, 'dur': (targetList[i].dur + targetList[i].ts - dealArr[j].startNS), 'percent': (targetList[i].dur + targetList[i].ts - dealArr[j].startNS) / sum * 100, 'state': 'Running', 'ts': dealArr[j].startNS / tsMutiple }); + break; + } else { + resultList.push({ 'thread': targetList[i].tid + '_' + targetList[i].thread, 'count': (dealArr[j].value * dealArr[j].dur) / 1000, 'cpu': targetList[i].cpu, 'freq': dealArr[j].value, 'dur': dealArr[j].dur, 'percent': dealArr[j].dur / sum * 100, 'state': 'Running', 'ts': dealArr[j].startNS / tsMutiple }); + } + } else { + resultList.push({ 'thread': targetList[i].tid + '_' + targetList[i].thread, 'count': 0, 'cpu': targetList[i].cpu, 'freq': 'unknown', 'dur': targetList[i].dur, 'percent': targetList[i].dur / sum * 100, 'state': 'Running', 'ts': targetList[i].ts / tsMutiple }); + break; + } + } + } + } + } + // 深拷贝,用来进行数据切割操作,避免数据污染 + this.initData = JSON.parse(JSON.stringify(resultList)); + } else { + this.threadStatesTblSource = []; + this.threadStatesTbl!.recycleDataSource = []; + this.initData = []; + } + }) + }) + + }); + } + initElements(): void { + this.threadStatesTbl = this.shadowRoot?.querySelector('#tb-running-percent'); + // 暂时屏蔽列排序功能,后续增加则重写排序方法 + this.threadStatesDIV = this.shadowRoot?.querySelector('#dataCut'); + this.threadStatesDIV?.children[2].children[0].addEventListener('click', (e) => { + this.dataSingleCut(this.threadStatesDIV?.children[0], this.threadStatesDIV?.children[1], this.initData); + }) + this.threadStatesDIV?.children[2].children[1].addEventListener('click', (e) => { + this.dataLoopCut(this.threadStatesDIV?.children[0], this.threadStatesDIV?.children[1], this.initData); + }) + } + connectedCallback() { + super.connectedCallback(); + resizeObserver(this.parentElement!, this.threadStatesTbl!); + } + initHtml(): string { + return ` + +
+ + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + ` + } + dataLoopCut(threadId: any, threadFunc: any, resultList: any) { + let threadIdValue = threadId.value.trim(); + let threadFuncName = threadFunc.value.trim(); + let leftNS = this.currentSelectionParam.leftNs; + let rightNS = this.currentSelectionParam.rightNs; + let tableValue: any = this.threadStatesTbl; + tableValue.value = []; + if (/^[0-9]*$/.test(threadIdValue)) { + querySearchFuncData(threadFuncName, Number(threadIdValue), leftNS, rightNS).then(res => { + let display = JSON.parse(JSON.stringify(resultList)); + let timeDur = this.currentSelectionParam.recordStartNs; + let cutArr = new Array(); + // 根据线程id及方法名获取的数据,处理后用作切割时间依据,时间跨度为整个方法开始时间到末个方法开始时间 + for (let i of res) { + cutArr.push({ 'ts': i.startTime + timeDur }); + } + // 将数据进行切割处理 + let finalArr = new Array(); + let finalResultArr = new Array(); + const tsMutiple = 1000000000; + finalResultArr.push({ 'thread': display[0].thread, 'ts': '', 'count': 0, 'cpu': '', 'freq': '', 'dur': 0, 'percent': 0, 'state': 'Running', children: new Array() }); + for (let i = 0; i < cutArr.length - 1; i++) { + let displayArr = JSON.parse(JSON.stringify(display)); + for (let j = 0; j < displayArr.length; j++) { + displayArr[j].ts = displayArr[j].ts * tsMutiple; + if (displayArr[j].ts >= cutArr[i].ts) { + if ((displayArr[j].ts + displayArr[j].dur) <= cutArr[i + 1].ts) { + finalArr.push({ 'thread': displayArr[j].thread, 'count': (displayArr[j].freq * displayArr[j].dur) / 1000, 'cpu': displayArr[j].cpu, 'freq': displayArr[j].freq, 'dur': displayArr[j].dur, 'percent': displayArr[j].percent, 'state': 'Running', 'ts': (displayArr[j].ts - timeDur) / tsMutiple, 'id': i }); + } else { + if (cutArr[i + 1].ts - displayArr[j].ts > 0) { + finalArr.push({ 'thread': displayArr[j].thread, 'count': (displayArr[j].freq * (cutArr[i + 1].ts - displayArr[j].ts)) / 1000, 'cpu': displayArr[j].cpu, 'freq': displayArr[j].freq, 'dur': cutArr[i + 1].ts - displayArr[j].ts, 'percent': displayArr[j].percent * ((cutArr[i + 1].ts - displayArr[j].ts) / displayArr[j].dur), 'state': 'Running', 'ts': (displayArr[j].ts - timeDur) / tsMutiple, 'id': i }); + break; + } + } + } else { + if ((displayArr[j].ts + displayArr[j].dur) > cutArr[i + 1].ts) { + finalArr.push({ 'thread': displayArr[j].thread, 'count': (displayArr[j].freq * (cutArr[i + 1].ts - cutArr[i].ts)) / 1000, 'cpu': displayArr[j].cpu, 'freq': displayArr[j].freq, 'dur': cutArr[i + 1].ts - cutArr[i].ts, 'percent': displayArr[j].percent * ((cutArr[i + 1].ts - cutArr[i].ts) / displayArr[j].dur), 'state': 'Running', 'ts': (cutArr[i].ts - timeDur) / tsMutiple, 'id': i }); + } + if ((displayArr[j].ts + displayArr[j].dur) > cutArr[i].ts && (displayArr[j].ts + displayArr[j].dur) < cutArr[i + 1].ts) { + finalArr.push({ 'thread': displayArr[j].thread, 'count': (displayArr[j].freq * (displayArr[j].dur + displayArr[j].ts - cutArr[i].ts)) / 1000, 'cpu': displayArr[j].cpu, 'freq': displayArr[j].freq, 'dur': displayArr[j].dur + displayArr[j].ts - cutArr[i].ts, 'percent': displayArr[j].percent * ((displayArr[j].dur + displayArr[j].ts - cutArr[i].ts) / displayArr[j].dur), 'state': 'Running', 'ts': (cutArr[i].ts - timeDur) / tsMutiple, 'id': i }); + } + } + } + finalResultArr[0].children.push({ 'thread': displayArr[0].thread, 'ts': (cutArr[i].ts - timeDur) / tsMutiple, 'count': 0, 'cpu': '', 'freq': '', 'dur': 0, 'percent': 0, 'state': 'Running', children: new Array(), 'id': i }); + } + + for (let i = 0; i < finalArr.length; i++) { + for (let j = i + 1; j < finalArr.length; j++) { + if (finalArr[i].cpu === finalArr[j].cpu && finalArr[i].freq === finalArr[j].freq && finalArr[i].id === finalArr[j].id) { + finalArr[i].dur += finalArr[j].dur; + finalArr[i].percent += finalArr[j].percent; + finalArr[i].count += finalArr[j].count; + finalArr.splice(j, 1); + j--; + } + } + finalArr[i].percent = Number((finalArr[i].percent).toFixed(2)); + } + + let newArr1 = JSON.parse(JSON.stringify(finalResultArr[0])); + let newArr2 = JSON.parse(JSON.stringify(finalArr)); + let finalResult = new Array(this.mergeTree(newArr1, newArr2)); + this.threadStatesTblSource = finalResult[0].children.length > 0 ? finalResult : []; + this.threadStatesTbl!.recycleDataSource = finalResult[0].children.length > 0 ? finalResult : []; + }) + } else { + alert('请输入正确的线程ID'); + } + } + dataSingleCut(threadId: any, threadFunc: any, resultList: any) { + let threadIdValue = threadId.value.trim(); + let threadFuncName = threadFunc.value.trim(); + let leftNS = this.currentSelectionParam.leftNs; + let rightNS = this.currentSelectionParam.rightNs; + let tableValue: any = this.threadStatesTbl; + tableValue.value = []; + if (/^[0-9]*$/.test(threadIdValue)) { + querySearchFuncData(threadFuncName, Number(threadIdValue), leftNS, rightNS).then(result => { + let [...target] = JSON.parse(JSON.stringify(resultList)); + let timeDur = this.currentSelectionParam.recordStartNs; + let dealArr = new Array(); + for (let i of result) { + if (i.startTime + timeDur + i.dur < this.currentSelectionParam.rightNs + timeDur) { + dealArr.push({ 'ts': i.startTime + timeDur, 'dur': i.dur }); + } + } + let finalResultArr = new Array(); + finalResultArr.push({ 'thread': target[0].thread, 'ts': '', 'count': 0, 'cpu': '', 'freq': '', 'dur': 0, 'percent': 0, 'state': 'Running', children: new Array() }); + let resList = new Array(); + const tsMutiple = 1000000000; + for (let i = 0; i < dealArr.length; i++) { + let targetList = JSON.parse(JSON.stringify(target)); + for (let j = 0; j < targetList.length; j++) { + targetList[j].ts = targetList[j].ts * tsMutiple; + if (dealArr[i].ts < targetList[j].ts) { + if (dealArr[i].ts + dealArr[i].dur > targetList[j].ts) { + if (dealArr[i].ts + dealArr[i].dur > targetList[j].ts + targetList[j].dur) { + resList.push({ 'thread': targetList[i].thread, 'ts': (targetList[j].ts - timeDur) / tsMutiple, 'count': (targetList[j].freq * targetList[j].dur) / 1000, 'cpu': targetList[j].cpu, 'freq': targetList[j].freq, 'dur': targetList[j].dur, 'percent': targetList[j].percent, 'state': 'Running', 'id': i }); + } else { + resList.push({ 'thread': targetList[j].thread, 'ts': (targetList[j].ts - timeDur) / tsMutiple, 'count': (dealArr[i].ts + dealArr[i].dur - targetList[j].ts) * targetList[j].freq / 1000, 'cpu': targetList[j].cpu, 'freq': targetList[j].freq, 'dur': dealArr[i].ts + dealArr[i].dur - targetList[j].ts, 'percent': (dealArr[i].ts + dealArr[i].dur - targetList[j].ts) / targetList[j].dur * targetList[j].percent, 'state': 'Running', 'id': i }); + break; + } + } + } else { + if (targetList[j].ts + targetList[j].dur > dealArr[i].ts) { + if (targetList[j].ts + targetList[j].dur > dealArr[i].ts + dealArr[i].dur) { + resList.push({ 'thread': targetList[j].thread, 'ts': (dealArr[i].ts - timeDur) / tsMutiple, 'count': dealArr[i].dur * targetList[j].freq / 1000, 'cpu': targetList[j].cpu, 'freq': targetList[j].freq, 'dur': dealArr[i].dur, 'percent': dealArr[i].dur / targetList[j].dur * targetList[j].percent, 'state': 'Running', 'id': i }); + break; + } else { + resList.push({ 'thread': targetList[j].thread, 'ts': (dealArr[i].ts - timeDur) / tsMutiple, 'count': (targetList[j].ts + targetList[j].dur - dealArr[i].ts) * targetList[j].freq / 1000, 'cpu': targetList[j].cpu, 'freq': targetList[j].freq, 'dur': targetList[j].ts + targetList[j].dur - dealArr[i].ts, 'percent': (targetList[j].ts + targetList[j].dur - dealArr[i].ts) / targetList[j].dur * targetList[j].percent, 'state': 'Running', 'id': i }); + } + } + } + } + finalResultArr[0].children.push({ 'thread': targetList[0].thread, 'ts': (dealArr[i].ts - timeDur) / tsMutiple, 'count': 0, 'cpu': '', 'freq': '', 'dur': 0, 'percent': 0, 'state': 'Running', children: new Array(), 'id': i }); + } + // 合并相同周期内的数据 + for (let i = 0; i < resList.length; i++) { + for (let j = i + 1; j < resList.length; j++) { + if (resList[i].cpu === resList[j].cpu && resList[i].freq === resList[j].freq && resList[i].id === resList[j].id) { + resList[i].dur += resList[j].dur; + resList[i].percent += resList[j].percent; + resList[i].count += resList[j].count; + resList.splice(j, 1); + j--; + } + } + resList[i].percent = Number((resList[i].percent).toFixed(2)); + } + + let newArr1 = JSON.parse(JSON.stringify(finalResultArr[0])); + let newArr2 = JSON.parse(JSON.stringify(resList)); + let finalResult = new Array(this.mergeTree(newArr1, newArr2)); + + this.threadStatesTblSource = finalResult[0].children.length > 0 ? finalResult : []; + this.threadStatesTbl!.recycleDataSource = finalResult[0].children.length > 0 ? finalResult : []; + }) + } else { + alert('请输入正确的线程ID'); + } + } + + mergeTree(arr1: any, arr2: any) { + for (let i = 0; i < arr1.children.length; i++) { + // 改成map对象做标记 + let cpuArr = new Array(); + let flagMap = new Map(); + let flag = 0; + for (let j = 0; j < arr2.length; j++) { + if (arr1.children[i].id == arr2[j].id) { + if (!cpuArr.includes(arr2[j].cpu)) { + flagMap.set(arr2[j].cpu, flag); + cpuArr.push(arr2[j].cpu); + arr1.children[i].children.push({ 'thread': arr2[j].thread, 'count': 0, 'cpu': arr2[j].cpu, 'freq': '', 'dur': 0, 'percent': 0, 'state': 'Running', 'ts': '', children: new Array(), 'id': arr2[j].id }); + if (arr1.children[i].children[flag].cpu == arr2[j].cpu && arr1.children[i].children[flag].id == arr2[j].id) { + arr1.children[i].children[flag].children.push(arr2[j]); + arr1.children[i].children[flag].dur += arr2[j].dur; + arr1.children[i].children[flag].percent += arr2[j].percent; + arr1.children[i].children[flag].count += arr2[j].count; + arr1.children[i].percent += arr1.children[i].children[flag].percent; + arr1.children[i].children[flag].percent = Number(arr1.children[i].children[flag].percent.toFixed(2)); + arr1.children[i].dur += arr1.children[i].children[flag].dur; + arr1.children[i].count += arr1.children[i].children[flag].count; + flag++; + arr2.splice(j, 1); + j--; + } + } else { + // 利用map做数据处理 + let count = flagMap.get(arr2[j].cpu); + if (arr1.children[i].children[count].cpu == arr2[j].cpu && arr1.children[i].children[count].id == arr2[j].id) { + arr1.children[i].children[count].children.push(arr2[j]); + arr1.children[i].children[count].dur += arr2[j].dur; + arr1.children[i].children[count].percent += arr2[j].percent; + arr1.children[i].children[count].count += arr2[j].count; + arr1.children[i].percent += arr2[j].percent; + arr1.children[i].children[count].percent = Number(arr2[j].percent.toFixed(2)); + arr1.children[i].dur += arr2[j].dur; + arr1.children[i].count += arr2[j].count; + arr2.splice(j, 1); + j--; + } + } + } else { + break; + } + } + arr1.children[i].children.sort((a: any, b: any) => a.cpu - b.cpu); + arr1.percent += arr1.children[i].percent; + arr1.children[i].percent = Number(arr1.children[i].percent.toFixed(2)); + arr1.dur += arr1.children[i].dur; + arr1.count += arr1.children[i].count; + } + arr1.percent = Number(arr1.percent.toFixed(2)); + return arr1; + } +} \ No newline at end of file diff --git a/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsage.ts b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsage.ts new file mode 100644 index 0000000000000000000000000000000000000000..8c75ee2fd0963ae698c9bce66c69879e540c2c6d --- /dev/null +++ b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsage.ts @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2023 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.js'; +import { LitTable } from '../../../../../base-ui/table/lit-table'; +import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; +import '../../../StackBar.js' +import { getTabRunningPercent, queryCpuFreqUsageData, queryCpuFreqFilterId } from '../../../../database/SqlLite.js'; +import { Utils } from '../../base/Utils.js'; +import { resizeObserver } from '../SheetUtils.js'; + +@element('tabpane-frequsage') +export class TabPaneFreqUsage extends BaseElement { + private threadStatesTbl: LitTable | null | undefined; + private threadStatesTblSource: Array = []; + private currentSelectionParam: Selection | undefined; + + set data(threadStatesParam: SelectionParam | any) { + if (this.currentSelectionParam === threadStatesParam) { + return; + } + this.currentSelectionParam = threadStatesParam; + this.threadStatesTblSource = []; + this.threadStatesTbl!.recycleDataSource = []; + let tableValue:any = this.threadStatesTbl; + tableValue.value = []; + getTabRunningPercent(threadStatesParam.threadIds, threadStatesParam.leftNs, threadStatesParam.rightNs).then((result) => { + queryCpuFreqFilterId().then(r => { + let IdMap = new Map(); + let queryId = new Array(); + for (let i = 0; i < r.length; i++) { + queryId.push(r[i].id); + IdMap.set(r[i].id, r[i].cpu); + } + queryCpuFreqUsageData(queryId).then((res) => { + if (result != null && result.length > 0) { + let sum = 0; + let dealArr = new Array(); + for (let i of res) { + dealArr.push({ 'startNS': i.startNS + threadStatesParam.recordStartNs, 'dur': i.dur, 'value': i.value, 'cpu': IdMap.get(i.filter_id) }); + } + let targetList = new Array(); + let cpuArr = new Array(); + let finalResultArr = new Array(); + finalResultArr.push({ 'thread': '', 'count': 0, 'cpu': '', 'freq': '', 'dur': 0, 'percent': '100.00', 'state': 'Running', children: new Array() }); + for (let e of result) { + if (threadStatesParam.processIds.includes(e.pid) && e.state == 'Running') { + let process = Utils.PROCESS_MAP.get(e.pid); + let thread = Utils.THREAD_MAP.get(e.tid); + e.process = process == null || process.length == 0 ? '[NULL]' : process; + e.thread = thread == null || thread.length == 0 ? '[NULL]' : thread; + e.stateJX = e.state; + e.state = Utils.getEndState(e.stateJX); + sum += e.dur; + targetList.push(e); + if (!cpuArr.includes(e.cpu)) { + cpuArr.push(e.cpu); + finalResultArr[0].thread = finalResultArr[0].thread == '' ? e.tid + '_' + e.thread : finalResultArr[0].thread; + finalResultArr[0].children.push({ 'thread': e.tid + '_' + e.thread, 'count': 0, 'cpu': e.cpu, 'freq': '', 'dur': 0, 'percent': 0, 'state': 'Running', children: new Array() }); + } + } + } + let resultList = new Array(); + for (let i = 0; i < targetList.length; i++) { + for (let j = 0; j < dealArr.length; j++) { + if (targetList[i].cpu == dealArr[j].cpu) { + if (targetList[i].ts > dealArr[j].startNS) { + if (targetList[i].ts < (dealArr[j].startNS + dealArr[j].dur)) { + if (targetList[i].dur < (dealArr[j].startNS + dealArr[j].dur - targetList[i].ts)) { + resultList.push({ 'thread': targetList[i].tid + '_' + targetList[i].thread, 'count': (dealArr[j].value * targetList[i].dur) / 1000, 'cpu': targetList[i].cpu, 'freq': dealArr[j].value, 'dur': targetList[i].dur, 'percent': targetList[i].dur / sum * 100, 'state': 'Running', 'ts': targetList[i].ts }); + break; + } else { + resultList.push({ 'thread': targetList[i].tid + '_' + targetList[i].thread, 'count': (dealArr[j].value * (dealArr[j].startNS + dealArr[j].dur - targetList[i].ts)) / 1000, 'cpu': targetList[i].cpu, 'freq': dealArr[j].value, 'dur': (dealArr[j].startNS + dealArr[j].dur - targetList[i].ts), 'percent': (dealArr[j].startNS + dealArr[j].dur - targetList[i].ts) / sum * 100, 'state': 'Running', 'ts': targetList[i].ts }); + } + } + } else { + if ((targetList[i].ts + targetList[i].dur) > dealArr[j].startNS) { + if ((targetList[i].dur + targetList[i].ts - dealArr[j].startNS) < dealArr[j].dur) { + resultList.push({ 'thread': targetList[i].tid + '_' + targetList[i].thread, 'count': (dealArr[j].value * (targetList[i].dur + targetList[i].ts - dealArr[j].startNS)) / 1000, 'cpu': targetList[i].cpu, 'freq': dealArr[j].value, 'dur': (targetList[i].dur + targetList[i].ts - dealArr[j].startNS), 'percent': (targetList[i].dur + targetList[i].ts - dealArr[j].startNS) / sum * 100, 'state': 'Running', 'ts': dealArr[j].startNS }); + break; + } else { + resultList.push({ 'thread': targetList[i].tid + '_' + targetList[i].thread, 'count': (dealArr[j].value * dealArr[j].dur) / 1000, 'cpu': targetList[i].cpu, 'freq': dealArr[j].value, 'dur': dealArr[j].dur, 'percent': dealArr[j].dur / sum * 100, 'state': 'Running', 'ts': dealArr[j].startNS }); + } + } else { + resultList.push({ 'thread': targetList[i].tid + '_' + targetList[i].thread, 'count': 0, 'cpu': targetList[i].cpu, 'freq': 'unknown', 'dur': targetList[i].dur, 'percent': targetList[i].dur / sum * 100, 'state': 'Running', 'ts': targetList[i].ts }); + break; + } + } + } + } + } + //合并同一线程内,当运行所在cpu和频点相同时,dur及percent进行累加求和,或许可以进行算法优化 + for (let i = 0; i < resultList.length; i++) { + for (let j = i + 1; j < resultList.length; j++) { + if (resultList[i].cpu == resultList[j].cpu && resultList[i].freq == resultList[j].freq) { + resultList[i].dur += resultList[j].dur; + resultList[i].percent += resultList[j].percent; + resultList[i].count += resultList[j].count; + resultList.splice(j, 1); + j--; + } + } + resultList[i].percent = Number((resultList[i].percent).toFixed(2)); + resultList[i].ts = resultList[i].ts - threadStatesParam.recordStartNs; + } + finalResultArr[0].children.sort((a: any, b: any) => a.cpu - b.cpu); + // 转成树结构数据进行展示 + for (let i = 0; i < finalResultArr[0].children.length; i++) { + for (let j = 0; j < resultList.length; j++) { + if (finalResultArr[0].children[i].cpu == resultList[j].cpu) { + finalResultArr[0].children[i].children.push(resultList[j]); + finalResultArr[0].children[i].dur += resultList[j].dur; + finalResultArr[0].children[i].percent += resultList[j].percent; + finalResultArr[0].children[i].count += resultList[j].count; + resultList.splice(j, 1); + j--; + } + } + finalResultArr[0].children[i].percent = finalResultArr[0].children[i].percent.toFixed(2); + finalResultArr[0].dur += finalResultArr[0].children[i].dur; + finalResultArr[0].count += finalResultArr[0].children[i].count; + } + this.threadStatesTblSource = finalResultArr; + this.threadStatesTbl!.recycleDataSource = finalResultArr; + } else { + this.threadStatesTblSource = []; + this.threadStatesTbl!.recycleDataSource = []; + } + }); + }); + + }) + } + initElements(): void { + this.threadStatesTbl = this.shadowRoot?.querySelector('#tb-running-percent'); + } + connectedCallback() { + super.connectedCallback(); + resizeObserver(this.parentElement!, this.threadStatesTbl!); + } + initHtml(): string { + return ` + + + + + + + + + + + + + + + + + + ` + } + +} \ No newline at end of file diff --git a/ide/src/trace/database/SqlLite.ts b/ide/src/trace/database/SqlLite.ts index 0d8e598656327283feddee0d4f92e4d655c4d7f5..c0852e003f09cda89922795a81430cf8f2e3b328 100644 --- a/ide/src/trace/database/SqlLite.ts +++ b/ide/src/trace/database/SqlLite.ts @@ -296,8 +296,8 @@ export class DbPool { } } }; - thread!.onmessageerror = (e) => {}; - thread!.onerror = (e) => {}; + thread!.onmessageerror = (e) => { }; + thread!.onerror = (e) => { }; thread!.id = i; thread!.busy = false; this.works?.push(thread!); @@ -1271,8 +1271,7 @@ export const queryVirtualMemory = (): Promise> => export const queryVirtualMemoryData = (filterId: number): Promise> => query( 'queryVirtualMemoryData', - `select ts-${ - (window as any).recordStartNS + `select ts-${(window as any).recordStartNS } as startTime,value,filter_id as filterID from sys_mem_measure where filter_id=$filter_id`, { $filter_id: filterId } ); @@ -3839,11 +3838,9 @@ export const queryEbpfSamplesCount = (startTime: number, endTime: number, ipids: select fsCount, vmCount from -(select count(1) as fsCount from file_system_sample s,trace_range t where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${ - ipids.length > 0 ? `and s.ipid in (${ipids.join(',')})` : '' +(select count(1) as fsCount from file_system_sample s,trace_range t where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${ipids.length > 0 ? `and s.ipid in (${ipids.join(',')})` : '' }) -,(select count(1) as vmCount from paged_memory_sample s,trace_range t where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${ - ipids.length > 0 ? `and s.ipid in (${ipids.join(',')})` : '' +,(select count(1) as vmCount from paged_memory_sample s,trace_range t where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${ipids.length > 0 ? `and s.ipid in (${ipids.join(',')})` : '' }); `, { $startTime: startTime, $endTime: endTime } @@ -5494,3 +5491,93 @@ export const queryTraceType = (): Promise< export const queryTransferList = (): Promise> => query('queryTransferList', `select id, report_value as cmdStr from perf_report where report_type = 'config_name'`); + +export const getTabRunningPercent = (tIds: Array, leftNS: number, rightNS: number): Promise> => + query( + 'getTabRunningPercent', + ` + select + B.pid,B.tid,B.state,B.cpu,B.dur,B.ts + from + thread_state AS B + left join + trace_range AS TR + where + B.tid in (${tIds.join(',')}) + and + B.state='Running' + and + not ((B.ts - TR.start_ts + ifnull(B.dur,0) < ${leftNS}) or (B.ts - TR.start_ts > ${rightNS})) + order by ts + `, + { $leftNS: leftNS, $rightNS: rightNS } + ); + +export const querySearchFuncData = (funcName: string, tIds: number, leftNS: number, rightNS: number): Promise> => + query( + 'querySearchFuncData', + ` + select + c.cookie, + c.id, + c.name as funName, + c.ts - r.start_ts as startTime, + c.dur, + c.depth, + t.tid, + t.name as threadName, + p.pid, + 'func' as type + from + callstack c + left join + thread t + on + c.callid = t.id + left join + process p + on + t.ipid = p.id + left join + trace_range r + where + c.name = '${funcName}' + and + t.tid = ${tIds} + and + not ((startTime < ${leftNS}) or (startTime > ${rightNS})); + `, + { $search: funcName } + ); + +export const queryCpuFreqUsageData = (Ids: Array): Promise> => + query( + 'queryCpuFreqUsageData', + `select + value, + ifnull(dur,tb.end_ts - c.ts) dur, + ts-tb.start_ts as startNS, + filter_id + from + measure c, + trace_range tb + where + c.filter_id in (${Ids.join(',')}) + ` + ); + +export const queryCpuFreqFilterId = (): Promise> => + query( + 'queryCpuFreqFilterId', + ` + select + id, + cpu + from + cpu_measure_filter + where + name='cpufreq' + or + name='cpu_frequency' + ` + ); \ No newline at end of file