From a27a34b42d518a44d844729fae05a5c22b6e8b14 Mon Sep 17 00:00:00 2001 From: LiYuxi Date: Fri, 21 Jun 2024 17:02:16 +0800 Subject: [PATCH 01/25] =?UTF-8?q?=E6=B7=BB=E5=8A=A0hangs=E8=81=9A=E5=90=88?= =?UTF-8?q?=E6=B3=B3=E9=81=93=E5=8F=8A=E5=AD=90=E6=B3=B3=E9=81=93=E8=89=B2?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: LiYuxi --- ide/src/trace/bean/BoxSelection.ts | 16 ++ ide/src/trace/component/SpFlags.ts | 75 ++++++- .../trace/component/SpSystemTrace.event.ts | 2 + ide/src/trace/component/SpSystemTrace.ts | 2 + .../trace/component/chart/SpChartManager.ts | 5 + ide/src/trace/component/chart/SpHangChart.ts | 196 ++++++++++++++++++ .../trace/component/trace/base/RangeSelect.ts | 4 + .../trace/component/trace/base/TraceRow.ts | 2 + .../trace/component/trace/base/TraceSheet.ts | 3 + .../component/trace/base/TraceSheetConfig.ts | 6 + .../trace/sheet/TabPaneCurrentSelection.ts | 26 +++ .../trace/sheet/hang/TabPaneHangCounter.ts | 192 +++++++++++++++++ ide/src/trace/database/TraceWorker.ts | 2 + .../database/data-trafic/ClockDataReceiver.ts | 42 ++-- .../database/data-trafic/HangDataReceiver.ts | 116 +++++++++++ .../database/data-trafic/HangDataSender.ts | 68 ++++++ .../data-trafic/utils/AllMemoryCache.ts | 3 + .../data-trafic/utils/ExecProtoForWorker.ts | 2 + .../database/data-trafic/utils/QueryEnum.ts | 1 + ide/src/trace/database/sql/Hang.sql.ts | 59 ++++++ .../database/ui-worker/ProcedureWorker.ts | 2 + .../database/ui-worker/ProcedureWorkerHang.ts | 128 ++++++++++++ 22 files changed, 931 insertions(+), 21 deletions(-) create mode 100644 ide/src/trace/component/chart/SpHangChart.ts create mode 100644 ide/src/trace/component/trace/sheet/hang/TabPaneHangCounter.ts create mode 100644 ide/src/trace/database/data-trafic/HangDataReceiver.ts create mode 100644 ide/src/trace/database/data-trafic/HangDataSender.ts create mode 100644 ide/src/trace/database/sql/Hang.sql.ts create mode 100644 ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts diff --git a/ide/src/trace/bean/BoxSelection.ts b/ide/src/trace/bean/BoxSelection.ts index 2b6bf58f..442700be 100644 --- a/ide/src/trace/bean/BoxSelection.ts +++ b/ide/src/trace/bean/BoxSelection.ts @@ -69,6 +69,7 @@ export class SelectionParam { string, ((arg: unknown) => Promise> | undefined) | undefined >(); + hangMapData: Map Promise> | undefined) | undefined> = new Map() irqCallIds: Array = []; softIrqCallIds: Array = []; funTids: Array = []; @@ -1091,6 +1092,20 @@ export class SelectionParam { } } + // @ts-ignore + // pushHang(it: TraceRow, sp: SpSystemTrace): void { + // if (it.rowType === TraceRow.ROW_TYPE_HANG_GROUP) { + // it.childrenList.forEach((it) => { + // it.rangeSelect = true; + // it.checkType = '2'; + // this.hangMapData.set(it.rowId || '', it.getCacheData); + // }); + // } + // if (it.rowType === TraceRow.ROW_TYPE_HANG) { + // this.hangMapData.set(it.rowId || '', it.getCacheData); + // } + // } + // @ts-ignore pushGpuMemoryVmTracker(it: TraceRow, sp: SpSystemTrace): void { if (it.rowType === TraceRow.ROW_TYPE_GPU_MEMORY_VMTRACKER) { @@ -1200,6 +1215,7 @@ export class SelectionParam { this.pushVmTracker(it, sp); this.pushVmTrackerShm(it, sp); this.pushClock(it, sp); + // this.pushHang(it, sp); this.pushGpuMemoryVmTracker(it, sp); this.pushDmaVmTracker(it, sp); this.pushPugreable(it, sp); diff --git a/ide/src/trace/component/SpFlags.ts b/ide/src/trace/component/SpFlags.ts index d50ac6e7..a047a0ad 100644 --- a/ide/src/trace/component/SpFlags.ts +++ b/ide/src/trace/component/SpFlags.ts @@ -76,6 +76,23 @@ export class SpFlags extends BaseElement { vsyncSelect?.setAttribute('disabled', 'disabled'); } + + let hangsSelect = this.shadowRoot?.querySelector('#hangsSelect'); + if (title === 'Hangs' && configSelect.selectedOptions[0].value === 'Enabled') { + hangsSelect?.removeAttribute('disabled'); + } + if (title === 'Hangs' && configSelect.selectedOptions[0].value === 'Disabled') { + hangsSelect?.childNodes.forEach((child: ChildNode) => { + let selectEl = child as HTMLOptionElement; + if (child.textContent === 'Micro') { + selectEl.selected = true; + FlagsConfig.updateFlagsConfig('hangValue', selectEl.value); + } else { + selectEl.selected = false; + } + }); + hangsSelect?.setAttribute('disabled', 'disabled'); + } }); let description = document.createElement('div'); description.className = 'flag-des-div'; @@ -134,6 +151,11 @@ export class SpFlags extends BaseElement { configDiv.appendChild(configFooterDiv); } + if (config.title === 'Hangs') { + let configFooterDiv = this.createHangsOption(); + configDiv.appendChild(configFooterDiv); + } + this.bodyEl!.appendChild(configDiv); }); } @@ -165,9 +187,6 @@ export class SpFlags extends BaseElement { FlagsConfig.updateFlagsConfig('vsyncValue', vsyncGenOption.value); vsyncTypeEl.addEventListener('change', function () { let selectValue = this.selectedOptions[0].value; - console.log(this); - console.log(this.selectedOptions[0]); - console.log(this.selectedOptions[0].value); FlagsConfig.updateFlagsConfig('vsyncValue', selectValue); }); @@ -184,6 +203,51 @@ export class SpFlags extends BaseElement { configFooterDiv.appendChild(vsyncTypeEl); return configFooterDiv; } + + private createHangsOption(): HTMLDivElement { + console.log("start createHangsOption.") + let configFooterDiv = document.createElement('div'); + configFooterDiv.className = 'config_footer'; + let hangsLableEl = document.createElement('lable'); + hangsLableEl.className = 'hangs_lable'; + let hangsTypeEl = document.createElement('select'); + hangsTypeEl.setAttribute('id', 'hangsSelect'); + hangsTypeEl.className = 'flag-select'; + + let hangInstantOption = document.createElement('option'); + hangInstantOption.value = 'Instant;Circumstantial;Micro;Severe'; + hangInstantOption.textContent = 'Instant'; + hangInstantOption.selected = true; + hangsTypeEl.appendChild(hangInstantOption) + + let hangMicroOption = document.createElement('option'); + hangMicroOption.value = 'Micro;Severe'; + hangMicroOption.textContent = 'micro'; + hangMicroOption.selected = true; + hangsTypeEl.appendChild(hangMicroOption) + + FlagsConfig.updateFlagsConfig('hangValue', hangInstantOption.value); + hangsTypeEl.addEventListener('change', function () { + let selectValue = this.selectedOptions[0].value; + console.log(this); + console.log(this.selectedOptions[0]); + console.log(this.selectedOptions[0].value); + FlagsConfig.updateFlagsConfig('hangValue', selectValue); + }); + + let flagsItem = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY); + let flagsItemJson = JSON.parse(flagsItem!); + let hangs = flagsItemJson['Hangs']; + if (hangs === 'Enabled') { + hangsTypeEl.removeAttribute('disabled'); + } else { + hangsTypeEl.setAttribute('disabled', 'disabled'); + FlagsConfig.updateFlagsConfig('hangValue', hangInstantOption.value); + } + configFooterDiv.appendChild(hangsLableEl); + configFooterDiv.appendChild(hangsTypeEl); + return configFooterDiv; + } } export type Params = { @@ -239,6 +303,11 @@ export class FlagsConfig { switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], describeContent: 'VSync Signal drawing', }, + { + title: 'Hangs', + switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], + describeContent: '', + }, { title: 'LTPO', switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], diff --git a/ide/src/trace/component/SpSystemTrace.event.ts b/ide/src/trace/component/SpSystemTrace.event.ts index ef1744b7..97835c82 100644 --- a/ide/src/trace/component/SpSystemTrace.event.ts +++ b/ide/src/trace/component/SpSystemTrace.event.ts @@ -45,6 +45,7 @@ import { FlagsConfig } from './SpFlags'; import { LitMainMenu } from '../../base-ui/menu/LitMainMenu'; import { PerfToolsStructOnClick } from '../database/ui-worker/ProcedureWorkerPerfTool'; import { BaseStruct } from '../bean/BaseStruct'; +import { HangStructOnClick } from '../database/ui-worker/ProcedureWorkerHang'; function timeoutJudge(sp: SpSystemTrace): number { let timeoutJudge = window.setTimeout((): void => { @@ -341,6 +342,7 @@ function allStructOnClick(clickRowType: string, sp: SpSystemTrace, row?: TraceRo .then(() => CpuStateStructOnClick(clickRowType, sp)) .then(() => CpuFreqLimitsStructOnClick(clickRowType, sp)) .then(() => ClockStructOnClick(clickRowType, sp)) + .then(() => HangStructOnClick(clickRowType, sp)) //@ts-ignore .then(() => SnapshotStructOnClick(clickRowType, sp, row!)) .then(() => IrqStructOnClick(clickRowType, sp)) diff --git a/ide/src/trace/component/SpSystemTrace.ts b/ide/src/trace/component/SpSystemTrace.ts index b42eec42..c9d7850f 100644 --- a/ide/src/trace/component/SpSystemTrace.ts +++ b/ide/src/trace/component/SpSystemTrace.ts @@ -124,6 +124,7 @@ import { SampleStruct } from '../database/ui-worker/ProcedureWorkerBpftrace'; import { readTraceFileBuffer } from '../SpApplicationPublicFunc'; import { PerfToolStruct } from '../database/ui-worker/ProcedureWorkerPerfTool'; import { BaseStruct } from '../bean/BaseStruct'; +import { HangStruct } from '../database/ui-worker/ProcedureWorkerHang'; function dpr(): number { return window.devicePixelRatio || 1; @@ -1085,6 +1086,7 @@ export class SpSystemTrace extends BaseElement { CpuStateStruct.selectStateStruct = undefined; CpuFreqLimitsStruct.selectCpuFreqLimitsStruct = undefined; ClockStruct.selectClockStruct = undefined; + HangStruct.selectHangStruct = undefined; IrqStruct.selectIrqStruct = undefined; JankStruct.selectJankStruct = undefined; HeapStruct.selectHeapStruct = undefined; diff --git a/ide/src/trace/component/chart/SpChartManager.ts b/ide/src/trace/component/chart/SpChartManager.ts index 62c1bc61..f96fafda 100644 --- a/ide/src/trace/component/chart/SpChartManager.ts +++ b/ide/src/trace/component/chart/SpChartManager.ts @@ -43,6 +43,7 @@ import { SpHiSysEventChart } from './SpHiSysEventChart'; import { SpAllAppStartupsChart } from './SpAllAppStartups'; import { procedurePool } from '../../database/Procedure'; import { SpSegmentationChart } from './SpSegmentationChart'; +import { SpHangChart } from './SpHangChart'; import { SpPerfOutputDataChart } from './SpPerfOutputDataChart'; import { queryAppStartupProcessIds, @@ -82,6 +83,7 @@ export class SpChartManager { private logChart: SpLogChart; private spHiSysEvent: SpHiSysEventChart; private spSegmentationChart: SpSegmentationChart; + private hangChart: SpHangChart; private spBpftraceChart: SpBpftraceChart; private tranceRange = { startTs: 0, endTs: 0 }; private spPerfOutputDataChart: SpPerfOutputDataChart; @@ -110,6 +112,7 @@ export class SpChartManager { this.spAllAppStartupsChart = new SpAllAppStartupsChart(trace); this.SpLtpoChart = new SpLtpoChart(trace); this.spSegmentationChart = new SpSegmentationChart(trace); + this.hangChart = new SpHangChart(trace); this.spBpftraceChart = new SpBpftraceChart(trace); this.spPerfOutputDataChart = new SpPerfOutputDataChart(trace); this.spUserFileChart = new SpUserFileChart(trace) @@ -170,6 +173,8 @@ export class SpChartManager { await this.initCpu(progress); await this.logChart.init(); await this.spHiSysEvent.init(); + progress('HangChart inin', 80); + await this.hangChart.init(); progress('Clock init', 82); await this.clockChart.init(); progress('Irq init', 84); diff --git a/ide/src/trace/component/chart/SpHangChart.ts b/ide/src/trace/component/chart/SpHangChart.ts new file mode 100644 index 00000000..1f5086d5 --- /dev/null +++ b/ide/src/trace/component/chart/SpHangChart.ts @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2024 Shenzhen Kaihong Digital Industry Development 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 { SpSystemTrace } from '../SpSystemTrace'; +import { TraceRow } from '../trace/base/TraceRow'; +import { renders } from '../../database/ui-worker/ProcedureWorker'; +import { info } from '../../../log/Log'; +import { HangRender, HangStruct } from '../../database/ui-worker/ProcedureWorkerHang'; +import { ColorUtils } from '../trace/base/ColorUtils'; +import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; +import { Utils } from '../trace/base/Utils'; +import { queryHangFuncName, queryRealHangData } from '../../database/sql/Hang.sql'; +import { realHangDataSender } from '../../database/data-trafic/HangDataSender'; +import { BaseStruct } from '../../bean/BaseStruct'; + +export type HangType = "Instant" | "Circumstantial" | "Micro" | "Severe" | "" + +export class SpHangChart { + private trace: SpSystemTrace; + private funcNameMap: Map = new Map() + + constructor(trace: SpSystemTrace) { + this.trace = trace; + } + + static calculateHangType(dur: number): HangType { + const durMS = dur / 1000000 + if (durMS < 33) { + return "" + } + else if (durMS < 100) { + return "Instant" + } + else if (durMS < 250) { + return "Circumstantial" + } + else if (durMS < 500) { + return "Micro" + } + else { + return "Severe" + } + } + + async init(): Promise { + for (const funcNameItem of await queryHangFuncName()) { + this.funcNameMap.set(funcNameItem.id, funcNameItem.name) + } + let folder = await this.initFolder(); + await this.initData(folder); + } + + private hangSupplierFrame( + traceRow: TraceRow, + it: { + id: number; + name: string; + num: number; + } + ): void { + traceRow.supplierFrame = (): Promise => { + let promiseData = realHangDataSender(it.id, traceRow) + if (promiseData === null) { + // @ts-ignore + return new Promise>((resolve) => resolve([])); + } else { + // @ts-ignore + return promiseData.then((resultHang: Array) => { + for (const hangItem of resultHang) { + hangItem.pname = it.name + hangItem.type = SpHangChart.calculateHangType(hangItem.dur!) + hangItem.content = this.funcNameMap.get(hangItem.id!) + } + return resultHang; + }); + } + }; + } + + private hangThreadHandler( + traceRow: TraceRow, + it: { + id: number; + name: string; + num: number; + }, + hangId: number + ): void { + traceRow.onThreadHandler = (useCache): void => { + let context: CanvasRenderingContext2D; + if (traceRow.currentContext) { + context = traceRow.currentContext; + } else { + context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; + } + traceRow.canvasSave(context); + (renders.hang as HangRender).renderMainThread( + { + context: context, + useCache: useCache, + type: it.name, + index: hangId, + processName: it.name + }, + traceRow + ); + traceRow.canvasRestore(context, this.trace); + }; + } + + async initData(folder: TraceRow): Promise { + let hangStartTime = new Date().getTime(); + let realHangList = await queryRealHangData(); + if (realHangList.length === 0) { + return; + } + this.trace.rowsEL?.appendChild(folder); + for (let i = 0; i < realHangList.length; i++) { + const it: { + id: number, + name: string, + num: number + } = realHangList[i]; + let traceRow = TraceRow.skeleton(); + traceRow.rowId = it.name ?? '' + it.id; + traceRow.rowType = TraceRow.ROW_TYPE_HANG; + traceRow.rowParentId = folder.rowId; + traceRow.style.height = '40px'; + traceRow.name = it.name ?? '-'; + traceRow.rowHidden = !folder.expansion; + traceRow.setAttribute('children', ''); + traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; + traceRow.selectChangeHandler = this.trace.selectChangeHandler; + this.hangSupplierFrame(traceRow, it); + traceRow.getCacheData = (args: unknown): Promise> => realHangDataSender(it.id, traceRow, args) + traceRow.focusHandler = (ev): void => { + this.trace?.displayTip( + traceRow, + HangStruct.hoverHangStruct, + `${JSON.stringify(HangStruct.hoverHangStruct?.content ?? "Hatsune Miku")}` + ); + }; + traceRow.findHoverStruct = (): void => { + HangStruct.hoverHangStruct = traceRow.getHoverStruct(); + }; + this.hangThreadHandler(traceRow, it, i); + folder.addChildTraceRow(traceRow); + } + let durTime = new Date().getTime() - hangStartTime; + info('The time to load the HangData is: ', durTime); + } + + async initFolder(): Promise> { + let hangFolder = TraceRow.skeleton(); + hangFolder.rowId = 'Hangs'; + hangFolder.index = 0; + hangFolder.rowType = TraceRow.ROW_TYPE_HANG_GROUP; + hangFolder.rowParentId = ''; + hangFolder.style.height = '40px'; + hangFolder.folder = true; + hangFolder.name = 'Hangs'; + hangFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler; + hangFolder.selectChangeHandler = this.trace.selectChangeHandler; // @ts-ignore + hangFolder.supplier = (): Promise => new Promise>((resolve) => resolve([])); + hangFolder.onThreadHandler = (useCache): void => { + hangFolder.canvasSave(this.trace.canvasPanelCtx!); + if (hangFolder.expansion) { + // @ts-ignore + this.trace.canvasPanelCtx?.clearRect(0, 0, hangFolder.frame.width, hangFolder.frame.height); + } else { + (renders.empty as EmptyRender).renderMainThread( + { + context: this.trace.canvasPanelCtx, + useCache: useCache, + type: '', + }, + hangFolder + ); + } + hangFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace); + }; + return hangFolder; + } +} diff --git a/ide/src/trace/component/trace/base/RangeSelect.ts b/ide/src/trace/component/trace/base/RangeSelect.ts index a7e2b0b0..47e0fa83 100644 --- a/ide/src/trace/component/trace/base/RangeSelect.ts +++ b/ide/src/trace/component/trace/base/RangeSelect.ts @@ -278,6 +278,7 @@ export class RangeSelect { document.body.style.cursor = 'default'; } if (this.isHover && this.isMouseDown) { + // 似乎调用不到 this.handleRangeSelectAndDraw(rows, ev); return; } @@ -291,6 +292,7 @@ export class RangeSelect { } // @ts-ignore private handleRangeSelect(rows: Array>): void { + // console.log("handleRangeSelect", arguments) let rangeSelect: RangeSelectStruct | undefined; let favoriteRect = this.trace?.favoriteChartListEL?.getBoundingClientRect(); let favoriteLimit = favoriteRect!.top + favoriteRect!.height; @@ -352,11 +354,13 @@ export class RangeSelect { } private handleDrawForNotMouseDown(): void { + // console.log("handleDrawForNotMouseDown", arguments) this.timerShaftEL!.sportRuler!.isRangeSelect = isNotEmpty(this.rangeTraceRow) ?? false; this.timerShaftEL!.sportRuler!.draw(); } // @ts-ignore private handleRangeSelectAndDraw(rows: Array>, ev: MouseEvent): void { + // console.log("handleRangeSelectAndDraw", arguments) let rangeSelect: RangeSelectStruct | undefined; this.rangeTraceRow = rows.filter((it) => { if (it.rangeSelect) { diff --git a/ide/src/trace/component/trace/base/TraceRow.ts b/ide/src/trace/component/trace/base/TraceRow.ts index 43655a21..7702083a 100644 --- a/ide/src/trace/component/trace/base/TraceRow.ts +++ b/ide/src/trace/component/trace/base/TraceRow.ts @@ -110,8 +110,10 @@ export class TraceRow extends HTMLElement { static ROW_TYPE_GPU_MEMORY_VMTRACKER = 'gpu-memory-vmTracker'; static ROW_TYPE_GPU_RESOURCE_VMTRACKER = 'sys-memory-gpu-resource'; static ROW_TYPE_VMTRACKER_SHM = 'VmTracker-shm'; + static ROW_TYPE_HANG_GROUP = 'hang-group'; static ROW_TYPE_CLOCK_GROUP = 'clock-group'; static ROW_TYPE_COLLECT_GROUP = 'collect-group'; + static ROW_TYPE_HANG = 'hang'; static ROW_TYPE_CLOCK = 'clock'; static ROW_TYPE_IRQ_GROUP = 'irq-group'; static ROW_TYPE_IRQ = 'irq'; diff --git a/ide/src/trace/component/trace/base/TraceSheet.ts b/ide/src/trace/component/trace/base/TraceSheet.ts index 27459b04..f2fbde57 100644 --- a/ide/src/trace/component/trace/base/TraceSheet.ts +++ b/ide/src/trace/component/trace/base/TraceSheet.ts @@ -31,6 +31,7 @@ import { type ThreadStruct } from '../../../database/ui-worker/ProcedureWorkerTh import { type FuncStruct } from '../../../database/ui-worker/ProcedureWorkerFunc'; import { ProcessMemStruct } from '../../../database/ui-worker/ProcedureWorkerMem'; import { CpuStateStruct } from '../../../database/ui-worker/cpu/ProcedureWorkerCpuState'; +import { type HangStruct } from '../../../database/ui-worker/ProcedureWorkerHang'; import { type ClockStruct } from '../../../database/ui-worker/ProcedureWorkerClock'; import { type IrqStruct } from '../../../database/ui-worker/ProcedureWorkerIrq'; import { type JankStruct } from '../../../database/ui-worker/ProcedureWorkerJank'; @@ -633,6 +634,8 @@ export class TraceSheet extends BaseElement { ); displayMemData = (data: ProcessMemStruct): void => this.displayTab('current-selection').setMemData(data); + displayHangData = (data: HangStruct): Promise => + this.displayTab('current-selection').setHangData(data); displayClockData = (data: ClockStruct): Promise => this.displayTab('current-selection').setClockData(data); displayPerfToolsData = (data: PerfToolStruct): void => diff --git a/ide/src/trace/component/trace/base/TraceSheetConfig.ts b/ide/src/trace/component/trace/base/TraceSheetConfig.ts index 4e14c5eb..2b014e49 100644 --- a/ide/src/trace/component/trace/base/TraceSheetConfig.ts +++ b/ide/src/trace/component/trace/base/TraceSheetConfig.ts @@ -64,6 +64,7 @@ import { TabPaneFreqLimit } from '../sheet/freq/TabPaneFreqLimit'; import { TabPaneCpuFreqLimits } from '../sheet/freq/TabPaneCpuFreqLimits'; import { TabpaneNMCalltree } from '../sheet/native-memory/TabPaneNMCallTree'; import { TabPaneClockCounter } from '../sheet/clock/TabPaneClockCounter'; +import { TabPaneHangCounter } from '../sheet/Hang/TabPaneHangCounter'; import { TabPaneIrqCounter } from '../sheet/irq/TabPaneIrqCounter'; import { TabPaneFrames } from '../sheet/jank/TabPaneFrames'; import { TabPanePerfAnalysis } from '../sheet/hiperf/TabPanePerfAnalysis'; @@ -212,6 +213,11 @@ export let tabConfig: unknown = { type: TabPaneClockCounter, require: (param: SelectionParam) => param.clockMapData.size > 0, }, + // 'box-hang-counters': { + // title: 'Hang Counters', + // type: TabPaneHangCounter, + // require: (param: SelectionParam) => param.hangMapData.size > 0, + // }, 'box-irq-counters': { title: 'Irq Counters', type: TabPaneIrqCounter, diff --git a/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts b/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts index c7f08564..1467e99f 100644 --- a/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts +++ b/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts @@ -58,6 +58,7 @@ import { queryWakeupListPriority } from '../../../database/sql/Cpu.sql'; import { TabPaneCurrentSelectionHtml } from './TabPaneCurrentSelection.html'; import { queryRealTime } from '../../../database/sql/Clock.sql'; import { PerfToolStruct } from '../../../database/ui-worker/ProcedureWorkerPerfTool'; +import { HangStruct } from '../../../database/ui-worker/ProcedureWorkerHang'; const INPUT_WORD = 'This is the interval from when the task became eligible to run \n(e.g.because of notifying a wait queue it was a suspended on) to\n when it started running.'; @@ -545,6 +546,31 @@ export class TabPaneCurrentSelection extends BaseElement { this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); } + async setHangData(data: HangStruct): Promise { + // TODO: + // console.log("setHangData", arguments) + // await this.setRealTime(); + // this.setTableHeight('auto'); + // //时钟信息 + // this.tabCurrentSelectionInit('Counter Details'); + // let list: unknown[] = []; + // list.push({ + // name: 'StartTime(Relative)', + // value: getTimeString(data.startNS || 0), + // }); + // this.createStartTimeNode(list, data.startNS || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); + // list.push({ + // name: 'Value', + // value: ColorUtils.formatNumberComma(data.value || 0), + // }); + // list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); + // list.push({ name: "Fake Data", value: "Useless Info." }) + // this.currentSelectionTbl!.dataSource = list; + // // @ts-ignore + // let startTimeAbsolute = (data.startNS || 0) + (window as unknown).recordStartNS; + // this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); + } + setPerfToolsData(data: PerfToolStruct): void { this.setTableHeight('auto'); //Perf Tools info diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHangCounter.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHangCounter.ts new file mode 100644 index 00000000..3bb170df --- /dev/null +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHangCounter.ts @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BaseElement, element } from '../../../../../base-ui/BaseElement'; +import { LitTable } from '../../../../../base-ui/table/lit-table'; +import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; +import { resizeObserver } from '../SheetUtils'; + +@element('tabpane-hang-counter') +export class TabPaneHangCounter extends BaseElement { + private hangCounterTbl: LitTable | null | undefined; + private hangCounterRange: HTMLLabelElement | null | undefined; + private hangCounterSource: Array = []; + + set data(hangCounterValue: SelectionParam) { + //@ts-ignore + this.hangCounterTbl?.shadowRoot?.querySelector('.table')?.style?.height = `${ + this.parentElement!.clientHeight - 45 + }px`; + this.hangCounterRange!.textContent = `Selected range: ${parseFloat( + ((hangCounterValue.rightNs - hangCounterValue.leftNs) / 1000000.0).toFixed(5) + )} ms`; + this.getCounterData(hangCounterValue).then(); + } + + async getCounterData(hangCounterValue: SelectionParam): Promise { + let dataSource: Array = []; + let collect = hangCounterValue.hangMapData; + let sumCount = 0; + this.hangCounterTbl!.loading = true; + for (let key of collect.keys()) { + let counters = collect.get(key); + let res = await counters?.({ + startNS: hangCounterValue.leftNs, + endNS: hangCounterValue.rightNs, + queryAll: true, + }); + let sd = this.createSelectCounterData(key, res || [], hangCounterValue.leftNs, hangCounterValue.rightNs); + sumCount += Number.parseInt(sd.count || '0'); + dataSource.push(sd); + } + let sumData = new SelectionData(); + sumData.count = sumCount.toString(); + sumData.process = ' '; + dataSource.splice(0, 0, sumData); + this.hangCounterTbl!.loading = false; + this.hangCounterSource = dataSource; + this.hangCounterTbl!.recycleDataSource = dataSource; + } + + initElements(): void { + this.hangCounterTbl = this.shadowRoot?.querySelector('#tb-counter'); + this.hangCounterRange = this.shadowRoot?.querySelector('#time-range'); + this.hangCounterTbl!.addEventListener('column-click', (evt): void => { + // @ts-ignore + this.sortByColumn(evt.detail); + }); + } + + connectedCallback(): void { + super.connectedCallback(); + resizeObserver(this.parentElement!, this.hangCounterTbl!); + } + + initHtml(): string { + return ` + + + + + + + + + + + + + + + + + + + + + + + `; + } + + createSelectCounterData(name: string, list: Array, leftNs: number, rightNs: number): SelectionData { + let selectCounterData = new SelectionData(); + if (list.length > 0) { + let range = rightNs - leftNs; + let first = list[0]; + // @ts-ignore + selectCounterData.trackId = first.filterId; + selectCounterData.name = name; + // @ts-ignore + selectCounterData.first = `${first.value}`; + selectCounterData.count = `${list.length}`; + // @ts-ignore + selectCounterData.last = `${list[list.length - 1].value}`; + selectCounterData.delta = `${parseInt(selectCounterData.last) - parseInt(selectCounterData.first)}`; + selectCounterData.rate = (parseInt(selectCounterData.delta) / ((range * 1.0) / 1000000000)).toFixed(4); + // @ts-ignore + selectCounterData.min = `${first.value}`; + selectCounterData.max = '0'; + let weightAvg = 0.0; + for (let i = 0; i < list.length; i++) { + let counter = list[i]; + // @ts-ignore + if (counter.value < parseInt(selectCounterData.min)) { + // @ts-ignore + selectCounterData.min = counter.value.toString(); + } + // @ts-ignore + if (counter.value > parseInt(selectCounterData.max)) { + // @ts-ignore + selectCounterData.max = counter.value.toString(); + } + // @ts-ignore + let start = i === 0 ? leftNs : counter.startNS; + // @ts-ignore + let end = i === list.length - 1 ? rightNs : list[i + 1].startNS; + // @ts-ignore + weightAvg += counter.value * (((end - start) * 1.0) / range); + } + selectCounterData.avgWeight = weightAvg.toFixed(2); + } + return selectCounterData; + } + + sortByColumn(detail: unknown): void { + // @ts-ignore + function compare(property, sort, type) { + return function (hangCounterLeftData: SelectionData, hangCounterRightData: SelectionData): number { + if (hangCounterLeftData.process === ' ' || hangCounterRightData.process === ' ') { + return 0; + } + if (type === 'number') { + return sort === 2 // @ts-ignore + ? parseFloat(hangCounterRightData[property]) - parseFloat(hangCounterLeftData[property]) // @ts-ignore + : parseFloat(hangCounterLeftData[property]) - parseFloat(hangCounterRightData[property]); + } else { + // @ts-ignore + if (hangCounterRightData[property] > hangCounterLeftData[property]) { + return sort === 2 ? 1 : -1; + } else { + // @ts-ignore + if (hangCounterRightData[property] === hangCounterLeftData[property]) { + return 0; + } else { + return sort === 2 ? -1 : 1; + } + } + } + }; + } + // @ts-ignore + if (detail.key === 'name') { + // @ts-ignore + this.hangCounterSource.sort(compare(detail.key, detail.sort, 'string')); + } else { + // @ts-ignore + this.hangCounterSource.sort(compare(detail.key, detail.sort, 'number')); + } + this.hangCounterTbl!.recycleDataSource = this.hangCounterSource; + } +} diff --git a/ide/src/trace/database/TraceWorker.ts b/ide/src/trace/database/TraceWorker.ts index 5b7bf056..4458a59a 100644 --- a/ide/src/trace/database/TraceWorker.ts +++ b/ide/src/trace/database/TraceWorker.ts @@ -545,6 +545,7 @@ function initTraceRange(thirdMode: unknown): void { function onmessageByExecAction(e: MessageEvent): void { query(e.data.name, e.data.sql, e.data.params); let jsonArray = convertJSON(); + //console.log('onmessageByExecAction - jsonArray', jsonArray, e) // @ts-ignore self.postMessage({ id: e.data.id, @@ -1083,6 +1084,7 @@ self.onmessage = async (e: MessageEvent): Promise => { } else if (e.data.action === 'exec') { onmessageByExecAction(e); } else if (e.data.action === 'exec-proto') { + // console.log('TraceWorker - onmessage', e) onmessageByExecProtoAction(e); } else if (e.data.action === 'exec-buf') { onmessageByExecBufAction(e); diff --git a/ide/src/trace/database/data-trafic/ClockDataReceiver.ts b/ide/src/trace/database/data-trafic/ClockDataReceiver.ts index 79dc5e01..952ecc14 100644 --- a/ide/src/trace/database/data-trafic/ClockDataReceiver.ts +++ b/ide/src/trace/database/data-trafic/ClockDataReceiver.ts @@ -94,7 +94,9 @@ export function clockDataReceiver(data: unknown, proc: Function): void { // @ts-ignore if (!clockList.has(data.params.sqlType + data.params.clockName)) { // @ts-ignore - list = proc(chartClockDataSqlMem(data.params)); + let sql = chartClockDataSqlMem(data.params); + // @ts-ignore + list = proc(sql); for (let j = 0; j < list.length; j++) { if (j === list.length - 1) { // @ts-ignore @@ -162,23 +164,27 @@ function arrayBufferHandler(data: unknown, res: unknown[], transfer: boolean): v // @ts-ignore value[i] = it.value; }); + + let arg1 = { + // @ts-ignore + id: data.id, + // @ts-ignore + action: data.action, + results: transfer + ? { + dur: dur.buffer, + startNS: startNS.buffer, + value: value.buffer, + filterId: filterId.buffer, + } + : {}, + len: res.length, + transfer: transfer, + }; + let arg2 = transfer ? [dur.buffer, startNS.buffer, value.buffer, filterId.buffer] : []; + (self as unknown as Worker).postMessage( - { - // @ts-ignore - id: data.id, - // @ts-ignore - action: data.action, - results: transfer - ? { - dur: dur.buffer, - startNS: startNS.buffer, - value: value.buffer, - filterId: filterId.buffer, - } - : {}, - len: res.length, - transfer: transfer, - }, - transfer ? [dur.buffer, startNS.buffer, value.buffer, filterId.buffer] : [] + arg1, + arg2 ); } diff --git a/ide/src/trace/database/data-trafic/HangDataReceiver.ts b/ide/src/trace/database/data-trafic/HangDataReceiver.ts new file mode 100644 index 00000000..f9ee3d65 --- /dev/null +++ b/ide/src/trace/database/data-trafic/HangDataReceiver.ts @@ -0,0 +1,116 @@ +// Copyright (c) 2021 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 { TraficEnum } from './utils/QueryEnum'; +import { clockList, hangList } from './utils/AllMemoryCache'; +import { Args } from './CommonArgs'; + +export const chartHangDataSql = (args: Args): string => ` +SELECT + c.id as id, + c.ts - r.start_ts as startNS, + c.dur as dur, + t.tid as tid, + t.name as tname, + p.pid as pid, + p.name as pname +FROM + callstack c, trace_range r +LEFT JOIN thread t ON + t.itid = c.callid +LEFT JOIN process p ON + p.ipid = t.ipid +WHERE + c.dur >= 33000000 + AND c.name LIKE 'H:Et%' + AND p.pid = ${args.pid} +`.trim(); + +export interface HangSQLStruct { + id: number + startNS: number + dur: number + tid: number + pid: number +} + +export function hangDataReceiver(data: any, proc: Function): void { + if (data.params.trafic === TraficEnum.Memory) { + let res: HangSQLStruct[] + let list: HangSQLStruct[] + + if (!hangList.has(data.params.pid)) { + let sql = chartHangDataSql(data.params) + list = proc(sql) + hangList.set(data.params.pid, list) + } + else { + list = hangList.get(data.params.pid) || [] + } + + if (data.params.queryAll) { + res = list.filter( + //@ts-ignore + (it) => it.startNS + it.dur >= data.params.selectStartNS && it.startNS <= data.params.selectEndNS + ) + } + else { + res = list + } + + arrayBufferHandler(data, res, true) + } + else { + let sql = chartHangDataSql(data.params) + let res: HangSQLStruct[] = proc(sql) + arrayBufferHandler(data, res, data.params.trafic !== TraficEnum.SharedArrayBuffer) + } +} + +function arrayBufferHandler(data: any, res: HangSQLStruct[], transfer: boolean = true): void { + let id = new Int32Array(transfer ? res.length : data.params.sharedArrayBuffers.id) + let startNS = new Float64Array(transfer ? res.length : data.params.sharedArrayBuffers.startNS) + let dur = new Float64Array(transfer ? res.length : data.params.sharedArrayBuffers.dur) + let tid = new Int32Array(transfer ? res.length : data.params.sharedArrayBuffers.tid) + let pid = new Int32Array(transfer ? res.length : data.params.sharedArrayBuffers.pid) + res.forEach((it, i) => { + id[i] = it.id + startNS[i] = it.startNS + dur[i] = it.dur + pid[i] = it.pid + }); + + let arg1 = { + id: data.id, + action: data.action, + results: { + id: id.buffer, + startNS: startNS.buffer, + dur: dur.buffer, + tid: tid.buffer, + pid: pid.buffer, + }, + len: res.length, + transfer: transfer, + } + let arg2 = [ + id.buffer, + startNS.buffer, + dur.buffer, + tid.buffer, + pid.buffer, + ]; + (self as unknown as Worker).postMessage( + arg1, arg2 + ) +} \ No newline at end of file diff --git a/ide/src/trace/database/data-trafic/HangDataSender.ts b/ide/src/trace/database/data-trafic/HangDataSender.ts new file mode 100644 index 00000000..4d505d6b --- /dev/null +++ b/ide/src/trace/database/data-trafic/HangDataSender.ts @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 Shenzhen Kaihong Digital Industry Development 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 { CHART_OFFSET_LEFT, MAX_COUNT, QueryEnum, TraficEnum } from './utils/QueryEnum'; +import { threadPool } from '../SqlLite'; +import { TraceRow } from '../../component/trace/base/TraceRow'; +import { HangStruct } from '../ui-worker/ProcedureWorkerHang'; + +export function realHangDataSender( + processId: number = 0, + row: TraceRow, + args?: any, +): Promise { + let trafic: number = TraficEnum.Memory; + let width = row.clientWidth - CHART_OFFSET_LEFT; + return new Promise((resolve, reject): void => { + threadPool.submitProto( + QueryEnum.HangData, + { + pid: processId, + // + queryAll: args && args.queryAll, + selectStartNS: args ? args.startNS : 0, + selectEndNS: args ? args.endNS : 0, + selectTotalNS: args ? args.endNS - args.startNS : 0, + trafic: trafic, + width: width, + + }, + (res: unknown, len: number, transfer: boolean): void => { + resolve(arrayBufferHandler(transfer ? res : row.sharedArrayBuffers, len)); + } + ) + }) +} + +function arrayBufferHandler(buffers: any, len: number): HangStruct[] { + let outArr: HangStruct[] = []; + let id = new Int32Array(buffers.id) + let startNS = new Float64Array(buffers.startNS) + let dur = new Float64Array(buffers.dur) + let tid = new Int32Array(buffers.tid) + let pid = new Int32Array(buffers.pid) + for (let i = 0; i < len; i += 1) { + outArr.push({ + id: id[i], + startNS: startNS[i], + dur: dur[i], + tid: tid[i], + pid: pid[i], + } as HangStruct) + } + return outArr; +} + + diff --git a/ide/src/trace/database/data-trafic/utils/AllMemoryCache.ts b/ide/src/trace/database/data-trafic/utils/AllMemoryCache.ts index ca92c457..502492bd 100644 --- a/ide/src/trace/database/data-trafic/utils/AllMemoryCache.ts +++ b/ide/src/trace/database/data-trafic/utils/AllMemoryCache.ts @@ -18,12 +18,15 @@ import { resetAbilityMonitor } from '../AbilityMonitorReceiver'; import { resetAbility } from '../VmTrackerDataReceiver'; import { resetDynamicEffect } from '../FrameDynamicEffectReceiver'; import { resetEnergyEvent } from '../EnergySysEventReceiver'; +import { HangSQLStruct } from '../HangDataReceiver'; // thread_state 表缓存 export const sliceList: Map> = new Map(); //cpu 泳道 memory 缓存 export const cpuList: Map> = new Map(); //clock 泳道 memory 模式缓存 export const clockList: Map> = new Map(); +//hang 泳道 memory 模式缓存 +export const hangList: Map> = new Map(); //cpu freq 泳道 memory模式缓存 export const cpuFreqList: Map> = new Map(); //cpu freq limit 泳道 memory模式缓存 diff --git a/ide/src/trace/database/data-trafic/utils/ExecProtoForWorker.ts b/ide/src/trace/database/data-trafic/utils/ExecProtoForWorker.ts index ed4cf2cd..57c549a9 100644 --- a/ide/src/trace/database/data-trafic/utils/ExecProtoForWorker.ts +++ b/ide/src/trace/database/data-trafic/utils/ExecProtoForWorker.ts @@ -79,6 +79,7 @@ import { clearMemoryCache } from './AllMemoryCache'; import { cpuFreqDataReceiver } from '../cpu/CpuFreqDataReceiver'; import { lostFrameReceiver } from './../LostFrameReceiver'; import { sliceReceiver, sliceSPTReceiver } from '../SliceReceiver'; +import { hangDataReceiver } from '../HangDataReceiver'; // @ts-ignore const traficHandlers: Map = new Map([]); // @ts-ignore @@ -101,6 +102,7 @@ traficHandlers.set(QueryEnum.HiperfThreadData, hiperfThreadDataReceiver); traficHandlers.set(QueryEnum.NativeMemoryChartCacheNormal, nativeMemoryDataHandler); traficHandlers.set(QueryEnum.NativeMemoryChartCacheStatistic, nativeMemoryDataHandler); traficHandlers.set(QueryEnum.NativeMemoryChartData, nativeMemoryDataHandler); +traficHandlers.set(QueryEnum.HangData, hangDataReceiver); traficHandlers.set(QueryEnum.ClockData, clockDataReceiver); traficHandlers.set(QueryEnum.IrqData, irqDataReceiver); traficHandlers.set(QueryEnum.VirtualMemoryData, virtualMemoryDataReceiver); diff --git a/ide/src/trace/database/data-trafic/utils/QueryEnum.ts b/ide/src/trace/database/data-trafic/utils/QueryEnum.ts index 4f268ff8..28045d3c 100644 --- a/ide/src/trace/database/data-trafic/utils/QueryEnum.ts +++ b/ide/src/trace/database/data-trafic/utils/QueryEnum.ts @@ -18,6 +18,7 @@ export enum QueryEnum { CpuStateData = 1, CpuFreqData = 2, CpuFreqLimitData = 3, + HangData = 3.5, ClockData = 4, IrqData = 5, ProcessData = 6, diff --git a/ide/src/trace/database/sql/Hang.sql.ts b/ide/src/trace/database/sql/Hang.sql.ts new file mode 100644 index 00000000..bccd3e2e --- /dev/null +++ b/ide/src/trace/database/sql/Hang.sql.ts @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 Shenzhen Kaihong Digital Industry Development 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 { query } from '../SqlLite'; + +export const queryRealHangData = (): Promise> => + query( + 'queryHangsData', + ` +SELECT + p.pid as id, + p.name as name, + count(*) as num +FROM + callstack c +LEFT JOIN thread t ON + t.itid = c.callid +LEFT JOIN process p ON + p.ipid = t.ipid +WHERE + c.name LIKE 'H:Et%' + AND c.dur >= 33000000 +GROUP BY + p.pid +`.trim() + ) + +export const queryHangFuncName = (): Promise> => + query('queryHangFuncName', + ` +SELECT + c.id as id, + c.name as name +FROM + callstack c +WHERE + c.dur >= 33000000 + AND c.name LIKE 'H:Et%' + `.trim() + ) \ No newline at end of file diff --git a/ide/src/trace/database/ui-worker/ProcedureWorker.ts b/ide/src/trace/database/ui-worker/ProcedureWorker.ts index 8151dfb3..3bbdb39f 100644 --- a/ide/src/trace/database/ui-worker/ProcedureWorker.ts +++ b/ide/src/trace/database/ui-worker/ProcedureWorker.ts @@ -39,6 +39,7 @@ import { EnergySystemRender } from './ProcedureWorkerEnergySystem'; import { EnergyPowerRender } from './ProcedureWorkerEnergyPower'; import { EnergyStateRender } from './ProcedureWorkerEnergyState'; import { CpuFreqLimitRender } from './cpu/ProcedureWorkerCpuFreqLimits'; +import { HangRender } from './ProcedureWorkerHang'; import { ClockRender } from './ProcedureWorkerClock'; import { IrqRender } from './ProcedureWorkerIrq'; import { JankRender } from './ProcedureWorkerJank'; @@ -113,6 +114,7 @@ export let renders = { energySystem: new EnergySystemRender(), energyPower: new EnergyPowerRender(), energyState: new EnergyStateRender(), + hang: new HangRender(), clock: new ClockRender(), irq: new IrqRender(), jank: new JankRender(), diff --git a/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts new file mode 100644 index 00000000..a1cbdd59 --- /dev/null +++ b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2024 Shenzhen Kaihong Digital Industry Development 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 { BaseStruct, dataFilterHandler, drawLoadingFrame, isFrameContainPoint, Render } from './ProcedureWorkerCommon' +import { TraceRow } from '../../component/trace/base/TraceRow' +import { SpSystemTrace } from '../../component/SpSystemTrace' +import { HangType } from '../../component/chart/SpHangChart' + +export class HangRender extends Render { + renderMainThread( + hangReq: { + context: CanvasRenderingContext2D + useCache: boolean + type: string + index: number + processName: string + }, + row: TraceRow + ): void { + HangStruct.index = hangReq.index + let hangList = row.dataList + let hangFilter = row.dataListCache + let filterConfig = { + startKey: 'startNS', + durKey: 'dur', + startNS: TraceRow.range?.startNS ?? 0, + endNS: TraceRow.range?.endNS ?? 0, + totalNS: TraceRow.range?.totalNS ?? 0, + frame: row.frame, + paddingTop: 5, + useCache: hangReq.useCache || !(TraceRow.range?.refresh ?? false), + } + dataFilterHandler(hangList, hangFilter, filterConfig) + drawLoadingFrame(hangReq.context, hangFilter, row) + hangReq.context.beginPath() + let find = false + for (let re of hangFilter) { + HangStruct.draw(hangReq.context, re) + if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { + HangStruct.hoverHangStruct = re + find = true + } + } + if (!find && row.isHover) { + HangStruct.hoverHangStruct = undefined + } + hangReq.context.closePath() + let s = hangReq.processName + let textMetrics = hangReq.context.measureText(s) + hangReq.context.globalAlpha = 0.8 + hangReq.context.fillStyle = '#f0f0f0' + hangReq.context.fillRect(0, 5, textMetrics.width + 8, 18) + hangReq.context.globalAlpha = 1 + hangReq.context.fillStyle = '#333' + hangReq.context.textBaseline = 'middle' + hangReq.context.fillText(s, 4, 5 + 9) + } +} + +export function HangStructOnClick(clickRowType: string, sp: SpSystemTrace): Promise { + return new Promise((resolve, reject) => { + if (clickRowType === TraceRow.ROW_TYPE_HANG && HangStruct.hoverHangStruct) { + HangStruct.selectHangStruct = HangStruct.hoverHangStruct + sp.traceSheetEL?.displayHangData(HangStruct.selectHangStruct) + sp.timerShaftEL?.modifyFlagList(undefined) + reject(new Error()) + } else { + resolve(null) + } + }) +} + +export class HangStruct extends BaseStruct { + static hoverHangStruct: HangStruct | undefined + static selectHangStruct: HangStruct | undefined + static index = 0 + id: number | undefined + startNS: number | undefined + dur: number | undefined + tid: number | undefined + pid: number | undefined + type: HangType | undefined // 手动补充 按时间分类 + pname: string | undefined // 手动补充 + content: string | undefined // 手动补充 在tab页中需要手动解析内容 + + static getFrameColor(data: HangStruct): string { + return ({ + "Instant": "#559CFF", + "Circumstantial": "#FFE44D", + "Micro": "#FEB354", + "Severe": "#FC7470", + "": "", + })[data.type!] + } + + static draw(hangContext: CanvasRenderingContext2D, data: HangStruct): void { + if (data.frame) { + hangContext.fillStyle = HangStruct.getFrameColor(data) + hangContext.strokeStyle = HangStruct.getFrameColor(data) + + hangContext.globalAlpha = 0.6 + hangContext.fillRect(data.frame.x, data.frame.y, data.frame.width, data.frame.height) + if (HangStruct.isHover(data)) { + hangContext.lineWidth = 3 + hangContext.globalAlpha = 1 + hangContext.strokeRect(data.frame.x, data.frame.y, data.frame.width, data.frame.height) + } + hangContext.globalAlpha = 1 + hangContext.lineWidth = 1 + } + } + + static isHover(data: HangStruct): boolean { + return data === HangStruct.hoverHangStruct || data === HangStruct.selectHangStruct + } +} -- Gitee From fce95f7d88462b80a2c89e8b94513b0eef7e33c6 Mon Sep 17 00:00:00 2001 From: LiYuxi Date: Mon, 8 Jul 2024 14:36:32 +0800 Subject: [PATCH 02/25] =?UTF-8?q?=E6=B7=BB=E5=8A=A0hangs=E7=9B=B8=E5=85=B3?= =?UTF-8?q?tab=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: LiYuxi --- ide/src/trace/bean/BoxSelection.ts | 26 +- ide/src/trace/component/SpFlags.ts | 47 +-- ide/src/trace/component/SpSystemTrace.init.ts | 1 + .../trace/component/chart/SpChartManager.ts | 8 +- ide/src/trace/component/chart/SpHangChart.ts | 85 +++-- .../trace/component/chart/SpProcessChart.ts | 116 +++++-- .../trace/component/trace/base/ColorUtils.ts | 11 + .../trace/component/trace/base/RangeSelect.ts | 4 - .../trace/component/trace/base/TraceRow.ts | 1 + .../trace/component/trace/base/TraceSheet.ts | 17 +- .../component/trace/base/TraceSheetConfig.ts | 28 +- .../trace/sheet/TabPaneCurrentSelection.ts | 86 +++-- .../trace/sheet/cpu/TabPaneCpuByThread.ts | 2 + .../trace/sheet/hang/TabPaneHang.html.ts | 122 +++++++ .../component/trace/sheet/hang/TabPaneHang.ts | 301 ++++++++++++++++++ .../trace/sheet/hang/TabPaneHangCounter.ts | 192 ----------- .../sheet/hang/TabPaneHangSummary.html.ts | 90 ++++++ .../trace/sheet/hang/TabPaneHangSummary.ts | 292 +++++++++++++++++ .../trace/database/data-trafic/CommonArgs.ts | 1 + .../database/data-trafic/HangDataReceiver.ts | 4 +- .../database/data-trafic/HangDataSender.ts | 7 +- ide/src/trace/database/sql/Hang.sql.ts | 57 +++- .../database/ui-worker/ProcedureWorkerHang.ts | 38 +-- 23 files changed, 1156 insertions(+), 380 deletions(-) create mode 100644 ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts create mode 100644 ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts delete mode 100644 ide/src/trace/component/trace/sheet/hang/TabPaneHangCounter.ts create mode 100644 ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.html.ts create mode 100644 ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.ts diff --git a/ide/src/trace/bean/BoxSelection.ts b/ide/src/trace/bean/BoxSelection.ts index 442700be..96df7d44 100644 --- a/ide/src/trace/bean/BoxSelection.ts +++ b/ide/src/trace/bean/BoxSelection.ts @@ -1093,18 +1093,18 @@ export class SelectionParam { } // @ts-ignore - // pushHang(it: TraceRow, sp: SpSystemTrace): void { - // if (it.rowType === TraceRow.ROW_TYPE_HANG_GROUP) { - // it.childrenList.forEach((it) => { - // it.rangeSelect = true; - // it.checkType = '2'; - // this.hangMapData.set(it.rowId || '', it.getCacheData); - // }); - // } - // if (it.rowType === TraceRow.ROW_TYPE_HANG) { - // this.hangMapData.set(it.rowId || '', it.getCacheData); - // } - // } + pushHang(it: TraceRow, sp: SpSystemTrace): void { + if (it.rowType === TraceRow.ROW_TYPE_HANG_GROUP) { + it.childrenList.forEach((it) => { + it.rangeSelect = true; + it.checkType = '2'; + this.hangMapData.set(it.rowId || '', it.getCacheData); + }); + } + if (it.rowType === TraceRow.ROW_TYPE_HANG || it.rowType === TraceRow.ROW_TYPE_HANG_INNER) { + this.hangMapData.set(it.rowId || '', it.getCacheData); + } + } // @ts-ignore pushGpuMemoryVmTracker(it: TraceRow, sp: SpSystemTrace): void { @@ -1215,7 +1215,7 @@ export class SelectionParam { this.pushVmTracker(it, sp); this.pushVmTrackerShm(it, sp); this.pushClock(it, sp); - // this.pushHang(it, sp); + this.pushHang(it, sp); this.pushGpuMemoryVmTracker(it, sp); this.pushDmaVmTracker(it, sp); this.pushPugreable(it, sp); diff --git a/ide/src/trace/component/SpFlags.ts b/ide/src/trace/component/SpFlags.ts index a047a0ad..1dadb580 100644 --- a/ide/src/trace/component/SpFlags.ts +++ b/ide/src/trace/component/SpFlags.ts @@ -84,7 +84,7 @@ export class SpFlags extends BaseElement { if (title === 'Hangs' && configSelect.selectedOptions[0].value === 'Disabled') { hangsSelect?.childNodes.forEach((child: ChildNode) => { let selectEl = child as HTMLOptionElement; - if (child.textContent === 'Micro') { + if (child.textContent === 'Instant') { selectEl.selected = true; FlagsConfig.updateFlagsConfig('hangValue', selectEl.value); } else { @@ -205,7 +205,6 @@ export class SpFlags extends BaseElement { } private createHangsOption(): HTMLDivElement { - console.log("start createHangsOption.") let configFooterDiv = document.createElement('div'); configFooterDiv.className = 'config_footer'; let hangsLableEl = document.createElement('lable'); @@ -214,24 +213,37 @@ export class SpFlags extends BaseElement { hangsTypeEl.setAttribute('id', 'hangsSelect'); hangsTypeEl.className = 'flag-select'; - let hangInstantOption = document.createElement('option'); - hangInstantOption.value = 'Instant;Circumstantial;Micro;Severe'; - hangInstantOption.textContent = 'Instant'; - hangInstantOption.selected = true; - hangsTypeEl.appendChild(hangInstantOption) - - let hangMicroOption = document.createElement('option'); - hangMicroOption.value = 'Micro;Severe'; - hangMicroOption.textContent = 'micro'; - hangMicroOption.selected = true; - hangsTypeEl.appendChild(hangMicroOption) + let hangOptions: Array = [] + for (const settings of [ + { + value: '33', + content: "Instant", + }, + { + value: '100', + content: 'Circumstantial' + }, + { + value: '250', + content: 'Micro' + }, + { + value: '500', + content: 'Severe' + } + ]) { + let hangOption = document.createElement('option') + hangOption.value = settings.value + '000000' + hangOption.textContent = settings.content + hangOption.selected = false + hangOptions.push(hangOption) + hangsTypeEl.appendChild(hangOption) + } - FlagsConfig.updateFlagsConfig('hangValue', hangInstantOption.value); + FlagsConfig.updateFlagsConfig('hangValue', hangOptions[0].value); + hangOptions[0].selected = true hangsTypeEl.addEventListener('change', function () { let selectValue = this.selectedOptions[0].value; - console.log(this); - console.log(this.selectedOptions[0]); - console.log(this.selectedOptions[0].value); FlagsConfig.updateFlagsConfig('hangValue', selectValue); }); @@ -242,7 +254,6 @@ export class SpFlags extends BaseElement { hangsTypeEl.removeAttribute('disabled'); } else { hangsTypeEl.setAttribute('disabled', 'disabled'); - FlagsConfig.updateFlagsConfig('hangValue', hangInstantOption.value); } configFooterDiv.appendChild(hangsLableEl); configFooterDiv.appendChild(hangsTypeEl); diff --git a/ide/src/trace/component/SpSystemTrace.init.ts b/ide/src/trace/component/SpSystemTrace.init.ts index a37157a6..21f252bc 100644 --- a/ide/src/trace/component/SpSystemTrace.init.ts +++ b/ide/src/trace/component/SpSystemTrace.init.ts @@ -1049,6 +1049,7 @@ export async function spSystemTraceInit( } if (sp.loadTraceCompleted) { sp.traceSheetEL?.displaySystemLogsData(); + sp.traceSheetEL?.displayHangsData(); sp.traceSheetEL?.displaySystemStatesData(); } sp.intersectionObserver?.observe(it); diff --git a/ide/src/trace/component/chart/SpChartManager.ts b/ide/src/trace/component/chart/SpChartManager.ts index f96fafda..355a1c20 100644 --- a/ide/src/trace/component/chart/SpChartManager.ts +++ b/ide/src/trace/component/chart/SpChartManager.ts @@ -150,7 +150,7 @@ export class SpChartManager { if (FlagsConfig.getFlagsConfigEnableStatus('Bpftrace')) { await this.spBpftraceChart.init(null); } - if (FlagsConfig.getFlagsConfigEnableStatus('UserPluginsRow')){ + if (FlagsConfig.getFlagsConfigEnableStatus('UserPluginsRow')) { await this.spUserFileChart.init(null) } if (FlagsConfig.getFlagsConfigEnableStatus('SchedulingAnalysis')) { @@ -173,8 +173,10 @@ export class SpChartManager { await this.initCpu(progress); await this.logChart.init(); await this.spHiSysEvent.init(); - progress('HangChart inin', 80); - await this.hangChart.init(); + if (FlagsConfig.getFlagsConfigEnableStatus("Hangs")) { + progress('Hang init', 80); + await this.hangChart.init(); + } progress('Clock init', 82); await this.clockChart.init(); progress('Irq init', 84); diff --git a/ide/src/trace/component/chart/SpHangChart.ts b/ide/src/trace/component/chart/SpHangChart.ts index 1f5086d5..80af79b2 100644 --- a/ide/src/trace/component/chart/SpHangChart.ts +++ b/ide/src/trace/component/chart/SpHangChart.ts @@ -17,12 +17,10 @@ import { SpSystemTrace } from '../SpSystemTrace'; import { TraceRow } from '../trace/base/TraceRow'; import { renders } from '../../database/ui-worker/ProcedureWorker'; import { info } from '../../../log/Log'; -import { HangRender, HangStruct } from '../../database/ui-worker/ProcedureWorkerHang'; -import { ColorUtils } from '../trace/base/ColorUtils'; +import { HangStruct } from '../../database/ui-worker/ProcedureWorkerHang'; import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; -import { Utils } from '../trace/base/Utils'; -import { queryHangFuncName, queryRealHangData } from '../../database/sql/Hang.sql'; -import { realHangDataSender } from '../../database/data-trafic/HangDataSender'; +import { queryHangFuncName, queryHangData } from '../../database/sql/Hang.sql'; +import { hangDataSender } from '../../database/data-trafic/HangDataSender'; import { BaseStruct } from '../../bean/BaseStruct'; export type HangType = "Instant" | "Circumstantial" | "Micro" | "Severe" | "" @@ -71,20 +69,18 @@ export class SpHangChart { } ): void { traceRow.supplierFrame = (): Promise => { - let promiseData = realHangDataSender(it.id, traceRow) + let promiseData = hangDataSender(it.id, traceRow) if (promiseData === null) { - // @ts-ignore - return new Promise>((resolve) => resolve([])); + return new Promise>((resolve) => resolve([])); } else { - // @ts-ignore - return promiseData.then((resultHang: Array) => { - for (const hangItem of resultHang) { - hangItem.pname = it.name - hangItem.type = SpHangChart.calculateHangType(hangItem.dur!) - hangItem.content = this.funcNameMap.get(hangItem.id!) - } - return resultHang; - }); + return promiseData.then((resultHang: Array) => + resultHang.map(hangItem => ({ + ...hangItem, + pname: it.name, + type: SpHangChart.calculateHangType(hangItem.dur!), + content: this.funcNameMap.get(hangItem.id!) + })) + ); } }; } @@ -106,13 +102,12 @@ export class SpHangChart { context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; } traceRow.canvasSave(context); - (renders.hang as HangRender).renderMainThread( + renders.hang.renderMainThread( { context: context, useCache: useCache, type: it.name, index: hangId, - processName: it.name }, traceRow ); @@ -122,44 +117,44 @@ export class SpHangChart { async initData(folder: TraceRow): Promise { let hangStartTime = new Date().getTime(); - let realHangList = await queryRealHangData(); - if (realHangList.length === 0) { + let hangList = await queryHangData(); + if (hangList.length === 0) { return; } this.trace.rowsEL?.appendChild(folder); - for (let i = 0; i < realHangList.length; i++) { + for (let i = 0; i < hangList.length; i++) { const it: { id: number, name: string, num: number - } = realHangList[i]; + } = hangList[i]; let traceRow = TraceRow.skeleton(); - traceRow.rowId = it.name ?? '' + it.id; - traceRow.rowType = TraceRow.ROW_TYPE_HANG; - traceRow.rowParentId = folder.rowId; - traceRow.style.height = '40px'; - traceRow.name = it.name ?? '-'; - traceRow.rowHidden = !folder.expansion; - traceRow.setAttribute('children', ''); - traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; - traceRow.selectChangeHandler = this.trace.selectChangeHandler; - this.hangSupplierFrame(traceRow, it); - traceRow.getCacheData = (args: unknown): Promise> => realHangDataSender(it.id, traceRow, args) + traceRow.rowId = `${it.name ?? 'Process'} ${it.id}` + traceRow.rowType = TraceRow.ROW_TYPE_HANG + traceRow.rowParentId = folder.rowId + traceRow.style.height = '40px' + traceRow.name = `${it.name ?? 'Process'} ${it.id}` + traceRow.rowHidden = !folder.expansion + traceRow.setAttribute('children', '') + traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler + traceRow.selectChangeHandler = this.trace.selectChangeHandler + this.hangSupplierFrame(traceRow, it) + traceRow.getCacheData = (args: unknown): Promise> => hangDataSender(it.id, traceRow, args) traceRow.focusHandler = (ev): void => { + let hangStruct = HangStruct.hoverHangStruct this.trace?.displayTip( - traceRow, - HangStruct.hoverHangStruct, - `${JSON.stringify(HangStruct.hoverHangStruct?.content ?? "Hatsune Miku")}` - ); - }; + traceRow, hangStruct, + `${hangStruct?.type} ${hangStruct?.dur}` + ) + } traceRow.findHoverStruct = (): void => { - HangStruct.hoverHangStruct = traceRow.getHoverStruct(); - }; - this.hangThreadHandler(traceRow, it, i); - folder.addChildTraceRow(traceRow); + HangStruct.hoverHangStruct = traceRow.getHoverStruct() + } + this.hangThreadHandler(traceRow, it, i) + folder.addChildTraceRow(traceRow) } - let durTime = new Date().getTime() - hangStartTime; - info('The time to load the HangData is: ', durTime); + let durTime = new Date().getTime() - hangStartTime + info('The time to load the HangData is: ', durTime) } async initFolder(): Promise> { diff --git a/ide/src/trace/component/chart/SpProcessChart.ts b/ide/src/trace/component/chart/SpProcessChart.ts index 47be8264..e5145c78 100644 --- a/ide/src/trace/component/chart/SpProcessChart.ts +++ b/ide/src/trace/component/chart/SpProcessChart.ts @@ -53,6 +53,10 @@ import { } from '../../database/sql/ProcessThread.sql'; import { queryAllJankProcess } from '../../database/sql/Janks.sql'; import { BaseStruct } from '../../bean/BaseStruct'; +import { HangStruct } from '../../database/ui-worker/ProcedureWorkerHang'; +import { hangDataSender } from '../../database/data-trafic/HangDataSender'; +import { SpHangChart } from './SpHangChart'; +import { queryHangData } from '../../database/sql/Hang.sql'; export class SpProcessChart { private readonly trace: SpSystemTrace; @@ -75,6 +79,7 @@ export class SpProcessChart { private processSrcSliceMap: Map = new Map(); private renderRow: TraceRow | null = null; private loadAppStartup: boolean = false; + private hangProcessSet: Set = new Set() constructor(trace: SpSystemTrace) { this.trace = trace; } @@ -239,8 +244,13 @@ export class SpProcessChart { if (FlagsConfig.getFlagsConfigEnableStatus('TaskPool')) { allTaskPoolPid = await queryTaskPoolProcessIds(); } - let renderServiceProcess = await queryRsProcess(); // @ts-ignore - info('ProcessList Data size is: ', processList!.length); // @ts-ignore + let renderServiceProcess = await queryRsProcess(); + // @ts-ignore + info('ProcessList Data size is: ', processList!.length); + + this.hangProcessSet = new Set((await queryHangData()).map(item => item.id)) + + // @ts-ignore await this.initProcessRow(processList, allTaskPoolPid, allJankProcess, renderServiceProcess); let durTime = new Date().getTime() - time; info('The time to load the Process data is: ', durTime); @@ -305,12 +315,14 @@ export class SpProcessChart { rsProcess: Array ): Promise { for (let i = 0; i < pArr.length; i++) { - const it = pArr[i]; + const it = pArr[i] as { + pid: number; + processName: string | null + }; if ( - //@ts-ignore - (this.processThreadDataCountMap.get(it.pid) || 0) === 0 && //@ts-ignore - (this.processThreadCountMap.get(it.pid) || 0) === 0 && //@ts-ignore - (this.processFuncDataCountMap.get(it.pid) || 0) === 0 && //@ts-ignore + (this.processThreadDataCountMap.get(it.pid) || 0) === 0 && + (this.processThreadCountMap.get(it.pid) || 0) === 0 && + (this.processFuncDataCountMap.get(it.pid) || 0) === 0 && (this.processMemDataCountMap.get(it.pid) || 0) === 0 ) { continue; @@ -321,10 +333,9 @@ export class SpProcessChart { let startupRow: TraceRow | undefined = undefined; let soRow: TraceRow | undefined = undefined; if (this.loadAppStartup) { - //@ts-ignore if (this.startupProcessArr.find((sp) => sp.pid === it.pid)) { startupRow = this.addStartUpRow(processRow); - } //@ts-ignore + } let maxSoDepth = this.processSoMaxDepth.find((md) => md.pid === it.pid); if (maxSoDepth) { soRow = this.addSoInitRow(processRow, maxSoDepth.maxDepth); @@ -333,28 +344,29 @@ export class SpProcessChart { let actualRow: TraceRow | null = null; let expectedRow: TraceRow | null = null; - this.renderRow = null; //@ts-ignore + let hangsRow: TraceRow | null = null; + this.renderRow = null; if (it.processName === 'render_service') { - //@ts-ignore - this.addThreadList(it, processRow, expectedRow, actualRow, soRow, startupRow); //@ts-ignore - this.addProcessMemInfo(it, processRow); //@ts-ignore + this.addThreadList(it, processRow, expectedRow, actualRow, hangsRow, soRow, startupRow); + this.addProcessMemInfo(it, processRow); if (jankArr.indexOf(it.pid!) > -1) { expectedRow = this.addExpectedRow(it, processRow, rsProcess); actualRow = this.addActualRow(it, processRow, rsProcess); } - this.addProcessRowListener(processRow, actualRow); //@ts-ignore - this.addAsyncFunction(it, processRow);//@ts-ignore + hangsRow = this.addHangRow(it, processRow) + this.addProcessRowListener(processRow, actualRow); + this.addAsyncFunction(it, processRow); this.addAsyncCatFunction(it, processRow); } else { - //@ts-ignore if (jankArr.indexOf(it.pid!) > -1) { expectedRow = this.addExpectedRow(it, processRow, rsProcess); actualRow = this.addActualRow(it, processRow, rsProcess); } - this.addProcessRowListener(processRow, actualRow); //@ts-ignore - this.addAsyncFunction(it, processRow); //@ts-ignore - this.addProcessMemInfo(it, processRow); //@ts-ignore - this.addThreadList(it, processRow, expectedRow, actualRow, soRow, startupRow);//@ts-ignore + hangsRow = this.addHangRow(it, processRow) + this.addProcessRowListener(processRow, actualRow); + this.addAsyncFunction(it, processRow); + this.addProcessMemInfo(it, processRow); + this.addThreadList(it, processRow, expectedRow, actualRow, hangsRow, soRow, startupRow); this.addAsyncCatFunction(it, processRow); } @@ -633,6 +645,64 @@ export class SpProcessChart { return actualRow; } + //@ts-ignore + addHangRow( + data: { + pid: number; + processName: string | null + }, + row: TraceRow + ): TraceRow | null { + if (!this.hangProcessSet.has(data.pid) && !FlagsConfig.getFlagsConfigEnableStatus("Hangs")) { + return null + } + let hangsRow = TraceRow.skeleton(); + hangsRow.rowType = TraceRow.ROW_TYPE_HANG_INNER; + hangsRow.rowId = `${data.processName ?? 'Process'} ${data.pid}` + hangsRow.rowParentId = `${data.pid}`; + hangsRow.rowHidden = !row.expansion; + hangsRow.style.width = '100%'; + hangsRow.name = 'Hangs'; + hangsRow.addTemplateTypes('FrameTimeline'); + hangsRow.setAttribute('children', ''); + hangsRow.supplierFrame = async (): Promise => { + let promiseData = hangDataSender(data.pid, hangsRow) + if (promiseData === null) { + return new Promise>((resolve) => resolve([])); + } else { + return promiseData.then((resultHang: Array) => + resultHang.map(hangItem => ({ + ...hangItem, + pname: data.processName ?? "process", + type: SpHangChart.calculateHangType(hangItem.dur!), + content: this.funcNameMap.get(hangItem.id!) + })) + ); + } + }; + hangsRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; + hangsRow.selectChangeHandler = this.trace.selectChangeHandler; + hangsRow.findHoverStruct = (): void => { + HangStruct.hoverHangStruct = hangsRow.getHoverStruct() + } + hangsRow.onThreadHandler = rowThreadHandler( + 'hang', + 'context', + { + type: 'hangs_frame_timeline_slice', + }, + hangsRow, + this.trace + ); + + if (this.renderRow) { + row.addChildTraceRowBefore(hangsRow, this.renderRow); + } else { + row.addChildTraceRow(hangsRow); + } + return hangsRow; + } + jankSenderCallback( res: JankStruct[], type: string, @@ -774,6 +844,7 @@ export class SpProcessChart { pRow: TraceRow, expectedRow: TraceRow | null, actualRow: TraceRow | null, + hangsRow: TraceRow | null, soRow: TraceRow | undefined, startupRow: TraceRow | undefined ): void { @@ -817,7 +888,7 @@ export class SpProcessChart { tRow, this.trace ); - this.insertRowToDoc(it, j, thread, pRow, tRow, threads, tRowArr, actualRow, expectedRow, startupRow, soRow); + this.insertRowToDoc(it, j, thread, pRow, tRow, threads, tRowArr, actualRow, expectedRow, hangsRow, startupRow, soRow); this.addFuncStackRow(it, thread, j, threads, tRowArr, tRow, pRow); if ((thread.switchCount || 0) === 0) { tRow.rowDiscard = true; @@ -852,6 +923,7 @@ export class SpProcessChart { threadRowArr: TraceRow[], //@ts-ignore actualRow: TraceRow | null, //@ts-ignore expectedRow: TraceRow | null, + hangsRow: TraceRow | null, startupRow: TraceRow | null | undefined, soRow: TraceRow | null | undefined ): void { @@ -869,6 +941,8 @@ export class SpProcessChart { processRow.addChildTraceRowAfter(threadRow, actualRow); } else if (expectedRow !== null) { processRow.addChildTraceRowAfter(threadRow, expectedRow); + } else if (hangsRow !== null) { + processRow.addChildTraceRowAfter(threadRow, hangsRow); } else if (soRow) { processRow.addChildTraceRowAfter(threadRow, soRow); } else if (startupRow) { diff --git a/ide/src/trace/component/trace/base/ColorUtils.ts b/ide/src/trace/component/trace/base/ColorUtils.ts index 12964507..e20ce824 100644 --- a/ide/src/trace/component/trace/base/ColorUtils.ts +++ b/ide/src/trace/component/trace/base/ColorUtils.ts @@ -14,6 +14,7 @@ */ import { CpuStruct } from '../../../database/ui-worker/cpu/ProcedureWorkerCPU'; +import { HangType } from '../../chart/SpHangChart'; export class ColorUtils { public static GREY_COLOR: string = '#f0f0f0'; @@ -121,6 +122,16 @@ export class ColorUtils { return logColor; } + public static getHangColor(hangType: HangType): string { + return ({ + "Instant": "#559CFF", + "Circumstantial": "#FFE44D", + "Micro": "#FEB354", + "Severe": "#FC7470", + "": "#000000", + })[hangType] + } + public static getHisysEventColor(level: string | number): string { let eventColor: string = '#00000'; switch (level) { diff --git a/ide/src/trace/component/trace/base/RangeSelect.ts b/ide/src/trace/component/trace/base/RangeSelect.ts index 47e0fa83..a7e2b0b0 100644 --- a/ide/src/trace/component/trace/base/RangeSelect.ts +++ b/ide/src/trace/component/trace/base/RangeSelect.ts @@ -278,7 +278,6 @@ export class RangeSelect { document.body.style.cursor = 'default'; } if (this.isHover && this.isMouseDown) { - // 似乎调用不到 this.handleRangeSelectAndDraw(rows, ev); return; } @@ -292,7 +291,6 @@ export class RangeSelect { } // @ts-ignore private handleRangeSelect(rows: Array>): void { - // console.log("handleRangeSelect", arguments) let rangeSelect: RangeSelectStruct | undefined; let favoriteRect = this.trace?.favoriteChartListEL?.getBoundingClientRect(); let favoriteLimit = favoriteRect!.top + favoriteRect!.height; @@ -354,13 +352,11 @@ export class RangeSelect { } private handleDrawForNotMouseDown(): void { - // console.log("handleDrawForNotMouseDown", arguments) this.timerShaftEL!.sportRuler!.isRangeSelect = isNotEmpty(this.rangeTraceRow) ?? false; this.timerShaftEL!.sportRuler!.draw(); } // @ts-ignore private handleRangeSelectAndDraw(rows: Array>, ev: MouseEvent): void { - // console.log("handleRangeSelectAndDraw", arguments) let rangeSelect: RangeSelectStruct | undefined; this.rangeTraceRow = rows.filter((it) => { if (it.rangeSelect) { diff --git a/ide/src/trace/component/trace/base/TraceRow.ts b/ide/src/trace/component/trace/base/TraceRow.ts index 7702083a..5ae1e194 100644 --- a/ide/src/trace/component/trace/base/TraceRow.ts +++ b/ide/src/trace/component/trace/base/TraceRow.ts @@ -114,6 +114,7 @@ export class TraceRow extends HTMLElement { static ROW_TYPE_CLOCK_GROUP = 'clock-group'; static ROW_TYPE_COLLECT_GROUP = 'collect-group'; static ROW_TYPE_HANG = 'hang'; + static ROW_TYPE_HANG_INNER = 'hang-inner'; static ROW_TYPE_CLOCK = 'clock'; static ROW_TYPE_IRQ_GROUP = 'irq-group'; static ROW_TYPE_IRQ = 'irq'; diff --git a/ide/src/trace/component/trace/base/TraceSheet.ts b/ide/src/trace/component/trace/base/TraceSheet.ts index f2fbde57..4fdc6618 100644 --- a/ide/src/trace/component/trace/base/TraceSheet.ts +++ b/ide/src/trace/component/trace/base/TraceSheet.ts @@ -318,7 +318,7 @@ export class TraceSheet extends BaseElement { let element = tabConfig[id]; let pane = this.shadowRoot!.querySelector(`#${id as string}`); if (element.require) { - pane!.hidden = !element.require(this.selection); + pane!.hidden = !element.require(this.selection!); } else { pane!.hidden = true; } @@ -634,8 +634,8 @@ export class TraceSheet extends BaseElement { ); displayMemData = (data: ProcessMemStruct): void => this.displayTab('current-selection').setMemData(data); - displayHangData = (data: HangStruct): Promise => - this.displayTab('current-selection').setHangData(data); + displayHangData = (data: HangStruct, sp: SpSystemTrace): Promise => + this.displayTab('current-selection').setHangData(data, sp); displayClockData = (data: ClockStruct): Promise => this.displayTab('current-selection').setClockData(data); displayPerfToolsData = (data: PerfToolStruct): void => @@ -853,6 +853,17 @@ export class TraceSheet extends BaseElement { } } }; + + displayHangsData = (): void => { + let tblHangPanel = this.shadowRoot?.querySelector("lit-tabpane[id='box-hang']"); + if (tblHangPanel) { + let tblHang = tblHangPanel.querySelector('tab-hang'); + if (tblHang) { + tblHang.initTabSheetEl(this); + } + } + }; + displaySampleData = (data: SampleStruct, reqProperty: any): void => { this.displayTab('box-sample-instruction').setSampleInstructionData(data, reqProperty); this.optionsDiv!.style.display = 'flex'; diff --git a/ide/src/trace/component/trace/base/TraceSheetConfig.ts b/ide/src/trace/component/trace/base/TraceSheetConfig.ts index 2b014e49..de27eb25 100644 --- a/ide/src/trace/component/trace/base/TraceSheetConfig.ts +++ b/ide/src/trace/component/trace/base/TraceSheetConfig.ts @@ -64,7 +64,8 @@ import { TabPaneFreqLimit } from '../sheet/freq/TabPaneFreqLimit'; import { TabPaneCpuFreqLimits } from '../sheet/freq/TabPaneCpuFreqLimits'; import { TabpaneNMCalltree } from '../sheet/native-memory/TabPaneNMCallTree'; import { TabPaneClockCounter } from '../sheet/clock/TabPaneClockCounter'; -import { TabPaneHangCounter } from '../sheet/Hang/TabPaneHangCounter'; +import { TabPaneHang } from '../sheet/Hang/TabPaneHang'; +import { TabPaneHangSummary } from '../sheet/Hang/TabPaneHangSummary'; import { TabPaneIrqCounter } from '../sheet/irq/TabPaneIrqCounter'; import { TabPaneFrames } from '../sheet/jank/TabPaneFrames'; import { TabPanePerfAnalysis } from '../sheet/hiperf/TabPanePerfAnalysis'; @@ -131,7 +132,13 @@ import { TabPaneSampleInstructionSelection } from '../sheet/bpftrace/TabPaneSamp import { TabPaneDataCut } from '../sheet/TabPaneDataCut'; import { TabPaneUserPlugin } from '../sheet/userPlugin/TabPaneUserPlugin'; -export let tabConfig: unknown = { +export let tabConfig: { + [key: string]: { + title: string + type: any + require?: (param: SelectionParam) => boolean + } +} = { 'current-selection': { title: 'Current Selection', type: TabPaneCurrentSelection, @@ -201,7 +208,7 @@ export let tabConfig: unknown = { 'box-slices': { title: 'Slices', type: TabPaneSlices, - require: (param: SelectionParam) => param.funTids.length > 0 || param.funAsync.length || param.funCatAsync.length > 0, + require: (param: SelectionParam) => param.funTids.length > 0 || param.funAsync.length > 0 || param.funCatAsync.length > 0, }, 'box-counters': { title: 'Counters', @@ -213,11 +220,16 @@ export let tabConfig: unknown = { type: TabPaneClockCounter, require: (param: SelectionParam) => param.clockMapData.size > 0, }, - // 'box-hang-counters': { - // title: 'Hang Counters', - // type: TabPaneHangCounter, - // require: (param: SelectionParam) => param.hangMapData.size > 0, - // }, + 'box-hang': { + title: 'Hangs', + type: TabPaneHang, + require: (param: SelectionParam) => param.hangMapData.size > 0, + }, + 'box-hang-summary': { + title: 'Hang Summary', + type: TabPaneHangSummary, + require: (param: SelectionParam) => param.hangMapData.size > 0, + }, 'box-irq-counters': { title: 'Irq Counters', type: TabPaneIrqCounter, diff --git a/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts b/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts index 1467e99f..4f74d2dd 100644 --- a/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts +++ b/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts @@ -59,6 +59,7 @@ import { TabPaneCurrentSelectionHtml } from './TabPaneCurrentSelection.html'; import { queryRealTime } from '../../../database/sql/Clock.sql'; import { PerfToolStruct } from '../../../database/ui-worker/ProcedureWorkerPerfTool'; import { HangStruct } from '../../../database/ui-worker/ProcedureWorkerHang'; +import { BaseStruct } from '../../../bean/BaseStruct'; const INPUT_WORD = 'This is the interval from when the task became eligible to run \n(e.g.because of notifying a wait queue it was a suspended on) to\n when it started running.'; @@ -546,29 +547,54 @@ export class TabPaneCurrentSelection extends BaseElement { this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); } - async setHangData(data: HangStruct): Promise { - // TODO: - // console.log("setHangData", arguments) - // await this.setRealTime(); - // this.setTableHeight('auto'); - // //时钟信息 - // this.tabCurrentSelectionInit('Counter Details'); - // let list: unknown[] = []; - // list.push({ - // name: 'StartTime(Relative)', - // value: getTimeString(data.startNS || 0), - // }); - // this.createStartTimeNode(list, data.startNS || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); - // list.push({ - // name: 'Value', - // value: ColorUtils.formatNumberComma(data.value || 0), - // }); - // list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); - // list.push({ name: "Fake Data", value: "Useless Info." }) - // this.currentSelectionTbl!.dataSource = list; - // // @ts-ignore - // let startTimeAbsolute = (data.startNS || 0) + (window as unknown).recordStartNS; - // this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); + async setHangData(data: HangStruct, sp: SpSystemTrace): Promise { + await this.setRealTime(); + this.setTableHeight('auto'); + this.tabCurrentSelectionInit('Hang Details'); + let list: unknown[] = []; + list.push({ + name: 'StartTime(Relative)', + value: getTimeString(data.startNS || 0), + }); + this.createStartTimeNode(list, data.startNS || 0, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); + list.push({ name: 'Duration', value: getTimeString(data.dur || 0) }); + list.push({ + name: 'HangType', + value: `
+
${data.type}
+ +
` + }); + data.content!.split(',').map((item, index) => ({ + name: [ + "Send Event TID", + "Send Time", + "Expect Handle Time", + "Task Name / Task ID", + "Caller" + ][index], + value: item, + })).forEach((item, index) => { + list.push(item) + }) + + this.currentSelectionTbl!.dataSource = list; + // @ts-ignore + let startTimeAbsolute = (data.startNS || 0) + window.recordStartNS; + this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); + + let scrollIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#thread-id'); + scrollIcon?.addEventListener('click', () => { + const rowId = `${data.pname ?? 'Process'} ${data.pid}` + const rowParentId = `${data.pid}` + const rowType = TraceRow.ROW_TYPE_HANG_INNER + + let row = sp.rowsEL?.querySelector>(`trace-row[row-id='${rowParentId}'][folder]`); + if (row) { + row.expansion = true + sp.scrollToProcess(rowId, rowParentId, rowType) + } + }); } setPerfToolsData(data: PerfToolStruct): void { @@ -697,7 +723,7 @@ export class TabPaneCurrentSelection extends BaseElement {
${ // @ts-ignore Utils.getEndState(near.state) - }
+ } `, }); @@ -709,7 +735,7 @@ export class TabPaneCurrentSelection extends BaseElement {
${ // @ts-ignore Utils.getEndState(near.state) - }
+ } `, }); @@ -1311,8 +1337,8 @@ export class TabPaneCurrentSelection extends BaseElement { index === 0 ? 'NULL' : `${AppStartupStruct.getStartupName(sortedArray[index - 1].startName)} ${getTimeString( - sortedArray[index - 1].startTs + sortedArray[index - 1].dur - )}`, + sortedArray[index - 1].startTs + sortedArray[index - 1].dur + )}`, }); list.push({ name: 'EndSlice', @@ -1320,8 +1346,8 @@ export class TabPaneCurrentSelection extends BaseElement { index === sortedArray.length - 1 ? 'NULL' : `${AppStartupStruct.getStartupName(sortedArray[index + 1].startName)} ${getTimeString( - sortedArray[index + 1].startTs - )}`, + sortedArray[index + 1].startTs + )}`, }); } }); @@ -1783,7 +1809,7 @@ export class TabPaneCurrentSelection extends BaseElement { this.currentSelectionTbl = this.shadowRoot?.querySelector('#selectionTbl'); this.wakeupListTbl = this.shadowRoot?.querySelector('#wakeupListTbl'); this.scrollView = this.shadowRoot?.querySelector('#scroll_view'); - this.currentSelectionTbl?.addEventListener('column-click', (ev: unknown): void => {}); //@ts-ignore + this.currentSelectionTbl?.addEventListener('column-click', (ev: unknown): void => { }); //@ts-ignore window.subscribe(window.SmartEvent.UI.WakeupList, (data: Array) => this.showWakeupListTableData(data)); } diff --git a/ide/src/trace/component/trace/sheet/cpu/TabPaneCpuByThread.ts b/ide/src/trace/component/trace/sheet/cpu/TabPaneCpuByThread.ts index 69aa8ce5..4f68932b 100644 --- a/ide/src/trace/component/trace/sheet/cpu/TabPaneCpuByThread.ts +++ b/ide/src/trace/component/trace/sheet/cpu/TabPaneCpuByThread.ts @@ -331,7 +331,9 @@ export class TabPaneCpuByThread extends BaseElement { totalWallDuration += obj.wallDuration; totalOccurrences += obj.occurrences; if (obj.tid !== '[NULL]' && obj.pid !== '[NULL]') { + // @ts-ignore let process = Utils.getInstance().getProcessMap(cpuByThreadValue.traceId).get(obj.pid); + // @ts-ignore let thread = Utils.getInstance().getThreadMap(cpuByThreadValue.traceId).get(obj.tid); obj.thread = thread == null || thread.length === 0 ? '[NULL]' : thread; obj.process = process == null || process.length === 0 ? '[NULL]' : process; diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts new file mode 100644 index 00000000..f775901d --- /dev/null +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts @@ -0,0 +1,122 @@ +/* + * 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 TabPaneHangHtml = ` + +
+ +
+
+ +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + +`; diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts new file mode 100644 index 00000000..95b8a5d2 --- /dev/null +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts @@ -0,0 +1,301 @@ +/* + * 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 { TraceRow } from '../../base/TraceRow'; +import { TraceSheet } from '../../base/TraceSheet'; +import { Flag } from '../../timer-shaft/Flag'; +import { SpSystemTrace } from '../../../SpSystemTrace'; +import { ns2Timestamp, ns2x, Rect } from '../../../../database/ui-worker/ProcedureWorkerCommon'; +import { ColorUtils } from '../../base/ColorUtils'; +import { LitPageTable } from '../../../../../base-ui/table/LitPageTable'; +import { LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar'; +import { TabPaneHangHtml } from './TabPaneHang.html'; +import { HangStruct } from '../../../../database/ui-worker/ProcedureWorkerHang'; +import { queryAllHangs } from '../../../../database/sql/Hang.sql'; +import { HangType, SpHangChart } from '../../../chart/SpHangChart'; +import { getTimeString } from '../TabPaneCurrentSelection'; + +@element('tab-hang') +export class TabPaneHang extends BaseElement { + // Elements + private spSystemTrace: SpSystemTrace | undefined | null; + private traceSheetEl: TraceSheet | undefined | null; + private levelFilterInput: HTMLSelectElement | undefined | null; + private searchFilterInput: HTMLInputElement | undefined | null; + private processFilter: HTMLInputElement | undefined | null; + private hangTableTitle: HTMLDivElement | undefined | null; + private hangTbl: LitPageTable | undefined | null; + + private tableTimeHandle: (() => void) | undefined; + private tableTitleTimeHandle: (() => void) | undefined; + private systemHangSource: HangStructInPane[] = []; + private filterData: HangStructInPane[] = []; + + private optionLevel: string[] = ['Instant', 'Circumstantial', 'Micro', 'Severe']; + private allowTag: Set = new Set(); + private progressEL: LitProgressBar | null | undefined; + private timeOutId: number | undefined; + + set data(selectionParam: SelectionParam) { + if (this.hangTbl) { + this.hangTbl.recycleDataSource = []; + this.filterData = []; + } + window.clearTimeout(this.timeOutId); + queryAllHangs().then((ret) => { + const filter = new Set([...selectionParam.hangMapData.keys()].map(key => key.split(' ').at(-1))) + ret = ret.filter(struct => ( + filter.has(`${struct.pid ?? 0}`) + && ((struct.startNS ?? 0) <= selectionParam.rightNs) + && (selectionParam.leftNs <= ((struct.startNS ?? 0) + (struct.dur ?? 0))) + )) + + if (ret.length == 0) { + this.progressEL!.loading = false + } + this.systemHangSource = ret.map(HangStructInPane.new) + this.refreshTable() + }) + } + + init(): void { + this.levelFilterInput = this.shadowRoot?.querySelector('#level-filter'); + this.hangTableTitle = this.shadowRoot?.querySelector('#hang-title'); + this.searchFilterInput = this.shadowRoot?.querySelector('#search-filter'); + this.processFilter = this.shadowRoot?.querySelector('#process-filter'); + this.spSystemTrace = document.querySelector('body > sp-application')?.shadowRoot?.querySelector('#sp-system-trace'); + this.tableTimeHandle = this.delayedRefresh(this.refreshTable); + this.tableTitleTimeHandle = this.delayedRefresh(this.refreshLogsTitle); + this.hangTbl = this.shadowRoot?.querySelector('#tb-hang'); + this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar; + this.hangTbl!.getItemTextColor = (data): string => { + const hangData = data as HangStructInPane + return ColorUtils.getHangColor(hangData.type as HangType); + }; + this.hangTbl!.itemTextHandleMap.set('startNS', (startTs) => { + // @ts-ignore + return ns2Timestamp(startTs); + }); + this.hangTbl!.addEventListener('row-hover', (e): void => { + // @ts-ignore + let data = e.detail.data as HangStructInPane + if (data) { + let pointX: number = ns2x( + data.startNS || 0, + TraceRow.range!.startNS, + TraceRow.range!.endNS, + TraceRow.range!.totalNS, + new Rect(0, 0, TraceRow.FRAME_WIDTH, 0) + ); + this.traceSheetEl!.systemLogFlag = new Flag( + Math.floor(pointX), + 0, + 0, + 0, + data.startNS, + '#999999', + '', + true, + '' + ); + this.spSystemTrace?.refreshCanvas(false); + } + }); + let tbl = this.hangTbl?.shadowRoot?.querySelector('.table'); + tbl!.addEventListener('scroll', () => { + this.tableTitleTimeHandle?.(); + }); + } + + initElements(): void { + this.init(); + this.searchFilterInput!.oninput = (): void => { + this.tableTimeHandle?.(); + }; + this.processFilter!.oninput = (): void => { + this.tableTimeHandle?.(); + }; + this.levelFilterInput!.onchange = (): void => { + this.tableTimeHandle?.(); + }; + } + + connectedCallback(): void { + super.connectedCallback(); + new ResizeObserver((): void => { + this.parentElement!.style.overflow = 'hidden'; + if (this.hangTbl) { + // @ts-ignore + this.hangTbl.shadowRoot.querySelector('.table').style.height = + this.parentElement!.clientHeight - 20 - 45 + 'px'; + } + if (this.filterData.length > 0) { + this.refreshTable(); + this.tableTitleTimeHandle?.(); + } + }).observe(this.parentElement!); + } + + disconnectedCallback(): void { + super.disconnectedCallback(); + } + + initHtml(): string { + return TabPaneHangHtml; + } + + refreshHangTab(): void { + let tbl = this.hangTbl?.shadowRoot?.querySelector('.table'); + let height = 0; + if (tbl) { + const trs = tbl.querySelectorAll('.tr') + trs.forEach((trEl: HTMLElement, index: number): void => { + if (index === 0) { + let frontTotalRowSize = Math.round((tbl!.scrollTop / trEl.clientHeight) * 100) / 100; + if (frontTotalRowSize.toString().indexOf('.') >= 0) { + let rowCount = frontTotalRowSize.toString().split('.'); + height += trEl.clientHeight - (Number(rowCount[1]) / 100) * trEl.clientHeight; + } + } + let allTdEl = trEl.querySelectorAll('.td'); + allTdEl[0].style.color = '#3D88C7'; + allTdEl[0].style.textDecoration = 'underline'; + allTdEl[0].style.textDecorationColor = '#3D88C7'; + }); + } + } + + refreshLogsTitle(): void { + let tbl = this.hangTbl?.shadowRoot?.querySelector('.table'); + let height = 0; + let firstRowHeight = 27; + let tableHeadHeight = 26; + this.refreshHangTab(); + if (this.hangTbl && this.hangTbl.currentRecycleList.length > 0) { + let startDataIndex = this.hangTbl.startSkip + 1; + let endDataIndex = startDataIndex; + let crossTopHeight = tbl!.scrollTop % firstRowHeight; + let topShowHeight = crossTopHeight === 0 ? 0 : firstRowHeight - crossTopHeight; + if (topShowHeight < firstRowHeight * 0.3) { + startDataIndex++; + } + let tableHeight = Number(tbl!.style.height.replace('px', '')) - tableHeadHeight; + while (height < tableHeight) { + if (firstRowHeight <= 0 || height + firstRowHeight > tableHeight) { + break; + } + height += firstRowHeight; + endDataIndex++; + } + if (tableHeight - height - topShowHeight > firstRowHeight * 0.3) { + endDataIndex++; + } + if (endDataIndex >= this.filterData.length) { + endDataIndex = this.filterData.length; + } else { + endDataIndex = this.hangTbl.startSkip === 0 ? endDataIndex - 1 : endDataIndex; + } + this.hangTableTitle!.textContent = `Hangs [${this.hangTbl.startSkip === 0 ? 1 : startDataIndex}, + ${endDataIndex}] / ${this.filterData.length || 0}`; + } else { + this.hangTableTitle!.textContent = 'Hangs [0, 0] / 0'; + } + if (this.hangTbl!.recycleDataSource.length > 0) { + this.progressEL!.loading = false; + } + } + + initTabSheetEl(traceSheet: TraceSheet): void { + this.traceSheetEl = traceSheet; + this.levelFilterInput!.selectedIndex = 0; + this.allowTag.clear(); + this.processFilter!.value = ''; + this.searchFilterInput!.value = ''; + } + + private updateFilterData(): void { + if (this.systemHangSource?.length > 0) { + this.filterData = this.systemHangSource.filter((data) => this.isFilterLog(data)); + } + if (this.hangTbl) { + // @ts-ignore + this.hangTbl.shadowRoot.querySelector('.table').style.height = this.parentElement.clientHeight - 20 - 45 + 'px'; + } + if (this.filterData.length > 0) { + this.hangTbl!.recycleDataSource = this.filterData; + } else { + this.hangTbl!.recycleDataSource = []; + } + this.refreshLogsTitle(); + } + + private isFilterLog(data: HangStructInPane): boolean { + let type = this.levelFilterInput?.selectedIndex ?? 0 + let search = this.searchFilterInput?.value.toLocaleLowerCase() ?? '' + let process = this.processFilter?.value.toLocaleLowerCase() ?? '' + return ( + (type === 0 || this.optionLevel.indexOf(data.type) >= type) && + (search === '' || data.caller.toLocaleLowerCase().indexOf(search) >= 0) && + (process === '' || data.pname.toLocaleLowerCase().indexOf(process) >= 0) + ) + } + + private refreshTable(): void { + if (this.traceSheetEl) { + this.traceSheetEl.systemLogFlag = undefined; + this.spSystemTrace?.refreshCanvas(false); + this.updateFilterData(); + } + } + + private delayedRefresh(optionFn: Function, dur: number = tableTimeOut): () => void { + return (...args: []): void => { + window.clearTimeout(this.timeOutId); + this.timeOutId = window.setTimeout((): void => { + optionFn.apply(this, ...args); + }, dur); + }; + } +} + +let defaultIndex: number = 1; +let tableTimeOut: number = 50; + +export class HangStructInPane { + startNS: number = 0 + dur: string = '0' + pname: string = 'Process' + type: string + + sendEventTid: string + sendTime: string + expectHandleTime: string + taskNameId: string + caller: string + + constructor(parent: HangStruct) { + this.startNS = parent.startNS ?? this.startNS + this.dur = getTimeString(parent.dur ?? 0) + this.pname = `${parent.pname ?? this.pname} ${parent.pid ?? ''}`.trim() + this.type = SpHangChart.calculateHangType(parent.dur ?? 0); + [this.sendEventTid, this.sendTime, this.expectHandleTime, this.taskNameId, this.caller] = (parent.content ?? ",0,0,,").split(',').map(i => i.trim()) + } + + static new(parent: HangStruct): HangStructInPane { + return new HangStructInPane(parent) + } +} \ No newline at end of file diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHangCounter.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHangCounter.ts deleted file mode 100644 index 3bb170df..00000000 --- a/ide/src/trace/component/trace/sheet/hang/TabPaneHangCounter.ts +++ /dev/null @@ -1,192 +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 { LitTable } from '../../../../../base-ui/table/lit-table'; -import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; -import { resizeObserver } from '../SheetUtils'; - -@element('tabpane-hang-counter') -export class TabPaneHangCounter extends BaseElement { - private hangCounterTbl: LitTable | null | undefined; - private hangCounterRange: HTMLLabelElement | null | undefined; - private hangCounterSource: Array = []; - - set data(hangCounterValue: SelectionParam) { - //@ts-ignore - this.hangCounterTbl?.shadowRoot?.querySelector('.table')?.style?.height = `${ - this.parentElement!.clientHeight - 45 - }px`; - this.hangCounterRange!.textContent = `Selected range: ${parseFloat( - ((hangCounterValue.rightNs - hangCounterValue.leftNs) / 1000000.0).toFixed(5) - )} ms`; - this.getCounterData(hangCounterValue).then(); - } - - async getCounterData(hangCounterValue: SelectionParam): Promise { - let dataSource: Array = []; - let collect = hangCounterValue.hangMapData; - let sumCount = 0; - this.hangCounterTbl!.loading = true; - for (let key of collect.keys()) { - let counters = collect.get(key); - let res = await counters?.({ - startNS: hangCounterValue.leftNs, - endNS: hangCounterValue.rightNs, - queryAll: true, - }); - let sd = this.createSelectCounterData(key, res || [], hangCounterValue.leftNs, hangCounterValue.rightNs); - sumCount += Number.parseInt(sd.count || '0'); - dataSource.push(sd); - } - let sumData = new SelectionData(); - sumData.count = sumCount.toString(); - sumData.process = ' '; - dataSource.splice(0, 0, sumData); - this.hangCounterTbl!.loading = false; - this.hangCounterSource = dataSource; - this.hangCounterTbl!.recycleDataSource = dataSource; - } - - initElements(): void { - this.hangCounterTbl = this.shadowRoot?.querySelector('#tb-counter'); - this.hangCounterRange = this.shadowRoot?.querySelector('#time-range'); - this.hangCounterTbl!.addEventListener('column-click', (evt): void => { - // @ts-ignore - this.sortByColumn(evt.detail); - }); - } - - connectedCallback(): void { - super.connectedCallback(); - resizeObserver(this.parentElement!, this.hangCounterTbl!); - } - - initHtml(): string { - return ` - - - - - - - - - - - - - - - - - - - - - - - `; - } - - createSelectCounterData(name: string, list: Array, leftNs: number, rightNs: number): SelectionData { - let selectCounterData = new SelectionData(); - if (list.length > 0) { - let range = rightNs - leftNs; - let first = list[0]; - // @ts-ignore - selectCounterData.trackId = first.filterId; - selectCounterData.name = name; - // @ts-ignore - selectCounterData.first = `${first.value}`; - selectCounterData.count = `${list.length}`; - // @ts-ignore - selectCounterData.last = `${list[list.length - 1].value}`; - selectCounterData.delta = `${parseInt(selectCounterData.last) - parseInt(selectCounterData.first)}`; - selectCounterData.rate = (parseInt(selectCounterData.delta) / ((range * 1.0) / 1000000000)).toFixed(4); - // @ts-ignore - selectCounterData.min = `${first.value}`; - selectCounterData.max = '0'; - let weightAvg = 0.0; - for (let i = 0; i < list.length; i++) { - let counter = list[i]; - // @ts-ignore - if (counter.value < parseInt(selectCounterData.min)) { - // @ts-ignore - selectCounterData.min = counter.value.toString(); - } - // @ts-ignore - if (counter.value > parseInt(selectCounterData.max)) { - // @ts-ignore - selectCounterData.max = counter.value.toString(); - } - // @ts-ignore - let start = i === 0 ? leftNs : counter.startNS; - // @ts-ignore - let end = i === list.length - 1 ? rightNs : list[i + 1].startNS; - // @ts-ignore - weightAvg += counter.value * (((end - start) * 1.0) / range); - } - selectCounterData.avgWeight = weightAvg.toFixed(2); - } - return selectCounterData; - } - - sortByColumn(detail: unknown): void { - // @ts-ignore - function compare(property, sort, type) { - return function (hangCounterLeftData: SelectionData, hangCounterRightData: SelectionData): number { - if (hangCounterLeftData.process === ' ' || hangCounterRightData.process === ' ') { - return 0; - } - if (type === 'number') { - return sort === 2 // @ts-ignore - ? parseFloat(hangCounterRightData[property]) - parseFloat(hangCounterLeftData[property]) // @ts-ignore - : parseFloat(hangCounterLeftData[property]) - parseFloat(hangCounterRightData[property]); - } else { - // @ts-ignore - if (hangCounterRightData[property] > hangCounterLeftData[property]) { - return sort === 2 ? 1 : -1; - } else { - // @ts-ignore - if (hangCounterRightData[property] === hangCounterLeftData[property]) { - return 0; - } else { - return sort === 2 ? -1 : 1; - } - } - } - }; - } - // @ts-ignore - if (detail.key === 'name') { - // @ts-ignore - this.hangCounterSource.sort(compare(detail.key, detail.sort, 'string')); - } else { - // @ts-ignore - this.hangCounterSource.sort(compare(detail.key, detail.sort, 'number')); - } - this.hangCounterTbl!.recycleDataSource = this.hangCounterSource; - } -} diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.html.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.html.ts new file mode 100644 index 00000000..17198556 --- /dev/null +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.html.ts @@ -0,0 +1,90 @@ +/* + * 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 TabPaneHangSummaryHtml = ` +
+
+
+ + +
+ + + +
+ +
+
+ + `; diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.ts new file mode 100644 index 00000000..a147d3c9 --- /dev/null +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.ts @@ -0,0 +1,292 @@ +/* + * 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 { ColorUtils } from '../../base/ColorUtils' +import { LitTable } from '../../../../../base-ui/table/lit-table' +import { LitIcon } from '../../../../../base-ui/icon/LitIcon' +import { TabPaneHangSummaryHtml } from '../hang/TabPaneHangSummary.html' +import { NUM_30, NUM_40 } from '../../../../bean/NumBean' +import { HangStruct } from '../../../../database/ui-worker/ProcedureWorkerHang' +import { HangType, SpHangChart } from '../../../chart/SpHangChart' +import { queryAllHangs } from '../../../../database/sql/Hang.sql' + +@element('tab-hang-summary') +export class TabPaneHangSummary extends BaseElement { + private hangSummaryTable: HTMLDivElement | undefined | null + private summaryDownLoadTbl: LitTable | undefined | null + private systemHangSource: HangStruct[] = [] + private hangTreeNodes: HangTreeNode[] = [] + private expansionDiv: HTMLDivElement | undefined | null + private expansionUpIcon: LitIcon | undefined | null + private expansionDownIcon: LitIcon | undefined | null + private expandedNodeList: Set = new Set() + private hangLevel: string[] = ['Instant', 'Circumstantial', 'Micro', 'Severe'] + private selectTreeDepth: number = 0 + private currentSelection: SelectionParam | undefined + + set data(selectionParam: SelectionParam) { + if (selectionParam === this.currentSelection) { + return + } + this.currentSelection = selectionParam + this.expandedNodeList.clear() + this.expansionUpIcon!.name = 'up' + this.expansionDownIcon!.name = 'down' + this.hangSummaryTable!.innerHTML = '' + this.summaryDownLoadTbl!.recycleDataSource = [] + queryAllHangs().then((ret) => { + const filter = new Set([...selectionParam.hangMapData.keys()].map(key => key.split(' ').at(-1))) + ret = ret.filter(struct => ( + filter.has(`${struct.pid ?? 0}`) + && ((struct.startNS ?? 0) <= selectionParam.rightNs) + && (selectionParam.leftNs <= ((struct.startNS ?? 0) + (struct.dur ?? 0))) + )) + this.systemHangSource = ret + if (filter.size > 0 && selectionParam) { + this.refreshRowNodeTable() + } + }) + } + + initElements(): void { + this.hangSummaryTable = this.shadowRoot?.querySelector('#tab-summary') + this.summaryDownLoadTbl = this.shadowRoot?.querySelector('#tb-hang-summary') + this.expansionDiv = this.shadowRoot?.querySelector('.expansion-div') + this.expansionUpIcon = this.shadowRoot?.querySelector('.expansion-up-icon') + this.expansionDownIcon = this.shadowRoot?.querySelector('.expansion-down-icon') + let summaryTreeLevel: string[] = ['Type', '/Process', '/Hang'] + this.shadowRoot?.querySelectorAll('.head-label').forEach((summaryTreeHead): void => { + summaryTreeHead.addEventListener('click', (): void => { + this.selectTreeDepth = summaryTreeLevel.indexOf(summaryTreeHead.textContent!) + this.expandedNodeList.clear() + this.refreshSelectDepth(this.hangTreeNodes) + this.refreshRowNodeTable(true) + }) + }) + this.hangSummaryTable!.onscroll = (): void => { + let hangTreeTableEl = this.shadowRoot?.querySelector('.hang-tree-table') + if (hangTreeTableEl) { + hangTreeTableEl.scrollTop = this.hangSummaryTable?.scrollTop || 0 + } + } + } + + initHtml(): string { + return TabPaneHangSummaryHtml + } + + connectedCallback(): void { + super.connectedCallback() + new ResizeObserver((): void => { + this.parentElement!.style.overflow = 'hidden' + this.refreshRowNodeTable() + }).observe(this.parentElement!) + this.expansionDiv?.addEventListener('click', this.expansionClickEvent) + } + + disconnectedCallback(): void { + super.disconnectedCallback() + this.expansionDiv?.removeEventListener('click', this.expansionClickEvent) + } + + expansionClickEvent = (): void => { + this.expandedNodeList.clear() + if (this.expansionUpIcon?.name === 'down') { + this.selectTreeDepth = 0 + this.expansionUpIcon!.name = 'up' + this.expansionDownIcon!.name = 'down' + } else { + this.selectTreeDepth = 4 + this.expansionUpIcon!.name = 'down' + this.expansionDownIcon!.name = 'up' + } + this.refreshSelectDepth(this.hangTreeNodes) + this.refreshRowNodeTable(true) + } + + private refreshSelectDepth(hangTreeNodes: HangTreeNode[]): void { + hangTreeNodes.forEach((item): void => { + if (item.depth < this.selectTreeDepth) { + this.expandedNodeList.add(item.id) + if (item.children.length > 0) { + this.refreshSelectDepth(item.children) + } + } + }) + } + + private createRowNodeTableEL( + rowNodeList: HangTreeNode[], + tableTreeEl: HTMLDivElement, + tableCountEl: HTMLDivElement, + rowColor: string = '' + ): void { + let unitPadding: number = 20 + let leftPadding: number = 5 + rowNodeList.forEach((rowNode): void => { + let tableTreeRowEl: HTMLElement = document.createElement('tr') + tableTreeRowEl.className = 'tree-row-tr' + tableTreeRowEl.title = rowNode.name + '' + let leftSpacingEl: HTMLElement = document.createElement('td') + leftSpacingEl.style.paddingLeft = `${rowNode.depth * unitPadding + leftPadding}px` + tableTreeRowEl.appendChild(leftSpacingEl) + this.addToggleIconEl(rowNode, tableTreeRowEl) + let rowNodeTextEL: HTMLElement = document.createElement('td') + rowNodeTextEL.textContent = rowNode.name + '' + rowNodeTextEL.className = 'row-name-td' + tableTreeRowEl.appendChild(rowNodeTextEL) + tableTreeEl.appendChild(tableTreeRowEl) + let tableCountRowEl: HTMLElement = document.createElement('tr') + tableCountRowEl.title = rowNode.count.toString() + let countEL: HTMLElement = document.createElement('td') + countEL.textContent = rowNode.count.toString() + countEL.className = 'count-column-td' + if (rowNode.depth === 0) { + rowNodeTextEL.style.color = ColorUtils.getHangColor((rowNode.name as HangType) ?? "") + countEL.style.color = ColorUtils.getHangColor((rowNode.name as HangType) ?? "") + } else { + rowNodeTextEL.style.color = rowColor + countEL.style.color = rowColor + } + tableCountRowEl.appendChild(countEL) + tableCountEl.appendChild(tableCountRowEl) + if (rowNode.children && this.expandedNodeList.has(rowNode.id)) { + this.createRowNodeTableEL(rowNode.children, tableTreeEl, tableCountEl, countEL.style.color) + } + }) + } + + private addToggleIconEl(rowNode: HangTreeNode, tableRowEl: HTMLElement): void { + let toggleIconEl: HTMLElement = document.createElement('td') + let expandIcon = document.createElement('lit-icon') + expandIcon.classList.add('tree-icon') + if (rowNode.children && rowNode.children.length > 0) { + toggleIconEl.appendChild(expandIcon) + // @ts-ignore + expandIcon.name = this.expandedNodeList.has(rowNode.id) ? 'minus-square' : 'plus-square' + toggleIconEl.classList.add('expand-icon') + toggleIconEl.addEventListener('click', (): void => { + let scrollTop = this.hangSummaryTable?.scrollTop ?? 0 + this.changeNode(rowNode.id) + this.hangSummaryTable!.scrollTop = scrollTop + }) + } + tableRowEl.appendChild(toggleIconEl) + } + + private changeNode(currentNode: number): void { + if (this.expandedNodeList.has(currentNode)) { + this.expandedNodeList.delete(currentNode) + } else { + this.expandedNodeList.add(currentNode) + } + this.refreshRowNodeTable(true) + } + + private refreshRowNodeTable(useCacheRefresh: boolean = false): void { + this.hangSummaryTable!.innerHTML = '' + if (this.hangSummaryTable && this.parentElement) { + this.hangSummaryTable.style.height = `${this.parentElement!.clientHeight - NUM_30}px` + } + if (!useCacheRefresh) { + this.hangTreeNodes = this.buildTreeTblNodes(this.systemHangSource) + if (this.hangTreeNodes.length > 0) { + this.summaryDownLoadTbl!.recycleDataSource = this.hangTreeNodes + } else { + this.summaryDownLoadTbl!.recycleDataSource = [] + } + } + let tableFragmentEl: DocumentFragment = document.createDocumentFragment() + let tableTreeEl: HTMLDivElement = document.createElement('div') + tableTreeEl.className = 'hang-tree-table' + let tableCountEl: HTMLDivElement = document.createElement('div') + if (this.parentElement) { + tableTreeEl.style.height = `${this.parentElement!.clientHeight - NUM_40}px` + } + this.createRowNodeTableEL(this.hangTreeNodes, tableTreeEl, tableCountEl, '') + let emptyTr = document.createElement('tr') + emptyTr.className = 'tree-row-tr' + tableTreeEl?.appendChild(emptyTr) + let emptyCountTr = document.createElement('tr') + emptyCountTr.className = 'tree-row-tr' + tableCountEl?.appendChild(emptyCountTr) + tableFragmentEl.appendChild(tableTreeEl) + tableFragmentEl.appendChild(tableCountEl) + this.hangSummaryTable!.appendChild(tableFragmentEl) + } + + private buildTreeTblNodes(hangTreeNodes: HangStruct[]): HangTreeNode[] { + let root: HangTreeNode = { + id: 0, depth: 0, children: [], + name: 'All', count: 0 + } + let id = 1 + hangTreeNodes = hangTreeNodes.map(node => ({ + ...node, + type: SpHangChart.calculateHangType(node.dur ?? 0) + })) + for (const item of hangTreeNodes) { + let typeNode = root.children.find((node) => node.name === item.type) + if (typeNode) { + typeNode.count += 1 + } else { + typeNode = { + id: id += 1, + depth: 0, + children: [], + count: 1, + name: item.type ?? "Undefined Type?" + } + root.children.push(typeNode) + } + + let processNode = typeNode.children.find((node) => node.name === item.pname) + if (processNode) { + processNode.count += 1 + } else { + processNode = { + id: id += 1, + depth: 1, + children: [], + count: 1, + name: item.pname ?? "Process" + } + typeNode.children.push(processNode) + } + + let contentNode = { + id: id += 1, + depth: 2, + children: [], + count: 1, + name: item.content ?? "" + } + processNode.children.push(contentNode) + } + + root.children.sort((a, b) => this.hangLevel.indexOf(b.name) - this.hangLevel.indexOf(a.name)) + return root.children + } +} + +export interface HangTreeNode { + id: number + depth: number + children: HangTreeNode[] + name: string + count: number +} diff --git a/ide/src/trace/database/data-trafic/CommonArgs.ts b/ide/src/trace/database/data-trafic/CommonArgs.ts index 8994ae61..ef61ea0a 100644 --- a/ide/src/trace/database/data-trafic/CommonArgs.ts +++ b/ide/src/trace/database/data-trafic/CommonArgs.ts @@ -41,4 +41,5 @@ export interface Args { windowId: number; isPin: number; scratchId: number; + minDur: number; } diff --git a/ide/src/trace/database/data-trafic/HangDataReceiver.ts b/ide/src/trace/database/data-trafic/HangDataReceiver.ts index f9ee3d65..5c695128 100644 --- a/ide/src/trace/database/data-trafic/HangDataReceiver.ts +++ b/ide/src/trace/database/data-trafic/HangDataReceiver.ts @@ -31,8 +31,8 @@ LEFT JOIN thread t ON LEFT JOIN process p ON p.ipid = t.ipid WHERE - c.dur >= 33000000 - AND c.name LIKE 'H:Et%' + c.dur >= ${args.minDur} + AND c.name LIKE 'H:Et:%' AND p.pid = ${args.pid} `.trim(); diff --git a/ide/src/trace/database/data-trafic/HangDataSender.ts b/ide/src/trace/database/data-trafic/HangDataSender.ts index 4d505d6b..095aff7b 100644 --- a/ide/src/trace/database/data-trafic/HangDataSender.ts +++ b/ide/src/trace/database/data-trafic/HangDataSender.ts @@ -17,8 +17,9 @@ import { CHART_OFFSET_LEFT, MAX_COUNT, QueryEnum, TraficEnum } from './utils/Que import { threadPool } from '../SqlLite'; import { TraceRow } from '../../component/trace/base/TraceRow'; import { HangStruct } from '../ui-worker/ProcedureWorkerHang'; +import { FlagsConfig } from '../../component/SpFlags'; -export function realHangDataSender( +export function hangDataSender( processId: number = 0, row: TraceRow, args?: any, @@ -26,10 +27,13 @@ export function realHangDataSender( let trafic: number = TraficEnum.Memory; let width = row.clientWidth - CHART_OFFSET_LEFT; return new Promise((resolve, reject): void => { + let flagsItemJson = JSON.parse(window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY)!) + let minDur = parseInt(flagsItemJson.hangValue) threadPool.submitProto( QueryEnum.HangData, { pid: processId, + minDur: minDur, // queryAll: args && args.queryAll, selectStartNS: args ? args.startNS : 0, @@ -37,7 +41,6 @@ export function realHangDataSender( selectTotalNS: args ? args.endNS - args.startNS : 0, trafic: trafic, width: width, - }, (res: unknown, len: number, transfer: boolean): void => { resolve(arrayBufferHandler(transfer ? res : row.sharedArrayBuffers, len)); diff --git a/ide/src/trace/database/sql/Hang.sql.ts b/ide/src/trace/database/sql/Hang.sql.ts index bccd3e2e..69184e1b 100644 --- a/ide/src/trace/database/sql/Hang.sql.ts +++ b/ide/src/trace/database/sql/Hang.sql.ts @@ -13,16 +13,23 @@ * limitations under the License. */ +import { FlagsConfig } from '../../component/SpFlags'; import { query } from '../SqlLite'; +import { HangStruct } from '../ui-worker/ProcedureWorkerHang'; -export const queryRealHangData = (): Promise> => - query( - 'queryHangsData', - ` +}>> => query( + 'queryHangData', + ` SELECT p.pid as id, p.name as name, @@ -34,26 +41,48 @@ LEFT JOIN thread t ON LEFT JOIN process p ON p.ipid = t.ipid WHERE - c.name LIKE 'H:Et%' - AND c.dur >= 33000000 + c.name LIKE 'H:Et:%' + AND c.dur >= ${getMinDur()} GROUP BY p.pid `.trim() - ) +) export const queryHangFuncName = (): Promise> => - query('queryHangFuncName', - ` +}>> => query('queryHangFuncName', + ` SELECT c.id as id, c.name as name FROM callstack c WHERE - c.dur >= 33000000 - AND c.name LIKE 'H:Et%' + c.dur >= ${getMinDur()} + AND c.name LIKE 'H:Et:%' `.trim() - ) \ No newline at end of file +) + +export const queryAllHangs = (): Promise> => query( + 'queryAllHangs', + ` +SELECT + c.id as id, + c.ts - r.start_ts as startNS, + c.dur as dur, + t.tid as tid, + p.pid as pid, + p.name as pname, + c.name as content +FROM + callstack c, trace_range r +LEFT JOIN thread t ON + t.itid = c.callid +LEFT JOIN process p ON + p.ipid = t.ipid +WHERE + c.dur >= ${getMinDur()} + AND c.name LIKE 'H:Et:%' +`.trim() +) \ No newline at end of file diff --git a/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts index a1cbdd59..694c8929 100644 --- a/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts +++ b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { BaseStruct, dataFilterHandler, drawLoadingFrame, isFrameContainPoint, Render } from './ProcedureWorkerCommon' +import { BaseStruct, dataFilterHandler, drawLoadingFrame, drawString, isFrameContainPoint, Render } from './ProcedureWorkerCommon' import { TraceRow } from '../../component/trace/base/TraceRow' import { SpSystemTrace } from '../../component/SpSystemTrace' import { HangType } from '../../component/chart/SpHangChart' @@ -25,7 +25,6 @@ export class HangRender extends Render { useCache: boolean type: string index: number - processName: string }, row: TraceRow ): void { @@ -39,7 +38,7 @@ export class HangRender extends Render { endNS: TraceRow.range?.endNS ?? 0, totalNS: TraceRow.range?.totalNS ?? 0, frame: row.frame, - paddingTop: 5, + paddingTop: 2, useCache: hangReq.useCache || !(TraceRow.range?.refresh ?? false), } dataFilterHandler(hangList, hangFilter, filterConfig) @@ -57,23 +56,14 @@ export class HangRender extends Render { HangStruct.hoverHangStruct = undefined } hangReq.context.closePath() - let s = hangReq.processName - let textMetrics = hangReq.context.measureText(s) - hangReq.context.globalAlpha = 0.8 - hangReq.context.fillStyle = '#f0f0f0' - hangReq.context.fillRect(0, 5, textMetrics.width + 8, 18) - hangReq.context.globalAlpha = 1 - hangReq.context.fillStyle = '#333' - hangReq.context.textBaseline = 'middle' - hangReq.context.fillText(s, 4, 5 + 9) } } export function HangStructOnClick(clickRowType: string, sp: SpSystemTrace): Promise { return new Promise((resolve, reject) => { - if (clickRowType === TraceRow.ROW_TYPE_HANG && HangStruct.hoverHangStruct) { + if ((clickRowType === TraceRow.ROW_TYPE_HANG || clickRowType === TraceRow.ROW_TYPE_HANG_INNER) && HangStruct.hoverHangStruct) { HangStruct.selectHangStruct = HangStruct.hoverHangStruct - sp.traceSheetEL?.displayHangData(HangStruct.selectHangStruct) + sp.traceSheetEL?.displayHangData(HangStruct.selectHangStruct, sp) sp.timerShaftEL?.modifyFlagList(undefined) reject(new Error()) } else { @@ -105,20 +95,18 @@ export class HangStruct extends BaseStruct { })[data.type!] } - static draw(hangContext: CanvasRenderingContext2D, data: HangStruct): void { + static draw(ctx: CanvasRenderingContext2D, data: HangStruct): void { if (data.frame) { - hangContext.fillStyle = HangStruct.getFrameColor(data) - hangContext.strokeStyle = HangStruct.getFrameColor(data) + ctx.fillStyle = HangStruct.getFrameColor(data) + ctx.strokeStyle = HangStruct.getFrameColor(data) - hangContext.globalAlpha = 0.6 - hangContext.fillRect(data.frame.x, data.frame.y, data.frame.width, data.frame.height) - if (HangStruct.isHover(data)) { - hangContext.lineWidth = 3 - hangContext.globalAlpha = 1 - hangContext.strokeRect(data.frame.x, data.frame.y, data.frame.width, data.frame.height) + ctx.globalAlpha = 1 + ctx.lineWidth = 1 + ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, data.frame.height) + if (data.frame.width > 10) { + ctx.fillStyle = '#fff'; + drawString(ctx, `${data.type || ''}`, 1, data.frame, data); } - hangContext.globalAlpha = 1 - hangContext.lineWidth = 1 } } -- Gitee From ce794b3d0e8530be9ff75278007e7864915113c6 Mon Sep 17 00:00:00 2001 From: LiYuxi Date: Mon, 15 Jul 2024 17:23:41 +0800 Subject: [PATCH 03/25] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=83=A8=E5=88=86hangs?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: LiYuxi --- ide/src/trace/component/chart/SpProcessChart.ts | 2 +- .../component/trace/sheet/TabPaneCurrentSelection.ts | 3 +++ .../component/trace/sheet/hang/TabPaneHang.html.ts | 12 ++++++------ .../trace/component/trace/sheet/hang/TabPaneHang.ts | 1 + .../trace/database/data-trafic/HangDataReceiver.ts | 1 + ide/src/trace/database/sql/Hang.sql.ts | 2 ++ .../trace/database/ui-worker/ProcedureWorkerHang.ts | 5 +++++ 7 files changed, 19 insertions(+), 7 deletions(-) diff --git a/ide/src/trace/component/chart/SpProcessChart.ts b/ide/src/trace/component/chart/SpProcessChart.ts index e5145c78..7f0f23c5 100644 --- a/ide/src/trace/component/chart/SpProcessChart.ts +++ b/ide/src/trace/component/chart/SpProcessChart.ts @@ -653,7 +653,7 @@ export class SpProcessChart { }, row: TraceRow ): TraceRow | null { - if (!this.hangProcessSet.has(data.pid) && !FlagsConfig.getFlagsConfigEnableStatus("Hangs")) { + if (!this.hangProcessSet.has(data.pid) || !FlagsConfig.getFlagsConfigEnableStatus("Hangs")) { return null } let hangsRow = TraceRow.skeleton(); diff --git a/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts b/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts index 4f74d2dd..758bca29 100644 --- a/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts +++ b/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts @@ -575,6 +575,9 @@ export class TabPaneCurrentSelection extends BaseElement { ][index], value: item, })).forEach((item, index) => { + if (index === 0) { + item.value = item.value.split(':').at(-1)! + } list.push(item) }) diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts index f775901d..1374b1db 100644 --- a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts @@ -100,7 +100,7 @@ option { - + @@ -108,15 +108,15 @@ option { - + - + - + - + - + `; diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts index 95b8a5d2..6f453824 100644 --- a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts @@ -293,6 +293,7 @@ export class HangStructInPane { this.pname = `${parent.pname ?? this.pname} ${parent.pid ?? ''}`.trim() this.type = SpHangChart.calculateHangType(parent.dur ?? 0); [this.sendEventTid, this.sendTime, this.expectHandleTime, this.taskNameId, this.caller] = (parent.content ?? ",0,0,,").split(',').map(i => i.trim()) + this.sendEventTid = this.sendEventTid.split(':').at(-1)! } static new(parent: HangStruct): HangStructInPane { diff --git a/ide/src/trace/database/data-trafic/HangDataReceiver.ts b/ide/src/trace/database/data-trafic/HangDataReceiver.ts index 5c695128..44176b22 100644 --- a/ide/src/trace/database/data-trafic/HangDataReceiver.ts +++ b/ide/src/trace/database/data-trafic/HangDataReceiver.ts @@ -33,6 +33,7 @@ LEFT JOIN process p ON WHERE c.dur >= ${args.minDur} AND c.name LIKE 'H:Et:%' + AND t.is_main_thread = 1 AND p.pid = ${args.pid} `.trim(); diff --git a/ide/src/trace/database/sql/Hang.sql.ts b/ide/src/trace/database/sql/Hang.sql.ts index 69184e1b..a6dfa9c4 100644 --- a/ide/src/trace/database/sql/Hang.sql.ts +++ b/ide/src/trace/database/sql/Hang.sql.ts @@ -42,6 +42,7 @@ LEFT JOIN process p ON p.ipid = t.ipid WHERE c.name LIKE 'H:Et:%' + AND t.is_main_thread = 1 AND c.dur >= ${getMinDur()} GROUP BY p.pid @@ -83,6 +84,7 @@ LEFT JOIN process p ON p.ipid = t.ipid WHERE c.dur >= ${getMinDur()} + AND t.is_main_thread = 1 AND c.name LIKE 'H:Et:%' `.trim() ) \ No newline at end of file diff --git a/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts index 694c8929..3cee7e93 100644 --- a/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts +++ b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts @@ -107,6 +107,11 @@ export class HangStruct extends BaseStruct { ctx.fillStyle = '#fff'; drawString(ctx, `${data.type || ''}`, 1, data.frame, data); } + + if (this.isHover(data)) { + ctx.fillStyle = '#ffffff66' + ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, data.frame.height) + } } } -- Gitee From e4963add6b0517691d06331a87730d900d79d26a Mon Sep 17 00:00:00 2001 From: zhangyan Date: Fri, 2 Aug 2024 18:14:04 +0800 Subject: [PATCH 04/25] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E6=A1=86=E9=80=89=E6=97=B6=E6=97=97=E5=AD=90=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=9A=84=E7=BA=BF=E6=9C=AA=E6=B6=88=E5=A4=B1=E5=8F=8A=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E6=A1=86=E9=80=89touch=E5=8F=8A=E5=85=B6=E4=BB=96func?= =?UTF-8?q?=E6=97=B6=E6=95=B0=E6=8D=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhangyan --- ide/src/trace/component/SpFlags.ts | 20 +++++++++++++------- ide/src/trace/component/SpSystemTrace.ts | 7 +++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/ide/src/trace/component/SpFlags.ts b/ide/src/trace/component/SpFlags.ts index 98016219..b1768ec8 100644 --- a/ide/src/trace/component/SpFlags.ts +++ b/ide/src/trace/component/SpFlags.ts @@ -26,7 +26,7 @@ const CAT_SORT = { 'Thread first': 'thread' }; -const CONFIG_STATE = { +const CONFIG_STATE:unknown = { 'VSync': ['vsyncValue', 'VsyncGeneratior'], 'Start&Finish Trace Category': ['catValue', 'Business first'] }; @@ -85,19 +85,23 @@ export class SpFlags extends BaseElement { private flagSelectListener(configSelect: unknown): void { // @ts-ignore let title = configSelect.getAttribute('title'); - let listSelect = this.shadowRoot?.querySelector(`#${CONFIG_STATE[title as keyof typeof CONFIG_STATE][0]}`); + //@ts-ignore + let listSelect = this.shadowRoot?.querySelector(`#${CONFIG_STATE[title]?.[0]}`); // @ts-ignore FlagsConfig.updateFlagsConfig(title!, configSelect.selectedOptions[0].value); - if (CONFIG_STATE[title as keyof typeof CONFIG_STATE]) { + //@ts-ignore + if (listSelect) { // @ts-ignore if (configSelect.selectedOptions[0].value === 'Enabled') { listSelect?.removeAttribute('disabled'); } else { listSelect?.childNodes.forEach((child: ChildNode) => { let selectEl = child as HTMLOptionElement; - if (child.textContent === CONFIG_STATE[title as keyof typeof CONFIG_STATE][1]) { + //@ts-ignore + if (child.textContent === CONFIG_STATE[title]?.[1]) { selectEl.selected = true; - FlagsConfig.updateFlagsConfig(CONFIG_STATE[title as keyof typeof CONFIG_STATE][0], selectEl.value); + //@ts-ignore + FlagsConfig.updateFlagsConfig(CONFIG_STATE[title]?.[0], selectEl.value); } else { selectEl.selected = false; } @@ -152,13 +156,15 @@ export class SpFlags extends BaseElement { } if (config.title === 'VSync') { - let configKey = CONFIG_STATE['VSync' as keyof typeof CONFIG_STATE][0]; + //@ts-ignore + let configKey = CONFIG_STATE[config.title]?.[0]; let configFooterDiv = this.createPersonOption(VSYNC_VAL, configKey, config.addInfo!.vsyncValue, config.title); configDiv.appendChild(configFooterDiv); } if (config.title === 'Start&Finish Trace Category') { - let configKey = CONFIG_STATE['Start&Finish Trace Category' as keyof typeof CONFIG_STATE][0]; + //@ts-ignore + let configKey = CONFIG_STATE[config.title]?.[0]; let configFooterDiv = this.createPersonOption(CAT_SORT, configKey, config.addInfo!.catValue, config.title); configDiv.appendChild(configFooterDiv); } diff --git a/ide/src/trace/component/SpSystemTrace.ts b/ide/src/trace/component/SpSystemTrace.ts index 74c89360..0970fb15 100644 --- a/ide/src/trace/component/SpSystemTrace.ts +++ b/ide/src/trace/component/SpSystemTrace.ts @@ -348,6 +348,7 @@ export class SpSystemTrace extends BaseElement { for (let i = 0; i < flagList.length; i++) { if (flagList[i].type === 'triangle') { flagList.splice(i, 1); + this.timerShaftELFlagChange(this.hoverFlag, null); i--; } } @@ -355,8 +356,10 @@ export class SpSystemTrace extends BaseElement { pushPidToSelection(selection: SelectionParam, id: string): void { let pid = parseInt(id); - if (!selection.processIds.includes(pid)) { - selection.processIds.push(pid); + if (!isNaN(pid)) { + if (!selection.processIds.includes(pid)) { + selection.processIds.push(pid); + } } } // @ts-ignore -- Gitee From d00430d20870153c685d6759915fa6f9bf82faa1 Mon Sep 17 00:00:00 2001 From: zhangzepeng Date: Sat, 3 Aug 2024 14:28:41 +0800 Subject: [PATCH 05/25] =?UTF-8?q?=E2=80=99feat:record-new-trace=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E6=A0=B7=E5=BC=8F=E8=B0=83=E6=95=B4=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=89=8B=E5=8A=A8=E6=90=9C=E7=B4=A2=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?,nativememory=E5=8E=BB=E6=8E=89ALL-Process2=E3=80=81fix:?= =?UTF-8?q?=E4=BF=AE=E5=A4=8Dabilitymemory=E6=B3=B3=E9=81=93=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E7=BB=98=E5=88=B6=E9=97=AE=E9=A2=983=E3=80=81fix:?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9F=A5=E8=AF=A2=E5=90=8E=E5=A4=9A=E6=AC=A1?= =?UTF-8?q?=E5=B1=95=E5=BC=80=E6=8A=98=E5=8F=A0=E4=BC=9A=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E5=88=86=E5=B8=83=E5=BC=8F=E8=BF=9E=E7=BA=BF=E7=9A=84=E6=83=85?= =?UTF-8?q?=E5=86=B5=E2=80=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhangzepeng --- ide/src/base-ui/select/LitSelectHtml.ts | 2 +- ide/src/base-ui/table/lit-table.ts | 45 +++++++ .../trace/component/chart/SpProcessChart.ts | 1 - .../trace/component/setting/SpAllocations.ts | 2 +- .../trace/component/setting/SpArkTs.html.ts | 4 +- ide/src/trace/component/setting/SpArkTs.ts | 34 +++-- .../trace/component/setting/SpFFRTConfig.ts | 2 - .../component/setting/SpHilogRecord.html.ts | 4 +- .../trace/component/setting/SpHilogRecord.ts | 33 ++--- .../component/setting/SpHisysEvent.html.ts | 8 +- .../trace/component/setting/SpHisysEvent.ts | 118 +++++++++--------- .../component/setting/SpSdkConfig.html.ts | 4 +- .../trace/component/setting/SpSdkConfig.ts | 25 ++-- .../component/setting/SpVmTracker.html.ts | 4 +- .../trace/component/setting/SpVmTracker.ts | 34 +++-- ide/src/trace/database/sql/Memory.sql.ts | 1 - 16 files changed, 193 insertions(+), 128 deletions(-) diff --git a/ide/src/base-ui/select/LitSelectHtml.ts b/ide/src/base-ui/select/LitSelectHtml.ts index 785ebaa1..069c8603 100644 --- a/ide/src/base-ui/select/LitSelectHtml.ts +++ b/ide/src/base-ui/select/LitSelectHtml.ts @@ -258,7 +258,7 @@ export const selectVHtmlStr = ` position: relative; overflow: visible; cursor: pointer; - border-radius: 2px; + border-radius: 16px; outline: none; -webkit-user-select:none ; -moz-user-select:none; diff --git a/ide/src/base-ui/table/lit-table.ts b/ide/src/base-ui/table/lit-table.ts index 167485ff..f485b256 100644 --- a/ide/src/base-ui/table/lit-table.ts +++ b/ide/src/base-ui/table/lit-table.ts @@ -67,6 +67,7 @@ export class LitTable extends HTMLElement { private _mode = TableMode.Expand; private columnResizeEnable: boolean = true; private _isSearch: boolean = false; + private maxLength: number = 0; constructor() { super(); @@ -164,6 +165,28 @@ export class LitTable extends HTMLElement { } set recycleDataSource(value) { + // 处理数据按小数点位置对齐 + if (value && value.length) { + // 找出数字部分的最大长度 + value.forEach((item: any) => { + // 提取数字部分(包括小数点) + if (item.durFormat) { + const match = item.durFormat.match(/^(\d+(\.\d+)?)/); + if (match && match[1]) { + // 计算长度(包括小数点) + const length = match[1].length; + this.maxLength = Math.max(this.maxLength, length); + } + } + if (item.percent) { + const match = item.percent.match(/^(\d+(\.\d+)?)/); + if (match && match[1]) { + const length = match[1].length; + this.maxLength = Math.max(this.maxLength, length); + } + } + }) + } if (this.tableElement) { this.isScrollXOutSide = this.tableElement!.scrollWidth > this.tableElement!.clientWidth; this.isRecycleList = true; @@ -1583,6 +1606,28 @@ export class LitTable extends HTMLElement { if (rowObject.data.rowName === 'cpu-profiler' && dataIndex === 'symbolName') { (child as HTMLElement).innerHTML = ''; } else { + //@ts-ignore + if (rowObject.data.durFormat) { //ebpf泳道下的analysis页 + // 提取数字部分(包括小数点) + if (dataIndex === 'durFormat') { + // @ts-ignore + const match = text.match(/^(\d+(\.\d+)?)(.*)$/); + if (match && match[1] && match[3]) { + // 计算需要添加的空格数 + const padding = '\xa0\xa0'.repeat(this.maxLength - match[1].length); + // 构造新的durFormat字符串 + text = padding + match[1] + match[3]; + } + } + if(dataIndex === 'percent'){ + // @ts-ignore + const match = text.match(/^(\d+(\.\d+)?)(.*)$/); + if (match && match[1]) { + const padding = '\xa0\xa0'.repeat(this.maxLength - match[1].length); + text = padding + match[1]; + } + } + } //@ts-ignore (child as HTMLElement).innerHTML = text; } //@ts-ignore diff --git a/ide/src/trace/component/chart/SpProcessChart.ts b/ide/src/trace/component/chart/SpProcessChart.ts index e558a624..054d97f5 100644 --- a/ide/src/trace/component/chart/SpProcessChart.ts +++ b/ide/src/trace/component/chart/SpProcessChart.ts @@ -641,7 +641,6 @@ export class SpProcessChart { this.trace.linkNodes.forEach((linkNodeItem) => this.handler3(e, linkNodeItem)); }, 300); } else { - FuncStruct.selectLineFuncStruct.push(FuncStruct.selectFuncStruct); offsetYTimeOut = setTimeout(() => { this.trace.linkNodes?.forEach((linkProcessItem) => { this.handler4(e, linkProcessItem, processRow); diff --git a/ide/src/trace/component/setting/SpAllocations.ts b/ide/src/trace/component/setting/SpAllocations.ts index 52bda968..091d1241 100644 --- a/ide/src/trace/component/setting/SpAllocations.ts +++ b/ide/src/trace/component/setting/SpAllocations.ts @@ -518,7 +518,7 @@ export class SpAllocations extends BaseElement { Cmd.getProcess().then((processList: string[]): void => { this.processId?.dataSource(processList, ''); if (processList.length > 0) { - this.processId?.dataSource(processList, 'ALL-Process'); + this.processId?.dataSource(processList, ''); } else { this.processId?.dataSource([], ''); } diff --git a/ide/src/trace/component/setting/SpArkTs.html.ts b/ide/src/trace/component/setting/SpArkTs.html.ts index ff474484..142fa8b4 100644 --- a/ide/src/trace/component/setting/SpArkTs.html.ts +++ b/ide/src/trace/component/setting/SpArkTs.html.ts @@ -131,8 +131,8 @@ lit-switch { Process Record process - +
diff --git a/ide/src/trace/component/setting/SpArkTs.ts b/ide/src/trace/component/setting/SpArkTs.ts index 94629c1d..fb993c35 100644 --- a/ide/src/trace/component/setting/SpArkTs.ts +++ b/ide/src/trace/component/setting/SpArkTs.ts @@ -25,10 +25,11 @@ import { SpCheckDesBox } from './SpCheckDesBox'; import LitSwitch from '../../../base-ui/switch/lit-switch'; import { SpApplication } from '../../SpApplication'; import { SpArkTsHtml } from './SpArkTs.html'; +import { LitSelectV } from '../../../base-ui/select/LitSelectV'; @element('sp-ark-ts') export class SpArkTs extends BaseElement { - private processInput: LitAllocationSelect | undefined | null; + private processInput: LitSelectV | undefined | null; private spCheckDesBox: SpCheckDesBox | undefined | null; private radioBox: LitRadioBox | undefined | null; private interval: HTMLInputElement | undefined | null; @@ -111,24 +112,31 @@ export class SpArkTs extends BaseElement { initElements(): void { this.interval = this.shadowRoot?.querySelector('#interval'); - this.processInput = this.shadowRoot?.querySelector('lit-allocation-select'); - let processInput = this.processInput?.shadowRoot?.querySelector('.multipleSelect') as HTMLDivElement; + this.processInput = this.shadowRoot?.querySelector('lit-select-v'); + let processInput = this.processInput?.shadowRoot?.querySelector('input') as HTMLDivElement; this.cpuSwitch = this.shadowRoot?.querySelector('#cpu-switch') as LitSwitch; processInput!.addEventListener('mousedown', () => { - if (SpRecordTrace.serialNumber === '') { - this.processInput!.processData = []; - this.processInput!.initData(); + if (this.startSamp && (SpRecordTrace.serialNumber === '')) { + this.processInput!.dataSource([], ''); } }); processInput!.addEventListener('mouseup', () => { - if (SpRecordTrace.serialNumber === '') { - this.processInput!.processData = []; - this.processInput!.initData(); + if (this.startSamp) { + if (SpRecordTrace.serialNumber === '') { + this.processInput!.dataSource([], ''); + } else { + Cmd.getDebugProcess().then((processList) => { + if (processList.length > 0) { + this.processInput!.dataSource(processList, ''); + } else { + this.processInput!.dataSource([], ''); + } + }); + } + processInput!.removeAttribute('readonly'); } else { - Cmd.getDebugProcess().then((processList) => { - this.processInput!.processData = processList; - this.processInput!.initData(); - }); + processInput!.setAttribute('readonly', 'readonly'); + return; } }); this.litSwitch = this.shadowRoot?.querySelector('lit-switch') as LitSwitch; diff --git a/ide/src/trace/component/setting/SpFFRTConfig.ts b/ide/src/trace/component/setting/SpFFRTConfig.ts index 86c6e66c..fc23522b 100644 --- a/ide/src/trace/component/setting/SpFFRTConfig.ts +++ b/ide/src/trace/component/setting/SpFFRTConfig.ts @@ -195,7 +195,6 @@ export class SpFFRTConfig extends BaseElement { Cmd.getPackage().then((packageList: string[]): void => { let finalDataList = packageList.map(str => str.replace(/\t/g, '')); if (finalDataList.length > 0) { - processInputEl.readOnly = true; startupPNameEl.dataSource(finalDataList, 'ALL-Process'); } else { startupPNameEl.dataSource([], ''); @@ -261,7 +260,6 @@ export class SpFFRTConfig extends BaseElement { Cmd.getProcess().then((processList: string[]): void => { selectInputEl.dataSource(processList, ''); if (processList.length > 0) { - processInputEl.readOnly = true; selectInputEl.dataSource(processList, 'ALL-Process'); } else { selectInputEl.dataSource([], ''); diff --git a/ide/src/trace/component/setting/SpHilogRecord.html.ts b/ide/src/trace/component/setting/SpHilogRecord.html.ts index d0454da9..a2f1c6cc 100644 --- a/ide/src/trace/component/setting/SpHilogRecord.html.ts +++ b/ide/src/trace/component/setting/SpHilogRecord.html.ts @@ -92,9 +92,9 @@ export const SpHiLogRecordHtml = ` Process Record process
- - +
diff --git a/ide/src/trace/component/setting/SpHilogRecord.ts b/ide/src/trace/component/setting/SpHilogRecord.ts index 1c3f87c4..628c23a2 100644 --- a/ide/src/trace/component/setting/SpHilogRecord.ts +++ b/ide/src/trace/component/setting/SpHilogRecord.ts @@ -23,11 +23,12 @@ import { Cmd } from '../../../command/Cmd'; import { LitAllocationSelect } from '../../../base-ui/select/LitAllocationSelect'; import { LitSelect } from '../../../base-ui/select/LitSelect'; import { SpHiLogRecordHtml } from './SpHilogRecord.html'; +import { LitSelectV } from '../../../base-ui/select/LitSelectV'; @element('sp-hi-log') export class SpHilogRecord extends BaseElement { private vmTrackerSwitch: LitSwitch | undefined | null; - private processSelectEl: LitAllocationSelect | undefined | null; + private processSelectEl: LitSelectV | undefined | null; private logsSelectEl: LitSelect | undefined | null; get recordHilog(): boolean { @@ -47,7 +48,7 @@ export class SpHilogRecord extends BaseElement { initElements(): void { this.vmTrackerSwitch = this.shadowRoot?.querySelector('.hilog-switch') as LitSwitch; - this.processSelectEl = this.shadowRoot?.querySelector('.record-process-select') as LitAllocationSelect; + this.processSelectEl = this.shadowRoot?.querySelector('.record-process-select') as LitSelectV; this.logsSelectEl = this.shadowRoot?.querySelector('.record-logs-select') as LitSelect; let hiLogConfigList = this.shadowRoot?.querySelectorAll('.hilog-config-top'); this.vmTrackerSwitch.addEventListener('change', () => { @@ -62,20 +63,24 @@ export class SpHilogRecord extends BaseElement { }); } }); - let processInputEl = this.processSelectEl.shadowRoot?.querySelector('.multipleSelect') as HTMLInputElement; + let processInputEl = this.processSelectEl.shadowRoot?.querySelector('input') as HTMLInputElement; processInputEl.addEventListener('mousedown', () => { - if (SpRecordTrace.serialNumber === '') { - this.processSelectEl!.processData = []; - this.processSelectEl!.initData(); + if (this.recordHilog) { + if ((SpRecordTrace.serialNumber === '')) { + this.processSelectEl!.dataSource([], ''); + } else { + Cmd.getProcess().then((processList) => { + if (processList.length > 0) { + this.processSelectEl!.dataSource(processList, 'ALL-Process'); + } else { + this.processSelectEl!.dataSource([], ''); + } + }); + } + processInputEl!.removeAttribute('readonly'); } else { - Cmd.getProcess().then((processList) => { - if (processList.length > 0 && this.recordHilog) { - processInputEl!.setAttribute('readonly', 'readonly'); - } - processList.unshift('ALL-Process'); - this.processSelectEl!.processData = processList; - this.processSelectEl!.initData(); - }); + processInputEl!.setAttribute('readonly', 'readonly'); + return; } }); } diff --git a/ide/src/trace/component/setting/SpHisysEvent.html.ts b/ide/src/trace/component/setting/SpHisysEvent.html.ts index 6df54d17..0afe05d6 100644 --- a/ide/src/trace/component/setting/SpHisysEvent.html.ts +++ b/ide/src/trace/component/setting/SpHisysEvent.html.ts @@ -88,17 +88,17 @@ export const SpHiSysEventHtml = `