diff --git a/ide/src/base-ui/chart/scatter/LitChartScatter.ts b/ide/src/base-ui/chart/scatter/LitChartScatter.ts new file mode 100644 index 0000000000000000000000000000000000000000..26d9f365327e30eb2bca6b25b4fee0e9733f82b3 --- /dev/null +++ b/ide/src/base-ui/chart/scatter/LitChartScatter.ts @@ -0,0 +1,546 @@ +/* + * 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 { resizeCanvas } from '../helper.js'; +import { BaseElement, element } from '../../BaseElement.js'; +import { LitChartScatterConfig } from "./LitChartScatterConfig.js"; + +@element('lit-chart-scatter') +export class LitChartScatter extends BaseElement { + private scatterTipEL: HTMLDivElement | null | undefined; + private labelsEL: HTMLDivElement | null | undefined; + canvas: HTMLCanvasElement | undefined | null; + canvas2: HTMLCanvasElement | undefined | null; + ctx: CanvasRenderingContext2D | undefined | null; + originX: number = 0; + finalX: number = 0; + originY: number = 0; + finalY: number = 0; + options: LitChartScatterConfig | undefined; + + set config(LitChartScatterConfig: LitChartScatterConfig) { + this.options = LitChartScatterConfig; + this.init(); + } + init(): void { + if (this.options) { + // 清楚上一次绘制的数据 + this.ctx?.clearRect(0, 0, this.clientWidth, this.clientHeight); + this.drawBackground(); + this.drawScatterChart(this.options); + //使用off-screen-canvas保存绘制的像素点 + this.setOffScreen(); + this.labelsEL!.innerText = this.options.title; + } + } + // 使用离屏技术保存绘制的像素点 + setOffScreen(): void { + this.canvas2 = document.createElement('canvas'); + this.canvas2.height = this.clientHeight; + this.canvas2.width = this.clientWidth; + let context2 = this.canvas2.getContext('2d'); + if (this.canvas?.width != 0 && this.canvas?.height != 0) { + context2!.drawImage(this.canvas!, 0, 0); + } + } + /*绘制渐变色背景*/ + drawBackground(): void { + let w: number = this.clientWidth; + let h: number = this.clientHeight; + let color: CanvasGradient = this.ctx?.createRadialGradient(w / 2, h / 2, 0.2 * w, w / 2, h / 2, 0.5 * w)!; + color?.addColorStop(0, '#eaeaea'); + color?.addColorStop(1, '#ccc'); + if (this.options) { + this.options!.globalGradient = color; + } + this.ctx?.save(); + this.ctx!.fillStyle = color; + this.ctx?.fillRect(0, 0, w, h); + this.ctx?.restore(); + } + /** + * 绘制散点图 + */ + drawScatterChart(options: LitChartScatterConfig): void { + this.drawAxis(options); //绘制坐标轴 + this.drawYLabels(options); //绘制y轴坐标 + this.drawXLabels(options); //绘制x轴坐标 + let drawload: boolean = false; + if (options) { + drawload = options.drawload; + } + if (drawload) { + let load: Array = []; + if (options) { + load = options.load; + this.drawBalanceLine(load);//绘制均衡线 + this.drawLoadLine(load);//绘制最大负载线 + } + } + this.drawData(options);//绘制散点图 + } + /** + * 绘制坐标轴 + */ + drawAxis(options: LitChartScatterConfig): void { + let text: Array = new Array(); + if (options) { + text = options.AxisLabel; + } + this.ctx!.font = "10px KATTI"; + this.ctx!.fillStyle = "#000000"; + this.ctx!.strokeStyle = "#000000"; + // 画x轴 + this.ctx?.beginPath(); + this.ctx?.moveTo(this.originX, this.originY); + this.ctx?.lineTo(this.finalX, this.originY); + this.ctx?.fillText(text[0], this.finalX, this.originY); + this.ctx?.stroke(); + // 画Y轴 + this.ctx?.beginPath(); + this.ctx?.moveTo(this.originX, this.originY); + this.ctx?.lineTo(this.originX, this.finalY); + this.ctx?.fillText(text[1], this.originX - 20, this.finalY - 10); + this.ctx?.stroke(); + } + /** + * 绘制y轴坐标 + */ + drawYLabels(options: LitChartScatterConfig): void { + // 添加原点刻度 + this.ctx!.font = "12px KATTI"; + this.ctx!.fillStyle = "#000000"; + this.ctx!.strokeStyle = "#000000"; + this.ctx?.fillText("0", this.originX - 5, this.originY + 10); + let yAxis: Array = []; + if (options) { + yAxis = options.yAxisLabel; + } + // 画Y轴坐标尺 + for (let i = 0; i < yAxis.length; i++) { + let length1 = ((this.originY - this.finalY) - ((this.originY - this.finalY) % 100)) * (yAxis[i] / yAxis[yAxis.length - 1]); + let length2 = this.originY - length1; + this.ctx?.beginPath(); + this.ctx?.moveTo(this.originX, length2); + this.ctx?.lineTo(this.originX + 5, length2); + this.ctx?.fillText(yAxis[i].toString(), this.originX - 40, length2 + 5); + this.ctx?.stroke(); + } + + } + /** + * 绘制x轴坐标 + */ + drawXLabels(options: LitChartScatterConfig): void { + // 画X轴坐标尺 + this.ctx!.fillStyle = "#000000"; + this.ctx!.strokeStyle = "#000000"; + let xAxis: Array = []; + if (options) { + xAxis = options.xAxisLabel; + } + for (let i = 0; i < xAxis.length; i++) { + let length3 = ((this.finalX - this.originX) - ((this.finalX - this.originX) % 100)) * (xAxis[i] / xAxis[xAxis.length - 1]); + let length4 = this.originX + length3; + this.ctx?.beginPath(); + this.ctx?.moveTo(length4, this.originY); + this.ctx?.lineTo(length4, this.originY - 5); + this.ctx?.fillText(xAxis[i].toString(), length4 - 5, this.originY + 10); + this.ctx?.stroke(); + } + } + + /** + * 绘制数据 + */ + drawData(options: LitChartScatterConfig): void { + let data: Array>> = []; + let yAxis: Array = []; + let xAxis: Array = []; + let colorPool: Array = new Array(); + let colorPoolText: Array = new Array(); + let rectY: number = this.clientHeight * 0.05; + if (options) { + data = options.data; + yAxis = options.yAxisLabel; + xAxis = options.xAxisLabel; + colorPool = options.colorPool(); + colorPoolText = options.colorPoolText(); + options.paintingData = [] + } + let xLength = ((this.finalX - this.originX) - ((this.finalX - this.originX) % 100)); + let yLength = ((this.originY - this.finalY) - ((this.originY - this.finalY) % 100)); + for (let i = 0; i < data.length; i++) { + for (let j = 0; j < data[i].length; j++) { + // 打点x坐标 + let x = this.originX + (data[i][j][0] / xAxis[xAxis.length - 1]) * xLength; + // 打点y坐标 + let y = this.originY - (data[i][j][1] / yAxis[yAxis.length - 1]) * yLength; + let r = 6; + if (i > 0) { + options.paintingData[data[i][j][2] - 1] = { + x, y, r, c: data[i][j], color: colorPool[i] + }; + } else { + options.paintingData.push({ + x, y, r, c: data[i][j], color: colorPool[i] + }); + } + this.drawCycle(x, y, r, 0.8, colorPool[i]); + } + if (data[i].length) { + rectY = rectY + 20; + this.ctx?.fillText(colorPoolText[i] + ": ", this.clientWidth - 70, rectY + 4); + this.drawCycle(this.clientWidth - 20, rectY, 7.5, 0.8, colorPool[i]); + } + } + } + /** + * 画圆点 + */ + drawCycle(x: number, y: number, r: number, transparency: number, color: string): void { + this.ctx!.fillStyle = color; + this.ctx?.beginPath(); + this.ctx!.globalAlpha = transparency; + this.ctx?.arc(x, y, r, 0, Math.PI * 2, true); + this.ctx?.closePath(); + this.ctx?.fill(); + + } + + /** + * 绘制最大负载线 + */ + drawLoadLine(data: Array): void { + let maxXAxis: number = 1; + if (this.options) { + maxXAxis = this.options.xAxisLabel[this.options.xAxisLabel.length - 1]; + } + // data[1]用来标注n Hz负载线 + let addr1: number = this.originX + ((this.finalX - this.originX) - ((this.finalX - this.originX) % 100)) * (data[0] / maxXAxis); + let addr2: number = ((this.originY - this.finalY) - ((this.originY - this.finalY) % 100)) / 60; + let y: number = this.originY; + this.ctx!.strokeStyle = "#ff0000"; + for (let i = 0; i < 60; i++) { + this.ctx?.beginPath(); + this.ctx?.moveTo(addr1, y); + y -= addr2; + this.ctx?.lineTo(addr1, y); + if (i % 2 != 0) { + this.ctx?.stroke(); + + } + } + this.ctx!.font = "10px KATTI"; + this.ctx!.fillStyle = "#ff0000"; + this.ctx?.fillText(data[1] + 'Hz最大负载线', addr1 - 20, this.originY - addr2 * 60 - 15); + this.ctx!.fillStyle = "#000000"; + this.ctx?.fillText('过供给区', addr1 / 2, y + 30); + this.ctx?.fillText('欠供给区', addr1 / 2, this.originY - this.finalY); + this.ctx?.fillText('超负载区', addr1 + 20, (this.finalY + this.originY) / 2); + } + + /** + * 绘制均衡线 + */ + drawBalanceLine(data: Array): void { + let maxXAxis: number = 1; + if (this.options) { + maxXAxis = this.options.xAxisLabel[this.options.xAxisLabel.length - 1]; + } + // data[1]用来标注n Hz均衡线 + let addr1: number = ((this.finalX - this.originX) - ((this.finalX - this.originX) % 100)) * (data[0] / maxXAxis) / 60; + let addr2: number = ((this.originY - this.finalY) - ((this.originY - this.finalY) % 100)) / 60; + let x: number = this.originX; + let y: number = this.originY; + this.ctx!.strokeStyle = "#00ff00"; + for (let i = 0; i < 60; i++) { + this.ctx?.beginPath(); + this.ctx?.moveTo(x, y); + x += addr1; + y -= addr2; + this.ctx?.lineTo(x, y); + if (i % 2 == 0) { + this.ctx?.stroke(); + + } + } + this.ctx?.save(); + this.ctx?.translate(addr1 * 25 + this.originX, addr2 * 40 + this.finalY); + this.ctx!.font = "10px KATTI"; + this.ctx!.fillStyle = "#ff0f00"; + this.ctx?.rotate(-Math.atan(addr2 / addr1)); + this.ctx?.fillText(data[1] + 'Hz均衡线', 0, 0); + this.ctx?.restore(); + } + + /*检测是否hover在散点之上*/ + checkHover(options: LitChartScatterConfig | undefined, pos: Object): Object | boolean { + let data: Array = []; + if (options) { + data = options.paintingData; + } + let found: boolean | Object = false; + for (let i = 0; i < data.length; i++) { + found = false; + // @ts-ignore + if (Math.sqrt(Math.pow(pos.x - data[i].x, 2) + Math.pow(pos.y - data[i].y, 2)) < data[i].r) { + found = data[i]; + break; + } + } + return found; + } + + /*绘制hover状态*/ + paintHover(): void { + let obj: Object | null = this.options!.hoverData; + // @ts-ignore + let x: number = obj?.x; + // @ts-ignore + let y: number = obj?.y; + // @ts-ignore + let r: number = obj?.r; + // @ts-ignore + let c: string = obj?.color; + let step: number = 0.5; + this.ctx!.globalAlpha = 1; + this.ctx!.fillStyle = c; + for (let i = 0; i < 10; i++) { + this.ctx?.beginPath(); + this.ctx?.arc(x, y, r + i * step, 0, 2 * Math.PI, false); + this.ctx?.fill(); + this.ctx?.closePath(); + } + } + //利用离屏canvas恢复hover前的状态 + resetHoverWithOffScreen(): void { + let obj: Object | null = null; + if (this.options) { + obj = this.options.hoverData; + } + if (!obj) return; + // @ts-ignore + let { x, y, r, c, color } = obj; + let step = 0.5; + this.ctx!.globalAlpha = 1; + for (let i = 10; i > 0; i--) { + this.ctx?.save(); + //绘制外圆范围 + this.ctx?.drawImage(this.canvas2!, x - r - 12 * step, y - r - 12 * step, 2 * (r + 12 * step), 2 * (r + 12 * step), x - r - 12 * step, y - r - 12 * step, 2 * (r + 12 * step), 2 * (r + 12 * step)); + //绘制内圆 + this.ctx?.beginPath(); + this.ctx?.arc(x, y, r + i * step, 0, 2 * Math.PI, false); + this.ctx?.closePath(); + this.ctx!.fillStyle = color; + this.ctx!.globalAlpha = 0.8; + //填充内圆 + this.ctx?.fill(); + this.ctx?.restore(); + } + this.options!.hoverData = null; + } + /** + * 显示提示框 + */ + showTip(data: any): void { + this.scatterTipEL!.style.display = 'flex'; + this.scatterTipEL!.style.top = `${data.y - 70}px`; + this.scatterTipEL!.style.left = `${data.x}px`; + this.scatterTipEL!.innerHTML = this.options!.tip(data); + // @ts-ignore + this.options!.hoverEvent('CPU-FREQ', true, data.c[2] - 1); + } + /** + * 隐藏提示框 + */ + hideTip(): void { + this.scatterTipEL!.style.display = 'none'; + if (this.options) { + // @ts-ignore + this.options!.hoverEvent('CPU-FREQ', false); + } + } + + connectedCallback(): void { + super.connectedCallback(); + this.canvas = this.shadowRoot!.querySelector('#canvas'); + this.scatterTipEL = this.shadowRoot!.querySelector('#tip'); + this.ctx = this.canvas!.getContext('2d', { alpha: true }); + this.labelsEL = this.shadowRoot!.querySelector('#shape'); + resizeCanvas(this.canvas!); + this.originX = this.clientWidth * 0.1; + this.originY = this.clientHeight * 0.9; + this.finalX = this.clientWidth; + this.finalY = this.clientHeight * 0.1; + + /*hover效果*/ + this.canvas!.onmousemove = (event) => { + let pos: Object = { + x: event.offsetX, + y: event.offsetY + } + let hoverPoint: Object | boolean = this.checkHover(this.options, pos); + /** + * 如果当前有聚焦点 + */ + if (hoverPoint) { + this.showTip(hoverPoint); + let samePoint: boolean = this.options!.hoverData === hoverPoint ? true : false; + if (!samePoint) { + this.resetHoverWithOffScreen(); + this.options!.hoverData = hoverPoint; + } + this.paintHover(); + } else { + //使用离屏canvas恢复 + this.resetHoverWithOffScreen(); + this.hideTip(); + } + } + } + + initElements(): void { + new ResizeObserver((entries, observer) => { + entries.forEach((it) => { + resizeCanvas(this.canvas!); + this.originX = this.clientWidth * 0.1; + this.originY = this.clientHeight * 0.95; + this.finalX = this.clientWidth * 0.9; + this.finalY = this.clientHeight * 0.1; + this.labelsEL!.innerText = ''; + this.init(); + }); + }).observe(this); + } + + initHtml(): string { + return ` + +
+
+ +
+
+
`; + } +} + + + + + + + + + + + + + + + + + + + + + diff --git a/ide/src/base-ui/chart/scatter/LitChartScatterConfig.ts b/ide/src/base-ui/chart/scatter/LitChartScatterConfig.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb9384ef58b212dd49306433d28ccee3798af676 --- /dev/null +++ b/ide/src/base-ui/chart/scatter/LitChartScatterConfig.ts @@ -0,0 +1,45 @@ +/* + * 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. + */ + +export interface LitChartScatterConfig { + // y轴坐标数组 + yAxisLabel: Array; + // x轴坐标数组 + xAxisLabel: Array; + // 坐标轴名称 + AxisLabel: Array; + // 用于判断是否绘制负载线及均衡线 + drawload: boolean; + // 用于存放最大负载线及均衡线的参数值 + load: Array; + // 打点数据 + data: Array>>; + // 用于存放绘图数据 + paintingData: Array; + // 用于存放移入点数据 + hoverData: Object | null; + // 渐变色信息 + globalGradient: CanvasGradient | undefined; + // 颜色池 + colorPool: () => Array; + // 移入事件 + hoverEvent: void; + // 图表名称 + title: string; + // 颜色数据名称 + colorPoolText: () => Array; + // 提示信息 + tip: (a: any) => string; +} \ No newline at end of file diff --git a/ide/src/trace/SpApplication.ts b/ide/src/trace/SpApplication.ts index 670470b23d88976b75347945767f5e1776151d26..299f7d8095164045d00ea4287df7ec9f240f1169 100644 --- a/ide/src/trace/SpApplication.ts +++ b/ide/src/trace/SpApplication.ts @@ -57,6 +57,7 @@ import { convertPool } from './database/Convert.js'; import { LongTraceDBUtils } from './database/LongTraceDBUtils.js'; import { type SpKeyboard } from './component/SpKeyboard.js'; import './component/SpKeyboard.js'; +import "../base-ui/chart/scatter/LitChartScatter.js"; @element('sp-application') export class SpApplication extends BaseElement { diff --git a/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqDataCut.ts b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqDataCut.ts index 69f4b271d3a2e1f2eac2c3ce16f4998a8137ff77..4f2accc5316b5830aa64a1362db397bb47349d2f 100644 --- a/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqDataCut.ts +++ b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqDataCut.ts @@ -12,1012 +12,903 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { BaseElement, element } from '../../../../../base-ui/BaseElement.js'; import { LitTable, RedrawTreeForm } from '../../../../../base-ui/table/lit-table.js'; -import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; -import '../../../StackBar.js'; -import { - getTabRunningPercent, - querySearchFuncData, - queryCpuFreqUsageData, - queryCpuFreqFilterId, -} from '../../../../database/SqlLite.js'; +import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection.js'; +import '../../../StackBar.js' +import { getTabRunningPercent, queryCpuFreqUsageData, queryCpuFreqFilterId, querySearchFuncData } from '../../../../database/SqlLite.js'; import { Utils } from '../../base/Utils.js'; import { resizeObserver } from '../SheetUtils.js'; import { SliceGroup } from '../../../../bean/StateProcessThread.js'; +import { LitChartScatter } from '../../../../../base-ui/chart/scatter/LitChartScatter.js'; +import { SegMenTaTion } from "../../../chart/SpSegmentationChart.js"; +import { TabPaneFreqUsageConfig, TabPaneRunningConfig, TabPaneCpuFreqConfig } from "./TabPaneFreqUsageConfig.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: any = new Map(); - private processArr: any = new Array(); - private threadArr: any = new Array(); - - set data(threadStatesParam: SelectionParam | any) { - if (this.currentSelectionParam === threadStatesParam) { - return; - } - this.currentSelectionParam = threadStatesParam; - this.threadStatesTblSource = []; - this.threadStatesTbl!.recycleDataSource = []; - this.threadStatesTbl!.loading = true; - this.initData = new Map(); - 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 = ''; - // 查询running状态线程数据 - getTabRunningPercent(threadStatesParam.threadIds, threadStatesParam.leftNs, threadStatesParam.rightNs).then( - (result) => { - // 查询cpu及id信息 - queryCpuFreqFilterId().then((r) => { - // 以键值对形式将cpu及id进行对应,后续会将频点数据与其对应cpu进行整合 - 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); - } - // 通过id去查询频点数据 - 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 needDeal = new Map(); - let pidArr = new Array(); - let threadArr = new Array(); - // 整理进程级的数组信息 - let processArr: any = - threadStatesParam.processIds.length > 1 - ? [...new Set(threadStatesParam.processIds)] - : threadStatesParam.processIds; - for (let i of processArr) { - pidArr.push({ - process: Utils.PROCESS_MAP.get(i) == null ? 'Process ' + i : Utils.PROCESS_MAP.get(i) + ' ' + i, - thread: Utils.PROCESS_MAP.get(i) == null ? 'Process ' + i : Utils.PROCESS_MAP.get(i) + ' ' + i, - pid: i, - tid: '', - count: 0, - cpu: '', - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); - } - // 拷贝给私有属性,以便后续进行数据切割时免除整理进程层级数据 - this.processArr = JSON.parse(JSON.stringify(pidArr)); - for (let e of result) { - if (processArr.includes(e.pid) && e.state == 'Running') { - if (needDeal.get(e.pid + '_' + e.tid) == undefined) { - threadArr.push({ - process: - Utils.PROCESS_MAP.get(e.pid) == null - ? 'Process ' + e.pid - : Utils.PROCESS_MAP.get(e.pid) + ' ' + e.pid, - thread: Utils.THREAD_MAP.get(e.tid) + ' ' + e.tid, - pid: e.pid, - tid: e.tid, - count: 0, - cpu: '', - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); - needDeal.set(e.pid + '_' + e.tid, new Array()); - } - if ( - e.ts < threadStatesParam.leftNs + threadStatesParam.recordStartNs && - e.ts + e.dur > threadStatesParam.leftNs + threadStatesParam.recordStartNs - ) { - const ts = e.ts; - e.ts = threadStatesParam.leftNs + threadStatesParam.recordStartNs; - e.dur = ts + e.dur - (threadStatesParam.leftNs + threadStatesParam.recordStartNs); - } - if (e.ts + e.dur > threadStatesParam.rightNs + threadStatesParam.recordStartNs) { - e.dur = threadStatesParam.rightNs + threadStatesParam.recordStartNs - e.ts; - } - let arr = needDeal.get(e.pid + '_' + e.tid); - 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; - arr.push(e); - } - } - // 拷贝给私有属性,以便后续进行数据切割时免除整理线程层级数据 - this.threadArr = JSON.parse(JSON.stringify(threadArr)); - // 整理running线程数据、cpu层级信息 - this.mergeFreqData(needDeal, dealArr, sum); - this.threadStatesTbl!.loading = false; - } else { - this.threadStatesTblSource = []; - this.threadStatesTbl!.recycleDataSource = []; - this.threadStatesTbl!.loading = false; - } - }); - }); - } - ); - } - private threadClick(data: Array) { - 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) { - this.threadStatesTbl!.setStatus(data, false); - this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); - } else if (label.includes('Thread') && i === 1) { - for (let item of data) { - item.status = true; - if (item.children != undefined && item.children.length > 0) { - this.threadStatesTbl!.setStatus(item.children, false); - } - } - this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); - } else if (label.includes('Cycle') && i === 2) { - for (let item of data) { - item.status = true; - for (let value of item.children ? item.children : []) { - value.status = true; - if (value.children != undefined && value.children.length > 0) { - this.threadStatesTbl!.setStatus(value.children, false); - } - } - } - this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); - } else if (label.includes('CPU') && i === 3) { - this.threadStatesTbl!.setStatus(data, true); - this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Expand); - } - }); - } - } - let scatterData: any = this.threadStatesTbl?.shadowRoot?.querySelectorAll('.tree-first-body'); - if (scatterData) { - for (let j = 0; j < scatterData.length; j++) { - let scatter = scatterData[j].data; - scatterData[j].addEventListener('click', (e: any) => { - console.log(scatter); - }); - } - } - } + private threadStatesTbl: LitTable | null | undefined; + private threadStatesTblSource: Array = []; + private currentSelectionParam: SelectionParam | any; + private threadStatesDIV: HTMLDivElement | null | undefined; + private scatterInput: HTMLInputElement | null | undefined; + private initData: Map> = new Map(); + private processArr: Array = []; + private threadArr: Array = []; + private statisticsScatter: LitChartScatter | null | undefined; - 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.threadStatesTbl!.loading = true; - this.dataSingleCut(this.threadStatesDIV?.children[0], this.threadStatesDIV?.children[1], this.initData); - }); - this.threadStatesDIV?.children[2].children[1].addEventListener('click', (e) => { - this.threadStatesTbl!.loading = true; - this.dataLoopCut(this.threadStatesDIV?.children[0], this.threadStatesDIV?.children[1], this.initData); - }); - } - connectedCallback() { - super.connectedCallback(); - resizeObserver(this.parentElement!, this.threadStatesTbl!); - } - initHtml(): string { - return ` - -
- - -
- - -
-
- - - - - - - - - - - - - - - - - - - `; - } - sortByColumn(treadStateDetail: any) { - function compare(property: any, treadStatesSort: any, type: any) { - return function (threadStatesLeftData: SelectionData | any, threadStatesRightData: SelectionData | any) { - if (threadStatesLeftData.process == ' ' || threadStatesRightData.process == ' ') { - return 0; + let dealArr: Array = []; + // 通过id去查询频点数据 + let res: Array = await queryCpuFreqUsageData(queryId); + for (let i of res) { + let obj = new TabPaneCpuFreqConfig(i.startNS + threadStatesParam.recordStartNs, IdMap.get(i.filter_id)!, i.value, i.dur) + dealArr.push(obj); } - if (type === 'number') { - return treadStatesSort === 2 - ? parseFloat(threadStatesRightData[property]) - parseFloat(threadStatesLeftData[property]) - : parseFloat(threadStatesLeftData[property]) - parseFloat(threadStatesRightData[property]); - } else { - if (threadStatesRightData[property] > threadStatesLeftData[property]) { - return treadStatesSort === 2 ? 1 : -1; - } else if (threadStatesRightData[property] == threadStatesLeftData[property]) { - return 0; - } else { - return treadStatesSort === 2 ? -1 : 1; - } + return dealArr; + } + + /** + * 查询框选区域内的所有running状态数据 + */ + async queryRunningData(threadStatesParam: SelectionParam | any): Promise> { + let result: Array = await getTabRunningPercent(threadStatesParam.threadIds, threadStatesParam.leftNs, threadStatesParam.rightNs); + let needDeal: Map> = new Map(), sum: number = 0; + if (result != null && result.length > 0) { + let processArr: Array = threadStatesParam.processIds.length > 1 ? [...new Set(threadStatesParam.processIds)] : threadStatesParam.processIds; + for (let e of result) { + if (processArr.includes(e.pid)) { + if (needDeal.get(e.pid + '_' + e.tid) === undefined) { + this.threadArr.push(new TabPaneFreqUsageConfig(Utils.THREAD_MAP.get(e.tid) + ' ' + e.tid, '', e.pid, e.tid, 0, '', '', 0, '', 0, 'thread', -1, [])); + needDeal.set(e.pid + '_' + e.tid, new Array()); + } + if ((e.ts < (threadStatesParam.leftNs + threadStatesParam.recordStartNs)) && ((e.ts + e.dur) > (threadStatesParam.leftNs + threadStatesParam.recordStartNs))) { + const ts = e.ts; + e.ts = threadStatesParam.leftNs + threadStatesParam.recordStartNs; + e.dur = ts + e.dur - (threadStatesParam.leftNs + threadStatesParam.recordStartNs); + } + if ((e.ts + e.dur) > (threadStatesParam.rightNs + threadStatesParam.recordStartNs)) { + e.dur = threadStatesParam.rightNs + threadStatesParam.recordStartNs - e.ts; + } + 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; + let arr: any = needDeal.get(e.pid + '_' + e.tid); + sum += e.dur; + arr.push(e); + } + } } - }; + return [needDeal, sum]; } - if (treadStateDetail.key === 'name' || treadStateDetail.key === 'thread' || treadStateDetail.key === 'state') { - this.threadStatesTblSource.sort(compare(treadStateDetail.key, treadStateDetail.sort, 'string')); - } else { - this.threadStatesTblSource.sort(compare(treadStateDetail.key, treadStateDetail.sort, 'number')); + /** + * 将cpu频点数据与running状态数据整合,保证其上该段时长内有对应的cpu频点数据 + */ + mergeFreqData(needDeal: Map>, dealArr: Array, sum: number): void { + needDeal.forEach((value: Array, key: string) => { + let resultList: Array = []; + for (let i = 0; i < value.length; i++) { + for (let j = 0; j < dealArr.length; j++) { + // 只需要判断running状态数据与频点数据cpu相同的情况 + if (value[i].cpu === dealArr[j].cpu) { + // running状态数据的开始时间大于频点数据开始时间,小于频点结束时间。且running状态数据的持续时间小于频点结束时间减去running状态数据开始时间的情况 + if (value[i].ts > dealArr[j].startNS && value[i].ts < (dealArr[j].startNS + dealArr[j].dur) && value[i].dur < (dealArr[j].startNS + dealArr[j].dur - value[i].ts)) { + resultList.push(new TabPaneFreqUsageConfig(value[i].thread, value[i].ts, value[i].pid, value[i].tid, 0, value[i].cpu, dealArr[j].value, value[i].dur, '', value[i].dur / sum * 100, 'freqdata', -1, undefined)); + break; + } + // running状态数据的开始时间大于频点数据开始时间,小于频点结束时间。且running状态数据的持续时间大于频点结束时间减去running状态数据开始时间的情况 + if (value[i].ts > dealArr[j].startNS && value[i].ts < (dealArr[j].startNS + dealArr[j].dur) && value[i].dur > (dealArr[j].startNS + dealArr[j].dur - value[i].ts)) { + resultList.push(new TabPaneFreqUsageConfig(value[i].thread, value[i].ts, value[i].pid, value[i].tid, 0, value[i].cpu, dealArr[j].value, (dealArr[j].startNS + dealArr[j].dur - value[i].ts), '', (dealArr[j].startNS + dealArr[j].dur - value[i].ts) / sum * 100, 'freqdata', -1, undefined)); + } + // running状态数据的开始时间小于频点数据开始时间,running状态数据的结束时间大于频点数据开始时间。且running状态数据在频点数据开始时间后的持续时间小于频点数据持续时间的情况 + if (value[i].ts < dealArr[j].startNS && (value[i].ts + value[i].dur) > dealArr[j].startNS && (value[i].dur + value[i].ts - dealArr[j].startNS) < dealArr[j].dur) { + resultList.push(new TabPaneFreqUsageConfig(value[i].thread, dealArr[j].startNS, value[i].pid, value[i].tid, 0, value[i].cpu, dealArr[j].value, (value[i].dur + value[i].ts - dealArr[j].startNS), '', (value[i].dur + value[i].ts - dealArr[j].startNS) / sum * 100, 'freqdata', -1, undefined)); + break; + } + // running状态数据的开始时间小于频点数据开始时间,running状态数据的结束时间大于频点数据开始时间。且running状态数据在频点数据开始时间后的持续时间大于频点数据持续时间的情况 + if (value[i].ts < dealArr[j].startNS && (value[i].ts + value[i].dur) > dealArr[j].startNS && (value[i].dur + value[i].ts - dealArr[j].startNS) > dealArr[j].dur) { + resultList.push(new TabPaneFreqUsageConfig(value[i].thread, dealArr[j].startNS, value[i].pid, value[i].tid, 0, value[i].cpu, dealArr[j].value, dealArr[j].dur, '', dealArr[j].dur / sum * 100, 'freqdata', -1, undefined)); + } + // running状态数据的开始时间小于频点数据开始时间,running状态数据的持续时间小于频点数据开始时间的情况 + if (value[i].ts < dealArr[j].startNS && (value[i].ts + value[i].dur) < dealArr[j].startNS) { + resultList.push(new TabPaneFreqUsageConfig(value[i].thread, value[i].ts, value[i].pid, value[i].tid, 0, value[i].cpu, 'unknown', value[i].dur, '', value[i].dur / sum * 100, 'freqdata', -1, undefined)); + break; + } + } + } + } + this.initData.set(key, resultList); + }) } - this.threadStatesTbl!.recycleDataSource = this.threadStatesTblSource; - } - // single方式切割数据功能 - dataSingleCut(threadId: any, threadFunc: any, resultList: any) { - threadId.style.border = '1px solid rgb(151,151,151)'; - threadFunc.style.border = '1px solid rgb(151,151,151)'; - 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 (threadIdValue != '' && threadFuncName != '') { - // 根据用户输入的线程ID,方法名去查询数据库,得到对应的方法起始时间,持续时间等数据,以便作为依据进行后续数据切割 - querySearchFuncData(threadFuncName, Number(threadIdValue), leftNS, rightNS).then((result) => { - if (result != null && result.length > 0) { - let targetMap = new Map(); - // 新创建map对象接收传过来的实参map - resultList.forEach((item: any, key: any) => { - targetMap.set(key, JSON.parse(JSON.stringify(item))); - }); - 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 }); + /** + * single方式切割数据功能 + */ + dataSingleCut(threadId: HTMLInputElement, threadFunc: HTMLInputElement, resultList: Map>): void { + threadId.style.border = '1px solid rgb(151,151,151)'; + threadFunc.style.border = '1px solid rgb(151,151,151)'; + 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 (threadIdValue != '' && threadFuncName != '') { + // 根据用户输入的线程ID,方法名去查询数据库,得到对应的方法起始时间,持续时间等数据,以便作为依据进行后续数据切割 + querySearchFuncData(threadFuncName, Number(threadIdValue), leftNS, rightNS).then(result => { + if (result != null && result.length > 0) { + // targetMap为全局initData的拷贝对象,dealArr数组用来存放周期切割依据数据 + let targetMap: Map> = new Map(), dealArr: Array = []; + // 新创建map对象接收传过来的实参map + resultList.forEach((item: Array, key: string) => { + targetMap.set(key, JSON.parse(JSON.stringify(item))); + }) + // 整理周期切割依据的数据 + for (let i of result) { + if (i.startTime + this.currentSelectionParam.recordStartNs + i.dur < this.currentSelectionParam.rightNs + this.currentSelectionParam.recordStartNs) { + dealArr.push({ 'ts': i.startTime + this.currentSelectionParam.recordStartNs, 'dur': i.dur }); + } + } + let cycleMap: Map> = new Map(), totalList: Map> = new Map(); + this.mergeSingleData(dealArr, targetMap, cycleMap, totalList); + // 拷贝线程数组,防止数据污染 + let threadArr = JSON.parse(JSON.stringify(this.threadArr)); + // 拷贝进程数组,防止数据污染 + let processArr = JSON.parse(JSON.stringify(this.processArr)); + // 将周期层级防止到线程层级下 + this.mergeThreadData(threadArr, cycleMap); + // 将原始数据放置到对应的线程层级下,周期数据前 + let totalData = this.merge(totalList); + this.mergeTotalData(threadArr, totalData); + // 合并数据到进程层级下 + this.mergePidData(processArr, threadArr); + this.fixedDeal(processArr); + this.threadStatesTblSource = processArr; + this.threadStatesTbl!.recycleDataSource = processArr; + this.threadStatesTbl!.loading = false; + this.threadClick(processArr); + } else { + this.threadStatesTblSource = []; + this.threadStatesTbl!.recycleDataSource = []; + this.threadStatesTbl!.loading = false; + } + }) + } else { + this.threadStatesTbl!.loading = false; + if (threadIdValue == '') { + threadId.style.border = '2px solid rgb(255,0,0)'; + } + if (threadFuncName == '') { + threadFunc.style.border = '2px solid rgb(255,0,0)'; } - } - let cycleMap = new Map(); - let totalList = new Map(); - targetMap.forEach((item, key) => { + } + } + /** + * 整合Single切割方式中的频点数据与方法周期数据 + */ + mergeSingleData(dealArr: Array, targetMap: Map>, cycleMap: Map>, totalList: Map>): void { + let timeDur = this.currentSelectionParam.recordStartNs; + targetMap.forEach((value, key) => { cycleMap.set(key, new Array()); totalList.set(key, new Array()); for (let i = 0; i < dealArr.length; i++) { - let cpuArr = new Array(); - let cpuMap = new Map(); - let resList = new Array(); - // 时间由纳秒转换为秒的倍数 - const multiple = 1000000000; - // 算力倍数值 - const countMutiple = 1000; - cpuMap.set(key, new Array()); - cycleMap - .get(key) - .push({ - thread: '周期' + (i + 1) + '—' + item[0].thread, - ts: (dealArr[i].ts - timeDur) / multiple, - count: 0, - cpu: '', - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); - let value = JSON.parse(JSON.stringify(item)); - for (let j = 0; j < value.length; j++) { - value[j].ts = value[j].ts * multiple; - value[j].freq = value[j].freq == 'unknown' ? 0 : value[j].freq; - if (!cpuArr.includes(value[j].cpu)) { - cpuArr.push(value[j].cpu); - cpuMap - .get(key) - .push({ - thread: '周期' + (i + 1) + '—' + value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: 0, - cpu: value[j].cpu, - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); - } - if (dealArr[i].ts < value[j].ts) { - if (dealArr[i].ts + dealArr[i].dur > value[j].ts) { - if (dealArr[i].ts + dealArr[i].dur > value[j].ts + value[j].dur) { - resList.push({ - thread: '周期' + (i + 1) + '—' + value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (value[j].freq * value[j].dur) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: value[j].dur, - percent: value[j].percent, - state: 'Running', - id: i, - }); - totalList - .get(key) - .push({ - thread: value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (value[j].freq * value[j].dur) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: value[j].dur, - percent: value[j].percent, - state: 'Running', - id: i, - }); - } else { - resList.push({ - thread: '周期' + (i + 1) + '—' + value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: ((dealArr[i].ts + dealArr[i].dur - value[j].ts) * value[j].freq) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: dealArr[i].ts + dealArr[i].dur - value[j].ts, - percent: ((dealArr[i].ts + dealArr[i].dur - value[j].ts) / value[j].dur) * value[j].percent, - state: 'Running', - id: i, - }); - totalList - .get(key) - .push({ - thread: value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: ((dealArr[i].ts + dealArr[i].dur - value[j].ts) * value[j].freq) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: dealArr[i].ts + dealArr[i].dur - value[j].ts, - percent: ((dealArr[i].ts + dealArr[i].dur - value[j].ts) / value[j].dur) * value[j].percent, - state: 'Running', - id: i, - }); - break; + let cpuArr: Array = [], resList: Array = [], cpuMap: Map> = new Map(); + // 时间倍数值 + const countMutiple = 1000000; + cpuMap.set(key, new Array()); + cycleMap.get(key)?.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[0].thread, ((dealArr[i].ts - timeDur) / 1000000).toFixed(3), key.split('_')[0], key.split('_')[1], 0, '', '', 0, (dealArr[i].dur / 1000000).toFixed(3), 0, 'cycle', i + 1, [])); + for (let j = 0; j < value.length; j++) { + // 判断若用户导入json文件,则替换为对应cpu下的对应频点的算力值进行算力消耗计算 + let consumptionMap: Map = SegMenTaTion.freqInfoMapData.size > 0 && SegMenTaTion.freqInfoMapData.get(value[j].cpu); + // 若存在算力值,则直接取值做计算。若不存在算力值,且频点值不为unknown的情况,则取频点值做计算,若为unknown,则取0做兼容 + const consumption = (consumptionMap && consumptionMap.get(value[j].freq)) ? consumptionMap.get(value[j].freq) : (value[j].freq == 'unknown' ? 0 : value[j].freq); + if (!cpuArr.includes(value[j].cpu)) { + cpuArr.push(value[j].cpu); + cpuMap.get(key)?.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[j].thread, '', value[j].pid, value[j].tid, 0, value[j].cpu, '', 0, '', 0, 'cpu', -1, [])); } - } - } else { - if (value[j].ts + value[j].dur > dealArr[i].ts) { - if (value[j].ts + value[j].dur > dealArr[i].ts + dealArr[i].dur) { - resList.push({ - thread: '周期' + (i + 1) + '—' + value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (dealArr[i].dur * value[j].freq) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: dealArr[i].dur, - percent: (dealArr[i].dur / value[j].dur) * value[j].percent, - state: 'Running', - id: i, - }); - totalList - .get(key) - .push({ - thread: value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (dealArr[i].dur * value[j].freq) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: dealArr[i].dur, - percent: (dealArr[i].dur / value[j].dur) * value[j].percent, - state: 'Running', - id: i, - }); - break; - } else { - resList.push({ - thread: '周期' + (i + 1) + '—' + value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: ((value[j].ts + value[j].dur - dealArr[i].ts) * value[j].freq) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: value[j].ts + value[j].dur - dealArr[i].ts, - percent: ((value[j].ts + value[j].dur - dealArr[i].ts) / value[j].dur) * value[j].percent, - state: 'Running', - id: i, - }); - totalList - .get(key) - .push({ - thread: value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: ((value[j].ts + value[j].dur - dealArr[i].ts) * value[j].freq) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: value[j].ts + value[j].dur - dealArr[i].ts, - percent: ((value[j].ts + value[j].dur - dealArr[i].ts) / value[j].dur) * value[j].percent, - state: 'Running', - id: i, - }); + // 以下为频点数据按Single周期切割数据如何取舍的判断条件,dealArr为周期切割依据,value为某一线程下的频点汇总数据 + // 如果频点数据开始时间大于某一周期起始时间,小于该周期的结束时间。且频点数据结束时间小于周期结束时间的情况 + if (dealArr[i].ts < value[j].ts && dealArr[i].ts + dealArr[i].dur > value[j].ts && dealArr[i].ts + dealArr[i].dur > value[j].ts + value[j].dur) { + resList.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[j].thread, '', value[j].pid, value[j].tid, (consumption * value[j].dur) / countMutiple, value[j].cpu, value[j].freq, value[j].dur, '', value[j].percent, 'freqdata', i, undefined)); + totalList.get(key)?.push(new TabPaneFreqUsageConfig(value[j].thread, '', value[j].pid, value[j].tid, (consumption * value[j].dur) / countMutiple, value[j].cpu, value[j].freq, value[j].dur, '', value[j].percent, 'freqdata', i, undefined)); + } + // 如果频点数据开始时间大于某一周期起始时间,小于该周期的结束时间。且频点数据结束时间大于等于周期结束时间的情况 + if (dealArr[i].ts < value[j].ts && dealArr[i].ts + dealArr[i].dur > value[j].ts && dealArr[i].ts + dealArr[i].dur <= value[j].ts + value[j].dur) { + resList.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[j].thread, '', value[j].pid, value[j].tid, (dealArr[i].ts + dealArr[i].dur - value[j].ts) * consumption / countMutiple, value[j].cpu, value[j].freq, dealArr[i].ts + dealArr[i].dur - value[j].ts, '', (dealArr[i].ts + dealArr[i].dur - value[j].ts) / value[j].dur * value[j].percent, 'freqdata', i, undefined)); + totalList.get(key)?.push(new TabPaneFreqUsageConfig(value[j].thread, '', value[j].pid, value[j].tid, (dealArr[i].ts + dealArr[i].dur - value[j].ts) * consumption / countMutiple, value[j].cpu, value[j].freq, dealArr[i].ts + dealArr[i].dur - value[j].ts, '', (dealArr[i].ts + dealArr[i].dur - value[j].ts) / value[j].dur * value[j].percent, 'freqdata', i, undefined)); + break; + } + // 如果频点数据开始时间小于某一周期起始时间,结束时间大于该周期的开始时间。且频点数据结束时间大于周期结束时间的情况 + if (dealArr[i].ts > value[j].ts && value[j].ts + value[j].dur > dealArr[i].ts && value[j].ts + value[j].dur > dealArr[i].ts + dealArr[i].dur) { + resList.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[j].thread, '', value[j].pid, value[j].tid, dealArr[i].dur * consumption / countMutiple, value[j].cpu, value[j].freq, dealArr[i].dur, '', dealArr[i].dur / value[j].dur * value[j].percent, 'freqdata', i, undefined)); + totalList.get(key)?.push(new TabPaneFreqUsageConfig(value[j].thread, '', value[j].pid, value[j].tid, dealArr[i].dur * consumption / countMutiple, value[j].cpu, value[j].freq, dealArr[i].dur, '', dealArr[i].dur / value[j].dur * value[j].percent, 'freqdata', i, undefined)); + break; + } + // 如果频点数据开始时间小于某一周期起始时间,结束时间大于该周期的开始时间。且频点数据结束时间小于等于周期结束时间的情况 + if (dealArr[i].ts > value[j].ts && value[j].ts + value[j].dur > dealArr[i].ts && value[j].ts + value[j].dur <= dealArr[i].ts + dealArr[i].dur) { + resList.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[j].thread, '', value[j].pid, value[j].tid, (value[j].ts + value[j].dur - dealArr[i].ts) * consumption / countMutiple, value[j].cpu, value[j].freq, value[j].ts + value[j].dur - dealArr[i].ts, '', (value[j].ts + value[j].dur - dealArr[i].ts) / value[j].dur * value[j].percent, 'freqdata', i, undefined)); + totalList.get(key)?.push(new TabPaneFreqUsageConfig(value[j].thread, '', value[j].pid, value[j].tid, (value[j].ts + value[j].dur - dealArr[i].ts) * consumption / countMutiple, value[j].cpu, value[j].freq, value[j].ts + value[j].dur - dealArr[i].ts, '', (value[j].ts + value[j].dur - dealArr[i].ts) / value[j].dur * value[j].percent, 'freqdata', i, undefined)); } - } - } - } - this.mergeData(resList); - resList.sort((a, b) => b.count - a.count); - cpuMap.get(key).sort((a: any, b: any) => a.cpu - b.cpu); - cpuMap.get(key).forEach((item: any) => { - for (let s = 0; s < resList.length; s++) { - if (item.cpu == resList[s].cpu) { - item.children.push(resList[s]); - item.count += resList[s].count; - item.dur += resList[s].dur; - item.percent += resList[s].percent; - } } - }); - // 将cpu数据放置到对应周期层级下 - this.mergeCycleData(cycleMap.get(key)[i], cpuMap.get(key)); + this.mergeData(resList); + // 整理排序相同周期下的数据 + this.mergeCpuData(cpuMap.get(key)!, resList); + // 将cpu数据放置到对应周期层级下 + this.mergeCycleData(cycleMap.get(key)![i], cpuMap.get(key)); } - }); - // 拷贝线程数组,防止数据污染 - let threadArr = JSON.parse(JSON.stringify(this.threadArr)); - // 拷贝进程数组,防止数据污染 - let processArr = JSON.parse(JSON.stringify(this.processArr)); - // 将周期层级防止到线程层级下 - this.mergeThreadData(threadArr, cycleMap); - // 将原始数据放置到对应的线程层级下,周期数据前 - let totalData = this.merge(totalList); - this.mergeTotalData(threadArr, totalData); - // 合并数据到进程层级下 - this.mergePidData(processArr, threadArr); - this.fixedDeal(processArr); - this.threadStatesTblSource = processArr; - this.threadStatesTbl!.recycleDataSource = processArr; - this.threadStatesTbl!.loading = false; - this.threadClick(processArr); + }); + } + /** + * Loop方式切割数据功能 + */ + dataLoopCut(threadId: HTMLInputElement, threadFunc: HTMLInputElement, resultList: Map>): void { + threadId.style.border = '1px solid rgb(151,151,151)'; + threadFunc.style.border = '1px solid rgb(151,151,151)'; + 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 (threadIdValue !== '' && threadFuncName !== '') { + querySearchFuncData(threadFuncName, Number(threadIdValue), leftNS, rightNS).then(res => { + if (res !== null && res.length > 0) { + // targetMap为全局initData的拷贝对象,cutArr数组用来存放周期切割依据数据 + let targetMap: Map> = new Map(), cutArr: Array = []; + // 新创建map对象接收传过来的实参map + resultList.forEach((item: Array, key: string) => { + targetMap.set(key, JSON.parse(JSON.stringify(item))); + }) + // 根据线程id及方法名获取的数据,处理后用作切割时间依据,时间跨度为整个方法开始时间到末个方法开始时间 + for (let i of res) { + cutArr[cutArr.length - 1] && (cutArr[cutArr.length - 1].dur = i.startTime ? i.startTime + this.currentSelectionParam.recordStartNs - cutArr[cutArr.length - 1].ts : 0); + cutArr.push({ 'ts': i.startTime + this.currentSelectionParam.recordStartNs }); + } + let cycleMap: Map> = new Map(); + let totalList: Map> = new Map(); + this.mergeLoopData(cutArr, targetMap, cycleMap, totalList); + let threadArr: Array = JSON.parse(JSON.stringify(this.threadArr)); + let processArr: Array = JSON.parse(JSON.stringify(this.processArr)); + this.mergeThreadData(threadArr, cycleMap); + let totalData = this.merge(totalList); + this.mergeTotalData(threadArr, totalData); + this.mergePidData(processArr, threadArr); + this.fixedDeal(processArr); + this.threadStatesTblSource = processArr; + this.threadStatesTbl!.recycleDataSource = processArr; + this.threadStatesTbl!.loading = false; + this.threadClick(processArr); + } else { + this.threadStatesTblSource = []; + this.threadStatesTbl!.recycleDataSource = []; + this.threadStatesTbl!.loading = false; + } + }) } else { - this.threadStatesTblSource = []; - this.threadStatesTbl!.recycleDataSource = []; - this.threadStatesTbl!.loading = false; + this.threadStatesTbl!.loading = false; + if (threadIdValue === '') { + threadId.style.border = '2px solid rgb(255,0,0)'; + } + if (threadFuncName === '') { + threadFunc.style.border = '2px solid rgb(255,0,0)'; + } } - }); - } else { - this.threadStatesTbl!.loading = false; - if (threadIdValue == '') { - threadId.style.border = '1px solid rgb(255,0,0)'; - threadId.setAttribute('placeholder', 'Please input thread id'); - } - if (threadFuncName == '') { - threadFunc.style.border = '1px solid rgb(255,0,0)'; - threadFunc.setAttribute('placeholder', 'Please input function name'); - } } - } - // Loop方式切割数据功能 - dataLoopCut(threadId: any, threadFunc: any, resultList: any) { - threadId.style.border = '1px solid rgb(151,151,151)'; - threadFunc.style.border = '1px solid rgb(151,151,151)'; - 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 (threadIdValue != '' && threadFuncName != '') { - querySearchFuncData(threadFuncName, Number(threadIdValue), leftNS, rightNS).then((res) => { - if (res != null && res.length > 0) { - let targetMap = new Map(); - // 新创建map对象接收传过来的实参map - resultList.forEach((item: any, key: any) => { - targetMap.set(key, JSON.parse(JSON.stringify(item))); - }); - let timeDur = this.currentSelectionParam.recordStartNs; - let cutArr = new Array(); - // 根据线程id及方法名获取的数据,处理后用作切割时间依据,时间跨度为整个方法开始时间到末个方法开始时间 - for (let i of res) { - cutArr.push({ ts: i.startTime + timeDur }); - } - let cycleMap = new Map(); - let totalList = new Map(); - targetMap.forEach((item, key) => { + /** + * 整合Loop切割方式中的频点数据与方法周期数据 + */ + mergeLoopData(cutArr: Array, targetMap: Map>, cycleMap: Map>, totalList: Map>): void { + let timeDur: number = this.currentSelectionParam.recordStartNs; + targetMap.forEach((value, key) => { cycleMap.set(key, new Array()); totalList.set(key, new Array()); for (let i = 0; i < cutArr.length - 1; i++) { - let cpuArr = new Array(); - let cpuMap = new Map(); - let resList = new Array(); - // 时间由纳秒转换为秒的倍数 - const multiple = 1000000000; - // 算力倍数值 - const countMutiple = 1000; - cpuMap.set(key, new Array()); - cycleMap - .get(key) - .push({ - thread: '周期' + (i + 1) + '—' + item[0].thread, - ts: (cutArr[i].ts - timeDur) / multiple, - count: 0, - cpu: '', - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); - let value = JSON.parse(JSON.stringify(item)); - for (let j = 0; j < value.length; j++) { - value[j].ts = value[j].ts * multiple; - value[j].freq = value[j].freq == 'unknown' ? 0 : value[j].freq; - if (!cpuArr.includes(value[j].cpu)) { - cpuArr.push(value[j].cpu); - cpuMap - .get(key) - .push({ - thread: '周期' + (i + 1) + '—' + value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: 0, - cpu: value[j].cpu, - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); - } - if (value[j].ts >= cutArr[i].ts) { - if (value[j].ts + value[j].dur <= cutArr[i + 1].ts) { - resList.push({ - thread: '周期' + (i + 1) + '—' + value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (value[j].freq * value[j].dur) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: value[j].dur, - percent: value[j].percent, - state: 'Running', - id: i, - }); - totalList - .get(key) - .push({ - thread: value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (value[j].freq * value[j].dur) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: value[j].dur, - percent: value[j].percent, - state: 'Running', - id: i, - }); - } else { - if (cutArr[i + 1].ts - value[j].ts > 0) { - resList.push({ - thread: '周期' + (i + 1) + '—' + value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (value[j].freq * (cutArr[i + 1].ts - value[j].ts)) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: cutArr[i + 1].ts - value[j].ts, - percent: value[j].percent * ((cutArr[i + 1].ts - value[j].ts) / value[j].dur), - state: 'Running', - id: i, - }); - totalList - .get(key) - .push({ - thread: value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (value[j].freq * (cutArr[i + 1].ts - value[j].ts)) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: cutArr[i + 1].ts - value[j].ts, - percent: value[j].percent * ((cutArr[i + 1].ts - value[j].ts) / value[j].dur), - state: 'Running', - id: i, - }); - break; + let cpuArr: Array = [], resList: Array = [], cpuMap: Map> = new Map(); + // 时间倍数值 + const countMutiple = 1000000; + cpuMap.set(key, new Array()); + // 创建周期层级数据 + cycleMap.get(key)?.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[0].thread, ((cutArr[i].ts - timeDur) / 1000000).toFixed(3), key.split('_')[0], key.split('_')[1], 0, '', '', 0, (cutArr[i].dur / 1000000).toFixed(3), 0, 'cycle', i + 1, [])); + for (let j = 0; j < value.length; j++) { + // 判断若用户导入json文件,则替换为对应cpu下的对应频点的算力值进行算力消耗计算 + let consumptionMap: Map = SegMenTaTion.freqInfoMapData.size > 0 && SegMenTaTion.freqInfoMapData.get(value[j].cpu); + // 若存在算力值,则直接取值做计算。若不存在算力值,且频点值不为unknown的情况,则取频点值做计算,若为unknown,则取0做兼容 + const consumption = (consumptionMap && consumptionMap.get(value[j].freq)) ? consumptionMap.get(value[j].freq) : (value[j].freq == 'unknown' ? 0 : value[j].freq); + if (!cpuArr.includes(value[j].cpu)) { + cpuArr.push(value[j].cpu); + // 创建cpu层级数据,以便后续生成树结构 + cpuMap.get(key)?.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[j].thread, '', value[j].pid, value[j].tid, 0, value[j].cpu, '', 0, '', 0, 'cpu', -1, [])); + } + // 以下为频点数据按Loop周期切割数据如何取舍的判断条件,cutArr为周期切割依据,value为某一线程下的频点汇总数据 + // 如果频点数据开始时间大于某一周期起始时间,且结束时间小于等于下一同名方法开始时间的情况 + if (value[j].ts >= cutArr[i].ts && (value[j].ts + value[j].dur) <= cutArr[i + 1].ts) { + resList.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[j].thread, '', value[j].pid, value[j].tid, (consumption * value[j].dur) / countMutiple, value[j].cpu, value[j].freq, value[j].dur, '', value[j].percent, 'freqdata', i, undefined)); + totalList.get(key)?.push(new TabPaneFreqUsageConfig(value[j].thread, '', value[j].pid, value[j].tid, (consumption * value[j].dur) / countMutiple, value[j].cpu, value[j].freq, value[j].dur, '', value[j].percent, 'freqdata', i, undefined)); + } + // 如果频点数据开始时间大于某一周期起始时间,且结束时间大于下一同名方法开始时间的情况 + if (value[j].ts >= cutArr[i].ts && (value[j].ts + value[j].dur) > cutArr[i + 1].ts) { + if (cutArr[i + 1].ts - value[j].ts > 0) { + resList.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[j].thread, '', value[j].pid, value[j].tid, (consumption * (cutArr[i + 1].ts - value[j].ts)) / countMutiple, value[j].cpu, value[j].freq, cutArr[i + 1].ts - value[j].ts, '', value[j].percent * ((cutArr[i + 1].ts - value[j].ts) / value[j].dur), 'freqdata', i, undefined)); + totalList.get(key)?.push(new TabPaneFreqUsageConfig(value[j].thread, '', value[j].pid, value[j].tid, (consumption * (cutArr[i + 1].ts - value[j].ts)) / countMutiple, value[j].cpu, value[j].freq, cutArr[i + 1].ts - value[j].ts, '', value[j].percent * ((cutArr[i + 1].ts - value[j].ts) / value[j].dur), 'freqdata', i, undefined)); + break; + } + } + // 如果频点数据开始时间小于某一周期起始时间,且结束时间大于下一同名方法开始时间的情况 + if (value[j].ts < cutArr[i].ts && (value[j].ts + value[j].dur) > cutArr[i + 1].ts) { + resList.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[j].thread, '', value[j].pid, value[j].tid, (consumption * (cutArr[i + 1].ts - cutArr[i].ts)) / countMutiple, value[j].cpu, value[j].freq, cutArr[i + 1].ts - cutArr[i].ts, '', value[j].percent * ((cutArr[i + 1].ts - cutArr[i].ts) / value[j].dur), 'freqdata', i, undefined)); + totalList.get(key)?.push(new TabPaneFreqUsageConfig(value[j].thread, '', value[j].pid, value[j].tid, (consumption * (cutArr[i + 1].ts - cutArr[i].ts)) / countMutiple, value[j].cpu, value[j].freq, cutArr[i + 1].ts - cutArr[i].ts, '', value[j].percent * ((cutArr[i + 1].ts - cutArr[i].ts) / value[j].dur), 'freqdata', i, undefined)); + } + // 如果频点数据开始时间小于某一周期起始时间,结束时间大于该方法开始时间。且频点数据结束时间小于下一同名方法开始时间 + if (value[j].ts < cutArr[i].ts && (value[j].ts + value[j].dur) > cutArr[i].ts && (value[j].ts + value[j].dur) < cutArr[i + 1].ts) { + resList.push(new TabPaneFreqUsageConfig('cycle' + (i + 1) + '—' + value[j].thread, '', value[j].pid, value[j].tid, (consumption * (value[j].dur + value[j].ts - cutArr[i].ts)) / countMutiple, value[j].cpu, value[j].freq, value[j].dur + value[j].ts - cutArr[i].ts, '', value[j].percent * ((value[j].dur + value[j].ts - cutArr[i].ts) / value[j].dur), 'freqdata', i, undefined)); + totalList.get(key)?.push(new TabPaneFreqUsageConfig(value[j].thread, '', value[j].pid, value[j].tid, (consumption * (value[j].dur + value[j].ts - cutArr[i].ts)) / countMutiple, value[j].cpu, value[j].freq, value[j].dur + value[j].ts - cutArr[i].ts, '', value[j].percent * ((value[j].dur + value[j].ts - cutArr[i].ts) / value[j].dur), 'freqdata', i, undefined)); } - } - } else { - if (value[j].ts + value[j].dur > cutArr[i + 1].ts) { - resList.push({ - thread: '周期' + (i + 1) + '—' + value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (value[j].freq * (cutArr[i + 1].ts - cutArr[i].ts)) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: cutArr[i + 1].ts - cutArr[i].ts, - percent: value[j].percent * ((cutArr[i + 1].ts - cutArr[i].ts) / value[j].dur), - state: 'Running', - id: i, - }); - totalList - .get(key) - .push({ - thread: value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (value[j].freq * (cutArr[i + 1].ts - cutArr[i].ts)) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: cutArr[i + 1].ts - cutArr[i].ts, - percent: value[j].percent * ((cutArr[i + 1].ts - cutArr[i].ts) / value[j].dur), - state: 'Running', - id: i, - }); - } - if (value[j].ts + value[j].dur > cutArr[i].ts && value[j].ts + value[j].dur < cutArr[i + 1].ts) { - resList.push({ - thread: '周期' + (i + 1) + '—' + value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (value[j].freq * (value[j].dur + value[j].ts - cutArr[i].ts)) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: value[j].dur + value[j].ts - cutArr[i].ts, - percent: value[j].percent * ((value[j].dur + value[j].ts - cutArr[i].ts) / value[j].dur), - state: 'Running', - id: i, - }); - totalList - .get(key) - .push({ - thread: value[j].thread, - pid: value[j].pid, - tid: value[j].tid, - count: (value[j].freq * (value[j].dur + value[j].ts - cutArr[i].ts)) / countMutiple, - cpu: value[j].cpu, - freq: value[j].freq, - dur: value[j].dur + value[j].ts - cutArr[i].ts, - percent: value[j].percent * ((value[j].dur + value[j].ts - cutArr[i].ts) / value[j].dur), - state: 'Running', - id: i, - }); - } } - } - // 合并相同周期内的数据 - this.mergeData(resList); - // 以算力消耗降序排列 - resList.sort((a, b) => b.count - a.count); - // 以cpu升序排列 - cpuMap.get(key).sort((a: any, b: any) => a.cpu - b.cpu); - cpuMap.get(key).forEach((item: any) => { - for (let s = 0; s < resList.length; s++) { - if (item.cpu == resList[s].cpu) { + // 合并相同周期内的数据 + this.mergeData(resList); + // 整理排序相同周期下的数据 + this.mergeCpuData(cpuMap.get(key)!, resList); + // 将cpu数据放置到对应周期层级下 + this.mergeCycleData(cycleMap.get(key)![i], cpuMap.get(key)); + } + }); + } + /** + * 切割后整合好的周期频点数据放置到对应的线程下 + */ + mergeThreadData(threadArr: Array, cycleMap: Map>): void { + for (let i = 0; i < threadArr.length; i++) { + let cycleMapData = cycleMap.get(threadArr[i].pid + '_' + threadArr[i].tid); + for (let j = 0; j < cycleMapData!.length; j++) { + threadArr[i].children.push(cycleMapData![j]); + threadArr[i].count += cycleMapData![j].count; + threadArr[i].dur += cycleMapData![j].dur; + threadArr[i].percent += cycleMapData![j].percent; + } + } + } + /** + * 切割后整合好的线程级频点数据放置到对应的进程 + */ + mergePidData(pidArr: Array, threadArr: Array): void { + for (let i = 0; i < pidArr.length; i++) { + for (let j = 0; j < threadArr.length; j++) { + if (pidArr[i].pid === threadArr[j].pid) { + pidArr[i].children.push(threadArr[j]); + pidArr[i].count += threadArr[j].count; + pidArr[i].dur += threadArr[j].dur; + pidArr[i].percent += threadArr[j].percent; + } + } + } + } + /** + * 合并相同周期内运行所在cpu相同、频点相同的数据 + */ + mergeData(resList: Array): void { + // 合并相同周期内的数据 + 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--; + } + } + } + } + /** + * 将cpu层级数据放到对应的周期层级下 + */ + mergeCycleData(obj: TabPaneFreqUsageConfig, arr: Array | undefined): void { + for (let i = 0; i < arr!.length; i++) { + if (arr![i].count === 0 && arr![i].dur === 0) { + continue; + } + obj.children?.push(arr![i]); + obj.count += arr![i].count; + obj.dur += arr![i].dur; + obj.percent += arr![i].percent; + } + } + /** + * 将切割好的不区分周期的数据作为total数据放到对应的线程层级下,周期数据前 + */ + mergeTotalData(threadArr: Array, totalData: Array): void { + for (let i = 0; i < threadArr.length; i++) { + for (let j = 0; j < totalData.length; j++) { + if (Number(threadArr[i].pid) === Number(totalData[j].pid) && Number(threadArr[i].tid) === Number(totalData[j].tid)) { + totalData[j].thread = 'TotalData'; + threadArr[i].children.unshift(totalData[j]); + } + } + } + } + /** + * 整理排序相同周期下的数据 + */ + mergeCpuData(cpuArray: Array, resList: Array): void { + // 以算力消耗降序排列 + resList.sort((a, b) => b.count - a.count); + // 以cpu升序排列 + cpuArray.sort((a: any, b: any) => a.cpu - b.cpu); + cpuArray.forEach((item: any) => { + for (let s = 0; s < resList.length; s++) { + if (item.cpu === resList[s].cpu) { item.children.push(resList[s]); item.count += resList[s].count; item.dur += resList[s].dur; item.percent += resList[s].percent; - } } - }); - // 将cpu数据放置到对应周期层级下 - this.mergeCycleData(cycleMap.get(key)[i], cpuMap.get(key)); } - }); - let threadArr = JSON.parse(JSON.stringify(this.threadArr)); - let processArr = JSON.parse(JSON.stringify(this.processArr)); - this.mergeThreadData(threadArr, cycleMap); - let totalData = this.merge(totalList); - this.mergeTotalData(threadArr, totalData); - this.mergePidData(processArr, threadArr); - this.fixedDeal(processArr); - this.threadStatesTblSource = processArr; - this.threadStatesTbl!.recycleDataSource = processArr; - this.threadStatesTbl!.setStatus(processArr, true); - this.threadStatesTbl!.loading = false; - this.threadClick(processArr); - } else { - this.threadStatesTblSource = []; - this.threadStatesTbl!.recycleDataSource = []; - this.threadStatesTbl!.loading = false; - } - }); - } else { - this.threadStatesTbl!.loading = false; - if (threadIdValue == '') { - threadId.style.border = '1px solid rgb(255,0,0)'; - threadId.setAttribute('placeholder', 'Please input thread id'); - } - if (threadFuncName == '') { - threadFunc.style.border = '1px solid rgb(255,0,0)'; - threadFunc.setAttribute('placeholder', 'Please input function name'); - } + }); + } - } - mergeFreqData(needDeal: any, dealArr: any, sum: number) { - needDeal.forEach((value: any, key: any) => { - let resultList = new Array(); - // 时间由纳秒转换为秒的倍数 - const multiple = 1000000000; - // 算力倍数值 - const countMutiple = 1000; - for (let i = 0; i < value.length; i++) { - for (let j = 0; j < dealArr.length; j++) { - if (value[i].cpu == dealArr[j].cpu) { - if (value[i].ts > dealArr[j].startNS) { - if (value[i].ts < dealArr[j].startNS + dealArr[j].dur) { - if (value[i].dur < dealArr[j].startNS + dealArr[j].dur - value[i].ts) { - resultList.push({ - thread: value[i].thread, - pid: value[i].pid, - tid: value[i].tid, - count: (dealArr[j].value * value[i].dur) / countMutiple, - cpu: value[i].cpu, - freq: dealArr[j].value, - dur: value[i].dur, - percent: (value[i].dur / sum) * 100, - state: 'Running', - ts: value[i].ts / multiple, - }); - break; - } else { - resultList.push({ - thread: value[i].thread, - pid: value[i].pid, - tid: value[i].tid, - count: (dealArr[j].value * (dealArr[j].startNS + dealArr[j].dur - value[i].ts)) / countMutiple, - cpu: value[i].cpu, - freq: dealArr[j].value, - dur: dealArr[j].startNS + dealArr[j].dur - value[i].ts, - percent: ((dealArr[j].startNS + dealArr[j].dur - value[i].ts) / sum) * 100, - state: 'Running', - ts: value[i].ts / multiple, - }); + /** + * 切割好的不区分周期的数据,以相同cpu相同频点的进行整合 + */ + merge(totalList: Map>): Array { + let result: Array = new Array(); + totalList.forEach((value: any, key: any) => { + let countNum = result.push(new TabPaneFreqUsageConfig('', '', key.split('_')[0], key.split('_')[1], 0, '', '', 0, '', 0, 'cycle', 0, [])); + let cpuArr: Array = []; + let flagArr: Array = []; + for (let i = 0; i < value.length; i++) { + if (!flagArr.includes(value[i].cpu)) { + flagArr.push(value[i].cpu); + let flag = cpuArr.push(new TabPaneFreqUsageConfig(value[i].thread, '', value[i].pid, value[i].tid, 0, value[i].cpu, '', 0, '', 0, 'cpu', -1, [])); + result[countNum - 1].children?.push(cpuArr[flag - 1]); } - } - } else { - if (value[i].ts + value[i].dur > dealArr[j].startNS) { - if (value[i].dur + value[i].ts - dealArr[j].startNS < dealArr[j].dur) { - resultList.push({ - thread: value[i].thread, - pid: value[i].pid, - tid: value[i].tid, - count: (dealArr[j].value * (value[i].dur + value[i].ts - dealArr[j].startNS)) / countMutiple, - cpu: value[i].cpu, - freq: dealArr[j].value, - dur: value[i].dur + value[i].ts - dealArr[j].startNS, - percent: ((value[i].dur + value[i].ts - dealArr[j].startNS) / sum) * 100, - state: 'Running', - ts: dealArr[j].startNS / multiple, - }); - break; + for (let j = i + 1; j < value.length; j++) { + if (value[i].cpu === value[j].cpu && value[i].freq === value[j].freq) { + value[i].dur += value[j].dur; + value[i].percent += value[j].percent; + value[i].count += value[j].count; + value.splice(j, 1); + j--; + } + } + } + result[countNum - 1].children?.sort((a: any, b: any) => a.cpu - b.cpu); + for (let i = 0; i < cpuArr.length; i++) { + for (let j = 0; j < value.length; j++) { + if (cpuArr[i].cpu === value[j].cpu) { + cpuArr[i].children.push(value[j]); + cpuArr[i].dur += value[j].dur; + cpuArr[i].count += value[j].count; + cpuArr[i].percent += value[j].percent; + } + } + result[countNum - 1].dur += cpuArr[i].dur; + result[countNum - 1].count += cpuArr[i].count; + result[countNum - 1].percent += cpuArr[i].percent; + } + }); + return result; + } + /** + * 递归整理数据,取小数位数,转换单位 + */ + fixedDeal(arr: Array): void { + if (arr === undefined) { + return; + } + for (let i = 0; i < arr.length; i++) { + arr[i].percent = arr[i].percent > 100 ? 100 : arr[i].percent; + arr[i].percent = arr[i].percent.toFixed(2); + arr[i].dur = (arr[i].dur / 1000000).toFixed(3); + if (arr[i].freq !== '') { + if (arr[i].freq === 'unknown') { + arr[i].freq = 'unknown'; } else { - resultList.push({ - thread: value[i].thread, - pid: value[i].pid, - tid: value[i].tid, - count: (dealArr[j].value * dealArr[j].dur) / countMutiple, - cpu: value[i].cpu, - freq: dealArr[j].value, - dur: dealArr[j].dur, - percent: (dealArr[j].dur / sum) * 100, - state: 'Running', - ts: dealArr[j].startNS / multiple, - }); + arr[i].freq = arr[i].freq / 1000; } - } else { - resultList.push({ - thread: value[i].thread, - pid: value[i].pid, - tid: value[i].tid, - count: 0, - cpu: value[i].cpu, - freq: 'unknown', - dur: value[i].dur, - percent: (value[i].dur / sum) * 100, - state: 'Running', - ts: value[i].ts / multiple, - }); - break; - } } - } + if (!(SegMenTaTion.freqInfoMapData.size > 0)) { + arr[i].count = (arr[i].count / 1000).toFixed(3); + } else { + arr[i].count = (arr[i].count).toFixed(3); + } + this.fixedDeal(arr[i].children); } - } - this.initData.set(key, JSON.parse(JSON.stringify(resultList))); - }); - } - mergeThreadData(threadArr: any, cpuMap: any) { - for (let i = 0; i < threadArr.length; i++) { - let cpuMapData = cpuMap.get(threadArr[i].pid + '_' + threadArr[i].tid); - for (let j = 0; j < cpuMapData.length; j++) { - threadArr[i].children.push(cpuMapData[j]); - threadArr[i].count += cpuMapData[j].count; - threadArr[i].dur += cpuMapData[j].dur; - threadArr[i].percent += cpuMapData[j].percent; - } } - } - mergePidData(pidArr: any, threadArr: any) { - for (let i = 0; i < pidArr.length; i++) { - for (let j = 0; j < threadArr.length; j++) { - if (pidArr[i].pid == threadArr[j].pid) { - pidArr[i].children.push(threadArr[j]); - pidArr[i].count += threadArr[j].count; - pidArr[i].dur += threadArr[j].dur; - pidArr[i].percent += threadArr[j].percent; + + /** + * 绑定表格点击事件 + */ + private threadClick(data: Array): void { + 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 (!this.threadStatesTblSource.length && !this.threadStatesTbl!.recycleDataSource.length) { + data = []; + } + 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) { + for (let item of data) { + item.status = true; + if (item.children != undefined && item.children.length > 0) { + this.threadStatesTbl!.setStatus(item.children, false); + } + } + this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); + } else if (label.includes('Cycle') && i === 2) { + for (let item of data) { + item.status = true; + for (let value of item.children ? item.children : []) { + value.status = true; + if (value.children != undefined && value.children.length > 0) { + this.threadStatesTbl!.setStatus(value.children, false); + } + } + } + this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); + } else if (label.includes('CPU') && i === 3) { + this.threadStatesTbl!.setStatus(data, true); + this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Expand); + } + }); + } } - } } - } - mergeData(resList: any) { - // 合并相同周期内的数据 - 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--; + /** + * 散点图渲染数据整理 + */ + render(res: Array, str: string, queryCycleScatter: Array): void { + let maxFreq: HTMLInputElement = this.scatterInput!.querySelector('#maxFreq')!; + let maxHz: HTMLInputElement = this.scatterInput!.querySelector('#maxHz')!; + maxFreq.style.border = '1px solid rgb(151,151,151)'; + maxHz.style.border = '1px solid rgb(151,151,151)'; + if (maxFreq.value && maxHz.value) { + if (/^[0-9]*$/.test(maxFreq.value) && /^[0-9]*$/.test(maxHz.value)) { + // @ts-ignore + this.shadowRoot?.querySelector('#cycleQuery')!.style.display = 'block'; + let freq: Map = (SegMenTaTion.freqInfoMapData.size > 0 && SegMenTaTion.freqInfoMapData.get(SegMenTaTion.freqInfoMapData.size - 1)); + // @ts-ignore + let yAxis: number = (freq && freq.get(Number(maxFreq.value) * 1000)) ? freq.get(Number(maxFreq.value) * 1000) : Number(maxFreq.value); + let xAxis: number = yAxis * 1000 / Number(maxHz.value);//MHz·s + // 需要做筛选时,会利用下面的cycleA、cycleB数组 + let scatterArr: Array> = [], traceRowdata: Array = [], cycleA: Array> = [], cycleB: Array> = []; + let cycleAStart: number = queryCycleScatter[0] || 0, cycleAEnd: number = queryCycleScatter[1] || 0, cycleBStart: number = queryCycleScatter[2] || 0, cycleBEnd: number = queryCycleScatter[3] || 0; + for (let i = 1; i < res.length; i++) { + const count: number = Number(res[i].count), dur: number = Number(res[i].cdur), r_dur: number = Number(res[i].dur);//MHz·ms ms ms + scatterArr.push([count, count / dur, i, dur, r_dur]); + traceRowdata.push( + { + 'dur': dur * 1000000, + 'value': count, + 'freq': Math.round(count / dur) * 1000, + 'startNS': Number(res[i].ts) * 1000000, + 'cycle': i - 1 + } + ); + if (dur >= cycleAStart && dur < cycleAEnd) { + cycleA.push([count, count / dur, i, dur, r_dur]); + } + if (dur >= cycleBStart && dur < cycleBEnd) { + cycleB.push([count, count / dur, i, dur, r_dur]); + } + } + this.setConfig(Number(maxHz.value), str, scatterArr, yAxis, xAxis, cycleA, cycleB); + SegMenTaTion.setChartData('CPU-FREQ', traceRowdata); + } else { + if (!/^[0-9]*$/.test(maxFreq.value)) { + maxFreq.style.border = '2px solid rgb(255,0,0)'; + } + if (!/^[0-9]*$/.test(maxHz.value)) { + maxHz.style.border = '2px solid rgb(255,0,0)'; + } + } + } else { + if (maxFreq.value === '') { + maxFreq.style.border = '2px solid rgb(255,0,0)'; + maxFreq.setAttribute('placeholder', 'Please input maxFreq'); + } + if (maxHz.value === '') { + maxHz.style.border = '2px solid rgb(255,0,0)'; + maxHz.setAttribute('placeholder', 'Please input Fps'); + } + SegMenTaTion.setChartData('CPU-FREQ', []); } - } - } - } - mergeCycleData(obj: any, arr: any) { - for (let i = 0; i < arr.length; i++) { - if (arr[i].count === 0 && arr[i].dur === 0) { - continue; - } - obj.children.push(arr[i]); - obj.count += arr[i].count; - obj.dur += arr[i].dur; - obj.percent += arr[i].percent; } - } - mergeTotalData(threadArr: any, totalData: any) { - for (let i = 0; i < threadArr.length; i++) { - for (let j = 0; j < totalData.length; j++) { - if (threadArr[i].pid == totalData[j].pid && threadArr[i].tid == totalData[j].tid) { - totalData[j].thread = 'TotalData'; - threadArr[i].children.unshift(totalData[j]); - } - } + + /** + * 配置散点图 + */ + setConfig(maxHz: number, str: string, scatterArr: Array>, yAxis: number, xAxis: number, cycleA: Array>, cycleB: Array>): void { + this.statisticsScatter!.config = { + // 纵轴坐标值 + yAxisLabel: [Math.round(yAxis / 5), Math.round(yAxis * 2 / 5), Math.round(yAxis * 3 / 5), Math.round(yAxis * 4 / 5), Math.round(yAxis)], + // 横轴坐标值 + xAxisLabel: [Math.round(xAxis / 5), Math.round(xAxis * 2 / 5), Math.round(xAxis * 3 / 5), Math.round(xAxis * 4 / 5), Math.round(xAxis), Math.round(xAxis * 6 / 5)], + // 横轴字段、纵轴字段 + AxisLabel: ['负载', '算力供给'], + // 是否加载最大负载线及均衡线 + drawload: true, + // 最大负载线及均衡线值 + load: [xAxis, maxHz], + // 绘制点数据信息存储数组 + paintingData: [], + // 当前移入点坐标信息 + hoverData: {}, + // 颜色池 + colorPool: () => ['#2f72f8', '#ffab67', '#a285d2'], + // 移入数据点时是否触发函数 + //@ts-ignore + hoverEvent: SegMenTaTion.tabHover, + // 渐变色背景信息 + globalGradient: undefined, + // 渲染数据点 + data: [scatterArr, cycleA, cycleB], + // 散点图title + title: str, + colorPoolText: () => ['Total', 'CycleA', 'CycleB'], + tip: (data: any) => { + return ` +
+ Cycle: ${data.c[2]};
+ Comsumption: ${data.c[0]};
+ Cycle_dur: ${data.c[3]} ms;
+ Running_dur: ${data.c[4]} ms;
+
+ ` + } + }; + } - } - fixedDeal(arr: any) { - if (arr == undefined) { - return; + + initElements(): void { + this.threadStatesTbl = this.shadowRoot?.querySelector('#tb-running-datacut'); + // 绑定single、loop按钮点击事件 + this.threadStatesDIV = this.shadowRoot?.querySelector('#dataCut'); + this.threadStatesDIV?.children[2].children[0].addEventListener('click', (e) => { + this.threadStatesTbl!.loading = true; + // @ts-ignore + this.dataSingleCut(this.threadStatesDIV?.children[0]!, this.threadStatesDIV?.children[1]!, this.initData); + }) + this.threadStatesDIV?.children[2].children[1].addEventListener('click', (e) => { + this.threadStatesTbl!.loading = true; + // @ts-ignore + this.dataLoopCut(this.threadStatesDIV?.children[0]!, this.threadStatesDIV?.children[1]!, this.initData); + }) + this.statisticsScatter = this.shadowRoot?.querySelector('#chart-scatter'); + // 增加表格thread层级点击更新散点图事件、周期层级点击高亮泳道图对应段事件 + let scatterData: Array = new Array(); + let str: string = ''; + this.threadStatesTbl!.addEventListener('row-click', (evt): void => { + // @ts-ignore + if (evt.detail.flag === 'thread') { + // @ts-ignore + scatterData = evt.detail.children; + // @ts-ignore + str = evt.detail.thread; + this.render(scatterData, str, []); + } + // @ts-ignore + if (evt.detail.flag === 'cycle' && evt.detail.pid === scatterData[evt.detail.id - 1].pid && evt.detail.tid === scatterData[evt.detail.id - 1].tid) { + // @ts-ignore + SegMenTaTion.tabHover('CPU-FREQ', true, evt.detail.id - 1); + + } + }); + this.scatterInput = this.shadowRoot?.querySelector('.chart-box'); + this.shadowRoot?.querySelector('#query-btn')!.addEventListener('click', (e) => { + // @ts-ignore + let cycleAStartValue = this.shadowRoot?.querySelector('#cycle-a-start-range')!.value; + // @ts-ignore + let cycleAEndValue = this.shadowRoot?.querySelector('#cycle-a-end-range')!.value; + // @ts-ignore + let cycleBStartValue = this.shadowRoot?.querySelector('#cycle-b-start-range')!.value; + // @ts-ignore + let cycleBEndValue = this.shadowRoot?.querySelector('#cycle-b-end-range')!.value; + let queryCycleScatter = [Number(cycleAStartValue), Number(cycleAEndValue), Number(cycleBStartValue), Number(cycleBEndValue)]; + this.render(scatterData, str, queryCycleScatter); + }) } - for (let i = 0; i < arr.length; i++) { - arr[i].percent = arr[i].percent > 100 ? 100 : arr[i].percent; - arr[i].percent = arr[i].percent.toFixed(2); - this.fixedDeal(arr[i].children); + + connectedCallback(): void { + super.connectedCallback(); + resizeObserver(this.parentElement!, this.threadStatesTbl!); } - } - merge(totalList: any) { - let result = new Array(); - totalList.forEach((value: any, key: any) => { - let countNum = result.push({ - thread: '', - pid: key.split('_')[0], - tid: key.split('_')[1], - count: 0, - cpu: '', - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); - let cpuArr = new Array(); - let flagArr = new Array(); - for (let i = 0; i < value.length; i++) { - if (!flagArr.includes(value[i].cpu)) { - flagArr.push(value[i].cpu); - let flag = cpuArr.push({ - thread: value[i].thread, - pid: value[i].pid, - tid: value[i].tid, - count: 0, - cpu: value[i].cpu, - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); - result[countNum - 1].children.push(cpuArr[flag - 1]); + + initHtml(): string { + return ` + +
+ + +
+ + +
+
+ + +
+ + + + + + + + + + + + + + + + + + +
+ +
+
+ maxFreq: + + Fps: + +
+
+
+ + +
+
+
+ ` + } +} \ 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 index 3cfd8e96c177868e0788fc26d91c44c0b2f07830..8814bed802417ce72c31d376ad0c8e99a07f4d1e 100644 --- a/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsage.ts +++ b/ide/src/trace/component/trace/sheet/frequsage/TabPaneFreqUsage.ts @@ -16,347 +16,304 @@ import { BaseElement, element } from '../../../../../base-ui/BaseElement.js'; import { LitTable, RedrawTreeForm } from '../../../../../base-ui/table/lit-table.js'; import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection.js'; -import '../../../StackBar.js'; +import '../../../StackBar.js' import { getTabRunningPercent, queryCpuFreqUsageData, queryCpuFreqFilterId } from '../../../../database/SqlLite.js'; import { Utils } from '../../base/Utils.js'; import { resizeObserver } from '../SheetUtils.js'; import { SliceGroup } from '../../../../bean/StateProcessThread.js'; +import { SegMenTaTion } from "../../../chart/SpSegmentationChart.js"; +import { TabPaneFreqUsageConfig, TabPaneRunningConfig, TabPaneCpuFreqConfig } from "./TabPaneFreqUsageConfig.js"; @element('tabpane-frequsage') export class TabPaneFreqUsage extends BaseElement { - private threadStatesTbl: LitTable | null | undefined; - private threadStatesTblSource: Array = []; - private currentSelectionParam: Selection | undefined; + private threadStatesTbl: LitTable | null | undefined; + private threadStatesTblSource: Array = []; + private currentSelectionParam: Selection | undefined; + private threadArr: Array = []; - set data(threadStatesParam: SelectionParam | any) { - if (this.currentSelectionParam === threadStatesParam) { - return; + set data(threadStatesParam: SelectionParam | any) { + if (this.currentSelectionParam === threadStatesParam) { + return; + } + this.threadStatesTbl!.loading = true; + this.currentSelectionParam = threadStatesParam; + this.threadStatesTblSource = []; + this.threadStatesTbl!.recycleDataSource = []; + let tableValue: any = this.threadStatesTbl; + tableValue.value = []; + this.threadArr = []; + this.init(threadStatesParam); } - this.threadStatesTbl!.loading = true; - this.currentSelectionParam = threadStatesParam; - this.threadStatesTblSource = []; - this.threadStatesTbl!.recycleDataSource = []; - let tableValue: any = this.threadStatesTbl; - tableValue.value = []; - // 查询框选区域内的状态为running的数据 - getTabRunningPercent(threadStatesParam.threadIds, threadStatesParam.leftNs, threadStatesParam.rightNs).then( - (result) => { - // 查询该trace文件中的cpu核数及id - queryCpuFreqFilterId().then((r) => { - // 将查询结果以键值对形式存入Map对象,仪表后续将频点数据与Cpu进行关联 - 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); - } - // 通过cpu的id去查询总的cpu频点数据 - queryCpuFreqUsageData(queryId).then((res) => { - if (result != null && result.length > 0) { - let sum = 0; - let dealArr = new Array(); - // 将cpu频点数据进行整合 - 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 needDeal = new Map(); - let cpuMap = new Map(); - let pidArr = new Array(); - let threadArr = new Array(); - // 创建进程级的数组 - let processArr: any = - threadStatesParam.processIds.length > 1 - ? [...new Set(threadStatesParam.processIds)] - : threadStatesParam.processIds; - for (let i of processArr) { - pidArr.push({ - process: Utils.PROCESS_MAP.get(i) == null ? 'Process ' + i : Utils.PROCESS_MAP.get(i) + ' ' + i, - thread: Utils.PROCESS_MAP.get(i) == null ? 'Process ' + i : Utils.PROCESS_MAP.get(i) + ' ' + i, - pid: i, - tid: '', - count: 0, - cpu: '', - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); - } - // 将running线程数据存到map中 - for (let e of result) { - if (processArr.includes(e.pid) && e.state == 'Running') { - if (needDeal.get(e.pid + '_' + e.tid) == undefined) { - threadArr.push({ - process: - Utils.PROCESS_MAP.get(e.pid) == null - ? 'Process ' + e.pid - : Utils.PROCESS_MAP.get(e.pid) + ' ' + e.pid, - thread: Utils.THREAD_MAP.get(e.tid) + ' ' + e.tid, - pid: e.pid, - tid: e.tid, - count: 0, - cpu: '', - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); - needDeal.set(e.pid + '_' + e.tid, new Array()); - } - if ( - e.ts < threadStatesParam.leftNs + threadStatesParam.recordStartNs && - e.ts + e.dur > threadStatesParam.leftNs + threadStatesParam.recordStartNs - ) { - const ts = e.ts; - e.ts = threadStatesParam.leftNs + threadStatesParam.recordStartNs; - e.dur = ts + e.dur - (threadStatesParam.leftNs + threadStatesParam.recordStartNs); - } - if (e.ts + e.dur > threadStatesParam.rightNs + threadStatesParam.recordStartNs) { - e.dur = threadStatesParam.rightNs + threadStatesParam.recordStartNs - e.ts; - } - let arr = needDeal.get(e.pid + '_' + e.tid); - 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; - arr.push(e); + /** + * 初始化数据 + */ + async init(threadStatesParam: SelectionParam | any): Promise { + let [runningData, sum] = await this.queryRunningData(threadStatesParam); + let cpuFreqData: Array = await this.queryCpuFreqData(threadStatesParam); + let cpuMap: Map> = new Map(); + if (runningData.size > 0) { + // 创建进程级的数组 + let pidArr: Array = [], processArr: Array = threadStatesParam.processIds.length > 1 ? [...new Set(threadStatesParam.processIds)] : threadStatesParam.processIds; + for (let i of processArr) { + pidArr.push(new TabPaneFreqUsageConfig(Utils.PROCESS_MAP.get(i) === null ? 'Process ' + i : Utils.PROCESS_MAP.get(i) + ' ' + i, '', i, '', 0, '', '', 0, '', 0, 'process', -1, [])); + } + // 将cpu频点数据与running状态数据整合,保证其上该段时长内有对应的cpu频点数据 + this.mergeFreqData(runningData, cpuMap, cpuFreqData, sum, threadStatesParam); + // 将频点数据放置到对应cpu层级下 + this.mergeCpuData(cpuMap, runningData); + // 将cpu层级数据放置到线程分组下 + this.mergeThreadData(this.threadArr, cpuMap); + // 将线程层级数据放置到进程级分组下 + this.mergePidData(pidArr, this.threadArr); + // 百分比保留两位小数 + this.fixedDeal(pidArr) + this.threadStatesTblSource = pidArr; + this.threadStatesTbl!.recycleDataSource = pidArr; + this.threadStatesTbl!.loading = false; + this.theadClick(pidArr); + this.threadStatesTbl!.loading = false; + } else { + this.threadStatesTblSource = []; + this.threadStatesTbl!.recycleDataSource = []; + this.threadStatesTbl!.loading = false; + } + } + /** + * 查询cpu频点信息 + */ + async queryCpuFreqData(threadStatesParam: SelectionParam | any): Promise> { + // 查询cpu及id信息 + let result: Array = await queryCpuFreqFilterId(); + // 以键值对形式将cpu及id进行对应,后续会将频点数据与其对应cpu进行整合 + let IdMap: Map = new Map(); + let queryId: Array = []; + for (let i = 0; i < result.length; i++) { + queryId.push(result[i].id); + IdMap.set(result[i].id, result[i].cpu); + } + let dealArr: Array = []; + // 通过id去查询频点数据 + let res: Array = await queryCpuFreqUsageData(queryId); + for (let i of res) { + let obj = new TabPaneCpuFreqConfig(i.startNS + threadStatesParam.recordStartNs, IdMap.get(i.filter_id)!, i.value, i.dur) + dealArr.push(obj); + } + return dealArr; + } + + /** + * 查询框选区域内的所有running状态数据 + */ + async queryRunningData(threadStatesParam: SelectionParam | any): Promise> { + // 查询running状态线程数据 + let result: Array = await getTabRunningPercent(threadStatesParam.threadIds, threadStatesParam.leftNs, threadStatesParam.rightNs); + let needDeal: Map> = new Map(), sum: number = 0; + if (result != null && result.length > 0) { + // 将running线程数据存到map中 + for (let e of result) { + if(threadStatesParam.processIds.includes(e.pid)){ + if (needDeal.get(e.pid + '_' + e.tid) === undefined) { + this.threadArr.push(new TabPaneFreqUsageConfig(Utils.THREAD_MAP.get(e.tid) + ' ' + e.tid, '', e.pid, e.tid, 0, '', '', 0, '', 0, 'thread', -1, [])); + needDeal.set(e.pid + '_' + e.tid, new Array()); + } + if ((e.ts < (threadStatesParam.leftNs + threadStatesParam.recordStartNs)) && ((e.ts + e.dur) > (threadStatesParam.leftNs + threadStatesParam.recordStartNs))) { + const ts = e.ts; + e.ts = threadStatesParam.leftNs + threadStatesParam.recordStartNs; + e.dur = ts + e.dur - (threadStatesParam.leftNs + threadStatesParam.recordStartNs); + } + if ((e.ts + e.dur) > (threadStatesParam.rightNs + threadStatesParam.recordStartNs)) { + e.dur = threadStatesParam.rightNs + threadStatesParam.recordStartNs - e.ts; + } + 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; + let arr: any = needDeal.get(e.pid + '_' + e.tid); + sum += e.dur; + arr.push(e); } - } - // 整理running线程数据、cpu层级信息 - this.mergeFreqData(needDeal, cpuMap, dealArr, sum, threadStatesParam); - // 将频点数据放置到对应cpu层级下 - this.mergeCpuData(cpuMap, needDeal); - // 将cpu层级数据放置到线程分组下 - this.mergeThreadData(threadArr, cpuMap); - // 将线程层级数据放置到进程级分组下 - this.mergePidData(pidArr, threadArr); - // 百分比保留两位小数 - this.fixedDeal(pidArr); - this.threadStatesTblSource = pidArr; - this.threadStatesTbl!.recycleDataSource = pidArr; - this.threadStatesTbl!.loading = false; - this.theadClick(pidArr); - } else { - this.threadStatesTblSource = []; - this.threadStatesTbl!.recycleDataSource = []; - this.threadStatesTbl!.loading = false; } - }); - }); - } - ); - } + } + return [needDeal, sum]; + } - private theadClick(data: Array) { - 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) { - this.threadStatesTbl!.setStatus(data, false); - this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); - } else if (label.includes('Thread') && i === 1) { - for (let item of data) { - item.status = true; - if (item.children != undefined && item.children.length > 0) { - this.threadStatesTbl!.setStatus(item.children, false); - } + /** + * 整合running数据与频点数据 + */ + mergeFreqData(needDeal: Map>, cpuMap: Map>, dealArr: Array, sum: number, threadStatesParam: SelectionParam | any): void { + needDeal.forEach((value: Array, key: string) => { + let resultList: Array = [], cpuArr: Array = []; + cpuMap.set(key, new Array()); + const multiple: number = 1000; + for (let i = 0; i < value.length; i++) { + if (!cpuArr.includes(value[i].cpu)) { + cpuArr.push(value[i].cpu); + cpuMap.get(key)?.push(new TabPaneFreqUsageConfig(value[i].tid + '_' + Utils.THREAD_MAP.get(value[i].tid), '', value[i].pid, value[i].tid, 0, value[i].cpu, '', 0, '', 0, 'cpu', -1, [])); + } + for (let j = 0; j < dealArr.length; j++) { + const consumption = SegMenTaTion.freqInfoMapData.size > 0 ? SegMenTaTion.freqInfoMapData.get(value[i].cpu).get(dealArr[j].value) : dealArr[j].value; + // 只需要合并相同cpu的数据 + if (value[i].cpu === dealArr[j].cpu) { + // 当running状态数据的开始时间大于频点数据开始时间,小于频点结束时间。且running数据的持续时间小于频点结束时间减去running数据开始时间的差值的情况 + if (value[i].ts > dealArr[j].startNS && value[i].ts < (dealArr[j].startNS + dealArr[j].dur) && value[i].dur < (dealArr[j].startNS + dealArr[j].dur - value[i].ts)) { + resultList.push(new TabPaneFreqUsageConfig(value[i].tid + '_' + value[i].thread, value[i].ts, '', '', (consumption * value[i].dur) / multiple, value[i].cpu, dealArr[j].value, value[i].dur, '', value[i].dur / sum * 100, 'freqdata', -1, undefined)); + break; + } + // 当running状态数据的开始时间大于频点数据开始时间,小于频点结束时间。且running数据的持续时间大于等于频点结束时间减去running数据开始时间的差值的情况 + if (value[i].ts > dealArr[j].startNS && value[i].ts < (dealArr[j].startNS + dealArr[j].dur) && value[i].dur >= (dealArr[j].startNS + dealArr[j].dur - value[i].ts)) { + resultList.push(new TabPaneFreqUsageConfig(value[i].tid + '_' + value[i].thread, value[i].ts, '', '', (consumption * (dealArr[j].startNS + dealArr[j].dur - value[i].ts)) / multiple, value[i].cpu, dealArr[j].value, (dealArr[j].startNS + dealArr[j].dur - value[i].ts), '', (dealArr[j].startNS + dealArr[j].dur - value[i].ts) / sum * 100, 'freqdata', -1, undefined)); + } + // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间大于频点开始时间。且running数据的持续时间减去频点数据开始时间的差值小于频点数据持续时间的情况 + if (value[i].ts <= dealArr[j].startNS && (value[i].ts + value[i].dur) > dealArr[j].startNS && (value[i].dur + value[i].ts - dealArr[j].startNS) < dealArr[j].dur) { + resultList.push(new TabPaneFreqUsageConfig(value[i].tid + '_' + value[i].thread, dealArr[j].startNS, '', '', (consumption * (value[i].dur + value[i].ts - dealArr[j].startNS)) / multiple, value[i].cpu, dealArr[j].value, (value[i].dur + value[i].ts - dealArr[j].startNS), '', (value[i].dur + value[i].ts - dealArr[j].startNS) / sum * 100, 'freqdata', -1, undefined)); + break; + } + // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间大于频点开始时间。且running数据的持续时间减去频点数据开始时间的差值大于等于频点数据持续时间的情况 + if (value[i].ts <= dealArr[j].startNS && (value[i].ts + value[i].dur) > dealArr[j].startNS && (value[i].dur + value[i].ts - dealArr[j].startNS) >= dealArr[j].dur) { + resultList.push(new TabPaneFreqUsageConfig(value[i].tid + '_' + value[i].thread, dealArr[j].startNS, '', '', (consumption * dealArr[j].dur) / multiple, value[i].cpu, dealArr[j].value, dealArr[j].dur, '', dealArr[j].dur / sum * 100, 'freqdata', -1, undefined)); + } + // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间小于等于频点开始时间的情况 + if (value[i].ts <= dealArr[j].startNS && (value[i].ts + value[i].dur) <= dealArr[j].startNS){ + resultList.push(new TabPaneFreqUsageConfig(value[i].tid + '_' + value[i].thread, value[i].ts, '', '', 0, value[i].cpu, 'unknown', value[i].dur, '', value[i].dur / sum * 100, 'freqdata', -1, undefined)); + break; + } + } + } } - 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); - } - }); - } + cpuMap.get(key)?.sort((a: any, b: any) => a.cpu - b.cpu); + needDeal.set(key, this.mergeData(resultList, threadStatesParam)); + }) } - } - mergeFreqData(needDeal: any, cpuMap: any, dealArr: any, sum: number, threadStatesParam: SelectionParam | any) { - needDeal.forEach((value: any, key: any) => { - let resultList = new Array(); - cpuMap.set(key, new Array()); - let cpuArr = new Array(); - const multiple = 1000; - for (let i = 0; i < value.length; i++) { - if (!cpuArr.includes(value[i].cpu)) { - cpuArr.push(value[i].cpu); - cpuMap - .get(key) - .push({ - process: - Utils.PROCESS_MAP.get(value[i].pid) == null - ? 'Process ' + value[i].pid - : Utils.PROCESS_MAP.get(value[i].pid) + ' ' + value[i].pid, - thread: value[i].tid + '_' + Utils.THREAD_MAP.get(value[i].tid), - pid: value[i].pid, - tid: value[i].tid, - count: 0, - cpu: value[i].cpu, - freq: '', - dur: 0, - percent: 0, - state: 'Running', - children: new Array(), - }); + + /** + * 合并同一线程内,当运行所在cpu和频点相同时,dur及percent进行累加求和 + */ + mergeData(resultList: Array, threadStatesParam: SelectionParam | any): Array{ + 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].ts = resultList[i].ts - threadStatesParam.recordStartNs; } - for (let j = 0; j < dealArr.length; j++) { - if (value[i].cpu == dealArr[j].cpu) { - if (value[i].ts > dealArr[j].startNS) { - if (value[i].ts < dealArr[j].startNS + dealArr[j].dur) { - if (value[i].dur < dealArr[j].startNS + dealArr[j].dur - value[i].ts) { - resultList.push({ - thread: value[i].tid + '_' + value[i].thread, - count: (dealArr[j].value * value[i].dur) / multiple, - cpu: value[i].cpu, - freq: dealArr[j].value, - dur: value[i].dur, - percent: (value[i].dur / sum) * 100, - state: 'Running', - ts: value[i].ts, - }); - break; - } else { - resultList.push({ - thread: value[i].tid + '_' + value[i].thread, - count: (dealArr[j].value * (dealArr[j].startNS + dealArr[j].dur - value[i].ts)) / multiple, - cpu: value[i].cpu, - freq: dealArr[j].value, - dur: dealArr[j].startNS + dealArr[j].dur - value[i].ts, - percent: ((dealArr[j].startNS + dealArr[j].dur - value[i].ts) / sum) * 100, - state: 'Running', - ts: value[i].ts, - }); + resultList.sort((a, b) => b.count - a.count); + return resultList; + } + + /** + * 将整理好的running频点数据通过map的键放到对应的cpu分组层级下 + */ + mergeCpuData(cpuMap: Map>, needDeal: Map>): void { + cpuMap.forEach((value: Array, key: string) => { + let arr = needDeal.get(key); + for (let i = 0; i < value.length; i++) { + for (let j = 0; j < arr!.length; j++) { + if (arr![j].cpu === value[i].cpu) { + value[i].children?.push(arr![j]); + value[i].count += arr![j].count; + value[i].dur += arr![j].dur; + value[i].percent += arr![j].percent; + } } - } - } else { - if (value[i].ts + value[i].dur > dealArr[j].startNS) { - if (value[i].dur + value[i].ts - dealArr[j].startNS < dealArr[j].dur) { - resultList.push({ - thread: value[i].tid + '_' + value[i].thread, - count: (dealArr[j].value * (value[i].dur + value[i].ts - dealArr[j].startNS)) / multiple, - cpu: value[i].cpu, - freq: dealArr[j].value, - dur: value[i].dur + value[i].ts - dealArr[j].startNS, - percent: ((value[i].dur + value[i].ts - dealArr[j].startNS) / sum) * 100, - state: 'Running', - ts: dealArr[j].startNS, - }); - break; - } else { - resultList.push({ - thread: value[i].tid + '_' + value[i].thread, - count: (dealArr[j].value * dealArr[j].dur) / multiple, - cpu: value[i].cpu, - freq: dealArr[j].value, - dur: dealArr[j].dur, - percent: (dealArr[j].dur / sum) * 100, - state: 'Running', - ts: dealArr[j].startNS, - }); + } + }); + } + + /** + * 将整理好的cpu层级数据放到对应的线程下 + */ + mergeThreadData(threadArr: Array, cpuMap: Map>): void { + for (let i = 0; i < threadArr.length; i++) { + let cpuMapData = cpuMap.get(threadArr[i].pid + '_' + threadArr[i].tid); + for (let j = 0; j < cpuMapData!.length; j++) { + threadArr[i].children.push(cpuMapData![j]); + threadArr[i].count += cpuMapData![j].count; + threadArr[i].dur += cpuMapData![j].dur; + threadArr[i].percent += cpuMapData![j].percent; + } + } + } + + /** + * 将整理好的线程层级数据放到对应的进程下 + */ + mergePidData(pidArr: Array, threadArr: Array): void { + for (let i = 0; i < pidArr.length; i++) { + for (let j = 0; j < threadArr.length; j++) { + if (pidArr[i].pid === threadArr[j].pid) { + pidArr[i].children.push(threadArr[j]); + pidArr[i].count += threadArr[j].count; + pidArr[i].dur += threadArr[j].dur; + pidArr[i].percent += threadArr[j].percent; } - } else { - resultList.push({ - thread: value[i].tid + '_' + value[i].thread, - count: 0, - cpu: value[i].cpu, - freq: 'unknown', - dur: value[i].dur, - percent: (value[i].dur / sum) * 100, - state: 'Running', - ts: value[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--; - } + } + + /** + * 递归整理数据小数位 + */ + fixedDeal(arr: Array): void { + if (arr == undefined) { + return; } - resultList[i].ts = resultList[i].ts - threadStatesParam.recordStartNs; - } - resultList.sort((a, b) => b.count - a.count); - cpuMap.get(key).sort((a: any, b: any) => a.cpu - b.cpu); - needDeal.set(key, resultList); - }); - } - mergeCpuData(cpuMap: any, needDeal: any) { - cpuMap.forEach((value: any, key: any) => { - let arr = needDeal.get(key); - for (let i = 0; i < value.length; i++) { - for (let j = 0; j < arr.length; j++) { - if (arr[j].cpu == value[i].cpu) { - value[i].children.push(arr[j]); - value[i].count += arr[j].count; - value[i].dur += arr[j].dur; - value[i].percent += arr[j].percent; - } + for (let i = 0; i < arr.length; i++) { + arr[i].percent = arr[i].percent > 100 ? 100 : arr[i].percent; + arr[i].percent = arr[i].percent.toFixed(2); + arr[i].dur = (arr[i].dur / 1000000).toFixed(3); + arr[i].count = (arr[i].count / 1000000).toFixed(3); + if (arr[i].freq !== '') { + if (arr[i].freq === 'unknown') { + arr[i].freq = 'unknown'; + } else { + arr[i].freq = arr[i].freq / 1000; + } + } + this.fixedDeal(arr[i].children); } - } - }); - } - mergeThreadData(threadArr: any, cpuMap: any) { - for (let i = 0; i < threadArr.length; i++) { - let cpuMapData = cpuMap.get(threadArr[i].pid + '_' + threadArr[i].tid); - for (let j = 0; j < cpuMapData.length; j++) { - threadArr[i].children.push(cpuMapData[j]); - threadArr[i].count += cpuMapData[j].count; - threadArr[i].dur += cpuMapData[j].dur; - threadArr[i].percent += cpuMapData[j].percent; - } } - } - mergePidData(pidArr: any, threadArr: any) { - for (let i = 0; i < pidArr.length; i++) { - for (let j = 0; j < threadArr.length; j++) { - if (pidArr[i].pid == threadArr[j].pid) { - pidArr[i].children.push(threadArr[j]); - pidArr[i].count += threadArr[j].count; - pidArr[i].dur += threadArr[j].dur; - pidArr[i].percent += threadArr[j].percent; + /** + * 表头点击事件 + */ + private theadClick(data: Array): void { + 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) { + this.threadStatesTbl!.setStatus(data, false); + this.threadStatesTbl!.recycleDs = this.threadStatesTbl!.meauseTreeRowElement(data, RedrawTreeForm.Retract); + } else if (label.includes('Thread') && i === 1) { + for (let item of data) { + item.status = true; + if (item.children != undefined && item.children.length > 0) { + this.threadStatesTbl!.setStatus(item.children, false); + } + } + 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); + } + }); + } } - } } - } - fixedDeal(arr: any) { - if (arr == undefined) { - return; + initElements(): void { + this.threadStatesTbl = this.shadowRoot?.querySelector('#tb-running-percent'); } - for (let i = 0; i < arr.length; i++) { - arr[i].percent = arr[i].percent > 100 ? 100 : arr[i].percent; - arr[i].percent = arr[i].percent.toFixed(2); - this.fixedDeal(arr[i].children); + connectedCallback(): void { + super.connectedCallback(); + resizeObserver(this.parentElement!, this.threadStatesTbl!); } - } - initElements(): void { - this.threadStatesTbl = this.shadowRoot?.querySelector('#tb-running-percent'); - } - connectedCallback() { - super.connectedCallback(); - resizeObserver(this.parentElement!, this.threadStatesTbl!); - } - initHtml(): string { - return ` + initHtml(): string { + return `