From 3fb0aba6e616c56c17cb33e833cf8e06b519f299 Mon Sep 17 00:00:00 2001 From: wangyujie Date: Sat, 16 Nov 2024 18:52:53 +0800 Subject: [PATCH 01/38] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8Dnative=20memory?= =?UTF-8?q?=E9=9D=9E=E7=BB=9F=E8=AE=A1=E6=A8=A1=E5=BC=8F=20=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E6=A8=A1=E5=BC=8F=E6=8A=93=E5=8F=96=EF=BC=8C=E9=A6=96?= =?UTF-8?q?=E6=AC=A1=E6=A1=86=E9=80=89=EF=BC=8Ctab=E9=A1=B5=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E6=8B=96=E5=8A=A8=E9=97=AE=E9=A2=98=EF=BC=9B=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=8C=89=E7=9D=80wasd=E5=BF=AB=E6=8D=B7=E9=94=AE?= =?UTF-8?q?=EF=BC=8C=E6=8B=96=E5=8A=A8tab=E9=A1=B5=EF=BC=8Ctab=E9=A1=B5?= =?UTF-8?q?=E4=BC=9A=E9=9A=8F=E9=BC=A0=E6=A0=87=E5=8A=A8=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: wangyujie --- ide/src/trace/component/SpSystemTrace.event.ts | 10 ++++------ ide/src/trace/component/SpSystemTrace.ts | 5 ++--- ide/src/trace/component/trace/base/TraceSheet.ts | 3 --- ide/src/trace/component/trace/search/Search.ts | 6 ------ ide/src/trace/component/trace/sheet/TabPaneCurrent.ts | 1 - ide/src/trace/component/trace/sheet/TabPaneFilter.ts | 1 - .../trace/component/trace/timer-shaft/TabPaneFlag.ts | 1 - 7 files changed, 6 insertions(+), 21 deletions(-) diff --git a/ide/src/trace/component/SpSystemTrace.event.ts b/ide/src/trace/component/SpSystemTrace.event.ts index 60d590885..27fd47bdb 100644 --- a/ide/src/trace/component/SpSystemTrace.event.ts +++ b/ide/src/trace/component/SpSystemTrace.event.ts @@ -621,7 +621,6 @@ export function spSystemTraceDocumentOnMouseOut(sp: SpSystemTrace, ev: MouseEven } export function spSystemTraceDocumentOnKeyPress(this: unknown, sp: SpSystemTrace, ev: KeyboardEvent): void { - SpSystemTrace.isKeyUp = false; if (!sp.loadTraceCompleted || SpSystemTrace.isAiAsk) { return; } @@ -771,6 +770,10 @@ export function spSystemTraceDocumentOnMouseUp(sp: SpSystemTrace, ev: MouseEvent if (!sp.loadTraceCompleted || !sp.mouseEventEnable) { return; } + //@ts-ignore + if ((window as unknown).isSheetMove) { + return; + } if (sp.isWASDKeyPress()) { ev.preventDefault(); ev.stopPropagation(); @@ -786,10 +789,6 @@ export function spSystemTraceDocumentOnMouseUp(sp: SpSystemTrace, ev: MouseEvent } TraceRow.isUserInteraction = false; sp.rangeSelect.isMouseDown = false; - //@ts-ignore - if ((window as unknown).isSheetMove) { - return; - } if (sp.isMouseInSheet(ev)) { return; } @@ -804,7 +803,6 @@ export function spSystemTraceDocumentOnKeyUp(sp: SpSystemTrace, ev: KeyboardEven if (SpSystemTrace.isAiAsk) { return; } - SpSystemTrace.isKeyUp = true; if (sp.times.size > 0) { for (let timerId of sp.times) { clearTimeout(timerId); diff --git a/ide/src/trace/component/SpSystemTrace.ts b/ide/src/trace/component/SpSystemTrace.ts index ec193b177..de0981083 100644 --- a/ide/src/trace/component/SpSystemTrace.ts +++ b/ide/src/trace/component/SpSystemTrace.ts @@ -154,7 +154,6 @@ type SlicesTimeAlias = SlicesTime | undefined | null; @element('sp-system-trace') export class SpSystemTrace extends BaseElement { mouseCurrentPosition = 0; - static isKeyUp: boolean = true; offsetMouse = 0; static isMouseLeftDown = false; static scrollViewWidth = 0; @@ -899,7 +898,7 @@ export class SpSystemTrace extends BaseElement { // @ts-ignore context!.moveTo(x0!, y0!); // @ts-ignore - if (SpSystemTrace.isKeyUp || curveDrawList.length < 90) { + if (curveDrawList.length < 90) { // @ts-ignore for (let i = 0; i < curveDrawList.length - 1; i++) { // @ts-ignore @@ -912,7 +911,7 @@ export class SpSystemTrace extends BaseElement { // @ts-ignore context.closePath(); // @ts-ignore - } else if (!SpSystemTrace.isKeyUp && curveDrawList.length >= 90) { + } else if (curveDrawList.length >= 90) { let x; let y; // @ts-ignore diff --git a/ide/src/trace/component/trace/base/TraceSheet.ts b/ide/src/trace/component/trace/base/TraceSheet.ts index aae578e1b..4a8d2bcd7 100644 --- a/ide/src/trace/component/trace/base/TraceSheet.ts +++ b/ide/src/trace/component/trace/base/TraceSheet.ts @@ -427,9 +427,6 @@ export class TraceSheet extends BaseElement { let that = this; // 节点挂载时给Tab面板绑定鼠标按下事件 this.nav!.onmousedown = (event): void => { - if (SpSystemTrace.isKeyUp === false) { - return; - } // @ts-ignore (window as unknown).isSheetMove = true; // 获取所有标签页的节点数组 diff --git a/ide/src/trace/component/trace/search/Search.ts b/ide/src/trace/component/trace/search/Search.ts index 03c14b425..517c8df9d 100644 --- a/ide/src/trace/component/trace/search/Search.ts +++ b/ide/src/trace/component/trace/search/Search.ts @@ -248,18 +248,15 @@ export class LitSearch extends BaseElement { this.searchBlurListener(); }); this.search!.addEventListener('keyup', (e: KeyboardEvent) => { - SpSystemTrace.isKeyUp = true; this._retarge_index!.value = ''; this.searchKeyupListener(e); }); //阻止事件冒泡 this.search!.addEventListener('keydown', (e: KeyboardEvent) => { - SpSystemTrace.isKeyUp = false; e.stopPropagation(); }); this.search!.addEventListener('keypress', (e: KeyboardEvent) => { - SpSystemTrace.isKeyUp = false; e.stopPropagation(); }); this.shadowRoot?.querySelector('#arrow-left')?.addEventListener('click', (): void => { @@ -283,12 +280,10 @@ export class LitSearch extends BaseElement { this.keyUpListener(); //阻止事件冒泡 this.shadowRoot?.querySelector("input[name='retarge_index']")?.addEventListener('keydown', (e: unknown) => { - SpSystemTrace.isKeyUp = false; // @ts-ignore e.stopPropagation(); }); this.shadowRoot?.querySelector("input[name='retarge_index']")?.addEventListener('keypress', (e: unknown) => { - SpSystemTrace.isKeyUp = false; // @ts-ignore e.stopPropagation(); }); @@ -359,7 +354,6 @@ export class LitSearch extends BaseElement { this._retarge_index!.value = ''; }, 2000); } - SpSystemTrace.isKeyUp = true; // @ts-ignore e.target.blur(); } diff --git a/ide/src/trace/component/trace/sheet/TabPaneCurrent.ts b/ide/src/trace/component/trace/sheet/TabPaneCurrent.ts index 414e01995..54762cc97 100644 --- a/ide/src/trace/component/trace/sheet/TabPaneCurrent.ts +++ b/ide/src/trace/component/trace/sheet/TabPaneCurrent.ts @@ -210,7 +210,6 @@ export class TabPaneCurrent extends BaseElement { tr[i].querySelector('#text-input')!.value = this.slicesTimeList[i - 1].text; // // 点击色块修改颜色 tr[i].querySelector('#text-input')?.addEventListener('keyup', (event: unknown) => { - SpSystemTrace.isKeyUp = true; if ( // @ts-ignore this.tableDataSource[i].startTime === this.slicesTimeList[i - 1].startTime && diff --git a/ide/src/trace/component/trace/sheet/TabPaneFilter.ts b/ide/src/trace/component/trace/sheet/TabPaneFilter.ts index 65238dded..121e104c6 100644 --- a/ide/src/trace/component/trace/sheet/TabPaneFilter.ts +++ b/ide/src/trace/component/trace/sheet/TabPaneFilter.ts @@ -714,7 +714,6 @@ export class TabPaneFilter extends BaseElement { this.value = this.value.replace(/\D/g, ''); }; e.addEventListener('keyup', (event: unknown): void => { - SpSystemTrace.isKeyUp = true; // @ts-ignore event.stopPropagation(); // @ts-ignore diff --git a/ide/src/trace/component/trace/timer-shaft/TabPaneFlag.ts b/ide/src/trace/component/trace/timer-shaft/TabPaneFlag.ts index ab73e814a..0e5be4219 100644 --- a/ide/src/trace/component/trace/timer-shaft/TabPaneFlag.ts +++ b/ide/src/trace/component/trace/timer-shaft/TabPaneFlag.ts @@ -189,7 +189,6 @@ export class TabPaneFlag extends BaseElement { private textInputKeyUpEventByFlag(index: number, tr: HTMLDivElement): void { tr.querySelector('#text-input')?.addEventListener('keyup', (event: unknown) => { - SpSystemTrace.isKeyUp = true; // @ts-ignore if (this.tableDataSource[index].startTime === this.flagList[index - 1].time && event.code === 'Enter' || event.code === 'NumpadEnter') { // @ts-ignore -- Gitee From 08447e666518f838f1e499860f4fff02bfffdb54 Mon Sep 17 00:00:00 2001 From: wangyujie Date: Sat, 16 Nov 2024 18:57:13 +0800 Subject: [PATCH 02/38] =?UTF-8?q?fix:=E5=88=A0=E9=99=A4Data=20Selection?= =?UTF-8?q?=E3=80=81Data=20Distribution=E3=80=81Total=20Duration=20tab?= =?UTF-8?q?=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: wangyujie --- .../component/trace/base/TraceSheetConfig.ts | 18 - .../TabPaneSampleInstructionDistributions.ts | 424 --------------- .../TabPaneSampleInstructionSelection.ts | 493 ------------------ ...PaneSampleInstructionSelectionTotalTime.ts | 399 -------------- 4 files changed, 1334 deletions(-) delete mode 100644 ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionDistributions.ts delete mode 100644 ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionSelection.ts delete mode 100644 ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionSelectionTotalTime.ts diff --git a/ide/src/trace/component/trace/base/TraceSheetConfig.ts b/ide/src/trace/component/trace/base/TraceSheetConfig.ts index f1d1af535..88665a2fc 100644 --- a/ide/src/trace/component/trace/base/TraceSheetConfig.ts +++ b/ide/src/trace/component/trace/base/TraceSheetConfig.ts @@ -127,9 +127,6 @@ import { TabPaneHiSysEventSummary } from '../sheet/hisysevent/TabPaneHiSysEventS import { TabPaneBinders } from '../sheet/binder/TabPaneBinders'; import { TabPaneGpufreq } from '../sheet/gpufreq/TabPaneGpufreqUsage'; import { TabPaneSampleInstruction } from '../sheet/bpftrace/TabPaneSampleInstruction'; -import { TabPaneSampleInstructionDistributions } from '../sheet/bpftrace/TabPaneSampleInstructionDistributions'; -import { TabPaneSampleInstructionTotalTime } from '../sheet/bpftrace/TabPaneSampleInstructionSelectionTotalTime'; -import { TabPaneSampleInstructionSelection } from '../sheet/bpftrace/TabPaneSampleInstructionSelection'; import { TabPaneDataCut } from '../sheet/TabPaneDataCut'; import { TabPaneGpuCounterSelection } from '../sheet/gpu-counter/TabPaneGpuCounterSelection'; import { TabPaneGpuCounter } from '../sheet/gpu-counter/TabPaneGpuCounter'; @@ -699,21 +696,6 @@ export let tabConfig: { require: (param: SelectionParam) => param.threadIds.length > 0 || (param.clockMapData.size > 0 && param.clockMapData.has('gpufreq Frequency') === true), }, - 'box-sample-instruction-selection': { - title: 'Data Selection', - type: TabPaneSampleInstructionSelection, - require: (param: SelectionParam) => param.sampleData.length > 0, - }, - 'box-sample-instruction-distribution-selection': { - title: 'Data Distribution', - type: TabPaneSampleInstructionDistributions, - require: (param: SelectionParam) => param.sampleData.length > 0, - }, - 'box-sample-instruction-totaltime-selection': { - title: 'Total Duration', - type: TabPaneSampleInstructionTotalTime, - require: (param: SelectionParam) => param.sampleData.length > 0, - }, 'box-sample-instruction': { title: 'Data Flow', type: TabPaneSampleInstruction, diff --git a/ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionDistributions.ts b/ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionDistributions.ts deleted file mode 100644 index ef58348a3..000000000 --- a/ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionDistributions.ts +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (C) 2022 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { BaseElement, element } from '../../../../../base-ui/BaseElement'; -import { SelectionParam } from '../../../../bean/BoxSelection'; -import { debounce } from '../../../Utils'; -const paddingLeft = 100; -const paddingBottom = 15; -const xStep = 50; // x轴间距 -const barWidth = 2; // 柱子宽度 - -@element('tab-sample-instructions-distrubtions-chart') -export class TabPaneSampleInstructionDistributions extends BaseElement { - private instructionChartEle: HTMLCanvasElement | undefined | null; - private ctx: CanvasRenderingContext2D | undefined | null; - private onReadableData: Array = []; - private hintContent = ''; //悬浮框内容 - private floatHint!: HTMLDivElement | undefined | null; //悬浮框 - private canvasScrollTop = 0; // tab页上下滚动位置 - private isUpdateCanvas = false; - private cacheData: Array = []; - private canvasX = -1; // 鼠标当前所在画布x坐标 - private canvasY = -1; // 鼠标当前所在画布y坐标 - private startX = 0; // 画布相对于整个界面的x坐标 - private startY = 0; // 画布相对于整个界面的y坐标 - private hoverBar: unknown; - private isChecked: boolean = false; - private xCount = 0; //x轴刻度个数 - private xMaxValue = 0; //x轴上数据最大值 - private xSpacing = 50; //x轴间距 - private xAvg = 0; //根据xMaxValue进行划分 用于x轴上刻度显示 - private yAvg = 0; //根据yMaxValue进行划分 用于y轴上刻度显示 - - initHtml(): string { - return ` - - -
- `; - } - - initElements(): void { - this.instructionChartEle = this.shadowRoot?.querySelector('#instruct-chart-canvas'); - this.ctx = this.instructionChartEle?.getContext('2d'); - this.floatHint = this.shadowRoot?.querySelector('#float_hint'); - } - - set data(SampleParam: SelectionParam) { - // @ts-ignore - this.onReadableData = SampleParam.sampleData[0].property; - this.calInstructionRangeCount(this.isChecked); - } - - connectedCallback(): void { - super.connectedCallback(); - this.parentElement!.onscroll = () => { - this.canvasScrollTop = this.parentElement!.scrollTop; - this.hideTip(); - }; - this.instructionChartEle!.onmousemove = (e): void => { - if (!this.isUpdateCanvas) { - this.updateCanvasCoord(); - } - this.canvasX = e.clientX - this.startX; - this.canvasY = e.clientY - this.startY + this.canvasScrollTop; - this.onMouseMove(); - }; - this.instructionChartEle!.onmouseleave = () => { - this.hideTip(); - }; - document.addEventListener('sample-popver-change', (e: unknown) => { - // @ts-ignore - const select = Number(e.detail.select); - this.isChecked = Boolean(select); - this.calInstructionRangeCount(this.isChecked); - }); - this.listenerResize(); - } - - /** - * 更新canvas坐标 - */ - updateCanvasCoord(): void { - if (this.instructionChartEle instanceof HTMLCanvasElement) { - this.isUpdateCanvas = this.instructionChartEle.clientWidth !== 0; - if (this.instructionChartEle.getBoundingClientRect()) { - const box = this.instructionChartEle.getBoundingClientRect(); - const D = this.parentElement!; - this.startX = box.left + Math.max(D.scrollLeft, document.body.scrollLeft) - D.clientLeft; - this.startY = box.top + Math.max(D.scrollTop, document.body.scrollTop) - D.clientTop + this.canvasScrollTop; - } - } - } - - /** - * 获取鼠标悬停的函数 - * @param nodes - * @param canvasX - * @param canvasY - * @returns - */ - searchDataByCoord(nodes: unknown, canvasX: number, canvasY: number): void | null { - // @ts-ignore - for (let i = 0; i < nodes.length; i++) { - // @ts-ignore - const element = nodes[i]; - if (this.isContains(element, canvasX, canvasY)) { - return element; - } - } - return null; - } - - /** - * 鼠标移动 - */ - onMouseMove(): void { - const lastNode = this.hoverBar; - //查找鼠标所在的node - const searchResult = this.searchDataByCoord(this.cacheData, this.canvasX, this.canvasY); - if (searchResult) { - this.hoverBar = searchResult; - //鼠标悬浮的node未改变则不需重新渲染文字 - if (searchResult !== lastNode) { - this.updateTipContent(); - } - this.showTip(); - } else { - this.hideTip(); - this.hoverBar = undefined; - } - } - - /** - * 隐藏悬浮框 - */ - hideTip(): void { - if (this.floatHint) { - this.floatHint.style.display = 'none'; - this.instructionChartEle!.style.cursor = 'default'; - } - } - - /** - * 显示悬浮框 - */ - showTip(): void { - this.floatHint!.innerHTML = this.hintContent; - this.floatHint!.style.display = 'block'; - this.instructionChartEle!.style.cursor = 'pointer'; - let x = this.canvasX; - let y = this.canvasY - this.canvasScrollTop; - //右边的函数悬浮框显示在左侧 - if (this.canvasX + this.floatHint!.clientWidth > (this.instructionChartEle!.clientWidth || 0)) { - x -= this.floatHint!.clientWidth - 1; - } else { - x += 20; - } - //最下边的函数悬浮框显示在上方 - y -= this.floatHint!.clientHeight - 1; - this.floatHint!.style.transform = `translate(${x}px, ${y}px)`; - } - - /** - * 更新悬浮框内容 - * @returns - */ - updateTipContent(): void { - const hoverNode = this.hoverBar; - if (!hoverNode) { - return; - } - const detail = hoverNode!; - this.hintContent = ` - ${ - // @ts-ignore - detail.instruct}
- ${ - // @ts-ignore - parseFloat(detail.heightPer)} - `; - } - - /** - * 判断鼠标当前在那个函数上 - * @param frame - * @param x - * @param y - * @returns - */ - isContains(point: unknown, x: number, y: number): boolean { - // @ts-ignore - return x >= point.x && x <= point.x + 2 && point.y <= y && y <= point.y + point.height; - } - - /** - * 统计onReadable数据各指令的个数 - * @param isCycles - */ - calInstructionRangeCount(isCycles: boolean) { - if (this.onReadableData.length === 0) return; - this.cacheData.length = 0; - const count = this.onReadableData.length; - let instructions = {}; - if (isCycles) { - // @ts-ignore - instructions = this.onReadableData.reduce((pre: unknown, current: unknown) => { - // @ts-ignore - (pre[`${Math.ceil(current.cycles)}`] = pre[`${Math.ceil(current.cycles)}`] || []).push(current); - return pre; - }, {}); - } else { - // @ts-ignore - instructions = this.onReadableData.reduce((pre: unknown, current: unknown) => { - // @ts-ignore - (pre[`${Math.ceil(current.instructions)}`] = pre[`${Math.ceil(current.instructions)}`] || []).push(current); - return pre; - }, {}); - } - this.ctx!.clearRect(0, 0, this.instructionChartEle!.width, this.instructionChartEle!.height); - this.instructionChartEle!.width = this.clientWidth; - - this.xMaxValue = - Object.keys(instructions) - .map((i) => Number(i)) - .reduce((pre, cur) => Math.max(pre, cur), 0) + 10; - const yMaxValue = Object.values(instructions).reduce( - // @ts-ignore - (pre: number, cur: unknown) => Math.max(pre, Number((cur.length / count).toFixed(2))), - 0 - ); - this.yAvg = Number(((yMaxValue / 5) * 1.5).toFixed(2)) || yMaxValue; - const height = this.instructionChartEle!.height; - const width = this.instructionChartEle!.width; - this.drawLineLabelMarkers(width, height, isCycles); - this.drawBar(instructions, height, count); - } - - /** - * 绘制柱状图 - * @param instructionData - * @param height - * @param count - */ - drawBar(instructionData: unknown, height: number, count: number): void { - const yTotal = Number((this.yAvg * 5).toFixed(2)); - const interval = Math.floor((height - paddingBottom) / 6); - // @ts-ignore - for (const x in instructionData) { - const xNum = Number(x); - const xPosition = xStep + (xNum / (this.xCount * this.xAvg)) * (this.xCount * this.xSpacing) - barWidth / 2; - // @ts-ignore - const yNum = Number((instructionData[x].length / count).toFixed(3)); - const percent = Number((yNum / yTotal).toFixed(2)); - const barHeight = (height - paddingBottom - interval) * percent; - this.drawRect(xPosition, height - paddingBottom - barHeight, barWidth, barHeight); - // @ts-ignore - const existX = this.cacheData.find((i) => i.instruct === x); - if (!existX) { - this.cacheData.push({ - instruct: x, - x: xPosition, - y: height - paddingBottom - barHeight, - height: barHeight, - heightPer: parseFloat((yNum * 100).toFixed(2)), - }); - } else { - // @ts-ignore - existX.x = xPosition; - } - } - } - - /** - * 绘制x y轴 - * @param width - * @param height - * @param isCycles - */ - drawLineLabelMarkers(width: number, height: number, isCycles: boolean) { - this.ctx!.font = '12px Arial'; - this.ctx!.lineWidth = 1; - this.ctx!.fillStyle = '#333'; - this.ctx!.strokeStyle = '#ccc'; - if (isCycles) { - this.ctx!.fillText('cycles数(1e5)', width - paddingLeft + 10, height - paddingBottom); - } else { - this.ctx!.fillText('指令数(1e5)', width - paddingLeft + 10, height - paddingBottom); - } - - //绘制x轴 - this.drawLine(xStep, height - paddingBottom, width - paddingLeft, height - paddingBottom); - //绘制y轴 - this.drawLine(xStep, 5, xStep, height - paddingBottom); - //绘制标记 - this.drawMarkers(width, height); - } - - /** - * 绘制横线 - * @param x - * @param y - * @param X - * @param Y - */ - drawLine(x: number, y: number, X: number, Y: number) { - this.ctx!.beginPath; - this.ctx!.moveTo(x, y); - this.ctx!.lineTo(X, Y); - this.ctx!.stroke(); - this.ctx!.closePath(); - } - - /** - * 绘制x y轴刻度 - * @param width - * @param height - */ - drawMarkers(width: number, height: number) { - this.xCount = 0; - //绘制x轴锯齿 - let serrateX = 50; - let y = height - paddingBottom; - const clientWidth = width - paddingLeft - 50; - if (clientWidth > this.xMaxValue) { - this.xSpacing = Math.floor(clientWidth / 20); - this.xAvg = Math.ceil(this.xMaxValue / 20); - } else { - this.xSpacing = Math.floor(clientWidth / 10); - this.xAvg = Math.ceil(this.xMaxValue / 10); - } - while (serrateX <= clientWidth) { - this.xCount++; - serrateX += this.xSpacing; - this.drawLine(serrateX, y, serrateX, y + 5); - } - //绘制x轴刻度 - this.ctx!.textAlign = 'center'; - for (let i = 0; i <= this.xCount; i++) { - const x = xStep + i * this.xSpacing; - this.ctx!.fillText(`${i * this.xAvg}`, x, height); - } - //绘制y轴刻度 - this.ctx!.textAlign = 'center'; - const yPadding = Math.floor((height - paddingBottom) / 6); - for (let i = 0; i < 6; i++) { - if (i === 0) { - this.ctx!.fillText(`${i}%`, 30, y); - } else { - const y = height - paddingBottom - i * yPadding; - this.drawLine(xStep, y, width - paddingLeft, y); - this.ctx!.fillText(`${parseFloat((i * this.yAvg).toFixed(2)) * 100}%`, 30, y); - } - } - } - - /** - * 监听页面size变化 - */ - listenerResize(): void { - new ResizeObserver( - debounce(() => { - if (this.instructionChartEle!.getBoundingClientRect()) { - const box = this.instructionChartEle!.getBoundingClientRect(); - const element = this.parentElement!; - this.startX = box.left + Math.max(element.scrollLeft, document.body.scrollLeft) - element.clientLeft; - this.startY = - box.top + Math.max(element.scrollTop, document.body.scrollTop) - element.clientTop + this.canvasScrollTop; - this.calInstructionRangeCount(this.isChecked); - } - }, 100) - ).observe(this.parentElement!); - } - /** - * 绘制方块 - * @param x - * @param y - * @param X - * @param Y - */ - drawRect(x: number, y: number, X: number, Y: number) { - this.ctx!.beginPath(); - this.ctx!.rect(x, y, X, Y); - this.ctx!.fill(); - this.ctx!.closePath(); - } -} diff --git a/ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionSelection.ts b/ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionSelection.ts deleted file mode 100644 index 96b91294e..000000000 --- a/ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionSelection.ts +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Copyright (C) 2022 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { BaseElement, element } from '../../../../../base-ui/BaseElement'; -import { SelectionParam } from '../../../../bean/BoxSelection'; -import { Rect, drawString } from '../../../../database/ui-worker/ProcedureWorkerCommon'; -import { ColorUtils } from '../../base/ColorUtils'; -import { SampleStruct } from '../../../../database/ui-worker/ProcedureWorkerBpftrace'; -import { SpApplication } from '../../../../SpApplication'; - -const SAMPLE_STRUCT_HEIGHT = 30; -const Y_PADDING = 4; - -@element('tab-sample-instruction-selection') -export class TabPaneSampleInstructionSelection extends BaseElement { - private instructionEle: HTMLCanvasElement | undefined | null; - private ctx: CanvasRenderingContext2D | undefined | null; - private textEle: HTMLSpanElement | undefined | null; - private instructionArray: Array = []; - private instructionData: Array = []; - private isUpdateCanvas = false; - private canvasX = -1; // 鼠标当前所在画布x坐标 - private canvasY = -1; // 鼠标当前所在画布y坐标 - private startX = 0; // 画布相对于整个界面的x坐标 - private startY = 0; // 画布相对于整个界面的y坐标 - private hintContent = ''; //悬浮框内容 - private floatHint: HTMLDivElement | undefined | null; //悬浮框 - private canvasScrollTop = 0; // tab页上下滚动位置 - private hoverSampleStruct: unknown | undefined; - private isChecked: boolean = false; - private maxDepth = 0; - - initHtml(): string { - return ` - -
- -
-
- 指令数数据流 -
-
- `; - } - - initElements(): void { - this.instructionEle = this.shadowRoot?.querySelector('#instruct-select-canvas'); - this.textEle = this.shadowRoot?.querySelector('.headline'); - this.ctx = this.instructionEle?.getContext('2d'); - this.floatHint = this.shadowRoot?.querySelector('#select_float_hint'); - } - - set data(SampleParam: SelectionParam) { - this.hoverSampleStruct = undefined; - this.instructionData = SampleParam.sampleData; - this.getAvgInstructionData(this.instructionData); - queueMicrotask(() => { - this.updateCanvas(this.clientWidth); - this.drawInstructionData(this.isChecked); - }); - } - - connectedCallback(): void { - super.connectedCallback(); - this.parentElement!.onscroll = () => { - this.canvasScrollTop = this.parentElement!.scrollTop; - this.hideTip(); - }; - this.instructionEle!.onmousemove = (e): void => { - if (!this.isUpdateCanvas) { - this.updateCanvasCoord(); - } - this.canvasX = e.clientX - this.startX; - this.canvasY = e.clientY - this.startY + this.canvasScrollTop; - this.onMouseMove(); - }; - this.instructionEle!.onmouseleave = () => { - this.hideTip(); - }; - document.addEventListener('sample-popver-change', (e: unknown) => { - // @ts-ignore - const select = Number(e.detail.select); - this.hoverSampleStruct = undefined; - this.isChecked = Boolean(select); - this.drawInstructionData(this.isChecked); - }); - this.listenerResize(); - } - - /** - * 初始化窗口大小 - * @param newWidth - */ - updateCanvas(newWidth?: number): void { - if (this.instructionEle instanceof HTMLCanvasElement) { - this.instructionEle.style.width = `${100}%`; - this.instructionEle.style.height = `${(this.maxDepth + 1) * SAMPLE_STRUCT_HEIGHT}px`; - if (this.instructionEle.clientWidth === 0 && newWidth) { - this.instructionEle.width = newWidth; - } else { - this.instructionEle.width = this.instructionEle.clientWidth; - } - this.instructionEle.height = (this.maxDepth + 1) * SAMPLE_STRUCT_HEIGHT; - } - } - - /** - * 鼠标移动 - */ - onMouseMove(): void { - const lastNode = this.hoverSampleStruct; - //查找鼠标所在的node - const searchResult = this.searchDataByCoord(this.instructionArray!, this.canvasX, this.canvasY); - if (searchResult) { - this.hoverSampleStruct = searchResult; - //鼠标悬浮的node未改变则不需重新渲染文字 - if (searchResult !== lastNode) { - this.updateTipContent(); - this.ctx!.clearRect(0, 0, this.instructionEle!.width, this.instructionEle!.height); - this.ctx!.beginPath(); - for (const key in this.instructionArray) { - // @ts-ignore - for (let i = 0; i < this.instructionArray[key].length; i++) { - // @ts-ignore - const cur = this.instructionArray[key][i]; - this.draw(this.ctx!, cur); - } - } - this.ctx!.closePath(); - } - this.showTip(); - } else { - this.hideTip(); - this.hoverSampleStruct = undefined; - } - } - - /** - * 隐藏悬浮框 - */ - hideTip(): void { - if (this.floatHint) { - this.floatHint.style.display = 'none'; - } - } - - /** - * 显示悬浮框 - */ - showTip(): void { - this.floatHint!.innerHTML = this.hintContent; - this.floatHint!.style.display = 'block'; - let x = this.canvasX; - let y = this.canvasY - this.canvasScrollTop; - //右边的函数悬浮框显示在左侧 - if (this.canvasX + this.floatHint!.clientWidth > this.instructionEle!.clientWidth || 0) { - x -= this.floatHint!.clientWidth - 1; - } else { - x += 30; - } - //最下边的函数悬浮框显示在上方 - y -= this.floatHint!.clientHeight - 1; - this.floatHint!.style.transform = `translate(${x}px, ${y}px)`; - } - - /** - * 更新悬浮框内容 - * @returns - */ - updateTipContent(): void { - const hoverNode = this.hoverSampleStruct; - if (!hoverNode) { - return; - } - // @ts-ignore - this.hintContent = `${hoverNode.detail}(${hoverNode.name})
- ${ - // @ts-ignore - this.isChecked ? hoverNode.hoverCycles : hoverNode.hoverInstructions} - - `; - } - - /** - * 设置绘制所需的坐标及宽高 - * @param sampleNode - * @param instructions - * @param x - */ - setSampleFrame(sampleNode: SampleStruct, instructions: number, x: number): void { - if (!sampleNode.frame) { - sampleNode.frame! = new Rect(0, 0, 0, 0); - } - sampleNode.frame!.x = x; - sampleNode.frame!.y = sampleNode.depth! * SAMPLE_STRUCT_HEIGHT; - sampleNode.frame!.width = instructions; - sampleNode.frame!.height = SAMPLE_STRUCT_HEIGHT; - } - - /** - * 判断鼠标当前在那个函数上 - * @param frame - * @param x - * @param y - * @returns - */ - isContains(frame: unknown, x: number, y: number): boolean { - // @ts-ignore - return x >= frame.x && x <= frame.x + frame.width && frame.y <= y && y <= frame.y + frame.height; - } - - /** - * 绘制 - * @param isCycles - */ - drawInstructionData(isCycles: boolean): void { - this.isChecked ? (this.textEle!.innerText = 'cycles数据流') : (this.textEle!.innerText = 'instructions数据流'); - const clientWidth = this.instructionEle!.width; - //将数据转换为层级结构 - const instructionArray = this.instructionData - // @ts-ignore - .filter((item: unknown) => (isCycles ? item.cycles : item.instructions)) - .reduce((pre: unknown, cur: unknown) => { - // @ts-ignore - (pre[`${cur.depth}`] = pre[`${cur.depth}`] || []).push(cur); - return pre; - }, {}); - // @ts-ignore - for (const key in instructionArray) { - // @ts-ignore - for (let i = 0; i < instructionArray[key].length; i++) { - // @ts-ignore - const cur = instructionArray[key][i]; - //第一级节点直接将宽度设置为容器宽度 - if (key === '0') { - this.setSampleFrame(cur, clientWidth, 0); - } else { - //获取上一层级节点数据 - // @ts-ignore - const preList = instructionArray[Number(key) - 1]; - //获取当前节点的父节点 - const parentNode = preList.find((node: SampleStruct) => node.name === cur.parentName); - //计算当前节点下指令数之和 用于计算每个节点所占的宽度比 - const total = isCycles - // @ts-ignore - ? instructionArray[key] - // @ts-ignore - .filter((i: unknown) => i.parentName === parentNode.name) - .reduce((pre: number, cur: SampleStruct) => pre + cur.cycles!, 0) - // @ts-ignore - : instructionArray[key] - // @ts-ignore - .filter((i: unknown) => i.parentName === parentNode.name) - .reduce((pre: number, cur: SampleStruct) => pre + cur.instructions!, 0); - const curWidth = isCycles ? cur.cycles : cur.instructions; - const width = Math.floor(parentNode.frame.width * (curWidth / total)); - if (i === 0) { - this.setSampleFrame(cur, width, parentNode.frame.x); - } else { - // @ts-ignore - const preNode = instructionArray[key][i - 1]; - preNode.parentName === parentNode.name - ? this.setSampleFrame(cur, width, preNode.frame.x + preNode.frame.width) - : this.setSampleFrame(cur, width, parentNode.frame.x); - } - } - } - } - // @ts-ignore - this.instructionArray = instructionArray; - - this.ctx!.clearRect(0, 0, this.instructionEle!.width, this.instructionEle!.height); - this.ctx!.beginPath(); - // @ts-ignore - for (const key in instructionArray) { - // @ts-ignore - for (let i = 0; i < instructionArray[key].length; i++) { - // @ts-ignore - const cur = instructionArray[key][i]; - this.draw(this.ctx!, cur); - } - } - this.ctx!.closePath(); - } - - /** - * 更新canvas坐标 - */ - updateCanvasCoord(): void { - if (this.instructionEle instanceof HTMLCanvasElement) { - this.isUpdateCanvas = this.instructionEle.clientWidth !== 0; - if (this.instructionEle.getBoundingClientRect()) { - const box = this.instructionEle.getBoundingClientRect(); - const D = document.documentElement; - this.startX = box.left + Math.max(D.scrollLeft, document.body.scrollLeft) - D.clientLeft; - this.startY = box.top + Math.max(D.scrollTop, document.body.scrollTop) - D.clientTop + this.canvasScrollTop; - } - } - } - - /** - * 获取鼠标悬停的函数 - * @param nodes - * @param canvasX - * @param canvasY - * @returns - */ - searchDataByCoord(nodes: unknown, canvasX: number, canvasY: number): void | null { - // @ts-ignore - for (const key in nodes) { - // @ts-ignore - for (let i = 0; i < nodes[key].length; i++) { - // @ts-ignore - const cur = nodes[key][i]; - if (this.isContains(cur.frame, canvasX, canvasY)) { - return cur; - } - } - } - return null; - } - - /** - * 绘制方法 - * @param ctx - * @param data - */ - draw(ctx: CanvasRenderingContext2D, data: SampleStruct) { - let spApplication = document.getElementsByTagName('sp-application')[0]; - if (data.frame) { - ctx.globalAlpha = 1; - ctx.fillStyle = - ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.name || '', data.depth!, ColorUtils.FUNC_COLOR.length)]; - const textColor = - ColorUtils.FUNC_COLOR[ColorUtils.hashFunc(data.name || '', data.depth!, ColorUtils.FUNC_COLOR.length)]; - ctx.lineWidth = 0.4; - // @ts-ignore - if (this.hoverSampleStruct && data.name == this.hoverSampleStruct.name) { - if (spApplication.dark) { - ctx.strokeStyle = '#fff'; - } else { - ctx.strokeStyle = '#000'; - } - } else { - if (spApplication.dark) { - ctx.strokeStyle = '#000'; - } else { - ctx.strokeStyle = '#fff'; - } - } - ctx.strokeRect(data.frame.x, data.frame.y, data.frame.width, SAMPLE_STRUCT_HEIGHT - Y_PADDING); - ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, SAMPLE_STRUCT_HEIGHT - Y_PADDING); - ctx.fillStyle = ColorUtils.funcTextColor(textColor); - drawString(ctx, `${data.detail}(${data.name})`, 5, data.frame, data); - } - } - - /** - * 统计框选指令数的平均值 - * @param instructionData - * @returns - */ - getAvgInstructionData(instructionData: Array): unknown { - // @ts-ignore - const length = instructionData[0].property.length; - // @ts-ignore - const knowData = instructionData.filter((instruction) => instruction.name.indexOf('unknown') < 0); - knowData.forEach((instruction) => { - // @ts-ignore - if (instruction.property.length > 0) { - // @ts-ignore - const totalInstruction = instruction.property.reduce( - (pre: number, cur: SampleStruct) => pre + Math.ceil(cur.instructions!), - 0 - ); - // @ts-ignore - const totalCycles = instruction.property.reduce( - (pre: number, cur: SampleStruct) => pre + Math.ceil(cur.cycles!), - 0 - ); - // @ts-ignore - instruction.instructions = Math.ceil(totalInstruction / length) || 1; - // @ts-ignore - instruction.cycles = Math.ceil(totalCycles / length) || 1; - // @ts-ignore - instruction.hoverInstructions = Math.ceil(totalInstruction / length); - // @ts-ignore - instruction.hoverCycles = Math.ceil(totalCycles / length); - // @ts-ignore - this.maxDepth = Math.max(this.maxDepth, instruction.depth); - } - }); - // @ts-ignore - const unknownData = instructionData.filter((instruction) => instruction.name.indexOf('unknown') > -1); - let instructionSum = 0; - let cyclesSum = 0; - let hoverInstructionsSum = 0; - let hoverCyclesSum = 0; - unknownData.forEach((unknown) => { - instructionSum = 0; - cyclesSum = 0; - hoverInstructionsSum = 0; - hoverCyclesSum = 0; - // @ts-ignore - for (const key in unknown.children) { - // @ts-ignore - const child = instructionData.find((instruction) => instruction.name === key); - // @ts-ignore - instructionSum += child.instructions ?? 0; - // @ts-ignore - cyclesSum += child.cycles ?? 0; - // @ts-ignore - hoverInstructionsSum += child.hoverInstructions ?? 0; - // @ts-ignore - hoverCyclesSum += child.hoverCycles ?? 0; - } - // @ts-ignore - unknown.instructions = instructionSum; - // @ts-ignore - unknown.cycles = cyclesSum; - // @ts-ignore - unknown.hoverInstructions = hoverInstructionsSum; - // @ts-ignore - unknown.hoverCycles = hoverCyclesSum; - }); - return instructionData; - } - - /** - * 监听页面size变化 - */ - listenerResize(): void { - new ResizeObserver(() => { - if (this.instructionEle!.getBoundingClientRect()) { - const box = this.instructionEle!.getBoundingClientRect(); - const element = document.documentElement; - this.startX = box.left + Math.max(element.scrollLeft, document.body.scrollLeft) - element.clientLeft; - this.startY = - box.top + Math.max(element.scrollTop, document.body.scrollTop) - element.clientTop + this.canvasScrollTop; - } - }).observe(this.parentElement!); - } -} diff --git a/ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionSelectionTotalTime.ts b/ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionSelectionTotalTime.ts deleted file mode 100644 index 5015f1577..000000000 --- a/ide/src/trace/component/trace/sheet/bpftrace/TabPaneSampleInstructionSelectionTotalTime.ts +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright (C) 2022 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { BaseElement, element } from '../../../../../base-ui/BaseElement'; -import { SelectionParam } from '../../../../bean/BoxSelection'; -import { debounce } from '../../../Utils'; - -const paddingLeft = 100; -const paddingBottom = 15; -const xStart = 50; // x轴起始位置 -const barWidth = 2; // 柱子宽度 -const millisecond = 1_000_000; - -@element('tab-sample-instructions-totaltime-selection') -export class TabPaneSampleInstructionTotalTime extends BaseElement { - private instructionChartEle: HTMLCanvasElement | undefined | null; - private ctx: CanvasRenderingContext2D | undefined | null; - private cacheData: Array = []; - private canvasX = -1; // 鼠标当前所在画布x坐标 - private canvasY = -1; // 鼠标当前所在画布y坐标 - private startX = 0; // 画布相对于整个界面的x坐标 - private startY = 0; // 画布相对于整个界面的y坐标 - private hoverBar: unknown; - private onReadableData: Array = []; - private hintContent = ''; //悬浮框内容 - private floatHint: HTMLDivElement | undefined | null; //悬浮框 - private canvasScrollTop = 0; // tab页上下滚动位置 - private isUpdateCanvas = false; - private xCount = 0; //x轴刻度个数 - private xMaxValue = 0; //x轴上数据最大值 - private xSpacing = 50; //x轴间距 - private xAvg = 0; //根据xMaxValue进行划分 用于x轴上刻度显示 - private yAvg = 0; //根据yMaxValue进行划分 用于y轴上刻度显示 - - initHtml(): string { - return ` - - -
- `; - } - - initElements(): void { - this.instructionChartEle = this.shadowRoot?.querySelector('#instruct-chart-canvas'); - this.ctx = this.instructionChartEle?.getContext('2d'); - this.floatHint = this.shadowRoot?.querySelector('#float_hint'); - } - - set data(SampleParam: SelectionParam) { - // @ts-ignore - this.onReadableData = SampleParam.sampleData[0].property; - this.calInstructionRangeCount(); - } - - connectedCallback(): void { - super.connectedCallback(); - this.parentElement!.onscroll = () => { - this.canvasScrollTop = this.parentElement!.scrollTop; - this.hideTip(); - }; - this.instructionChartEle!.onmousemove = (e): void => { - if (!this.isUpdateCanvas) { - this.updateCanvasCoord(); - } - this.canvasX = e.clientX - this.startX; - this.canvasY = e.clientY - this.startY + this.canvasScrollTop; - this.onMouseMove(); - }; - this.instructionChartEle!.onmouseleave = () => { - this.hideTip(); - }; - this.listenerResize(); - } - - /** - * 更新canvas坐标 - */ - updateCanvasCoord(): void { - if (this.instructionChartEle instanceof HTMLCanvasElement) { - this.isUpdateCanvas = this.instructionChartEle.clientWidth !== 0; - if (this.instructionChartEle.getBoundingClientRect()) { - const box = this.instructionChartEle.getBoundingClientRect(); - const D = this.parentElement!; - this.startX = box.left + Math.max(D.scrollLeft, document.body.scrollLeft) - D.clientLeft; - this.startY = box.top + Math.max(D.scrollTop, document.body.scrollTop) - D.clientTop + this.canvasScrollTop; - } - } - } - - /** - * 获取鼠标悬停的函数 - * @param nodes - * @param canvasX - * @param canvasY - * @returns - */ - searchDataByCoord(nodes: unknown, canvasX: number, canvasY: number):unknown { - // @ts-ignore - for (let i = 0; i < nodes.length; i++) { - // @ts-ignore - const element = nodes[i]; - if (this.isContains(element, canvasX, canvasY)) { - return element; - } - } - return null; - } - - /** - * 鼠标移动 - */ - onMouseMove(): void { - const lastNode = this.hoverBar; - //查找鼠标所在的node - const searchResult = this.searchDataByCoord(this.cacheData, this.canvasX, this.canvasY); - if (searchResult) { - this.hoverBar = searchResult; - //鼠标悬浮的node未改变则不需重新渲染文字 - if (searchResult !== lastNode) { - this.updateTipContent(); - } - this.showTip(); - } else { - this.hideTip(); - this.hoverBar = undefined; - } - } - - /** - * 隐藏悬浮框 - */ - hideTip(): void { - if (this.floatHint) { - this.floatHint.style.display = 'none'; - this.instructionChartEle!.style.cursor = 'default'; - } - } - - /** - * 显示悬浮框 - */ - showTip(): void { - this.floatHint!.innerHTML = this.hintContent; - this.floatHint!.style.display = 'block'; - this.instructionChartEle!.style.cursor = 'pointer'; - let x = this.canvasX; - let y = this.canvasY - this.canvasScrollTop; - //右边的函数悬浮框显示在左侧 - if (this.canvasX + this.floatHint!.clientWidth > (this.instructionChartEle!.clientWidth || 0)) { - x -= this.floatHint!.clientWidth - 1; - } else { - x += 30; - } - //最下边的函数悬浮框显示在上方 - y -= this.floatHint!.clientHeight - 1; - this.floatHint!.style.transform = `translate(${x}px, ${y}px)`; - } - - /** - * 更新悬浮框内容 - */ - updateTipContent(): void { - const hoverNode = this.hoverBar; - if (!hoverNode) { - return; - } - const detail = hoverNode!; - // @ts-ignore - this.hintContent = ` ${detail.instruct}
${parseFloat( - // @ts-ignore - detail.heightPer - )} `; - } - - /** - * 判断鼠标当前在那个函数上 - * @param frame - * @param x - * @param y - * @returns - */ - isContains(point: unknown, x: number, y: number): boolean { - // @ts-ignore - return x >= point.x && x <= point.x + 2 && point.y <= y && y <= point.y + point.height; - } - - /** - * 统计onReadable数据各指令数个数 - */ - calInstructionRangeCount() { - if (this.onReadableData.length === 0) return; - this.cacheData.length = 0; - const count = this.onReadableData.length; - const onReadableData = this.onReadableData; - const instructionArray = onReadableData.reduce((pre: unknown, current: unknown) => { - // @ts-ignore - const dur = parseFloat(((current.end - current.begin) / millisecond).toFixed(1)); - // @ts-ignore - (pre[dur] = pre[dur] || []).push(current); - return pre; - }, {}); - this.ctx!.clearRect(0, 0, this.instructionChartEle!.width, this.instructionChartEle!.height); - this.instructionChartEle!.width = this.clientWidth; - - this.xMaxValue = - // @ts-ignore - Object.keys(instructionArray) - .map((i) => Number(i)) - .reduce((pre, cur) => Math.max(pre, cur), 0) + 5; // @ts-ignore - const yMaxValue = Object.values(instructionArray).reduce( - // @ts-ignore - (pre: number, cur: unknown) => Math.max(pre, Number((cur.length / count).toFixed(2))), - 0 - ); - this.yAvg = Number(((yMaxValue / 5) * 1.5).toFixed(2)); - const height = this.instructionChartEle!.height; - const width = this.instructionChartEle!.width; - this.drawLineLabelMarkers(width, height); - this.drawBar(instructionArray, height, count); - } - - /** - * 绘制柱状图 - * @param instructionData - * @param height - * @param count - */ - drawBar(instructionData: unknown, height: number, count: number): void { - const yTotal = Number((this.yAvg * 5).toFixed(2)); - const interval = Math.floor((height - paddingBottom) / 6); // @ts-ignore - for (const x in instructionData) { - const xNum = Number(x); - const xPosition = xStart + (xNum / (this.xCount * this.xAvg)) * (this.xCount * this.xSpacing) - barWidth / 2; // @ts-ignore - const yNum = Number((instructionData[x].length / count).toFixed(3)); - const percent = Number((yNum / yTotal).toFixed(2)); - const barHeight = (height - paddingBottom - interval) * percent; - this.drawRect(xPosition, height - paddingBottom - barHeight, barWidth, barHeight); // @ts-ignore - const existX = this.cacheData.find((i) => i.instruct === x); - if (!existX) { - this.cacheData.push({ - instruct: x, - x: xPosition, - y: height - paddingBottom - barHeight, - height: barHeight, - heightPer: parseFloat((yNum * 100).toFixed(2)), - }); - } else { - // @ts-ignore - existX.x = xPosition; - } - } - } - - /** - * 绘制x y轴 - * @param width - * @param height - */ - drawLineLabelMarkers(width: number, height: number) { - this.ctx!.font = '12px Arial'; - this.ctx!.lineWidth = 1; - this.ctx!.fillStyle = '#333'; - this.ctx!.strokeStyle = '#ccc'; - - this.ctx!.fillText('时长 / ms', width - paddingLeft, height - paddingBottom); - - //绘制x轴 - this.drawLine(xStart, height - paddingBottom, width - paddingLeft, height - paddingBottom); - //绘制y轴 - this.drawLine(xStart, 5, xStart, height - paddingBottom); - //绘制标记 - this.drawMarkers(width, height); - } - - /** - * 绘制横线 - * @param x - * @param y - * @param X - * @param Y - */ - drawLine(x: number, y: number, X: number, Y: number) { - this.ctx!.beginPath; - this.ctx!.moveTo(x, y); - this.ctx!.lineTo(X, Y); - this.ctx!.stroke(); - this.ctx!.closePath(); - } - - /** - * 绘制x y轴刻度 - * @param width - * @param height - */ - drawMarkers(width: number, height: number) { - this.xCount = 0; - //绘制x轴锯齿 - let serrateX = 50; - let yHeight = height - paddingBottom; - const clientWidth = width - paddingLeft - 50; - if (clientWidth > this.xMaxValue) { - this.xSpacing = Math.floor(clientWidth / 20); - this.xAvg = Math.ceil(this.xMaxValue / 20); - } else { - this.xSpacing = Math.floor(clientWidth / 10); - this.xAvg = Math.ceil(this.xMaxValue / 10); - } - while (serrateX <= clientWidth) { - this.xCount++; - serrateX += this.xSpacing; - this.drawLine(serrateX, yHeight, serrateX, yHeight + 5); - } - //绘制x轴刻度 - this.ctx!.textAlign = 'center'; - for (let i = 0; i <= this.xCount; i++) { - const x = xStart + i * this.xSpacing; - this.ctx!.fillText(`${i * this.xAvg}`, x, height); - } - //绘制y轴刻度 - this.ctx!.textAlign = 'center'; - const yPadding = Math.floor((height - paddingBottom) / 6); - for (let i = 0; i < 6; i++) { - const y = height - paddingBottom - i * yPadding; - if (i === 0) { - this.ctx!.fillText(`${i}%`, 30, y); - } else { - this.drawLine(xStart, y, width - paddingLeft, y); - this.ctx!.fillText(`${parseFloat((i * this.yAvg).toFixed(2)) * 100}%`, 30, y); - } - } - } - - /** - * 监听页面size变化 - */ - listenerResize(): void { - new ResizeObserver( - debounce(() => { - if (this.instructionChartEle!.getBoundingClientRect()) { - const box = this.instructionChartEle!.getBoundingClientRect(); - const element = this.parentElement!; - this.startX = box.left + Math.max(element.scrollLeft, document.body.scrollLeft) - element.clientLeft; - this.startY = - box.top + Math.max(element.scrollTop, document.body.scrollTop) - element.clientTop + this.canvasScrollTop; - this.calInstructionRangeCount(); - } - }, 100) - ).observe(this.parentElement!); - } - /** - * 绘制方块 - * @param x - * @param y - * @param X - * @param Y - */ - drawRect(x: number, y: number, X: number, Y: number) { - this.ctx!.beginPath(); - this.ctx!.rect(x, y, X, Y); - this.ctx!.fill(); - this.ctx!.closePath(); - } -} -- Gitee From 50b837c8fe20d2d6fff3e5a9eb428e5732905619 Mon Sep 17 00:00:00 2001 From: wangyujie Date: Mon, 18 Nov 2024 14:08:30 +0800 Subject: [PATCH 03/38] =?UTF-8?q?feat:slice=E9=A1=B5=E6=96=B0=E5=A2=9Eself?= =?UTF-8?q?time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: wangyujie --- ide/src/trace/bean/BoxSelection.ts | 1 + .../trace/sheet/process/TabPaneSliceChild.ts | 38 +++++++++- .../trace/sheet/process/TabPaneSlices.ts | 41 ++++++++++- ide/src/trace/database/sql/Func.sql.ts | 71 +++++++++++++++++++ .../trace/database/sql/ProcessThread.sql.ts | 1 + 5 files changed, 148 insertions(+), 4 deletions(-) diff --git a/ide/src/trace/bean/BoxSelection.ts b/ide/src/trace/bean/BoxSelection.ts index c0509abc1..f4ae309f8 100644 --- a/ide/src/trace/bean/BoxSelection.ts +++ b/ide/src/trace/bean/BoxSelection.ts @@ -1346,6 +1346,7 @@ export class SelectionData { maxDuration: number = 0; maxDurationFormat: string = ''; occurrences: number = 0; + selfTime: number = 0; state: string = ''; trackId: number = 0; delta: string = ''; diff --git a/ide/src/trace/component/trace/sheet/process/TabPaneSliceChild.ts b/ide/src/trace/component/trace/sheet/process/TabPaneSliceChild.ts index 1e3cdfa83..65b1ddcdf 100644 --- a/ide/src/trace/component/trace/sheet/process/TabPaneSliceChild.ts +++ b/ide/src/trace/component/trace/sheet/process/TabPaneSliceChild.ts @@ -18,7 +18,7 @@ import { LitTable } from '../../../../../base-ui/table/lit-table'; import { SelectionData, SelectionParam, SliceBoxJumpParam } from '../../../../bean/BoxSelection'; import { Utils } from '../../base/Utils'; import { resizeObserver } from '../SheetUtils'; -import { getTabDetails, getGhDetails, getSfDetails } from '../../../../database/sql/Func.sql'; +import { getTabDetails, getGhDetails, getSfDetails, getParentDetail, getFuncChildren } from '../../../../database/sql/Func.sql'; @element('box-slice-child') export class TabPaneSliceChild extends BaseElement { @@ -120,14 +120,46 @@ export class TabPaneSliceChild extends BaseElement { return promises; }; this.sliceChildTbl!.loading = true; - Promise.all([result1(), result2(), result3()]).then(res => { + Promise.all([result1(), result2(), result3()]).then(async res => { this.sliceChildTbl!.loading = false; let result: unknown = (res[0] || []).concat(res[1] || []).concat(res[2] || []); this.sliceChildTbl!.loading = false; // @ts-ignore if (result.length !== null && result.length > 0) { + let funcIdArr: Array = []; + let minStartTS = Infinity; + let maxEndTS = -Infinity; + // @ts-ignore + let parentDetail: [{ startTS: number, endTS: number, depth: number, id: number, name: string }] = await getParentDetail(val.param.processId, val.param.threadId, val.param.leftNs, val.param.rightNs); + + // @ts-ignore + parentDetail.forEach(item => { + funcIdArr.push(item.id); + if (item.depth === 0) { + if (item.startTS < minStartTS) { + minStartTS = item.startTS; + } + if (item.endTS > maxEndTS) { + maxEndTS = item.endTS; + } + } + }); + + let FuncChildrenList = await getFuncChildren(funcIdArr, val.param.processId, val.param.threadId, minStartTS, maxEndTS, true); + let childDurMap = new Map(); + FuncChildrenList.forEach((it: any) => { + if (!childDurMap.has(it.parentId)) { + childDurMap.set(it.parentId, it.duration); + } else { + let dur = childDurMap.get(it.parentId) + dur += it.duration + childDurMap.set(it.parentId, dur!); + } + }); // @ts-ignore result.map((e: unknown) => { + // @ts-ignore + e.selfTime = childDurMap.has(e.id) ? (e.duration - childDurMap.get(e.id)) / 1000000 : e.duration / 1000000; // @ts-ignore e.startTime = Utils.getTimeString(e.startNs); // @ts-ignore @@ -179,6 +211,8 @@ export class TabPaneSliceChild extends BaseElement { + + `; } diff --git a/ide/src/trace/component/trace/sheet/process/TabPaneSlices.ts b/ide/src/trace/component/trace/sheet/process/TabPaneSlices.ts index 49509b0b0..6edab339c 100644 --- a/ide/src/trace/component/trace/sheet/process/TabPaneSlices.ts +++ b/ide/src/trace/component/trace/sheet/process/TabPaneSlices.ts @@ -20,7 +20,7 @@ import { SpSystemTrace } from '../../../SpSystemTrace'; import { TraceRow } from '../../base/TraceRow'; import { LitSearch } from '../../search/Search'; import { resizeObserver } from '../SheetUtils'; -import { getTabSlicesAsyncFunc, getTabSlicesAsyncCatFunc } from '../../../../database/sql/Func.sql'; +import { getTabSlicesAsyncFunc, getTabSlicesAsyncCatFunc, getParentDetail, getFuncChildren } from '../../../../database/sql/Func.sql'; import { getTabSlices } from '../../../../database/sql/ProcessThread.sql'; import { FuncStruct } from '../../../../database/ui-worker/ProcedureWorkerFunc'; import { Utils } from '../../base/Utils'; @@ -147,13 +147,45 @@ export class TabPaneSlices extends BaseElement { }; this.slicesTbl!.loading = true; - Promise.all([result1(), result2(), result3()]).then(res => { + Promise.all([result1(), result2(), result3()]).then(async res => { let processSlicesResult = (res[0] || []).concat(res[1] || []).concat(res[2] || []); if (processSlicesResult !== null && processSlicesResult.length > 0) { + let funcIdArr: Array = []; + let minStartTS = Infinity; + let maxEndTS = -Infinity; + // @ts-ignore + let parentDetail: [{ startTS: number, endTS: number, depth: number, id: number, name: string }] = await getParentDetail(slicesParam.processIds, slicesParam.funTids, slicesParam.leftNs, slicesParam.rightNs); + // @ts-ignore + parentDetail.forEach(item => { + funcIdArr.push(item.id); + if (item.depth === 0) { + if (item.startTS < minStartTS) { + minStartTS = item.startTS; + } + if (item.endTS > maxEndTS) { + maxEndTS = item.endTS; + } + } + }); + + let FuncChildrenList = await getFuncChildren(funcIdArr, slicesParam.processIds, slicesParam.funTids, minStartTS, maxEndTS, false); + let childDurMap: Map> = new Map(); + FuncChildrenList.forEach((it: any) => { + if (!childDurMap.has(it.parentId)) { + childDurMap.set(it.parentId, it.duration); + } else { + let dur = childDurMap.get(it.parentId) + dur += it.duration + childDurMap.set(it.parentId, dur!); + } + }); let sumWall = 0.0; let sumOcc = 0; + let sumSelf = 0.0; let processSlicesResultMap: Map = new Map(); for (let processSliceItem of processSlicesResult) { + //@ts-ignore + processSliceItem.selfTime = childDurMap.has(processSliceItem.id) ? parseFloat(((processSliceItem.wallDuration - childDurMap.get(processSliceItem.id)) / 1000000).toFixed(5)) : parseFloat((processSliceItem.wallDuration / 1000000).toFixed(5)); //@ts-ignore processSliceItem.name = processSliceItem.name === null ? '' : processSliceItem.name; //@ts-ignore @@ -161,6 +193,8 @@ export class TabPaneSlices extends BaseElement { //@ts-ignore sumOcc += processSliceItem.occurrences; //@ts-ignore + sumSelf += processSliceItem.selfTime; + //@ts-ignore processSliceItem.wallDuration = parseFloat((processSliceItem.wallDuration / 1000000.0).toFixed(5)); //@ts-ignore if (processSlicesResultMap.has(processSliceItem.name)) {//@ts-ignore @@ -185,6 +219,7 @@ export class TabPaneSlices extends BaseElement { count.process = ' '; count.wallDuration = parseFloat((sumWall / 1000000.0).toFixed(5)); count.occurrences = sumOcc; + count.selfTime = parseFloat(sumSelf.toFixed(5)); count.tabTitle = 'Summary'; // @ts-ignore count.allName = processSlicesResultsValue.map((item: unknown) => item.name); @@ -337,6 +372,8 @@ export class TabPaneSlices extends BaseElement { + + `; } diff --git a/ide/src/trace/database/sql/Func.sql.ts b/ide/src/trace/database/sql/Func.sql.ts index f2a47dc58..eeaba5ff9 100644 --- a/ide/src/trace/database/sql/Func.sql.ts +++ b/ide/src/trace/database/sql/Func.sql.ts @@ -326,6 +326,7 @@ export const getTabSlicesAsyncFunc = ( let sql = ` SELECT c.name AS name, + c.id, sum( c.dur ) AS wallDuration, count( c.name ) AS occurrences FROM @@ -368,6 +369,7 @@ export const getTabDetails = ( SELECT c.name AS name, c.dur AS duration, + c.id, P.pid AS processId, P.name AS process, A.tid AS threadId, @@ -408,6 +410,7 @@ export const getSfDetails = ( P.name AS process, A.tid AS threadId, A.name AS thread, + c.id, c.ts - D.start_ts as startNs FROM (SELECT id, ts, parent_id, dur, name from callstack where cookie NOT NULL) C, @@ -423,6 +426,73 @@ export const getSfDetails = ( `; return query('getSfDetails', sql, {}); }; +export const getParentDetail = ( + asyncPid: Array, + funTids: Array, + leftNS: number, + rightNS: number): + Promise> => + query( + 'getParentTime', + ` SELECT + C.ts - D.start_ts AS startTS, + C.ts - D.start_ts + C.dur AS endTS, + C.depth, + c.id, + c.name + FROM + thread A + JOIN trace_range D + LEFT JOIN process P ON P.id = A.ipid + LEFT JOIN callstack C ON A.id = C.callid + WHERE + C.ts > 0 + AND C.dur >= - 1 + AND NOT ( + ( C.ts - D.start_ts + C.dur < ${leftNS} ) + OR ( C.ts - D.start_ts > ${rightNS} ) + ) + AND C.cookie IS NULL + AND A.tid IN (${funTids!.join(',')}) + AND P.pid IN (${asyncPid.join(',')}) + ` + ); +export const getFuncChildren = ( + funcIds: Array, + asyncPid: Array, + funTids: Array, + leftNS: number, + rightNS: number, + isChild: boolean +): //@ts-ignore + Promise> => { + let durStr = isChild ? 'C.dur AS duration,' : 'SUM(COALESCE(C.dur, 0)) AS duration,'; + let condition = isChild ? '' : 'group by parentName'; + let sql = ` + SELECT + c.parent_id parentId, + ${durStr} + c.id, + c.name, + c1.name parentName + FROM + thread A,trace_range D + LEFT JOIN process P ON P.id = A.ipid + LEFT JOIN callstack C ON A.id = C.callid + LEFT JOIN callstack C1 ON c.parent_id = C1.id + where + C.ts > 0 + and + c.dur >= -1 + and + not ((C.ts - D.start_ts + C.dur < ${leftNS}) or (C.ts - D.start_ts > ${rightNS})) + and A.tid in (${funTids!.join(',')}) + and c.cookie is null + and P.pid in (${asyncPid.join(',')}) + and c.parent_id in (${funcIds.join(',')})${condition} + `; + return query('getTabDetails', sql, {}); +}; export const getGhDetails = ( asyncNames: Array, catName: string, @@ -476,6 +546,7 @@ export const getTabSlicesAsyncCatFunc = ( ` select c.name as name, + c.id, sum(c.dur) as wallDuration, count(c.name) as occurrences from diff --git a/ide/src/trace/database/sql/ProcessThread.sql.ts b/ide/src/trace/database/sql/ProcessThread.sql.ts index 1fa345fe3..40812eabc 100644 --- a/ide/src/trace/database/sql/ProcessThread.sql.ts +++ b/ide/src/trace/database/sql/ProcessThread.sql.ts @@ -1215,6 +1215,7 @@ export const getTabSlices = ( ` select c.name as name, + c.id, sum(c.dur) as wallDuration, count(c.name) as occurrences from -- Gitee From 51bdd3b33887b19be8a540dcb03fbde9b0ea0617 Mon Sep 17 00:00:00 2001 From: liufei Date: Tue, 19 Nov 2024 10:02:22 +0800 Subject: [PATCH 04/38] =?UTF-8?q?feat:=E9=85=8D=E7=BD=AEtrace=E7=82=B9?= =?UTF-8?q?=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liufei --- ide/src/doc/funDetail.json | 384 +++++++++++++++++++++++++++++++++++-- 1 file changed, 372 insertions(+), 12 deletions(-) diff --git a/ide/src/doc/funDetail.json b/ide/src/doc/funDetail.json index e9a08eb4d..9f8b36213 100644 --- a/ide/src/doc/funDetail.json +++ b/ide/src/doc/funDetail.json @@ -31,12 +31,6 @@ "EN": "this is an english translate for CN", "flag": "0" }, - { - "slice": "FlushLayoutTask", - "CN": "组件的布局,这一块可以确认是什么组件在创建或者复用", - "EN": "this is an english translate for CN", - "flag": "0" - }, { "slice": "CreateTaskMeasure", "CN": "组件的测量。这一块可以确认是什么组件在创建", @@ -49,12 +43,6 @@ "EN": "this is an english translate for CN", "flag": "0" }, - { - "slice": "JSAnimation", - "CN": "如果FlushVsync下面出现这个trace点,说明触发了ArkUI的动效,例如使用了属性动画animation属性或者显示动画animateTo。可以排查一下代码看一下这种场景是否需要触发这个动画,有时候会因为组件的刷新而出现该动画的冗余绘制", - "EN": "this is an english translate for CN", - "flag": "0" - }, { "slice": "BuildRecyle", "CN": "如果出现这个trace说明,这个组件是走复用逻辑的。当前如果组件节点较多,也会导致组件复用的时候耗时比较长导致丢帧。", @@ -186,6 +174,378 @@ "CN": "触发了shader编译,需要通过与之shader来避免。shader缓存是业界通用方案,在动效前几次执行的时候会概率性出现,用于创建该场景的着色器并将其缓存,以使之后的操作可以直接复用原有的着丝琪,从而提升系统的性能。但shader编译耗时太长导致RS侧丢帧造成动效卡顿", "EN": "this is an english translate for CN", "flag": "0" + }, + { + "slice": "OnVsyncEvent now", + "CN": "收到Vsync信号,渲染流程开始(框架trace,)不用验证", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "UITaskScheduler::FlushTask", + "CN": "刷新UI界面,包括布局、渲染和动画等(框架trace,不用验证)", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "FlushMessages", + "CN": "发送消息通知图形侧进行渲染(框架trace,不用验证)", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "FlushRenderTask", + "CN": "总渲染任务执行(框架trace,不用验证)", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Layout", + "CN": "节点布局(框架trace,不用验证)", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "FrameNode::RenderTask", + "CN": "单个渲染任务执行(框架trace,不用验证)", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ListLayoutAlgorithm::MeasureListItem:", + "CN": "计算列表项的布局尺寸", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ListLayoutAlgorithm::MeasureListItemGroup:", + "CN": "计算列表ListItemGroup项的布局尺寸", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "HandleDragStart, id:", + "CN": "拖拽开始", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "HandleDragUpdate, mainDelta", + "CN": "拖拽中", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "HandleDragEnd, mainPosition:", + "CN": "拖拽结束", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Scrollable friction animation start,", + "CN": "抛滑动画开始", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Scrollable friction animation finish, id:", + "CN": "抛滑动画结束", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "StopFrictionAnimation, id:", + "CN": "关闭抛滑动画", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Scrollable spring animation start, start:", + "CN": "过界回弹动画开始", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Scrollable spring animation finish, id:", + "CN": "过界回弹动画结束", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Scrollable spring animation update, start:", + "CN": "更新过界回弹动画开始", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "StopSpringAnimation, id:", + "CN": "关闭过界回弹动画", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Scroll snap animation start, start:", + "CN": "Scroll的限位动画开始", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Scroll snap animation finish, id:", + "CN": "Scroll的限位动画结束", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "List start snap animation, start:", + "CN": "List的限位动画开始", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "List snap animation finish, id:", + "CN": "List的限位动画结束", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "StopSnapAnimation isSnapAnimationStop_:", + "CN": "关闭限位动画", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "APP_LIST_FLING", + "CN": "滑动阶段(包括所有的拖拽,抛滑、回弹、滚动控制器动画)", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "TRAILING_ANIMATION id:", + "CN": "动画的拖尾阶段", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "HandleTouchDown, panDirection:", + "CN": "手指按下", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "HandleTouchUp, isDragging_:", + "CN": "手指抬起(新增)", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "HandleTouchCancel, id:", + "CN": "取消手势", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ScrollAbort, no OnScrollStart, id:", + "CN": "ScrollAbort为true,触发OnScrollStart失败,(一般出现在抛滑后调用滚动控制器启动滚动动画)", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "OnScrollStart, id:", + "CN": "触发OnScrollStart成功", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ScrollAbort, no OnScrollStop, id:", + "CN": "ScrollAbort为true,触发OnScrollStop失败(一般出现在抛滑后调用滚动控制器启动滚动动画)", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "OnScrollStop, id:", + "CN": "触发OnScrollStop成功", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ScrollBy, offset:", + "CN": "滚动控制器触发ScrollBy", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ScrollTo with animation, position:", + "CN": "滚动控制器触发带动画的ScrollTo", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ScrollTo without animation, position:", + "CN": "滚动控制器触发不带动画的ScrollTo", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ScrollPage with animation, position", + "CN": "滚动控制器触发带动画的Scrollpage", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ScrollPage without animation, position:", + "CN": "滚动控制器触发不带动画的Scrollpage", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Fling, flingVelocity:", + "CN": "滚动控制器触发惯性滚动", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Scroll ScrollToEdge scrollEdgeType:", + "CN": "Scroll触发ScrollEdge", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ScrollToEdge scrollEdgeType:", + "CN": "滚动控制器触发的ScrollEdge", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ScrollToIndex, index:", + "CN": "滚动控制器触发ScrollToIndex", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "CUSTOM_ANIMATOR_SCROLLER_ANIMATION", + "CN": "滚动控制器触发ScrollTo滑动动画", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "HandleScroll, initOffset:", + "CN": "嵌套滚动处理偏移量", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "HandleScrollVelocity, IsOutOfBoundary:", + "CN": "嵌套滚动中处理离手速度", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "HandleOverScroll, IsOutOfBoundary:", + "CN": "离手时处理过界滚动", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "SCROLLER_FIX_VELOCITY_ANIMATION", + "CN": "控制器触发按固定速度滚动到边缘", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "CheckRestartSpring, sizeDiminished is true, do ProcessSpringUpdate, id:", + "CN": "回弹动画期间组件大小被修改导致触发回弹更新动画", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "CheckRestartSpring, do ProcessScrollOver, id:", + "CN": "在静态场景下因组件大小修改原因导致组件处于过界状态,触发回弹动画恢复到未过界状态", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ListItemGroup:", + "CN": "设置lane的ListItemGroup列表布局", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "ListItem:", + "CN": "设置lane的列表布局", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "change direction in spring animation and start fling animation, distance::", + "CN": "过界回弹动效中出现方向改变,并启动抛滑动效", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "scrollPause set true to stop ProcessScrollMotion, canOverScroll:", + "CN": "抛滑动效期间达成停止条件从而关闭抛滑动效,并触发结束回调(一般出现在快速抛滑过界的场景)", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Tabs.onChange index ", + "CN": "tabs页面切换后", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Tabs/Swiper drag start", + "CN": "跟手拖拽开始", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Tabs/Swiper drag end", + "CN": "跟手拖拽结束", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Tabs/Swiper start property animation, X:", + "CN": "属性动画开始", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Tabs/Swiper finish property animation, X: ", + "CN": "属性动画结束", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Tabs/Swiper start spring animation", + "CN": "回弹动画开始", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Tabs/Swiper finish spring animation, offset:", + "CN": "回弹动画结束", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Tabs/Swiper start translate animation", + "CN": "自定义属性动画开始", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "Tabs/Swiper finish translate animation", + "CN": "自定义属性动画结束", + "EN": "this is an english translate for CN", + "flag": "0" + }, + { + "slice": "FlushVsync", + "CN": "刷新视图同步事件,包括记录帧信息、刷新任务、绘制渲染上下文、处理用户输入", + "EN": "this is an english translate for CN", + "flag": "0" } ] } \ No newline at end of file -- Gitee From b2af125fd7fef3e2ab567d2dfd3169d4e14420b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=86=E5=B7=9D?= Date: Wed, 20 Nov 2024 11:44:24 +0800 Subject: [PATCH 05/38] =?UTF-8?q?bugfix:=20WaitForParserEnd()=E5=9C=A8=5FT?= =?UTF-8?q?raceStreamerParseDataEx=E5=92=8C=5FTraceStreamerInParseDataOver?= =?UTF-8?q?=E9=83=BD=E6=9C=89=E8=B0=83=E7=94=A8,=20=E6=B8=85=E7=90=86?= =?UTF-8?q?=E4=B8=80=E4=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 集川 --- ide/webpack.config.js | 2 +- trace_streamer/src/rpc/rpc_server.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ide/webpack.config.js b/ide/webpack.config.js index 05f6d5aa4..955e330d8 100644 --- a/ide/webpack.config.js +++ b/ide/webpack.config.js @@ -89,7 +89,7 @@ function buildMultiPlatform() { path.normalize(path.join(outPath, '/', `main.exe`)) : path.normalize(path.join(outPath, '/', `main_${platform}`)); setEnv = ''; - if (os.type() === "Windows_NT") { + if (os.type() === 'Windows_NT') { setEnv += `SET CGO_ENABLED=0&&SET GOOS=${platform}&&SET GOARCH=amd64&&`; } else { setEnv += `CGO_ENABLED=0 GOOS=${platform} GOARCH=amd64`; diff --git a/trace_streamer/src/rpc/rpc_server.cpp b/trace_streamer/src/rpc/rpc_server.cpp index 396aa27c4..c999fd5de 100644 --- a/trace_streamer/src/rpc/rpc_server.cpp +++ b/trace_streamer/src/rpc/rpc_server.cpp @@ -179,7 +179,6 @@ bool RpcServer::ReadAndParseData(const std::string &filePath) break; } } - ts_->WaitForParserEnd(); inputFile.close(); return true; } -- Gitee From 8c5a487f3fd934ecd2e2e067f21df270674a74ef Mon Sep 17 00:00:00 2001 From: zhangyan Date: Thu, 21 Nov 2024 09:44:09 +0800 Subject: [PATCH 06/38] =?UTF-8?q?fix:=E4=BC=98=E5=8C=96ai=E8=AF=8A?= =?UTF-8?q?=E6=96=AD=E6=8F=90=E7=A4=BA=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhangyan --- .../trace/component/SpAiAnalysisPage.html.ts | 4 +- ide/src/trace/component/SpAiAnalysisPage.ts | 61 +++-- ide/src/webSocket/Constants.ts | 4 + ide/src/webSocket/WebSocketManager.ts | 218 +++++++++++++----- 4 files changed, 210 insertions(+), 77 deletions(-) diff --git a/ide/src/trace/component/SpAiAnalysisPage.html.ts b/ide/src/trace/component/SpAiAnalysisPage.html.ts index de484aaff..ef10bdd7c 100644 --- a/ide/src/trace/component/SpAiAnalysisPage.html.ts +++ b/ide/src/trace/component/SpAiAnalysisPage.html.ts @@ -135,11 +135,11 @@ export const SpAiAnalysisPageHtml = ` display: flex; justify-content: center; align-items: center; - width: 320px; + width: 90%; height: 50%; position: absolute; top: 23%; - left: 44%; + left: 45%; transform: translateX(-50%); background-color: sky-blue; } diff --git a/ide/src/trace/component/SpAiAnalysisPage.ts b/ide/src/trace/component/SpAiAnalysisPage.ts index ee8c48995..b7469e7e9 100644 --- a/ide/src/trace/component/SpAiAnalysisPage.ts +++ b/ide/src/trace/component/SpAiAnalysisPage.ts @@ -62,6 +62,7 @@ export class SpAiAnalysisPage extends BaseElement { private isNodata: boolean = true; private md: unknown; private isResultBack: boolean = true; + private timerId: unknown = undefined; static startTime: number = 0; static endTime: number = 0; activeTime: Element | undefined | null; @@ -187,15 +188,6 @@ export class SpAiAnalysisPage extends BaseElement { this.tipsContainer!.style.display = 'none'; this.tipsContent!.style.display = 'none'; this.downloadBtn!.style.display = 'none'; - // 没有登陆,弹窗提示,退出逻辑 - if (!WebSocketManager.getInstance()?.isReady()) { - this.tipsContent!.style.display = 'flex'; - let guideSrc = `https://${window.location.host.split(':')[0]}:${window.location.port - }/application/?action=help_27`; - let linkNodeTips = `未连接,请启动本地扩展程序再试![指导]`; - this.abnormalPageTips(linkNodeTips, '', 4000); - return; - } // 清空诊断报告的内容 this.reportContent = ''; // 隐藏诊断按钮 @@ -205,7 +197,7 @@ export class SpAiAnalysisPage extends BaseElement { this.initiateDiagnosis(); } else { // 首次诊断 - WebSocketManager.getInstance()!.registerMessageListener(TypeConstants.DIAGNOSIS_TYPE, this.webSocketCallBack); + WebSocketManager.getInstance()!.registerMessageListener(TypeConstants.DIAGNOSIS_TYPE, this.webSocketCallBack, this.eventCallBack); // 看缓存中有没有db,没有的话拿一个进行诊断并存缓存 let fileName = sessionStorage.getItem('fileName'); caches.match(`${fileName}.db`).then(async (res) => { @@ -510,6 +502,11 @@ export class SpAiAnalysisPage extends BaseElement { //控制页面异常场景的显示 abnormalPageTips(tipStr: string, imgSrc: string, setTimeoutTime: number): void { + // 清除延时器,防止弹窗重叠互相影响 + if (this.timerId) { + // @ts-ignore + clearTimeout(this.timerId); + } this.tipsContainer!.style.display = 'flex'; this.tipsContainer!.innerHTML = ''; if (imgSrc !== '') { @@ -531,7 +528,7 @@ export class SpAiAnalysisPage extends BaseElement { } if (setTimeoutTime) { setTimeout(() => { - this.tipsContainer!.style.display = 'none'; + this.timerId = this.tipsContainer!.style.display = 'none'; }, setTimeoutTime); } } @@ -562,7 +559,7 @@ export class SpAiAnalysisPage extends BaseElement { }).catch((error) => { this.appendMsg(dataList, i, suggestonDiv, timeList, error); }); - this.draftList!.appendChild(itemDiv!); + this.draftList!.insertBefore(itemDiv!, this.loadingItem!); itemDiv!.style.animation = 'opcityliner 3s'; } @@ -651,7 +648,7 @@ export class SpAiAnalysisPage extends BaseElement { this.isNodata = true; this.draftList!.innerHTML = ''; this.contentsTable!.style.display = 'none'; - let textStr = '服务异常'; + let textStr = '服务异常,请重新导trace!'; let imgsrc = 'img/no-report.png'; this.tipsContent!.style.display = 'none'; this.abnormalPageTips(textStr, imgsrc, 0); @@ -679,6 +676,15 @@ export class SpAiAnalysisPage extends BaseElement { } } + // eventCallBack + eventCallBack = async (result: string) => { + this.draftList!.innerHTML = ''; + this.tipsContent!.style.display = 'flex'; + // @ts-ignore + this.abnormalPageTips(this.getStatusesPrompt()[result].prompt, '', 4000); + this.draftBtn!.style.display = 'inline-block'; + } + // 发起诊断 initiateDiagnosis(): void { let requestBodyObj = { @@ -719,6 +725,35 @@ export class SpAiAnalysisPage extends BaseElement { return true; } + // 获取提示语 + getStatusesPrompt(): unknown { + let guideSrc = `https://${window.location.host.split(':')[0]}:${window.location.port + }/application/?action=help_27`; + return { + unconnected: { + prompt: `未连接,请启动本地扩展程序再试![指导]` + },// 重连 + connected: { + prompt: '扩展程序连接中,请稍后再试!' + }, // 中间 + logined: { + prompt: '扩展程序连接中,请稍后再试!' + }, // 中间 + loginFailedByLackSession: { + prompt: '当前所有会话都在使用中,请释放一些会话再试!' + }, // 重连 + upgrading: { + prompt: '扩展程序连接中,请稍后再试!' + }, // 中间 + upgradeSuccess: { + prompt: '扩展程序已完成升级,重启中,请稍后再试!' + }, // 重连 + upgradeFailed: { + prompt: '刷新页面触发升级,或卸载扩展程序重装!' + },// 重连 + } + } + initHtml(): string { return SpAiAnalysisPageHtml; } diff --git a/ide/src/webSocket/Constants.ts b/ide/src/webSocket/Constants.ts index f39f64e1b..0c8595626 100644 --- a/ide/src/webSocket/Constants.ts +++ b/ide/src/webSocket/Constants.ts @@ -17,8 +17,12 @@ export class Constants { static INTERVAL_TIME = 30000; static LOGIN_PARAM = { type: 0, cmd: 1 }; static LOGIN_CMD = 2;// cmd 2 有效 3无效 + static SESSION_EXCEED = 3; // session满了 static GET_CMD = 1; static UPDATE_CMD = 3; + static GET_VERSION_CMD = 1; + static UPDATE_SUCCESS_CMD = 2; // 升级成功 + static UPDATE_FAIL_CMD = 4; // 升级失败 } export class TypeConstants { diff --git a/ide/src/webSocket/WebSocketManager.ts b/ide/src/webSocket/WebSocketManager.ts index cb9689055..e049ba855 100644 --- a/ide/src/webSocket/WebSocketManager.ts +++ b/ide/src/webSocket/WebSocketManager.ts @@ -15,15 +15,32 @@ import { Utils, MessageParam } from './Util'; import { Constants, TypeConstants } from './Constants'; +// 状态枚举 +enum GetStatuses { + UNCONNECTED = 'unconnected', // 服务器未连接 + CONNECTED = 'connected', // 服务器已连接 + LOGINED = 'logined', // 已登录 + LOGINFAILEDBYLACKSESSION = 'loginFailedByLackSession', // session已满 + UPFRADING = 'upgrading', // 正在升级 + UPGRADESUCCESS = 'upgradeSuccess', // 升级成功 + UPGRADEFAILED = 'upgradeFailed', // 升级失败 + READY = 'ready' // 服务与扩展程序准备就绪 +} +const INTERMEDIATE_STATE = 'Intermediate state'; +const FAILED_STATE = 'Failed state'; + + export class WebSocketManager { static instance: WebSocketManager | null | undefined = null; - url: string = `ws://localhost:${Constants.NODE_PORT}`;//8080后期需要修改 + url: string = `ws://localhost:${Constants.NODE_PORT}`; private websocket: WebSocket | null | undefined = null; - private ready: boolean = false; - private distributeMap: Map = new Map(); + private distributeMap: Map = new Map(); private sessionId: number | null | undefined; private session: bigint | null | undefined; private heartbeatInterval: number | null | undefined; + private status: string = GetStatuses.UNCONNECTED; + private cacheInfo: Map = new Map(); + private reconnect: number = -1; constructor() { if (WebSocketManager.instance) { @@ -38,10 +55,11 @@ export class WebSocketManager { this.websocket = new WebSocket(this.url); this.websocket.binaryType = 'arraybuffer'; this.websocket.onopen = (): void => { + this.status = GetStatuses.CONNECTED; // 设置心跳定时器 this.sendHeartbeat(); - //检查版本 - this.getVersion(); + // 连接后登录 + this.login(); }; //接受webSocket的消息 @@ -59,9 +77,11 @@ export class WebSocketManager { }; this.websocket.onclose = (event): void => { - this.initLoginInfor(); + this.status = GetStatuses.UNCONNECTED; + this.finalStatus(); + //初始化标志位 + this.initLoginInfo(); this.clearHeartbeat(); - this.reconnect(event); }; } @@ -71,39 +91,61 @@ export class WebSocketManager { * 其他业务数据分发 */ onmessage(decode: MessageParam): void { - // 解码event 调decode - if (decode.type === TypeConstants.UPDATE_TYPE) {// 升级 - if (decode.cmd === Constants.GET_CMD) { - // 小于则升级 - let targetVersion = '1.0.1'; - let currentVersion = new TextDecoder().decode(decode.data); - let result = this.compareVersion(currentVersion, targetVersion); - if (result === -1) { - this.updateVersion(); - } else { - // 连接后登录 - this.login(); - } - } - } else if (decode.type === TypeConstants.LOGIN_TYPE) {// 登录 - if (decode.cmd === Constants.LOGIN_CMD) { - this.ready = true; - this.sessionId = decode.session_id; - this.session = decode.session; - } + if (decode.type === TypeConstants.LOGIN_TYPE) { // 先登录 + this.loginMessage(decode); + } else if (decode.type === TypeConstants.UPDATE_TYPE) {// 升级 + this.updateMessage(decode); } else {// type其他 - for (let [key, callback] of this.distributeMap.entries()) { - if (key === decode.type) { - callback(decode.cmd, decode.data); - } - }; + this.businessMessage(decode) + } + } + + // 登录 + loginMessage(decode: MessageParam): void { + if (decode.cmd === Constants.LOGIN_CMD) { + this.status = GetStatuses.LOGINED; + this.sessionId = decode.session_id; + this.session = decode.session; + //检查版本 + this.getVersion(); + } else if (decode.cmd === Constants.SESSION_EXCEED) { // session满了 + this.status = GetStatuses.LOGINFAILEDBYLACKSESSION; + this.finalStatus(); + } + } + + // 升级 + updateMessage(decode: MessageParam): void { + if (decode.cmd === Constants.GET_VERSION_CMD) { + // 小于则升级 + let targetVersion = '1.0.1'; + let currentVersion = new TextDecoder().decode(decode.data); + let result = this.compareVersion(currentVersion, targetVersion); + if (result === -1) { + this.status = GetStatuses.UPFRADING; + this.updateVersion(); + return; + } + this.status = GetStatuses.READY; + this.finalStatus(); + } else if (decode.cmd === Constants.UPDATE_SUCCESS_CMD) { // 升级成功 + this.status = GetStatuses.UPGRADESUCCESS; + this.finalStatus(); + } else if (decode.cmd === Constants.UPDATE_FAIL_CMD) { // 升级失败 + this.status = GetStatuses.UPGRADEFAILED; + this.finalStatus(); } } + // 业务 + businessMessage(decode: MessageParam): void { + this.distributeMap.get(decode.type!)?.messageCallback(decode.cmd, decode.data); + } + // get版本 getVersion(): void { // 获取扩展程序版本 - this.send(TypeConstants.UPDATE_TYPE, Constants.GET_CMD); + this.send(TypeConstants.UPDATE_TYPE, Constants.GET_VERSION_CMD); } //check版本 @@ -140,6 +182,8 @@ export class WebSocketManager { }).then((arrayBuffer) => { this.send(TypeConstants.UPDATE_TYPE, Constants.UPDATE_CMD, new Uint8Array(arrayBuffer)); }).catch((error) => { + this.status = GetStatuses.UPGRADEFAILED; + this.finalStatus(); console.error(error); }); } @@ -157,19 +201,14 @@ export class WebSocketManager { return WebSocketManager.instance } - // WebSocket是否登录成功 - isReady(): boolean { - return this.ready; - } - /** * 消息监听器 * listener是不同模块传来接收数据的函数 * 模块调用 */ - registerMessageListener(type: number, callback: Function): void { + registerMessageListener(type: number, callback: Function, eventCallBack: Function): void { if (!this.distributeMap.has(type)) { - this.distributeMap.set(type, callback); + this.distributeMap.set(type, { 'messageCallback': callback, 'eventCallBack': eventCallBack }); } } @@ -178,11 +217,16 @@ export class WebSocketManager { * 模块调 */ sendMessage(type: number, cmd?: number, data?: Uint8Array): void { - // 检查WebSocket是否登录成功 - if (!this.ready) {// 改判断条件 ready - return; + // 初始化重连标志位 + this.reconnect = -1; + // 检查状态 + if (this.status !== GetStatuses.READY) { + // 缓存数据 + this.cache(type, cmd, data); + this.checkStatus(type); + } else { + this.send(type, cmd, data); } - this.send(type, cmd, data); } send(type: number, cmd?: number, data?: Uint8Array): void { @@ -198,25 +242,12 @@ export class WebSocketManager { this.websocket!.send(encode!); } - /** - * 传递数据信息至webSocket - * 模块调 - */ - reconnect(event: unknown): void { - //@ts-ignore - if (event.wasClean) {// 正常关闭 - return; - } - // 未连接成功打开定时器 - setTimeout(() => { - this.connectWebSocket(); - }, Constants.INTERVAL_TIME); - } - // 定时检查心跳 sendHeartbeat(): void { this.heartbeatInterval = window.setInterval(() => { - this.sendMessage(TypeConstants.HEARTBEAT_TYPE); + if (this.status === GetStatuses.READY) { + this.send(TypeConstants.HEARTBEAT_TYPE, undefined, undefined); + } }, Constants.INTERVAL_TIME); } @@ -224,8 +255,7 @@ export class WebSocketManager { * 重连时初始化登录信息 * 在异常关闭时调用 */ - initLoginInfor(): void { - this.ready = false; + initLoginInfo(): void { this.sessionId = null; this.session = null; } @@ -237,4 +267,68 @@ export class WebSocketManager { this.heartbeatInterval = null; } } + + // 缓存数据 + cache(type: number, cmd?: number, data?: Uint8Array): void { + if (!this.cacheInfo.has(type)) { + this.cacheInfo.set(type, { type, cmd, data }); + } else { + let obj = this.cacheInfo.get(type); + // @ts-ignore + obj.cmd = cmd; + //@ts-ignore + obj.data = data + } + } + + // 检查状态 中间状态,最终失败状态,最终成功状态 + checkStatus(reconnect: number): void { + // @ts-ignore + let statuses = this.getStatusesPrompt()[this.status]; + if (statuses.type === INTERMEDIATE_STATE) { + this.distributeMap.get(reconnect)!.eventCallBack(this.status); + } else if (statuses.type === FAILED_STATE) { + this.reconnect = reconnect; + this.connectWebSocket() + } + } + + // 重连后确认最终状态 + finalStatus(): void { + if (this.reconnect !== -1) { + if (this.status === GetStatuses.READY) { + // @ts-ignore + this.sendMessage(this.reconnect, this.cacheInfo.get(this.reconnect)!.cmd, this.cacheInfo.get(this.reconnect)!.data); + return; + } + this.distributeMap.get(this.reconnect)!.eventCallBack(this.status); + } + this.reconnect = -1; + } + + getStatusesPrompt(): unknown { + return { + unconnected: { + type: FAILED_STATE + },// 重连 + connected: { + type: INTERMEDIATE_STATE + }, // 中间 + logined: { + type: INTERMEDIATE_STATE + }, // 中间 + loginFailedByLackSession: { + type: FAILED_STATE + }, // 重连 + upgrading: { + type: INTERMEDIATE_STATE + }, // 中间 + upgradeSuccess: { + type: FAILED_STATE, + }, // 重连 + upgradeFailed: { + type: FAILED_STATE, + },// 重连 + } + } } -- Gitee From 7a98d2c13adaebb3b776b659029cf7a9a810c672 Mon Sep 17 00:00:00 2001 From: wangyujie Date: Thu, 21 Nov 2024 09:56:21 +0800 Subject: [PATCH 07/38] =?UTF-8?q?fix:=E5=B9=BF=E5=91=8A=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: wangyujie --- ide/src/trace/component/SpAdvertisement.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ide/src/trace/component/SpAdvertisement.ts b/ide/src/trace/component/SpAdvertisement.ts index acf2ad462..7b041238d 100644 --- a/ide/src/trace/component/SpAdvertisement.ts +++ b/ide/src/trace/component/SpAdvertisement.ts @@ -39,9 +39,11 @@ export class SpAdvertisement extends BaseElement { this.message = resp.data.data; localStorage.setItem('message', this.message); let parts = this.message.split(';'); - let linkInfo = parts[2].match(/链接:([^\s]+)/)![1] || ''; - let link = `${parts[1]}`; - let finalString = `${parts[0]}
${link}`; + let registrationLinkInfo = (parts[2].match(/报名链接:([^\s]+)/) || [])[1] || ''; + let onlineMeetingLinkInfo = (parts[3].match(/线上会议链接:([^\s]+)/) || [])[1] || ''; + let registrationLink = `报名链接`; + let onlineMeetingLink = `线上会议链接`; + let finalString = `${parts[0]}
${parts[1]}
${registrationLink}   ${onlineMeetingLink}
${parts[4]}`; this.noticeEl!.innerHTML = `

${finalString}

`; if (publish) { if (resp.data.data !== publish) { -- Gitee From d35778b827869edb18eb95d74a97bb2859476569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=86=E5=B7=9D?= Date: Thu, 21 Nov 2024 09:59:42 +0800 Subject: [PATCH 08/38] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DRpcServer::ReadA?= =?UTF-8?q?ndParseData=E5=87=BD=E6=95=B0=E6=9C=AA=E5=A4=84=E7=90=86ParseTr?= =?UTF-8?q?aceDataSegment=E7=9A=84=E8=BF=94=E5=9B=9E=E5=80=BC=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E9=99=B7=E5=85=A5=E5=BE=AA=E7=8E=AF=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 集川 --- trace_streamer/src/rpc/rpc_server.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/trace_streamer/src/rpc/rpc_server.cpp b/trace_streamer/src/rpc/rpc_server.cpp index c999fd5de..524a9b1bb 100644 --- a/trace_streamer/src/rpc/rpc_server.cpp +++ b/trace_streamer/src/rpc/rpc_server.cpp @@ -174,7 +174,9 @@ bool RpcServer::ReadAndParseData(const std::string &filePath) std::unique_ptr buf = std::make_unique(G_CHUNK_SIZE); inputFile.read(reinterpret_cast(buf.get()), G_CHUNK_SIZE); auto readSize = inputFile.gcount(); - ts_->ParseTraceDataSegment(std::move(buf), readSize, false, inputFile.eof()); + if (!ts_->ParseTraceDataSegment(std::move(buf), readSize, false, inputFile.eof())) { + return false; + } if (inputFile.eof()) { break; } -- Gitee From 86496ba1f278009b62c3612ae3e5c5dfad5682f2 Mon Sep 17 00:00:00 2001 From: liufei Date: Thu, 21 Nov 2024 14:04:09 +0800 Subject: [PATCH 09/38] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9nativememorytab?= =?UTF-8?q?=E9=A1=B5=E6=95=B0=E6=8D=AE=E9=87=8F=E8=B6=8550000=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E9=97=AE=E9=A2=98=EF=BC=8C=E5=8E=BB=E9=99=A4trace?= =?UTF-8?q?=E7=82=B9=E8=AF=A6=E6=83=85=E5=BD=95=E5=85=A5=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liufei --- ide/src/doc/funDetail.json | 12 ++++++------ .../trace/sheet/native-memory/TabPaneNMemory.ts | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/ide/src/doc/funDetail.json b/ide/src/doc/funDetail.json index 9f8b36213..9bca062d7 100644 --- a/ide/src/doc/funDetail.json +++ b/ide/src/doc/funDetail.json @@ -177,37 +177,37 @@ }, { "slice": "OnVsyncEvent now", - "CN": "收到Vsync信号,渲染流程开始(框架trace,)不用验证", + "CN": "收到Vsync信号,渲染流程开始", "EN": "this is an english translate for CN", "flag": "0" }, { "slice": "UITaskScheduler::FlushTask", - "CN": "刷新UI界面,包括布局、渲染和动画等(框架trace,不用验证)", + "CN": "刷新UI界面,包括布局、渲染和动画等", "EN": "this is an english translate for CN", "flag": "0" }, { "slice": "FlushMessages", - "CN": "发送消息通知图形侧进行渲染(框架trace,不用验证)", + "CN": "发送消息通知图形侧进行渲染", "EN": "this is an english translate for CN", "flag": "0" }, { "slice": "FlushRenderTask", - "CN": "总渲染任务执行(框架trace,不用验证)", + "CN": "总渲染任务执行", "EN": "this is an english translate for CN", "flag": "0" }, { "slice": "Layout", - "CN": "节点布局(框架trace,不用验证)", + "CN": "节点布局", "EN": "this is an english translate for CN", "flag": "0" }, { "slice": "FrameNode::RenderTask", - "CN": "单个渲染任务执行(框架trace,不用验证)", + "CN": "单个渲染任务执行", "EN": "this is an english translate for CN", "flag": "0" }, diff --git a/ide/src/trace/component/trace/sheet/native-memory/TabPaneNMemory.ts b/ide/src/trace/component/trace/sheet/native-memory/TabPaneNMemory.ts index bf838087e..0752fed98 100644 --- a/ide/src/trace/component/trace/sheet/native-memory/TabPaneNMemory.ts +++ b/ide/src/trace/component/trace/sheet/native-memory/TabPaneNMemory.ts @@ -135,7 +135,20 @@ export class TabPaneNMemory extends BaseElement { this.tblData!.recycleDataSource = []; this.setNmMemoryLoading(false); if (results.length > 0) { - results.forEach((item) => { + let isTwoArray: boolean = results.some(item => { + return Array.isArray(item); + }) + let dataList: unknown = []; + if (isTwoArray) { + results.forEach(v => { + // @ts-ignore + dataList = dataList.concat(v); + }) + } else { + dataList = results; + } + // @ts-ignore + dataList.forEach((item) => { //@ts-ignore let tmpNumber = item.addr.split('x'); //@ts-ignore -- Gitee From 0c380e5c411084a7c4af7808b0aaa78f0105988b Mon Sep 17 00:00:00 2001 From: danghongquan Date: Thu, 21 Nov 2024 17:07:42 +0800 Subject: [PATCH 10/38] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8DTab=E9=A1=B5?= =?UTF-8?q?=E7=82=B9=E5=87=BB=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: danghongquan --- ide/src/trace/component/trace/sheet/process/TabPaneSlices.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ide/src/trace/component/trace/sheet/process/TabPaneSlices.ts b/ide/src/trace/component/trace/sheet/process/TabPaneSlices.ts index 6edab339c..21adc2c47 100644 --- a/ide/src/trace/component/trace/sheet/process/TabPaneSlices.ts +++ b/ide/src/trace/component/trace/sheet/process/TabPaneSlices.ts @@ -239,6 +239,10 @@ export class TabPaneSlices extends BaseElement { }); } async orgnazitionData(data: Object): Promise { + // @ts-ignore + if (data!.tabTitle === 'Summary') { + FuncStruct.funcSelect = true; + } let spApplication = document.querySelector('body > sp-application'); let spSystemTrace = spApplication?.shadowRoot?.querySelector( 'div > div.content > sp-system-trace' -- Gitee From 9b286aac3624df5d0d8cff364610fe15ab0bdf76 Mon Sep 17 00:00:00 2001 From: danghongquan Date: Thu, 21 Nov 2024 20:03:10 +0800 Subject: [PATCH 11/38] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E5=A4=B1=E8=B4=A5=E5=90=8E=E4=B8=8D=E6=8F=90?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: danghongquan --- ide/src/trace/component/SpRecordTrace.ts | 26 +++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/ide/src/trace/component/SpRecordTrace.ts b/ide/src/trace/component/SpRecordTrace.ts index 158bcd66d..01fdb6cf9 100644 --- a/ide/src/trace/component/SpRecordTrace.ts +++ b/ide/src/trace/component/SpRecordTrace.ts @@ -196,10 +196,10 @@ export class SpRecordTrace extends BaseElement { let option = document.createElement('option'); option.className = 'select'; if (typeof dev.serialNumber === 'string') { - optionNum++; - option.value = dev.serialNumber; - option.textContent = dev!.serialNumber ? dev!.serialNumber!.toString() : 'hdc Device'; - this.deviceSelect!.appendChild(option); + optionNum++; + option.value = dev.serialNumber; + option.textContent = dev!.serialNumber ? dev!.serialNumber!.toString() : 'hdc Device'; + this.deviceSelect!.appendChild(option); if (dev.serialNumber === sn) { option.selected = true; this.recordButton!.hidden = false; @@ -501,7 +501,19 @@ export class SpRecordTrace extends BaseElement { // @ts-ignore HdcDeviceManager.findDevice().then((usbDevices): void => { log(usbDevices); - this.refreshDeviceList(usbDevices.serialNumber); + HdcDeviceManager.connect(usbDevices.serialNumber).then((res) => { + if (res) { + this.refreshDeviceList(usbDevices.serialNumber); + } else { + this.recordButton!.hidden = true; + this.disconnectButton!.hidden = true; + this.devicePrompt!.innerText = 'Device not connected'; + this.hintEl!.innerHTML = DEVICE_NOT_CONNECT; + if (!this.showHint) { + this.showHint = true; + } + } + }) }); } }; @@ -794,7 +806,7 @@ export class SpRecordTrace extends BaseElement { this.MenuItemEbpfHtml = th; } this.menuGroup!.appendChild(th); - if (item.title === 'Ark Ts') { + if (item.title === 'Ark Ts') { this.menuGroup!.removeChild(th); } }); @@ -901,7 +913,7 @@ export class SpRecordTrace extends BaseElement { this.buildMenuItem('Ark Ts', 'file-config', this.spArkTs!), this.buildMenuItem('FFRT', 'file-config', this.spFFRTConfig!), this.buildMenuItem('Hilog', 'realIntentionBulb', this.spHiLog!), - this.buildMenuItem('Xpower', 'externaltools', this.spXPower!), + this.buildMenuItem('Xpower', 'externaltools', this.spXPower!), ]; } -- Gitee From d562fc894f72f5ebbf8d883dc213f0eeb9f8000a Mon Sep 17 00:00:00 2001 From: wangyujie Date: Fri, 22 Nov 2024 10:11:03 +0800 Subject: [PATCH 12/38] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8Dtab=E9=A1=B5?= =?UTF-8?q?=E5=B7=A6=E5=8F=B3=E6=8B=96=E5=8A=A8=EF=BC=8C=E9=BC=A0=E6=A0=87?= =?UTF-8?q?=E4=B8=8D=E5=8F=97=E6=8E=A7=E5=88=B6=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: wangyujie --- ide/src/base-ui/slicer/lit-slicer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ide/src/base-ui/slicer/lit-slicer.ts b/ide/src/base-ui/slicer/lit-slicer.ts index e9a1bc387..86f9061f0 100644 --- a/ide/src/base-ui/slicer/lit-slicer.ts +++ b/ide/src/base-ui/slicer/lit-slicer.ts @@ -153,6 +153,8 @@ export class LitSlicerTrack extends HTMLElement { ) { previousElementSibling!.style.width = preWidth + e1.pageX - preX + 'px'; nextElementSibling!.style.width = nextWidth + preX - e1.pageX + 'px'; + } else { + this.draging = false; } } }; -- Gitee From af749aba7abe9dea7ecdbdfab85086bcadfeb823 Mon Sep 17 00:00:00 2001 From: liufei Date: Fri, 22 Nov 2024 17:13:30 +0800 Subject: [PATCH 13/38] =?UTF-8?q?fix:=E5=A4=9A=E7=BD=91=E9=A1=B5=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E8=AF=8A=E6=96=AD=E5=90=8C=E4=B8=80trace=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E5=A4=84=E7=90=86=EF=BC=8Cnative=5Fmamoey=E2=80=94?= =?UTF-8?q?=E2=80=94call=20info=E9=A1=B5=E7=AD=9B=E9=80=89=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E9=97=AE=E9=A2=98=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liufei --- ide/src/trace/SpApplication.ts | 11 ++--- ide/src/trace/component/SpAiAnalysisPage.ts | 48 ++++++++++--------- .../ProcedureLogicWorkerCommon.ts | 2 +- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/ide/src/trace/SpApplication.ts b/ide/src/trace/SpApplication.ts index 62e1e7d2c..a793515bc 100644 --- a/ide/src/trace/SpApplication.ts +++ b/ide/src/trace/SpApplication.ts @@ -2534,12 +2534,11 @@ export class SpApplication extends BaseElement { clearInterval(timer); }, 4000); // 存入缓存 - await caches.open(`${fileName}`).then(async (cache) => { - let headers = new Headers(); - headers.append('Content-type', 'application/octet-stream'); - headers.append('Content-Transfer-Encoding', 'binary'); - return cache.put(`${fileName}`, new Response(reqBufferDB, { status: 200 })); - }); + const blob = new Blob([reqBufferDB]); + const response = new Response(blob) + caches.open('DB-file').then(cache => { + return cache.put(`/${fileName}`, response); + }) }, 'download-db' ); diff --git a/ide/src/trace/component/SpAiAnalysisPage.ts b/ide/src/trace/component/SpAiAnalysisPage.ts index ee8c48995..2d8fc5041 100644 --- a/ide/src/trace/component/SpAiAnalysisPage.ts +++ b/ide/src/trace/component/SpAiAnalysisPage.ts @@ -208,17 +208,28 @@ export class SpAiAnalysisPage extends BaseElement { WebSocketManager.getInstance()!.registerMessageListener(TypeConstants.DIAGNOSIS_TYPE, this.webSocketCallBack); // 看缓存中有没有db,没有的话拿一个进行诊断并存缓存 let fileName = sessionStorage.getItem('fileName'); - caches.match(`${fileName}.db`).then(async (res) => { - if (!res) { - this.cacheDb(fileName); + await caches.match(`/${fileName}.db`).then(response => { + if (response) { + response.blob().then(blob => { + const reader = new FileReader(); + reader.readAsArrayBuffer(blob); + reader.onloadend = () => { + const dbBuffer = reader.result; + // @ts-ignore + const reqBufferDB = new Uint8Array(dbBuffer); + // 使用uint8Array + WebSocketManager.getInstance()!.sendMessage( + TypeConstants.DIAGNOSIS_TYPE, + TypeConstants.SENDDB_CMD, + reqBufferDB + ); + }; + }); } else { - WebSocketManager.getInstance()!.sendMessage( - TypeConstants.DIAGNOSIS_TYPE, - TypeConstants.SENDDB_CMD, - new TextEncoder().encode(await res!.text()) - ); + // 如果缓存中没有,从网络获取并存储 + this.cacheDb(fileName); } - }); + }) }; // 点击一键诊断时先挂载loading this.loadingItem = this.loading('style="position:absolute;top:45%;left:45%;z-index:999"'); @@ -553,7 +564,7 @@ export class SpAiAnalysisPage extends BaseElement { timeList: Array ): void { SpStatisticsHttpUtil.askAi({ - token: this.token, + token: this.token, // @ts-ignore question: dataList[i].description + ',请问该怎么优化?', collection: '' @@ -613,18 +624,11 @@ export class SpAiAnalysisPage extends BaseElement { (reqBufferDB: Uint8Array) => { WebSocketManager.getInstance()!.sendMessage(TypeConstants.DIAGNOSIS_TYPE, TypeConstants.SENDDB_CMD, reqBufferDB); // 存入缓存 - caches.open(`${fileName}.db`).then((cache) => { - let headers = new Headers(); - headers.append('Content-Type', 'application/octet-stream'); - headers.append('Content-Transfer-Encoding', 'binary'); - return cache - .put( - `${fileName}.db`, - new Response(reqBufferDB, { - status: 200, - }) - ); - }); + const blob = new Blob([reqBufferDB]); + const response = new Response(blob) + caches.open('DB-file').then(cache => { + return cache.put(`/${fileName}.db`, response); + }) }, 'download-db' ); diff --git a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerCommon.ts b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerCommon.ts index e610e76e9..46f2e0cfe 100644 --- a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerCommon.ts +++ b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerCommon.ts @@ -264,7 +264,7 @@ class MerageBeanDataSplit { */ //@ts-ignore let parentNode = item.parentNode; - while (parentNode !== undefined && !(parentNode.isStore === 0 && parentNode.searchShow)) { + while (parentNode !== undefined && !(parentNode.isStore === 0 && parentNode.searchShow) && parentNode.addr) { parentNode = parentNode.parentNode; } if (parentNode) { -- Gitee From 4e027e29912093cf2ae665e65dbfaa7190916718 Mon Sep 17 00:00:00 2001 From: danghongquan Date: Sat, 23 Nov 2024 10:26:49 +0800 Subject: [PATCH 14/38] =?UTF-8?q?fix:=E6=89=A9=E5=B1=95=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=B7=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: danghongquan --- ide/src/webSocket/WebSocketManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ide/src/webSocket/WebSocketManager.ts b/ide/src/webSocket/WebSocketManager.ts index cb9689055..eab76dd8d 100644 --- a/ide/src/webSocket/WebSocketManager.ts +++ b/ide/src/webSocket/WebSocketManager.ts @@ -75,7 +75,7 @@ export class WebSocketManager { if (decode.type === TypeConstants.UPDATE_TYPE) {// 升级 if (decode.cmd === Constants.GET_CMD) { // 小于则升级 - let targetVersion = '1.0.1'; + let targetVersion = '1.0.2'; let currentVersion = new TextDecoder().decode(decode.data); let result = this.compareVersion(currentVersion, targetVersion); if (result === -1) { -- Gitee From b92857f2d8351bbc2712d777c4d70424db487c4a Mon Sep 17 00:00:00 2001 From: danghongquan Date: Sat, 23 Nov 2024 11:55:00 +0800 Subject: [PATCH 15/38] =?UTF-8?q?fix:=E6=9B=B4=E6=96=B0=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=9A=84=E7=89=88=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: danghongquan --- ide/src/webSocket/WebSocketManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ide/src/webSocket/WebSocketManager.ts b/ide/src/webSocket/WebSocketManager.ts index e049ba855..364779920 100644 --- a/ide/src/webSocket/WebSocketManager.ts +++ b/ide/src/webSocket/WebSocketManager.ts @@ -118,7 +118,7 @@ export class WebSocketManager { updateMessage(decode: MessageParam): void { if (decode.cmd === Constants.GET_VERSION_CMD) { // 小于则升级 - let targetVersion = '1.0.1'; + let targetVersion = '1.0.2'; let currentVersion = new TextDecoder().decode(decode.data); let result = this.compareVersion(currentVersion, targetVersion); if (result === -1) { -- Gitee From 1e9e12fc1add0b35aea39e66bc967c5bab9b8638 Mon Sep 17 00:00:00 2001 From: danghongquan Date: Sat, 23 Nov 2024 14:41:03 +0800 Subject: [PATCH 16/38] =?UTF-8?q?fix:=E6=9B=B4=E6=94=B9=E5=B8=AE=E5=8A=A9?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=89=A9=E5=B1=95=E6=9C=8D=E5=8A=A1=E7=9A=84?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: danghongquan --- ide/src/doc/quickstart_extensions.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ide/src/doc/quickstart_extensions.html b/ide/src/doc/quickstart_extensions.html index 647ab76b2..c9e47bf43 100644 --- a/ide/src/doc/quickstart_extensions.html +++ b/ide/src/doc/quickstart_extensions.html @@ -811,7 +811,7 @@ 在hi-smart-perf-host-extend目录下,找到stop.bat文件,右键选择以管理员身份运行,即可关闭扩展服务。

- 备注:当前扩展服务版本为1.0.1,如果本地存在以其他方式安装的非正式版本,请手动关闭扩展服务,并重新按照该指导安装 + 备注:当前扩展服务版本为1.0.2,如果本地存在以其他方式安装的非正式版本,请手动关闭扩展服务,并重新按照该指导安装

-- Gitee From 08b9f9aa03c6b9024ba7d8b1d59a8efbaad9f995 Mon Sep 17 00:00:00 2001 From: "wupoli3@huawei.com" Date: Mon, 25 Nov 2024 10:45:19 +0800 Subject: [PATCH 17/38] =?UTF-8?q?fix:=20F=E9=94=AE=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E5=BD=93duration=E4=B8=BA0=E6=97=B6=E4=B8=94=E4=B8=8D?= =?UTF-8?q?=E5=9C=A8=E5=8F=AF=E8=A7=86=E5=8C=BA=E5=9F=9F=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=97=B6=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: wupoli3@huawei.com --- .../component/trace/timer-shaft/RangeRuler.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/ide/src/trace/component/trace/timer-shaft/RangeRuler.ts b/ide/src/trace/component/trace/timer-shaft/RangeRuler.ts index 841a2d440..e1d9f65f5 100644 --- a/ide/src/trace/component/trace/timer-shaft/RangeRuler.ts +++ b/ide/src/trace/component/trace/timer-shaft/RangeRuler.ts @@ -633,18 +633,15 @@ export class RangeRuler extends Graph { } if (startTime === endTime) { let midNs = (this.range.endNS - this.range.startNS) / 2; - if (startTime > midNs && startTime - midNs < this.range.totalNS - this.range.endNS) { - this.range.startNS = this.range.startNS + startTime - midNs; - this.range.endNS = this.range.endNS + startTime - midNs; - } else if (startTime < midNs && midNs - startTime < this.range.startNS) { - this.range.startNS = this.range.startNS - midNs + startTime; - this.range.endNS = this.range.endNS - midNs + startTime; - } else if (startTime > midNs && startTime - midNs > this.range.totalNS - this.range.endNS) { - this.range.startNS = 2 * startTime - this.range.totalNS; - this.range.endNS = this.range.totalNS; - } else if (startTime < midNs && midNs - startTime > this.range.startNS) { + if (startTime === midNs) { + return; + } + if(startTime * 2 < this.range.totalNS) { this.range.startNS = 0; - this.range.endNS = 2 * startTime; + this.range.endNS = startTime * 2; + } else { + this.range.startNS = (startTime * 2) - this.range.totalNS; + this.range.endNS = this.range.totalNS; } } else { let startX = midX - 150; -- Gitee From cdcef5940d2eec429a0d9c1e0e283eea7ad0563e Mon Sep 17 00:00:00 2001 From: JustinTY Date: Mon, 25 Nov 2024 14:22:31 +0800 Subject: [PATCH 18/38] =?UTF-8?q?feat=20SmartPerf=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E6=A0=88=E5=88=86=E6=9E=90=E8=83=BD=E5=8A=9B?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA-=20=E7=81=AB=E7=84=B0=E5=9B=BE=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=8F=AA=E5=B1=95=E7=A4=BA=E5=86=85=E6=A0=B8=E6=A0=88?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../database/logic-worker/ProcedureLogicWorkerPerf.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts index e3279c731..b05a55870 100644 --- a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts +++ b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts @@ -586,10 +586,12 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { //@ts-ignore symbolName = perfCallChains[topIndex].name; } - let perfRootNode = this.currentTreeMapData[symbolName + perfSample.pid]; + // 只展示内核栈合并进程栈 + const usePidAsKey = this.isOnlyKernel ? '': perfSample.pid; + let perfRootNode = this.currentTreeMapData[symbolName + usePidAsKey]; if (perfRootNode === undefined) { perfRootNode = new PerfCallChainMerageData(); - this.currentTreeMapData[symbolName + perfSample.pid] = perfRootNode; + this.currentTreeMapData[symbolName + usePidAsKey] = perfRootNode; this.currentTreeList.push(perfRootNode); } PerfCallChainMerageData.merageCallChainSample(perfRootNode, perfCallChains[topIndex], perfSample, false); @@ -602,6 +604,11 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { this.allProcess = Object.values(rootMerageMap); } private mergeNodeData(totalEventCount: number, totalSamplesCount: number): MergeMap { + // 只展示内核栈不添加进程这一级的结构 + if (this.isOnlyKernel){ + return this.currentTreeMapData; + } + // 添加进程级结构 let rootMerageMap: MergeMap = {}; // @ts-ignore Object.values(this.currentTreeMapData).forEach((merageData: PerfCallChainMerageData): void => { -- Gitee From ff7f2c809edf431feebd9f633b5dbe75599b69f9 Mon Sep 17 00:00:00 2001 From: "wupoli3@huawei.com" Date: Tue, 26 Nov 2024 19:08:32 +0800 Subject: [PATCH 19/38] =?UTF-8?q?fix:=20perf=20output=20data=20chart?= =?UTF-8?q?=E6=B3=B3=E9=81=93=E6=8A=A5=E9=94=99=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: wupoli3@huawei.com --- ide/src/trace/component/chart/SpPerfOutputDataChart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ide/src/trace/component/chart/SpPerfOutputDataChart.ts b/ide/src/trace/component/chart/SpPerfOutputDataChart.ts index 5a3f79c20..5c2330c68 100644 --- a/ide/src/trace/component/chart/SpPerfOutputDataChart.ts +++ b/ide/src/trace/component/chart/SpPerfOutputDataChart.ts @@ -44,7 +44,7 @@ export class SpPerfOutputDataChart { this.dur = 3000000000; } // @ts-ignore - this.perfOutputArr = perfOutputData[0].name.split(':')[2].split(','); + this.perfOutputArr = perfOutputData[0].name.includes(':') ? perfOutputData[0].name.split(':')[2].includes(',') ? perfOutputData[0].name.split(':')[2].split(',') : [] : []; // @ts-ignore let endTime: number = perfOutputData[0].ts; this.startTime = endTime - window.recordStartNS - this.dur!; -- Gitee From 4a3cd5c76aa52b0febcef62983f46b0ec2528d2e Mon Sep 17 00:00:00 2001 From: danghongquan Date: Wed, 27 Nov 2024 15:50:14 +0800 Subject: [PATCH 20/38] =?UTF-8?q?fix:=E8=AE=BE=E5=A4=87=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E7=89=88=E6=9C=AC=E5=8F=B7=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: danghongquan --- ide/src/trace/component/SpRecordConfigModel.ts | 4 ++-- ide/src/trace/component/SpRecordTrace.ts | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ide/src/trace/component/SpRecordConfigModel.ts b/ide/src/trace/component/SpRecordConfigModel.ts index 01cb2789b..3c9703764 100644 --- a/ide/src/trace/component/SpRecordConfigModel.ts +++ b/ide/src/trace/component/SpRecordConfigModel.ts @@ -512,7 +512,7 @@ export function createNativePluginConfig( if (spAllocations!.appProcess !== '' && spAllocations!.startSamp) { let nativeConfig = initNativePluginConfig(spAllocations, selectVersion); let maxProcessSize = 4; - if (selectVersion !== undefined && selectVersion !== '3.2') { + if (selectVersion !== undefined && selectVersion !== '3.2' && selectVersion !=='unknown') { nativeConfig.callframeCompress = true; nativeConfig.recordAccurately = spAllocations!.record_accurately; nativeConfig.offlineSymbolization = spAllocations!.offline_symbolization; @@ -552,7 +552,7 @@ function initNativePluginConfig(spAllocations: SpAllocations, selectVersion: str let appProcess = spAllocations!.appProcess; let processName = ''; let processId = ''; - if (spAllocations!.startup_mode && selectVersion !== '3.2') { + if (spAllocations!.startup_mode && selectVersion !== '3.2'&& selectVersion !=='unknown') { processName = appProcess; } else { if (appProcess.indexOf('(') !== -1) { diff --git a/ide/src/trace/component/SpRecordTrace.ts b/ide/src/trace/component/SpRecordTrace.ts index 01fdb6cf9..e428c237f 100644 --- a/ide/src/trace/component/SpRecordTrace.ts +++ b/ide/src/trace/component/SpRecordTrace.ts @@ -81,7 +81,7 @@ export class SpRecordTrace extends BaseElement { public static selectVersion: string | null; public static isVscode = false; public static cancelRecord = false; - static supportVersions = ['3.2', '4.0+', '5.0+']; + static supportVersions = ['unknown','3.2', '4.0+', '5.0+']; public deviceSelect: HTMLSelectElement | undefined; public deviceVersion: HTMLSelectElement | undefined; private _menuItems: Array | undefined; @@ -589,6 +589,7 @@ export class SpRecordTrace extends BaseElement { disconnectButtonClickEvent = (): void => { // --------------我修改的 + this.setDeviceVersionSelect('unknown') let index = this.deviceSelect!.selectedIndex; if (index !== -1 && this.deviceSelect!.options.length > 0) { for (let i = 0; i < this.deviceSelect!.options.length; i++) { @@ -660,7 +661,7 @@ export class SpRecordTrace extends BaseElement { private nativeMemoryHideBySelectVersion(): void { let divConfigs = this.spAllocations?.shadowRoot?.querySelectorAll('.version-controller'); if (divConfigs) { - if (SpRecordTrace.selectVersion !== '3.2') { + if (SpRecordTrace.selectVersion !== '3.2' && SpRecordTrace.selectVersion !=='unknown') { for (let divConfig of divConfigs) { divConfig!.style.zIndex = '1'; } @@ -681,11 +682,11 @@ export class SpRecordTrace extends BaseElement { SpRecordTrace.supportVersions.forEach((supportVersion) => { let option = document.createElement('option'); option.className = 'select'; - option.selected = supportVersion === '4.0+'; - option.textContent = `OpenHarmony-${supportVersion}`; + option.selected = supportVersion === 'unknown'; + option.textContent = supportVersion === 'unknown'? 'unknown' : `OpenHarmony-${supportVersion}`; option.setAttribute('device-version', supportVersion); this.deviceVersion!.append(option); - SpRecordTrace.selectVersion = '4.0+'; + SpRecordTrace.selectVersion = 'unknown'; this.nativeMemoryHideBySelectVersion(); }); } -- Gitee From eb60a0bd0642ff86c5674a5eb9b281bfec10dd27 Mon Sep 17 00:00:00 2001 From: wangyujie Date: Thu, 28 Nov 2024 10:22:24 +0800 Subject: [PATCH 21/38] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E6=A1=86=E9=80=89c?= =?UTF-8?q?lock=E3=80=81hangs=E3=80=81xpower=EF=BC=8Cperf=20profile?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: wangyujie --- .../trace/component/trace/sheet/hiperf/TabPerfProfile.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPerfProfile.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPerfProfile.ts index 91a16eacd..d24f40db0 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPerfProfile.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPerfProfile.ts @@ -111,8 +111,13 @@ export class TabpanePerfProfile extends BaseElement { this.perfProfilerFilter!.initializeFilterTree(true, true, true); this.perfProfilerFilter!.filterValue = ''; this.perfProfileProgressEL!.loading = true; // @ts-ignore - this.perfProfileLoadingPage.style.visibility = 'visible'; // @ts-ignore - this.getDataByWorkAndUpDateCanvas(perfProfilerSelection); + this.perfProfileLoadingPage.style.visibility = 'visible'; + const newPerfProfilerSelection = Object.fromEntries(// @ts-ignore + Object.entries(perfProfilerSelection).filter(([key, value]) => + !['clockMapData', 'xpowerMapData', 'hangMapData'].includes(key) + ) + ) as Partial;// @ts-ignore + this.getDataByWorkAndUpDateCanvas(newPerfProfilerSelection); } getDataByWorkAndUpDateCanvas(perfProfilerSelection: SelectionParam): void { -- Gitee From 40f00a6c6effa4a61362f2a15cf24b102e5449b1 Mon Sep 17 00:00:00 2001 From: JustinTY Date: Thu, 28 Nov 2024 15:40:25 +0800 Subject: [PATCH 22/38] =?UTF-8?q?feat=20smartperf=E6=8C=81zip=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E7=9A=84=E6=96=87=E4=BB=B6=E6=89=93=E5=BC=80=20-=20?= =?UTF-8?q?=E6=AD=A3=E5=B8=B8zip=E6=A0=BC=E5=BC=8F=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- trace_streamer/src/base/file.cpp | 7 ++++++- trace_streamer/src/base/ts_common.h | 1 + trace_streamer/src/main.cpp | 1 - .../parser/rawtrace_parser/rawtrace_parser.cpp | 11 +++++++---- .../src/parser/rawtrace_parser/rawtrace_parser.h | 13 +++++++++++++ trace_streamer/src/rpc/rpc_server.cpp | 13 ++++++++++--- .../trace_streamer/trace_streamer_selector.cpp | 15 ++++++++++++--- .../src/trace_streamer/trace_streamer_selector.h | 8 ++++++-- 8 files changed, 55 insertions(+), 14 deletions(-) diff --git a/trace_streamer/src/base/file.cpp b/trace_streamer/src/base/file.cpp index f879ec211..72a2b9a03 100644 --- a/trace_streamer/src/base/file.cpp +++ b/trace_streamer/src/base/file.cpp @@ -215,7 +215,12 @@ bool LocalZip::Unzip(std::string &traceFile) #ifdef _WIN32 auto fileName = std::filesystem::path(tmpDir_).append(String2WString(filenameInZip)); #else - auto fileName = std::filesystem::path(tmpDir_).append(filenameInZip); + std::string tempFileName = filenameInZip; + if (base::GetCoding(reinterpret_cast(tempFileName.c_str()), tempFileName.length()) != + base::CODING::UTF8) { + tempFileName = "temp_" + std::to_string(i); + } + auto fileName = std::filesystem::path(tmpDir_).append(tempFileName); #endif // 是目录,则创建目录; 是文件,打开 -> 读取 -> 写入解压文件 -> 关闭 auto isDir = fileName.string().back() == '/' || fileName.string().back() == '\\'; diff --git a/trace_streamer/src/base/ts_common.h b/trace_streamer/src/base/ts_common.h index 109dd2ecb..a32daabdc 100644 --- a/trace_streamer/src/base/ts_common.h +++ b/trace_streamer/src/base/ts_common.h @@ -26,6 +26,7 @@ namespace SysTuning { using ClockId = uint32_t; constexpr size_t G_CHUNK_SIZE = 1024 * 1024; constexpr size_t FLUSH_CHUNK_THRESHOLD = G_CHUNK_SIZE - 10000; +constexpr uint8_t RAW_TRACE_PARSE_MAX = 2; const std::string INVALID_STRING = "INVALID_STRING"; const uint64_t INVALID_ITID = std::numeric_limits::max(); const uint64_t INVALID_IPID = std::numeric_limits::max(); diff --git a/trace_streamer/src/main.cpp b/trace_streamer/src/main.cpp index 78c2ae0e6..2c5367a8c 100644 --- a/trace_streamer/src/main.cpp +++ b/trace_streamer/src/main.cpp @@ -38,7 +38,6 @@ using namespace SysTuning::TraceStreamer; using namespace SysTuning::base; constexpr int G_MIN_PARAM_NUM = 2; constexpr size_t G_FILE_PERMISSION = 664; -constexpr uint8_t RAW_TRACE_PARSE_MAX = 2; constexpr uint8_t PARSER_THREAD_MAX = 16; constexpr uint8_t PARSER_THREAD_MIN = 1; std::regex traceInvalidStr("\\\\"); diff --git a/trace_streamer/src/parser/rawtrace_parser/rawtrace_parser.cpp b/trace_streamer/src/parser/rawtrace_parser/rawtrace_parser.cpp index 0459ccdb4..bc7e3cf03 100644 --- a/trace_streamer/src/parser/rawtrace_parser/rawtrace_parser.cpp +++ b/trace_streamer/src/parser/rawtrace_parser/rawtrace_parser.cpp @@ -89,7 +89,9 @@ bool RawTraceParser::InitRawTraceFileHeader(std::deque::iterator &packa bool RawTraceParser::InitEventFormats(const std::string &buffer) { #ifdef IS_WASM - restCommDataCnt_ = INVALID_UINT8; // ensure that the restCommData is parsed only once + if (!isWasmReadFile_) { + restCommDataCnt_ = INVALID_UINT8; // ensure that the restCommData is parsed only once + } #endif std::string line; std::istringstream iss(buffer); @@ -226,10 +228,11 @@ bool RawTraceParser::ParseLastCommData(uint8_t type, const std::string &buffer) break; default: #ifdef IS_WASM - return false; -#else - break; + if (!isWasmReadFile_) { + return false; + } #endif + break; } ++restCommDataCnt_; return true; diff --git a/trace_streamer/src/parser/rawtrace_parser/rawtrace_parser.h b/trace_streamer/src/parser/rawtrace_parser/rawtrace_parser.h index 849582b64..e98383649 100644 --- a/trace_streamer/src/parser/rawtrace_parser/rawtrace_parser.h +++ b/trace_streamer/src/parser/rawtrace_parser/rawtrace_parser.h @@ -44,6 +44,16 @@ public: { return rawTraceSplitCommData_; } +#ifdef IS_WASM + bool IsWasmReadFile() + { + return isWasmReadFile_; + } + void SetWasmReadFile(const bool isWasmReadFile) + { + isWasmReadFile_ = isWasmReadFile; + } +#endif private: bool ParseDataRecursively(std::deque::iterator &packagesCurIter); @@ -64,6 +74,9 @@ private: std::unique_ptr ksymsProcessor_ = nullptr; TraceDataCache *traceDataCache_ = nullptr; bool hasGotHeader_ = false; +#ifdef IS_WASM + bool isWasmReadFile_ = false; +#endif uint8_t fileType_ = 0; uint8_t restCommDataCnt_ = 0; uint32_t curCpuCoreNum_ = 0; diff --git a/trace_streamer/src/rpc/rpc_server.cpp b/trace_streamer/src/rpc/rpc_server.cpp index 396aa27c4..81662a43b 100644 --- a/trace_streamer/src/rpc/rpc_server.cpp +++ b/trace_streamer/src/rpc/rpc_server.cpp @@ -170,16 +170,23 @@ bool RpcServer::ReadAndParseData(const std::string &filePath) TS_LOGE("can not open %s.", filePath.c_str()); return false; } + uint8_t curParseCnt = 1; while (true) { std::unique_ptr buf = std::make_unique(G_CHUNK_SIZE); inputFile.read(reinterpret_cast(buf.get()), G_CHUNK_SIZE); auto readSize = inputFile.gcount(); - ts_->ParseTraceDataSegment(std::move(buf), readSize, false, inputFile.eof()); - if (inputFile.eof()) { + if (!ts_->ParseTraceDataSegment(std::move(buf), readSize, false, inputFile.eof(), true)) { + return false; + } + // for rawtrace next parse.the first parse is for last comm data; + if (inputFile.eof() && ts_->GetFileType() == TRACE_FILETYPE_RAW_TRACE && curParseCnt < RAW_TRACE_PARSE_MAX) { + ++curParseCnt; + inputFile.clear(); + inputFile.seekg(0, std::ios::beg); + } else if (inputFile.eof()) { break; } } - ts_->WaitForParserEnd(); inputFile.close(); return true; } diff --git a/trace_streamer/src/trace_streamer/trace_streamer_selector.cpp b/trace_streamer/src/trace_streamer/trace_streamer_selector.cpp index b6663a6bb..78f61f496 100644 --- a/trace_streamer/src/trace_streamer/trace_streamer_selector.cpp +++ b/trace_streamer/src/trace_streamer/trace_streamer_selector.cpp @@ -289,7 +289,10 @@ void TraceStreamerSelector::InitializeParser() } } -void TraceStreamerSelector::ProcessTraceData(std::unique_ptr data, size_t size, int32_t isFinish) +void TraceStreamerSelector::ProcessTraceData(std::unique_ptr data, + size_t size, + int32_t isFinish, + bool isWasmReadFile) { if (fileType_ == TRACE_FILETYPE_H_TRACE) { pbreaderParser_->ParseTraceDataSegment(std::move(data), size); @@ -303,6 +306,11 @@ void TraceStreamerSelector::ProcessTraceData(std::unique_ptr data, si #endif } else if (fileType_ == TRACE_FILETYPE_RAW_TRACE) { #ifdef ENABLE_RAWTRACE +#ifdef IS_WASM + if (isWasmReadFile && !rawTraceParser_->IsWasmReadFile()) { + rawTraceParser_->SetWasmReadFile(true); + } +#endif rawTraceParser_->ParseTraceDataSegment(std::move(data), size, isFinish); #endif } @@ -313,7 +321,8 @@ void TraceStreamerSelector::ProcessTraceData(std::unique_ptr data, si bool TraceStreamerSelector::ParseTraceDataSegment(std::unique_ptr data, size_t size, bool isSplitFile, - int32_t isFinish) + int32_t isFinish, + bool isWasmReadFile) { if (size == 0) { return true; @@ -344,7 +353,7 @@ bool TraceStreamerSelector::ParseTraceDataSegment(std::unique_ptr dat traceDataCache_->SetSplitFileMinTime(minTs_); traceDataCache_->SetSplitFileMaxTime(maxTs_); traceDataCache_->isSplitFile_ = isSplitFile; - ProcessTraceData(std::move(data), size, isFinish); + ProcessTraceData(std::move(data), size, isFinish, isWasmReadFile); #if !IS_WASM // in the linux,isFinish = 1,clear markinfo diff --git a/trace_streamer/src/trace_streamer/trace_streamer_selector.h b/trace_streamer/src/trace_streamer/trace_streamer_selector.h index c4d9bdeef..4ca22b7d1 100644 --- a/trace_streamer/src/trace_streamer/trace_streamer_selector.h +++ b/trace_streamer/src/trace_streamer/trace_streamer_selector.h @@ -32,7 +32,11 @@ class TraceStreamerSelector { public: TraceStreamerSelector(); ~TraceStreamerSelector(); - bool ParseTraceDataSegment(std::unique_ptr data, size_t size, bool isSplitFile, int32_t isFinish); + bool ParseTraceDataSegment(std::unique_ptr data, + size_t size, + bool isSplitFile, + int32_t isFinish, + bool isWasmReadFile = false); void EnableMetaTable(bool enabled); void EnableFileSave(bool enabled); static void SetCleanMode(bool cleanMode); @@ -95,7 +99,7 @@ public: return streamFilters_.get(); } void InitializeParser(); - void ProcessTraceData(std::unique_ptr data, size_t size, int32_t isFinish); + void ProcessTraceData(std::unique_ptr data, size_t size, int32_t isFinish, bool isWasmReadFile); // Used to obtain markinfo,skip under Linux void ClearMarkPositionInfo() -- Gitee From ccb3651521e83e5e74695521d1b598ab59c502da Mon Sep 17 00:00:00 2001 From: JustinYT Date: Thu, 28 Nov 2024 17:53:15 +0800 Subject: [PATCH 23/38] fix ut Signed-off-by: JustinYT --- trace_streamer/build_operator.sh | 2 +- trace_streamer/src/base/codec_cov.cpp | 5 + trace_streamer/src/rpc/wasm_func.cpp | 2 + .../test/unittest/ebpf/bio_parser_test.cpp | 2 + .../ebpf/paged_memory_parser_test.cpp | 2 + .../unittest/filter/animation_filter_test.cpp | 9 +- .../unittest/filter/frame_filter_test.cpp | 218 ++++++++++-------- .../unittest/filter/measure_filter_test.cpp | 46 ++-- .../pbreader_parser/diskio_parser_test.cpp | 2 + .../pbreader_parser/hilog_parser_test.cpp | 3 +- .../native_memory/native_hook_parser_test.cpp | 3 +- .../pbreader_cpu_data_parser_test.cpp | 2 + .../pbreader_mem_parser_test.cpp | 4 +- .../pbreader_network_parser_test.cpp | 2 + .../pbreader_process_parser_test.cpp | 2 + .../pbreader_parser/smaps_parser_test.cpp | 2 +- .../ptreader_parser/event_parser_test.cpp | 6 +- .../ptreader_parser/ptreader_parser_test.cpp | 4 +- .../query/sqllite_prepar_cache_data_test.cpp | 2 + .../rawtrace/rawtrace_parser_test.cpp | 1 - .../test/unittest/table/table_test.cpp | 48 ++-- 21 files changed, 213 insertions(+), 154 deletions(-) diff --git a/trace_streamer/build_operator.sh b/trace_streamer/build_operator.sh index 3cdcf6ec7..f4b861773 100755 --- a/trace_streamer/build_operator.sh +++ b/trace_streamer/build_operator.sh @@ -62,6 +62,6 @@ else echo "begin to build ..." prebuilts/"$gn_path"/"$ninja" -C "$out_dir" fi -if [ "$out_dir" == "macx" ];then +if [ "$out_dir" == "out/macx" ];then ./mac_depend.sh fi \ No newline at end of file diff --git a/trace_streamer/src/base/codec_cov.cpp b/trace_streamer/src/base/codec_cov.cpp index 648c6186a..7ccaaab7c 100644 --- a/trace_streamer/src/base/codec_cov.cpp +++ b/trace_streamer/src/base/codec_cov.cpp @@ -138,21 +138,26 @@ std::wstring String2WString(const std::string &strInput) std::cout << "strInput is empty" << std::endl; return L""; } + // 获取待转换的数据的长度 int len_in = MultiByteToWideChar(codePage, 0, (LPCSTR)strInput.c_str(), -1, NULL, 0); if (len_in <= 0) { std::cout << "The result of WideCharToMultiByte is Invalid!" << std::endl; return L""; } + // 为输出数据申请空间 std::wstring wstr_out; wstr_out.resize(len_in - 1, L'\0'); + // 数据格式转换 int to_result = MultiByteToWideChar(codePage, 0, (LPCSTR)strInput.c_str(), -1, (LPWSTR)wstr_out.c_str(), len_in); + // 判断转换结果 if (0 == to_result) { std::cout << "Can't transfer String to WString" << std::endl; } + return wstr_out; } #endif diff --git a/trace_streamer/src/rpc/wasm_func.cpp b/trace_streamer/src/rpc/wasm_func.cpp index 4a89700db..fda99bb05 100644 --- a/trace_streamer/src/rpc/wasm_func.cpp +++ b/trace_streamer/src/rpc/wasm_func.cpp @@ -248,7 +248,9 @@ EMSCRIPTEN_KEEPALIVE int32_t TraceStreamerParseDataEx(int32_t dataLen, bool isFi return g_wasmTraceStreamer.SaveAndParseFfrtData(g_reqBuf, dataLen, &FfrtConvertedResultCallback, isFinish); #endif } else if (g_isZipTrace) { +#if IS_WASM return g_wasmTraceStreamer.SaveAndParseZipTraceData(g_reqBuf, dataLen, &FfrtConvertedResultCallback, isFinish); +#endif } else if (g_wasmTraceStreamer.ParseData(g_reqBuf, dataLen, nullptr, isFinish)) { return 0; } diff --git a/trace_streamer/test/unittest/ebpf/bio_parser_test.cpp b/trace_streamer/test/unittest/ebpf/bio_parser_test.cpp index e5fd8939d..f3b3a39df 100644 --- a/trace_streamer/test/unittest/ebpf/bio_parser_test.cpp +++ b/trace_streamer/test/unittest/ebpf/bio_parser_test.cpp @@ -26,6 +26,7 @@ using namespace testing::ext; using namespace SysTuning::TraceStreamer; using namespace SysTuning::EbpfStdtype; namespace SysTuning ::TraceStreamer { +namespace BioParserUnitTest { const std::string COMMAND_LINE = "hiebpf --events ptrace --duration 50"; const uint64_t EPBF_ERROR_MAGIC = 0x12345678; const uint32_t EPBF_ERROR_HEAD_SIZE = 0; @@ -232,4 +233,5 @@ HWTEST_F(EbpfBioParserTest, EbpfBioParserCorrectWithMultipleCallback, TestSize.L auto ips1 = stream_.traceDataCache_->GetConstEbpfCallStackData().Ips()[0]; EXPECT_EQ(ips1, ExpectIps1); } +} // namespace BioParserUnitTest } // namespace SysTuning::TraceStreamer diff --git a/trace_streamer/test/unittest/ebpf/paged_memory_parser_test.cpp b/trace_streamer/test/unittest/ebpf/paged_memory_parser_test.cpp index a2dd377b3..4a52108fd 100644 --- a/trace_streamer/test/unittest/ebpf/paged_memory_parser_test.cpp +++ b/trace_streamer/test/unittest/ebpf/paged_memory_parser_test.cpp @@ -26,6 +26,7 @@ using namespace testing::ext; using namespace SysTuning::TraceStreamer; using namespace SysTuning::EbpfStdtype; namespace SysTuning ::TraceStreamer { +namespace PagedMemoryParserUnitTest { const std::string COMMAND_LINE = "hiebpf --events ptrace --duration 50"; const uint64_t START_TIME = 1725645867369; const uint64_t END_TIME = 1725645967369; @@ -200,4 +201,5 @@ HWTEST_F(EbpfPagedMemoryParserTest, EbpfPagedMemoryParserCorrectWithMultipleCall EXPECT_EQ(stream_.traceDataCache_->GetConstEbpfCallStackData().Ips()[0], ebpfDataParser->ConvertToHexTextIndex(ips[1])); } +} // namespace PagedMemoryParserUnitTest } // namespace SysTuning::TraceStreamer diff --git a/trace_streamer/test/unittest/filter/animation_filter_test.cpp b/trace_streamer/test/unittest/filter/animation_filter_test.cpp index ed3f3fb71..ffa002a0d 100644 --- a/trace_streamer/test/unittest/filter/animation_filter_test.cpp +++ b/trace_streamer/test/unittest/filter/animation_filter_test.cpp @@ -93,7 +93,8 @@ HWTEST_F(AnimationFilterTest, InvalidCallStack, TestSize.Level1) "H:RSUniRender::Process:[xxx]", }; // invalid parentId - for (size_t i = 0, depth = 0; i < callStackNames.size(); i++) { + uint8_t depth = 0; + for (size_t i = 0; i < callStackNames.size(); i++) { std::optional parentId = 0; CallStackInternalRow callStackInternalRow = {INVALID_TIME, INVALID_TIME, INVALID_UINT32, INVALID_UINT64, callStackNames[i], ++depth}; @@ -104,7 +105,8 @@ HWTEST_F(AnimationFilterTest, InvalidCallStack, TestSize.Level1) } // the current or the parent callStackNames haven't WindowScene_ uint64_t index = INVALID_UINT64; - for (size_t i = 0, depth = 0; i < callStackNames.size(); i++) { + depth = 0; + for (size_t i = 0; i < callStackNames.size(); i++) { std::optional parentId; if (index != INVALID_UINT64) { parentId = index; @@ -242,7 +244,8 @@ HWTEST_F(AnimationFilterTest, UpdateDynamicFrameInfo, TestSize.Level1) uint64_t index = INVALID_UINT64; uint64_t startTime = 59557002299000; uint64_t dur = ONE_MILLION_NANOSECONDS; - for (size_t i = 0, depth = 0; i < callStackNames.size(); i++) { + uint8_t depth = 0; + for (size_t i = 0; i < callStackNames.size(); i++) { std::optional parentId; if (index != INVALID_UINT64) { parentId = index; diff --git a/trace_streamer/test/unittest/filter/frame_filter_test.cpp b/trace_streamer/test/unittest/filter/frame_filter_test.cpp index eaa35d1f9..03fa7f2d9 100644 --- a/trace_streamer/test/unittest/filter/frame_filter_test.cpp +++ b/trace_streamer/test/unittest/filter/frame_filter_test.cpp @@ -17,23 +17,23 @@ #include #include "frame_filter.h" -#include "trace_data_cache.h" -#include "trace_streamer_filters.h" +#include "process_filter.h" +#include "trace_streamer_selector.h" using namespace testing::ext; using namespace SysTuning::TraceStreamer; namespace SysTuning { namespace TraceStreamer { const uint64_t START_TS = 1; -const uint32_t IPID = 1; -const uint32_t ITID = 1; +const uint32_t PID1 = 156; +const uint32_t TID1 = 248; const uint64_t EXPECTED_START = 5; const uint64_t EXPECTED_END = 10; const uint32_t VSYNC_ID = 1; const uint32_t CALLSTACK_SLICE_ID = 1; const uint64_t RS_START_TS = 5; -const uint32_t RS_IPID = 2; -const uint32_t RS_ITID = 2; +const uint32_t RS_PID = 2; +const uint32_t RS_TID = 2; const uint64_t RS_EXPECTED_START = 6; const uint64_t RS_EXPECTED_END = 11; const uint32_t RS_VSYNC_ID = 2; @@ -43,14 +43,13 @@ class FrameFilterTest : public ::testing::Test { public: void SetUp() { - streamFilters_.frameFilter_ = std::make_unique(&traceDataCache_, &streamFilters_); + stream_.InitFilter(); } void TearDown() {} public: - TraceStreamerFilters streamFilters_; - TraceDataCache traceDataCache_; + TraceStreamerSelector stream_ = {}; }; /** @@ -63,17 +62,20 @@ HWTEST_F(FrameFilterTest, AppVsyncNoFrameNum, TestSize.Level1) TS_LOGI("test6-1"); // ut 1 no frameNum // app ---------------VSYNCStart------------------End---uint64_t ts, - streamFilters_.frameFilter_->BeginVsyncEvent(START_TS, IPID, ITID, EXPECTED_START, EXPECTED_END, VSYNC_ID, - CALLSTACK_SLICE_ID); + BytraceLine line = {START_TS, TID1}; + line.tgid = PID1; + auto itid = stream_.streamFilters_->processFilter_->GetOrCreateThreadWithPid(TID1, PID1); + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line, EXPECTED_START, EXPECTED_END, VSYNC_ID, + CALLSTACK_SLICE_ID); const uint64_t END_TS = 10; - auto res = streamFilters_.frameFilter_->EndVsyncEvent(END_TS, ITID); + auto res = stream_.streamFilters_->frameFilter_->EndVsyncEvent(END_TS, itid); EXPECT_FALSE(res); - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->Flags()[0], 2); // actural frame, no frameNum - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->Flags()[1], 2); // expect frame, no frameNum - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->TimeStampData()[0], START_TS); // actural frame - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->TimeStampData()[1], EXPECTED_START); // expect frame - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->Durs()[0], END_TS - START_TS); // actural frame - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->Durs()[1], EXPECTED_END - EXPECTED_START); // expect frame + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->Flags()[0], 2); // actural frame, no frameNum + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->Flags()[1], 2); // expect frame, no frameNum + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->TimeStampData()[0], START_TS); // actural frame + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->TimeStampData()[1], EXPECTED_START); // expect frame + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->Durs()[0], END_TS - START_TS); // actural frame + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->Durs()[1], EXPECTED_END - EXPECTED_START); // expect frame } /** @@ -87,22 +89,25 @@ HWTEST_F(FrameFilterTest, AppVsyncHasFrameNum, TestSize.Level1) // ut 2 has frameNum // app -----VSYNCStart------------------End--- // -----------------frameNum-------------- - streamFilters_.frameFilter_->BeginVsyncEvent(START_TS, IPID, ITID, EXPECTED_START, EXPECTED_END, VSYNC_ID, - CALLSTACK_SLICE_ID); + BytraceLine line = {START_TS, TID1}; + line.tgid = PID1; + auto itid = stream_.streamFilters_->processFilter_->GetOrCreateThreadWithPid(TID1, PID1); + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line, EXPECTED_START, EXPECTED_END, VSYNC_ID, + CALLSTACK_SLICE_ID); const uint64_t FRAME_TS = 5; const uint32_t FRAME_NUM = 1; - bool res = streamFilters_.frameFilter_->BeginRSTransactionData(FRAME_TS, ITID, FRAME_NUM); + bool res = stream_.streamFilters_->frameFilter_->BeginRSTransactionData(FRAME_TS, itid, FRAME_NUM); EXPECT_TRUE(res); const uint64_t END_TS = 10; - res = streamFilters_.frameFilter_->EndVsyncEvent(END_TS, ITID); + res = stream_.streamFilters_->frameFilter_->EndVsyncEvent(END_TS, itid); EXPECT_TRUE(res); - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->Flags()[0], 0); // actural frame, no frameNum - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->Flags()[1], 255); // expect frame, no frameNum - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->TimeStampData()[0], START_TS); // actural frame - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->TimeStampData()[1], EXPECTED_START); // expect frame - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->Durs()[0], END_TS - START_TS); // actural frame - EXPECT_EQ(traceDataCache_.GetFrameSliceData()->Durs()[1], EXPECTED_END - EXPECTED_START); // expect frame - EXPECT_EQ(streamFilters_.frameFilter_->dstRenderSlice_[ITID][FRAME_NUM].get()->startTs_, START_TS); + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->Flags()[0], 0); // actural frame, no frameNum + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->Flags()[1], 255); // expect frame, no frameNum + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->TimeStampData()[0], START_TS); // actural frame + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->TimeStampData()[1], EXPECTED_START); // expect frame + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->Durs()[0], END_TS - START_TS); // actural frame + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->Durs()[1], EXPECTED_END - EXPECTED_START); // expect frame + EXPECT_EQ(stream_.streamFilters_->frameFilter_->dstRenderSlice_[itid][FRAME_NUM].get()->startTs_, START_TS); } /** * @tc.name: RSVsyncHasFrameNum @@ -114,15 +119,18 @@ HWTEST_F(FrameFilterTest, RSVsyncHasNoFrameNum, TestSize.Level1) TS_LOGI("test6-3"); // ut3 RS no frame // RS ---------------VSYNCStart------------------End--- - streamFilters_.frameFilter_->BeginVsyncEvent(START_TS, IPID, ITID, EXPECTED_START, EXPECTED_END, VSYNC_ID, - CALLSTACK_SLICE_ID); + BytraceLine line = {START_TS, TID1}; + line.tgid = PID1; + auto itid = stream_.streamFilters_->processFilter_->GetOrCreateThreadWithPid(TID1, PID1); + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line, EXPECTED_START, EXPECTED_END, VSYNC_ID, + CALLSTACK_SLICE_ID); const uint64_t ON_DO_COMPOSITION_TS = 2; - auto res = streamFilters_.frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS, ITID); + auto res = stream_.streamFilters_->frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS, itid); EXPECT_TRUE(res); const uint64_t END_TS = 10; - res = streamFilters_.frameFilter_->EndVsyncEvent(END_TS, ITID); + res = stream_.streamFilters_->frameFilter_->EndVsyncEvent(END_TS, itid); EXPECT_TRUE(res); - EXPECT_TRUE(streamFilters_.frameFilter_->vsyncRenderSlice_[ITID].begin()->get()->isRsMainThread_ == true); + EXPECT_TRUE(stream_.streamFilters_->frameFilter_->vsyncRenderSlice_[itid].begin()->get()->isRsMainThread_ == true); } /** @@ -136,10 +144,13 @@ HWTEST_F(FrameFilterTest, RSVsyncHasFrameNumNotMatched, TestSize.Level1) // ut4 RS has frame, bu not matched // RS -----VSYNCStart------------------End--- // -----------frameNum------------------- - streamFilters_.frameFilter_->BeginVsyncEvent(START_TS, IPID, ITID, EXPECTED_START, EXPECTED_END, VSYNC_ID, - CALLSTACK_SLICE_ID); + BytraceLine line = {START_TS, TID1}; + line.tgid = PID1; + auto itid = stream_.streamFilters_->processFilter_->GetOrCreateThreadWithPid(TID1, PID1); + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line, EXPECTED_START, EXPECTED_END, VSYNC_ID, + CALLSTACK_SLICE_ID); const uint64_t ON_DO_COMPOSITION_TS = 2; - auto res = streamFilters_.frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS, ITID); + auto res = stream_.streamFilters_->frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS, itid); EXPECT_TRUE(res); const uint32_t SOURCE_ITID1 = 2; @@ -147,12 +158,12 @@ HWTEST_F(FrameFilterTest, RSVsyncHasFrameNumNotMatched, TestSize.Level1) const uint64_t UNI_TS = 3; std::vector frames; frames.push_back({SOURCE_ITID1, SOURCE_FRAME_NUM}); - streamFilters_.frameFilter_->BeginProcessCommandUni(UNI_TS, ITID, frames, 0); + stream_.streamFilters_->frameFilter_->BeginProcessCommandUni(UNI_TS, itid, frames, 0); const uint64_t END_TS = 10; - res = streamFilters_.frameFilter_->EndVsyncEvent(END_TS, ITID); + res = stream_.streamFilters_->frameFilter_->EndVsyncEvent(END_TS, itid); EXPECT_TRUE(res); - EXPECT_TRUE(streamFilters_.frameFilter_->vsyncRenderSlice_[ITID].begin()->get()->isRsMainThread_ == true); - EXPECT_TRUE(traceDataCache_.GetFrameSliceData()->Srcs()[0].empty() == true); + EXPECT_TRUE(stream_.streamFilters_->frameFilter_->vsyncRenderSlice_[itid].begin()->get()->isRsMainThread_ == true); + EXPECT_TRUE(stream_.traceDataCache_->GetFrameSliceData()->Srcs()[0].empty() == true); } /** @@ -166,10 +177,13 @@ HWTEST_F(FrameFilterTest, RSVsyncHasGpu, TestSize.Level1) // ut5 RS has gpu inner // RS -----VSYNCStart------------------End--- // --------------gpuStart----gpuEnd---------- - streamFilters_.frameFilter_->BeginVsyncEvent(START_TS, IPID, ITID, EXPECTED_START, EXPECTED_END, VSYNC_ID, - CALLSTACK_SLICE_ID); + BytraceLine line = {START_TS, TID1}; + line.tgid = PID1; + auto itid = stream_.streamFilters_->processFilter_->GetOrCreateThreadWithPid(TID1, PID1); + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line, EXPECTED_START, EXPECTED_END, VSYNC_ID, + CALLSTACK_SLICE_ID); const uint64_t ON_DO_COMPOSITION_TS = 2; - auto res = streamFilters_.frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS, ITID); + auto res = stream_.streamFilters_->frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS, itid); EXPECT_TRUE(res); const uint32_t SOURCE_ITID1 = 2; @@ -177,12 +191,13 @@ HWTEST_F(FrameFilterTest, RSVsyncHasGpu, TestSize.Level1) const uint64_t UNI_TS = 3; std::vector frames; frames.push_back({SOURCE_ITID1, SOURCE_FRAME_NUM}); - streamFilters_.frameFilter_->BeginProcessCommandUni(UNI_TS, ITID, frames, 0); + stream_.streamFilters_->frameFilter_->BeginProcessCommandUni(UNI_TS, itid, frames, 0); const uint64_t END_TS = 10; - res = streamFilters_.frameFilter_->EndVsyncEvent(END_TS, ITID); + res = stream_.streamFilters_->frameFilter_->EndVsyncEvent(END_TS, itid); EXPECT_TRUE(res); - EXPECT_TRUE((streamFilters_.frameFilter_->vsyncRenderSlice_[ITID].begin()->get()->isRsMainThread_ == true)); - EXPECT_TRUE(traceDataCache_.GetFrameSliceData()->Srcs()[0].empty() == true); + EXPECT_TRUE( + (stream_.streamFilters_->frameFilter_->vsyncRenderSlice_[itid].begin()->get()->isRsMainThread_ == true)); + EXPECT_TRUE(stream_.traceDataCache_->GetFrameSliceData()->Srcs()[0].empty() == true); } /** @@ -196,23 +211,27 @@ HWTEST_F(FrameFilterTest, RSVsyncHasGpuCross, TestSize.Level1) // ut6 RS has gpu later // RS -----VSYNCStart------------------End------------ // ------------------------------gpuStart----gpuEnd--- - streamFilters_.frameFilter_->BeginVsyncEvent(START_TS, IPID, ITID, EXPECTED_START, EXPECTED_END, VSYNC_ID, - CALLSTACK_SLICE_ID); + BytraceLine line = {START_TS, TID1}; + line.tgid = PID1; + auto itid = stream_.streamFilters_->processFilter_->GetOrCreateThreadWithPid(TID1, PID1); + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line, EXPECTED_START, EXPECTED_END, VSYNC_ID, + CALLSTACK_SLICE_ID); const uint64_t ON_DO_COMPOSITION_TS = 2; - auto res = streamFilters_.frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS, ITID); + auto res = stream_.streamFilters_->frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS, itid); EXPECT_TRUE(res); const uint64_t GPU_START_TS = 3; - streamFilters_.frameFilter_->StartFrameQueue(GPU_START_TS, ITID); + stream_.streamFilters_->frameFilter_->StartFrameQueue(GPU_START_TS, itid); const uint64_t END_TS = 10; - res = streamFilters_.frameFilter_->EndVsyncEvent(END_TS, ITID); + res = stream_.streamFilters_->frameFilter_->EndVsyncEvent(END_TS, itid); EXPECT_TRUE(res); const uint64_t GPU_END_TS = 15; - res = streamFilters_.frameFilter_->EndFrameQueue(GPU_END_TS, ITID); + res = stream_.streamFilters_->frameFilter_->EndFrameQueue(GPU_END_TS, itid); EXPECT_TRUE(res); - EXPECT_TRUE((streamFilters_.frameFilter_->vsyncRenderSlice_[ITID].begin()->get()->isRsMainThread_ == true)); - EXPECT_TRUE(traceDataCache_.GetFrameSliceData()->Durs()[0] == GPU_END_TS - START_TS); + EXPECT_TRUE( + (stream_.streamFilters_->frameFilter_->vsyncRenderSlice_[itid].begin()->get()->isRsMainThread_ == true)); + EXPECT_TRUE(stream_.traceDataCache_->GetFrameSliceData()->Durs()[0] == GPU_END_TS - START_TS); } /** @@ -226,15 +245,18 @@ HWTEST_F(FrameFilterTest, RSVsyncHasGpu2Slices, TestSize.Level1) // ut7 RS two slice across // RS -----VSYNCStart------------------End-----VSYNCStart------------------End-------- // --------------gpuStart------------------------------gpuEnd---------gpuStart----gpuEnd------ - streamFilters_.frameFilter_->BeginVsyncEvent(START_TS, IPID, ITID, EXPECTED_START, EXPECTED_END, VSYNC_ID, - CALLSTACK_SLICE_ID); + BytraceLine line1 = {START_TS, TID1}; + line1.tgid = PID1; + auto itid = stream_.streamFilters_->processFilter_->GetOrCreateThreadWithPid(TID1, PID1); + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line1, EXPECTED_START, EXPECTED_END, VSYNC_ID, + CALLSTACK_SLICE_ID); const uint64_t ON_DO_COMPOSITION_TS = 2; - auto res = streamFilters_.frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS, ITID); + auto res = stream_.streamFilters_->frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS, itid); EXPECT_TRUE(res); const uint64_t GPU_START_TS = 3; - streamFilters_.frameFilter_->StartFrameQueue(GPU_START_TS, ITID); + stream_.streamFilters_->frameFilter_->StartFrameQueue(GPU_START_TS, itid); const uint64_t END_TS = 10; - res = streamFilters_.frameFilter_->EndVsyncEvent(END_TS, ITID); + res = stream_.streamFilters_->frameFilter_->EndVsyncEvent(END_TS, itid); EXPECT_TRUE(res); const uint64_t START_TS2 = 4; @@ -242,29 +264,32 @@ HWTEST_F(FrameFilterTest, RSVsyncHasGpu2Slices, TestSize.Level1) const uint64_t EXPECTED_END2 = 11; const uint32_t VSYNC_ID2 = 2; const uint32_t CALLSTACK_SLICE_ID2 = 2; - - streamFilters_.frameFilter_->BeginVsyncEvent(START_TS2, IPID, ITID, EXPECTED_START2, EXPECTED_END2, VSYNC_ID2, - CALLSTACK_SLICE_ID2); + BytraceLine line2 = {START_TS2, TID1}; + line2.tgid = PID1; + auto itid2 = stream_.streamFilters_->processFilter_->GetOrCreateThreadWithPid(TID1, PID1); + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line2, EXPECTED_START2, EXPECTED_END2, VSYNC_ID2, + CALLSTACK_SLICE_ID2); const uint64_t ON_DO_COMPOSITION_TS2 = 5; - res = streamFilters_.frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS2, ITID); + res = stream_.streamFilters_->frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS2, itid2); EXPECT_TRUE(res); const uint64_t GPU_END_TS = 15; - res = streamFilters_.frameFilter_->EndFrameQueue(GPU_END_TS, ITID); + res = stream_.streamFilters_->frameFilter_->EndFrameQueue(GPU_END_TS, itid2); const uint64_t GPU_START_TS2 = 16; - streamFilters_.frameFilter_->StartFrameQueue(GPU_START_TS2, ITID); + stream_.streamFilters_->frameFilter_->StartFrameQueue(GPU_START_TS2, itid2); const uint64_t END_TS2 = 18; - res = streamFilters_.frameFilter_->EndVsyncEvent(END_TS2, ITID); + res = stream_.streamFilters_->frameFilter_->EndVsyncEvent(END_TS2, itid2); const uint64_t GPU_END_TS2 = 20; - res = streamFilters_.frameFilter_->EndFrameQueue(GPU_END_TS2, ITID); + res = stream_.streamFilters_->frameFilter_->EndFrameQueue(GPU_END_TS2, itid2); EXPECT_TRUE(res); - EXPECT_TRUE((streamFilters_.frameFilter_->vsyncRenderSlice_[ITID].begin()->get()->isRsMainThread_ == true)); - EXPECT_TRUE(traceDataCache_.GetFrameSliceData()->Durs()[0] == GPU_END_TS - START_TS); - EXPECT_TRUE(traceDataCache_.GetFrameSliceData()->Durs()[2] == GPU_END_TS2 - START_TS2); - EXPECT_TRUE(streamFilters_.frameFilter_->vsyncRenderSlice_.size() == 1); + EXPECT_TRUE( + (stream_.streamFilters_->frameFilter_->vsyncRenderSlice_[itid].begin()->get()->isRsMainThread_ == true)); + EXPECT_TRUE(stream_.traceDataCache_->GetFrameSliceData()->Durs()[0] == GPU_END_TS - START_TS); + EXPECT_TRUE(stream_.traceDataCache_->GetFrameSliceData()->Durs()[2] == GPU_END_TS2 - START_TS2); + EXPECT_TRUE(stream_.streamFilters_->frameFilter_->vsyncRenderSlice_.size() == 1); } /** @@ -281,48 +306,55 @@ HWTEST_F(FrameFilterTest, SliceFromAppToRS, TestSize.Level1) // -----------------frameNum-------------- // RS -------------------------VSYNCStart------------------End-----VSYNCStart------------------End----------------- // -----------------------------------gpuStart------------------------------gpuEnd---------gpuStart----gpuEnd------ - streamFilters_.frameFilter_->BeginVsyncEvent(START_TS, IPID, ITID, EXPECTED_START, EXPECTED_END, VSYNC_ID, - CALLSTACK_SLICE_ID); + BytraceLine line = {START_TS, TID1}; + line.tgid = PID1; + auto itid = stream_.streamFilters_->processFilter_->GetOrCreateThreadWithPid(TID1, PID1); + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line, EXPECTED_START, EXPECTED_END, VSYNC_ID, + CALLSTACK_SLICE_ID); const uint64_t ON_DO_COMPOSITION_TS = 2; const uint32_t FRAME_NUM = 1; - EXPECT_TRUE(streamFilters_.frameFilter_->BeginRSTransactionData(ON_DO_COMPOSITION_TS, ITID, FRAME_NUM)); - streamFilters_.frameFilter_->BeginVsyncEvent(RS_START_TS, RS_IPID, RS_ITID, RS_EXPECTED_START, RS_EXPECTED_END, - RS_VSYNC_ID, RS_CALLSTACK_SLICE_ID); + EXPECT_TRUE(stream_.streamFilters_->frameFilter_->BeginRSTransactionData(ON_DO_COMPOSITION_TS, itid, FRAME_NUM)); + BytraceLine line2 = {RS_START_TS, RS_PID}; + line.tgid = RS_TID; + auto itid2 = stream_.streamFilters_->processFilter_->GetOrCreateThreadWithPid(RS_TID, RS_PID); + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line2, RS_EXPECTED_START, RS_EXPECTED_END, RS_VSYNC_ID, + RS_CALLSTACK_SLICE_ID); const uint64_t ON_DO_COMPOSITION_TS2 = 7; - streamFilters_.frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS2, RS_ITID); + stream_.streamFilters_->frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS2, RS_TID); const uint64_t GPU_START_TS2 = 7; - streamFilters_.frameFilter_->StartFrameQueue(GPU_START_TS2, RS_ITID); + stream_.streamFilters_->frameFilter_->StartFrameQueue(GPU_START_TS2, RS_TID); const uint64_t END_TS = 10; - EXPECT_TRUE(streamFilters_.frameFilter_->EndVsyncEvent(END_TS, ITID)); + EXPECT_TRUE(stream_.streamFilters_->frameFilter_->EndVsyncEvent(END_TS, itid)); const uint64_t RS_END_TS = 10; - streamFilters_.frameFilter_->EndVsyncEvent(RS_END_TS, RS_ITID); + stream_.streamFilters_->frameFilter_->EndVsyncEvent(RS_END_TS, itid2); const uint64_t RS_START_TS2 = 11; const uint64_t RS_EXPECTED_START2 = 11; const uint64_t RS_EXPECTED_END2 = 25; const uint32_t RS_VSYNC_ID2 = 3; const uint32_t RS_CALLSTACK_SLICE_ID2 = 3; - streamFilters_.frameFilter_->BeginVsyncEvent(RS_START_TS2, RS_IPID, RS_ITID, RS_EXPECTED_START2, RS_EXPECTED_END2, - RS_VSYNC_ID2, RS_CALLSTACK_SLICE_ID2); + BytraceLine line3 = {RS_START_TS2, RS_PID, RS_TID}; + stream_.streamFilters_->frameFilter_->BeginVsyncEvent(line3, RS_EXPECTED_START2, RS_EXPECTED_END2, RS_VSYNC_ID2, + RS_CALLSTACK_SLICE_ID2); const uint64_t ON_DO_COMPOSITION_TS3 = 12; - streamFilters_.frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS3, RS_ITID); + stream_.streamFilters_->frameFilter_->MarkRSOnDoCompositionEvent(ON_DO_COMPOSITION_TS3, RS_TID); const uint64_t GPU_END_TS = 15; - streamFilters_.frameFilter_->EndFrameQueue(GPU_END_TS, RS_ITID); + stream_.streamFilters_->frameFilter_->EndFrameQueue(GPU_END_TS, RS_TID); const uint64_t GPU_START_TS3 = 16; - streamFilters_.frameFilter_->StartFrameQueue(GPU_START_TS3, RS_ITID); + stream_.streamFilters_->frameFilter_->StartFrameQueue(GPU_START_TS3, RS_TID); const uint64_t END_TS3 = 20; - streamFilters_.frameFilter_->EndVsyncEvent(END_TS3, RS_ITID); + stream_.streamFilters_->frameFilter_->EndVsyncEvent(END_TS3, RS_TID); const uint64_t GPU_END_TS3 = 25; - streamFilters_.frameFilter_->EndFrameQueue(GPU_END_TS3, RS_ITID); + stream_.streamFilters_->frameFilter_->EndFrameQueue(GPU_END_TS3, RS_TID); - EXPECT_TRUE(traceDataCache_.GetFrameSliceData()->Durs()[0] == END_TS - START_TS); - EXPECT_TRUE(traceDataCache_.GetFrameSliceData()->Durs()[2] == GPU_END_TS - RS_START_TS); - EXPECT_TRUE(atoi(traceDataCache_.GetFrameSliceData()->Srcs()[2].c_str()) == - traceDataCache_.GetFrameSliceData()->IdsData()[0]); + EXPECT_TRUE(stream_.traceDataCache_->GetFrameSliceData()->Durs()[0] == END_TS - START_TS); + EXPECT_EQ(stream_.traceDataCache_->GetFrameSliceData()->Durs()[2], GPU_END_TS3 - END_TS3); + EXPECT_TRUE(atoi(stream_.traceDataCache_->GetFrameSliceData()->Srcs()[2].c_str()) == + stream_.traceDataCache_->GetFrameSliceData()->IdsData()[0]); } } // namespace TraceStreamer } // namespace SysTuning diff --git a/trace_streamer/test/unittest/filter/measure_filter_test.cpp b/trace_streamer/test/unittest/filter/measure_filter_test.cpp index 7e1527975..e9c3ccab7 100644 --- a/trace_streamer/test/unittest/filter/measure_filter_test.cpp +++ b/trace_streamer/test/unittest/filter/measure_filter_test.cpp @@ -57,11 +57,12 @@ HWTEST_F(MeasureFilterTest, CpuFilter, TestSize.Level1) { TS_LOGI("test23-3"); auto nameIndex_0 = stream_.traceDataCache_->GetDataIndex(CPU_TYPE_0); - uint32_t filterId = stream_.streamFilters_->cpuMeasureFilter_->GetOrCreateFilterId(CPU_ID_0, nameIndex_0); + auto &measureFilter = stream_.streamFilters_->measureFilter_; + uint32_t filterId = measureFilter->GetOrCreateFilterId(EnumMeasureFilter::CPU, CPU_ID_0, nameIndex_0); EXPECT_TRUE(filterId == 0); auto nameIndex_1 = stream_.traceDataCache_->GetDataIndex(CPU_TYPE_1); - filterId = stream_.streamFilters_->cpuMeasureFilter_->GetOrCreateFilterId(CPU_ID_1, nameIndex_1); + filterId = measureFilter->GetOrCreateFilterId(EnumMeasureFilter::CPU, CPU_ID_1, nameIndex_1); EXPECT_TRUE(filterId == 1); Filter *filterTable = stream_.traceDataCache_->GetFilterData(); @@ -84,11 +85,12 @@ HWTEST_F(MeasureFilterTest, ClockRateFilter, TestSize.Level1) { TS_LOGI("test23-5"); auto nameIndex_0 = stream_.traceDataCache_->GetDataIndex(TASK_NAME_0); - uint32_t filterId = stream_.streamFilters_->clockRateFilter_->GetOrCreateFilterId(CPU_ID_0, nameIndex_0); + auto &measureFilter = stream_.streamFilters_->measureFilter_; + uint32_t filterId = measureFilter->GetOrCreateFilterId(EnumMeasureFilter::CLOCK_RATE, CPU_ID_0, nameIndex_0); EXPECT_TRUE(filterId == 0); auto nameIndex_1 = stream_.traceDataCache_->GetDataIndex(TASK_NAME_1); - filterId = stream_.streamFilters_->clockRateFilter_->GetOrCreateFilterId(CPU_ID_1, nameIndex_1); + filterId = measureFilter->GetOrCreateFilterId(EnumMeasureFilter::CLOCK_RATE, CPU_ID_1, nameIndex_1); EXPECT_TRUE(filterId == 1); Filter *filterTable = stream_.traceDataCache_->GetFilterData(); @@ -110,12 +112,13 @@ HWTEST_F(MeasureFilterTest, ClockRateFilter, TestSize.Level1) HWTEST_F(MeasureFilterTest, ClockEnableFilter, TestSize.Level1) { TS_LOGI("test23-6"); + auto &measureFilter = stream_.streamFilters_->measureFilter_; auto nameIndex_0 = stream_.traceDataCache_->GetDataIndex(TASK_NAME_0); - uint32_t filterId = stream_.streamFilters_->clockEnableFilter_->GetOrCreateFilterId(CPU_ID_0, nameIndex_0); + uint32_t filterId = measureFilter->GetOrCreateFilterId(EnumMeasureFilter::CLOCK_ENABLE, CPU_ID_0, nameIndex_0); EXPECT_TRUE(filterId == 0); auto nameIndex_1 = stream_.traceDataCache_->GetDataIndex(TASK_NAME_1); - filterId = stream_.streamFilters_->clockEnableFilter_->GetOrCreateFilterId(CPU_ID_1, nameIndex_1); + filterId = measureFilter->GetOrCreateFilterId(EnumMeasureFilter::CLOCK_ENABLE, CPU_ID_1, nameIndex_1); EXPECT_TRUE(filterId == 1); Filter *filterTable = stream_.traceDataCache_->GetFilterData(); @@ -137,12 +140,13 @@ HWTEST_F(MeasureFilterTest, ClockEnableFilter, TestSize.Level1) HWTEST_F(MeasureFilterTest, ClockDisableFilter, TestSize.Level1) { TS_LOGI("test23-7"); + auto &measureFilter = stream_.streamFilters_->measureFilter_; auto nameIndex_0 = stream_.traceDataCache_->GetDataIndex(TASK_NAME_0); - uint32_t filterId = stream_.streamFilters_->clockDisableFilter_->GetOrCreateFilterId(CPU_ID_0, nameIndex_0); + uint32_t filterId = measureFilter->GetOrCreateFilterId(EnumMeasureFilter::CLOCK_DISABLE, CPU_ID_0, nameIndex_0); EXPECT_TRUE(filterId == 0); auto nameIndex_1 = stream_.traceDataCache_->GetDataIndex(TASK_NAME_1); - filterId = stream_.streamFilters_->clockDisableFilter_->GetOrCreateFilterId(CPU_ID_1, nameIndex_1); + filterId = measureFilter->GetOrCreateFilterId(EnumMeasureFilter::CLOCK_DISABLE, CPU_ID_1, nameIndex_1); EXPECT_TRUE(filterId == 1); Filter *filterTable = stream_.traceDataCache_->GetFilterData(); @@ -167,8 +171,8 @@ HWTEST_F(MeasureFilterTest, MeasureFilterTest, TestSize.Level1) uint64_t itid = 1; const std::string_view MEASURE_ITEM_NAME = "mem_rss"; auto nameIndex0 = stream_.traceDataCache_->GetDataIndex(MEASURE_ITEM_NAME); - auto threadMeasureFilter = stream_.streamFilters_->processMeasureFilter_.get(); - threadMeasureFilter->AppendNewMeasureData(itid, nameIndex0, 168758682476000, 1200); + stream_.streamFilters_->measureFilter_->AppendNewMeasureData(EnumMeasureFilter::PROCESS, itid, nameIndex0, + 168758682476000, 1200); EXPECT_TRUE(stream_.traceDataCache_->GetConstProcessMeasureData().Size() == 1); } @@ -182,13 +186,14 @@ HWTEST_F(MeasureFilterTest, MeasureFilterAddMultiMemToSingleThread, TestSize.Lev { TS_LOGI("test23-9"); uint64_t itid = 1; - auto threadMeasureFilter = stream_.streamFilters_->processMeasureFilter_.get(); const std::string_view MEASURE_ITEM_NAME = "mem_rss"; auto nameIndex0 = stream_.traceDataCache_->GetDataIndex(MEASURE_ITEM_NAME); - threadMeasureFilter->AppendNewMeasureData(itid, nameIndex0, 168758682476000, 1200); + stream_.streamFilters_->measureFilter_->AppendNewMeasureData(EnumMeasureFilter::PROCESS, itid, nameIndex0, + 168758682476000, 1200); const std::string_view MEASURE_ITEM_NAME2 = "mem_vm"; auto nameIndex1 = stream_.traceDataCache_->GetDataIndex(MEASURE_ITEM_NAME2); - threadMeasureFilter->AppendNewMeasureData(itid, nameIndex1, 168758682477000, 9200); + stream_.streamFilters_->measureFilter_->AppendNewMeasureData(EnumMeasureFilter::PROCESS, itid, nameIndex1, + 168758682477000, 9200); EXPECT_TRUE(stream_.traceDataCache_->GetConstProcessMeasureData().Size() == 2); } @@ -203,13 +208,14 @@ HWTEST_F(MeasureFilterTest, MeasureFilterAddMultiMemToMultiThread, TestSize.Leve TS_LOGI("test23-10"); uint64_t itid = 1; uint64_t itid2 = 2; - auto threadMeasureFilter = stream_.streamFilters_->processMeasureFilter_.get(); const std::string_view MEASURE_ITEM_NAME = "mem_rss"; auto nameIndex0 = stream_.traceDataCache_->GetDataIndex(MEASURE_ITEM_NAME); - threadMeasureFilter->AppendNewMeasureData(itid, nameIndex0, 168758682476000, 1200); + stream_.streamFilters_->measureFilter_->AppendNewMeasureData(EnumMeasureFilter::PROCESS, itid, nameIndex0, + 168758682476000, 1200); const std::string_view MEASURE_ITEM_NAME2 = "mem_vm"; auto nameIndex1 = stream_.traceDataCache_->GetDataIndex(MEASURE_ITEM_NAME2); - threadMeasureFilter->AppendNewMeasureData(itid2, nameIndex1, 168758682477000, 9200); + stream_.streamFilters_->measureFilter_->AppendNewMeasureData(EnumMeasureFilter::PROCESS, itid2, nameIndex1, + 168758682477000, 9200); EXPECT_TRUE(stream_.traceDataCache_->GetConstProcessMeasureData().Size() == 2); EXPECT_TRUE(stream_.traceDataCache_->GetConstProcessMeasureData().ValuesData()[0] == 1200); EXPECT_TRUE(stream_.traceDataCache_->GetConstProcessMeasureData().ValuesData()[1] == 9200); @@ -225,10 +231,10 @@ HWTEST_F(MeasureFilterTest, MeasureFilterAddPerfclLfMux, TestSize.Level1) TS_LOGI("test23-11"); uint64_t cpuId = 1; int64_t state = 0; - auto threadMeasureFilter = stream_.streamFilters_->clockDisableFilter_.get(); const std::string_view MEASURE_ITEM_NAME = "perfcl_lf_mux"; auto nameIndex0 = stream_.traceDataCache_->GetDataIndex(MEASURE_ITEM_NAME); - threadMeasureFilter->AppendNewMeasureData(cpuId, nameIndex0, 168758682476000, state); + stream_.streamFilters_->measureFilter_->AppendNewMeasureData(EnumMeasureFilter::CLOCK_DISABLE, cpuId, nameIndex0, + 168758682476000, state); EXPECT_TRUE(stream_.traceDataCache_->GetConstMeasureData().Size() == 1); EXPECT_TRUE(stream_.traceDataCache_->GetConstMeasureData().ValuesData()[0] == state); } @@ -243,10 +249,10 @@ HWTEST_F(MeasureFilterTest, MeasureFilterAddPerfclPll, TestSize.Level1) TS_LOGI("test23-12"); uint64_t cpuId = 1; int64_t state = 1747200000; - auto threadMeasureFilter = stream_.streamFilters_->clockRateFilter_.get(); const std::string_view MEASURE_ITEM_NAME = "perfcl_pll"; auto nameIndex0 = stream_.traceDataCache_->GetDataIndex(MEASURE_ITEM_NAME); - threadMeasureFilter->AppendNewMeasureData(cpuId, nameIndex0, 168758682476000, state); + stream_.streamFilters_->measureFilter_->AppendNewMeasureData(EnumMeasureFilter::CLOCK_RATE, cpuId, nameIndex0, + 168758682476000, state); EXPECT_TRUE(stream_.traceDataCache_->GetConstMeasureData().Size() == 1); EXPECT_TRUE(stream_.traceDataCache_->GetConstMeasureData().ValuesData()[0] == state); } diff --git a/trace_streamer/test/unittest/pbreader_parser/diskio_parser_test.cpp b/trace_streamer/test/unittest/pbreader_parser/diskio_parser_test.cpp index 5480e2cde..f253ed093 100644 --- a/trace_streamer/test/unittest/pbreader_parser/diskio_parser_test.cpp +++ b/trace_streamer/test/unittest/pbreader_parser/diskio_parser_test.cpp @@ -30,6 +30,7 @@ using namespace SysTuning::TraceStreamer; namespace SysTuning { namespace TraceStreamer { +namespace DiskioParserUnitTest { const uint64_t TS = 100; const uint64_t RD_01 = 100; const uint64_t WR_01 = 101; @@ -237,5 +238,6 @@ HWTEST_F(PbreaderDiskioParserTest, ParsePbreaderDiskioWithMultipleDiskioData, Te EXPECT_EQ(diskIOData.WrCountDatas()[1], WR_03); EXPECT_EQ(diskIOData.WrCountDatas()[2], WR_04); } +} // namespace DiskioParserUnitTest } // namespace TraceStreamer } // namespace SysTuning \ No newline at end of file diff --git a/trace_streamer/test/unittest/pbreader_parser/hilog_parser_test.cpp b/trace_streamer/test/unittest/pbreader_parser/hilog_parser_test.cpp index 5d80c5861..3066d358a 100644 --- a/trace_streamer/test/unittest/pbreader_parser/hilog_parser_test.cpp +++ b/trace_streamer/test/unittest/pbreader_parser/hilog_parser_test.cpp @@ -30,6 +30,7 @@ using namespace SysTuning::TraceStreamer; namespace SysTuning { namespace TraceStreamer { +namespace HilogUnitTest { const uint32_t PID = 2716; const uint32_t TID = 1532; const uint64_t LOG_ID = 1; @@ -359,6 +360,6 @@ HWTEST_F(HilogParserTest, ParseTxtHilogInfoWithTimeFormat, TestSize.Level1) EXPECT_TRUE(ta->traceDataCache_->GetConstHilogData().HilogLineSeqs().size() == 5); } - +} // namespace HilogUnitTest } // namespace TraceStreamer } // namespace SysTuning \ No newline at end of file diff --git a/trace_streamer/test/unittest/pbreader_parser/native_memory/native_hook_parser_test.cpp b/trace_streamer/test/unittest/pbreader_parser/native_memory/native_hook_parser_test.cpp index 1b8bf60aa..374e1863f 100644 --- a/trace_streamer/test/unittest/pbreader_parser/native_memory/native_hook_parser_test.cpp +++ b/trace_streamer/test/unittest/pbreader_parser/native_memory/native_hook_parser_test.cpp @@ -32,7 +32,7 @@ using namespace SysTuning::TraceStreamer; namespace SysTuning { namespace TraceStreamer { -bool ParseTraceFile(TraceStreamerSelector &ts_, const std::string &tracePath); +namespace NativeMemoryUnitTest { const uint32_t INDEX_SIZE_02 = 2; const uint32_t INDEX_SIZE_03 = 3; const uint32_t INDEX_SIZE_04 = 4; @@ -1639,5 +1639,6 @@ HWTEST_F(NativeHookParserTest, ParseOfflineSymMixedStack, TestSize.Level1) auto &statAndInfo = stream_.traceDataCache_->GetConstStatAndInfo(); EXPECT_TRUE(1 == statAndInfo.GetValue(TRACE_NATIVE_HOOK_MALLOC, STAT_EVENT_RECEIVED)); } +} // namespace NativeMemoryUnitTest } // namespace TraceStreamer } // namespace SysTuning \ No newline at end of file diff --git a/trace_streamer/test/unittest/pbreader_parser/pbreader_cpu_data_parser_test.cpp b/trace_streamer/test/unittest/pbreader_parser/pbreader_cpu_data_parser_test.cpp index f7e32096f..66ad48744 100644 --- a/trace_streamer/test/unittest/pbreader_parser/pbreader_cpu_data_parser_test.cpp +++ b/trace_streamer/test/unittest/pbreader_parser/pbreader_cpu_data_parser_test.cpp @@ -30,6 +30,7 @@ using namespace SysTuning::TraceStreamer; namespace SysTuning { namespace TraceStreamer { +namespace PbReaderCpuDataParserUnitTest { const uint64_t TS = 104; const uint64_t TOTALLOAD_01 = 4; const uint64_t USERLOAD_01 = 44; @@ -230,5 +231,6 @@ HWTEST_F(HtraceCpuDataParserTest, ParseHtraceWithMultipleCpuData, TestSize.Level EXPECT_EQ(cpuUsageInfoData.SystemLoad()[1], SYSTEMLOAD_03); EXPECT_EQ(cpuUsageInfoData.SystemLoad()[2], SYSTEMLOAD_04); } +} // namespace PbReaderCpuDataParserUnitTest } // namespace TraceStreamer } // namespace SysTuning diff --git a/trace_streamer/test/unittest/pbreader_parser/pbreader_mem_parser_test.cpp b/trace_streamer/test/unittest/pbreader_parser/pbreader_mem_parser_test.cpp index a976d1106..89fca1f18 100644 --- a/trace_streamer/test/unittest/pbreader_parser/pbreader_mem_parser_test.cpp +++ b/trace_streamer/test/unittest/pbreader_parser/pbreader_mem_parser_test.cpp @@ -144,7 +144,7 @@ HWTEST_F(HtraceMemParserTest, ParseMemParseTestMeasureDataSize, TestSize.Level1) EXPECT_TRUE(stream_.traceDataCache_->GetConstProcessData(1).pid_ == pid); auto processMeasureData = stream_.traceDataCache_->GetConstProcessMeasureData(); - EXPECT_EQ(processMeasureData.Size(), MEM_PURG_SUM * 1); + EXPECT_EQ(processMeasureData.Size(), MEM_OOM_SCORE_ADJ * 1); EXPECT_EQ(stream_.traceDataCache_->GetConstProcessData().size(), 2); for (auto i = 0; i < MEM_PURG_SUM; i++) { @@ -230,7 +230,7 @@ HWTEST_F(HtraceMemParserTest, ParseMultiEmptyProcessMemoryInfo, TestSize.Level1) auto eventCount = stream_.traceDataCache_->GetConstStatAndInfo().GetValue(TRACE_MEMORY, STAT_EVENT_RECEIVED); EXPECT_TRUE(1 == eventCount); - EXPECT_TRUE(stream_.traceDataCache_->GetConstProcessMeasureData().Size() == MEM_PURG_SUM * 2); + EXPECT_TRUE(stream_.traceDataCache_->GetConstProcessMeasureData().Size() == MEM_OOM_SCORE_ADJ * 2); } /** diff --git a/trace_streamer/test/unittest/pbreader_parser/pbreader_network_parser_test.cpp b/trace_streamer/test/unittest/pbreader_parser/pbreader_network_parser_test.cpp index fb1055967..31f144973 100644 --- a/trace_streamer/test/unittest/pbreader_parser/pbreader_network_parser_test.cpp +++ b/trace_streamer/test/unittest/pbreader_parser/pbreader_network_parser_test.cpp @@ -30,6 +30,7 @@ using namespace SysTuning::TraceStreamer; namespace SysTuning { namespace TraceStreamer { +namespace PbReaderNetworkParserTest { const uint64_t TS = 100; const uint64_t DURS_01 = 1999632781; const uint64_t TX_01 = 712921; @@ -223,5 +224,6 @@ HWTEST_F(HtraceNetworkParserTest, ParseHtraceNetworkWithMultipleNetworkData, Tes EXPECT_EQ(netDetailData.PacketOut()[1], PACKETOUT_03); EXPECT_EQ(netDetailData.PacketOut()[2], PACKETOUT_04); } +} // namespace PbReaderNetworkParserTest } // namespace TraceStreamer } // namespace SysTuning \ No newline at end of file diff --git a/trace_streamer/test/unittest/pbreader_parser/pbreader_process_parser_test.cpp b/trace_streamer/test/unittest/pbreader_parser/pbreader_process_parser_test.cpp index 6b31035df..128ac2800 100644 --- a/trace_streamer/test/unittest/pbreader_parser/pbreader_process_parser_test.cpp +++ b/trace_streamer/test/unittest/pbreader_parser/pbreader_process_parser_test.cpp @@ -30,6 +30,7 @@ using namespace SysTuning::TraceStreamer; namespace SysTuning { namespace TraceStreamer { +namespace PbreaderProcessParserUnitTest { uint64_t TS = 100; const uint32_t PID_01 = 311; const std::string NAME_01 = "resource_schedu01"; @@ -217,5 +218,6 @@ HWTEST_F(HtraceProcessParserTest, ParseHtraceProcessWithMultipleProcessData, Tes EXPECT_EQ(liveProcessData.UserName()[1], std::to_string(UID_03)); EXPECT_EQ(liveProcessData.UserName()[2], std::to_string(UID_04)); } +} // namespace PbreaderProcessParserUnitTest } // namespace TraceStreamer } // namespace SysTuning \ No newline at end of file diff --git a/trace_streamer/test/unittest/pbreader_parser/smaps_parser_test.cpp b/trace_streamer/test/unittest/pbreader_parser/smaps_parser_test.cpp index 71be72d85..564b65024 100644 --- a/trace_streamer/test/unittest/pbreader_parser/smaps_parser_test.cpp +++ b/trace_streamer/test/unittest/pbreader_parser/smaps_parser_test.cpp @@ -78,7 +78,7 @@ public: public: SysTuning::TraceStreamer::TraceStreamerSelector stream_ = {}; - const std::string dbPath_ = "data/resource/out.db"; + const std::string dbPath_ = "out.db"; }; /** * @tc.name: ParseSmapsParse diff --git a/trace_streamer/test/unittest/ptreader_parser/event_parser_test.cpp b/trace_streamer/test/unittest/ptreader_parser/event_parser_test.cpp index 8875eb206..442b28ab5 100644 --- a/trace_streamer/test/unittest/ptreader_parser/event_parser_test.cpp +++ b/trace_streamer/test/unittest/ptreader_parser/event_parser_test.cpp @@ -147,8 +147,7 @@ HWTEST_F(EventParserTest, ParseSchedSwitchNoArgs, TestSize.Level1) BytraceEventParser eventParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get()); eventParser.ParseDataItem(bytraceLine); eventParser.FilterAllEvents(); - EXPECT_EQ(1, - stream_.traceDataCache_->GetStatAndInfo()->GetValue(TRACE_EVENT_SCHED_SWITCH, STAT_EVENT_DATA_INVALID)); + EXPECT_EQ(1, stream_.traceDataCache_->GetStatAndInfo()->GetValue(TRACE_EVENT_SCHED_SWITCH, STAT_EVENT_RECEIVED)); } /** @@ -164,8 +163,6 @@ HWTEST_F(EventParserTest, ParseSchedWakeupNoArgs, TestSize.Level1) bytraceLine.pid = 1; bytraceLine.cpu = 0; bytraceLine.task = "ACCS0-2716"; - bytraceLine.pidStr = "12"; - bytraceLine.tGidStr = "12"; bytraceLine.eventName = "sched_wakeup"; BytraceEventParser eventParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get()); eventParser.ParseDataItem(bytraceLine); @@ -694,7 +691,6 @@ HWTEST_F(EventParserTest, ParseTaskNewtaskByInitParam, TestSize.Level1) { TS_LOGI("test5-27"); BytraceLine bytraceLine; - bytraceLine.tGidStr = "12"; static std::unordered_map args{{"comm", "POSIX"}, {"pid", "8542"}, {"clone_flags", "1"}}; BytraceEventParser eventParser(stream_.traceDataCache_.get(), stream_.streamFilters_.get()); int32_t result = eventParser.TaskNewtaskEvent(args, bytraceLine); diff --git a/trace_streamer/test/unittest/ptreader_parser/ptreader_parser_test.cpp b/trace_streamer/test/unittest/ptreader_parser/ptreader_parser_test.cpp index 8305d3954..d7c376955 100644 --- a/trace_streamer/test/unittest/ptreader_parser/ptreader_parser_test.cpp +++ b/trace_streamer/test/unittest/ptreader_parser/ptreader_parser_test.cpp @@ -197,8 +197,8 @@ HWTEST_F(PtreaderParserTest, LineParser_abnormal_pid_err, TestSize.Level1) ptreaderParser.WaitForParserEnd(); EXPECT_TRUE(ptreaderParser.TraceCommentLines() == 0); - EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 0); - EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 1); + EXPECT_TRUE(ptreaderParser.ParsedTraceValidLines() == 1); + EXPECT_TRUE(ptreaderParser.ParsedTraceInvalidLines() == 0); } /** diff --git a/trace_streamer/test/unittest/query/sqllite_prepar_cache_data_test.cpp b/trace_streamer/test/unittest/query/sqllite_prepar_cache_data_test.cpp index 9273f0ab2..f4eb03942 100644 --- a/trace_streamer/test/unittest/query/sqllite_prepar_cache_data_test.cpp +++ b/trace_streamer/test/unittest/query/sqllite_prepar_cache_data_test.cpp @@ -32,6 +32,7 @@ using namespace SysTuning; using namespace SysTuning::TraceStreamer; namespace SysTuning { namespace TraceStreamer { +namespace SqlitePreparCacheUnitTest { const int32_t PROCESS_ID = 100; const int32_t CPU = 101; const int32_t PID = 102; @@ -1211,5 +1212,6 @@ HWTEST_F(SqllitePreparCacheDataTest, ParseBatchSphCpuAbilityData, TestSize.Level EXPECT_EQ(batchsphCpuAbilityDataRes.values(0).cpu_ability_data().start_ns(), START_NS); EXPECT_EQ(batchsphCpuAbilityDataRes.values(0).cpu_ability_data().dur(), DUR); } +} // namespace SqlitePreparCacheUnitTest } // namespace TraceStreamer } // namespace SysTuning diff --git a/trace_streamer/test/unittest/rawtrace/rawtrace_parser_test.cpp b/trace_streamer/test/unittest/rawtrace/rawtrace_parser_test.cpp index 583da9533..57d22f76d 100644 --- a/trace_streamer/test/unittest/rawtrace/rawtrace_parser_test.cpp +++ b/trace_streamer/test/unittest/rawtrace/rawtrace_parser_test.cpp @@ -33,7 +33,6 @@ using namespace SysTuning::base; namespace SysTuning { namespace TraceStreamer { -bool ParseTraceFile(TraceStreamerSelector &ts, const std::string &tracePath); constexpr uint64_t PRINTK_VALID_ADDR = 0xffffffc011bdd3ea; class RawTraceParserTest : public ::testing::Test { diff --git a/trace_streamer/test/unittest/table/table_test.cpp b/trace_streamer/test/unittest/table/table_test.cpp index 130edca9f..410a7e503 100644 --- a/trace_streamer/test/unittest/table/table_test.cpp +++ b/trace_streamer/test/unittest/table/table_test.cpp @@ -484,7 +484,7 @@ HWTEST_F(TableTest, HisysEventMeasureTableTest, TestSize.Level1) int32_t type = 1; double numericValue = 0; DataIndex stringValue = stream_.traceDataCache_->GetDataIndex("stringValue"); - HiSysEventMeasureDataRow hiSysEventMeasureDataRow = {ts, nameId, keyId, type, numericValue, stringValue, serial}; + HiSysEventMeasureDataRow hiSysEventMeasureDataRow = {serial, ts, nameId, keyId, type, numericValue, stringValue}; stream_.traceDataCache_->GetHiSysEventMeasureData()->AppendData(hiSysEventMeasureDataRow); EXPECT_EQ(stream_.traceDataCache_->SearchDatabase(sqlSelect, false), 1); } @@ -554,10 +554,10 @@ HWTEST_F(TableTest, IoLatencySampleTableTest, TestSize.Level1) uint64_t filePathId = stream_.traceDataCache_->GetDataIndex("filePathId"); uint64_t durPer4k = 1; - uint64_t callChainId1 = 2; + uint32_t callChainId1 = 2; uint64_t type1 = 2; - uint64_t ipid1 = 2; - uint64_t itid1 = 2; + uint32_t ipid1 = 2; + uint32_t itid1 = 2; uint64_t startTs1 = 1663869224160; uint64_t endTs1 = 1663869424160; uint64_t latencyDur1 = 200; @@ -577,8 +577,8 @@ HWTEST_F(TableTest, IoLatencySampleTableTest, TestSize.Level1) stream_.traceDataCache_->GetHidumpData()->AppendNewHidumpInfo(timestamp1, fps1); EXPECT_EQ(stream_.traceDataCache_->SearchDatabase(sqlSelect1, false), 2); - GetBioLatencySampleDataRow bioLatencySampleDataRow = {CALLCHAIN_ID, TYPE, IPID, ITID, startTs, endTs, - latencyDur, tier, size, blockNumber, filePathId, durPer4k}; + BioLatencySampleDataRow bioLatencySampleDataRow = {CALLCHAIN_ID, TYPE, IPID, ITID, startTs, endTs, + latencyDur, tier, size, blockNumber, filePathId, durPer4k}; stream_.traceDataCache_->GetBioLatencySampleData()->AppendNewData(bioLatencySampleDataRow); bioLatencySampleDataRow = {callChainId1, type1, ipid1, itid1, startTs1, endTs1, latencyDur1, tier1, size1, blockNumber1, filePathId1, durPer4k1}; @@ -748,7 +748,7 @@ HWTEST_F(TableTest, NativeHookTableTest, TestSize.Level1) int64_t memSize = 1; int64_t curMemSize = 1; - uint64_t callChainId1 = 2; + uint32_t callChainId1 = 2; uint32_t ipid1 = 2; uint32_t itid1 = 2; std::string eventType1 = "eventType1"; @@ -785,7 +785,7 @@ HWTEST_F(TableTest, NativeHookFrameTableTest, TestSize.Level1) std::string sqlSelect2 = "select * from native_hook_frame where callchain_id > 1"; std::string sqlSelect3 = "select * from native_hook_frame where symbol_id >= 1"; std::string sqlSelect4 = "select * from native_hook_frame where file_id < 2"; - uint64_t depth = 1; + uint16_t depth = 1; uint64_t ip = 1; DataIndex symbolName = stream_.traceDataCache_->GetDataIndex("symbolName"); DataIndex filePath = stream_.traceDataCache_->GetDataIndex("filePath"); @@ -793,7 +793,7 @@ HWTEST_F(TableTest, NativeHookFrameTableTest, TestSize.Level1) uint64_t symbolOffset = 1; const std::string vaddr = "addr"; - uint64_t depth1 = 2; + uint16_t depth1 = 2; uint64_t ip1 = 2; DataIndex symbolName1 = stream_.traceDataCache_->GetDataIndex("symbolName1"); DataIndex filePath1 = stream_.traceDataCache_->GetDataIndex("filePath1"); @@ -803,8 +803,9 @@ HWTEST_F(TableTest, NativeHookFrameTableTest, TestSize.Level1) NativeHookFrameVaddrRow nativeHookFrameVaddrRow = {CALLCHAIN_ID, depth, ip, symbolName, filePath, offset, symbolOffset, vaddr}; stream_.traceDataCache_->GetNativeHookFrameData()->AppendNewNativeHookFrame(nativeHookFrameVaddrRow); - nativeHookFrameVaddrRow = {CALLCHAIN_ID1, depth1, ip1, symbolName1, filePath1, offset1, symbolOffset1, vaddr1}; - stream_.traceDataCache_->GetNativeHookFrameData()->AppendNewNativeHookFrame(nativeHookFrameVaddrRow); + NativeHookFrameVaddrRow nativeHookFrameVaddrRow2 = {CALLCHAIN_ID1, depth1, ip1, symbolName1, + filePath1, offset1, symbolOffset1, vaddr1}; + stream_.traceDataCache_->GetNativeHookFrameData()->AppendNewNativeHookFrame(nativeHookFrameVaddrRow2); EXPECT_EQ(stream_.traceDataCache_->SearchDatabase(sqlSelect, false), 2); EXPECT_EQ(stream_.traceDataCache_->SearchDatabase(sqlSelect1, false), 1); EXPECT_EQ(stream_.traceDataCache_->SearchDatabase(sqlSelect2, false), 1); @@ -852,14 +853,14 @@ HWTEST_F(TableTest, PerfCallchainTableTest, TestSize.Level1) std::string sqlSelect2 = "select * from perf_callchain where callchain_id > 1"; std::string sqlSelect3 = "select * from perf_callchain where file_id < 1"; std::string sqlSelect4 = "select * from perf_callchain where symbol_id >= 1"; - uint64_t callChainId = stream_.traceDataCache_->GetDataIndex("callChain"); + uint32_t callChainId = stream_.traceDataCache_->GetDataIndex("callChain"); uint32_t depth = 0; uint64_t ip = 123; uint64_t vaddrInFile = 1; uint64_t fileId = stream_.traceDataCache_->GetDataIndex("file"); uint64_t symbolId = stream_.traceDataCache_->GetDataIndex("symbolId"); - uint64_t callChainId1 = 2; + uint32_t callChainId1 = 2; uint32_t depth1 = 1; uint64_t ip1 = 234; uint64_t vaddrInFile1 = 2; @@ -931,18 +932,18 @@ HWTEST_F(TableTest, PerfSampleTableTest, TestSize.Level1) std::string sqlSelect3 = "select * from perf_sample where thread_id < 1"; std::string sqlSelect4 = "select * from perf_sample where event_type_id >= 1"; std::string sqlSelect5 = "select * from perf_sample where cpu_id <= 1"; - uint64_t sampleId = stream_.traceDataCache_->GetDataIndex("type"); - uint64_t timeStamp = 1663869124160; - uint64_t tid = 1; + uint32_t sampleId = stream_.traceDataCache_->GetDataIndex("type"); + uint64_t timestamp = 1663869124160; + uint32_t tid = 1; uint64_t eventCount = 2; uint64_t eventTypeId = 1; uint64_t timestampTrace = 1; uint64_t cpuId = 1; uint64_t threadState = stream_.traceDataCache_->GetDataIndex("threadState"); - uint64_t sampleId1 = stream_.traceDataCache_->GetDataIndex("type1"); + uint32_t sampleId1 = stream_.traceDataCache_->GetDataIndex("type1"); uint64_t timestamp1 = 1663869124160; - uint64_t tid1 = 2; + uint32_t tid1 = 2; uint64_t eventCount1 = 3; uint64_t eventTypeId1 = 2; uint64_t timestampTrace1 = 2; @@ -1096,16 +1097,16 @@ HWTEST_F(TableTest, SchedSliceTest, TestSize.Level1) uint64_t ts = 1663869124160; uint64_t dur = 200; uint64_t cpu = 1; - uint64_t internalTid = 1; + uint32_t internalTid = 1; uint64_t endState = 1; - uint64_t priority = 1; + int32_t priority = 1; uint64_t ts1 = 1663869224160; uint64_t dur1 = 200; uint64_t cpu1 = 2; - uint64_t internalTid1 = 2; + uint32_t internalTid1 = 2; uint64_t endState1 = 2; - uint64_t priority1 = 2; + int32_t priority1 = 2; SchedSliceRow schedSliceRow = {ts, dur, cpu, internalTid, endState, priority}; SchedSliceRow schedSliceRow1 = {ts1, dur1, cpu1, internalTid1, endState1, priority1}; @@ -1165,8 +1166,7 @@ HWTEST_F(TableTest, StatTableTest, TestSize.Level1) { TS_LOGI("test31-37"); std::string sqlSelect = "select * from stat"; - stream_.traceDataCache_->GetStatAndInfo(); - EXPECT_EQ(stream_.traceDataCache_->SearchDatabase(sqlSelect, false), 460); + EXPECT_EQ(stream_.traceDataCache_->SearchDatabase(sqlSelect, false), TRACE_EVENT_MAX * STAT_EVENT_MAX); } /** * @tc.name: SymbolsTableTest -- Gitee From 9da94a05dc48325784626bc70f9c826aad89c9b7 Mon Sep 17 00:00:00 2001 From: liufei Date: Fri, 29 Nov 2024 11:55:34 +0800 Subject: [PATCH 24/38] =?UTF-8?q?fix:=E8=AF=8A=E6=96=AD=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E6=A0=B7=E5=BC=8F=E4=BC=98=E5=8C=96=EF=BC=8C?= =?UTF-8?q?=E8=AF=8A=E6=96=AD=E5=BC=82=E5=B8=B8=E6=8F=90=E7=A4=BA=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liufei --- .../trace/component/SpAiAnalysisPage.html.ts | 6 +- ide/src/trace/component/SpAiAnalysisPage.ts | 116 +++++++++++------- .../trace/component/SpSystemTrace.event.ts | 6 +- ide/src/trace/component/SpSystemTrace.ts | 16 +-- 4 files changed, 86 insertions(+), 58 deletions(-) diff --git a/ide/src/trace/component/SpAiAnalysisPage.html.ts b/ide/src/trace/component/SpAiAnalysisPage.html.ts index ef10bdd7c..24ede5f22 100644 --- a/ide/src/trace/component/SpAiAnalysisPage.html.ts +++ b/ide/src/trace/component/SpAiAnalysisPage.html.ts @@ -74,12 +74,12 @@ export const SpAiAnalysisPageHtml = ` 诊断报告 - + - + - +
diff --git a/ide/src/trace/component/SpAiAnalysisPage.ts b/ide/src/trace/component/SpAiAnalysisPage.ts index fd9b7febf..05d3b7a1d 100644 --- a/ide/src/trace/component/SpAiAnalysisPage.ts +++ b/ide/src/trace/component/SpAiAnalysisPage.ts @@ -51,6 +51,10 @@ export class SpAiAnalysisPage extends BaseElement { private startTimeEl: HTMLSpanElement | null | undefined; private endTimeEl: HTMLSpanElement | null | undefined; private contentsTable: LitTable | null | undefined; + private chatBar: HTMLDivElement | null | undefined; + private reportDetails: HTMLDivElement | null | undefined; + private showPageFlag: string = 'chat'; + private tipContentArr: Array = []; private question: string = ''; private token: string = ''; // 是否点击了新建聊天 @@ -81,12 +85,12 @@ export class SpAiAnalysisPage extends BaseElement { typographer: true }); let aiAssistant = document.querySelector('body > sp-application')!.shadowRoot!.querySelector('#sp-ai-analysis'); - let chatBar = this.shadowRoot?.querySelector('.chatBar'); + this.chatBar = this.shadowRoot?.querySelector('.chatBar'); let closeBtn = document.querySelector('body > sp-application')!.shadowRoot!.querySelector('#sp-ai-analysis')!.shadowRoot!.querySelector('div.rightTabBar > lit-icon')!.shadowRoot!.querySelector('#icon'); this.askQuestion = this.shadowRoot?.querySelector('.ask_question'); this.reportBar = this.shadowRoot?.querySelector('.report'); this.q_a_window = this.shadowRoot?.querySelector('.q_a_window'); - let reportDetails = this.shadowRoot?.querySelector('.report_details'); + this.reportDetails = this.shadowRoot?.querySelector('.report_details'); this.contentWindow = this.shadowRoot?.querySelector('.ask_question'); this.tipsContainer = this.shadowRoot?.querySelector('.tipsContainer'); this.inputEl = this.shadowRoot?.querySelector('.inputText'); @@ -110,6 +114,57 @@ export class SpAiAnalysisPage extends BaseElement { this.endTimeEl = this.shadowRoot?.querySelector('.endTime'); this.endTimeEl!.innerHTML = getTimeString(TraceRow.range?.endNS!); + let rightBarGroup: any = [ + { + barName: '聊天', + barEl: this.chatBar, + imgEl: this.chatImg, + barFlag: 'chat', + img: 'img/talk.png', + activeImg: 'img/talk_active.png', + showPage: this.askQuestion, + isMustLoadedTrace: false + }, + { + barName: '诊断', + barEl: this.reportBar, + imgEl: this.reportImg, + barFlag: 'detect', + img: 'img/report.png', + activeImg: 'img/report_active.png', + showPage: this.reportDetails, + isMustLoadedTrace: true + } + ] + + // 给右边栏添加点击事件 + rightBarGroup.forEach((barItem: any, index: number) => { + barItem.barEl.addEventListener('click', (ev: Event) => { + if (barItem.isMustLoadedTrace && !SpApplication.isTraceLoaded) { + let importTraceTips = '请先导入trace,再使用诊断功能'; + this.tipContentArr = ['chat']; + this.abnormalPageTips(importTraceTips, '', 4000, this.tipContentArr); + return; + } + // this.tipsContent!.style.display = this.isNodata && barItem.barFlag === 'detect' ? 'flex' : 'none'; + this.tipsContainer!.style.display = 'none'; + this.showPageFlag = barItem.barFlag; + barItem.imgEl.src = barItem.activeImg; + barItem.barEl.classList.add('active'); + barItem.showPage.style.display = 'block'; + if (this.tipContentArr.indexOf(barItem.barFlag) > -1) { + this.tipsContainer!.style.display = 'flex'; + } + for (let i = 0; i < rightBarGroup.length; i++) { + if (i !== index) { + rightBarGroup[i].barEl.classList.remove('active'); + rightBarGroup[i].imgEl.src = rightBarGroup[i].img; + rightBarGroup[i].showPage.style.display = 'none'; + } + } + }) + }) + // 发送消息图标点击事件 this.sendImg?.addEventListener('click', () => { this.sendMessage(); @@ -228,38 +283,6 @@ export class SpAiAnalysisPage extends BaseElement { this.draftList?.appendChild(this.loadingItem!); }); - // 侧边栏诊断点击事件 *************优化,考虑多个按钮 - this.reportBar!.addEventListener('click', () => { - if (!SpApplication.isTraceLoaded) { - let importTraceTips = '请先导入trace,再使用诊断功能'; - this.abnormalPageTips(importTraceTips, '', 4000); - return; - } - this.reportImg!.src = 'img/report_active.png'; - this.chatImg!.src = 'img/talk.png'; - this.reportBar!.classList.add('active'); - chatBar!.classList.remove('active'); - //@ts-ignore - this.askQuestion!.style.display = 'none'; - //@ts-ignore - reportDetails!.style.display = 'block'; - this.tipsContent!.style.display = this.isNodata ? 'flex' : 'none'; - this.tipsContainer!.style.display = 'none'; - }); - - // 侧边栏聊天点击事件 - chatBar!.addEventListener('click', () => { - this.reportImg!.src = 'img/report.png'; - this.chatImg!.src = 'img/talk_active.png'; - this.reportBar!.classList.remove('active'); - chatBar!.classList.add('active'); - //@ts-ignore - this.askQuestion!.style.display = 'block'; - //@ts-ignore - reportDetails!.style.display = 'none'; - this.tipsContainer!.style.display = 'none'; - }); - // 监听表格目录row点击事件,跳转至对应问题行 this.contentsTable!.addEventListener('row-click', (evt) => { // @ts-ignore @@ -490,13 +513,14 @@ export class SpAiAnalysisPage extends BaseElement { // @ts-ignore return { ...item, id: index + 1 }; }); + let tbody = this.contentsTable!.shadowRoot!.querySelector('.table') as HTMLElement; + tbody.style.height = 30 + 25 * source.length + 'px'; + tbody.style.maxHeight = TBODY_HEIGHT + 'px'; this.contentsTable!.recycleDataSource = source; } connectedCallback(): void { super.connectedCallback(); - let tbody = this.contentsTable!.shadowRoot!.querySelector('.table') as HTMLElement; - tbody.style.height = TBODY_HEIGHT + 'px'; } async getToken(isChat?: boolean): Promise { @@ -512,13 +536,12 @@ export class SpAiAnalysisPage extends BaseElement { } //控制页面异常场景的显示 - abnormalPageTips(tipStr: string, imgSrc: string, setTimeoutTime: number): void { + abnormalPageTips(tipStr: string, imgSrc: string, setTimeoutTime: number, flag: Array): void { // 清除延时器,防止弹窗重叠互相影响 if (this.timerId) { // @ts-ignore clearTimeout(this.timerId); } - this.tipsContainer!.style.display = 'flex'; this.tipsContainer!.innerHTML = ''; if (imgSrc !== '') { let mixedTipsBox = document.createElement('div'); @@ -531,15 +554,20 @@ export class SpAiAnalysisPage extends BaseElement { mixedTipsBox.appendChild(mixedImg); mixedTipsBox.appendChild(mixedText); this.tipsContainer!.appendChild(mixedTipsBox); + this.tipsContainer!.style.display = 'none'; } else { let textTipsBox = document.createElement('div'); textTipsBox.className = 'textTips'; textTipsBox!.innerHTML = tipStr; this.tipsContainer!.appendChild(textTipsBox); } + if (flag.indexOf(this.showPageFlag) > -1) { + this.tipsContainer!.style.display = 'flex'; + } if (setTimeoutTime) { setTimeout(() => { this.timerId = this.tipsContainer!.style.display = 'none'; + this.tipContentArr = []; }, setTimeoutTime); } } @@ -655,7 +683,8 @@ export class SpAiAnalysisPage extends BaseElement { let textStr = '服务异常,请重新导trace!'; let imgsrc = 'img/no-report.png'; this.tipsContent!.style.display = 'none'; - this.abnormalPageTips(textStr, imgsrc, 0); + this.tipContentArr = ['detect']; + this.abnormalPageTips(textStr, imgsrc, 0, this.tipContentArr); this.draftBtn!.style.display = 'inline-block'; } if (this.isJsonString(jsonRes.resultMessage)) { @@ -668,9 +697,11 @@ export class SpAiAnalysisPage extends BaseElement { let textStr = '当前未诊断出问题'; let imgsrc = 'img/no-report.png'; this.tipsContent!.style.display = 'none'; - this.abnormalPageTips(textStr, imgsrc, 0); + this.tipContentArr = ['detect']; + this.abnormalPageTips(textStr, imgsrc, 0, this.tipContentArr); this.draftBtn!.style.display = 'inline-block'; } else { + this.tipContentArr = []; SpStatisticsHttpUtil.generalRecord('AI_statistic', 'large_model_detect', ['1']); this.isNodata = false; // 整理数据,渲染数据 @@ -684,8 +715,9 @@ export class SpAiAnalysisPage extends BaseElement { eventCallBack = async (result: string) => { this.draftList!.innerHTML = ''; this.tipsContent!.style.display = 'flex'; + this.tipContentArr = ['detect']; // @ts-ignore - this.abnormalPageTips(this.getStatusesPrompt()[result].prompt, '', 4000); + this.abnormalPageTips(this.getStatusesPrompt()[result].prompt, '', 4000, ['detect']); this.draftBtn!.style.display = 'inline-block'; } @@ -753,7 +785,7 @@ export class SpAiAnalysisPage extends BaseElement { prompt: '扩展程序已完成升级,重启中,请稍后再试!' }, // 重连 upgradeFailed: { - prompt: '刷新页面触发升级,或卸载扩展程序重装!' + prompt: '刷新页面触发升级,或卸载扩展程序重装!' },// 重连 } } diff --git a/ide/src/trace/component/SpSystemTrace.event.ts b/ide/src/trace/component/SpSystemTrace.event.ts index 27fd47bdb..f86ac41f8 100644 --- a/ide/src/trace/component/SpSystemTrace.event.ts +++ b/ide/src/trace/component/SpSystemTrace.event.ts @@ -418,11 +418,7 @@ function allStructOnClick(clickRowType: string, sp: SpSystemTrace, row?: TraceRo } }) .catch((e): void => { }); - // @ts-ignore - if (entry && entry.dur && (entry.startTime! || entry.startTs)) { - // @ts-ignore - SpAiAnalysisPage.selectChangeListener(entry.startTime || entry.startTs, (entry.startTime! || entry.startTs) + entry.dur); - } + SpAiAnalysisPage.selectChangeListener(TraceRow.range?.startNS!, TraceRow.range?.endNS!); } export default function spSystemTraceOnClickHandler( sp: SpSystemTrace, diff --git a/ide/src/trace/component/SpSystemTrace.ts b/ide/src/trace/component/SpSystemTrace.ts index de0981083..8a3481eed 100644 --- a/ide/src/trace/component/SpSystemTrace.ts +++ b/ide/src/trace/component/SpSystemTrace.ts @@ -599,7 +599,7 @@ export class SpSystemTrace extends BaseElement { ) ); } - if (!TraceRow.rangeSelectObject && !this.isSlectStruct()) { + if (!TraceRow.rangeSelectObject) { SpAiAnalysisPage.selectChangeListener(TraceRow.range?.startNS!, TraceRow.range?.endNS!); } //在rowsEL显示范围内的 trace-row组件将收到时间区间变化通知 @@ -1105,7 +1105,7 @@ export class SpSystemTrace extends BaseElement { // 一直按着回车键的时候执行搜索功能 continueSearch = (ev: KeyboardEvent): void => { - if(!this.keyboardEnable){ + if (!this.keyboardEnable) { return; } if (ev.key === 'Enter') { @@ -1657,14 +1657,14 @@ export class SpSystemTrace extends BaseElement { let startParentRow = startRow ? this.shadowRoot?.querySelector>(`trace-row[row-id='${startRow.rowParentId}'][folder]`) : this.shadowRoot?.querySelector>( `trace-row[row-id='${pid}'][folder]` ); - if (startParentRow && startParentRow.expansion){ - let filterRow = startParentRow?.childrenList.filter((item)=>item.rowId === tid)[0]; - !filterRow && startParentRow?.childrenList.forEach((i)=>{ - if(i.rowId === 'sameThreadProcess'){// @ts-ignore - filterRow = startParentRow?.childrenList.concat(i.childrenList).filter((item)=>item.rowId === String(tid))[0]; + if (startParentRow && startParentRow.expansion) { + let filterRow = startParentRow?.childrenList.filter((item) => item.rowId === tid)[0]; + !filterRow && startParentRow?.childrenList.forEach((i) => { + if (i.rowId === 'sameThreadProcess') {// @ts-ignore + filterRow = startParentRow?.childrenList.concat(i.childrenList).filter((item) => item.rowId === String(tid))[0]; // @ts-ignore startParentRow = filterRow!.parentRowEl!; - } + } }); } const expansionFlag = this.collectionHasThread(startRow); -- Gitee From 96e5fa3622977c769a6dfde92b1b57c7f8616dea Mon Sep 17 00:00:00 2001 From: zhangyan Date: Fri, 29 Nov 2024 13:52:55 +0800 Subject: [PATCH 25/38] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9getbusytime?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E4=B8=8D=E5=87=86=E7=A1=AE=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhangyan --- .../trace/sheet/cpu/TabPaneFrequencySample.ts | 2 +- ide/src/trace/database/StateBusyTimeWorker.ts | 44 +++++++------------ 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/ide/src/trace/component/trace/sheet/cpu/TabPaneFrequencySample.ts b/ide/src/trace/component/trace/sheet/cpu/TabPaneFrequencySample.ts index 2624391df..5ebfd694c 100644 --- a/ide/src/trace/component/trace/sheet/cpu/TabPaneFrequencySample.ts +++ b/ide/src/trace/component/trace/sheet/cpu/TabPaneFrequencySample.ts @@ -281,7 +281,7 @@ export class TabPaneFrequencySample extends BaseElement { return; } // @ts-ignore - let includeData = initFreqResult.findIndex((a) => a.ts >= leftStartNs); + let includeData = initFreqResult.findIndex((a) => a.ts > leftStartNs); if (includeData !== 0) { initFreqResult = initFreqResult.slice( includeData === -1 ? initFreqResult.length - 1 : includeData - 1, diff --git a/ide/src/trace/database/StateBusyTimeWorker.ts b/ide/src/trace/database/StateBusyTimeWorker.ts index 51f62eec3..6dc26e63b 100644 --- a/ide/src/trace/database/StateBusyTimeWorker.ts +++ b/ide/src/trace/database/StateBusyTimeWorker.ts @@ -20,37 +20,27 @@ function getBusyTime( leftStartNs: number, rightEndNs: number ): void { - if (initFreqResult.length === 0) { + if (initFreqResult.length === 0 || initStateResult.length === 0) { return; } - if (initStateResult.length === 0) { - return; - } - //处理被框选的freq的第一个数据 - //@ts-ignore - let includeData = initFreqResult.findIndex((a) => a.ts >= leftStartNs); - if (includeData !== 0) { - initFreqResult = initFreqResult.slice( - includeData === -1 ? initFreqResult.length - 1 : includeData - 1, - initFreqResult.length - ); - } - //@ts-ignore - let startNS = includeData === 0 ? initFreqResult[0].ts : leftStartNs; - //处理对应的state泳道被框选的第一个数据 - //@ts-ignore - let includeStateData = initStateResult.findIndex((a) => a.ts >= startNS); - if (includeStateData !== 0) { - initStateResult = initStateResult.slice( - includeStateData === -1 ? initStateResult.length - 1 : includeStateData - 1, - initStateResult.length - ); - } - //@ts-ignore - if (initStateResult[0].ts < startNS && includeStateData !== 0 && includeStateData !== -1) { + let handle = (result: Array, startNS: number): Array => { //@ts-ignore - initStateResult[0].ts = startNS; + let firstDataIndex = result.findIndex((a) => a.ts > startNS); + if (firstDataIndex !== 0) { + result = result.slice( + firstDataIndex === -1 ? result.length - 1 : firstDataIndex - 1, + result.length + ); + // @ts-ignore + result[0].ts = startNS; + } + return result } + + // @ts-ignore + let startNS = Math.max(initFreqResult[0].ts, initStateResult[0].ts, leftStartNs); + initFreqResult = handle(initFreqResult, startNS); + initStateResult = handle(initStateResult, startNS); //处理被框选的freq最后一个数据 //@ts-ignore if (initFreqResult[initFreqResult.length - 1].ts !== rightEndNs) { -- Gitee From bf913956ba9df89bb3ab21ac4d27e9cd8d1061c2 Mon Sep 17 00:00:00 2001 From: liufei Date: Fri, 29 Nov 2024 16:47:07 +0800 Subject: [PATCH 26/38] =?UTF-8?q?fix:call=20info=20Tab=E9=A1=B5=E6=94=AF?= =?UTF-8?q?=E6=8C=81so=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liufei --- ide/src/base-ui/select/LitSelect.ts | 103 ++++++++++-------- .../component/trace/sheet/TabPaneFilter.ts | 2 +- 2 files changed, 56 insertions(+), 49 deletions(-) diff --git a/ide/src/base-ui/select/LitSelect.ts b/ide/src/base-ui/select/LitSelect.ts index da9d3e108..61311ebed 100644 --- a/ide/src/base-ui/select/LitSelect.ts +++ b/ide/src/base-ui/select/LitSelect.ts @@ -459,6 +459,13 @@ export class LitSelect extends BaseElement { if (this.hasAttribute('disabled')) { return; } + // @ts-ignore + if (this.selectInputEl.value.length > 0) { + // @ts-ignore + this.selectInputEl.placeholder = this.selectInputEl.value; + // @ts-ignore + this.selectInputEl.value = ''; + } if (this.hasAttribute('show-search')) { // @ts-ignore this.selectSearchEl.style.display = 'flex'; // @ts-ignore @@ -475,57 +482,57 @@ export class LitSelect extends BaseElement { // @ts-ignore this.selectInputEl.onkeydown = (ev: KeyboardEvent): void => { ev.stopPropagation(); - if (this.hasAttribute('tabselect')) { - // @ts-ignore - this.selectInputEl.readOnly = true; - } else { - // @ts-ignore - if (ev.key === 'Backspace') { - if (this.isMultiple()) { - // @ts-ignore - let tag = this.selectMultipleRootEl.lastElementChild.previousElementSibling; - if (tag) { - this.querySelector(`lit-select-option[value=${tag.value}]`)?.removeAttribute('selected'); - tag.remove(); - if (this.shadowRoot!.querySelectorAll('.tag').length === 0) { - // @ts-ignore - this.selectInputEl.style.width = 'auto'; // @ts-ignore - this.selectInputEl.placeholder = this.defaultPlaceholder; - } - } - } else { - this.clear(); - this.dispatchEvent(new CustomEvent('onClear', { detail: ev })); //向外派发清理事件 - } // @ts-ignore - } else if (ev.key === 'Enter') { - if (!this.canInsert) { - let filter = [...this.querySelectorAll('lit-select-option')].filter( - // @ts-ignore - (a: unknown) => a.style.display !== 'none' - ); - if (filter.length > 0) { - // @ts-ignore - this.selectInputEl.value = filter[0].textContent; // @ts-ignore - this.selectInputEl.placeholder = filter[0].textContent; - this.blur(); + if (this.hasAttribute('tabselect')) { + // @ts-ignore + this.selectInputEl.readOnly = true; + } else { + // @ts-ignore + if (ev.key === 'Backspace') { + if (this.isMultiple()) { // @ts-ignore - this.value = filter[0].getAttribute('value'); - this.dispatchEvent( - new CustomEvent('change', { - detail: { - selected: true, - value: filter[0].getAttribute('value'), - text: filter[0].textContent, - }, - }) + let tag = this.selectMultipleRootEl.lastElementChild.previousElementSibling; + if (tag) { + this.querySelector(`lit-select-option[value=${tag.value}]`)?.removeAttribute('selected'); + tag.remove(); + if (this.shadowRoot!.querySelectorAll('.tag').length === 0) { + // @ts-ignore + this.selectInputEl.style.width = 'auto'; // @ts-ignore + this.selectInputEl.placeholder = this.defaultPlaceholder; + } + } + } else { + this.clear(); + this.dispatchEvent(new CustomEvent('onClear', { detail: ev })); //向外派发清理事件 + } // @ts-ignore + } else if (ev.key === 'Enter') { + if (!this.canInsert) { + let filter = [...this.querySelectorAll('lit-select-option')].filter( + // @ts-ignore + (a: unknown) => a.style.display !== 'none' ); - } - }// @ts-ignore - } else if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') { - // @ts-ignore - ev.preventDefault(); + if (filter.length > 0) { + // @ts-ignore + this.selectInputEl.value = filter[0].textContent; // @ts-ignore + this.selectInputEl.placeholder = filter[0].textContent; + this.blur(); + // @ts-ignore + this.value = filter[0].getAttribute('value'); + this.dispatchEvent( + new CustomEvent('change', { + detail: { + selected: true, + value: filter[0].getAttribute('value'), + text: filter[0].textContent, + }, + }) + ); + } + }// @ts-ignore + } else if (ev.key === '0' && ev.target.value.length === 1 && ev.target.value === '0') { + // @ts-ignore + ev.preventDefault(); + } } - } }; } diff --git a/ide/src/trace/component/trace/sheet/TabPaneFilter.ts b/ide/src/trace/component/trace/sheet/TabPaneFilter.ts index 121e104c6..161ce9adf 100644 --- a/ide/src/trace/component/trace/sheet/TabPaneFilter.ts +++ b/ide/src/trace/component/trace/sheet/TabPaneFilter.ts @@ -403,7 +403,7 @@ export class TabPaneFilter extends BaseElement { if (thirdList) { this.setAttribute('third', ''); } - thtml += ``; + thtml += `` if (thirdList) { if (thirdTitle !== '') { thtml += `${thirdTitle}`; -- Gitee From 24591cd8a7aeed94b1d47f03ec8928eb665d04f6 Mon Sep 17 00:00:00 2001 From: wangziyi Date: Tue, 3 Dec 2024 16:08:12 +0800 Subject: [PATCH 27/38] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9appstartup=E5=B8=AE?= =?UTF-8?q?=E5=8A=A9=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: wangziyi --- ide/src/doc/md/quickstart_app_startup.md | 2 +- ide/src/doc/quickstart_app_startup.html | 8 ++++---- ide/src/figures/appstartup/appstartuprow.jpg | Bin 18350 -> 103615 bytes 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ide/src/doc/md/quickstart_app_startup.md b/ide/src/doc/md/quickstart_app_startup.md index f6f20762c..f16c4d98a 100644 --- a/ide/src/doc/md/quickstart_app_startup.md +++ b/ide/src/doc/md/quickstart_app_startup.md @@ -17,7 +17,7 @@ App startup 的泳道图展示: ![GitHub Logo](../../figures/appstartup/appstartuprow.jpg) -应用启动包括六个阶段,分别是 Process Creating(应用进程创建阶段)、Application Launching(加载应用阶段)、UI Ability Launching(加载 UI Ability)、UI Ability OnForeground(应用进入前台)、First Frame - APP Phase(首帧渲染提交-应用)、First Frame - Render Phase(首帧渲染提交-Render Service)。每个阶段的 Slice 上都会显示出该阶段的时延。 +应用启动包括八个阶段,分别是ProcessTouchEvent(处理点击事件阶段)、StartUIAbilityBySCB(SCB发送请求阶段)、LoadAbility(AMS加载Ability阶段) 、Application Launching(加载应用阶段)、UI Ability Launching(加载UI Ability)、UI Ability OnForeground(应用进入前台)、First Frame - APP Phase(首帧渲染提交-应用)、First Frame - Render Phase(首帧渲染提交-Render Service)。每个阶段的Slice上都会显示出该阶段的时延。 ### App startup 泳道图点选功能 diff --git a/ide/src/doc/quickstart_app_startup.html b/ide/src/doc/quickstart_app_startup.html index fc3a13a3e..0d5dc3e95 100644 --- a/ide/src/doc/quickstart_app_startup.html +++ b/ide/src/doc/quickstart_app_startup.html @@ -811,10 +811,10 @@ App startup:配置项的总开关。解析时请打开对应的Flags标记(Dis
GitHub Logo
- 应用启动包括六个阶段,分别是Process Creating(应用进程创建阶段)、Application Launching(加载应用阶段)、UI Ability - Launching(加载UI Ability)、UI Ability OnForeground(应用进入前台)、First Frame - APP - Phase(首帧渲染提交-应用)、First Frame - Render Phase(首帧渲染提交-Render - Service)。每个阶段的Slice上都会显示出该阶段的时延。 + 应用启动包括八个阶段,分别是ProcessTouchEvent(处理点击事件阶段)、StartUIAbilityBySCB(SCB发送请求阶段)、 + LoadAbility(AMS加载Ability阶段) 、Application Launching(加载应用阶段)、UI Ability Launching(加载UI Ability)、 + UI Ability OnForeground(应用进入前台)、First Frame - APP Phase(首帧渲染提交-应用)、First Frame - Render Phase(首帧渲染提交-Render Service)。 + 每个阶段的Slice上都会显示出该阶段的时延。

App startup泳道图点选功能

diff --git a/ide/src/figures/appstartup/appstartuprow.jpg b/ide/src/figures/appstartup/appstartuprow.jpg index fd4352512879725ee74f7271855b3beafd1d6798..26eb17eb85e77351cb9767f8249ef8ac95a74aa9 100644 GIT binary patch literal 103615 zcmeEv2Urxz)^?L5NCqW?lA}lvM8b$jmLMt!2q=goVI)f&6a^$oR>=YaA|N?43QCZi zB}>j}hyxS;)^*pg;qKmV_x|_WZ}m|g?dtBTQ>RXybKdt8pIXanRhk$zk2_Yd7;Q0IL! zGj+I2Omdixo`I2zn}?T=U;KoG~wfuR)YxXqkqIZaB&xEJszb57o(^)%Y>d;AYm{WY5YyIPpK8@)2 z4e0K_HKLye^wW5-g8(TW4me?WQ~(6nW{%$y2i7ccz~4YJt`7;|H-l$S9AXi3ppjiA zfHY$rpDhJn8&7bl^=Zw1c2MlY>BJj^2!MdihjumnzO9{p+VM}f@&0i4w?6`JE56|t z;|!loRT{diO>iN@h+1pAcWrxs5DPFR6V{n=I%GFz8eOrKBAKT3K6=P;w}apv06v2# zr^&|h0cV6ygm={F2X&Q?9sBs-D>N;o(Rc-RXC#l-3A4y=rEMuHB8qB9WGXEmR?79@ zImQ>-_aqq{4&X$_U5{$|bkmRbB0NzRA=MB@AaJmKsauO@(2i>(&Sk*FA;Xx~g&A`< zT|i8~XI52Tz-4hkgVy^ZAM1`+ARYKbvvRqjzJLdEHwM%$KOrkwv==)#Ff3L$ok~DI zPI2=PeU|m0t7LFg^-OFpW!{>C*Qu_;UC|xN# zs&!Q&*j-0mH-w9;nRvh|BeJ-3e@pOvTffBN9df|RRPXAk>ps>`S0Y8T50OL!&J8GE z>E-D@Domo(@XiAHTE;)bgH0Kdes*f2=+yBd4g=j-SSP%rJhNqc(Du#xq^Er$>E4|u z<KUpRS%bQdC^Q()1ZK%Y>kVt zNs2cwDqWy@Ot3XpN+!a7li}5Nwz5pZ$g76OF&ZlpSw_jIjFVGg=R@V5g{F83iq4EJ zQ{CH04`P_%9(2=zts~gA*CkP za?Fi%>wy;$!G|gqcZ=k@+LwY#mnCUXDUM<*va0R@#iIu?ZxhGg7c0p>QeBA& zN*)mxe<`%l%yFXUe5I1%ltSsQ^39omg}hUnHtA05;@PP$RU_3K{MZ?5Jyd{lu6^z|#UC^g!9ke&%MOX8)W_)6f7OtU~ z$3awnfpU-oJPVFGL!n9i3YKp#X=)gZRC8hhb$3P)<>@owtFG@;T&0>Nq$3w~i2(9#1zGTN&S+GTy>zHMA$z~nGjqwYp@Sr9G_1Vp zLu5Q81`9kfh^8|rmz2m~3ZgNn6T5ZWBK+cH;v?>igb}7HL8s}1sV$4Nm}J) zRO7GZd8w~2%U$uuOMLtQCb9J)D&7!Ni3P%GE$d&H!L;fjsMx8Sx*3$(1VU$&L*K1j zbRi>g%k~}dzR6}5%w}qBUU;L3+L_kGNDfW1$C@RVLF5*3LQV(XNHkRyG`x2NZGDZ0 zE31b8p5?pF)jgiQhpIVOp848)QwvK73*Zq#Tk<2v(1m+Cvz{SO1O-`ccUKm|Ka3%n zqO)f2|DdM?fU*cX_mK}B_)#C#_Mt z!mrRNScyFxfzzg|;|otlEzrhA!*kl6M{zL9EPCYC^F1j}si(zZKAV7)LWTgUKPcCy z2jeI&7oZE92IIVYr)CMIJrl4%B1|pR!UJZ=!rJ8E=w^ZixL+2ktS`Jwb{P^s8_nZ$ z$xmc!)NzwF3e!oGfW-#POR(np zeJpT$U?{P!!$U{yZJTFbr+|PJ+D_w~Pco69g-zT+4}U;BU*IK+=xK4#K#6}gP_km8 zCsjpoH8s8-rs!qVdt=#^xSrgpljiNWysTu;pZClWC0jn6_R>$VF!h})^RhtxX;$9| z@1hcyV@=&N%jP>HQG@z7p%x+LZkGn>(2IwMW>kyr%mK_KaYX~A{8{uRIjIJ20%&xr zb;s0^s)U?zz0<^^_3?XR_aP~1BjM>Mv((xPQ8cE-HFL9Cr_Rq>JRHt=N7y{y4bicD z8MV}1(7nFJi!6BG%z2Qbg>LD+)uVjmP&4G|?1E}$WoV+mGJj^&lhU`P5%5LmY0-BT zq+M>I#{DgWZ$nMqnT0gexCHM_Z9ZL%?cOB6xR`Fs1w}n}+N|+Qw{!p%2Ionv&dBIO zjG$-H9ZHUdhm#HwnqJD*_l9g)MDJ#3CMagRj@i=h-`WwovJ#_6)x^lF+d%Rh3uKv6 zWa2*QEikuKk}%BJ9q~Tg-2%UO^^^JhWRh@BQ)S871h0vlrUq4$(pa-^Ejt1z`6f*}53zo#We^@W)=0{(<6bdj|)wz}UEQZ#Il$|B;8g zNlVQd7Pyxxj|Gyelh7DiFN0D9q};zW9o4)_v8(pa9skJczgSBUCuP)AxT-7%s~_?E zP|XUbQ;-=V*@oXP&%;Fbn;_%7Kdl5d-5gZdirJ@?FpdR?m3*;)ya!|-#k*Gwoq{=1 zK-H14qikzYmXJ-lLM#BU4f|^C{P8;`AP+X#gAe3cL_lT>I=8IH8Zk4hqp=&pRWxt* zC>06y3LDur1}wjRxiE^0kpfMygauaDQWtvR;pi6GYT+4^+pya6$ylJnxE+H9NaS)p zj<9a13uC_H|6h4WFlbQX&TN#zYz8LnApJr=i2mBAIcdNG|GNhBt0Hdl)JPG(PG;N^ zi3K!bzE+k=EbueM`(gBT80rv0%-wC+&hGiIRpRH0`rp~UUid-LQI5#$fe({xH3na6 zV%Xng6IqYA9e2jItaoYmD(vs7xxZ>of`i@+QrF}3F1^MAxy@hu2wVH#-dc?=rifQ6^?iFm5NL|C`QkaO|5w8l}^{OHGi?Z`}vT|MJb{*EYxN-rOCr zF3uium&5m`ztLJO@HYui7)?~^ zKj5VQnH$Tmg$|RDaqf<)<*~u-+e2Yn-`L~7&#ua`n{0 zd&u0u0UW=mY zac68xP&_YDS-iujMlKSZG^mQ6w9AoeWv18;;NF-V3rMmLL&t2&^-Rj$=zUVUm3-2G zU(W!I1w^a$A88YWoEkZ{C2gq3DY(ITJv7x32W^vd=zMo7pR%Jn<%P_r?Ha7aYug8I z5}#b0RS`p;@Z9MWfQjw5TfFbaT-u>6P9n67**h_{y`4k)u(s;}b)i>>rW8-tbNc6< z(a6W5DVl1-nH%nVj<3JqNME5W|9AiMAm%8D!o*(00(njFO|dJx&<}N;-b!0oAV^r( zB5+RRRI>^5aNW}rl_?%?k!Ft&A`4zBXq(nV++%s7L-|9@SE8k5+w@1K!_&fpq?rq8 zA~f9jIA4TvudLK}Q}tReW`)QN?H_MK2Mg-i2V>^qJ@@yaBZ*bB^s|R%&pTD8Rp-5J z-6&OeE=4gVtPb)Y-}W#xbX|nS^dlJyn8I@;P+Q>%1p6f`my%Fu zc-syt2a%t{-80#QlJ=}xo%I;q8@7veyH)BAuE;RWp9G}B`tIKO^*x=enR1=$M5*7^j4V$R-v z{?ZA7cQI@ljInc!F-cr2N!jnMN@nzr^pX0}$8u?B^By4|KxL+%KXY;R{M@7sy1p!6 z<5YfNuQ(PUENzX=TUzle%rY{l9%?3a=|oCU<3|_xChT$^X}H$TFt2|hi_L=mU7wxd z@{TLNeT2L5g2<3-o^sv?ITfKiPsQwcoqH->mxcW~@GMQl*XvbG9o0mmPfea~0s-@ev+{fJ_&mK~4ryYQq@@8nB5_j(eJz0@4dvDynQ z8s}9j7}PcPsKgN86uXkRAW>FQ)@^t1zzU?WRrXzx3ZDH`ru=K;5IX{YOX1Mf%fi)zWn7 zBK8oHyG*88O%qqTIvp@7E-7A)nO1V_>gDIMY~0u$=5z^G3OCQcddp84%p?C+zi4&# z>Gj_Dv}`^ncdzXe&|>M%7q?@CWveP}&T|sEpPjU0@M4N^knR^crR`*;HdwB|XOoil zTz%*r3oUd*RUCsTkW7GGb5Lu_(nz%{jW~DM?e$SjE!|fqc?OGPD~aEF@*515>KseU zQiB~z$Q#Ru**D3lcz>68^{TIW?8Typ4ZYCiLHc8cG~&_}M#E@SbYq+HA%|ctzUP`9 zB{{YpSRe*t7?FO0XkWv;?e;c)Bg}GGLCRtZ17}_ zE@6GaLFW{cWmRbFgsHi%o57dXyX`L9Ws+fLZMgXOM7j7(RGdO8ssjrgWaE@jS#b$ZahN<%U=)oxpRaUF6VQ^uUtCJ?hSl<<0KnXzP;kEygD3SLhv+j*X_ ze4$hno?gr?Qx%m5uZ9{Y8ZLX6NDU2l+#&N0D7_@TI6Js`ou@B=+_(EtySkH#lrCG!V)=cQPLmqD8<&{E1wOr9s)TErhgO z^@W0?73-R%UN^V%b)i;Z!;=ebM@h5hZ>auJPCYaTb5z4E&(AI6XGT zl>IBkL;C?vv$xaB!#jQRj^rQN$~zV+O~H6RC?@Mz7%J>cE_Km!)%4zNJ}{`En$X%K zfTK}b>pAeE<72J65QOMn0&E(kJ^mq#8rC5M;yV+t5Vk>j=nRStN!SDWlEM?d7|MSx z{v(8cG~stFhkLBV>r8;sqbXK-YM$Lrbc!Da`Wg#7^wNYQFuW$aXJk~d!0A#D#C?Df zhgn{O$F0Gp*e>Lu;DHqXpDwCMoiYT87jLitBjmGDpvdaSn7k!K9cAU@;82I{?#HH)v3nn2tJ_h@T`_8(z}NY9-Tx&=lw1>}K<}(W z>cf^vB{#e&P`(S!SRi&?9t#jDKOk!C8pB|IZiFG@MC&|g+`sKLt=%)8s_`)M8fCP& z)_Ur`0vT!yq{|)oA&P;o-Mbx$xdR3Hr9U?}Y=57NN7u9-08L%P6nk?&^UP9Ue{&W1 zEftslkVMV;#4u^!@;@V&J5>TNQV-c&)Fs4cm~4dX^nMpQ-vixblgl9dVhLuI*&!JI zUs%jF`B*@%6AOGe3qxJ{?tWhkm6sXDD-|ro2mmPs&%bxj+4mYy`^s404iy$yzxaJT zp|}M@ZiDnUTn;QSk@=nP%C;7?$hNV}hQSB#e|!GBNCh%Z*9Vy$grJDP`vbpUXRun~ z7qIps*e(?ag&+Eb(BS{)n8W@DWA1H7RsM!J|0j?B4RL0V8=1f7A50ZF323o`9&63_ zbEWmk=0oNV|Hh8+S6n>X!XqB#Jdli)W+@jI%WDzvis}OmqTQ&EibUZjna@#`rLUg zzKD}2YZJFhIS-YO4d?Bp9&>}-_lB&|6Q5w-u(1i-Lqaj^LFX|RJHi_WdzNZI%Zy*L z@wl_}Xh>&CMPxi{wfDV?nlgDFPbZIE*U;7m^R-k4SfJqrWJMV9YzP0R^3(%On4B6v zA0jN!o7bU@m-!7`9ELH5d|>PI;rvpm2d3O{rLaIx@z*s_pTYIjOftpDJ2ANF!sb?o`b+!0c5a%q+D$V-wy8|m#r~*$^1*4O&0Jxbf z>M7I>tVpKP?b~3$lC^a#fGHNfHgg0s2m&`S6buXGD8JkQzWpZ-d44=BbyI2|3!trK z{U1?t=fELl^^hU(c{)zEJ@x6v>Xy>`T9`*zU~&v?on$d%zLmtzSDeHH-IH^~jDaB2 zQ~T*tyzPTy=>hO@u<|j(1oZ$5^u1VtE}=m7AH^Q%dp9Pbt&g#`w?MvXkek_Ap*bba zw)PO#orgIKzVN~}>(#IRd45Ue)z>$qzIXEp%3G%27yi`x<7Jaw707%)7GQ^MW&Zr; z7#54x2+n;unfikXXsL#DzVijuZPw99?o`{>W(p+H5Ul??#F%DY6UipR6hLBXCo#(?waPIl(L|B=nX(~IwWvB0Bgzw-y$B@UH@ zH#IS|_$&v1Sfghh-caLH)14cE=ZoeaOI4sKj*Bp9|g;9+E}H49XxN*KBspZ=7T<{ z6RC4&&@j%8>k2gRux=R0GF?+8{#T7S1v zaD6zmwz|A&akzl5_Xw(H6x(Xx&)fQ<)iC1Uw}tP`f%)|B8vlVM`1!@kkiD4CcJm9| zihB|shTiy8rbz(Yav$4kRaoTN%Fb9%$!Rkss%kXaF}vC((*I;7uIL5zFurIsDZ8hn zLea*BmdXe@0sZTbl+O2uCql1Pc(CX+~LKG11k8z zH=c`X0Qr>9moxze@n;SbrwG6;((0ayc1jybWH&0XRXV;)*X2l08X|?-Oj`cr3mYft zTQHj`ohOs;+MDB#t<>zkAm47vWH{n0kt4TSF>W8Yc}<8Kaoi)~ih&zd;p&M0!Cva7(>Ue@5e(a+jTcUm=g-Qxq-dPD#Vp7WY{PKF3iqLl zXDUZ|dVBS&7jvEE%J`!3l-Au9Wn|9?Dz$cZLDk6hMfPZoTO z6p0M{AdB~>nn`>LGukJ?dTnjS^aE6)5+QG{Pxc3E7r`IN+D>Wri(hcI%2Kyx$vw7w zk6Y-Kz=wEN4}`J-o#IT=6sxxe4a|(FPt{;zV#OX$ks`5q|E2PgtK?D0HY=7M z$aX@z9x8>KbYF`#X1cq$%->&Xb*NjQm0Fa*1t1%9h18yGlg-p@HB7D8Um-KzS$JV` z_nm-8b6lE*u+zmd&a1Zc^l2q8_r|6{vc}_cx4m)9xsof&5y7LLS8}h~4q+~o_JEW` zO9KZaEE;~qeXJ!T>$YvkhQrLZ zptt^g;}9&+ue|%!(FC6isD##*?WQ=OjLGr7>%;O50|Va*Q=wJ+JN?tQDA?-f^Dgjw z`#{&qnoS>)V+08yvvZW_7PLQAaF|@A&Wsr?Q1Z0uu3=9 zz7kBnT*ohRNB`2C%ZDXojUt{cN48smpDFI$ET038-6!r0nch@Qswu+)PJ3uCUWOZd zokwq953Ud)tFt|VQK}|-TlTDyzPdrn#UdmsnJI1T^?bd^C3^h;w;tjH5J5L3*|^*7 zv%y>GOM6?mTFb||LMmmdn{_lk)rBQsLAuCLMCD}cZ4$778`C+^)bN<82& zdNDp83%p<(*RyiPr>w7zZwS~kLs%%s|OX)2VZJLoy|zlSv{4Ph#IHV(aV zC$#-x&hTLf(3u7>Mc?gIzqx6Ct5mt?DnqG=+Fc>zr^ydMkuymZs!z{V_4ygAMR~6JFt8GY>b+H!u?x(4F?YE+4kPMiub5MUfg& z=U_G9i2kBid{JVYK=TcgsdMWnP@!>_s3=l_ z2#kY0|HAhj5Q%LKn>x4gSIX@~%McT90>wILDR4+f4aRAbF=C*xS^8^?3H>vZ6PUB@ zY#$fWU!->bS_0hTLBPGfofyWrReQ3lm@`I7P7!L20pX~%=A1+#=}rmE9`HN?F_-z#Q|8Y zWG2je2($n3B^1Ba+ne6ZaFM&LV{EIRdQ1MBFCpo$($k~c0eSq?21}#r@ho*5%pMoB zxCfWcJaMpJGA`*5POb>lwQ=CM^@h-{0X_J%&G+}O>i>iVz0#Ur@BrMI7(Q;^l&#ab zRHrDwO2E_Pf6bQtZyQluS8<#N6U5OcSdK|uue{7@<@vVQNcK_$$PC+%XfBHbHw5<8d*EKrB41ye(pgS5LQLPtkrcATS0XGzaQ1~0XpB$Q!& zPn&8X;8ePBS0i&Q2P~Ia++c^&4F+{nN{p%vdnFV)J+&Pe3O_7$WvB6EQ$rR(D^wQD z-Xu>Up;MCjeZrhw#d`V0a*7423}rOS4^J{jU2(u!En_9eX%Op_?8U&d)F(=zi1N~D zq29m-4po2ohzL_-qRa5y2`NcQbUaZxGJE^@1L%Wd7IeKRMK-OnB2U1iWJoxOzLBQ*TbomOuuOK9Y`~t4~tVr4sC#G z(1Rv)1|y>ij7(-^Z)z@j*hRG_-ZGH6Ma-Ip5G6Dffv&)}5Ln=76QXo#d5_AfKcc)F zzwnB*s+Uta4JFY9Zah+!S2(XG=~<&i1|TbA2;x=Y^}~WFMf+PZlGO9H0)_*g(J+W( zwxe$;RG~E2art6uPGvTR?!YpP2U=gp^mhB{<{Proh*CC-n$&_ONKlGIoR0p98Yrim`shZKrC-q-TDYdnWKULlRzt)qJ0)Cjmk! zwgi3_?J0o?>{QhE5hoMRUdbLK0HVmcaQV~ZKjfJ|V->id>u-RZ02QXCLpZIxWFm}~}{{TstS-}xvi!gN<8t6N}JB^T<2QvyVjvK|N z{zb6-2fB_!=XjddnM`hCVWDfGZ{{Rd@iFDc#Nmdw8csK(<_x5fh6q@)?4_zzFwOC( zxth`F{Lvtt3e{_;-%k2*(7VbAI5aTHe{kOzqIIr7y1DtQLlzsK^)WtQbWs$>QzeT` z9j-3F{&3Zp{s8l2BE5s`$>*9YTGeo)2R15sCfyG0+jz=kl|!R9tNknvviO2C{8!V8*UJwnoxB@Oh-AhW#@m&5~|(x$fRTvtW%@lkaPz$892*KCijz zgbss+mSaxMmA>YZLuPck5n`*y&6yk?Dvi7frC?4>nA6pPN5h2?2jW8R6?d(?$OyMx zjlMbcsCDX*!-8rvuh;w6QZiXGiSCp_)LO~1hBjFS8+H9+Lu2|w3ug|h_gOe2Bu^DO z*V*CWBjg#T5Yk))S{cY;FGC~PIGn>y95H#Z&BaGdqp~vc(7On+Zk2(Ig~N&7-h+sm zEiSin5dI zSFb2YR4lEzR2X3<8F8&Si$^eJ#|(c8k%n-aJ7d6^blSb7|ANlL+OHBjc>IIYPH&%# zP8>|yLa5|1Q}AA0X-5H*O!@8Uz_}A~I`-2hU{+d_28|9MH1YXSM(oScd2ih!aUSI+&YQk~j#cAYo; zxLPlFWch$a1|jIu(@MWX#Qc6Z4{>F~%AQ-ds16%u6cqTh-^vX4yVTZ#P{B*yVvp!e zANJsGstSP3M!?3{eJI%3kW)!JjmaeKC!SiLe@{;sC#b0~GvcC6X6fXuTQ4g$#_az`GBd zL9y4im&xkd;AWOpk0oxX_)xr(GCALFVy3L_Z%E@ti3Qq0x_pfyzZ;4Cdi0St#>zkIgOhntnH z`*r)C9^2-=BuHgCP2O#GZe-{rHTnrm$f(Ee$kVc>)sXk?#emUQB9Qsn6c8eU!S_DQ?KdR3!cIW4J|8_G5aS@_N0T0=B6amPe+UcKOixWg@ zY!k+JTm;4L!}gPS{~Tq(5oO0Ivhlt?d|Zd`Fw zy%RQ0`@c{%!xgB;9z8@gfJ^qz)&2wOqzX6Ke~ zQJ_S54Qb$+Hr%_XD1^ufC$wgDO0O#}pkZeXzG(8(y5G0(|4K8&xORpr;K3<2P+Xjp zk9rV!KF5IKDYdfaZ3$pxMTB%J2-Y@LwYwyb^F7nsn`z6dS~DBFwp7azGlQ zfdzzQDLsbUhKdHaN|6Jo2J4R7?oje`dp9L?6lfn-7V(ngwTWt;Y#W}fIn`_ZT(KFx zwMUa0RBC}d9J8a@?K~UXc9%(iiR18FB4X}7Xtc;{$njUK#qqi0;yds;-Pj;Q8)Pas z-*S4#X0||jfue-c*@$h9BWCe8&o(yi`zFb^8W{~4QEv^cVJh)WCdOv__f{8=j-9J6E#)JKI#n`oF~xH7UMA-ue9ySBBb3LrtOF_$Qf}xS7fkhm zO~=yMdDyFmLvP6+R}3QhCPp=s-aj9xHM>vosC;PS_-ycpt#*$kq4 z!jH~mvD|6IcTG!}WVOx3tRl(`x;!ffymo9>3&c@UP$=_`=)l%PJT`gnjM&IW1n$mHXc9B&-ybRc70B{{fMRC*iMn}@obJ}6O=Xw#uGLX8@~m!7 zY2ylI1niKGT(?Nt%)|mnMjv4-#hB5vmnBppsE$V%wV3!u*tf|O&IbaO{{m+W9d6#X zd>NqhI=Fmu?}Eo2?k~t|+83nnU&7V@S-6SeF$m9CMKNIICjcP`i)2x0%GbsKVG~+8pX*^e>UCRv_ z1EG_@=w&#;<{M_9&^}`|f05M493|@C@hE(R0s#fdn6k5TKY-EFzaO^xBfO3Se6g*t zaIuDu$n9UHcmG+{zOH1FM3e-QuKy3|b|`OJplZT-)yF#}CMkn-p|Z3(YPD+poIkC) z`aeSO!4nfs+M$t#)p&VF0jSo$gb&0xV;d5s>Hm@c0{B6NZawJu!4|pvMYiaI@Dnh{ zViEQcTKxm|fu=l}#I@pH%+7HGgI9$knhep#8iCKtnnhl7WU^3j-_PD81and+r1PB* z1s^_NJs#8=ZS0U=U*0fEqS`c%*d#nTuOToxB;21|QoM(`M>avsC?HQ3x*rteUvm42 z(NiAnH`6a2pb2fb-8=%C;ydT~ReyU8KWw*gPEwZ?x(%WQ)tk`24K8sPDcgACV zUV@7|oiljrwcqU{wy(F^uk>ts>!P3-1^17s{gjv*zQ*eFWG@hdLvg_dMh`AUF;i-G z7W+SgFuLf%pP<}>jO~moFf2{e>s(cO{Sq_mW_LS=AC%tO1)`aqUtiemvPwTTKeUIO zqp~X7hV>((S7r2B(n4s`pKIW&bFR1E&VR{iVT~*27`CIV>BawUwZMhiRlW5}rI|~t z!AMic46p6gaQR3+KjFJ{%&byUvteq2T@hX^aD3*bvf^HobzaryV*}QV!{I|rnL-RK zzU_skNNW{&-^iZHJF6ECm(zHS%>-JN>${-!9VYe5_w9@FUmD&O(w%jRM4I7;m#!q< z-gBN0HN5g%10Alo;V_hkqS#E2KmS6Cl-Lv~_{h+E8i1Am#K&`x3&{bDlSB}MuA4y=pGrMkjoZ8+Z|TXrSg z(Uw-kO}C4FiW@fS8=5Xsn|RByJ)JX;^{QRlTdy+<+rrcXbyjQ%GY0R@LHSe=401O< z?B994xFw!iRb!H=rO2j-Bn`W??^$s+o%>FD;`Q5o&?r#>T&A%2> |URl}3XdzfL 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

y4yMwWyfK9`Kruv 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 literal 18350 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 -- Gitee From 6b71f8804994932147b9bd43974e15c27269439e Mon Sep 17 00:00:00 2001 From: liufei Date: Wed, 4 Dec 2024 15:11:59 +0800 Subject: [PATCH 28/38] =?UTF-8?q?hiperf=E8=8E=B7=E5=8F=96perfAnalysis?= =?UTF-8?q?=E9=A1=B5=E8=8E=B7=E5=8F=96=E6=9C=80=E5=BA=95=E5=B1=82=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E6=A0=88vaddr=5Fin=5Ffile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liufei --- .../logic-worker/ProcedureLogicWorkerPerf.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts index e3279c731..f8bec5e69 100644 --- a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts +++ b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts @@ -377,6 +377,7 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { `select c.name, c.callchain_id as sampleId, c.vaddr_in_file as vaddrInFile, + c.offset_to_vaddr as offsetToVaddr, c.file_id as fileId, c.depth, c.symbol_id as symbolId @@ -1054,6 +1055,9 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { ) { let analysisSample = new PerfAnalysisSample( threadName, + lastCallChain.depth, + lastCallChain.vaddrInFile, + lastCallChain.offsetToVaddr, processName, lastCallChain.fileId, lastCallChain.fileName, @@ -1243,6 +1247,7 @@ export class PerfCallChain { sampleId: number = 0; callChainId: number = 0; vaddrInFile: number = 0; + offsetToVaddr:number = 0; tid: number = 0; pid: number = 0; name: number | string = 0; @@ -1314,6 +1319,7 @@ export class PerfCallChainMerageData extends ChartStruct { initChildren: PerfCallChainMerageData[] = []; type: number = 0; vaddrInFile: number = 0; + offsetToVaddr:number = 0; isSelected: boolean = false; searchShow: boolean = true; isSearch: boolean = false; @@ -1365,6 +1371,7 @@ export class PerfCallChainMerageData extends ChartStruct { currentNode.tid = sample.tid; currentNode.libName = callChain.fileName; currentNode.vaddrInFile = callChain.vaddrInFile; + currentNode.offsetToVaddr = callChain.offsetToVaddr; currentNode.lib = callChain.fileName; currentNode.addr = `${'0x'}${callChain.vaddrInFile.toString(16)}`; currentNode.canCharge = callChain.canCharge; @@ -1412,6 +1419,9 @@ export class PerfCmdLine { class PerfAnalysisSample extends PerfCountSample { threadName: string; + depth:number; + vaddr_in_file:number; + offset_to_vaddr:number; processName: string; libId: number; libName: string; @@ -1420,6 +1430,9 @@ class PerfAnalysisSample extends PerfCountSample { constructor( threadName: string, + depth:number, + vaddr_in_file:number, + offset_to_vaddr:number, processName: string, libId: number, libName: string, @@ -1428,6 +1441,9 @@ class PerfAnalysisSample extends PerfCountSample { ) { super(); this.threadName = threadName; + this.depth = depth; + this.vaddr_in_file = vaddr_in_file; + this.offset_to_vaddr = offset_to_vaddr; this.processName = processName; this.libId = libId; this.libName = libName; -- Gitee From 5a198f4d057d3a54856a0391666165d7dfb19b1b Mon Sep 17 00:00:00 2001 From: z30061262 Date: Thu, 5 Dec 2024 09:31:09 +0800 Subject: [PATCH 29/38] =?UTF-8?q?so=E3=80=81an=E6=96=87=E4=BB=B6=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=97=B6=E5=8F=91=E9=80=81=E5=88=B0=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhuheng --- ide/src/trace/database/SqlLiteWorker.ts | 102 +++++++++++++++++++++++- ide/src/trace/database/TraceWorker.ts | 30 +++++++ ide/src/webSocket/Constants.ts | 5 ++ ide/src/webSocket/WebSocketManager.ts | 21 ++++- 4 files changed, 153 insertions(+), 5 deletions(-) diff --git a/ide/src/trace/database/SqlLiteWorker.ts b/ide/src/trace/database/SqlLiteWorker.ts index 29506f47a..3560a0e22 100644 --- a/ide/src/trace/database/SqlLiteWorker.ts +++ b/ide/src/trace/database/SqlLiteWorker.ts @@ -13,14 +13,21 @@ * limitations under the License. */ +import {WebSocketManager} from "../../webSocket/WebSocketManager"; + importScripts('sql-wasm.js'); // @ts-ignore import { temp_init_sql_list } from './TempSql'; import { execProtoForWorker } from './data-trafic/utils/ExecProtoForWorker'; import { TraficEnum } from './data-trafic/utils/QueryEnum'; +import {Constants, TypeConstants} from "../../webSocket/Constants"; let conn: unknown = null; - +let enc = new TextEncoder(); +let dec = new TextDecoder(); +const REQ_BUF_SIZE = 4 * 1024 * 1024; +let uploadSoActionId: string = ''; +const failedArray: Array = []; self.onerror = function (error): void { }; self.onmessage = async (e: unknown): Promise => { @@ -97,5 +104,98 @@ self.onmessage = async (e: unknown): Promise => { return []; } }); + } else if (action === 'upload-so') { + onmessageByUploadSoAction(e); + } +}; + +function onmessageByUploadSoAction(e: unknown): void { + // @ts-ignore + uploadSoActionId = e.data.id; + // @ts-ignore + const fileList = e.data.params as Array; + const file = fileList[0]; + const result = 'ok'; + if (fileList) { + fileList.sort((a, b) => b.size - a.size); + uploadAllFilesRecursively(fileList); + } + self.postMessage({ + id: uploadSoActionId, + action: 'upload-so', + results: { result: result, failedArray: failedArray }, + }); +} + +// 递归上传文件 +function uploadAllFilesRecursively(fileList: Array): void { + if (fileList.length === 0) { + console.log("All files have been uploaded."); + return; // 所有文件上传完成 + } + + // 上传第一个文件 + const file = fileList[0]; + uploadSoFile(file).then(() => { + console.log(`File ${file.name} uploaded successfully.`); + // 删除数组中的第一个元素 + fileList.shift(); + // 递归调用上传下一个文件 + uploadAllFilesRecursively(fileList); + }).catch((error) => { + console.error(`Failed to upload file: ${file.name}`, error); + // 继续上传下一个,即使失败也继续 + fileList.shift(); + uploadAllFilesRecursively(fileList); + }); +} + + +const uploadSoFile = async (file: File | null): Promise => { + if (file) { + let fileNameBuffer: Uint8Array | null = enc.encode(file.webkitRelativePath); + let fileNameLength = fileNameBuffer.length; + let writeSize = 0; + let wsInstance = WebSocketManager.getInstance() + const fileName = file.name; + let bufferIndex = 0; + while (writeSize < file.size) { + let sliceLen = Math.min(file.size - writeSize, REQ_BUF_SIZE); + let blob: Blob | null = file.slice(writeSize, writeSize + sliceLen); + let buffer: ArrayBuffer | null = await blob.arrayBuffer(); + let data: Uint8Array | null = new Uint8Array(buffer); + let fileTotalSize = file.size; + writeSize += sliceLen; + //@ts-ignore + if (wsInstance) { + // 构造包含元数据和文件内容的对象 + const dataObject = { + file_name: fileName, + buffer_index: bufferIndex, + buffer_size: sliceLen, + total_size: fileTotalSize, + is_last: writeSize >= file.size, + buffer: Array.from(data), // 将 Uint8Array 转换为普通数组,以便可以序列化为 JSON + }; + + // 将对象序列化为 JSON 字符串 + const dataString = JSON.stringify(dataObject); + + // 使用 TextEncoder 将字符串编码为 Uint8Array + const textEncoder = new TextEncoder(); + const encodedData = textEncoder.encode(dataString); + + // 通过 WebSocket 发送数据 + wsInstance.sendMessage(TypeConstants.DISASSEMBLY_TYPE, Constants.DISASSEMBLY_SAVE_CMD, encodedData); + } + + // 更新当前片段索引 + bufferIndex++; + data = null; + buffer = null; + blob = null; + } + file = null; + fileNameBuffer = null; } }; diff --git a/ide/src/trace/database/TraceWorker.ts b/ide/src/trace/database/TraceWorker.ts index 1923ed8af..18f3937e0 100644 --- a/ide/src/trace/database/TraceWorker.ts +++ b/ide/src/trace/database/TraceWorker.ts @@ -13,6 +13,8 @@ * limitations under the License. */ +import {WebSocketManager} from "../../webSocket/WebSocketManager"; + importScripts('trace_streamer_builtin.js'); import { execProtoForWorker } from './data-trafic/utils/ExecProtoForWorker'; import { QueryEnum, TraficEnum } from './data-trafic/utils/QueryEnum'; @@ -20,6 +22,7 @@ import { QueryEnum, TraficEnum } from './data-trafic/utils/QueryEnum'; import { temp_init_sql_list } from './TempSql'; // @ts-ignore import { BatchSphData } from '../proto/SphBaseData'; +import {Constants, TypeConstants} from "../../webSocket/Constants"; enum TsLogLevel { DEBUG = 0, @@ -1328,6 +1331,9 @@ const uploadSoFile = async (file: File | null): Promise => { wasmModule.HEAPU8.set(fileNameBuffer, addr); let writeSize = 0; let upRes = -1; + let wsInstance = WebSocketManager.getInstance() + const fileName = file.name; + let bufferIndex = 0; while (writeSize < file.size) { let sliceLen = Math.min(file.size - writeSize, REQ_BUF_SIZE); let blob: Blob | null = file.slice(writeSize, writeSize + sliceLen); @@ -1339,6 +1345,30 @@ const uploadSoFile = async (file: File | null): Promise => { writeSize += sliceLen; //@ts-ignore upRes = wasmModule._TraceStreamerDownloadELFEx(size, fileNameLength, sliceLen, 1); + if (wsInstance) { + // 构造包含元数据和文件内容的对象 + const dataObject = { + file_name: fileName, + buffer_index: bufferIndex, + buffer_size: sliceLen, + total_size: size, + is_last: writeSize >= file.size, + buffer: Array.from(data), // 将 Uint8Array 转换为普通数组,以便可以序列化为 JSON + }; + + // 将对象序列化为 JSON 字符串 + const dataString = JSON.stringify(dataObject); + + // 使用 TextEncoder 将字符串编码为 Uint8Array + const textEncoder = new TextEncoder(); + const encodedData = textEncoder.encode(dataString); + + // 通过 WebSocket 发送数据 + wsInstance.sendMessage(TypeConstants.DISASSEMBLY_TYPE, Constants.DISASSEMBLY_SAVE_CMD, encodedData); + } + + // 更新当前片段索引 + bufferIndex++; data = null; buffer = null; blob = null; diff --git a/ide/src/webSocket/Constants.ts b/ide/src/webSocket/Constants.ts index 0c8595626..6381b8b69 100644 --- a/ide/src/webSocket/Constants.ts +++ b/ide/src/webSocket/Constants.ts @@ -23,6 +23,10 @@ export class Constants { static GET_VERSION_CMD = 1; static UPDATE_SUCCESS_CMD = 2; // 升级成功 static UPDATE_FAIL_CMD = 4; // 升级失败 + static DISASSEMBLY_SAVE_CMD = 1; + static DISASSEMBLY_SAVE_BACK_CMD = 2; + static DISASSEMBLY_QUERY_CMD = 3; + static DISASSEMBLY_QUERY_BACK_CMD = 4; } export class TypeConstants { @@ -32,4 +36,5 @@ export class TypeConstants { static DIAGNOSIS_TYPE = 8; static SENDDB_CMD = 1; static DIAGNOSIS_CMD = 3; + static DISASSEMBLY_TYPE = 10; } \ No newline at end of file diff --git a/ide/src/webSocket/WebSocketManager.ts b/ide/src/webSocket/WebSocketManager.ts index 364779920..5434d24fa 100644 --- a/ide/src/webSocket/WebSocketManager.ts +++ b/ide/src/webSocket/WebSocketManager.ts @@ -56,7 +56,7 @@ export class WebSocketManager { this.websocket.binaryType = 'arraybuffer'; this.websocket.onopen = (): void => { this.status = GetStatuses.CONNECTED; - // 设置心跳定时器 + // 设置心跳定时器 this.sendHeartbeat(); // 连接后登录 this.login(); @@ -83,6 +83,7 @@ export class WebSocketManager { this.initLoginInfo(); this.clearHeartbeat(); }; + WebSocketManager.getInstance()!.registerMessageListener(TypeConstants.DISASSEMBLY_TYPE, this.webSocketCallBack, () => {}); } /** @@ -99,7 +100,7 @@ export class WebSocketManager { this.businessMessage(decode) } } - + // 登录 loginMessage(decode: MessageParam): void { if (decode.cmd === Constants.LOGIN_CMD) { @@ -133,7 +134,7 @@ export class WebSocketManager { this.finalStatus(); } else if (decode.cmd === Constants.UPDATE_FAIL_CMD) { // 升级失败 this.status = GetStatuses.UPGRADEFAILED; - this.finalStatus(); + this.finalStatus(); } } @@ -280,7 +281,7 @@ export class WebSocketManager { obj.data = data } } - + // 检查状态 中间状态,最终失败状态,最终成功状态 checkStatus(reconnect: number): void { // @ts-ignore @@ -331,4 +332,16 @@ export class WebSocketManager { },// 重连 } } + // 汇编、源码展开接口回调函数 + // @ts-ignore + webSocketCallBack = async (cmd: number, result: Uint8Array): unknown => { + const decoder = new TextDecoder(); + const jsonString = decoder.decode(result); + let jsonRes = JSON.parse(jsonString); + if (cmd === Constants.DISASSEMBLY_SAVE_BACK_CMD) { + return + }else if (cmd === Constants.DISASSEMBLY_QUERY_BACK_CMD) { + return + } + } } -- Gitee From f6dee13c6331fa76c4bd3d80f5c6704ce9664d5a Mon Sep 17 00:00:00 2001 From: liufei Date: Fri, 6 Dec 2024 11:09:15 +0800 Subject: [PATCH 30/38] =?UTF-8?q?feat:hiperf=5Fso=E7=82=B9=E5=87=BBfunc?= =?UTF-8?q?=E8=8E=B7=E5=BE=97vaddr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liufei --- .../trace/sheet/hiperf/TabPanePerfAnalysis.ts | 19 +++ .../logic-worker/ProcedureLogicWorkerPerf.ts | 114 ++++++++++++++++-- 2 files changed, 123 insertions(+), 10 deletions(-) diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts index a2ae261cd..111ad862b 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts @@ -64,6 +64,8 @@ export class TabPanePerfAnalysis extends BaseElement { private tableArray: NodeListOf | undefined | null; private isComplete: boolean = true; private currentSelectionParam: SelectionParam | undefined | null; + private vaddrList: Array = []; + private clickFuncVaddrList: Array = []; set data(val: SelectionParam) { if (val === this.currentSelection) { @@ -201,6 +203,7 @@ export class TabPanePerfAnalysis extends BaseElement { this.addRowClickEventListener(this.perfTableProcess!, this.perfProcessLevelClickEvent.bind(this)); this.addRowClickEventListener(this.perfTableThread!, this.perfThreadLevelClickEvent.bind(this)); this.addRowClickEventListener(this.perfTableSo!, this.perfSoLevelClickEvent.bind(this)); + this.addRowClickEventListener(this.tableFunction!, this.functionClickEvent.bind(this)); } private addRowClickEventListener(table: LitTable, clickEvent: Function): void { @@ -558,6 +561,13 @@ export class TabPanePerfAnalysis extends BaseElement { this.perfAnalysisPie?.hideTip(); } + private functionClickEvent(it: unknown) { + this.clickFuncVaddrList = this.vaddrList.filter((item: unknown) => { + // @ts-ignore + return item.symbolName === it.tableName + }) + } + private sortByColumn(): void { let currentTable: LitTable | null | undefined; switch (this.currentLevel) { @@ -1111,6 +1121,15 @@ export class TabPanePerfAnalysis extends BaseElement { this.progressEL!.loading = false; this.getHiperfProcess(val); }); + const args = [ + { + funcName: 'getVaddrToFile', + funcArgs: [val], + }, + ]; + procedurePool.submitWithName('logic0', 'perf-vaddr', args, undefined, (results: Array) => { + this.vaddrList = results; + }) } private getDataByWorker(val: SelectionParam, handler: Function): void { diff --git a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts index 0c5a6b07f..e7534d392 100644 --- a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts +++ b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts @@ -102,6 +102,12 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { case 'perf-action': this.perfAction(data); break; + case 'perf-vaddr-back': + this.rebackVaddrList(data); + break; + case 'perf-vaddr': + this.perfGetVaddr(data); + break; case 'perf-reset': this.perfReset(); break; @@ -201,6 +207,37 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { this.isAnalysis = false; } } + + rebackVaddrList(data: unknown) { + // @ts-ignore + let vaddrCallchainList = convertJSON(data.params.list); + let sampleCallChainList: unknown = []; + for (let i = 0; i < vaddrCallchainList.length; i++) { + let funcVaddrLastItem = {}; + // @ts-ignore + let callChains = [...this.callChainData[vaddrCallchainList[i].callchain_id]]; + const lastCallChain = callChains[callChains.length - 1]; + // @ts-ignore + funcVaddrLastItem.callchain_id = lastCallChain.sampleId; + // @ts-ignore + funcVaddrLastItem.symbolName = this.dataCache.dataDict.get(lastCallChain.name as number); + // @ts-ignore + funcVaddrLastItem.vaddrInFile = lastCallChain.vaddrInFile; + // @ts-ignore + funcVaddrLastItem.offsetToVaddr = lastCallChain.offsetToVaddr; + // @ts-ignores + sampleCallChainList.push(funcVaddrLastItem); + } + + self.postMessage({ + //@ts-ignore + id: data.id, + //@ts-ignore + action: data.action, + results: sampleCallChainList, + }); + } + private perfAction(data: unknown): void { //@ts-ignore const params = data.params; @@ -228,6 +265,18 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { } } } + + private perfGetVaddr(data: unknown) { + // @ts-ignore + const params = data.params; + this.backVaddrData(data) + } + + backVaddrData(data: unknown) { + // @ts-ignore + this.handleDataByFuncName(data.params[0].funcName, data.params[0].funcArgs); + } + private perfReset(): void { this.isHideThread = false; this.isHideThreadState = false; @@ -588,7 +637,7 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { symbolName = perfCallChains[topIndex].name; } // 只展示内核栈合并进程栈 - const usePidAsKey = this.isOnlyKernel ? '': perfSample.pid; + const usePidAsKey = this.isOnlyKernel ? '' : perfSample.pid; let perfRootNode = this.currentTreeMapData[symbolName + usePidAsKey]; if (perfRootNode === undefined) { perfRootNode = new PerfCallChainMerageData(); @@ -606,7 +655,7 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { } private mergeNodeData(totalEventCount: number, totalSamplesCount: number): MergeMap { // 只展示内核栈不添加进程这一级的结构 - if (this.isOnlyKernel){ + if (this.isOnlyKernel) { return this.currentTreeMapData; } // 添加进程级结构 @@ -983,6 +1032,48 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { this.getCurrentDataFromDb(funcArgs[0]); } } + + private queryVaddrToFile(funcArgs: unknown[]): void { + if (funcArgs[1]) { + let sql = ''; + //@ts-ignore + if (funcArgs[1].processId !== undefined) { + //@ts-ignore + sql += `and thread.process_id = ${funcArgs[1].processId}`; + } + //@ts-ignore + if (funcArgs[1].threadId !== undefined) { + //@ts-ignore + sql += ` and s.thread_id = ${funcArgs[1].threadId}`; + } + //@ts-ignore + this.getVaddrToFile(funcArgs[0], sql); + } else { + //@ts-ignore + this.getVaddrToFile(funcArgs[0]); + } + } + + private getVaddrToFile(selectionParam: SelectionParam, sql?: string): void { + let filterSql = this.setFilterSql(selectionParam, sql); + this.queryData( + this.currentEventId, + 'perf-vaddr-back', + `select s.callchain_id + from perf_sample s, trace_range t + where timestamp_trace between ${selectionParam.leftNs} + t.start_ts + and ${selectionParam.rightNs} + t.start_ts + and s.callchain_id != -1 + and s.thread_id != 0 ${filterSql} + group by s.callchain_id`, + { + $startTime: selectionParam.leftNs, + $endTime: selectionParam.rightNs, + $sql: filterSql, + } + ); + } + private handleDataByFuncName(funcName: string, funcArgs: unknown[]): unknown { let result; switch (funcName) { @@ -992,6 +1083,9 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { case 'getCurrentDataFromDb': this.queryDataFromDb(funcArgs); break; + case 'getVaddrToFile': + this.queryVaddrToFile(funcArgs); + break; case 'hideSystemLibrary': this.hideSystemLibrary(); break; @@ -1254,7 +1348,7 @@ export class PerfCallChain { sampleId: number = 0; callChainId: number = 0; vaddrInFile: number = 0; - offsetToVaddr:number = 0; + offsetToVaddr: number = 0; tid: number = 0; pid: number = 0; name: number | string = 0; @@ -1326,7 +1420,7 @@ export class PerfCallChainMerageData extends ChartStruct { initChildren: PerfCallChainMerageData[] = []; type: number = 0; vaddrInFile: number = 0; - offsetToVaddr:number = 0; + offsetToVaddr: number = 0; isSelected: boolean = false; searchShow: boolean = true; isSearch: boolean = false; @@ -1426,9 +1520,9 @@ export class PerfCmdLine { class PerfAnalysisSample extends PerfCountSample { threadName: string; - depth:number; - vaddr_in_file:number; - offset_to_vaddr:number; + depth: number; + vaddr_in_file: number; + offset_to_vaddr: number; processName: string; libId: number; libName: string; @@ -1437,9 +1531,9 @@ class PerfAnalysisSample extends PerfCountSample { constructor( threadName: string, - depth:number, - vaddr_in_file:number, - offset_to_vaddr:number, + depth: number, + vaddr_in_file: number, + offset_to_vaddr: number, processName: string, libId: number, libName: string, -- Gitee From 990c42b8467551bd4fc5aee424c2995066e9d81f Mon Sep 17 00:00:00 2001 From: liufei Date: Sat, 7 Dec 2024 13:57:35 +0800 Subject: [PATCH 31/38] =?UTF-8?q?feat:=E7=82=B9=E5=87=BB=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=AF=B9=E5=BA=94vaddr=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=BF=9B=E7=A8=8B=E3=80=81=E7=BA=BF=E7=A8=8B=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liufei --- .../trace/sheet/hiperf/TabPanePerfAnalysis.ts | 49 ++++++++++++------- .../logic-worker/ProcedureLogicWorkerPerf.ts | 13 ++++- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts index 111ad862b..09559fc19 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts @@ -65,6 +65,9 @@ export class TabPanePerfAnalysis extends BaseElement { private isComplete: boolean = true; private currentSelectionParam: SelectionParam | undefined | null; private vaddrList: Array = []; + private selectedTabProcessId: number = 0; + private selectedTabThreadId: number = 0; + private selectedTabfileName: string = ''; private clickFuncVaddrList: Array = []; set data(val: SelectionParam) { @@ -339,25 +342,25 @@ export class TabPanePerfAnalysis extends BaseElement { tip: (perfObj): string => { return `

Process:${ - // @ts-ignore - perfObj.obj.tableName - }
+ // @ts-ignore + perfObj.obj.tableName + }
Sample Count:${ - // @ts-ignore - perfObj.obj.count - }
+ // @ts-ignore + perfObj.obj.count + }
Percent:${ - // @ts-ignore - perfObj.obj.percent - }%
+ // @ts-ignore + perfObj.obj.percent + }%
Event Count:${ - // @ts-ignore - perfObj.obj.eventCount - }
+ // @ts-ignore + perfObj.obj.eventCount + }
Percent:${ - // @ts-ignore - perfObj.obj.eventPercent - }%
+ // @ts-ignore + perfObj.obj.eventPercent + }% `; }, @@ -406,6 +409,8 @@ export class TabPanePerfAnalysis extends BaseElement { // @ts-ignore this.processName = it.tableName; this.perfAnalysisPie?.hideTip(); + // @ts-ignore + this.selectedTabProcessId = it.pid; } private threadPieChart(val: SelectionParam): void { @@ -477,6 +482,8 @@ export class TabPanePerfAnalysis extends BaseElement { // @ts-ignore this.threadName = it.tableName; this.perfAnalysisPie?.hideTip(); + // @ts-ignore + this.selectedTabThreadId = it.tid; } private initPerfAnalysisPieConfig(): void { @@ -559,12 +566,20 @@ export class TabPanePerfAnalysis extends BaseElement { } this.titleEl!.textContent = title; this.perfAnalysisPie?.hideTip(); + // @ts-ignore + this.selectedTabfileName = it.tableName; } private functionClickEvent(it: unknown) { this.clickFuncVaddrList = this.vaddrList.filter((item: unknown) => { // @ts-ignore - return item.symbolName === it.tableName + return item.process_id === this.selectedTabProcessId && + // @ts-ignore + item.thread_id === this.selectedTabThreadId && + // @ts-ignore + item.libName === this.selectedTabfileName && + // @ts-ignore + item.symbolName === it.tableName }) } @@ -1090,7 +1105,7 @@ export class TabPanePerfAnalysis extends BaseElement { // @ts-ignore other.percent = ((other.count / this.sumCount!) * 100).toFixed(2); // @ts-ignore - other.eventCount += res[i].eventCount; + other.eventCount += res[i].eventCount; // @ts-ignore other.eventPercent = ((other.eventCount / this.sumEventCount!) * 100).toFixed(2); } diff --git a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts index e7534d392..5650e3195 100644 --- a/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts +++ b/ide/src/trace/database/logic-worker/ProcedureLogicWorkerPerf.ts @@ -225,7 +225,13 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { funcVaddrLastItem.vaddrInFile = lastCallChain.vaddrInFile; // @ts-ignore funcVaddrLastItem.offsetToVaddr = lastCallChain.offsetToVaddr; - // @ts-ignores + // @ts-ignore + funcVaddrLastItem.process_id = vaddrCallchainList[i].process_id; + // @ts-ignore + funcVaddrLastItem.thread_id = vaddrCallchainList[i].thread_id; + // @ts-ignore + funcVaddrLastItem.libName = lastCallChain.fileName; + // @ts-ignore sampleCallChainList.push(funcVaddrLastItem); } @@ -1059,8 +1065,11 @@ export class ProcedureLogicWorkerPerf extends LogicHandler { this.queryData( this.currentEventId, 'perf-vaddr-back', - `select s.callchain_id + `select s.callchain_id, + s.thread_id, + thread.process_id from perf_sample s, trace_range t + left join perf_thread thread on s.thread_id = thread.thread_id where timestamp_trace between ${selectionParam.leftNs} + t.start_ts and ${selectionParam.rightNs} + t.start_ts and s.callchain_id != -1 -- Gitee From 347f35034f0b38c3728630b121c892ce74af2385 Mon Sep 17 00:00:00 2001 From: z30061262 Date: Fri, 6 Dec 2024 12:47:40 +0800 Subject: [PATCH 32/38] =?UTF-8?q?so=E3=80=81an=E6=96=87=E4=BB=B6=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=97=B6=E5=8F=91=E9=80=81=E5=88=B0=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhuheng --- .../trace/component/trace/base/TraceSheet.ts | 141 +++++++++++++++--- ide/src/trace/database/SqlLiteWorker.ts | 84 +---------- ide/src/trace/database/TraceWorker.ts | 30 ---- ide/src/webSocket/WebSocketManager.ts | 71 ++++++--- 4 files changed, 175 insertions(+), 151 deletions(-) diff --git a/ide/src/trace/component/trace/base/TraceSheet.ts b/ide/src/trace/component/trace/base/TraceSheet.ts index 4a8d2bcd7..6acf03775 100644 --- a/ide/src/trace/component/trace/base/TraceSheet.ts +++ b/ide/src/trace/component/trace/base/TraceSheet.ts @@ -95,6 +95,9 @@ import { PerfToolStruct } from '../../../database/ui-worker/ProcedureWorkerPerfT import { GpuCounterStruct } from '../../../database/ui-worker/ProcedureWorkerGpuCounter'; import { TabPaneGpuCounter } from '../sheet/gpu-counter/TabPaneGpuCounter'; import { TabPaneSliceChild } from '../sheet/process/TabPaneSliceChild'; +import {WebSocketManager} from "../../../../webSocket/WebSocketManager"; +import {Constants, TypeConstants} from "../../../../webSocket/Constants"; +import {SpStatisticsHttpUtil} from "../../../../statistics/util/SpStatisticsHttpUtil"; @element('trace-sheet') export class TraceSheet extends BaseElement { @@ -119,6 +122,9 @@ export class TraceSheet extends BaseElement { private optionsDiv: LitPopover | undefined | null; private optionsSettingTree: LitTree | undefined | null; private tabPaneHeight: string = ''; + private enc = new TextEncoder(); + private dec = new TextDecoder(); + private REQ_BUF_SIZE = 4 * 1024 * 1024; static get observedAttributes(): string[] { return ['mode']; @@ -532,7 +538,7 @@ export class TraceSheet extends BaseElement { private importClickEvent(): void { let importFileBt: HTMLInputElement | undefined | null = - this.shadowRoot?.querySelector('#import-file'); + this.shadowRoot?.querySelector('#import-file'); importFileBt!.addEventListener('change', (event): void => { let files = importFileBt?.files; if (files) { @@ -543,30 +549,129 @@ export class TraceSheet extends BaseElement { if (fileList.length > 0) { importFileBt!.disabled = true; window.publish(window.SmartEvent.UI.Loading, { loading: true, text: 'Import So File' }); - threadPool.submit( - 'upload-so', - '', - fileList, - (res: unknown) => { - importFileBt!.disabled = false; // @ts-ignore - if (res.result === 'ok') { - window.publish(window.SmartEvent.UI.UploadSOFile, {}); - } else { - // @ts-ignore - const failedList = res.failedArray.join(','); - window.publish(window.SmartEvent.UI.Error, `parse so file ${failedList} failed!`); - } - }, - 'upload-so' - ); + this.uploadSoOrAN(fileList).then(r => + threadPool.submit( + 'upload-so', + '', + fileList, + (res: unknown) => { + importFileBt!.disabled = false; // @ts-ignore + if (res.result === 'ok') { + window.publish(window.SmartEvent.UI.UploadSOFile, {}); + } else { + // @ts-ignore + const failedList = res.failedArray.join(','); + window.publish(window.SmartEvent.UI.Error, `parse so file ${failedList} failed!`); + } + }, + 'upload-so' + )).finally(() => { + fileList.length = 0; + }) } - fileList.length = 0; } importFileBt!.files = null; importFileBt!.value = ''; }); } + private async uploadSoOrAN(fileList: Array): Promise { + if (fileList) { + fileList.sort((a, b) => b.size - a.size); + await this.uploadAllFiles(fileList); + } + } + + + // 上传文件 + private async uploadAllFiles(fileList: Array): Promise { + // 创建一个副本,避免修改原始的 fileList + const filesToUpload = [...fileList]; + + for (let i = 0; i < filesToUpload.length; i++) { + const file = filesToUpload[i]; + try { + await this.uploadSingleFile(file); + console.log('File ${file.name} uploaded successfully.'); + } catch (error) { + console.error('Failed to upload file: ${file.name}, error'); + } + } + console.log("All files have been uploaded."); + } + + + private uploadSingleFile = async (file: File | null): Promise => { + if (file) { + let writeSize = 0; + let wsInstance = WebSocketManager.getInstance(); + const fileName = file.name; + let bufferIndex = 0; + + // 定义一个 ACK 回调函数的等待机制 + const waitForAck = (): Promise => { + return new Promise((resolve, reject) => { + wsInstance!.registerCallback(TypeConstants.DISASSEMBLY_TYPE, onAckReceived); + // 定义超时定时器 + const timeout = setTimeout(() => { + // 超时后注销回调并拒绝 Promise + wsInstance!.unregisterCallback(TypeConstants.DISASSEMBLY_TYPE, onAckReceived); + reject(new Error('等待 ACK 超时:文件 ${fileName},索引 ${bufferIndex})')); + }, 10000); + function onAckReceived(cmd: number, result: Uint8Array) { + const decoder = new TextDecoder(); + const jsonString = decoder.decode(result); + let jsonRes = JSON.parse(jsonString); + if (cmd === Constants.DISASSEMBLY_SAVE_BACK_CMD) { + if (jsonRes.fileName === fileName && jsonRes.bufferIndex === bufferIndex) { + wsInstance!.unregisterCallback(TypeConstants.DISASSEMBLY_TYPE, onAckReceived) + clearTimeout(timeout); + if (jsonRes.resultCode == 0) { + console.log('ACK received for file: ${jsonRes.fileName}, index: ${jsonRes.bufferIndex}'); + bufferIndex++; + // 当收到对应分片的 ACK 时,resolve Promise,继续上传下一个分片 + resolve(); + }else{ + // 上传失败,拒绝 Promise 并返回 + reject(new Error('Upload failed for file: ${fileName}, index: ${jsonRes.bufferIndex})')); + } + } + } + } + }); + }; + + while (writeSize < file.size) { + let sliceLen = Math.min(file.size - writeSize, this.REQ_BUF_SIZE); + let blob: Blob | null = file.slice(writeSize, writeSize + sliceLen); + let buffer: ArrayBuffer | null = await blob.arrayBuffer(); + let data: Uint8Array | null = new Uint8Array(buffer); + + const dataObject = { + file_name: fileName, + buffer_index: bufferIndex, + buffer_size: sliceLen, + total_size: file.size, + is_last: writeSize + sliceLen >= file.size, + buffer: Array.from(data), + }; + + + const dataString = JSON.stringify(dataObject); + const textEncoder = new TextEncoder(); + const encodedData = textEncoder.encode(dataString); + wsInstance!.sendMessage(TypeConstants.DISASSEMBLY_TYPE, Constants.DISASSEMBLY_SAVE_CMD, encodedData); + writeSize += sliceLen; + // 等待服务器端确认当前分片的 ACK + await waitForAck(); + data = null; + buffer = null; + blob = null; + } + console.log('Upload complete for file: ${fileName}'); + } + }; + private exportClickEvent(): void { this.exportBt!.onclick = (): void => { let currentTab = this.getTabpaneByKey(this.litTabs?.activekey!); diff --git a/ide/src/trace/database/SqlLiteWorker.ts b/ide/src/trace/database/SqlLiteWorker.ts index 3560a0e22..673cc414f 100644 --- a/ide/src/trace/database/SqlLiteWorker.ts +++ b/ide/src/trace/database/SqlLiteWorker.ts @@ -13,6 +13,8 @@ * limitations under the License. */ +importScripts('sql-wasm.js'); +// @ts-ignore import {WebSocketManager} from "../../webSocket/WebSocketManager"; importScripts('sql-wasm.js'); @@ -20,7 +22,6 @@ importScripts('sql-wasm.js'); import { temp_init_sql_list } from './TempSql'; import { execProtoForWorker } from './data-trafic/utils/ExecProtoForWorker'; import { TraficEnum } from './data-trafic/utils/QueryEnum'; -import {Constants, TypeConstants} from "../../webSocket/Constants"; let conn: unknown = null; let enc = new TextEncoder(); @@ -113,89 +114,10 @@ function onmessageByUploadSoAction(e: unknown): void { // @ts-ignore uploadSoActionId = e.data.id; // @ts-ignore - const fileList = e.data.params as Array; - const file = fileList[0]; const result = 'ok'; - if (fileList) { - fileList.sort((a, b) => b.size - a.size); - uploadAllFilesRecursively(fileList); - } self.postMessage({ id: uploadSoActionId, action: 'upload-so', results: { result: result, failedArray: failedArray }, }); -} - -// 递归上传文件 -function uploadAllFilesRecursively(fileList: Array): void { - if (fileList.length === 0) { - console.log("All files have been uploaded."); - return; // 所有文件上传完成 - } - - // 上传第一个文件 - const file = fileList[0]; - uploadSoFile(file).then(() => { - console.log(`File ${file.name} uploaded successfully.`); - // 删除数组中的第一个元素 - fileList.shift(); - // 递归调用上传下一个文件 - uploadAllFilesRecursively(fileList); - }).catch((error) => { - console.error(`Failed to upload file: ${file.name}`, error); - // 继续上传下一个,即使失败也继续 - fileList.shift(); - uploadAllFilesRecursively(fileList); - }); -} - - -const uploadSoFile = async (file: File | null): Promise => { - if (file) { - let fileNameBuffer: Uint8Array | null = enc.encode(file.webkitRelativePath); - let fileNameLength = fileNameBuffer.length; - let writeSize = 0; - let wsInstance = WebSocketManager.getInstance() - const fileName = file.name; - let bufferIndex = 0; - while (writeSize < file.size) { - let sliceLen = Math.min(file.size - writeSize, REQ_BUF_SIZE); - let blob: Blob | null = file.slice(writeSize, writeSize + sliceLen); - let buffer: ArrayBuffer | null = await blob.arrayBuffer(); - let data: Uint8Array | null = new Uint8Array(buffer); - let fileTotalSize = file.size; - writeSize += sliceLen; - //@ts-ignore - if (wsInstance) { - // 构造包含元数据和文件内容的对象 - const dataObject = { - file_name: fileName, - buffer_index: bufferIndex, - buffer_size: sliceLen, - total_size: fileTotalSize, - is_last: writeSize >= file.size, - buffer: Array.from(data), // 将 Uint8Array 转换为普通数组,以便可以序列化为 JSON - }; - - // 将对象序列化为 JSON 字符串 - const dataString = JSON.stringify(dataObject); - - // 使用 TextEncoder 将字符串编码为 Uint8Array - const textEncoder = new TextEncoder(); - const encodedData = textEncoder.encode(dataString); - - // 通过 WebSocket 发送数据 - wsInstance.sendMessage(TypeConstants.DISASSEMBLY_TYPE, Constants.DISASSEMBLY_SAVE_CMD, encodedData); - } - - // 更新当前片段索引 - bufferIndex++; - data = null; - buffer = null; - blob = null; - } - file = null; - fileNameBuffer = null; - } -}; +} \ No newline at end of file diff --git a/ide/src/trace/database/TraceWorker.ts b/ide/src/trace/database/TraceWorker.ts index 18f3937e0..1923ed8af 100644 --- a/ide/src/trace/database/TraceWorker.ts +++ b/ide/src/trace/database/TraceWorker.ts @@ -13,8 +13,6 @@ * limitations under the License. */ -import {WebSocketManager} from "../../webSocket/WebSocketManager"; - importScripts('trace_streamer_builtin.js'); import { execProtoForWorker } from './data-trafic/utils/ExecProtoForWorker'; import { QueryEnum, TraficEnum } from './data-trafic/utils/QueryEnum'; @@ -22,7 +20,6 @@ import { QueryEnum, TraficEnum } from './data-trafic/utils/QueryEnum'; import { temp_init_sql_list } from './TempSql'; // @ts-ignore import { BatchSphData } from '../proto/SphBaseData'; -import {Constants, TypeConstants} from "../../webSocket/Constants"; enum TsLogLevel { DEBUG = 0, @@ -1331,9 +1328,6 @@ const uploadSoFile = async (file: File | null): Promise => { wasmModule.HEAPU8.set(fileNameBuffer, addr); let writeSize = 0; let upRes = -1; - let wsInstance = WebSocketManager.getInstance() - const fileName = file.name; - let bufferIndex = 0; while (writeSize < file.size) { let sliceLen = Math.min(file.size - writeSize, REQ_BUF_SIZE); let blob: Blob | null = file.slice(writeSize, writeSize + sliceLen); @@ -1345,30 +1339,6 @@ const uploadSoFile = async (file: File | null): Promise => { writeSize += sliceLen; //@ts-ignore upRes = wasmModule._TraceStreamerDownloadELFEx(size, fileNameLength, sliceLen, 1); - if (wsInstance) { - // 构造包含元数据和文件内容的对象 - const dataObject = { - file_name: fileName, - buffer_index: bufferIndex, - buffer_size: sliceLen, - total_size: size, - is_last: writeSize >= file.size, - buffer: Array.from(data), // 将 Uint8Array 转换为普通数组,以便可以序列化为 JSON - }; - - // 将对象序列化为 JSON 字符串 - const dataString = JSON.stringify(dataObject); - - // 使用 TextEncoder 将字符串编码为 Uint8Array - const textEncoder = new TextEncoder(); - const encodedData = textEncoder.encode(dataString); - - // 通过 WebSocket 发送数据 - wsInstance.sendMessage(TypeConstants.DISASSEMBLY_TYPE, Constants.DISASSEMBLY_SAVE_CMD, encodedData); - } - - // 更新当前片段索引 - bufferIndex++; data = null; buffer = null; blob = null; diff --git a/ide/src/webSocket/WebSocketManager.ts b/ide/src/webSocket/WebSocketManager.ts index 5434d24fa..800b699ba 100644 --- a/ide/src/webSocket/WebSocketManager.ts +++ b/ide/src/webSocket/WebSocketManager.ts @@ -34,7 +34,7 @@ export class WebSocketManager { static instance: WebSocketManager | null | undefined = null; url: string = `ws://localhost:${Constants.NODE_PORT}`; private websocket: WebSocket | null | undefined = null; - private distributeMap: Map = new Map(); + private distributeMap: Map = new Map(); private sessionId: number | null | undefined; private session: bigint | null | undefined; private heartbeatInterval: number | null | undefined; @@ -83,7 +83,6 @@ export class WebSocketManager { this.initLoginInfo(); this.clearHeartbeat(); }; - WebSocketManager.getInstance()!.registerMessageListener(TypeConstants.DISASSEMBLY_TYPE, this.webSocketCallBack, () => {}); } /** @@ -140,7 +139,13 @@ export class WebSocketManager { // 业务 businessMessage(decode: MessageParam): void { - this.distributeMap.get(decode.type!)?.messageCallback(decode.cmd, decode.data); + if (this.distributeMap.has(decode.type!)){ + const callbackObj = this.distributeMap.get(decode.type!)!; + // 遍历调用所有 eventCallBacks + callbackObj.messageCallbacks.forEach(callback => { + callback(decode.cmd, decode.data); + }); + } } // get版本 @@ -209,7 +214,35 @@ export class WebSocketManager { */ registerMessageListener(type: number, callback: Function, eventCallBack: Function): void { if (!this.distributeMap.has(type)) { - this.distributeMap.set(type, { 'messageCallback': callback, 'eventCallBack': eventCallBack }); + this.distributeMap.set(type, { 'messageCallbacks': [], 'eventCallBack': eventCallBack }); + } + const callbackObj = this.distributeMap.get(type)!; + callbackObj.messageCallbacks.push(callback); + } + + // 注册回调函数 + registerCallback(type: number, callback: Function): void { + if (!this.distributeMap.has(type)) { + this.distributeMap.set(type, { 'messageCallbacks': [], 'eventCallBack': () => {} }); + } + const callbackObj = this.distributeMap.get(type)!; + callbackObj.messageCallbacks.push(callback); + } + + // 删除回调函数 + unregisterCallback(type: number, callback: Function): void { + if (!this.distributeMap.has(type)) { + return; + } + // 获取指定类型的回调对象 + const callbackObj = this.distributeMap.get(type)!; + + // 在回调数组中查找并移除与传入的回调函数匹配的项 + callbackObj.messageCallbacks = callbackObj.messageCallbacks.filter((cb) => cb !== callback); + + // 如果回调数组为空,同时 eventCallBack 也为空,则可以删除整个类型 + if (callbackObj.messageCallbacks.length === 0 && !callbackObj.eventCallBack) { + this.distributeMap.delete(type); } } @@ -283,14 +316,20 @@ export class WebSocketManager { } // 检查状态 中间状态,最终失败状态,最终成功状态 - checkStatus(reconnect: number): void { + checkStatus(type: number): void { // 更改参数名称为 type,以反映实际传递的值 // @ts-ignore let statuses = this.getStatusesPrompt()[this.status]; - if (statuses.type === INTERMEDIATE_STATE) { - this.distributeMap.get(reconnect)!.eventCallBack(this.status); - } else if (statuses.type === FAILED_STATE) { - this.reconnect = reconnect; - this.connectWebSocket() + const distributeEntry = this.distributeMap.get(type); + + if (distributeEntry && typeof distributeEntry.eventCallBack === 'function') { + if (statuses.type === INTERMEDIATE_STATE) { + distributeEntry.eventCallBack(this.status); + } else if (statuses.type === FAILED_STATE) { + this.reconnect = type; // 确认这是您想要的逻辑 + this.connectWebSocket(); + } + } else { + console.error('No valid eventCallBack found for type: ${type}'); } } @@ -332,16 +371,4 @@ export class WebSocketManager { },// 重连 } } - // 汇编、源码展开接口回调函数 - // @ts-ignore - webSocketCallBack = async (cmd: number, result: Uint8Array): unknown => { - const decoder = new TextDecoder(); - const jsonString = decoder.decode(result); - let jsonRes = JSON.parse(jsonString); - if (cmd === Constants.DISASSEMBLY_SAVE_BACK_CMD) { - return - }else if (cmd === Constants.DISASSEMBLY_QUERY_BACK_CMD) { - return - } - } } -- Gitee From 7f2026454fc56fe1c4f0f456f06fcc2f7f7f0c46 Mon Sep 17 00:00:00 2001 From: luobinghao Date: Mon, 9 Dec 2024 16:00:34 +0800 Subject: [PATCH 33/38] =?UTF-8?q?=E6=96=B0=E5=A2=9Efunc=20=E6=B1=87?= =?UTF-8?q?=E7=BC=96=E5=88=86=E6=9E=90=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: luobinghao --- ide/src/trace/bean/PerfAnalysis.ts | 37 +++++ .../trace/sheet/hiperf/TabPerfFuncAsm.html.ts | 61 ++++++++ .../trace/sheet/hiperf/TabPerfFuncAsm.ts | 142 ++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 ide/src/trace/bean/PerfAnalysis.ts create mode 100644 ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts create mode 100644 ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts diff --git a/ide/src/trace/bean/PerfAnalysis.ts b/ide/src/trace/bean/PerfAnalysis.ts new file mode 100644 index 000000000..6ca9673df --- /dev/null +++ b/ide/src/trace/bean/PerfAnalysis.ts @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class PerfFunctionAsmStruct { + totalCount: number = 0; + functionName: string = ''; +} + +export class PerfFunctionSelfCountPerAssembler { + addr: number = 0; + selfcount: number = 0; +} + +export class AsmInstruction { + addr: number = 0; + instruction: string = ''; +} + +export class PerfFunctionAsmShowUpData { + addr: number = 0; + instruction: string = ''; + selfCount: number = 0; + percent: number = 0; +} + diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts new file mode 100644 index 000000000..162497b59 --- /dev/null +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const TabPerfFuncAsmHtml = ` + +
+
+
+
Function Name:
+
Total Count:
+
+ + + + + + + +
+
+`; diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts new file mode 100644 index 000000000..a326fb6b2 --- /dev/null +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts @@ -0,0 +1,142 @@ +import { TabPerfFuncAsmHtml } from "./TabPerfFuncAsm.html"; +import { BaseElement, element } from "../../../../../base-ui/BaseElement"; +import { LitTable } from "../../../../../base-ui/table/lit-table"; +import { + AsmInstruction, + PerfFunctionAsmShowUpData, + PerfFunctionAsmStruct, + PerfFunctionSelfCountPerAssembler, +} from "../../../../bean/PerfAnalysis"; + +@element("tab-perf-func-asm") +export class TabPerfFuncAsm extends BaseElement { + private assmblerTable: LitTable | null | undefined; + private loadingElement: HTMLElement | null | undefined; + private functionName: string = ""; + private totalCount: number = 0; + private functionNameElement: HTMLDivElement | null | undefined; + private totalCountElement: HTMLDivElement | null | undefined; + private functionSelfCountPerAssembler: PerfFunctionSelfCountPerAssembler[] = + []; + private asmInstruction: AsmInstruction[] = []; + private showUpData: PerfFunctionAsmShowUpData[] = []; + private originalShowUpData: PerfFunctionAsmShowUpData[] = []; + + initHtml(): string { + return TabPerfFuncAsmHtml; + } + + initElements(): void { + this.assmblerTable = this.shadowRoot!.querySelector( + "#perf-function-asm-table" + ); + this.loadingElement = + this.shadowRoot!.querySelector("#loading"); + this.functionNameElement = + this.shadowRoot!.querySelector("#function-name"); + this.totalCountElement = + this.shadowRoot!.querySelector("#total-count"); + this.assmblerTable!.style.display = "grid"; + + this.assmblerTable!.itemTextHandleMap.set("addr", (value: unknown) => { + return `0x${(value as number).toString(16)}`; + }); + + this.assmblerTable!.itemTextHandleMap.set("selfCount", (value: unknown) => { + return (value as number) === 0 ? "" : (value as number).toString(); + }); + + this.assmblerTable!.itemTextHandleMap.set("percent", (value: unknown) => { + return (value as number) === 0 ? "" : (value as number).toString(); + }); + + this.assmblerTable!.addEventListener("column-click", ((evt: Event) => { + const { key, sort } = (evt as CustomEvent).detail; + if (key === "selfCount") { + if (sort === 0) { + this.resetSort(); + } else { + this.showUpData.sort((a, b) => { + return sort === 1 + ? a.selfCount - b.selfCount + : b.selfCount - a.selfCount; + }); + this.refreshFunctionAsmData(); + } + } else if (key === "percent") { + if (sort === 0) { + this.resetSort(); + } else { + this.showUpData.sort((a, b) => { + return sort === 1 ? a.percent - b.percent : b.percent - a.percent; + }); + this.refreshFunctionAsmData(); + } + } + }) as EventListener); + } + + private updateTitle(): void { + if (this.functionName) { + this.functionNameElement!.innerHTML = `Function Name: ${this.functionName}`; + this.totalCountElement!.innerHTML = `Total Count: ${this.totalCount}`; + } + } + + private showLoading(): void { + if (this.loadingElement) { + this.loadingElement.removeAttribute("hidden"); + } + } + + private hideLoading(): void { + if (this.loadingElement) { + this.loadingElement.setAttribute("hidden", ""); + } + } + + set data(data: PerfFunctionAsmStruct) { + this.showUpData = []; + this.refreshFunctionAsmData(); + } + + private calcutelateShowUpData(): void { + const selfCountMap = new Map(); + this.functionSelfCountPerAssembler.forEach((item) => { + selfCountMap.set(item.addr, item.selfcount); + }); + + this.showUpData = this.asmInstruction.map((asmItem: AsmInstruction) => ({ + addr: asmItem.addr, + instruction: asmItem.instruction, + selfCount: selfCountMap.get(asmItem.addr) || 0, + percent: + Math.round( + ((selfCountMap.get(asmItem.addr) || 0) / this.totalCount) * 10000 + ) / 100, + })); + this.originalShowUpData = [...this.showUpData]; + } + + private refreshFunctionAsmData(): void { + this.assmblerTable!.recycleDataSource = this.showUpData; + console.log(this.assmblerTable!.recycleDataSource); + this.assmblerTable!.reMeauseHeight(); + } + + public connectedCallback(): void { + new ResizeObserver(() => { + if (this.assmblerTable && this.parentElement) { + this.assmblerTable.style.height = `${ + this.parentElement.clientHeight - 50 + }px`; + this.assmblerTable.reMeauseHeight(); + } + }).observe(this.parentElement!); + } + + private resetSort(): void { + this.showUpData = [...this.originalShowUpData]; + this.refreshFunctionAsmData(); + } +} -- Gitee From 3e187f53ab8c2941f668ed3124e2eeafc7c4e4f0 Mon Sep 17 00:00:00 2001 From: yangxiaoshuai2022 Date: Mon, 9 Dec 2024 17:52:11 +0800 Subject: [PATCH 34/38] activate tab-perf-func-asm when click funcRow Signed-off-by: yangxiaoshuai2022 --- .../trace/component/trace/base/TraceSheet.ts | 17 +++++++++++++ .../component/trace/base/TraceSheetConfig.ts | 5 ++++ .../trace/sheet/hiperf/TabPanePerfAnalysis.ts | 24 ++++++++++++++++++- .../trace/sheet/hiperf/TabPerfFuncAsm.ts | 14 +++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/ide/src/trace/component/trace/base/TraceSheet.ts b/ide/src/trace/component/trace/base/TraceSheet.ts index 6acf03775..d0795db32 100644 --- a/ide/src/trace/component/trace/base/TraceSheet.ts +++ b/ide/src/trace/component/trace/base/TraceSheet.ts @@ -213,6 +213,8 @@ export class TraceSheet extends BaseElement { this.perfAnalysisListener(evt); }); // @ts-ignore + this.getComponentByID('box-perf-analysis')?.addFunctionRowClickEventListener(this.functionAnalysisListener.bind(this)); + // @ts-ignore this.getComponentByID('box-native-statistic-analysis')?.addEventListener('row-click', (e: MouseEvent) => { this.nativeAnalysisListener(e); }); @@ -291,6 +293,21 @@ export class TraceSheet extends BaseElement { } } + private functionAnalysisListener(evt: unknown, vaddrList: Array): void { + // @ts-ignore + this.currentPaneID = 'box-perf-analysis'; + //隐藏除了当前Tab页的其他Tab页 + this.shadowRoot!.querySelectorAll('lit-tabpane').forEach((it): boolean => + it.id !== this.currentPaneID ? (it.hidden = true) : (it.hidden = false) + ); + let pane = this.getPaneByID('tab-perf-func-asm');//通过Id找到需要展示的Tab页 + pane.closeable = true; + pane.hidden = false; + this.litTabs!.activeByKey(pane.key); //显示key值(sheetconfig里面对应的index是一个数字)对应的Tab页 + // @ts-ignore + pane.tab = evt.tableName;//设置Tab页标题 + } + private nativeAnalysisListener(e: MouseEvent): void { //@ts-ignore if (e.detail.button === 2 && e.detail.tableName) { diff --git a/ide/src/trace/component/trace/base/TraceSheetConfig.ts b/ide/src/trace/component/trace/base/TraceSheetConfig.ts index 88665a2fc..b948e0e7b 100644 --- a/ide/src/trace/component/trace/base/TraceSheetConfig.ts +++ b/ide/src/trace/component/trace/base/TraceSheetConfig.ts @@ -136,6 +136,7 @@ import { TabPanePerfAsync } from '../sheet/hiperf/TabPerfAsyncList'; import { TabPaneUserPlugin } from '../sheet/userPlugin/TabPaneUserPlugin'; import { TabPaneDmaFence } from '../sheet/dma-fence/TabPaneDmaFenceSelect'; import { TabPaneSliceChild } from '../sheet/process/TabPaneSliceChild'; +import { TabPerfFuncAsm } from '../sheet/hiperf/TabPerfFuncAsm' export let tabConfig: { [key: string]: { @@ -732,4 +733,8 @@ export let tabConfig: { title: '', type: TabPaneSliceChild, }, + 'tab-perf-func-asm': { + title: '', + type: TabPerfFuncAsm, + } }; diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts index 09559fc19..5dd1a8cc5 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts @@ -25,6 +25,8 @@ import { LitCheckBox } from '../../../../../base-ui/checkbox/LitCheckBox'; import { initSort } from '../SheetUtils'; import { TabpanePerfProfile } from './TabPerfProfile'; import { TabPanePerfAnalysisHtml } from './TabPanePerfAnalysis.html'; +import { WebSocketManager } from '../../../../../webSocket/WebSocketManager'; +import { Constants, TypeConstants } from '../../../../../webSocket/Constants'; @element('tabpane-perf-analysis') export class TabPanePerfAnalysis extends BaseElement { @@ -69,6 +71,8 @@ export class TabPanePerfAnalysis extends BaseElement { private selectedTabThreadId: number = 0; private selectedTabfileName: string = ''; private clickFuncVaddrList: Array = []; + private functionListener!: Function | undefined | null; + private currentSoName: string = ''; set data(val: SelectionParam) { if (val === this.currentSelection) { @@ -551,6 +555,8 @@ export class TabPanePerfAnalysis extends BaseElement { private perfSoLevelClickEvent(it: unknown): void { this.reset(this.tableFunction!, true); this.showAssignLevel(this.tableFunction!, this.perfTableSo!, 3, this.functionData); + // @ts-ignore + this.currentSoName = it.tableName; this.getHiperfFunction(it); let title = ''; if (this.processName.length > 0) { @@ -581,7 +587,19 @@ export class TabPanePerfAnalysis extends BaseElement { // @ts-ignore item.symbolName === it.tableName }) - } + if (this.clickFuncVaddrList.length > 0) { + const textEncoder = new TextEncoder(); + const queryData = { + elf_name: this.currentSoName, //@ts-ignore + vaddr: this.clickFuncVaddrList[0].vaddrInFile, //@ts-ignore + func: it.tableName + }; + const dataString = JSON.stringify(queryData); + const encodedData = textEncoder.encode(dataString); + WebSocketManager.getInstance()?.sendMessage(TypeConstants.DISASSEMBLY_TYPE, Constants.DISASSEMBLY_QUERY_CMD, encodedData); + } + this.functionListener!(it, this.clickFuncVaddrList); + } private sortByColumn(): void { let currentTable: LitTable | null | undefined; @@ -1186,6 +1204,10 @@ export class TabPanePerfAnalysis extends BaseElement { }).observe(this.parentElement!); } + public addFunctionRowClickEventListener(clickEvent: Function): void { + this.functionListener = clickEvent; + } + initHtml(): string { return TabPanePerfAnalysisHtml; } diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts index a326fb6b2..2ad1d175b 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts @@ -7,6 +7,9 @@ import { PerfFunctionAsmStruct, PerfFunctionSelfCountPerAssembler, } from "../../../../bean/PerfAnalysis"; +import { WebSocketManager } from "../../../../../webSocket/WebSocketManager"; +import { ConstructorType } from "../../../../../js-heap/model/UiStruct"; +import { TypeConstants } from "../../../../../webSocket/Constants"; @element("tab-perf-func-asm") export class TabPerfFuncAsm extends BaseElement { @@ -21,6 +24,7 @@ export class TabPerfFuncAsm extends BaseElement { private asmInstruction: AsmInstruction[] = []; private showUpData: PerfFunctionAsmShowUpData[] = []; private originalShowUpData: PerfFunctionAsmShowUpData[] = []; + private currentAsmList: Array = []; initHtml(): string { return TabPerfFuncAsmHtml; @@ -74,6 +78,16 @@ export class TabPerfFuncAsm extends BaseElement { } } }) as EventListener); + // 注册汇编代码请求回调函数 + WebSocketManager.getInstance()?.registerCallback(TypeConstants.DISASSEMBLY_TYPE, this.receiveAsmData.bind(this)); + } + + private receiveAsmData(cmd: unknown, e: unknown): void { + // @ts-ignore + const result = JSON.parse(new TextDecoder().decode(e)); + if (result.resultCode === 0) { + this.currentAsmList = JSON.parse(result.resultMessage); + } } private updateTitle(): void { -- Gitee From 52f412622298a23311bd0f96541c193b5f4d6e19 Mon Sep 17 00:00:00 2001 From: luobinghao Date: Wed, 11 Dec 2024 14:36:03 +0800 Subject: [PATCH 35/38] =?UTF-8?q?function=20=E6=B1=87=E7=BC=96=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1=E8=81=94=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: luobinghao --- ide/src/trace/bean/PerfAnalysis.ts | 19 +- .../trace/component/trace/base/TraceSheet.ts | 24 +- .../trace/sheet/hiperf/TabPanePerfAnalysis.ts | 3 +- .../trace/sheet/hiperf/TabPerfFuncAsm.html.ts | 8 +- .../trace/sheet/hiperf/TabPerfFuncAsm.ts | 213 ++++++++++++++---- 5 files changed, 205 insertions(+), 62 deletions(-) diff --git a/ide/src/trace/bean/PerfAnalysis.ts b/ide/src/trace/bean/PerfAnalysis.ts index 6ca9673df..abe0cf25d 100644 --- a/ide/src/trace/bean/PerfAnalysis.ts +++ b/ide/src/trace/bean/PerfAnalysis.ts @@ -13,21 +13,24 @@ * limitations under the License. */ -export class PerfFunctionAsmStruct { +export class PerfFunctionAsmParam { totalCount: number = 0; - functionName: string = ''; + functionName: string = ""; + vaddrList: Array = []; } -export class PerfFunctionSelfCountPerAssembler { - addr: number = 0; - selfcount: number = 0; -} - -export class AsmInstruction { +export class FormattedAsmInstruction { + selfcount:number = 0; + percent:number = 0; addr: number = 0; instruction: string = ''; } +export class OriginAsmInstruction { + addr:string = ''; + instruction:string = ''; +} + export class PerfFunctionAsmShowUpData { addr: number = 0; instruction: string = ''; diff --git a/ide/src/trace/component/trace/base/TraceSheet.ts b/ide/src/trace/component/trace/base/TraceSheet.ts index d0795db32..d0a797e27 100644 --- a/ide/src/trace/component/trace/base/TraceSheet.ts +++ b/ide/src/trace/component/trace/base/TraceSheet.ts @@ -98,6 +98,8 @@ import { TabPaneSliceChild } from '../sheet/process/TabPaneSliceChild'; import {WebSocketManager} from "../../../../webSocket/WebSocketManager"; import {Constants, TypeConstants} from "../../../../webSocket/Constants"; import {SpStatisticsHttpUtil} from "../../../../statistics/util/SpStatisticsHttpUtil"; +import { PerfFunctionAsmParam } from '../../../bean/PerfAnalysis'; +import { TabPerfFuncAsm } from '../sheet/hiperf/TabPerfFuncAsm'; @element('trace-sheet') export class TraceSheet extends BaseElement { @@ -295,17 +297,27 @@ export class TraceSheet extends BaseElement { private functionAnalysisListener(evt: unknown, vaddrList: Array): void { // @ts-ignore - this.currentPaneID = 'box-perf-analysis'; + this.currentPaneID = "box-perf-analysis"; //隐藏除了当前Tab页的其他Tab页 - this.shadowRoot!.querySelectorAll('lit-tabpane').forEach((it): boolean => - it.id !== this.currentPaneID ? (it.hidden = true) : (it.hidden = false) + this.shadowRoot!.querySelectorAll("lit-tabpane").forEach( + (it): boolean => + it.id !== this.currentPaneID ? (it.hidden = true) : (it.hidden = false) ); - let pane = this.getPaneByID('tab-perf-func-asm');//通过Id找到需要展示的Tab页 + let pane = this.getPaneByID("tab-perf-func-asm"); //通过Id找到需要展示的Tab页 pane.closeable = true; pane.hidden = false; - this.litTabs!.activeByKey(pane.key); //显示key值(sheetconfig里面对应的index是一个数字)对应的Tab页 // @ts-ignore - pane.tab = evt.tableName;//设置Tab页标题 + pane.tab = evt.tableName; //设置Tab页标题 + console.log("lbh: evt", evt); + console.log("lbh: vaadrlist", vaddrList); + let param = new PerfFunctionAsmParam(); + param.vaddrList = vaddrList; + // @ts-ignore + param.functionName = evt.tableName; + // @ts-ignore + param.totalCount = evt.count; + (pane.children.item(0) as TabPerfFuncAsm)!.data = param; + this.litTabs!.activeByKey(pane.key); //显示key值(sheetconfig里面对应的index是一个数字)对应的Tab页 } private nativeAnalysisListener(e: MouseEvent): void { diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts index 5dd1a8cc5..2279015bc 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts @@ -596,10 +596,11 @@ export class TabPanePerfAnalysis extends BaseElement { }; const dataString = JSON.stringify(queryData); const encodedData = textEncoder.encode(dataString); + console.log("lbh: queryData",queryData); WebSocketManager.getInstance()?.sendMessage(TypeConstants.DISASSEMBLY_TYPE, Constants.DISASSEMBLY_QUERY_CMD, encodedData); } this.functionListener!(it, this.clickFuncVaddrList); - } + } private sortByColumn(): void { let currentTable: LitTable | null | undefined; diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts index 162497b59..f3c2b86ea 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts @@ -42,16 +42,22 @@ export const TabPerfFuncAsmHtml = ` left: 50%; transform: translate(-50%, -50%); } +.error-message { + color: red; + margin-top: 5px; + display: none; /* 默认隐藏 */ +}
Function Name:
Total Count:
+
- + diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts index 2ad1d175b..c8ff2d33e 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts @@ -2,14 +2,12 @@ import { TabPerfFuncAsmHtml } from "./TabPerfFuncAsm.html"; import { BaseElement, element } from "../../../../../base-ui/BaseElement"; import { LitTable } from "../../../../../base-ui/table/lit-table"; import { - AsmInstruction, - PerfFunctionAsmShowUpData, - PerfFunctionAsmStruct, - PerfFunctionSelfCountPerAssembler, + FormattedAsmInstruction, + PerfFunctionAsmParam, + OriginAsmInstruction, } from "../../../../bean/PerfAnalysis"; import { WebSocketManager } from "../../../../../webSocket/WebSocketManager"; -import { ConstructorType } from "../../../../../js-heap/model/UiStruct"; -import { TypeConstants } from "../../../../../webSocket/Constants"; +import { Constants, TypeConstants } from "../../../../../webSocket/Constants"; @element("tab-perf-func-asm") export class TabPerfFuncAsm extends BaseElement { @@ -19,13 +17,14 @@ export class TabPerfFuncAsm extends BaseElement { private totalCount: number = 0; private functionNameElement: HTMLDivElement | null | undefined; private totalCountElement: HTMLDivElement | null | undefined; - private functionSelfCountPerAssembler: PerfFunctionSelfCountPerAssembler[] = - []; - private asmInstruction: AsmInstruction[] = []; - private showUpData: PerfFunctionAsmShowUpData[] = []; - private originalShowUpData: PerfFunctionAsmShowUpData[] = []; + private errorMessageElement: HTMLDivElement | null | undefined; + private funcBaseAddr: bigint = BigInt(0); + // Key: offset; Value: selfcount + private funcSampleMap: Map = new Map(); + private showUpData: FormattedAsmInstruction[] = []; + private originalShowUpData: FormattedAsmInstruction[] = []; private currentAsmList: Array = []; - + private formattedAsmIntructionArray: FormattedAsmInstruction[] = []; initHtml(): string { return TabPerfFuncAsmHtml; } @@ -40,13 +39,14 @@ export class TabPerfFuncAsm extends BaseElement { this.shadowRoot!.querySelector("#function-name"); this.totalCountElement = this.shadowRoot!.querySelector("#total-count"); + this.errorMessageElement = this.shadowRoot!.querySelector("#error-message"); this.assmblerTable!.style.display = "grid"; this.assmblerTable!.itemTextHandleMap.set("addr", (value: unknown) => { return `0x${(value as number).toString(16)}`; }); - this.assmblerTable!.itemTextHandleMap.set("selfCount", (value: unknown) => { + this.assmblerTable!.itemTextHandleMap.set("selfcount", (value: unknown) => { return (value as number) === 0 ? "" : (value as number).toString(); }); @@ -54,27 +54,40 @@ export class TabPerfFuncAsm extends BaseElement { return (value as number) === 0 ? "" : (value as number).toString(); }); + this.assmblerTable!.itemTextHandleMap.set("instruction", (value: unknown) => { + return (value as string) === "" ? "INVALID" : (value as string); + }); + this.assmblerTable!.addEventListener("column-click", ((evt: Event) => { const { key, sort } = (evt as CustomEvent).detail; - if (key === "selfCount") { + console.log("lbh:sort", sort) + if (key === "selfcount") { if (sort === 0) { - this.resetSort(); + console.log("lbh: sort0") + this.assmblerTable!.recycleDataSource = this.originalShowUpData; + console.log("lbh:sort recycle", this.assmblerTable!.recycleDataSource) + console.log("lbh:sort originalShowUpData", this.originalShowUpData) + this.assmblerTable!.reMeauseHeight(); } else { this.showUpData.sort((a, b) => { return sort === 1 - ? a.selfCount - b.selfCount - : b.selfCount - a.selfCount; + ? a.selfcount - b.selfcount + : b.selfcount - a.selfcount; }); - this.refreshFunctionAsmData(); + this.assmblerTable!.recycleDataSource = this.showUpData; + this.assmblerTable!.reMeauseHeight(); } + console.log("after sort: ", this.originalShowUpData) } else if (key === "percent") { if (sort === 0) { - this.resetSort(); + this.assmblerTable!.recycleDataSource = this.originalShowUpData; + this.assmblerTable!.reMeauseHeight(); } else { this.showUpData.sort((a, b) => { return sort === 1 ? a.percent - b.percent : b.percent - a.percent; }); - this.refreshFunctionAsmData(); + this.assmblerTable!.recycleDataSource = this.showUpData; + this.assmblerTable!.reMeauseHeight(); } } }) as EventListener); @@ -95,6 +108,7 @@ export class TabPerfFuncAsm extends BaseElement { this.functionNameElement!.innerHTML = `Function Name: ${this.functionName}`; this.totalCountElement!.innerHTML = `Total Count: ${this.totalCount}`; } + console.log(this.totalCount) } private showLoading(): void { @@ -109,33 +123,145 @@ export class TabPerfFuncAsm extends BaseElement { } } - set data(data: PerfFunctionAsmStruct) { - this.showUpData = []; - this.refreshFunctionAsmData(); + private showError(message: string): void { + if (this.errorMessageElement) { + this.errorMessageElement.textContent = message; + this.errorMessageElement.style.display = 'block'; + } } - private calcutelateShowUpData(): void { - const selfCountMap = new Map(); - this.functionSelfCountPerAssembler.forEach((item) => { - selfCountMap.set(item.addr, item.selfcount); + private hideError(): void { + if (this.errorMessageElement) { + this.errorMessageElement.style.display = 'none'; + } + } + + set data(data: PerfFunctionAsmParam) { + if (this.functionName === data.functionName) { + return; + } + + (async () => { + try { + this.clearData(); + this.functionName = data.functionName; + this.totalCount = data.totalCount; + this.updateTitle(); + this.showLoading(); + // @ts-ignore + const vaddrInFile = data.vaddrList[0].vaddrInFile; + // 1. 先转成 BigInt + // 2. 用 asUintN 转成无符号64位 + // 3. 如果需要用作数值运算,再转回 Number + this.funcBaseAddr = BigInt.asUintN(64, BigInt(vaddrInFile)); + // 1. 计算采样数据 + this.calculateFuncAsmSapleCount(data.vaddrList); + // 2. 等待汇编指令数据 + let callback: (cmd: number, e: Uint8Array) => void; + + await Promise.race([ + new Promise((resolve, reject) => { + callback = (cmd: number, e: Uint8Array) => { + try { + console.log('Received cmd:', cmd, 'Expected:', Constants.DISASSEMBLY_QUERY_BACK_CMD); + + if (cmd === Constants.DISASSEMBLY_QUERY_BACK_CMD) { + const result = JSON.parse(new TextDecoder().decode(e)); + if (result.resultCode === 0) { + this.formatAsmInstruction(JSON.parse(result.resultMessage)); + this.calcutelateShowUpData(); + resolve(); + } else { + reject(new Error(`Failed with code: ${result.resultCode}`)); + } + } else { + reject(new Error(`Unexpected command: ${cmd}`)); + } + WebSocketManager.getInstance()?.unregisterCallback(TypeConstants.DISASSEMBLY_TYPE, callback); + } catch (error) { + WebSocketManager.getInstance()?.unregisterCallback(TypeConstants.DISASSEMBLY_TYPE, callback); + reject(error); + } + }; + + WebSocketManager.getInstance()?.registerCallback(TypeConstants.DISASSEMBLY_TYPE, callback); + }), + new Promise((_, reject) => setTimeout(() => { + WebSocketManager.getInstance()?.unregisterCallback(TypeConstants.DISASSEMBLY_TYPE, callback); + reject(new Error('Request timeout')); + }, 5000)) + ]); + + // 5. 更新表格 + + } catch (error) { + console.error('Error:', error); + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + this.showError(`Error: can't get assembly code because ${errorMessage},show sample list without assembly code`); + this.calcutelateErrorShowUpData(); + } finally { + console.log("lbh:finally:originalShowUpData ", this.originalShowUpData) + console.log("lbh:finally:funcBaseAddr ", this.funcBaseAddr) + console.log("lbh:finally:funcSampleMap ", this.funcSampleMap) + this.showUpData = [...this.originalShowUpData]; + this.assmblerTable!.recycleDataSource = this.showUpData; + this.assmblerTable!.reMeauseHeight(); + this.hideLoading(); + } + })(); + } + + private calcutelateErrorShowUpData(): void { + this.funcSampleMap.forEach((selfCount, offsetToVaddr) => { + this.originalShowUpData.push({ + selfcount: selfCount, + percent: Math.round((selfCount / this.totalCount) * 10000) / 100, + // 地址计算也使用 BigInt + addr: Number(BigInt.asUintN(64, this.funcBaseAddr + BigInt(offsetToVaddr))), + instruction: '' + }) + }) + console.log("lbh:calcutelateErrorShowUpData originalShowUpData ", this.originalShowUpData) + console.log("lbh:calcutelateErrorShowUpData funcBaseAddr ", this.funcBaseAddr) + console.log("lbh:calcutelateErrorShowUpData funcSampleMap ", this.funcSampleMap) + } + + private calculateFuncAsmSapleCount(vaddrList: Array): void { + vaddrList.forEach(item => { + // @ts-ignore + const count = this.funcSampleMap.get(item.offsetToVaddr) || 0; + // @ts-ignore + this.funcSampleMap.set(item.offsetToVaddr, count + 1); }); + } - this.showUpData = this.asmInstruction.map((asmItem: AsmInstruction) => ({ - addr: asmItem.addr, - instruction: asmItem.instruction, - selfCount: selfCountMap.get(asmItem.addr) || 0, - percent: - Math.round( - ((selfCountMap.get(asmItem.addr) || 0) / this.totalCount) * 10000 - ) / 100, - })); - this.originalShowUpData = [...this.showUpData]; + private formatAsmInstruction(originAsmInstruction: Array) { + this.formattedAsmIntructionArray = originAsmInstruction.map(instructs => ({ + selfcount: 0, + percent: 0, + addr: parseInt(instructs.addr, 16), + instruction: instructs.instruction, + }) as FormattedAsmInstruction); } - private refreshFunctionAsmData(): void { - this.assmblerTable!.recycleDataSource = this.showUpData; - console.log(this.assmblerTable!.recycleDataSource); - this.assmblerTable!.reMeauseHeight(); + + private clearData(): void { + this.hideError(); + this.funcSampleMap.clear(); + this.showUpData = []; + this.originalShowUpData = []; + this.currentAsmList = []; + this.formattedAsmIntructionArray = []; + this.assmblerTable!.recycleDataSource = []; + } + + private calcutelateShowUpData(): void { + this.funcSampleMap.forEach((selfCount, offsetToVaddr) => { + let instructionPosition = offsetToVaddr / 4; + this.formattedAsmIntructionArray[instructionPosition].selfcount = selfCount; + this.formattedAsmIntructionArray[instructionPosition].percent = Math.round((selfCount / this.totalCount) * 10000) / 100; + }) + this.originalShowUpData = this.formattedAsmIntructionArray; } public connectedCallback(): void { @@ -148,9 +274,4 @@ export class TabPerfFuncAsm extends BaseElement { } }).observe(this.parentElement!); } - - private resetSort(): void { - this.showUpData = [...this.originalShowUpData]; - this.refreshFunctionAsmData(); - } } -- Gitee From 1e5f81a6c2684cf79a0b90951a70dd24d0861531 Mon Sep 17 00:00:00 2001 From: liufei Date: Thu, 12 Dec 2024 20:11:42 +0800 Subject: [PATCH 36/38] =?UTF-8?q?fix:hiperf=5FsoFunc=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E4=BF=AE=E6=94=B9pid,tid=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liufei --- .../trace/sheet/hiperf/TabPanePerfAnalysis.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts index 2279015bc..afe6161c9 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts @@ -67,8 +67,6 @@ export class TabPanePerfAnalysis extends BaseElement { private isComplete: boolean = true; private currentSelectionParam: SelectionParam | undefined | null; private vaddrList: Array = []; - private selectedTabProcessId: number = 0; - private selectedTabThreadId: number = 0; private selectedTabfileName: string = ''; private clickFuncVaddrList: Array = []; private functionListener!: Function | undefined | null; @@ -413,8 +411,6 @@ export class TabPanePerfAnalysis extends BaseElement { // @ts-ignore this.processName = it.tableName; this.perfAnalysisPie?.hideTip(); - // @ts-ignore - this.selectedTabProcessId = it.pid; } private threadPieChart(val: SelectionParam): void { @@ -486,8 +482,6 @@ export class TabPanePerfAnalysis extends BaseElement { // @ts-ignore this.threadName = it.tableName; this.perfAnalysisPie?.hideTip(); - // @ts-ignore - this.selectedTabThreadId = it.tid; } private initPerfAnalysisPieConfig(): void { @@ -579,9 +573,9 @@ export class TabPanePerfAnalysis extends BaseElement { private functionClickEvent(it: unknown) { this.clickFuncVaddrList = this.vaddrList.filter((item: unknown) => { // @ts-ignore - return item.process_id === this.selectedTabProcessId && + return item.process_id === it.pid && // @ts-ignore - item.thread_id === this.selectedTabThreadId && + item.thread_id === it.tid && // @ts-ignore item.libName === this.selectedTabfileName && // @ts-ignore @@ -596,7 +590,7 @@ export class TabPanePerfAnalysis extends BaseElement { }; const dataString = JSON.stringify(queryData); const encodedData = textEncoder.encode(dataString); - console.log("lbh: queryData",queryData); + console.log("lbh: queryData", queryData); WebSocketManager.getInstance()?.sendMessage(TypeConstants.DISASSEMBLY_TYPE, Constants.DISASSEMBLY_QUERY_CMD, encodedData); } this.functionListener!(it, this.clickFuncVaddrList); -- Gitee From 6c63da84085cf0429ba47a6770d701919fd4c8b5 Mon Sep 17 00:00:00 2001 From: luobinghao Date: Fri, 13 Dec 2024 15:37:09 +0800 Subject: [PATCH 37/38] delete wrong register callback and add timeout limit Signed-off-by: luobinghao --- .../trace/sheet/hiperf/TabPerfFuncAsm.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts index c8ff2d33e..4f30b35f2 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts @@ -91,16 +91,6 @@ export class TabPerfFuncAsm extends BaseElement { } } }) as EventListener); - // 注册汇编代码请求回调函数 - WebSocketManager.getInstance()?.registerCallback(TypeConstants.DISASSEMBLY_TYPE, this.receiveAsmData.bind(this)); - } - - private receiveAsmData(cmd: unknown, e: unknown): void { - // @ts-ignore - const result = JSON.parse(new TextDecoder().decode(e)); - if (result.resultCode === 0) { - this.currentAsmList = JSON.parse(result.resultMessage); - } } private updateTitle(): void { @@ -189,11 +179,8 @@ export class TabPerfFuncAsm extends BaseElement { new Promise((_, reject) => setTimeout(() => { WebSocketManager.getInstance()?.unregisterCallback(TypeConstants.DISASSEMBLY_TYPE, callback); reject(new Error('Request timeout')); - }, 5000)) + }, 20000)) ]); - - // 5. 更新表格 - } catch (error) { console.error('Error:', error); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; -- Gitee From 9bed2e5677d23f6f82fc624b4bf52fe80ee2ddfc Mon Sep 17 00:00:00 2001 From: z30061262 Date: Sun, 29 Dec 2024 20:46:18 +0800 Subject: [PATCH 38/38] =?UTF-8?q?so=E6=B1=87=E7=BC=96=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E8=A1=8C=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhuheng --- ide/src/trace/bean/PerfAnalysis.ts | 2 + .../trace/component/trace/base/TraceSheet.ts | 140 ++++++++++++------ .../trace/sheet/hiperf/TabPanePerfAnalysis.ts | 11 +- .../trace/sheet/hiperf/TabPerfFuncAsm.html.ts | 10 +- .../trace/sheet/hiperf/TabPerfFuncAsm.ts | 84 ++++++----- ide/src/webSocket/Constants.ts | 11 +- ide/src/webSocket/WebSocketManager.ts | 43 +++--- 7 files changed, 193 insertions(+), 108 deletions(-) diff --git a/ide/src/trace/bean/PerfAnalysis.ts b/ide/src/trace/bean/PerfAnalysis.ts index abe0cf25d..1f73a5a16 100644 --- a/ide/src/trace/bean/PerfAnalysis.ts +++ b/ide/src/trace/bean/PerfAnalysis.ts @@ -24,11 +24,13 @@ export class FormattedAsmInstruction { percent:number = 0; addr: number = 0; instruction: string = ''; + sourceLine: string = ''; } export class OriginAsmInstruction { addr:string = ''; instruction:string = ''; + sourceLine: string = ''; } export class PerfFunctionAsmShowUpData { diff --git a/ide/src/trace/component/trace/base/TraceSheet.ts b/ide/src/trace/component/trace/base/TraceSheet.ts index d0a797e27..0dbe92d48 100644 --- a/ide/src/trace/component/trace/base/TraceSheet.ts +++ b/ide/src/trace/component/trace/base/TraceSheet.ts @@ -95,11 +95,20 @@ import { PerfToolStruct } from '../../../database/ui-worker/ProcedureWorkerPerfT import { GpuCounterStruct } from '../../../database/ui-worker/ProcedureWorkerGpuCounter'; import { TabPaneGpuCounter } from '../sheet/gpu-counter/TabPaneGpuCounter'; import { TabPaneSliceChild } from '../sheet/process/TabPaneSliceChild'; +import { TabPerfFuncAsm } from '../sheet/hiperf/TabPerfFuncAsm'; +import { XpowerStatisticStruct } from '../../../database/ui-worker/ProcedureWorkerXpowerStatistic'; +import { XpowerAppDetailStruct } from '../../../database/ui-worker/ProcedureWorkerXpowerAppDetail'; +import { XpowerWifiStruct } from '../../../database/ui-worker/ProcedureWorkerXpowerWifi'; +import { TabPaneXpowerStatisticCurrentData } from '../sheet/xpower/TabPaneXpowerStatisticCurrentData'; +import { XpowerThreadInfoStruct } from '../../../database/ui-worker/ProcedureWorkerXpowerThreadInfo'; +import { TabPaneXpowerThreadInfoSelection } from '../sheet/xpower/TabPaneXpowerThreadInfoSelection'; +import { TabPaneXpowerGpuFreqSelection } from '../sheet/xpower/TabPaneXpowerGpuFreqSelection'; +import { XpowerGpuFreqStruct } from '../../../database/ui-worker/ProcedureWorkerXpowerGpuFreq'; import {WebSocketManager} from "../../../../webSocket/WebSocketManager"; import {Constants, TypeConstants} from "../../../../webSocket/Constants"; -import {SpStatisticsHttpUtil} from "../../../../statistics/util/SpStatisticsHttpUtil"; import { PerfFunctionAsmParam } from '../../../bean/PerfAnalysis'; -import { TabPerfFuncAsm } from '../sheet/hiperf/TabPerfFuncAsm'; +import { info,error } from '../../../../log/Log'; + @element('trace-sheet') export class TraceSheet extends BaseElement { @@ -126,7 +135,7 @@ export class TraceSheet extends BaseElement { private tabPaneHeight: string = ''; private enc = new TextEncoder(); private dec = new TextDecoder(); - private REQ_BUF_SIZE = 4 * 1024 * 1024; + private REQ_BUF_SIZE = 1024 * 1024; static get observedAttributes(): string[] { return ['mode']; @@ -221,13 +230,16 @@ export class TraceSheet extends BaseElement { this.nativeAnalysisListener(e); }); // @ts-ignore - this.getComponentByID('box-io-tier-statistics-analysis')?.addEventListener('row-click', (evt: MouseEvent) => { - // @ts-ignore - if (evt.detail.button === 2 && evt.detail.tableName) { - let pane = this.getPaneByID('box-io-calltree'); - this.litTabs!.activeByKey(pane.key); + this.getComponentByID('box-io-tier-statistics-analysis')?.addEventListener( + 'row-click', + (evt: MouseEvent) => { + // @ts-ignore + if (evt.detail.button === 2 && evt.detail.tableName) { + let pane = this.getPaneByID('box-io-calltree'); + this.litTabs!.activeByKey(pane.key); + } } - }); + ); // @ts-ignore this.getComponentByID('box-virtual-memory-statistics-analysis')?.addEventListener( 'row-click', @@ -279,7 +291,7 @@ export class TraceSheet extends BaseElement { }); // @ts-ignore this.getComponentByID('box-thread-states')?.addEventListener('td-click', (evt: unknown) => { - this.tdClickHandler(evt); + this.tdClickHandler(evt, false); }); // @ts-ignore this.getComponentByID('box-slices')?.addEventListener('td-click', (evt: unknown) => { @@ -308,8 +320,6 @@ export class TraceSheet extends BaseElement { pane.hidden = false; // @ts-ignore pane.tab = evt.tableName; //设置Tab页标题 - console.log("lbh: evt", evt); - console.log("lbh: vaadrlist", vaddrList); let param = new PerfFunctionAsmParam(); param.vaddrList = vaddrList; // @ts-ignore @@ -420,17 +430,19 @@ export class TraceSheet extends BaseElement { this.initNavElements(tabsPackUp!, borderTop, initialHeight); this.exportBt = this.shadowRoot?.querySelector('#export-btn'); tabsOpenUp!.onclick = (): void => { - this.tabs!.style.height = `${window.innerHeight - this.search!.offsetHeight - this.timerShaft!.offsetHeight - borderTop - }px`; + this.tabs!.style.height = `${ + window.innerHeight - this.search!.offsetHeight - this.timerShaft!.offsetHeight - borderTop + }px`; let litTabpane: NodeListOf | undefined | null = this.shadowRoot?.querySelectorAll('#tabs > lit-tabpane'); litTabpane!.forEach((node: HTMLDivElement): void => { - node!.style.height = `${window.innerHeight - + node!.style.height = `${ + window.innerHeight - this.search!.offsetHeight - this.timerShaft!.offsetHeight - this.navRoot!.offsetHeight - borderTop - }px`; + }px`; initialHeight.node = node!.style.height; }); initialHeight.tabs = this.tabs!.style.height; @@ -526,7 +538,7 @@ export class TraceSheet extends BaseElement { // 只要没有移动到边界区域都会进入该条件 that.navRoot!.offsetHeight <= newHeight && that.search!.offsetHeight + that.timerShaft!.offsetHeight + borderTop + that.spacer!.offsetHeight <= - window.innerHeight - newHeight + window.innerHeight - newHeight ) { that.tabs!.style.height = `${newHeight}px`; litTabpane!.style.height = `${newHeight - that.navRoot!.offsetHeight}px`; @@ -541,19 +553,21 @@ export class TraceSheet extends BaseElement { window.innerHeight - newHeight ) { // 该条件在面板高度置顶时触发 - that.tabs!.style.height = `${window.innerHeight - + that.tabs!.style.height = `${ + window.innerHeight - that.search!.offsetHeight - that.timerShaft!.offsetHeight - borderTop - that.spacer!.offsetHeight - }px`; - litTabpane!.style.height = `${window.innerHeight - + }px`; + litTabpane!.style.height = `${ + window.innerHeight - that.search!.offsetHeight - that.timerShaft!.offsetHeight - that.navRoot!.offsetHeight - borderTop - that.spacer!.offsetHeight - }px`; + }px`; tabsPackUp!.name = 'down'; } that.tabPaneHeight = litTabpane!.style.height; @@ -621,12 +635,12 @@ export class TraceSheet extends BaseElement { const file = filesToUpload[i]; try { await this.uploadSingleFile(file); - console.log('File ${file.name} uploaded successfully.'); - } catch (error) { - console.error('Failed to upload file: ${file.name}, error'); + info(`File ${file.name} uploaded successfully.`); + } catch (err) { + error(`Failed to upload file: ${file.name}, error: `, err); } } - console.log("All files have been uploaded."); + info(`All files have been uploaded.`); } @@ -656,13 +670,12 @@ export class TraceSheet extends BaseElement { wsInstance!.unregisterCallback(TypeConstants.DISASSEMBLY_TYPE, onAckReceived) clearTimeout(timeout); if (jsonRes.resultCode == 0) { - console.log('ACK received for file: ${jsonRes.fileName}, index: ${jsonRes.bufferIndex}'); bufferIndex++; // 当收到对应分片的 ACK 时,resolve Promise,继续上传下一个分片 resolve(); }else{ // 上传失败,拒绝 Promise 并返回 - reject(new Error('Upload failed for file: ${fileName}, index: ${jsonRes.bufferIndex})')); + reject(new Error(`Upload failed for file: ${fileName}, index: ${jsonRes.bufferIndex})`)); } } } @@ -697,7 +710,6 @@ export class TraceSheet extends BaseElement { buffer = null; blob = null; } - console.log('Upload complete for file: ${fileName}'); } }; @@ -711,7 +723,17 @@ export class TraceSheet extends BaseElement { let table2 = Array.from( (currentTab.firstChild as BaseElement).shadowRoot?.querySelectorAll('lit-table') || [] ); - let tables = [...table1, ...table2]; + let componentTopTable = undefined; + if ( + (currentTab.firstChild as BaseElement).shadowRoot?.querySelector('#tb-counter') && + ((currentTab.firstChild as BaseElement).shadowRoot?.querySelector('#tb-counter')?.firstChild as BaseElement) + ) { + componentTopTable = ((currentTab.firstChild as BaseElement).shadowRoot?.querySelector('#tb-counter') + ?.firstChild as BaseElement)!.shadowRoot?.querySelectorAll('lit-table'); + } + + let table3 = Array.from(componentTopTable || []); + let tables = [...table1, ...table2, ...table3]; for (let table of tables) { if (!table.hasAttribute('hideDownload')) { @@ -814,11 +836,30 @@ export class TraceSheet extends BaseElement { this.displayTab('current-selection').setHangData(data, sp, scrollCallback); displayClockData = (data: ClockStruct): Promise => this.displayTab('current-selection').setClockData(data); - displayDmaFenceData = (data: DmaFenceStruct, rowData: unknown): void =>//展示tab页内容 + displayDmaFenceData = ( + data: DmaFenceStruct, + rowData: unknown + ): void => //展示tab页内容 // @ts-ignore this.displayTab('current-selection').setDmaFenceData(data, rowData); displayXpowerData = (data: XpowerStruct): Promise => this.displayTab('current-selection').setXpowerData(data); + displayXpowerDisplayData = (data: XpowerAppDetailStruct): Promise => + this.displayTab('current-selection').setXpowerDisplayData(data); + displayXpowerWifiPacketsData = (data: XpowerWifiStruct): Promise => + this.displayTab('current-selection').setXpowerWifiPacketsData(data); + displayXpowerBytesWifiData = (data: XpowerWifiStruct): Promise => + this.displayTab('current-selection').setXpowerWifiBytesData(data); + displayXpowerStatisticData = (data: XpowerStatisticStruct): void => + this.displayTab( + 'box-xpower-statistic-current-data' + ).setXpowerStatisticCurrentData(data); + displayXpowerThreadInfoData = (dataList: Array): void => { + this.displayTab('box-xpower-thread-info-selection').setThreadInfoData(dataList); + }; + displayXpowerGpuFreqData = (dataList: Array): void => { + this.displayTab('box-xpower-gpu-freq-selection').setGpuFreqData(dataList); + }; displayPerfToolsData = (data: PerfToolStruct): void => this.displayTab('current-selection').setPerfToolsData(data); displayIrqData = (data: IrqStruct): void => @@ -859,9 +900,15 @@ export class TraceSheet extends BaseElement { data: FuncStruct, scrollCallback: Function, callback?: (data: Array, str: string, binderTid: number) => void, - distributedCallback?: (dataList: FuncStruct[]) => void, + distributedCallback?: (dataList: FuncStruct[]) => void ): Promise => - this.displayTab(...names).setFunctionData(data, threadName, scrollCallback, callback, distributedCallback); + this.displayTab(...names).setFunctionData( + data, + threadName, + scrollCallback, + callback, + distributedCallback + ); displayCpuData = ( data: CpuStruct, callback: ((data: WakeupBean | null) => void) | undefined = undefined, @@ -1063,10 +1110,9 @@ export class TraceSheet extends BaseElement { ]; }; displayUserPlugin = (selectData: unknown): void => { - this.displayTab("tab-pane-userplugin").data = selectData; + this.displayTab('tab-pane-userplugin').data = selectData; }; - displayGpuCounterData = (data: GpuCounterStruct): void => { this.displayTab('box-gpu-counter').data = data; }; @@ -1257,32 +1303,32 @@ export class TraceSheet extends BaseElement { window.publish(window.SmartEvent.UI.ShowBottomTab, { show: show, delta: delta }); } - tdClickHandler(e: unknown): void { + tdClickHandler(e: unknown, isDependCpu?: boolean): void { // @ts-ignore this.currentPaneID = e.target.parentElement.id; //隐藏除了当前Tab页的其他Tab页 this.shadowRoot!.querySelectorAll('lit-tabpane').forEach((it): boolean => it.id !== this.currentPaneID ? (it.hidden = true) : (it.hidden = false) - );//todo:看能不能优化 - let pane = this.getPaneByID('box-cpu-child');//通过Id找到需要展示的Tab页 - pane.closeable = true;//关闭的ican显示 + ); //todo:看能不能优化 + let pane = this.getPaneByID('box-cpu-child'); //通过Id找到需要展示的Tab页 + pane.closeable = true; //关闭的ican显示 pane.hidden = false; this.litTabs!.activeByKey(pane.key); //显示key值对应的Tab页 // @ts-ignore - pane.tab = e.detail.tabTitle ? e.detail.tabTitle : Utils.transferPTSTitle(e.detail.title);//设置Tab页标题,有的标题可直接用,有的标题需在此转换成需要展示的字符串 + pane.tab = e.detail.tabTitle ? e.detail.tabTitle : Utils.transferPTSTitle(e.detail.title); //设置Tab页标题,有的标题可直接用,有的标题需在此转换成需要展示的字符串 let param = new BoxJumpParam(); param.traceId = this.selection!.traceId; param.leftNs = this.selection!.leftNs; param.rightNs = this.selection!.rightNs; - param.cpus = this.selection!.cpus; + param.cpus = isDependCpu ? this.selection!.cpus : []; // @ts-ignore param.state = e.detail.summary ? '' : e.detail.state; // @ts-ignore param.processId = e.detail.summary ? this.selection.processIds : e.detail.pid; // @ts-ignore param.threadId = e.detail.summary ? this.selection.threadIds : e.detail.tid; - param.isJumpPage = true;// @ts-ignore - param.currentId = e.target.parentElement.id;//根据父Tab页的标题,确认子Tab页的dur是否需要处理 + param.isJumpPage = true; // @ts-ignore + param.currentId = e.target.parentElement.id; //根据父Tab页的标题,确认子Tab页的dur是否需要处理 (pane.children.item(0) as TabPaneBoxChild).data = param; } @@ -1294,20 +1340,20 @@ export class TraceSheet extends BaseElement { this.shadowRoot!.querySelectorAll('lit-tabpane').forEach((it): boolean => it.id !== this.currentPaneID ? (it.hidden = true) : (it.hidden = false) ); - let pane = this.getPaneByID('box-slice-child');//通过Id找到需要展示的Tab页 + let pane = this.getPaneByID('box-slice-child'); //通过Id找到需要展示的Tab页 pane.closeable = true; pane.hidden = false; this.litTabs!.activeByKey(pane.key); //显示key值(sheetconfig里面对应的index是一个数字)对应的Tab页 // @ts-ignore - pane.tab = e.detail.tabTitle;//设置Tab页标题 + pane.tab = e.detail.tabTitle; //设置Tab页标题 let param = new SliceBoxJumpParam(); param.traceId = this.selection!.traceId; param.leftNs = this.selection!.leftNs; param.rightNs = this.selection!.rightNs; param.processId = this.selection!.processIds; - param.threadId = this.selection!.funTids;//@ts-ignore2 - param.name = e.detail.allName ? e.detail.allName : [e.detail.name];//@ts-ignore2 - param.isJumpPage = true;// @ts-ignore + param.threadId = this.selection!.funTids; //@ts-ignore2 + param.name = e.detail.allName ? e.detail.allName : [e.detail.name]; //@ts-ignore2 + param.isJumpPage = true; // @ts-ignore param.isSummary = e.detail.allName ? true : false; (pane.children.item(0) as TabPaneSliceChild).data = { param: param, selection: this.selection }; } diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts index afe6161c9..5763fa5b0 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPanePerfAnalysis.ts @@ -67,6 +67,8 @@ export class TabPanePerfAnalysis extends BaseElement { private isComplete: boolean = true; private currentSelectionParam: SelectionParam | undefined | null; private vaddrList: Array = []; + private selectedTabProcessId: number = 0; + private selectedTabThreadId: number = 0; private selectedTabfileName: string = ''; private clickFuncVaddrList: Array = []; private functionListener!: Function | undefined | null; @@ -411,6 +413,8 @@ export class TabPanePerfAnalysis extends BaseElement { // @ts-ignore this.processName = it.tableName; this.perfAnalysisPie?.hideTip(); + // @ts-ignore + this.selectedTabProcessId = it.pid; } private threadPieChart(val: SelectionParam): void { @@ -482,6 +486,8 @@ export class TabPanePerfAnalysis extends BaseElement { // @ts-ignore this.threadName = it.tableName; this.perfAnalysisPie?.hideTip(); + // @ts-ignore + this.selectedTabThreadId = it.tid; } private initPerfAnalysisPieConfig(): void { @@ -573,9 +579,9 @@ export class TabPanePerfAnalysis extends BaseElement { private functionClickEvent(it: unknown) { this.clickFuncVaddrList = this.vaddrList.filter((item: unknown) => { // @ts-ignore - return item.process_id === it.pid && + return item.process_id === this.selectedTabProcessId && // @ts-ignore - item.thread_id === it.tid && + item.thread_id === this.selectedTabThreadId && // @ts-ignore item.libName === this.selectedTabfileName && // @ts-ignore @@ -590,7 +596,6 @@ export class TabPanePerfAnalysis extends BaseElement { }; const dataString = JSON.stringify(queryData); const encodedData = textEncoder.encode(dataString); - console.log("lbh: queryData", queryData); WebSocketManager.getInstance()?.sendMessage(TypeConstants.DISASSEMBLY_TYPE, Constants.DISASSEMBLY_QUERY_CMD, encodedData); } this.functionListener!(it, this.clickFuncVaddrList); diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts index f3c2b86ea..278d48687 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.html.ts @@ -53,14 +53,16 @@ export const TabPerfFuncAsmHtml = `
Function Name:
Total Count:
+
.text Section File Off:
- - - - + + + + +
diff --git a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts index 4f30b35f2..7b07fb176 100644 --- a/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts +++ b/ide/src/trace/component/trace/sheet/hiperf/TabPerfFuncAsm.ts @@ -17,29 +17,35 @@ export class TabPerfFuncAsm extends BaseElement { private totalCount: number = 0; private functionNameElement: HTMLDivElement | null | undefined; private totalCountElement: HTMLDivElement | null | undefined; + private textFileOffElement: HTMLDivElement | null | undefined; private errorMessageElement: HTMLDivElement | null | undefined; private funcBaseAddr: bigint = BigInt(0); // Key: offset; Value: selfcount private funcSampleMap: Map = new Map(); private showUpData: FormattedAsmInstruction[] = []; private originalShowUpData: FormattedAsmInstruction[] = []; - private currentAsmList: Array = []; private formattedAsmIntructionArray: FormattedAsmInstruction[] = []; + private resizeObserver: ResizeObserver | null = null; + initHtml(): string { return TabPerfFuncAsmHtml; } initElements(): void { this.assmblerTable = this.shadowRoot!.querySelector( - "#perf-function-asm-table" + "#perf-function-asm-table" ); this.loadingElement = - this.shadowRoot!.querySelector("#loading"); + this.shadowRoot!.querySelector("#loading"); this.functionNameElement = - this.shadowRoot!.querySelector("#function-name"); + this.shadowRoot!.querySelector("#function-name"); this.totalCountElement = - this.shadowRoot!.querySelector("#total-count"); + this.shadowRoot!.querySelector("#total-count"); + this.textFileOffElement = + this.shadowRoot!.querySelector("#text-file-off"); + this.textFileOffElement!.style.display = 'none'; this.errorMessageElement = this.shadowRoot!.querySelector("#error-message"); + this.assmblerTable!.style.display = "grid"; this.assmblerTable!.itemTextHandleMap.set("addr", (value: unknown) => { @@ -58,26 +64,25 @@ export class TabPerfFuncAsm extends BaseElement { return (value as string) === "" ? "INVALID" : (value as string); }); + this.assmblerTable!.itemTextHandleMap.set("sourceLine", (value: unknown) => { + return (value as string) || ""; + }); + this.assmblerTable!.addEventListener("column-click", ((evt: Event) => { - const { key, sort } = (evt as CustomEvent).detail; - console.log("lbh:sort", sort) + const {key, sort} = (evt as CustomEvent).detail; if (key === "selfcount") { if (sort === 0) { - console.log("lbh: sort0") this.assmblerTable!.recycleDataSource = this.originalShowUpData; - console.log("lbh:sort recycle", this.assmblerTable!.recycleDataSource) - console.log("lbh:sort originalShowUpData", this.originalShowUpData) this.assmblerTable!.reMeauseHeight(); } else { this.showUpData.sort((a, b) => { return sort === 1 - ? a.selfcount - b.selfcount - : b.selfcount - a.selfcount; + ? a.selfcount - b.selfcount + : b.selfcount - a.selfcount; }); this.assmblerTable!.recycleDataSource = this.showUpData; this.assmblerTable!.reMeauseHeight(); } - console.log("after sort: ", this.originalShowUpData) } else if (key === "percent") { if (sort === 0) { this.assmblerTable!.recycleDataSource = this.originalShowUpData; @@ -98,7 +103,6 @@ export class TabPerfFuncAsm extends BaseElement { this.functionNameElement!.innerHTML = `Function Name: ${this.functionName}`; this.totalCountElement!.innerHTML = `Total Count: ${this.totalCount}`; } - console.log(this.totalCount) } private showLoading(): void { @@ -127,7 +131,7 @@ export class TabPerfFuncAsm extends BaseElement { } set data(data: PerfFunctionAsmParam) { - if (this.functionName === data.functionName) { + if (this.functionName === data.functionName || data.functionName === undefined) { return; } @@ -153,11 +157,16 @@ export class TabPerfFuncAsm extends BaseElement { new Promise((resolve, reject) => { callback = (cmd: number, e: Uint8Array) => { try { - console.log('Received cmd:', cmd, 'Expected:', Constants.DISASSEMBLY_QUERY_BACK_CMD); - + if (cmd === Constants.DISASSEMBLY_QUERY_BACK_CMD) { const result = JSON.parse(new TextDecoder().decode(e)); if (result.resultCode === 0) { + if (result.anFileOff) { + this.textFileOffElement!.innerHTML = `.text Section File Off ${result.anFileOff}`; + this.textFileOffElement!.style.display = 'block'; + } else { + this.textFileOffElement!.style.display = 'none'; + } this.formatAsmInstruction(JSON.parse(result.resultMessage)); this.calcutelateShowUpData(); resolve(); @@ -173,7 +182,7 @@ export class TabPerfFuncAsm extends BaseElement { reject(error); } }; - + WebSocketManager.getInstance()?.registerCallback(TypeConstants.DISASSEMBLY_TYPE, callback); }), new Promise((_, reject) => setTimeout(() => { @@ -182,14 +191,10 @@ export class TabPerfFuncAsm extends BaseElement { }, 20000)) ]); } catch (error) { - console.error('Error:', error); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - this.showError(`Error: can't get assembly code because ${errorMessage},show sample list without assembly code`); + this.showError(`Error: can't get assembly code because ${errorMessage}, show sample list without assembly code`); this.calcutelateErrorShowUpData(); } finally { - console.log("lbh:finally:originalShowUpData ", this.originalShowUpData) - console.log("lbh:finally:funcBaseAddr ", this.funcBaseAddr) - console.log("lbh:finally:funcSampleMap ", this.funcSampleMap) this.showUpData = [...this.originalShowUpData]; this.assmblerTable!.recycleDataSource = this.showUpData; this.assmblerTable!.reMeauseHeight(); @@ -205,12 +210,10 @@ export class TabPerfFuncAsm extends BaseElement { percent: Math.round((selfCount / this.totalCount) * 10000) / 100, // 地址计算也使用 BigInt addr: Number(BigInt.asUintN(64, this.funcBaseAddr + BigInt(offsetToVaddr))), - instruction: '' + instruction: '', + sourceLine: '' }) }) - console.log("lbh:calcutelateErrorShowUpData originalShowUpData ", this.originalShowUpData) - console.log("lbh:calcutelateErrorShowUpData funcBaseAddr ", this.funcBaseAddr) - console.log("lbh:calcutelateErrorShowUpData funcSampleMap ", this.funcSampleMap) } private calculateFuncAsmSapleCount(vaddrList: Array): void { @@ -228,6 +231,7 @@ export class TabPerfFuncAsm extends BaseElement { percent: 0, addr: parseInt(instructs.addr, 16), instruction: instructs.instruction, + sourceLine: instructs.sourceLine }) as FormattedAsmInstruction); } @@ -237,7 +241,6 @@ export class TabPerfFuncAsm extends BaseElement { this.funcSampleMap.clear(); this.showUpData = []; this.originalShowUpData = []; - this.currentAsmList = []; this.formattedAsmIntructionArray = []; this.assmblerTable!.recycleDataSource = []; } @@ -252,13 +255,24 @@ export class TabPerfFuncAsm extends BaseElement { } public connectedCallback(): void { - new ResizeObserver(() => { + super.connectedCallback(); + // 初始化 ResizeObserver + this.resizeObserver = new ResizeObserver(() => { if (this.assmblerTable && this.parentElement) { - this.assmblerTable.style.height = `${ - this.parentElement.clientHeight - 50 - }px`; - this.assmblerTable.reMeauseHeight(); + this.assmblerTable.style.height = `${this.parentElement.clientHeight - 50}px`; + this.assmblerTable!.reMeauseHeight(); } - }).observe(this.parentElement!); + }); + this.resizeObserver.observe(this.parentElement!); + } + + public disconnectedCallback(): void { + super.disconnectedCallback(); + + // 断开 ResizeObserver + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } } -} +} \ No newline at end of file diff --git a/ide/src/webSocket/Constants.ts b/ide/src/webSocket/Constants.ts index 6381b8b69..c44949c08 100644 --- a/ide/src/webSocket/Constants.ts +++ b/ide/src/webSocket/Constants.ts @@ -36,5 +36,14 @@ export class TypeConstants { static DIAGNOSIS_TYPE = 8; static SENDDB_CMD = 1; static DIAGNOSIS_CMD = 3; - static DISASSEMBLY_TYPE = 10; + static DISASSEMBLY_TYPE = 12; + static ARKTS_TYPE = 9; + static PROCESS_TYPE = 3; + static USB_TYPE = 10; + static USB_SN_CMD = 1; + static USB_GET_PROCESS = 2; + static USB_GET_CPU_COUNT = 3; + static USB_GET_EVENT = 4; + static USB_GET_APP = 5; + static USB_GET_VERSION = 6; } \ No newline at end of file diff --git a/ide/src/webSocket/WebSocketManager.ts b/ide/src/webSocket/WebSocketManager.ts index 800b699ba..35129400b 100644 --- a/ide/src/webSocket/WebSocketManager.ts +++ b/ide/src/webSocket/WebSocketManager.ts @@ -213,20 +213,32 @@ export class WebSocketManager { * 模块调用 */ registerMessageListener(type: number, callback: Function, eventCallBack: Function): void { - if (!this.distributeMap.has(type)) { - this.distributeMap.set(type, { 'messageCallbacks': [], 'eventCallBack': eventCallBack }); - } - const callbackObj = this.distributeMap.get(type)!; - callbackObj.messageCallbacks.push(callback); + this.register(type, callback, eventCallBack); } - // 注册回调函数 + /** + * 消息监听器 + * listener是不同模块传来接收数据的函数 + * 模块调用 + */ registerCallback(type: number, callback: Function): void { - if (!this.distributeMap.has(type)) { - this.distributeMap.set(type, { 'messageCallbacks': [], 'eventCallBack': () => {} }); + this.register(type, callback); + } + + private register(type: number, callback: Function, eventCallBack: Function = () => {}): void { + let callbackObj = this.distributeMap.get(type); + if (!callbackObj) { + callbackObj = { + messageCallbacks: [callback], + eventCallBack: eventCallBack + }; + this.distributeMap.set(type, callbackObj); + } else { + if (!callbackObj.messageCallbacks.includes(callback)) { + callbackObj.messageCallbacks.push(callback); + } + callbackObj.eventCallBack = eventCallBack; } - const callbackObj = this.distributeMap.get(type)!; - callbackObj.messageCallbacks.push(callback); } // 删除回调函数 @@ -234,10 +246,7 @@ export class WebSocketManager { if (!this.distributeMap.has(type)) { return; } - // 获取指定类型的回调对象 const callbackObj = this.distributeMap.get(type)!; - - // 在回调数组中查找并移除与传入的回调函数匹配的项 callbackObj.messageCallbacks = callbackObj.messageCallbacks.filter((cb) => cb !== callback); // 如果回调数组为空,同时 eventCallBack 也为空,则可以删除整个类型 @@ -276,7 +285,7 @@ export class WebSocketManager { this.websocket!.send(encode!); } - // 定时检查心跳 + // 定时检查心跳 sendHeartbeat(): void { this.heartbeatInterval = window.setInterval(() => { if (this.status === GetStatuses.READY) { @@ -316,7 +325,7 @@ export class WebSocketManager { } // 检查状态 中间状态,最终失败状态,最终成功状态 - checkStatus(type: number): void { // 更改参数名称为 type,以反映实际传递的值 + checkStatus(type: number): void { // @ts-ignore let statuses = this.getStatusesPrompt()[this.status]; const distributeEntry = this.distributeMap.get(type); @@ -325,11 +334,9 @@ export class WebSocketManager { if (statuses.type === INTERMEDIATE_STATE) { distributeEntry.eventCallBack(this.status); } else if (statuses.type === FAILED_STATE) { - this.reconnect = type; // 确认这是您想要的逻辑 + this.reconnect = type; this.connectWebSocket(); } - } else { - console.error('No valid eventCallBack found for type: ${type}'); } } -- Gitee