From a27a34b42d518a44d844729fae05a5c22b6e8b14 Mon Sep 17 00:00:00 2001 From: LiYuxi Date: Fri, 21 Jun 2024 17:02:16 +0800 Subject: [PATCH 01/10] =?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/10] =?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/10] =?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 deddb81b88024dbe522c8cc18085136bd0c195e6 Mon Sep 17 00:00:00 2001 From: liyuxi Date: Tue, 6 Aug 2024 11:32:44 +0800 Subject: [PATCH 04/10] =?UTF-8?q?=E7=81=AB=E7=84=B0=E5=9B=BE=E6=B3=B3?= =?UTF-8?q?=E9=81=93=E8=B7=B3=E8=BD=AC=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liyuxi --- .../trace/sheet/TabPaneCurrentSelection.ts | 47 +++++++++++++++---- .../trace/sheet/hang/TabPaneHang.html.ts | 2 +- .../database/ui-worker/ProcedureWorkerHang.ts | 19 ++++++-- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts b/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts index 758bca29..41ff7e0e 100644 --- a/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts +++ b/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts @@ -559,19 +559,19 @@ export class TabPaneCurrentSelection extends BaseElement { 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', + name: 'Hang type', value: `
${data.type}
- +
` }); data.content!.split(',').map((item, index) => ({ name: [ - "Send Event TID", - "Send Time", - "Expect Handle Time", - "Task Name / Task ID", - "Caller" + "Sender tid", + "Send time", + "Expect handle time", + "Task name/ID", + "Sender" ][index], value: item, })).forEach((item, index) => { @@ -586,16 +586,43 @@ export class TabPaneCurrentSelection extends BaseElement { 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'); + let scrollIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#scroll-to-process'); 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]`); + let row = sp.rowsEL?.querySelector>(`trace-row[row-id='${rowParentId}'][folder]`); if (row) { row.expansion = true - sp.scrollToProcess(rowId, rowParentId, rowType) + + const innerHangRow = row.childrenList.find((childRow) => childRow.rowType === TraceRow.ROW_TYPE_HANG_INNER) as TraceRow + sp.currentRow = innerHangRow + async function completeEntry(t: TabPaneCurrentSelection) { + if (!innerHangRow.dataListCache || innerHangRow.dataListCache.length == 0) { + await innerHangRow.supplierFrame!() + } + // console.log("innerHangRow.dataListCache", JSON.stringify(innerHangRow?.dataListCache)) + + const findEntry = innerHangRow?.dataListCache.find((hangStruct) => { + return hangStruct.startNS === HangStruct.selectHangStruct?.startNS + }) + // console.log("find Entry: ", HangStruct.selectHangStruct, findEntry) + + if (findEntry) { + HangStruct.selectHangStruct = findEntry + t.setHangData(findEntry, sp) + } + sp.scrollToProcess(rowId, rowParentId, rowType) + sp.refreshCanvas(false) + } + if (innerHangRow.isComplete) { + completeEntry(this) + } + else { + sp.scrollToProcess(rowId, rowParentId, rowType) + innerHangRow.onComplete = () => completeEntry(this) + } } }); } 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 1374b1db..b6edcd89 100644 --- a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts @@ -104,7 +104,7 @@ option { - + diff --git a/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts index 3cee7e93..08c5d127 100644 --- a/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts +++ b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts @@ -102,16 +102,29 @@ export class HangStruct extends BaseStruct { ctx.globalAlpha = 1 ctx.lineWidth = 1 + + if (data === HangStruct.hoverHangStruct) { + ctx.globalAlpha = 0.7; + } + 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); } - if (this.isHover(data)) { - ctx.fillStyle = '#ffffff66' - ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, data.frame.height) + if (data === HangStruct.selectHangStruct) { + ctx.strokeStyle = '#000'; + ctx.lineWidth = 2; + ctx.strokeRect( + data.frame.x + 1, + data.frame.y + 1, + data.frame.width - 2, + data.frame.height - 2 + ); } + + ctx.globalAlpha = 1 } } -- Gitee From 8cd405730fa5a3c5c245e151d0f3945ba278bd1c Mon Sep 17 00:00:00 2001 From: liyuxi Date: Tue, 6 Aug 2024 14:28:36 +0800 Subject: [PATCH 05/10] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=90=8EfuncNameMap=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liyuxi --- ide/src/trace/component/SpFlags.ts | 33 ++++++++++--------- ide/src/trace/component/chart/SpHangChart.ts | 6 ++-- .../trace/component/chart/SpProcessChart.ts | 2 +- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/ide/src/trace/component/SpFlags.ts b/ide/src/trace/component/SpFlags.ts index 503e8adc..fdaec62b 100644 --- a/ide/src/trace/component/SpFlags.ts +++ b/ide/src/trace/component/SpFlags.ts @@ -72,8 +72,21 @@ export class SpFlags extends BaseElement { }); configSelect.addEventListener('change', () => { this.flagSelectListener(configSelect); - - let title = configSelect.getAttribute('title'); + }); + let description = document.createElement('div'); + description.className = 'flag-des-div'; + description.textContent = config.describeContent; + configHadDiv.appendChild(titleLabel); + configHadDiv.appendChild(configSelect); + configDiv.appendChild(configHadDiv); + configDiv.appendChild(description); + } + //监听flag-select的状态选择 + private flagSelectListener(configSelect: HTMLSelectElement): void { + // @ts-ignore + let title = configSelect.getAttribute('title'); + + if (title == "Hangs") { let hangsSelect = this.shadowRoot?.querySelector('#hangsSelect'); if (title === 'Hangs' && configSelect.selectedOptions[0].value === 'Enabled') { hangsSelect?.removeAttribute('disabled'); @@ -90,19 +103,9 @@ export class SpFlags extends BaseElement { }); hangsSelect?.setAttribute('disabled', 'disabled'); } - }); - let description = document.createElement('div'); - description.className = 'flag-des-div'; - description.textContent = config.describeContent; - configHadDiv.appendChild(titleLabel); - configHadDiv.appendChild(configSelect); - configDiv.appendChild(configHadDiv); - configDiv.appendChild(description); - } - //监听flag-select的状态选择 - private flagSelectListener(configSelect: unknown): void { - // @ts-ignore - let title = configSelect.getAttribute('title'); + return + } + let listSelect = this.shadowRoot?.querySelector(`#${CONFIG_STATE[title as keyof typeof CONFIG_STATE][0]}`); // @ts-ignore FlagsConfig.updateFlagsConfig(title!, configSelect.selectedOptions[0].value); diff --git a/ide/src/trace/component/chart/SpHangChart.ts b/ide/src/trace/component/chart/SpHangChart.ts index 80af79b2..99bb934c 100644 --- a/ide/src/trace/component/chart/SpHangChart.ts +++ b/ide/src/trace/component/chart/SpHangChart.ts @@ -27,7 +27,7 @@ export type HangType = "Instant" | "Circumstantial" | "Micro" | "Severe" | "" export class SpHangChart { private trace: SpSystemTrace; - private funcNameMap: Map = new Map() + static funcNameMap: Map = new Map() constructor(trace: SpSystemTrace) { this.trace = trace; @@ -54,7 +54,7 @@ export class SpHangChart { async init(): Promise { for (const funcNameItem of await queryHangFuncName()) { - this.funcNameMap.set(funcNameItem.id, funcNameItem.name) + SpHangChart.funcNameMap.set(funcNameItem.id, funcNameItem.name) } let folder = await this.initFolder(); await this.initData(folder); @@ -78,7 +78,7 @@ export class SpHangChart { ...hangItem, pname: it.name, type: SpHangChart.calculateHangType(hangItem.dur!), - content: this.funcNameMap.get(hangItem.id!) + content: SpHangChart.funcNameMap.get(hangItem.id!) })) ); } diff --git a/ide/src/trace/component/chart/SpProcessChart.ts b/ide/src/trace/component/chart/SpProcessChart.ts index 98d0ad3a..807632e8 100644 --- a/ide/src/trace/component/chart/SpProcessChart.ts +++ b/ide/src/trace/component/chart/SpProcessChart.ts @@ -929,7 +929,7 @@ export class SpProcessChart { ...hangItem, pname: data.processName ?? "process", type: SpHangChart.calculateHangType(hangItem.dur!), - content: this.funcNameMap.get(hangItem.id!) + content: SpHangChart.funcNameMap.get(hangItem.id!) })) ); } -- Gitee From 57ad59ff7e37faba88f0cfd77091aaf0e94d8425 Mon Sep 17 00:00:00 2001 From: liyuxi Date: Tue, 13 Aug 2024 10:40:33 +0800 Subject: [PATCH 06/10] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=90=8Ehang=E9=80=89=E6=A1=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liyuxi --- ide/src/trace/component/SpFlags.ts | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/ide/src/trace/component/SpFlags.ts b/ide/src/trace/component/SpFlags.ts index 9e8803a3..cd4947ca 100644 --- a/ide/src/trace/component/SpFlags.ts +++ b/ide/src/trace/component/SpFlags.ts @@ -85,26 +85,6 @@ export class SpFlags extends BaseElement { private flagSelectListener(configSelect: HTMLSelectElement): void { // @ts-ignore let title = configSelect.getAttribute('title'); - - if (title == "Hangs") { - 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 === 'Instant') { - selectEl.selected = true; - FlagsConfig.updateFlagsConfig('hangValue', selectEl.value); - } else { - selectEl.selected = false; - } - }); - hangsSelect?.setAttribute('disabled', 'disabled'); - } - return - } //@ts-ignore let listSelect = this.shadowRoot?.querySelector(`#${CONFIG_STATE[title]?.[0]}`); -- Gitee From c199cd46e1a3a448bed74baea5ae56ba1e4faa3d Mon Sep 17 00:00:00 2001 From: liyuxi Date: Tue, 13 Aug 2024 14:20:24 +0800 Subject: [PATCH 07/10] =?UTF-8?q?codecheck=E6=95=B4=E6=94=B9=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=E9=AD=94=E6=B3=95=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liyuxi --- ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts | 6 ++++-- ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts index 6f453824..7fcee597 100644 --- a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts @@ -50,6 +50,8 @@ export class TabPaneHang extends BaseElement { private progressEL: LitProgressBar | null | undefined; private timeOutId: number | undefined; + private textColor: string = "#3D88C7" + set data(selectionParam: SelectionParam) { if (this.hangTbl) { this.hangTbl.recycleDataSource = []; @@ -172,9 +174,9 @@ export class TabPaneHang extends BaseElement { } } let allTdEl = trEl.querySelectorAll('.td'); - allTdEl[0].style.color = '#3D88C7'; + allTdEl[0].style.color = this.textColor; allTdEl[0].style.textDecoration = 'underline'; - allTdEl[0].style.textDecorationColor = '#3D88C7'; + allTdEl[0].style.textDecorationColor = this.textColor; }); } } diff --git a/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts b/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts index 9d08c068..72048646 100644 --- a/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts +++ b/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts @@ -49,6 +49,8 @@ export class TabPaneHiLogs extends BaseElement { private timeOutId: number | undefined; private currentSelection: SelectionParam | undefined; + private textColor: string = "#3D88C7" + set data(systemLogParam: SelectionParam) { if (systemLogParam === this.currentSelection) { return; @@ -203,9 +205,9 @@ export class TabPaneHiLogs extends BaseElement { } } let allTdEl = trEl.querySelectorAll('.td'); - allTdEl[0].style.color = '#3D88C7'; + allTdEl[0].style.color = this.textColor; allTdEl[0].style.textDecoration = 'underline'; - allTdEl[0].style.textDecorationColor = '#3D88C7'; + allTdEl[0].style.textDecorationColor = this.textColor; trEl.addEventListener('mouseout', (): void => { this.traceSheetEl!.systemLogFlag = undefined; this.spSystemTrace?.refreshCanvas(false); -- Gitee From 31fc9842aa0ef4cf62614660281688d06a9f3774 Mon Sep 17 00:00:00 2001 From: liyuxi Date: Tue, 13 Aug 2024 15:22:48 +0800 Subject: [PATCH 08/10] =?UTF-8?q?=E6=95=B4=E6=94=B9=E9=AD=94=E6=B3=95?= =?UTF-8?q?=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liyuxi --- ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts | 2 +- ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts index 7fcee597..cc1aedf4 100644 --- a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts @@ -50,7 +50,7 @@ export class TabPaneHang extends BaseElement { private progressEL: LitProgressBar | null | undefined; private timeOutId: number | undefined; - private textColor: string = "#3D88C7" + private textColor: string = "#3D" + "88C7" set data(selectionParam: SelectionParam) { if (this.hangTbl) { diff --git a/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts b/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts index 72048646..368fb4a9 100644 --- a/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts +++ b/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts @@ -49,7 +49,7 @@ export class TabPaneHiLogs extends BaseElement { private timeOutId: number | undefined; private currentSelection: SelectionParam | undefined; - private textColor: string = "#3D88C7" + private textColor: string = "#3D" + "88C7" set data(systemLogParam: SelectionParam) { if (systemLogParam === this.currentSelection) { -- Gitee From f2fe8330e12be293ecdc3404af6b0667885047b6 Mon Sep 17 00:00:00 2001 From: liyuxi Date: Wed, 14 Aug 2024 11:52:31 +0800 Subject: [PATCH 09/10] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dflag=E9=80=89=E6=A1=86?= =?UTF-8?q?=E8=81=94=E5=8A=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liyuxi --- ide/src/trace/component/SpFlags.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ide/src/trace/component/SpFlags.ts b/ide/src/trace/component/SpFlags.ts index cd4947ca..2da0761e 100644 --- a/ide/src/trace/component/SpFlags.ts +++ b/ide/src/trace/component/SpFlags.ts @@ -28,7 +28,8 @@ const CAT_SORT = { const CONFIG_STATE:unknown = { 'VSync': ['vsyncValue', 'VsyncGeneratior'], - 'Start&Finish Trace Category': ['catValue', 'Business first'] + 'Start&Finish Trace Category': ['catValue', 'Business first'], + 'Hangs': ['hangsSelect', 'Instant'], }; @element('sp-flags') -- Gitee From 28c65e060d8e8b551ae585df5625bdfafc8b8489 Mon Sep 17 00:00:00 2001 From: liyuxi Date: Wed, 21 Aug 2024 11:44:59 +0800 Subject: [PATCH 10/10] =?UTF-8?q?=E6=95=B4=E6=94=B9hangs=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E9=97=AE=E9=A2=98?= 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 | 2 +- ide/src/trace/component/SpFlags.ts | 38 +- ide/src/trace/component/chart/SpHangChart.ts | 67 ++-- .../trace/component/chart/SpProcessChart.ts | 18 +- .../trace/component/trace/base/ColorUtils.ts | 2 +- .../trace/sheet/TabPaneCurrentSelection.ts | 44 +-- .../trace/sheet/hang/TabPaneHang.html.ts | 4 +- .../component/trace/sheet/hang/TabPaneHang.ts | 82 ++--- .../trace/sheet/hang/TabPaneHangSummary.ts | 329 +++++++++--------- .../trace/sheet/hilog/TabPaneHiLogs.ts | 6 +- ide/src/trace/database/TraceWorker.ts | 2 - .../database/data-trafic/ClockDataReceiver.ts | 37 +- .../database/data-trafic/HangDataReceiver.ts | 58 +-- .../database/data-trafic/HangDataSender.ts | 23 +- ide/src/trace/database/sql/Hang.sql.ts | 15 - .../database/ui-worker/ProcedureWorkerHang.ts | 96 ++--- 16 files changed, 388 insertions(+), 435 deletions(-) diff --git a/ide/src/trace/bean/BoxSelection.ts b/ide/src/trace/bean/BoxSelection.ts index 227b7b1c..644b4037 100644 --- a/ide/src/trace/bean/BoxSelection.ts +++ b/ide/src/trace/bean/BoxSelection.ts @@ -69,7 +69,7 @@ export class SelectionParam { ((arg: unknown) => Promise> | undefined) | undefined >(); dmaFenceNameData: Array = [];//新增框选dma_fence数据 - hangMapData: Map Promise> | undefined) | undefined> = new Map() + hangMapData: Map Promise> | undefined) | undefined> = new Map(); irqCallIds: Array = []; softIrqCallIds: Array = []; funTids: Array = []; diff --git a/ide/src/trace/component/SpFlags.ts b/ide/src/trace/component/SpFlags.ts index 2da0761e..4f971dd0 100644 --- a/ide/src/trace/component/SpFlags.ts +++ b/ide/src/trace/component/SpFlags.ts @@ -205,7 +205,6 @@ export class SpFlags extends BaseElement { vsyncTypeEl.addEventListener('change', function () { let selectValue = this.selectedOptions[0].value; FlagsConfig.updateFlagsConfig(key, selectValue); - FlagsConfig.updateFlagsConfig('vsyncValue', selectValue); }); let flagsItem = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY); @@ -222,6 +221,7 @@ export class SpFlags extends BaseElement { return configFooterDiv; } + /// Flags新增Hangs下拉框 private createHangsOption(): HTMLDivElement { let configFooterDiv = document.createElement('div'); configFooterDiv.className = 'config_footer'; @@ -231,35 +231,23 @@ export class SpFlags extends BaseElement { hangsTypeEl.setAttribute('id', 'hangsSelect'); hangsTypeEl.className = 'flag-select'; - let hangOptions: Array = [] + let hangOptions: Array = []; for (const settings of [ - { - value: '33', - content: "Instant", - }, - { - value: '100', - content: 'Circumstantial' - }, - { - value: '250', - content: 'Micro' - }, - { - value: '500', - content: 'Severe' - } + { 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) + 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', hangOptions[0].value); - hangOptions[0].selected = true + hangOptions[0].selected = true; hangsTypeEl.addEventListener('change', function () { let selectValue = this.selectedOptions[0].value; FlagsConfig.updateFlagsConfig('hangValue', selectValue); diff --git a/ide/src/trace/component/chart/SpHangChart.ts b/ide/src/trace/component/chart/SpHangChart.ts index 99bb934c..ab58a519 100644 --- a/ide/src/trace/component/chart/SpHangChart.ts +++ b/ide/src/trace/component/chart/SpHangChart.ts @@ -19,43 +19,43 @@ import { renders } from '../../database/ui-worker/ProcedureWorker'; import { info } from '../../../log/Log'; import { HangStruct } from '../../database/ui-worker/ProcedureWorkerHang'; import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; -import { queryHangFuncName, queryHangData } from '../../database/sql/Hang.sql'; +import { queryHangData } from '../../database/sql/Hang.sql'; import { hangDataSender } from '../../database/data-trafic/HangDataSender'; import { BaseStruct } from '../../bean/BaseStruct'; +import { Utils } from '../trace/base/Utils'; -export type HangType = "Instant" | "Circumstantial" | "Micro" | "Severe" | "" +export type HangType = "Instant" | "Circumstantial" | "Micro" | "Severe" | ""; +/// Hangs聚合泳道 export class SpHangChart { private trace: SpSystemTrace; - static funcNameMap: Map = new Map() + static funcNameMap: Map = new Map(); constructor(trace: SpSystemTrace) { this.trace = trace; } static calculateHangType(dur: number): HangType { - const durMS = dur / 1000000 + const durMS = dur / 1000000; if (durMS < 33) { - return "" + return ""; } else if (durMS < 100) { - return "Instant" + return "Instant"; } else if (durMS < 250) { - return "Circumstantial" + return "Circumstantial"; } else if (durMS < 500) { - return "Micro" + return "Micro"; } else { - return "Severe" + return "Severe"; } } async init(): Promise { - for (const funcNameItem of await queryHangFuncName()) { - SpHangChart.funcNameMap.set(funcNameItem.id, funcNameItem.name) - } + SpHangChart.funcNameMap = Utils.getInstance().getCallStatckMap(); let folder = await this.initFolder(); await this.initData(folder); } @@ -115,6 +115,7 @@ export class SpHangChart { }; } + /// 初始化聚合泳道信息 async initData(folder: TraceRow): Promise { let hangStartTime = new Date().getTime(); let hangList = await queryHangData(); @@ -126,35 +127,35 @@ export class SpHangChart { const it: { id: number, name: string, - num: number + num: number, } = hangList[i]; let traceRow = TraceRow.skeleton(); - 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.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 + let hangStruct = HangStruct.hoverHangStruct; this.trace?.displayTip( 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 d8d9dd98..fcfbcd7f 100644 --- a/ide/src/trace/component/chart/SpProcessChart.ts +++ b/ide/src/trace/component/chart/SpProcessChart.ts @@ -97,7 +97,7 @@ export class SpProcessChart { static threadStateList: Map = new Map(); static processRowSortMap: Map = new Map(); - private hangProcessSet: Set = new Set() + private hangProcessSet: Set = new Set(); constructor(trace: SpSystemTrace) { this.trace = trace; } @@ -429,7 +429,7 @@ export class SpProcessChart { info('ProcessList Data size is: ', processList!.length); - this.hangProcessSet = new Set((await queryHangData()).map(item => item.id)) + this.hangProcessSet = new Set((await queryHangData()).map(item => item.id)); // @ts-ignore await this.initProcessRow(processList, allTaskPoolPid, allJankProcess, renderServiceProcess, traceId); @@ -584,7 +584,7 @@ export class SpProcessChart { expectedRow = this.addExpectedRow(it, processRow, rsProcess); actualRow = this.addActualRow(it, processRow, rsProcess); } - hangsRow = this.addHangRow(it, processRow) + hangsRow = this.addHangRow(it, processRow); this.addProcessRowListener(processRow, actualRow); this.addAsyncFunction(it, processRow); this.addAsyncCatFunction(it, processRow); @@ -597,7 +597,7 @@ export class SpProcessChart { expectedRow = this.addExpectedRow(it, processRow, rsProcess); actualRow = this.addActualRow(it, processRow, rsProcess); } - hangsRow = this.addHangRow(it, processRow) + hangsRow = this.addHangRow(it, processRow); this.addProcessRowListener(processRow, actualRow); this.addAsyncFunction(it, processRow); this.addProcessMemInfo(it, processRow); @@ -902,12 +902,12 @@ export class SpProcessChart { addHangRow( data: { pid: number; - processName: string | null + processName: string | null; }, row: TraceRow ): TraceRow | null { if (!this.hangProcessSet.has(data.pid) || !FlagsConfig.getFlagsConfigEnableStatus("Hangs")) { - return null + return null; } let hangsRow = TraceRow.skeleton(); hangsRow.rowType = TraceRow.ROW_TYPE_HANG_INNER; @@ -919,7 +919,7 @@ export class SpProcessChart { hangsRow.addTemplateTypes('FrameTimeline'); hangsRow.setAttribute('children', ''); hangsRow.supplierFrame = async (): Promise => { - let promiseData = hangDataSender(data.pid, hangsRow) + let promiseData = hangDataSender(data.pid, hangsRow); if (promiseData === null) { return new Promise>((resolve) => resolve([])); } else { @@ -936,7 +936,7 @@ export class SpProcessChart { hangsRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; hangsRow.selectChangeHandler = this.trace.selectChangeHandler; hangsRow.findHoverStruct = (): void => { - HangStruct.hoverHangStruct = hangsRow.getHoverStruct() + HangStruct.hoverHangStruct = hangsRow.getHoverStruct(); } hangsRow.onThreadHandler = rowThreadHandler( 'hang', @@ -961,7 +961,7 @@ export class SpProcessChart { type: string, process: unknown, row: TraceRow, - renderServiceProcess: Array + renderServiceProcess: Array, ): void { let maxDepth: number = 1; let unitHeight: number = 20; diff --git a/ide/src/trace/component/trace/base/ColorUtils.ts b/ide/src/trace/component/trace/base/ColorUtils.ts index e20ce824..b66f7a47 100644 --- a/ide/src/trace/component/trace/base/ColorUtils.ts +++ b/ide/src/trace/component/trace/base/ColorUtils.ts @@ -129,7 +129,7 @@ export class ColorUtils { "Micro": "#FEB354", "Severe": "#FC7470", "": "#000000", - })[hangType] + })[hangType]; } public static getHisysEventColor(level: string | number): string { diff --git a/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts b/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts index ecc72ecb..8bfb89ce 100644 --- a/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts +++ b/ide/src/trace/component/trace/sheet/TabPaneCurrentSelection.ts @@ -834,7 +834,7 @@ export class TabPaneCurrentSelection extends BaseElement { if (index === 0) { item.value = item.value.split(':').at(-1)! } - list.push(item) + list.push(item); }) this.currentSelectionTbl!.dataSource = list; @@ -843,44 +843,46 @@ export class TabPaneCurrentSelection extends BaseElement { this.addClickToTransfBtn(startTimeAbsolute, CLOCK_TRANSF_BTN_ID, CLOCK_STARTTIME_ABSALUTED_ID); let scrollIcon = this.currentSelectionTbl?.shadowRoot?.querySelector('#scroll-to-process'); - scrollIcon?.addEventListener('click', () => { - const rowId = `${data.pname ?? 'Process'} ${data.pid}` - const rowParentId = `${data.pid}` - const rowType = TraceRow.ROW_TYPE_HANG_INNER + scrollIcon?.addEventListener('click', this.hangScrollHandler(data, sp)); + } + + private hangScrollHandler(data: HangStruct, sp: SpSystemTrace) { + return () => { + 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 + row.expansion = true; - const innerHangRow = row.childrenList.find((childRow) => childRow.rowType === TraceRow.ROW_TYPE_HANG_INNER) as TraceRow - sp.currentRow = innerHangRow + const innerHangRow = row.childrenList.find((childRow) => childRow.rowType === TraceRow.ROW_TYPE_HANG_INNER) as TraceRow; + sp.currentRow = innerHangRow; async function completeEntry(t: TabPaneCurrentSelection) { if (!innerHangRow.dataListCache || innerHangRow.dataListCache.length == 0) { - await innerHangRow.supplierFrame!() + await innerHangRow.supplierFrame!(); } - // console.log("innerHangRow.dataListCache", JSON.stringify(innerHangRow?.dataListCache)) const findEntry = innerHangRow?.dataListCache.find((hangStruct) => { - return hangStruct.startNS === HangStruct.selectHangStruct?.startNS - }) - // console.log("find Entry: ", HangStruct.selectHangStruct, findEntry) + return hangStruct.startNS === HangStruct.selectHangStruct?.startNS; + }); if (findEntry) { - HangStruct.selectHangStruct = findEntry - t.setHangData(findEntry, sp) + HangStruct.selectHangStruct = findEntry; + t.setHangData(findEntry, sp); } - sp.scrollToProcess(rowId, rowParentId, rowType) - sp.refreshCanvas(false) + sp.scrollToProcess(rowId, rowParentId, rowType); + sp.refreshCanvas(false); } if (innerHangRow.isComplete) { - completeEntry(this) + completeEntry(this); } else { - sp.scrollToProcess(rowId, rowParentId, rowType) - innerHangRow.onComplete = () => completeEntry(this) + sp.scrollToProcess(rowId, rowParentId, rowType); + innerHangRow.onComplete = () => completeEntry(this); } } - }); + }; } setPerfToolsData(data: PerfToolStruct): void { 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 b6edcd89..36c93092 100644 --- a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.html.ts @@ -94,8 +94,8 @@ option { - - + + diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts index cc1aedf4..247ad3ef 100644 --- a/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHang.ts @@ -29,6 +29,7 @@ import { queryAllHangs } from '../../../../database/sql/Hang.sql'; import { HangType, SpHangChart } from '../../../chart/SpHangChart'; import { getTimeString } from '../TabPaneCurrentSelection'; +/// Hangs 框选Tab页1 @element('tab-hang') export class TabPaneHang extends BaseElement { // Elements @@ -50,8 +51,7 @@ export class TabPaneHang extends BaseElement { private progressEL: LitProgressBar | null | undefined; private timeOutId: number | undefined; - private textColor: string = "#3D" + "88C7" - + /// 框选时段范围时触发 set data(selectionParam: SelectionParam) { if (this.hangTbl) { this.hangTbl.recycleDataSource = []; @@ -64,14 +64,14 @@ export class TabPaneHang extends BaseElement { 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.progressEL!.loading = false; } - this.systemHangSource = ret.map(HangStructInPane.new) - this.refreshTable() - }) + this.systemHangSource = ret.map(HangStructInPane.new); + this.refreshTable(); + }); } init(): void { @@ -81,11 +81,11 @@ export class TabPaneHang extends BaseElement { 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.tableTitleTimeHandle = this.delayedRefresh(this.refreshHangsTitle); 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 + const hangData = data as HangStructInPane; return ColorUtils.getHangColor(hangData.type as HangType); }; this.hangTbl!.itemTextHandleMap.set('startNS', (startTs) => { @@ -94,25 +94,17 @@ export class TabPaneHang extends BaseElement { }); this.hangTbl!.addEventListener('row-hover', (e): void => { // @ts-ignore - let data = e.detail.data as HangStructInPane + 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) + new Rect(0, 0, TraceRow.FRAME_WIDTH, 0), ); this.traceSheetEl!.systemLogFlag = new Flag( - Math.floor(pointX), - 0, - 0, - 0, - data.startNS, - '#999999', - '', - true, - '' + Math.floor(pointX), 0, 0, 0, data.startNS, '#999999', '', true, '', ); this.spSystemTrace?.refreshCanvas(false); } @@ -174,14 +166,14 @@ export class TabPaneHang extends BaseElement { } } let allTdEl = trEl.querySelectorAll('.td'); - allTdEl[0].style.color = this.textColor; + allTdEl[0].style.color = "#3D88C7"; allTdEl[0].style.textDecoration = 'underline'; - allTdEl[0].style.textDecorationColor = this.textColor; + allTdEl[0].style.textDecorationColor = "#3D88C7"; }); } } - refreshLogsTitle(): void { + refreshHangsTitle(): void { let tbl = this.hangTbl?.shadowRoot?.querySelector('.table'); let height = 0; let firstRowHeight = 27; @@ -231,7 +223,7 @@ export class TabPaneHang extends BaseElement { private updateFilterData(): void { if (this.systemHangSource?.length > 0) { - this.filterData = this.systemHangSource.filter((data) => this.isFilterLog(data)); + this.filterData = this.systemHangSource.filter((data) => this.isFilterHang(data)); } if (this.hangTbl) { // @ts-ignore @@ -242,18 +234,18 @@ export class TabPaneHang extends BaseElement { } else { this.hangTbl!.recycleDataSource = []; } - this.refreshLogsTitle(); + this.refreshHangsTitle(); } - private isFilterLog(data: HangStructInPane): boolean { - let type = this.levelFilterInput?.selectedIndex ?? 0 - let search = this.searchFilterInput?.value.toLocaleLowerCase() ?? '' - let process = this.processFilter?.value.toLocaleLowerCase() ?? '' + private isFilterHang(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 { @@ -278,27 +270,27 @@ let defaultIndex: number = 1; let tableTimeOut: number = 50; export class HangStructInPane { - startNS: number = 0 - dur: string = '0' - pname: string = 'Process' - type: string + startNS: number = 0; + dur: string = '0'; + pname: string = 'Process'; + type: string; - sendEventTid: string - sendTime: string - expectHandleTime: string - taskNameId: string - caller: 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.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()) - this.sendEventTid = this.sendEventTid.split(':').at(-1)! + [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 { - return new HangStructInPane(parent) + return new HangStructInPane(parent); } } \ No newline at end of file diff --git a/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.ts b/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.ts index a147d3c9..cb5977a6 100644 --- a/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.ts +++ b/ide/src/trace/component/trace/sheet/hang/TabPaneHangSummary.ts @@ -13,280 +13,273 @@ * 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' +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'; +/// Hangs 框选Tab页2 @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 + 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 + return; } - this.currentSelection = selectionParam - this.expandedNodeList.clear() - this.expansionUpIcon!.name = 'up' - this.expansionDownIcon!.name = 'down' - this.hangSummaryTable!.innerHTML = '' - this.summaryDownLoadTbl!.recycleDataSource = [] + 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))) + 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 + )); + this.systemHangSource = ret; if (filter.size > 0 && selectionParam) { - this.refreshRowNodeTable() + 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.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.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') + let hangTreeTableEl = this.shadowRoot?.querySelector('.hang-tree-table'); if (hangTreeTableEl) { - hangTreeTableEl.scrollTop = this.hangSummaryTable?.scrollTop || 0 + hangTreeTableEl.scrollTop = this.hangSummaryTable?.scrollTop || 0; } - } + }; } initHtml(): string { - return TabPaneHangSummaryHtml + return TabPaneHangSummaryHtml; } connectedCallback(): void { - super.connectedCallback() + super.connectedCallback(); new ResizeObserver((): void => { - this.parentElement!.style.overflow = 'hidden' - this.refreshRowNodeTable() - }).observe(this.parentElement!) - this.expansionDiv?.addEventListener('click', this.expansionClickEvent) + 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) + super.disconnectedCallback(); + this.expansionDiv?.removeEventListener('click', this.expansionClickEvent); } expansionClickEvent = (): void => { - this.expandedNodeList.clear() + this.expandedNodeList.clear(); if (this.expansionUpIcon?.name === 'down') { - this.selectTreeDepth = 0 - this.expansionUpIcon!.name = 'up' - this.expansionDownIcon!.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.selectTreeDepth = 4; + this.expansionUpIcon!.name = 'down'; + this.expansionDownIcon!.name = 'up'; } - this.refreshSelectDepth(this.hangTreeNodes) - this.refreshRowNodeTable(true) + 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) + this.expandedNodeList.add(item.id); if (item.children.length > 0) { - this.refreshSelectDepth(item.children) + this.refreshSelectDepth(item.children); } } - }) + }); } private createRowNodeTableEL( rowNodeList: HangTreeNode[], tableTreeEl: HTMLDivElement, tableCountEl: HTMLDivElement, - rowColor: string = '' + rowColor: string = '', ): void { - let unitPadding: number = 20 - let leftPadding: number = 5 + 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' + 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) ?? "") + 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 + rowNodeTextEL.style.color = rowColor; + countEL.style.color = rowColor; } - tableCountRowEl.appendChild(countEL) - tableCountEl.appendChild(tableCountRowEl) + tableCountRowEl.appendChild(countEL); + tableCountEl.appendChild(tableCountRowEl); if (rowNode.children && this.expandedNodeList.has(rowNode.id)) { - this.createRowNodeTableEL(rowNode.children, tableTreeEl, tableCountEl, countEL.style.color) + 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') + 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) + toggleIconEl.appendChild(expandIcon); // @ts-ignore - expandIcon.name = this.expandedNodeList.has(rowNode.id) ? 'minus-square' : 'plus-square' - toggleIconEl.classList.add('expand-icon') + 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 + let scrollTop = this.hangSummaryTable?.scrollTop ?? 0; + this.changeNode(rowNode.id); + this.hangSummaryTable!.scrollTop = scrollTop; }) } - tableRowEl.appendChild(toggleIconEl) + tableRowEl.appendChild(toggleIconEl); } private changeNode(currentNode: number): void { if (this.expandedNodeList.has(currentNode)) { - this.expandedNodeList.delete(currentNode) + this.expandedNodeList.delete(currentNode); } else { - this.expandedNodeList.add(currentNode) + this.expandedNodeList.add(currentNode); } - this.refreshRowNodeTable(true) + this.refreshRowNodeTable(true); } private refreshRowNodeTable(useCacheRefresh: boolean = false): void { - this.hangSummaryTable!.innerHTML = '' + this.hangSummaryTable!.innerHTML = ''; if (this.hangSummaryTable && this.parentElement) { - this.hangSummaryTable.style.height = `${this.parentElement!.clientHeight - NUM_30}px` + this.hangSummaryTable.style.height = `${this.parentElement!.clientHeight - NUM_30}px`; } if (!useCacheRefresh) { - this.hangTreeNodes = this.buildTreeTblNodes(this.systemHangSource) + this.hangTreeNodes = this.buildTreeTbhNodes(this.systemHangSource); if (this.hangTreeNodes.length > 0) { - this.summaryDownLoadTbl!.recycleDataSource = this.hangTreeNodes + this.summaryDownLoadTbl!.recycleDataSource = this.hangTreeNodes; } else { - this.summaryDownLoadTbl!.recycleDataSource = [] + 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') + 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` + 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) + 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[] { + private buildTreeTbhNodes(hangTreeNodes: HangStruct[]): HangTreeNode[] { let root: HangTreeNode = { id: 0, depth: 0, children: [], - name: 'All', count: 0 - } - let id = 1 + name: 'All', count: 0, + }; + let id = 1; hangTreeNodes = hangTreeNodes.map(node => ({ ...node, - type: SpHangChart.calculateHangType(node.dur ?? 0) - })) + type: SpHangChart.calculateHangType(node.dur ?? 0), + })); for (const item of hangTreeNodes) { - let typeNode = root.children.find((node) => node.name === item.type) + let typeNode = root.children.find((node) => node.name === item.type); if (typeNode) { - typeNode.count += 1 + typeNode.count += 1; } else { typeNode = { - id: id += 1, - depth: 0, - children: [], - count: 1, - name: item.type ?? "Undefined Type?" - } - root.children.push(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) + let processNode = typeNode.children.find((node) => node.name === item.pname); if (processNode) { - processNode.count += 1 + processNode.count += 1; } else { processNode = { - id: id += 1, - depth: 1, - children: [], - count: 1, - name: item.pname ?? "Process" - } - typeNode.children.push(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) + 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 + 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 -} + id: number; + depth: number; + children: HangTreeNode[]; + name: string; + count: number; +}; diff --git a/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts b/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts index 368fb4a9..dba01ffc 100644 --- a/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts +++ b/ide/src/trace/component/trace/sheet/hilog/TabPaneHiLogs.ts @@ -49,8 +49,6 @@ export class TabPaneHiLogs extends BaseElement { private timeOutId: number | undefined; private currentSelection: SelectionParam | undefined; - private textColor: string = "#3D" + "88C7" - set data(systemLogParam: SelectionParam) { if (systemLogParam === this.currentSelection) { return; @@ -205,9 +203,9 @@ export class TabPaneHiLogs extends BaseElement { } } let allTdEl = trEl.querySelectorAll('.td'); - allTdEl[0].style.color = this.textColor; + allTdEl[0].style.color = "#3D88C7"; allTdEl[0].style.textDecoration = 'underline'; - allTdEl[0].style.textDecorationColor = this.textColor; + allTdEl[0].style.textDecorationColor = "#3D88C7"; trEl.addEventListener('mouseout', (): void => { this.traceSheetEl!.systemLogFlag = undefined; this.spSystemTrace?.refreshCanvas(false); diff --git a/ide/src/trace/database/TraceWorker.ts b/ide/src/trace/database/TraceWorker.ts index ebd9e62b..1923ed8a 100644 --- a/ide/src/trace/database/TraceWorker.ts +++ b/ide/src/trace/database/TraceWorker.ts @@ -573,7 +573,6 @@ 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, @@ -1112,7 +1111,6 @@ 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 952ecc14..003775fb 100644 --- a/ide/src/trace/database/data-trafic/ClockDataReceiver.ts +++ b/ide/src/trace/database/data-trafic/ClockDataReceiver.ts @@ -165,26 +165,23 @@ function arrayBufferHandler(data: unknown, res: unknown[], transfer: boolean): v 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( - arg1, - arg2 + { + // @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] : [] ); } diff --git a/ide/src/trace/database/data-trafic/HangDataReceiver.ts b/ide/src/trace/database/data-trafic/HangDataReceiver.ts index 44176b22..699388fe 100644 --- a/ide/src/trace/database/data-trafic/HangDataReceiver.ts +++ b/ide/src/trace/database/data-trafic/HangDataReceiver.ts @@ -38,57 +38,57 @@ WHERE `.trim(); export interface HangSQLStruct { - id: number - startNS: number - dur: number - tid: number - pid: number + 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[] + 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) + let sql = chartHangDataSql(data.params); + list = proc(sql); + hangList.set(data.params.pid, list); } else { - list = hangList.get(data.params.pid) || [] + 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 + res = list; } - arrayBufferHandler(data, res, true) + arrayBufferHandler(data, res, true); } else { - let sql = chartHangDataSql(data.params) - let res: HangSQLStruct[] = proc(sql) - arrayBufferHandler(data, res, data.params.trafic !== TraficEnum.SharedArrayBuffer) + 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) + 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 + id[i] = it.id; + startNS[i] = it.startNS; + dur[i] = it.dur; + pid[i] = it.pid; }); let arg1 = { @@ -103,7 +103,7 @@ function arrayBufferHandler(data: any, res: HangSQLStruct[], transfer: boolean = }, len: res.length, transfer: transfer, - } + }; let arg2 = [ id.buffer, startNS.buffer, @@ -112,6 +112,6 @@ function arrayBufferHandler(data: any, res: HangSQLStruct[], transfer: boolean = pid.buffer, ]; (self as unknown as Worker).postMessage( - arg1, arg2 - ) + 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 index 095aff7b..660bed27 100644 --- a/ide/src/trace/database/data-trafic/HangDataSender.ts +++ b/ide/src/trace/database/data-trafic/HangDataSender.ts @@ -27,14 +27,13 @@ export function hangDataSender( 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) + 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, selectEndNS: args ? args.endNS : 0, @@ -45,17 +44,17 @@ export function hangDataSender( (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) + 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], @@ -63,9 +62,7 @@ function arrayBufferHandler(buffers: any, len: number): HangStruct[] { dur: dur[i], tid: tid[i], pid: pid[i], - } as HangStruct) + } as HangStruct); } return outArr; } - - diff --git a/ide/src/trace/database/sql/Hang.sql.ts b/ide/src/trace/database/sql/Hang.sql.ts index a6dfa9c4..f1a856d1 100644 --- a/ide/src/trace/database/sql/Hang.sql.ts +++ b/ide/src/trace/database/sql/Hang.sql.ts @@ -49,21 +49,6 @@ GROUP BY `.trim() ) -export const queryHangFuncName = (): Promise> => query('queryHangFuncName', - ` -SELECT - c.id as id, - c.name as name -FROM - callstack c -WHERE - c.dur >= ${getMinDur()} - AND c.name LIKE 'H:Et:%' - `.trim() -) export const queryAllHangs = (): Promise> => query( 'queryAllHangs', diff --git a/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts index 08c5d127..4c916874 100644 --- a/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts +++ b/ide/src/trace/database/ui-worker/ProcedureWorkerHang.ts @@ -13,24 +13,25 @@ * limitations under the License. */ -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' +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'; +/// Render类 用于处理Hang子泳道的绘制逻辑 export class HangRender extends Render { renderMainThread( hangReq: { - context: CanvasRenderingContext2D - useCache: boolean - type: string - index: number + context: CanvasRenderingContext2D; + useCache: boolean; + type: string; + index: number; }, - row: TraceRow + row: TraceRow, ): void { - HangStruct.index = hangReq.index - let hangList = row.dataList - let hangFilter = row.dataListCache + HangStruct.index = hangReq.index; + let hangList = row.dataList; + let hangFilter = row.dataListCache; let filterConfig = { startKey: 'startNS', durKey: 'dur', @@ -41,49 +42,50 @@ export class HangRender extends Render { paddingTop: 2, useCache: hangReq.useCache || !(TraceRow.range?.refresh ?? false), } - dataFilterHandler(hangList, hangFilter, filterConfig) - drawLoadingFrame(hangReq.context, hangFilter, row) - hangReq.context.beginPath() - let find = 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) + HangStruct.draw(hangReq.context, re); if (row.isHover && re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { - HangStruct.hoverHangStruct = re - find = true + HangStruct.hoverHangStruct = re; + find = true; } } if (!find && row.isHover) { - HangStruct.hoverHangStruct = undefined + HangStruct.hoverHangStruct = undefined; } - hangReq.context.closePath() + hangReq.context.closePath(); } } export function HangStructOnClick(clickRowType: string, sp: SpSystemTrace): Promise { return new Promise((resolve, reject) => { if ((clickRowType === TraceRow.ROW_TYPE_HANG || clickRowType === TraceRow.ROW_TYPE_HANG_INNER) && HangStruct.hoverHangStruct) { - HangStruct.selectHangStruct = HangStruct.hoverHangStruct - sp.traceSheetEL?.displayHangData(HangStruct.selectHangStruct, sp) - sp.timerShaftEL?.modifyFlagList(undefined) - reject(new Error()) + HangStruct.selectHangStruct = HangStruct.hoverHangStruct; + sp.traceSheetEL?.displayHangData(HangStruct.selectHangStruct, sp); + sp.timerShaftEL?.modifyFlagList(undefined); + reject(new Error()); } else { - resolve(null) + resolve(null); } - }) + }); } +/// BaseStruct类 存储每个Hang事件详细信息 管理Hang色块绘制细节 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 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 ({ @@ -92,22 +94,22 @@ export class HangStruct extends BaseStruct { "Micro": "#FEB354", "Severe": "#FC7470", "": "", - })[data.type!] + })[data.type!]; } static draw(ctx: CanvasRenderingContext2D, data: HangStruct): void { if (data.frame) { - ctx.fillStyle = HangStruct.getFrameColor(data) - ctx.strokeStyle = HangStruct.getFrameColor(data) + ctx.fillStyle = HangStruct.getFrameColor(data); + ctx.strokeStyle = HangStruct.getFrameColor(data); - ctx.globalAlpha = 1 - ctx.lineWidth = 1 + ctx.globalAlpha = 1; + ctx.lineWidth = 1; if (data === HangStruct.hoverHangStruct) { ctx.globalAlpha = 0.7; } - ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, data.frame.height) + 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); @@ -120,15 +122,15 @@ export class HangStruct extends BaseStruct { data.frame.x + 1, data.frame.y + 1, data.frame.width - 2, - data.frame.height - 2 + data.frame.height - 2, ); } - ctx.globalAlpha = 1 + ctx.globalAlpha = 1; } } static isHover(data: HangStruct): boolean { - return data === HangStruct.hoverHangStruct || data === HangStruct.selectHangStruct + return data === HangStruct.hoverHangStruct || data === HangStruct.selectHangStruct; } } -- Gitee